@estiva-app/ui 0.16.1 → 0.18.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/README.md +3 -1
- package/dist/Button.d.ts.map +1 -1
- package/dist/Checkbox.d.ts +14 -1
- package/dist/Checkbox.d.ts.map +1 -1
- package/dist/CommandPalette.d.ts.map +1 -1
- package/dist/FilePicker.d.ts +22 -0
- package/dist/FilePicker.d.ts.map +1 -0
- package/dist/Form.d.ts +49 -0
- package/dist/Form.d.ts.map +1 -0
- package/dist/IconButton.d.ts.map +1 -1
- package/dist/SearchInput.d.ts.map +1 -1
- package/dist/TextInput.d.ts.map +1 -1
- package/dist/Textarea.d.ts.map +1 -1
- package/dist/eslint/index.d.ts +1 -1
- package/dist/eslint/index.d.ts.map +1 -1
- package/dist/eslint/index.js +88 -8
- package/dist/eslint/index.js.map +4 -4
- package/dist/eslint/no-raw-element.d.ts +88 -0
- package/dist/eslint/no-raw-element.d.ts.map +1 -0
- package/dist/formBusy.d.ts +15 -0
- package/dist/formBusy.d.ts.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +479 -350
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
- package/src/Button.tsx +4 -1
- package/src/Checkbox.mdx +19 -1
- package/src/Checkbox.stories.tsx +12 -0
- package/src/Checkbox.test.tsx +54 -0
- package/src/Checkbox.tsx +40 -3
- package/src/CommandPalette.mdx +6 -2
- package/src/CommandPalette.stories.tsx +6 -3
- package/src/CommandPalette.test.tsx +15 -0
- package/src/CommandPalette.tsx +34 -46
- package/src/FilePicker.mdx +49 -0
- package/src/FilePicker.stories.tsx +61 -0
- package/src/FilePicker.test.tsx +79 -0
- package/src/FilePicker.tsx +40 -0
- package/src/Form.mdx +78 -0
- package/src/Form.stories.tsx +68 -0
- package/src/Form.test.tsx +385 -0
- package/src/Form.tsx +173 -0
- package/src/IconButton.tsx +4 -1
- package/src/SearchInput.mdx +2 -2
- package/src/SearchInput.tsx +3 -1
- package/src/TextInput.mdx +2 -2
- package/src/TextInput.test.tsx +7 -0
- package/src/TextInput.tsx +4 -1
- package/src/Textarea.tsx +4 -1
- package/src/eslint/index.test.ts +24 -15
- package/src/eslint/index.ts +6 -5
- package/src/eslint/{no-raw-button.test.ts → no-raw-element.test.ts} +53 -8
- package/src/eslint/no-raw-element.ts +180 -0
- package/src/formBusy.ts +19 -0
- package/src/index.ts +2 -0
- package/stories/Choosing.mdx +3 -0
- package/dist/eslint/no-raw-button.d.ts +0 -11
- package/dist/eslint/no-raw-button.d.ts.map +0 -1
- package/src/eslint/no-raw-button.ts +0 -39
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/cn.ts", "../src/ScrollArea.tsx", "../src/TopBar.tsx", "../src/AppShell.tsx", "../src/AttachmentCard.tsx", "../src/Link.tsx", "../src/Card.tsx", "../src/IconButton.tsx", "../src/Tooltip.tsx", "../src/Kbd.tsx", "../src/Avatar.tsx", "../src/AvatarGroup.tsx", "../src/Banner.tsx", "../src/Button.tsx", "../src/Checkbox.tsx", "../src/Chip.tsx", "../src/CommandPalette.tsx", "../src/ChipInput.tsx", "../src/Menu.tsx", "../src/triggerDisabled.ts", "../src/SectionLabel.tsx", "../src/EmptyState.tsx", "../src/Field.tsx", "../src/Skeleton.tsx", "../src/InlineChip.tsx", "../src/IdentityMenu.tsx", "../src/Divider.tsx", "../src/Person.tsx", "../src/PersonTrigger.tsx", "../src/Select.tsx", "../src/TextInput.tsx", "../src/Textarea.tsx", "../src/ConfirmDialog.tsx", "../src/DialogShell.tsx", "../src/EditableText.tsx", "../src/ProgressBar.tsx", "../src/Breadcrumb.tsx", "../src/NavItem.tsx", "../src/Popover.tsx", "../src/Toolbar.tsx", "../src/ReactionPicker.tsx", "../src/PreviewCard.tsx", "../src/Rail.tsx", "../src/RailItem.tsx", "../src/Sidebar.tsx", "../src/Property.tsx", "../src/Reaction.tsx", "../src/SearchInput.tsx", "../src/SectionHeader.tsx", "../src/CollapsibleSection.tsx", "../src/Tabs.tsx", "../src/Toast.tsx"],
|
|
4
|
-
"sourcesContent": ["import { clsx, type ClassValue } from 'clsx'\nimport { extendTailwindMerge } from 'tailwind-merge'\n\n/**\n * The preset's type ramp, by name, so tailwind-merge can classify the token\n * classes as sizes. Stock tailwind-merge has never heard of `text-body-2`,\n * files it under text-COLOUR, and lets `text-text-primary` in the same\n * merged list knock it out \u2014 the trap Katerina's Tabs review confirmed by\n * measurement (2026-09-01), and the reason shared components used to spell\n * sizes as arbitrary values. Naming the ramp here retires that rule for\n * everything using this cn \u2014 the package and, since the 2026-09-01 dedupe,\n * both apps.\n *\n * Must match `tailwind-preset.js` `fontSize` key for key; `cn.test.ts`\n * asserts it (the preset imports node-only modules, so it cannot be\n * imported here without dragging them into the browser bundle).\n */\nconst FONT_SIZE_TOKENS = [\n 'h1', 'h2', 'h3', 'h4', 'h5',\n 'body-1', 'body-2', 'body-2-strong', 'caption', 'menu',\n 'btn-default', 'btn-small', 'input-label', 'input-value', 'input-helper', 'chip', 'small',\n]\n\n/**\n * The preset's box-shadow and drop-shadow keys, for the same reason: stock\n * tailwind-merge files an unknown `shadow-x` under shadow-COLOUR, so\n * `shadow-focus-ring` beside `shadow-glow-warning` lost one of them, and\n * `shadow-glow-warning` beside `shadow-md` kept both (measured 2026-09-06).\n * `cn.test.ts` pins these to the preset too.\n */\nconst BOX_SHADOW_TOKENS = ['sm', 'md', 'lg', 'focus-ring', 'glow-warning', 'glow-success', 'glow-accent', 'highlight-inset']\nconst DROP_SHADOW_TOKENS = ['glow-success', 'glow-accent', 'glow-warning']\n\nconst twMerge = extendTailwindMerge({\n extend: {\n classGroups: {\n 'font-size': [{ text: FONT_SIZE_TOKENS }],\n shadow: [{ shadow: BOX_SHADOW_TOKENS }],\n 'drop-shadow': [{ 'drop-shadow': DROP_SHADOW_TOKENS }],\n },\n },\n})\n\n/** Merge class names, last-one-wins on conflicting Tailwind utilities. */\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n\nexport { FONT_SIZE_TOKENS as _fontSizeTokens, BOX_SHADOW_TOKENS as _boxShadowTokens, DROP_SHADOW_TOKENS as _dropShadowTokens }\n", "import type { ReactNode, Ref, UIEventHandler } from 'react'\nimport { ScrollArea as BaseScrollArea } from '@base-ui/react/scroll-area'\nimport { cn } from './cn'\n\n/**\n * A region that scrolls without taking width for its scrollbar.\n *\n * A native scrollbar is part of the layout: the moment content overflows, the\n * bar takes its width from the content, the text column narrows, and the\n * right-hand padding looks wider than the left. Katerina saw it in every\n * scrolling surface of both apps (2026-09-08). Base UI's `ScrollArea` hides\n * the native bar and draws its own over the content, so nothing moves when\n * it appears; the bar shows while the pointer is over the region or the\n * content is moving, and fades otherwise.\n *\n * Three boxes, and each class prop lands on one:\n *\n * - the **region** (`className`): its size and placement \u2014 `h-[240px]`, a\n * grid cell, `flex-1 min-h-0`;\n * - the **viewport** (`viewportClassName`): the box that scrolls \u2014 a\n * `max-h-*` cap goes here, and nowhere else, because a cap on the region\n * lets the viewport grow to its content and nothing scrolls (measured,\n * 2026-09-08);\n * - the **content** (`contentClassName`): the box the children sit in \u2014 the\n * padding, the gap, `flex flex-col`. Base UI watches this box for size\n * changes; without it the bar kept showing on Ship's page for overflow that\n * was no longer there (measured, 2026-09-09).\n *\n * The bar is `border-strong` on nothing, 6px wide, inset 2px from the edge \u2014\n * the thin scrollbar both apps had styled by hand in their `index.css`, drawn\n * once here instead.\n *\n * `orientation` says which way the region scrolls; a table that is wider\n * than its box scrolls `horizontal`, a list `vertical` (the default), a board\n * `both`. A vertical region keeps its content no wider than itself, so a\n * long label still truncates.\n */\nexport interface ScrollAreaProps {\n orientation?: 'vertical' | 'horizontal' | 'both'\n /** On the region: its size and placement. */\n className?: string\n /** On the viewport, the box that scrolls: a `max-h-*` cap. */\n viewportClassName?: string\n /** On the content, the box the children sit in: padding, gap, layout. */\n contentClassName?: string\n /**\n * The scrolling box itself, for a region that reads or drives its own\n * scrolling: `ref.current.scrollTop`, `scrollTo`, `scrollHeight`.\n *\n * A conversation is the case this exists for (ADOPTION B20): it arrives at\n * the newest message, keeps its place when older ones load above, and jumps\n * to the bottom when a reply is sent \u2014 none of which the region can do for\n * the caller, and all of which the caller cannot do without the box.\n */\n viewportRef?: Ref<HTMLDivElement>\n /** Fires as the viewport scrolls \u2014 the unread policy's input, beside `viewportRef`. */\n onScroll?: UIEventHandler<HTMLDivElement>\n children: ReactNode\n}\n\nconst BAR = 'flex touch-none select-none rounded-full opacity-0 transition-opacity delay-300 data-[hovering]:opacity-100 data-[hovering]:delay-0 data-[scrolling]:opacity-100 data-[scrolling]:delay-0'\nconst THUMB = 'rounded-full bg-border-strong'\n\nexport function ScrollArea({ orientation = 'vertical', className, viewportClassName, contentClassName, viewportRef, onScroll, children }: ScrollAreaProps) {\n const vertical = orientation !== 'horizontal'\n const horizontal = orientation !== 'vertical'\n return (\n <BaseScrollArea.Root className={cn('relative min-h-0 min-w-0', className)}>\n {/* `overscroll-contain`, per axis and only while that axis has more to\n show: a list that reaches its end does not hand the wheel to the page\n behind it \u2014 the rule every popup here has kept since Peek \u2014 but a\n region with nothing to scroll one way passes that way's wheel through.\n Base UI's viewport is `overflow: scroll` on both axes whatever\n `orientation` says, so a sideways table is also a vertical scroll box\n with no room in it, and an unconditional `contain` stopped the page\n under every table and board (Katerina, 2026-09-09: 0px of page scroll\n with the pointer over Ship's table, 446px without the line; PLAN\n Finding 40). `data-has-overflow-x` / `-y` are Base UI's word for\n \"this axis really overflows\", written on the viewport as it changes. */}\n <BaseScrollArea.Viewport\n ref={viewportRef}\n onScroll={onScroll}\n className={cn(\n 'h-full w-full outline-none data-[has-overflow-x]:overscroll-x-contain data-[has-overflow-y]:overscroll-y-contain',\n viewportClassName,\n )}\n >\n {/* Base UI gives the content box `min-width: fit-content`, which is\n right when the region scrolls sideways and wrong when it does not:\n a row's `truncate` needs a box no wider than the viewport. */}\n <BaseScrollArea.Content className={contentClassName} style={horizontal ? undefined : { minWidth: 0 }}>\n {children}\n </BaseScrollArea.Content>\n </BaseScrollArea.Viewport>\n {vertical && (\n <BaseScrollArea.Scrollbar orientation=\"vertical\" className={cn(BAR, 'w-1.5 justify-center py-0.5 pr-0.5')}>\n <BaseScrollArea.Thumb className={cn(THUMB, 'w-full')} />\n </BaseScrollArea.Scrollbar>\n )}\n {horizontal && (\n <BaseScrollArea.Scrollbar orientation=\"horizontal\" className={cn(BAR, 'h-1.5 flex-col justify-center px-0.5 pb-0.5')}>\n <BaseScrollArea.Thumb className={cn(THUMB, 'h-full')} />\n </BaseScrollArea.Scrollbar>\n )}\n {vertical && horizontal && <BaseScrollArea.Corner />}\n </BaseScrollArea.Root>\n )\n}\n", "import type { ReactNode } from 'react'\nimport { cn } from './cn'\n\n/**\n * The 52px top bar, in two manners:\n *\n * - `solid` \u2014 in flow: a hairline under it, the surface behind it, the\n * frame's first row (2026-09-02, verbatim from the structured app; its\n * product title moved to the caller).\n * - `floating` \u2014 an overlay: absolute over the content, no border and no\n * background, and the bar itself ignores the pointer \u2014 only its clusters\n * catch clicks, so the content underneath stays reachable (2026-09-02,\n * verbatim from the floating app).\n *\n * Geometry both apps agreed on before extraction: 52px tall, `pl-5\n * pr-[26px]`, right cluster `gap-[6px]`. The left region is two slots\n * side by side \u2014 `menu` (the menu button, in an app whose navigation\n * collapses) and `logo` (the mark or name beside it; Katerina,\n * 2026-09-02). This component does not know who you are or what is being\n * searched; it only knows where those go.\n */\nexport interface TopBarProps {\n variant?: 'solid' | 'floating'\n /** Far left: the menu button, in an app whose navigation collapses. Stays the caller's \u2014 the bar only places it. */\n menu?: ReactNode\n /** Beside the menu button: the app's logo mark, or its name as text. */\n logo?: ReactNode\n /** The centre: a search field, or a launcher affordance \u2014 or nothing; the slot holds its place. */\n search?: ReactNode\n /** The right cluster \u2014 an `IdentityMenu` and its neighbours. */\n right?: ReactNode\n className?: string\n}\n\nexport function TopBar({ variant = 'solid', menu, logo, search, right, className }: TopBarProps) {\n const floating = variant === 'floating'\n return (\n <header\n className={cn(\n 'flex h-[52px] items-center pl-5 pr-[26px]',\n floating\n ? 'pointer-events-none absolute left-0 right-0 top-0 z-10'\n : 'shrink-0 gap-4 border-b border-border-default bg-bg-surface',\n className,\n )}\n >\n {(menu || logo) && (\n <div className={cn('flex shrink-0 items-center gap-2', floating && 'pointer-events-auto')}>\n {menu}\n {logo && <div className=\"flex items-center text-body-2-strong text-text-primary\">{logo}</div>}\n </div>\n )}\n <div className={cn('flex min-w-0 flex-1 items-center justify-center', floating && 'pointer-events-auto')}>{search}</div>\n <div className={cn('flex shrink-0 items-center gap-[6px]', floating && 'pointer-events-auto')}>{right}</div>\n </header>\n )\n}\n", "import type { ReactNode } from 'react'\nimport { cn } from './cn'\nimport { ScrollArea } from './ScrollArea'\nimport { TopBar } from './TopBar'\n\n/**\n * The frame of an app, in the two manners that pair with the TopBar's\n * (Katerina, 2026-09-02: the two shells go together \u2014 the solid bar with\n * the sidebar, the floating bar with the rail):\n *\n * - `solid` \u2014 the structured frame: a solid TopBar, the navigation\n * column, the content beside it, sidebar and main scrolling\n * independently (2026-09-02, verbatim from the structured app).\n * - `floating` \u2014 the floating frame: the TopBar floats over the top\n * edge, the rail stands on the app background, and the content lives\n * in the rounded card beside it (2026-09-02, the floating app's frame\n * geometry; its collapse animation and launcher stay in the app).\n *\n * The banner belongs to the content area (her ruling, 2026-09-02): it\n * renders at the top of the main column \u2014 inside the card, in the\n * floating manner \u2014 never across the navigation.\n *\n * Layout only. What goes in each region is the caller's; this component\n * has no data and no routes.\n */\nexport interface AppShellProps {\n variant?: 'solid' | 'floating'\n /** The top bar's menu button, in an app whose navigation collapses. */\n menu?: ReactNode\n /** The top bar's left: the app's logo mark, or its name as text. */\n logo?: ReactNode\n /** The top bar's centre \u2014 a search field, when there is one. */\n search?: ReactNode\n /** The top bar's right cluster \u2014 an `IdentityMenu`. */\n identity?: ReactNode\n /** A `Banner`, or nothing \u2014 drawn at the top of the content area. */\n banner?: ReactNode\n /** The navigation: a `Sidebar` in the solid manner, a `Rail` in the floating one. */\n nav: ReactNode\n children: ReactNode\n}\n\nexport function AppShell({ variant = 'solid', menu, logo, search, identity, banner, nav, children }: AppShellProps) {\n const bar = <TopBar variant={variant} menu={menu} logo={logo} search={search} right={identity} />\n\n if (variant === 'floating') {\n return (\n <div className=\"relative h-full min-h-0 overflow-hidden bg-bg-base\">\n {bar}\n <div className=\"flex h-full pb-4 pr-4 pt-[52px]\">\n {nav}\n <div\n className={cn(\n 'flex min-w-0 flex-1 flex-col overflow-hidden rounded-2xl bg-bg-surface',\n 'signal:border signal:border-border-subtle signal:shadow-highlight-inset',\n )}\n >\n {banner}\n <main className=\"flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden\">{children}</main>\n </div>\n </div>\n </div>\n )\n }\n\n return (\n /* `relative overflow-hidden` is the seal the floating manner already has\n (and the ScrollArea's own root is `relative`, so an absolutely placed\n stray inside a page now belongs to the region and scrolls with it),\n and it takes both halves: an absolutely positioned descendant with no\n positioned ancestor belongs to the *viewport*, so a scroll container\n never clips it and the document itself gains its position as scroll\n range \u2014 the whole page scrolls, navigation and top bar included.\n `relative` claims such strays for the shell; `overflow-hidden` clips\n them at its edge. Either alone seals nothing. */\n <div className=\"relative flex h-full min-h-0 flex-col overflow-hidden bg-bg-base text-text-primary\">\n {bar}\n <div className=\"flex min-h-0 flex-1\">\n {nav}\n <div className=\"flex min-h-0 min-w-0 flex-1 flex-col\">\n {banner}\n {/* The frame owns the page's scrollbar (Katerina, 2026-09-09: the\n Issues page still had a native bar \u2014 D40 said everywhere, and the\n one place every page passes through had been left out). A\n `ScrollArea` here means no page can forget it. The content box is\n at least the viewport's height and grows with a tall page, so Base\n UI sees its size change (the morning's phantom bar was a content\n box it could not watch); `main` fills it as a flex column. The\n page contract (the AppShell page says it): a page is a flex\n child of `main` \u2014 `flex-1` to fill and scroll here; `flex-1\n min-h-0 [contain:size]` to take exactly the frame's height and\n scroll inside itself, as Ship's detail grids do \u2014 and the outer\n region, with nothing to scroll, passes the wheel through. Never\n `h-full`: a box that is *at least* the viewport's height gives a\n percentage nothing to resolve against \u2014 measured, all four Ship\n pages, 2026-09-09. */}\n <ScrollArea className=\"min-h-0 flex-1\" contentClassName=\"flex min-h-full flex-col\">\n <main className=\"flex min-w-0 flex-1 flex-col\">{children}</main>\n </ScrollArea>\n </div>\n </div>\n </div>\n )\n}\n", "import { useLayoutEffect, useRef, useState, type ComponentPropsWithRef, type FC } from 'react'\nimport { Button as BaseButton } from '@base-ui/react/button'\nimport {\n IconAlertCircle,\n IconAlertTriangle,\n IconDownload,\n IconFile,\n IconFileTypeCsv,\n IconFileTypeDocx,\n IconFileTypePdf,\n IconFileTypePpt,\n IconFileTypeTxt,\n IconFileTypeXls,\n IconFileTypeZip,\n IconJson,\n IconLoader2,\n IconMarkdown,\n IconX,\n} from '@tabler/icons-react'\nimport { Card } from './Card'\nimport { IconButton } from './IconButton'\nimport { Link } from './Link'\nimport { WithTooltip } from './Tooltip'\nimport { cn } from './cn'\n\n/**\n * Something attached \u2014 a document or an image \u2014 drawn as a card.\n *\n * Peek's two attachment components, class for class (UIG-27, Katerina's ruling\n * of 14 September that both apps draw one): `FileAttachmentCard`, the card on\n * something already posted, and `PendingAttachmentChip`, the card in a composer\n * waiting to go. What stays in each app is what only the app can do: fetch the\n * bytes with the reader's own permission, open an image full screen, save a\n * file. The card is told the result \u2014 a `src`, an `href`, a `state` \u2014 and hands\n * a click back through `onOpen`, `onDownload` and `onRemove`.\n *\n * Two shapes, decided by the file: an **image** with a picture is a 180px\n * thumbnail you can open; anything else is a 240px **row** \u2014 a type tile, the\n * name, and what it is. Waiting to be sent (`pending`) it is a 200px row with a\n * remove control on its corner.\n *\n * One change from Peek, by ruling: a file that could not be read has a dashed\n * hairline, as every card that cannot be read does.\n */\nexport type AttachmentCardState = 'ready' | 'loading' | 'unreadable' | 'uploading' | 'failed' | 'warning'\n\nconst ICON_BY_EXTENSION: Record<string, FC<{ size?: number; stroke?: number; className?: string }>> = {\n pdf: IconFileTypePdf,\n doc: IconFileTypeDocx,\n docx: IconFileTypeDocx,\n xls: IconFileTypeXls,\n xlsx: IconFileTypeXls,\n ppt: IconFileTypePpt,\n pptx: IconFileTypePpt,\n csv: IconFileTypeCsv,\n txt: IconFileTypeTxt,\n rtf: IconFileTypeTxt,\n zip: IconFileTypeZip,\n json: IconJson,\n md: IconMarkdown,\n}\n\nconst IMAGE_EXTENSIONS = new Set(['png', 'jpg', 'jpeg', 'gif', 'webp', 'heic', 'heif', 'svg'])\n\nconst extensionOf = (name: string) => {\n const dot = name.lastIndexOf('.')\n return dot >= 0 ? name.slice(dot + 1).toLowerCase() : ''\n}\n\nconst isImage = (name: string, contentType?: string) => IMAGE_EXTENSIONS.has(extensionOf(name)) || (contentType?.startsWith('image/') ?? false)\n\n/** `'2.4 MB'`, `'812 KB'`, `'340 bytes'`. */\nconst formatBytes = (bytes: number) => {\n if (bytes < 1024) return `${bytes} bytes`\n const kb = bytes / 1024\n if (kb < 1024) return `${Math.round(kb)} KB`\n return `${(kb / 1024).toFixed(1)} MB`\n}\n\n/** `'PDF'`, `'ZIP'`, `'DOCX'`, `'FILE'`. */\nconst typeLabelOf = (name: string) => extensionOf(name).toUpperCase() || 'FILE'\n\nconst TILE_CLASSES = 'size-9 rounded-md bg-bg-active flex items-center justify-center shrink-0 text-text-secondary'\nconst NAME_CLASSES = 'text-caption font-medium text-text-primary truncate'\nconst NOTE_CLASSES = 'text-small tracking-wide leading-tight truncate'\n\nfunction TypeIcon({ name }: { name: string }) {\n const Icon = ICON_BY_EXTENSION[extensionOf(name)] ?? IconFile\n return <Icon size={20} stroke={1.5} />\n}\n\n/**\n * A line that truncates, with its full words on hover only when they are cut off \u2014 a name that fits, or a size,\n * shows nothing extra (Katerina, 2026-09-14; Breadcrumb's rule, 2026-09-01). `hint`, when given, is always on hover:\n * it says more than the line, not the same words again.\n *\n * Measured like Breadcrumb's crumbs: on mount, when the line resizes, and when the fonts arrive \u2014 a name set in\n * the fallback face can fit and then not, in a line whose box never changes. The wrapper is `flex-col` so the\n * text stretches across it, as wide as it is without one.\n */\nfunction Truncating({ text, hint, className, wrapperClassName }: { text: string; hint?: string; className: string; wrapperClassName?: string }) {\n const ref = useRef<HTMLSpanElement>(null)\n const [cut, setCut] = useState(false)\n useLayoutEffect(() => {\n const el = ref.current\n if (!el) return\n let live = true\n const measure = () => {\n if (live) setCut(el.scrollWidth > el.clientWidth)\n }\n measure()\n void document.fonts?.ready.then(measure)\n // jsdom has neither layout nor ResizeObserver; without this guard an app cannot render a card in its tests.\n if (typeof ResizeObserver === 'undefined') return () => void (live = false)\n const observer = new ResizeObserver(measure)\n observer.observe(el)\n return () => {\n live = false\n observer.disconnect()\n }\n // `cut` re-runs it: wrapping the line mounts a new element, and that one is the one to watch.\n }, [text, cut])\n const line = (\n <span ref={ref} className={className}>\n {text}\n </span>\n )\n if (!hint && !cut) return line\n return (\n <WithTooltip label={hint ?? text} wrapperClassName={cn('min-w-0 flex-col', wrapperClassName)}>\n {line}\n </WithTooltip>\n )\n}\n\nexport interface AttachmentCardProps extends Omit<ComponentPropsWithRef<'div'>, 'children'> {\n /** The name it was attached under, extension included: it decides the icon and the type label. */\n name: string\n /** In bytes. */\n size?: number\n /** Its MIME type, where known: an `image/\u2026` type is an image whatever its name. */\n contentType?: string\n /** Where a document opens, in a new tab. Without it the row is dimmed and opens nothing. */\n href?: string\n /** The picture an image card draws. Without it an image is drawn as a row. */\n src?: string\n /** What a click on an image's picture does \u2014 the app's full-screen view. */\n onOpen?: () => void\n /** Shows a download control on hover or focus, and calls this. */\n onDownload?: () => void\n /** Waiting to be sent: the composer's 200px row, with a remove control on its corner. */\n pending?: boolean\n /** Removes a pending card. */\n onRemove?: () => void\n /**\n * `ready` \u00B7 `loading` (an image on its way) \u00B7 `unreadable` (it could not be read) \u00B7\n * `uploading`, `failed`, `warning` (while pending). Default `ready`.\n */\n state?: AttachmentCardState\n /** The line under the name, in place of its type and size \u2014 why it failed, or what the warning is. */\n note?: string\n /** The note's full words, on hover, where the line is short. */\n noteHint?: string\n}\n\nexport function AttachmentCard({\n name,\n size,\n contentType,\n href,\n src,\n onOpen,\n onDownload,\n pending = false,\n onRemove,\n state = 'ready',\n note,\n noteHint,\n className,\n ...props\n}: AttachmentCardProps) {\n const image = isImage(name, contentType)\n const sizeText = size === undefined ? '' : formatBytes(size)\n\n if (pending) {\n const failed = state === 'failed'\n const warning = state === 'warning'\n return (\n <Card\n fill=\"elevated\"\n className={cn('group relative flex items-center gap-2 w-[200px] p-1.5 pr-3', failed ? 'border-error-default' : warning && 'border-warning-default', className)}\n {...props}\n >\n <div className={cn(TILE_CLASSES, 'overflow-hidden')}>\n {state === 'uploading' ? (\n <IconLoader2 size={16} stroke={1.5} className=\"animate-spin\" />\n ) : failed ? (\n <IconAlertCircle size={16} stroke={1.5} className=\"text-error-default\" />\n ) : warning ? (\n <IconAlertTriangle size={16} stroke={1.5} className=\"text-warning-default\" />\n ) : image && src ? (\n <img src={src} alt={name} className=\"size-full object-cover\" />\n ) : (\n <span className=\"text-menu font-semibold\">{typeLabelOf(name)}</span>\n )}\n </div>\n <div className=\"flex flex-col gap-[1px] min-w-0\">\n <Truncating text={name} className={NAME_CLASSES} />\n <Truncating\n text={(failed || warning ? note : state === 'uploading' ? (note ?? 'Uploading\u2026') : (note ?? sizeText)) ?? ''}\n hint={noteHint}\n className={cn(NOTE_CLASSES, failed ? 'text-error-default' : warning ? 'text-warning-default' : 'text-text-secondary')}\n />\n </div>\n {/* On Base UI's Button, as InputChip's \u2715 is (Katerina, 2026-09-14): IconButton is a 24px square\n that fills on hover, and this is Peek's 20px round badge on the card's corner. */}\n {onRemove && (\n <BaseButton\n type=\"button\"\n aria-label={`Remove ${name}`}\n className=\"absolute -top-1.5 -right-1.5 size-5 rounded-full bg-bg-elevated border border-border-strong flex items-center justify-center text-text-secondary hover:text-text-primary opacity-0 group-hover:opacity-100 transition-opacity\"\n onClick={(event) => {\n event.stopPropagation()\n onRemove()\n }}\n >\n <IconX size={11} stroke={1.75} />\n </BaseButton>\n )}\n </Card>\n )\n }\n\n // Fades in with the card's hover or keyboard focus, and keeps its slot when hidden, so revealing it never shifts the row.\n const download = onDownload ? (\n <IconButton\n variant=\"muted\"\n tooltip=\"Download\"\n aria-label={`Download ${name}`}\n className=\"opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 focus-visible:opacity-100 transition-opacity\"\n onClick={(event) => {\n event.stopPropagation()\n onDownload()\n }}\n >\n <IconDownload size={16} stroke={1.5} />\n </IconButton>\n ) : null\n\n if (state === 'loading') {\n return (\n // `role=\"status\"`: a plain box may not carry a name (axe, aria-prohibited-attr), and a\n // status says what it is doing \u2014 loading \u2014 to a reader that cannot see the pulse.\n <Card role=\"status\" fill=\"inset\" aria-busy=\"true\" aria-label={`Loading ${name}`} className={cn('w-[180px] h-28 animate-pulse', className)} {...props}>\n {null}\n </Card>\n )\n }\n\n if (state === 'unreadable') {\n return (\n <Card fill=\"inset\" unreadable className={cn('flex items-center gap-2 w-[240px] p-1.5 opacity-70', className)} {...props}>\n <div className={TILE_CLASSES}>\n <TypeIcon name={name} />\n </div>\n <div className=\"flex flex-col gap-[1px] min-w-0 text-left\">\n <Truncating text={name} className={NAME_CLASSES} />\n <span className={cn(NOTE_CLASSES, 'text-text-secondary')}>{note ?? 'Could not be loaded'}</span>\n </div>\n </Card>\n )\n }\n\n if (image && src) {\n const picture = <img src={src} alt={name} className=\"w-full h-28 object-cover\" />\n return (\n <Card fill=\"inset\" hover=\"hairline\" className={cn('group relative flex flex-col w-[180px] overflow-hidden', className)} {...props}>\n {onOpen ? (\n <BaseButton\n type=\"button\"\n className=\"block w-full\"\n aria-label={`Preview ${name}`}\n onClick={(event) => {\n event.stopPropagation()\n onOpen()\n }}\n >\n {picture}\n </BaseButton>\n ) : (\n <div className=\"block w-full\">{picture}</div>\n )}\n <div className=\"flex items-center gap-1 pl-2 pr-1 py-1 min-w-0\">\n <Truncating text={name} className=\"flex-1 text-caption text-text-primary truncate\" wrapperClassName=\"flex-1\" />\n {download}\n </div>\n </Card>\n )\n }\n\n const body = (\n <>\n <div className={TILE_CLASSES}>\n <TypeIcon name={name} />\n </div>\n <div className=\"flex flex-col gap-[1px] min-w-0 text-left\">\n <Truncating text={name} className={NAME_CLASSES} />\n <span className={cn(NOTE_CLASSES, 'text-text-secondary')}>{note ?? `${typeLabelOf(name)} \u00B7 ${sizeText}`}</span>\n </div>\n </>\n )\n return (\n <Card fill=\"inset\" hover={href ? 'hairline' : 'none'} className={cn('group flex items-center gap-2 w-[240px] p-1.5 pr-1', !href && 'opacity-70', className)} {...props}>\n {href ? (\n <Link href={href} external variant=\"plain\" className=\"flex items-center gap-2 min-w-0 flex-1 cursor-pointer\" onClick={(event) => event.stopPropagation()}>\n {body}\n </Link>\n ) : (\n <div className=\"flex items-center gap-2 min-w-0 flex-1\">{body}</div>\n )}\n {download}\n </Card>\n )\n}\n", "import type { ComponentPropsWithRef, ReactNode } from 'react'\nimport { cn } from './cn'\n\n/**\n * A link: an anchor with one of four looks, and nothing else of its own.\n *\n * It exists because both apps hand-wrote every link they have \u2014 18 raw `<a>`\n * on 13 September 2026, in three jobs (UIG-27). The looks are the ones those\n * links already had, reduced by Katerina's ruling:\n *\n * - `text` \u2014 a link inside written text: the info colour, always underlined,\n * dimming on hover. The look a link in a body of text already had.\n * - `quiet` \u2014 a title or a timestamp that is also a link. It takes the colour\n * and size of the text it sits in and underlines on hover. Two looks that\n * differed only in their text became this one (her ruling, 13 September).\n * - `underlined` \u2014 a short \"open it elsewhere\" beside a note. It takes the\n * note's colour, is always underlined, and brightens on hover. A dotted\n * and a solid version became this one, solid (her ruling, 13 September).\n * - `plain` \u2014 no look at all: a card or a row that is a link draws itself.\n *\n * **Navigation is the app's.** The package cannot know a router, so like\n * `NavItem` it renders a real anchor and passes every anchor prop through: a\n * router app hands in `onClick`, prevents the default and navigates, and the\n * `href` stays a real address for a modified click or a new tab.\n */\nexport type LinkVariant = 'text' | 'quiet' | 'underlined' | 'plain'\n\nconst VARIANT_CLASSES: Record<LinkVariant, string> = {\n text: 'text-info-default underline underline-offset-2 hover:opacity-80',\n quiet: 'hover:underline',\n underlined: 'underline underline-offset-2 hover:text-text-primary',\n plain: '',\n}\n\nexport interface LinkProps extends ComponentPropsWithRef<'a'> {\n href: string\n /** Default `text`. */\n variant?: LinkVariant\n /**\n * Another site, or another app: opens in a new tab, with `noopener\n * noreferrer` so the page it opens cannot reach back into this one.\n */\n external?: boolean\n children: ReactNode\n}\n\nexport function Link({ href, variant = 'text', external = false, className, children, ...props }: LinkProps) {\n return (\n <a\n href={href}\n {...(external ? { target: '_blank', rel: 'noopener noreferrer' } : {})}\n className={cn(VARIANT_CLASSES[variant], className)}\n {...props}\n >\n {children}\n </a>\n )\n}\n", "import type { ComponentPropsWithRef, ReactNode } from 'react'\nimport { cn } from './cn'\nimport { Link } from './Link'\n\n/**\n * A box that stands for one thing \u2014 a conversation, a project, a file, an\n * object from another app \u2014 drawn as its frame: 8px corners, a fill, a\n * hairline. What goes inside, and the space around it, is the caller's.\n *\n * Both apps drew their cards by hand: 18 of them, sorted by Katerina from\n * photographs on 14 September (UIG-27, docs/GATES.md \u00A70). Her rulings:\n *\n * - **The fill is chosen by what the card sits on**: `surface` on the page,\n * `elevated` on something already filled, `inset` inside a message, `none`\n * for a hairline alone.\n * - **The hairline follows the fill**: the default hairline on `surface` and\n * `elevated`, the subtle one on `inset` and `none`. Every card has 8px\n * corners.\n * - **A card you can pick answers the pointer.** `hairline`: the hairline one\n * step stronger \u2014 what a card that is a link does. `fill`: the card lights\n * up and its hairline shows \u2014 what a card in a feed does, where the light\n * says which one you are on.\n * - **A card that cannot be read has a dashed hairline**, in both apps.\n *\n * No padding of its own: the cards measured 0 to 12px, and a card with none\n * was one whose content pads itself.\n */\nexport type CardFill = 'surface' | 'elevated' | 'inset' | 'none'\nexport type CardHover = 'none' | 'hairline' | 'fill'\nexport type CardAttention = 'accent' | 'warning'\n\nconst FILL_CLASSES: Record<CardFill, string> = {\n surface: 'bg-bg-surface border-border-default',\n elevated: 'bg-bg-elevated border-border-default',\n inset: 'bg-bg-inset border-border-subtle',\n none: 'border-border-subtle',\n}\n\n/** One step stronger than the fill's own hairline. */\nconst HAIRLINE_HOVER_CLASSES: Record<CardFill, string> = {\n surface: 'hover:border-border-strong',\n elevated: 'hover:border-border-strong',\n inset: 'hover:border-border-default',\n none: 'hover:border-border-default',\n}\n\n/** The same step, held by `hovered` rather than by the pointer. */\nconst HAIRLINE_HELD_CLASSES: Record<CardFill, string> = {\n surface: 'border-border-strong',\n elevated: 'border-border-strong',\n inset: 'border-border-default',\n none: 'border-border-default',\n}\n\nconst ATTENTION_CLASSES: Record<CardAttention, string> = {\n accent: 'border-accent-muted hover:border-accent-muted',\n warning: 'border-warning-muted hover:border-warning-muted',\n}\n\nexport interface CardProps extends ComponentPropsWithRef<'div'> {\n /** What the card sits on. Default `surface`. */\n fill?: CardFill\n /** The whole card is a link: an anchor, and its hairline answers the pointer. Navigation stays the app's, as with `Link`. */\n href?: string\n /** How it answers the pointer. Default `hairline` with an `href`, `none` without. */\n hover?: CardHover\n /** No hairline until it is pointed at \u2014 a row in a feed that shows its edge only when you are on it. */\n quietUntilHover?: boolean\n /**\n * Draw the hover look now, whatever the pointer does. For a card whose own menu or picker is open: the menu\n * opens outside the card, so the pointer on it leaves the card, and the card would go dark under its own menu.\n * Ignored while `selected` or `active`, like the pointer.\n */\n hovered?: boolean\n /** The one you are on: the selected fill, a subtle hairline, and no hover. */\n selected?: boolean\n /** Being changed in place: the selected fill with the accent hairline, and no hover. */\n active?: boolean\n /** A hairline that asks to be looked at \u2014 something new (`accent`), or urgent (`warning`). */\n attention?: CardAttention\n /** It stands for something that could not be read: the hairline is dashed. */\n unreadable?: boolean\n children: ReactNode\n}\n\nexport function Card({\n fill = 'surface',\n href,\n hover = href ? 'hairline' : 'none',\n quietUntilHover = false,\n hovered = false,\n selected = false,\n active = false,\n attention,\n unreadable = false,\n className,\n children,\n ...props\n}: CardProps) {\n const still = selected || active\n const classes = cn(\n 'rounded-lg border',\n // A card that never changes has nothing to ease between, and the apps' still cards carry no transition.\n hover !== 'none' && 'transition-colors',\n FILL_CLASSES[fill],\n quietUntilHover && 'border-transparent',\n !still && hover === 'hairline' && HAIRLINE_HOVER_CLASSES[fill],\n !still && hover === 'fill' && 'hover:bg-bg-hover hover:border-border-default',\n !still && hovered && hover === 'hairline' && HAIRLINE_HELD_CLASSES[fill],\n !still && hovered && hover === 'fill' && 'bg-bg-hover border-border-default',\n // The one you are on can still be clicked (it opens again); the one being changed cannot.\n !active && hover !== 'none' && !href && props.onClick && 'cursor-pointer',\n selected && 'bg-bg-selected border-border-subtle',\n active && 'bg-bg-selected border-accent-primary',\n !active && attention && ATTENTION_CLASSES[attention],\n unreadable && 'border-dashed',\n href && 'block',\n className,\n )\n if (href !== undefined) {\n const { ref, ...anchorProps } = props as ComponentPropsWithRef<'a'>\n return (\n <Link href={href} variant=\"plain\" ref={ref} className={classes} {...anchorProps}>\n {children}\n </Link>\n )\n }\n return (\n <div className={classes} {...props}>\n {children}\n </div>\n )\n}\n", "import type { ComponentPropsWithRef, ReactNode } from 'react'\nimport { Button as BaseButton } from '@base-ui/react/button'\nimport { cn } from './cn'\nimport { TooltipTrigger } from './Tooltip'\n\n/**\n * Peek's IconButton (2026-08-28), verbatim: a square 4px-padded button\n * around a 16px icon, 8px radius, three variants, an optional tooltip that\n * portals above or below. Plus `type=\"button\"` by default (Ship's addition),\n * so it never submits a form by accident. On Base UI's Button since stage 2\n * of the migration (2026-09-07).\n *\n * `disabledReason` disables it, keeps it reachable by keyboard, and shows\n * the reason in place of the tooltip \u2014 see Button.\n */\nexport type IconButtonVariant = 'muted' | 'outlined' | 'primary'\n\n/** `ComponentPropsWithRef` so a `ref` reaches the element \u2014 see Button. */\nexport interface IconButtonProps extends ComponentPropsWithRef<'button'> {\n variant?: IconButtonVariant\n tooltip?: string\n /** A key hint drawn as the `Kbd` chip inside the tooltip \u2014 for a button\n * whose only other affordance is a keyboard shortcut. */\n tooltipShortcut?: string\n tooltipPlacement?: 'top' | 'bottom'\n /** Why the action cannot succeed right now. Disables the button, keeps it\n * reachable by keyboard, and shows the reason as the tooltip. */\n disabledReason?: string\n /** The icon: 16px, stroke 1.5. */\n children: ReactNode\n}\n\nexport function IconButton({\n variant = 'muted',\n className,\n children,\n disabled,\n disabledReason,\n tooltip,\n tooltipShortcut,\n tooltipPlacement,\n type = 'button',\n ...props\n}: IconButtonProps) {\n const button = (\n <BaseButton\n type={type}\n disabled={disabled || !!disabledReason}\n focusableWhenDisabled={!!disabledReason}\n className={(state) =>\n cn(\n // `shrink-0` stops a flex parent squashing the button; `self-center`\n // stops one stretching it. Two different failures, and both have\n // happened here: a row with no `items-*` drew this 24 wide and 228\n // tall in Peek's TopicMoreMenu story (Katerina, 2026-09-11). A button\n // is the size of its icon and its padding, whatever box it lands in.\n //\n // This is the one of the two that needs it: an IconButton states no\n // height, so its cross size is `auto` and a stretching parent takes\n // it. Measured in the same 260px row on 2026-09-12: 24px with this\n // class, 226px without. `Button` states `h-8`/`h-6`, so it cannot be\n // stretched and carries no `self-center` \u2014 see the note there.\n 'flex items-center justify-center p-1 rounded-lg transition-colors shrink-0 self-center cursor-pointer',\n !state.disabled && variant === 'primary' && 'bg-accent-primary hover:bg-accent-hover text-text-inverse',\n !state.disabled && variant === 'muted' && 'text-text-secondary hover:bg-bg-hover hover:text-text-primary',\n !state.disabled && variant === 'outlined' && 'border border-border-default hover:bg-bg-hover text-text-secondary',\n state.disabled && variant === 'primary' && 'bg-bg-disabled text-text-disabled',\n state.disabled && variant === 'muted' && 'text-text-disabled',\n state.disabled && variant === 'outlined' && 'border border-border-default text-text-disabled',\n // `pointer-events-none` only where the button is truly out of reach:\n // with a `disabledReason` the button IS the tooltip's trigger, and a\n // trigger the pointer cannot land on never opens one. Base UI already\n // swallows the click.\n state.disabled && !disabledReason && 'pointer-events-none',\n state.disabled && 'cursor-not-allowed',\n className,\n )\n }\n {...props}\n >\n {children}\n </BaseButton>\n )\n\n const label = disabledReason ?? tooltip\n if (label) {\n return (\n <TooltipTrigger label={label} shortcut={disabledReason ? undefined : tooltipShortcut} placement={tooltipPlacement}>\n {button}\n </TooltipTrigger>\n )\n }\n return button\n}\n", "import type { ComponentPropsWithRef, ReactElement, ReactNode } from 'react'\nimport { Tooltip as BaseTooltip } from '@base-ui/react/tooltip'\nimport { cn } from './cn'\nimport { Kbd } from './Kbd'\n\n/**\n * Peek's Tooltip and WithTooltip (2026-08-28) \u2014 the same 30px elevated pill,\n * on Base UI's `Tooltip` since stage 4 of the migration (2026-09-07).\n *\n * **What that fixed, and this page used to say it did not: it shows on\n * keyboard focus.** `WithTooltip` was hover-only, which meant the reason a\n * disabled control gives \u2014 the whole point of `disabledReason` \u2014 could not be\n * read without a mouse. Base UI opens it on `:focus-visible` too, so tabbing\n * to a control says what it is.\n *\n * Gone with it: the `createPortal`, the `getBoundingClientRect` arithmetic and\n * the two clamps this file kept against the viewport's edges. Floating UI\n * places and flips it now.\n */\n\n/**\n * How long the pointer must rest on a trigger before the tooltip appears\n * (Katerina, D23, 2026-09-07). It was instant, which flashed a pill per\n * button when sweeping a toolbar; 300ms is long enough to be a decision and\n * short enough not to feel like waiting.\n *\n * Inside a `TooltipProvider` the *neighbours* then open instantly, so a row\n * of icon buttons pauses once and reads freely after that.\n */\nconst OPEN_DELAY = 300\n\nexport interface TooltipProviderProps {\n /** Before the first tooltip in the group opens. Default 300ms (D23). */\n delay?: number\n /** Before one closes. Default: immediately, as it always has. */\n closeDelay?: number\n /** How long after one closes its neighbours still open instantly. */\n timeout?: number\n children: ReactNode\n}\n\n/**\n * Mount once at the top of an app: it makes every tooltip below it share one\n * delay, so the second and third open instantly while the group is warm.\n *\n * Without it each tooltip still waits its own 300ms \u2014 nothing breaks, the row\n * simply pauses on every button instead of once.\n */\nexport function TooltipProvider({ delay = OPEN_DELAY, closeDelay, timeout, children }: TooltipProviderProps) {\n return (\n <BaseTooltip.Provider delay={delay} closeDelay={closeDelay} timeout={timeout}>\n {children}\n </BaseTooltip.Provider>\n )\n}\n\n/** `ComponentPropsWithRef` because the pill is what `Tooltip.Popup` renders \u2014\n * Base UI merges its own props and ref into this element, so both must land. */\nexport interface TooltipProps extends Omit<ComponentPropsWithRef<'div'>, 'children'> {\n label: string\n /** The key that does the same thing, drawn as the `Kbd` chip after the label.\n * Pass it already formatted for the platform \u2014 this renders, it does not\n * decide whether the modifier is a glyph or a word. */\n shortcut?: string\n}\n\nexport function Tooltip({ label, shortcut, className, ...props }: TooltipProps) {\n return (\n <div role=\"tooltip\" className={cn('bg-bg-elevated border border-border-default rounded-lg h-[30px] flex items-center justify-center gap-1.5 px-2 shadow-lg', className)} {...props}>\n <span className=\"text-caption text-text-primary whitespace-nowrap\">{label}</span>\n {shortcut && <Kbd>{shortcut}</Kbd>}\n </div>\n )\n}\n\nexport interface WithTooltipProps {\n label: string\n /** Passed straight to the surface \u2014 see `TooltipProps.shortcut`. */\n shortcut?: string\n placement?: 'top' | 'bottom'\n /** Extra classes on the wrapper \u2014 e.g. `min-w-0 shrink` so a truncating label keeps truncating inside it. */\n wrapperClassName?: string\n children: ReactNode\n}\n\n/** The 6px between the trigger and the pill, and the 8px it keeps clear of\n * every viewport edge \u2014 the two numbers the deleted arithmetic used. */\nconst GAP = 6\nconst VIEWPORT_PAD = 8\n\n/**\n * The portalled surface: everything after the trigger, written once because\n * `WithTooltip` and the two buttons all need it.\n *\n * `pointer-events-none` and `disableHoverablePopup` together keep the pill\n * untouchable, as it has always been \u2014 a tooltip is not somewhere you can put\n * the pointer, and anything you must reach belongs in a Menu or a dialog.\n */\nfunction TooltipSurface({ label, shortcut, placement }: { label: string; shortcut?: string; placement: 'top' | 'bottom' }) {\n return (\n <BaseTooltip.Portal>\n <BaseTooltip.Positioner\n side={placement}\n align=\"center\"\n sideOffset={GAP}\n collisionPadding={VIEWPORT_PAD}\n // `fixed`, as the hand-written one was: the pill is placed against the\n // viewport, so a scroll container between it and the body cannot drag\n // it out of place.\n positionMethod=\"fixed\"\n className=\"pointer-events-none z-[9999]\"\n >\n <BaseTooltip.Popup\n render={\n <Tooltip\n label={label}\n shortcut={shortcut}\n /*\n * The motion (Katerina, D26, 2026-09-07). It had none \u2014 it was\n * mounted and instantly there \u2014 and Base UI is what makes one\n * possible: `data-starting-style` on the opening frame,\n * `data-ending-style` while it closes, and the popup is kept in\n * the DOM until the transition finishes, which is the half a\n * hand-written tooltip cannot do.\n *\n * It fades and travels 4px away from the trigger: from below\n * when it stands above the control (`data-side=top`), from above\n * when it hangs beneath it. 120ms in, 80ms out.\n *\n * `data-instant` is Base UI saying skip it \u2014 the pointer moved\n * to a neighbouring trigger inside the warm delay group, or the\n * tooltip was dismissed. Sweeping a toolbar therefore moves the\n * pill without re-animating it, which is the whole point of the\n * shared delay (D23).\n */\n className={cn(\n 'transition-[opacity,transform] duration-[120ms] ease-out motion-reduce:transition-none',\n 'data-[ending-style]:duration-[80ms] data-[instant]:duration-0',\n 'data-[starting-style]:opacity-0 data-[ending-style]:opacity-0',\n 'data-[side=top]:data-[starting-style]:translate-y-[4px] data-[side=top]:data-[ending-style]:translate-y-[4px]',\n 'data-[side=bottom]:data-[starting-style]:-translate-y-[4px] data-[side=bottom]:data-[ending-style]:-translate-y-[4px]',\n )}\n />\n }\n />\n </BaseTooltip.Positioner>\n </BaseTooltip.Portal>\n )\n}\n\nexport function WithTooltip({ label, shortcut, placement = 'top', wrapperClassName, children }: WithTooltipProps) {\n return (\n <BaseTooltip.Root disableHoverablePopup>\n {/* The wrapper stays the trigger: `WithTooltip` wraps whatever it is\n given \u2014 a raw button, a span, a Chip \u2014 and only an element of its own\n can carry the handlers for all of them. A control that IS the trigger\n (Button, IconButton) composes the part onto itself instead, which is\n what lets a `Dialog.Close` or a `Menu.Trigger` be one of those. */}\n <BaseTooltip.Trigger delay={OPEN_DELAY} render={<div className={cn('inline-flex shrink-0', wrapperClassName)} />}>\n {children}\n </BaseTooltip.Trigger>\n <TooltipSurface label={label} shortcut={shortcut} placement={placement} />\n </BaseTooltip.Root>\n )\n}\n\n/**\n * The tooltip a control wears itself, rather than through a wrapper.\n *\n * `Button` and `IconButton` call this: the Base UI button element becomes the\n * `Tooltip.Trigger`, so the component's root stays the `<button>`. That is\n * what stage 3 ran into and could not fix \u2014 an `IconButton` with a tooltip\n * returned the wrapper `<div>`, so a `Dialog.Close` composed onto the wrapper\n * and the \u2715 could never be the part.\n *\n * Internal: the two buttons' `tooltip` / `disabledReason` props are the API.\n */\nexport function TooltipTrigger({ label, shortcut, placement = 'top', children }: { label: string; shortcut?: string; placement?: 'top' | 'bottom'; children: ReactElement }) {\n return (\n <BaseTooltip.Root disableHoverablePopup>\n {/* `children` is the control itself, so it is what the part renders \u2014\n a `ReactElement`, not a `ReactNode`, because `render` takes one\n element and there is nothing sensible to do with two. */}\n <BaseTooltip.Trigger delay={OPEN_DELAY} render={children} />\n <TooltipSurface label={label} shortcut={shortcut} placement={placement} />\n </BaseTooltip.Root>\n )\n}\n", "import type { ReactNode } from 'react'\nimport { cn } from './cn'\n\n/**\n * A keyboard hint \u2014 the small chip that names the key which does the same thing.\n *\n * **The look is SearchInput's shortcut chip** (Katerina, 2026-09-05), which is\n * the one the apps already show: an inset pill with a hairline border, and under\n * Signal and Ship the keycap treatment \u2014 mono, 10px, a lighter fill and a thicker\n * bottom edge, so it reads as a key rather than a label.\n *\n * It existed before this file did, drawn inline inside `MenuItem` and again\n * inside `SearchInput`. Extracted when `Tooltip` needed a third copy.\n *\n * **Sizes are arbitrary values on purpose.** `text-caption` beside a\n * `text-{color}` is dropped by tailwind-merge, so the chip silently inherited\n * whatever size surrounded it \u2014 10px in a composer, something else elsewhere.\n * `text-[12px]` survives the merge.\n *\n * Content is whatever names the key \u2014 a trigger character (`/`, `@`), a chord\n * (`Cmd+B`, `Ctrl+Alt+1`), or a word (`Esc`). It does not format anything: a caller\n * that knows the platform passes the label it wants.\n */\nexport interface KbdProps {\n children: ReactNode\n className?: string\n}\n\nexport function Kbd({ children, className }: KbdProps) {\n return (\n <kbd\n className={cn(\n 'inline-flex shrink-0 items-center justify-center whitespace-nowrap rounded-sm border border-border-strong bg-bg-inset px-1 py-px font-sans text-caption text-text-secondary',\n 'signal:border-b-2 signal:pt-[2px] signal:pb-px signal:bg-bg-wash signal:font-mono signal:text-small',\n 'ship:border-b-2 ship:pt-[2px] ship:pb-px ship:bg-bg-wash ship:font-mono ship:text-small',\n className,\n )}\n >\n {children}\n </kbd>\n )\n}\n", "import { Avatar as BaseAvatar } from '@base-ui/react/avatar'\nimport { IconUserFilled } from '@tabler/icons-react'\nimport { cn } from './cn'\n\n/**\n * A person's face: their picture, or their initials on a colour that is\n * theirs, or \u2014 when there is no name to take initials from \u2014 a silhouette.\n *\n * Peek's Avatar (2026-08-28), made self-contained: Peek's version found the\n * picture itself, by name, from Peek's data (an upload in Convex, else the\n * demo portrait), and switched the initials on only under its Signal theme.\n * Neither belongs in a shared component, so this one takes the picture URL\n * from its caller and draws the initials in every theme. Peek keeps a\n * three-line wrapper that resolves the picture; Ship passes the relay's\n * `kind:0` picture straight in.\n *\n * On Base UI's `Avatar` since stage 6 of the migration (2026-09-14): Base UI\n * loads the picture and says when it has arrived or failed, and the initials\n * or the silhouette are its fallback. The hand-kept \"broken\" state is gone.\n *\n * Initials come from a *name*, never from a key: a component that accepted a\n * pubkey would eventually show one (Ship's ruling, 2026-08-24). An unnamed\n * person is the silhouette \u2014 `?` on a bright gradient read as an error badge\n * rather than a person (Peek, FEE-1).\n *\n * The hue is a fallback palette, not a token: it is per person, chosen\n * from the name so the same person is the same colour everywhere, and a\n * theme has no say in it. Peek's eight, verbatim.\n */\nconst HUES = ['#56c8ff', '#ff8f6b', '#4ade8c', '#b18cff', '#ffc94d', '#ff7eb0', '#7ea8ff', '#5fdfd6']\n\nexport const hueFor = (key: string) => {\n let h = 0\n for (let i = 0; i < key.length; i++) h = (h * 31 + key.charCodeAt(i)) | 0\n return HUES[Math.abs(h) % HUES.length]\n}\n\n/**\n * Peek's rule, sharpened: the first letter of the first two words \u2014 counting\n * only words that begin with a letter or digit, so \"Claude (steered by\n * Katerina Kelepouri)\" shows \"C\", never \"C(\" (Katerina, 2026-09-01: a name\n * carrying such symbols gets a single letter rather than one of them).\n */\nexport const initialsFor = (name: string) => {\n const words = name.split(/\\s+/).slice(0, 2).filter((w) => /^[\\p{L}\\p{N}]/u.test(w))\n const initials = words.map((w) => w[0]).join('') || name.trim().charAt(0)\n return initials.toUpperCase()\n}\n\nexport interface AvatarProps {\n /** The picture URL, resolved by the caller. Until it arrives, and if it fails, the initials show. */\n src?: string\n /** The person's name \u2014 where the initials and the colour come from. */\n name?: string\n /** Alt text for the picture; the name when absent. Also the initials' source when there is no name. */\n alt?: string\n /** Pixels. The scale: 16 \u00B7 24 \u00B7 32 \u00B7 36 (default). */\n size?: number\n /**\n * **Say the person's name out loud**, for a face that stands on its own \u2014\n * `PersonTrigger`'s compact shape, a stack of members.\n *\n * A face is silent by default, because almost every face in the suite sits\n * beside the name it belongs to: `Person`, a `MenuItem`'s `leading`, a row.\n * A picture that spoke there would double the name \u2014 measured 2026-09-08,\n * before this existed: a `Person` in a button announced **\"AD Ana Duarte\"**,\n * and with a picture **\"Ana Duarte Ana Duarte\"**. The initials are a drawing\n * of a name, not text, and a screen reader was reading them as text.\n */\n label?: string\n className?: string\n}\n\nexport function Avatar({ src, name, alt = '', size = 36, label: spoken, className }: AvatarProps) {\n const label = name || alt\n return (\n <BaseAvatar.Root\n // A div, as it was drawn: Base UI's default is a span.\n render={<div />}\n /* Named or silent, never half of either: with a `label` the tile is one\n image with one name, and everything inside it is that image's pixels;\n without one it is not in the accessibility tree at all. */\n {...(spoken ? { role: 'img', 'aria-label': spoken } : { 'aria-hidden': true })}\n className={cn('rounded-sm overflow-hidden shrink-0 bg-bg-inset', className)}\n style={{ width: size, height: size }}\n >\n {src && (\n <BaseAvatar.Image\n src={src}\n alt=\"\"\n /*\n `keepMounted`: the <img> is in the tile from the first render, as it\n always was, so a picture still loading leaves the tile empty rather\n than flashing initials and swapping them out, and an app's test that\n finds the <img> still finds it. The fallback is rendered after it,\n below the tile's hidden overflow, until the picture arrives (then it\n unmounts) or fails (then the picture is hidden and it moves up).\n */\n keepMounted\n className=\"w-full h-full object-cover data-[error]:hidden\"\n />\n )}\n {label ? (\n <BaseAvatar.Fallback\n render={<div />}\n /*\n `leading-none` is load-bearing (2026-09-03). Centring a flex child\n centres its LINE BOX, and a line box reserves room under the\n baseline for descenders \u2014 which capitals never use \u2014 so initials\n floated above the middle of every tile. It also inherited whatever\n line-height surrounded it, so the same face sat differently in a\n members pill and in a replies row. Measured over six letter pairs\n at 18/24/36px: mean 0.64px high before, 0.06px after.\n\n Per-letter variation stays (a \"Y\" carries its mass up top, a \"ZB\"\n more than an \"AJ\") \u2014 that is the letterform, not the box, and it\n is not something a rule here can flatten.\n */\n className=\"w-full h-full flex items-center justify-center font-semibold leading-none\"\n style={{\n /* eslint-disable no-restricted-syntax -- @estiva-escape: the per-person palette (the note at\n the top): eight hues picked from the name, the one ink that reads on all\n of them, and a size that follows `size`. None of it can be a token. */\n // The one ink colour that reads on all eight hues, which are a\n // palette rather than tokens (see the note at the top) \u2014 so its\n // ink cannot be a token either.\n color: '#08121c',\n fontSize: Math.round(size * 0.36),\n background: `linear-gradient(160deg, color-mix(in srgb, ${hueFor(label)} 92%, #fff) 0%, color-mix(in srgb, ${hueFor(label)} 70%, #0b0d11) 100%)`,\n /* eslint-enable no-restricted-syntax */\n }}\n >\n {/*\n D27 (ruled 2026-09-08, built 2026-09-14): what is centred is the\n capitals' own box, cap height to baseline, not the line box. The\n line box's ascent and descent are rounded to whole pixels before\n it is centred, which put the baseline up to 0.73px from where\n centred capitals put it (at 24px: 14.5 against 15.23); trimmed, it\n lands within 0.06px. On screen, averaged over eight sub-pixel\n positions, the ink leaned 0.66px high at 24px, 0.44px at 28px and\n 0.27px low at 22px; now every size averages within 0.07px of the\n middle. Any one face can still sit up to half a pixel off, because\n the screen rounds text to its pixel grid wherever the tile falls.\n A browser without `text-box` draws the old line box, as before.\n */}\n <span className=\"[text-box:trim-both_cap_alphabetic]\">{initialsFor(label)}</span>\n </BaseAvatar.Fallback>\n ) : (\n <BaseAvatar.Fallback render={<div />} className=\"w-full h-full bg-accent-muted flex items-center justify-center text-text-muted\">\n <IconUserFilled size={Math.round(size * 0.5)} />\n </BaseAvatar.Fallback>\n )}\n </BaseAvatar.Root>\n )\n}\n", "import { Avatar } from './Avatar'\n\n/**\n * Up to three overlapping 24px faces with a `bg-surface` ring \u2014 Peek's member\n * stack (2026-09-01), with one generalisation on the way in: a member is a\n * name plus an optional picture URL, resolved by the caller, exactly as\n * Avatar takes its `src`. Peek's version took bare names because its Avatar\n * wrapper resolves pictures itself; a consumer without such a wrapper could\n * never have shown one.\n *\n * The ring is drawn OUTSIDE the face, as a shadow (2026-09-03). It was a\n * `border` before, which box-sizing takes out of the inside: a 24px avatar\n * showed 20px of face, and at 18px only 14px \u2014 noticeably smaller than the\n * number asked for. A shadow costs the face nothing, so `size` means the\n * size. It replaced an even older wrapper whose `overflow-hidden` clipped\n * 4px of face and the right edge of the initials with it (\"CO\" as \"C(\") \u2014\n * hence the rule this file keeps: nothing that clips ever wraps the face.\n */\nexport interface AvatarGroupMember {\n name: string\n /** Resolved by the caller. Absent draws the initials. */\n picture?: string\n}\n\nexport interface AvatarGroupProps {\n members: AvatarGroupMember[]\n /**\n * Face size in pixels; the overlap follows it. 24 is the members pill,\n * 18 the replies row (Katerina, 2026-09-03) \u2014 that row drew its own stack\n * for two months for want of this one number, and drifted while it did.\n */\n size?: number\n}\n\nexport function AvatarGroup({ members, size = 24 }: AvatarGroupProps) {\n const visible = members.slice(0, 3)\n /*\n A third of the face, so a stack reads the same at any size \u2014 which is\n exactly the ratio both hand-drawn stacks had arrived at independently\n (8px on 24, 6px on 18). The container gives the last face its overlap\n back as padding, so the group measures its true width.\n */\n const overlap = Math.round(size / 3)\n // The ring scales with the face \u2014 2px at 24, 1.5px at 18, which is what both\n // hand-drawn stacks used before this component carried either of them.\n const ring = size / 12\n return (\n <div className=\"flex items-center\" style={{ paddingRight: overlap }}>\n {visible.map((member, i) => (\n /*\n The wrapper carries the overlap and the ring; the face carries\n neither. It must never clip \u2014 see the note at the top of the file \u2014\n so it has the avatar's own radius and no overflow of its own.\n */\n <span\n key={i}\n className=\"relative flex rounded-sm\"\n /* eslint-disable-next-line no-restricted-syntax -- @estiva-escape: the ring's width is the `ring` prop, so it cannot be a class; its colour is the surface token */\n style={{ marginRight: -overlap, boxShadow: `0 0 0 ${ring}px var(--bg-surface)` }}\n >\n {/* Each face says whose it is: a stack stands on its own, with no\n name beside it to borrow. It read as its initials before \u2014\n \"AD BC CD\" \u2014 which is the drawing, not the person. */}\n <Avatar size={size} name={member.name} src={member.picture} label={member.name} />\n </span>\n ))}\n </div>\n )\n}\n", "import { IconX } from '@tabler/icons-react'\nimport { cn } from './cn'\nimport { IconButton } from './IconButton'\n\n/**\n * The strip under the header where the app says something happened \u2014\n * one line, gone when there is nothing to say (Ship's Banner,\n * 2026-09-02, verbatim; `info` and `warning` added on extraction,\n * Katerina's ruling \u2014 the semantic set is four everywhere else).\n *\n * body-2, her ruling (2026-09-01): the caption this once claimed never\n * rendered, so 14px is what it has always been and what she kept.\n * An `error` announces itself (`role=\"alert\"`); the other tones are\n * polite (`role=\"status\"`).\n *\n * `onDismiss` adds an `\u2715` at the right-hand end (Katerina, D21,\n * 2026-09-07). Without it the strip is exactly what it was \u2014 a banner an\n * app removes by not rendering it. With it the row is 40px rather than\n * 36px, because the button is taller than the line of text.\n *\n * **A banner is not a toast.** This is the strip under the header, at the\n * app's full width, for something that stays true. Transient feedback is\n * `Toast`, and something that floats over the page and must not fade is a\n * `Toast` with `durationMs: 0` \u2014 which is what Peek's \"topic deleted\"\n * notice is (D21), not this.\n */\nexport type BannerTone = 'ok' | 'error' | 'info' | 'warning'\n\nexport interface BannerProps {\n tone: BannerTone\n children: string\n /** Adds an `\u2715` at the right-hand end. Absent: the app removes the banner. */\n onDismiss?: () => void\n /** The dismiss button's accessible name. Defaults to \"Dismiss\". */\n dismissLabel?: string\n className?: string\n}\n\nconst TONE_STYLES: Record<BannerTone, string> = {\n ok: 'bg-success-muted text-success-default',\n error: 'bg-error-muted text-error-default',\n info: 'bg-info-muted text-info-default',\n warning: 'bg-warning-muted text-warning-default',\n}\n\nexport function Banner({ tone, children, onDismiss, dismissLabel = 'Dismiss', className }: BannerProps) {\n const role = tone === 'error' ? 'alert' : 'status'\n // No dismiss, no row: every banner that predates the prop keeps the DOM and\n // the height it had.\n if (!onDismiss) {\n return (\n <div role={role} className={cn('px-4 py-2 text-body-2', TONE_STYLES[tone], className)}>\n {children}\n </div>\n )\n }\n return (\n <div role={role} className={cn('flex items-center gap-3 px-4 py-2 text-body-2', TONE_STYLES[tone], className)}>\n <span className=\"min-w-0 flex-1\">{children}</span>\n {/* `text-current` so the \u2715 takes the tone's colour rather than the\n muted grey an IconButton wears on a neutral surface. */}\n <IconButton aria-label={dismissLabel} onClick={onDismiss} className=\"-mr-1 shrink-0 text-current hover:text-current\">\n <IconX size={16} stroke={1.5} />\n </IconButton>\n </div>\n )\n}\n", "import type { ComponentPropsWithRef, ReactNode } from 'react'\nimport { Button as BaseButton } from '@base-ui/react/button'\nimport { cn } from './cn'\nimport { TooltipTrigger } from './Tooltip'\n\n/**\n * Peek's Button (2026-08-28), verbatim, plus what Ship added and Peek should\n * adopt: a `destructive` variant \u2014 the muted button in the error colour, for\n * \"Delete project\" and its kind; a destructive action is a button like any\n * other, not a dotted link \u2014 and `type=\"button\"` by default, so a button\n * inside a form submits it only when asked to. On Base UI's Button since\n * stage 2 of the migration (2026-09-07).\n *\n * Three variants and two sizes: 32px default / 24px small, 6px radius, 500\n * weight. The sizes are the `btn-default` and `btn-small` type tokens.\n *\n * The primary reads in `text-inverse` on the accent \u2014 a theme decides what\n * that is (dark on Peek's light accents, light on Ship's dark one). Under\n * Signal it is also semibold, as Peek has it.\n *\n * `disabledReason` is the package's own rule, \"only offer actions that can\n * succeed\", done once: the button is disabled, stays reachable by keyboard\n * (Base UI's `focusableWhenDisabled`), and shows the reason as a tooltip on\n * hover. Ship wrote that wrapper by hand six times.\n */\nexport type ButtonVariant = 'primary' | 'outlined' | 'muted' | 'destructive'\nexport type ButtonSize = 'default' | 'small'\n\n/**\n * `ComponentPropsWithRef`, not `ButtonHTMLAttributes`, so a `ref` reaches the\n * element. React 19 passes `ref` to a function component as an ordinary prop,\n * so nothing here forwards it by hand \u2014 it rides in on the spread below. What\n * was missing was only the *type* saying so, which is enough to stop a caller.\n *\n * It matters because a Base UI part composes through `render`: at stage 4 a\n * `Menu.Trigger` or a `Dialog.Close` **is** this Button rather than wrapping\n * one, and a trigger that cannot be measured or focused is not a trigger.\n */\nexport interface ButtonProps extends ComponentPropsWithRef<'button'> {\n variant?: ButtonVariant\n size?: ButtonSize\n /** 16px, stroke 1.5 on the default size; 14px on small. */\n leadingIcon?: ReactNode\n /** Why the action cannot succeed right now. Disables the button, keeps it\n * reachable by keyboard, and shows the reason as a tooltip on hover. */\n disabledReason?: string\n children: ReactNode\n}\n\nexport function Button({\n variant = 'muted',\n size = 'default',\n leadingIcon,\n className,\n children,\n disabled,\n disabledReason,\n type = 'button',\n ...props\n}: ButtonProps) {\n const hasLeadingIcon = !!leadingIcon\n const button = (\n <BaseButton\n type={type}\n disabled={disabled || !!disabledReason}\n focusableWhenDisabled={!!disabledReason}\n className={(state) =>\n cn(\n // No `self-center` here, deliberately \u2014 `IconButton` needs it and this\n // does not, and the difference is the `h-8` / `h-6` on the next two\n // lines. `align-items: stretch` only stretches a child whose cross\n // size is `auto`; a Button always states its height, so a stretching\n // parent cannot change it. Measured in Chrome on 2026-09-12, in the\n // 260px row of Finding 49: 32px with `self-center` and 32px without.\n // What the class DID do was override the caller on the other axis \u2014\n // in a column it beats the parent's `items-start`, which centred the\n // four actions in Ship's `ProjectRail` and the trigger in five of\n // this package's own story frames, each of which asks for the top.\n // A control does not get to decide where its caller puts it.\n 'inline-flex items-center justify-center gap-1 rounded-md transition-colors font-sans font-medium',\n size === 'default' && 'h-8 text-btn-default',\n size === 'small' && 'h-6 text-btn-small',\n // Extra right padding beside a leading icon, for optical balance.\n size === 'default' && (hasLeadingIcon ? 'pl-2 pr-3' : 'px-2'),\n size === 'small' && (hasLeadingIcon ? 'pl-1.5 pr-2' : 'px-1.5'),\n !state.disabled && variant === 'primary' && 'bg-accent-primary hover:bg-accent-hover text-text-inverse cursor-pointer signal:font-semibold',\n !state.disabled && variant === 'outlined' && 'border border-border-default hover:bg-bg-hover text-text-primary cursor-pointer',\n !state.disabled && variant === 'muted' && 'hover:bg-bg-hover text-text-primary cursor-pointer',\n !state.disabled && variant === 'destructive' && 'hover:bg-error-muted text-error-default cursor-pointer',\n state.disabled && 'bg-bg-disabled text-text-disabled',\n // `pointer-events-none` only where the button is truly out of reach.\n // With a `disabledReason` the button IS the tooltip's trigger, and a\n // trigger the pointer cannot land on never opens one \u2014 Base UI already\n // swallows the click (`focusableWhenDisabled` gives `aria-disabled`\n // and a prevented `onClick`), so nothing else needs it.\n state.disabled && !disabledReason && 'pointer-events-none',\n state.disabled && variant === 'outlined' && 'border border-border-default',\n className,\n )\n }\n {...props}\n >\n {leadingIcon}\n {children}\n </BaseButton>\n )\n return disabledReason ? <TooltipTrigger label={disabledReason}>{button}</TooltipTrigger> : button\n}\n", "import { Checkbox as BaseCheckbox } from '@base-ui/react/checkbox'\nimport { IconCheck } from '@tabler/icons-react'\nimport { cn } from './cn'\n\n/**\n * A 16px square that fills with the accent when checked \u2014 Peek's Checkbox\n * (2026-09-01), verbatim; on Base UI Checkbox since stage 1 of the migration\n * (2026-09-06).\n *\n * Controlled only; parents own the state. Two renderings, one class list:\n *\n * - With `onChange` it is the control, as Base UI renders it: a\n * `<span role=\"checkbox\">` with a hidden `<input>` beside it. Space\n * toggles, Enter does not, and its click never reaches the row around it.\n * - Without `onChange` it is a picture of the state, for a row that toggles\n * on its own click. It is hidden from assistive technology and takes no\n * focus, so the row is one control and says the state itself\n * (`aria-pressed`, or `aria-selected` on an option). Two focusable controls\n * nested in one another is what axe's `nested-interactive` rule rejects.\n *\n * The tick is always in the box, hidden when unchecked. A box that is empty\n * in one state and holds an icon in the other hangs on a line of text\n * differently in each, and moved 1px on every click (Katerina, 2026-09-07:\n * \"it should be fixed\"). With the tick always there, Base UI's `<span>` and\n * the `<button>` this used to be land on the same pixel, measured. In a flex\n * row nothing ever moved.\n */\nexport interface CheckboxProps {\n checked: boolean\n onChange?: (checked: boolean) => void\n disabled?: boolean\n 'aria-label'?: string\n /** Set by a `Field` with `required`; a caller inside one owes nothing. */\n 'aria-required'?: boolean | 'true' | 'false'\n className?: string\n}\n\nfunction squareClasses(checked: boolean, disabled: boolean, interactive: boolean, className?: string) {\n return cn(\n 'inline-flex items-center justify-center size-4 shrink-0 rounded-sm border transition-colors',\n checked ? 'bg-accent-primary border-accent-primary text-text-inverse' : 'bg-transparent border-border-strong hover:border-text-muted',\n disabled && 'opacity-50 pointer-events-none',\n interactive ? 'cursor-pointer' : 'pointer-events-none',\n className,\n )\n}\n\n/** `flex`, so the icon is a flex item and not an inline box with a line height of its own. */\nconst tickClasses = (checked: boolean) => cn('flex', !checked && 'invisible')\n\nexport function Checkbox({ checked, onChange, disabled = false, className, ...aria }: CheckboxProps) {\n if (!onChange) {\n return (\n <span aria-hidden=\"true\" className={squareClasses(checked, disabled, false, className)}>\n <span className={tickClasses(checked)}>\n <IconCheck size={12} stroke={3} />\n </span>\n </span>\n )\n }\n return (\n <BaseCheckbox.Root\n checked={checked}\n disabled={disabled}\n aria-label={aria['aria-label']}\n aria-required={aria['aria-required']}\n onCheckedChange={(next) => onChange(next)}\n onClick={(e) => e.stopPropagation()}\n className={(state) => squareClasses(state.checked, state.disabled, true, className)}\n >\n <BaseCheckbox.Indicator keepMounted className={(state) => tickClasses(state.checked)}>\n <IconCheck size={12} stroke={3} />\n </BaseCheckbox.Indicator>\n </BaseCheckbox.Root>\n )\n}\n", "import type { ReactNode } from 'react'\nimport { cn } from './cn'\n\n/**\n * Peek's Chip (2026-08-28), verbatim: a 20px pill in one of six colour\n * types, with room for a 12px icon either side. Under Signal the label is\n * mono and the coloured types carry a hairline, as Peek has it. The icon\n * slots centre their icon (Ship's addition \u2014 an icon beside text otherwise\n * rides on the baseline).\n *\n * The label is the `chip` type token, 11px / 500. It used to be spelled as a\n * plain class under a note that it must never be merged; `cn()` knows the ramp\n * now, so a token size survives beside a colour and the note is retired.\n */\nexport type ChipType = 'neutral' | 'brand' | 'info' | 'warning' | 'success' | 'error'\n\nexport interface ChipProps {\n type?: ChipType\n label?: string\n leadingIcon?: ReactNode\n trailingIcon?: ReactNode\n className?: string\n}\n\nconst typeStyles: Record<ChipType, string> = {\n neutral: 'bg-bg-inset text-text-primary',\n brand: 'bg-accent-muted text-accent-primary signal:border signal:border-accent-outline',\n info: 'bg-info-muted text-info-default signal:border signal:border-info-outline',\n warning: 'bg-warning-muted text-warning-default signal:border signal:border-warning-outline signal:shadow-glow-warning',\n success: 'bg-success-muted text-success-default signal:border signal:border-success-outline',\n error: 'bg-error-muted text-error-default signal:border signal:border-error-outline',\n}\n\nexport function Chip({ type = 'neutral', label, leadingIcon, trailingIcon, className }: ChipProps) {\n return (\n <div className={cn('inline-flex min-w-0 items-center justify-center gap-1.5 rounded-full max-h-[20px] px-2 py-1', typeStyles[type], className)}>\n {leadingIcon && <span className=\"flex size-3 shrink-0 items-center justify-center\">{leadingIcon}</span>}\n {label && (\n // A chip given a `max-w-*` cuts a long label with an ellipsis instead\n // of growing past it \u2014 Ship's project chip on an issue row\n // (2026-09-09). Clipped sideways only: `overflow-x-clip`, not\n // `truncate`, because `truncate` is `overflow: hidden` on both axes and\n // the label's line box (11px under Signal) is tighter than its glyphs \u2014\n // the screenshot diff caught every descender cut off. `clip` is the\n // one overflow that leaves the other axis visible. A chip with no cap\n // draws exactly as before.\n <span className=\"min-w-0 overflow-x-clip text-ellipsis whitespace-nowrap text-chip signal:font-mono signal:text-small signal:font-semibold signal:tracking-wide signal:tabular-nums\">{label}</span>\n )}\n {trailingIcon && <span className=\"flex size-3 shrink-0 items-center justify-center\">{trailingIcon}</span>}\n </div>\n )\n}\n", "import {\n createContext,\n useContext,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type KeyboardEvent,\n type ReactNode,\n type RefObject,\n} from 'react'\nimport { Dialog } from '@base-ui/react/dialog'\nimport { Autocomplete } from '@base-ui/react/autocomplete'\nimport type { BaseUIEvent } from '@base-ui/react/types'\nimport { IconLoader2, IconSearch } from '@tabler/icons-react'\nimport { cn } from './cn'\nimport { Button } from './Button'\nimport { InputChip } from './ChipInput'\nimport { EmptyState } from './EmptyState'\nimport { FieldLine } from './Field'\nimport { Kbd } from './Kbd'\nimport { EnterHint, MenuItemBody, menuItemClassName } from './Menu'\nimport { ScrollArea } from './ScrollArea'\nimport { SectionLabel } from './SectionLabel'\nimport { SkeletonBar } from './Skeleton'\n\n/**\n * A window that searches and runs things: a field, rows in groups, and a\n * footer naming the keys that work right now. Built the way Base UI's own\n * command palette example is \u2014 `Dialog` with an `Autocomplete` inside it,\n * the list drawn inline \u2014 which is migration decision D8, brought forward\n * by UIG-29 (Katerina, 15\u201316 September 2026).\n *\n * **Why not `DialogShell`.** A palette has no title bar, no close button and\n * no button row; its top is a field and its bottom is a key footer. Built on\n * the shell it would be the shell with every slot switched off and a second\n * card drawn inside it. `SearchInput` and `ChipInput` do not fit either: the\n * field here drives a list that is always open and never a popup.\n *\n * **What it owns, and what the caller owns** (Katerina, 16 September, P1 and\n * P2). Every key is this component's: the arrows and Enter are Base UI's,\n * and Tab to go in, Ctrl+Backspace to forget, Backspace to go back, Ctrl+Enter\n * to submit and where focus goes are written here, once, so the footer can be\n * written from the same state and never name a key that does nothing. The\n * caller owns the words, which rows exist, what each one does, and the levels\n * \u2014 a palette shows one level at a time, a `CommandPaletteSearch` or a\n * `CommandPaletteForm`, and the caller decides which.\n */\n\n/* \u2500\u2500 The window \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\ninterface PaletteContextValue {\n where?: string\n modKey: string\n popupRef: RefObject<HTMLDivElement | null>\n}\n\nconst PaletteContext = createContext<PaletteContextValue | null>(null)\n\nfunction usePalette(part: string) {\n const palette = useContext(PaletteContext)\n if (!palette) throw new Error(`[@estiva-app/ui] ${part} must be inside a CommandPalette.`)\n return palette\n}\n\nexport interface CommandPaletteProps {\n open: boolean\n onOpenChange: (open: boolean) => void\n /** The window's name for a screen reader \u2014 there is no title on screen. */\n label: string\n /** The footer's left side: where the palette was opened from. */\n where?: string\n /**\n * How the footer spells the modifier in `Ctrl+Backspace` and `Ctrl+Enter` \u2014\n * `Cmd` on a Mac. The keys answer to Ctrl and Cmd either way; the package\n * does not guess the platform, the app says it.\n */\n modKey?: string\n /** One level: a `CommandPaletteSearch` or a `CommandPaletteForm`. */\n children: ReactNode\n}\n\n/** The field a level starts on: the search field, or the form's first control. */\nconst CONTROL = 'input:not([type=\"hidden\"]):not([tabindex=\"-1\"]), textarea, button:not([tabindex=\"-1\"]), [role=\"combobox\"]'\n\nfunction firstControl(popup: HTMLElement | null): HTMLElement | null {\n if (!popup) return null\n const field = popup.querySelector<HTMLElement>('[data-command-palette-field]')\n if (field) return field\n return popup.querySelector<HTMLElement>('[data-command-palette-fields]')?.querySelector<HTMLElement>(CONTROL) ?? null\n}\n\nexport function CommandPalette({ open, onOpenChange, label, where, modKey = 'Ctrl', children }: CommandPaletteProps) {\n const popupRef = useRef<HTMLDivElement>(null)\n\n /*\n Focus never falls out of a level.\n\n Measured in the prototype: when Enter picks a row that then leaves the\n list, or a level is swapped for another, the element that had focus is\n gone, and focus lands on the page or on the dialog's own box. Every key\n after that goes nowhere \u2014 Esc still closes, and nothing else works. So\n after every render, a frame later, focus that is nowhere goes to the\n level's first control. Focus that is somewhere \u2014 a list opened from a\n field, which is portalled out of this box \u2014 is left alone.\n */\n useEffect(() => {\n if (!open) return\n const frame = requestAnimationFrame(() => {\n const popup = popupRef.current\n const active = document.activeElement\n if (popup && (!active || active === document.body || active === popup)) firstControl(popup)?.focus()\n })\n return () => cancelAnimationFrame(frame)\n })\n\n const context = useMemo(() => ({ where, modKey, popupRef }), [where, modKey])\n\n return (\n <Dialog.Root open={open} onOpenChange={(next) => onOpenChange(next)}>\n <Dialog.Portal>\n <Dialog.Backdrop className=\"fixed inset-0 z-40 bg-scrim\" />\n <Dialog.Viewport className=\"fixed inset-0 z-50 flex items-start justify-center pt-[16vh]\">\n <Dialog.Popup\n ref={popupRef}\n aria-label={label}\n initialFocus={() => firstControl(popupRef.current) ?? true}\n onFocus={(e) => {\n if (e.target === popupRef.current) firstControl(popupRef.current)?.focus()\n }}\n /* `outline-none`: the box is a programmatic focus target, not a\n Tab stop \u2014 the same reason as DialogShell's card. */\n className=\"flex w-[658px] max-w-[calc(100vw-32px)] flex-col overflow-hidden rounded-lg border border-border-default bg-bg-elevated shadow-lg outline-none\"\n >\n <PaletteContext.Provider value={context}>{children}</PaletteContext.Provider>\n </Dialog.Popup>\n </Dialog.Viewport>\n </Dialog.Portal>\n </Dialog.Root>\n )\n}\n\n/* \u2500\u2500 The parts every level shares \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n/**\n * The level you are in, drawn as a chip before the field. Removing it goes\n * back, and so does Backspace at the start of the field \u2014 one meaning for the\n * chip, whichever way you reach it.\n */\nexport interface CommandPaletteChip {\n label: string\n /** Before the label, 16px \u2014 an icon, a mark. */\n leading?: ReactNode\n onBack: () => void\n}\n\nfunction LevelChip({ chip, onBack }: { chip: CommandPaletteChip; onBack: () => void }) {\n return <InputChip label={chip.label} leading={chip.leading} onRemove={onBack} removeLabel={`Leave ${chip.label}`} truncate className=\"max-w-[272px] shrink-0\" />\n}\n\n/** Going back leaves focus in the level you land on, whichever way you went. */\nfunction useBack(chip: CommandPaletteChip | undefined, popupRef: RefObject<HTMLDivElement | null>) {\n return () => {\n if (!chip) return\n chip.onBack()\n requestAnimationFrame(() => firstControl(popupRef.current)?.focus())\n }\n}\n\ntype Key = [key: string, word: string]\n\nfunction Footer({ keys }: { keys: Key[] }) {\n const { where } = usePalette('The footer')\n return (\n <div className=\"flex h-9 shrink-0 items-center gap-4 border-t border-border-subtle px-5 text-caption text-text-secondary signal:font-mono signal:text-small signal:text-text-muted\">\n <span className=\"min-w-0 flex-1 truncate\">{where}</span>\n {keys.map(([key, word]) => (\n <span key={key} className=\"flex shrink-0 items-center gap-1.5\">\n <Kbd>{key}</Kbd> {word}\n </span>\n ))}\n </div>\n )\n}\n\nconst lowerFirst = (s: string) => s.charAt(0).toLowerCase() + s.slice(1)\n\n/* \u2500\u2500 A level of rows \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nexport interface CommandPaletteRow {\n /** Unique in the level. The footer follows the lit row by it. */\n id: string\n label: string\n /** The second line \u2014 where it is, who said it, when. */\n description?: string\n /** A 16px icon, drawn on the row's 32px tile. */\n icon?: ReactNode\n /** Instead of `icon`: something with a look of its own \u2014 a face, a status mark \u2014 in the same 32px space, with no tile. */\n leading?: ReactNode\n /** Enter, or a click. */\n onSelect: () => void\n /** The row leads to more rows: Tab, or \u2192 at the end of the text, goes in, and the row shows a chevron. */\n onGoIn?: () => void\n /** The row is the person's own history: Ctrl+Backspace forgets it. */\n onForget?: () => void\n}\n\nexport interface CommandPaletteGroup {\n /** The heading over the rows. A group with no rows is not drawn. */\n label: string\n rows: CommandPaletteRow[]\n}\n\nexport interface CommandPaletteSearchProps {\n query: string\n onQueryChange: (query: string) => void\n placeholder: string\n groups: CommandPaletteGroup[]\n /** The level you are in. Without one, this is the first level and there is nowhere to go back to. */\n chip?: CommandPaletteChip\n /** A line under the rows while more are on their way \u2014 \"Searching\u2026\". */\n pending?: string\n /**\n * Quiet lines under the rows, for what a person should know about them and\n * cannot act on \u2014 \"2 more where you cannot open them\", \"the search was\n * refused, so this list is incomplete\". Plain lines, never rows: the arrows\n * do not stop on them.\n */\n notes?: string[]\n /** The line when there are no rows. Leave it out while rows are still on their way. */\n empty?: string\n /** Above the rows: a `CommandPaletteWorking`, `CommandPaletteAnswer` or `CommandPaletteQuote`. */\n children?: ReactNode\n}\n\ntype ListGroup = { value: string; items: CommandPaletteRow[] }\n\nexport function CommandPaletteSearch({ query, onQueryChange, placeholder, groups, chip, pending, notes = [], empty, children }: CommandPaletteSearchProps) {\n const { modKey, popupRef } = usePalette('CommandPaletteSearch')\n const back = useBack(chip, popupRef)\n const items = useMemo<ListGroup[]>(() => groups.filter((g) => g.rows.length > 0).map((g) => ({ value: g.label, items: g.rows })), [groups])\n const rows = items.flatMap((g) => g.items)\n\n /*\n The lit row, followed by id rather than held as an object: a caller that\n builds its rows again on every render hands Base UI new objects with the\n same ids, and a key must run the handler of the row on screen now, not\n of the one that was lit a render ago.\n */\n const [litId, setLitId] = useState<string | undefined>()\n const lit = litId === undefined ? undefined : rows.find((r) => r.id === litId)\n\n /*\n The lit row stays lit when rows arrive above it (F7).\n\n Base UI keeps the highlight's *position*, not its row: measured, with the\n second row lit, two rows arriving above it handed the highlight to\n whatever now sat second, and Enter would have opened that. Base UI has no\n public way to set the highlight, so the palette walks it back with the\n arrow keys, exactly as a person would. It does so only when Base UI moved\n the highlight by itself (\"none\") \u2014 never after an arrow, the pointer, or\n typing, which starts again from the first row.\n\n And only once the person has moved the highlight on this level. Until then\n the lit row is simply the first one, and a row that arrives above it \u2014 a\n thread's own rows, a moment after opening \u2014 becomes the first one.\n */\n const inputRef = useRef<HTMLInputElement>(null)\n const reported = useRef<{ id?: string; reason?: string }>({})\n const settled = useRef<{ id?: string; query: string; level?: string } | null>(null)\n /** The person has moved the highlight since the text or the level last changed. */\n const moved = useRef(false)\n useLayoutEffect(() => {\n const before = settled.current\n const now = reported.current\n const sameLevel = before?.query === query && before?.level === chip?.label\n if (!sameLevel) moved.current = false\n if (moved.current && before?.id && now.reason === 'none' && now.id !== before.id && sameLevel) {\n const ids = rows.map((r) => r.id)\n const want = ids.indexOf(before.id)\n const at = now.id === undefined ? -1 : ids.indexOf(now.id)\n const input = inputRef.current\n if (input && want !== -1 && at !== -1) {\n const key = want > at ? 'ArrowDown' : 'ArrowUp'\n for (let step = 0; step < Math.abs(want - at); step++) {\n input.dispatchEvent(new KeyboardEvent('keydown', { key, bubbles: true, cancelable: true }))\n }\n }\n }\n settled.current = { id: reported.current.id, query, level: chip?.label }\n })\n\n const onKeyDown = (e: BaseUIEvent<KeyboardEvent<HTMLInputElement>>) => {\n if (e.key === 'Home' || e.key === 'End') {\n // Home and End move the text cursor and nothing else (the key list).\n // Base UI also sends the highlight to the first or last row (measured),\n // so its handling stops here and the field's own takes over.\n e.preventBaseUIHandler()\n return\n }\n const field = e.currentTarget\n const collapsed = field.selectionStart === field.selectionEnd\n const atStart = collapsed && field.selectionStart === 0\n const atEnd = collapsed && field.selectionEnd === field.value.length\n if (e.key === 'Backspace' && (e.ctrlKey || e.metaKey)) {\n // Forgetting is for a row that can be forgotten; anywhere else the key\n // deletes a word, as it does in any field.\n if (lit?.onForget) {\n e.preventDefault()\n lit.onForget()\n }\n return\n }\n if (e.key === 'Backspace' && atStart && chip && !e.shiftKey && !e.altKey) {\n e.preventDefault()\n back()\n return\n }\n if (e.key === 'Tab' && !e.shiftKey) {\n // Tab never walks out of the field at a level of rows: it goes in, or\n // it does nothing (the key list).\n e.preventDefault()\n lit?.onGoIn?.()\n return\n }\n if (e.key === 'ArrowRight' && atEnd && lit?.onGoIn && !e.shiftKey) {\n e.preventDefault()\n lit.onGoIn()\n }\n }\n\n const keys: Key[] = [\n ...(rows.length > 1 ? ([['\u2191\u2193', 'move']] as Key[]) : []),\n ...(lit ? ([lit.onGoIn ? ['Tab', 'go in'] : ['Enter', 'open']] as Key[]) : []),\n ...(lit?.onForget ? ([[`${modKey}+Backspace`, 'forget']] as Key[]) : []),\n // Backspace goes back from the start of the field; it is named while the\n // field is empty, when that is what it will do.\n ...(chip && query === '' ? ([['Backspace', 'back']] as Key[]) : []),\n ['Esc', 'close'],\n ]\n\n return (\n <Autocomplete.Root\n items={items}\n mode=\"none\"\n inline\n open\n value={query}\n onValueChange={(value, details) => {\n // Pressing a row would write the row's name into the field.\n if (details.reason === 'item-press') return\n onQueryChange(value)\n }}\n itemToStringValue={(row) => (row as CommandPaletteRow).label}\n /* The first row is always lit, so Enter always has something to do;\n the highlight stays where it is when the pointer leaves; and the\n arrows stop at the ends rather than wrapping (the key list). */\n autoHighlight=\"always\"\n keepHighlight\n loopFocus={false}\n onItemHighlighted={(row, details) => {\n const id = (row as CommandPaletteRow | undefined)?.id\n reported.current = { id, reason: details.reason }\n if (details.reason !== 'none') moved.current = true\n setLitId(id)\n }}\n >\n <div className=\"flex h-12 shrink-0 items-center gap-3 border-b border-border-subtle px-5\">\n <IconSearch size={16} stroke={1.5} className=\"shrink-0 text-text-secondary\" />\n {chip && <LevelChip chip={chip} onBack={back} />}\n <Autocomplete.Input\n ref={inputRef}\n data-command-palette-field=\"\"\n placeholder={placeholder}\n /* Named by what it asks for. Chrome would fall back to the\n placeholder by itself; said outright, the name does not depend\n on a fallback (ChipInput's field lost its name to one, Finding 6). */\n aria-label={placeholder}\n onKeyDown={onKeyDown}\n className=\"min-w-0 flex-1 bg-transparent text-input-value text-text-primary outline-none placeholder:text-text-muted\"\n />\n </div>\n\n {/* The rows' box is a menu's (P4, Katerina 16 September): 8px in from\n the edge, so the lit row's fill sits where it sits in every menu,\n and the tiles line up with the magnifier at 21px. */}\n <ScrollArea viewportClassName=\"max-h-[420px]\" contentClassName=\"flex flex-col p-2\">\n {children != null && <div className=\"flex flex-col gap-2 px-3 pb-2 pt-3\">{children}</div>}\n\n <Autocomplete.List className=\"flex flex-col\">\n {(group: ListGroup) => (\n <Autocomplete.Group key={group.value} items={group.items} className=\"flex flex-col\">\n {/* A heading labels the rows, it is not one of them: read\n secondary, as in a menu (MenuSection). */}\n <Autocomplete.GroupLabel className=\"flex h-7 shrink-0 items-center px-3\">\n <SectionLabel className=\"text-text-secondary\">{group.value}</SectionLabel>\n </Autocomplete.GroupLabel>\n <Autocomplete.Collection>\n {(row: CommandPaletteRow) => (\n /* The menu row, as the list's own option \u2014 the way Select\n puts it on `Select.Item`. One row, two parts. */\n <Autocomplete.Item key={row.id} value={row} onClick={() => row.onSelect()} className={menuItemClassName({ size: 'tall' })}>\n <MenuItemBody\n size=\"tall\"\n label={row.label}\n description={row.description}\n leading={<RowLeading row={row} />}\n submenu={!!row.onGoIn}\n hint={row.onGoIn ? <Kbd>Tab</Kbd> : <EnterHint />}\n />\n </Autocomplete.Item>\n )}\n </Autocomplete.Collection>\n </Autocomplete.Group>\n )}\n </Autocomplete.List>\n\n {/* Both lines stay mounted, as Base UI asks, so a screen reader hears\n them change; they take no room while they say nothing. */}\n <Autocomplete.Status className=\"flex items-center gap-2 px-3 [&:not(:empty)]:h-8\">\n {pending && (\n <>\n <IconLoader2 size={12} stroke={1.5} className=\"shrink-0 animate-spin text-text-muted\" />\n <span className=\"text-caption text-text-muted\">{pending}</span>\n </>\n )}\n </Autocomplete.Status>\n {notes.map((note) => (\n <div key={note} className=\"flex min-h-8 items-center px-3\">\n <FieldLine>{note}</FieldLine>\n </div>\n ))}\n <Autocomplete.Empty className=\"flex items-center px-3 [&:not(:empty)]:min-h-10\">\n {empty && <EmptyState scope=\"section\" message={empty} />}\n </Autocomplete.Empty>\n </ScrollArea>\n\n <Footer keys={keys} />\n </Autocomplete.Root>\n )\n}\n\n/** Every row leads with the same 32px space, so labels line up whatever leads them. */\nfunction RowLeading({ row }: { row: CommandPaletteRow }) {\n if (row.icon != null) {\n return <span className=\"flex size-8 items-center justify-center rounded-sm bg-bg-inset text-text-secondary\">{row.icon}</span>\n }\n return <span className=\"flex size-8 items-center justify-center\">{row.leading}</span>\n}\n\n/* \u2500\u2500 A level that is a form \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nexport interface CommandPaletteFormProps {\n /** Names what the form makes, and goes back when removed. */\n chip: CommandPaletteChip\n /** Before the chip, 16px \u2014 the mark of whatever the form writes to. */\n icon?: ReactNode\n /** The button's word, and the footer's beside Ctrl+Enter. */\n submitLabel: string\n onSubmit: () => void\n /** Why submitting has to wait \u2014 nothing changed yet. The button says it on hover, and Ctrl+Enter does nothing. */\n submitWaits?: string\n /** While the thing is being made: the fields lock, the button reads `button`, and `line` says what is happening. */\n working?: { button: string; line: string }\n /** What went wrong, beside the button. */\n error?: string\n /** The fields, each in a `Field`. */\n children: ReactNode\n}\n\nconst TEXT_TYPES = new Set(['', 'text', 'search', 'email', 'url', 'tel', 'password', 'number'])\n\n/** A field you type into \u2014 not a list, and not a chip field, whose Backspace takes a chip. */\nfunction isTextField(el: Element | null): el is HTMLInputElement | HTMLTextAreaElement {\n if (el instanceof HTMLTextAreaElement) return true\n return el instanceof HTMLInputElement && TEXT_TYPES.has(el.getAttribute('type') ?? '') && el.getAttribute('role') !== 'combobox' && el.tabIndex >= 0\n}\n\nexport function CommandPaletteForm({ chip, icon, submitLabel, onSubmit, submitWaits, working, error, children }: CommandPaletteFormProps) {\n const { modKey, popupRef } = usePalette('CommandPaletteForm')\n const back = useBack(chip, popupRef)\n const frameRef = useRef<HTMLDivElement>(null)\n const busy = !!working\n const busyRef = useRef(busy)\n busyRef.current = busy\n const returnTo = useRef<HTMLElement | null>(null)\n\n /** A field that says it needs something: Base UI marks the `Field` itself. */\n const firstInvalid = () => frameRef.current?.querySelector<HTMLElement>('[data-invalid]')?.querySelector<HTMLElement>(CONTROL) ?? null\n\n const submit = () => {\n if (busyRef.current || submitWaits) return\n const active = document.activeElement\n returnTo.current = active instanceof HTMLElement && frameRef.current?.contains(active) ? active : null\n onSubmit()\n // If the caller marked fields instead of starting, the first of them\n // takes focus: the key list's \"focus goes to the first\".\n requestAnimationFrame(() => {\n if (!busyRef.current) firstInvalid()?.focus()\n })\n }\n\n /*\n The lock must not lose focus.\n\n Locking disables the fields, and a disabled field drops focus to the page\n \u2014 measured in the prototype, where after Ctrl+Enter no key but Esc ever\n worked again, even after the error came back. So while the form works,\n focus sits on the form's own box, where its keys still arrive; when it\n stops, focus goes to the first field that needs something, or back where\n it was, or to the first field.\n */\n useLayoutEffect(() => {\n if (busy) {\n frameRef.current?.focus()\n return\n }\n if (document.activeElement !== frameRef.current) return\n const was = returnTo.current\n const target = firstInvalid() ?? (was?.isConnected && !(was as HTMLButtonElement).disabled ? was : null) ?? firstControl(popupRef.current)\n target?.focus()\n // Runs when the lock changes, and reads the DOM it leaves behind.\n }, [busy])\n\n /*\n Backspace goes back only where nothing typed is lost: from an empty text\n field, or from anywhere in a form that has no text field at all (a form\n that is one list). In a list inside a form with text fields it does\n nothing, so walking the form with Tab never throws the draft away.\n */\n const backWorksFrom = (el: Element | null) => {\n const frame = frameRef.current\n if (!frame || busy || !el || !frame.contains(el)) return false\n if (isTextField(el)) return el.value === ''\n return ![...frame.querySelectorAll('input, textarea')].some(isTextField)\n }\n\n // The footer names Backspace only where it works, so it follows focus and typing.\n const [backNamed, setBackNamed] = useState(false)\n const measure = () => setBackNamed(backWorksFrom(document.activeElement))\n useLayoutEffect(measure)\n\n const onKeyDown = (e: KeyboardEvent<HTMLDivElement>) => {\n if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {\n e.preventDefault()\n submit()\n return\n }\n if (e.key === 'Backspace' && !e.ctrlKey && !e.metaKey && !e.altKey && backWorksFrom(e.target as Element)) {\n e.preventDefault()\n back()\n }\n }\n\n const keys: Key[] = [\n ...(!busy && !submitWaits ? ([[`${modKey}+Enter`, lowerFirst(submitLabel)]] as Key[]) : []),\n ...(backNamed ? ([['Backspace', 'back']] as Key[]) : []),\n ['Esc', 'close'],\n ]\n\n return (\n <div ref={frameRef} tabIndex={-1} onKeyDown={onKeyDown} onFocus={measure} onBlur={measure} onInput={measure} className=\"flex min-h-0 flex-col outline-none\">\n <div className=\"flex h-12 shrink-0 items-center gap-3 border-b border-border-subtle px-5\">\n {icon != null && <span className=\"flex shrink-0 items-center text-text-secondary\">{icon}</span>}\n <LevelChip chip={chip} onBack={back} />\n </div>\n <ScrollArea viewportClassName=\"max-h-[420px]\" contentClassName=\"flex flex-col px-5 py-4\">\n {/* `contents`: the fieldset only locks; the fields lay out as if it\n were not there. */}\n <fieldset data-command-palette-fields=\"\" disabled={busy} className=\"contents\">\n <div className=\"flex flex-col gap-4\">{children}</div>\n </fieldset>\n </ScrollArea>\n <div className=\"flex shrink-0 items-center gap-3 px-5 pb-4\">\n <div className=\"min-w-0 flex-1\">\n {working ? <FieldLine>{working.line}</FieldLine> : error ? <FieldLine tone=\"error\">{error}</FieldLine> : null}\n </div>\n <Button\n variant=\"primary\"\n onClick={submit}\n disabled={busy}\n disabledReason={busy ? undefined : submitWaits}\n leadingIcon={busy ? <IconLoader2 size={16} stroke={1.5} className=\"animate-spin\" /> : undefined}\n >\n {working ? working.button : submitLabel}\n </Button>\n </div>\n <Footer keys={keys} />\n </div>\n )\n}\n\n/* \u2500\u2500 What sits above the rows \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nconst BAR_WIDTHS = ['w-4/5', 'w-3/5', 'w-2/3']\n\nexport interface CommandPaletteWorkingProps {\n /** What is happening \u2014 \"Reading 12 messages\u2026\". */\n children: ReactNode\n /** Grey bars under the line, where the result will be. Default 2. */\n bars?: 0 | 1 | 2 | 3\n}\n\n/** A line saying what is being worked on, with grey bars where the result will land. */\nexport function CommandPaletteWorking({ children, bars = 2 }: CommandPaletteWorkingProps) {\n return (\n <div role=\"status\" className=\"flex flex-col gap-2\">\n <span className=\"flex items-center gap-2 text-caption text-text-secondary\">\n <IconLoader2 size={14} stroke={1.5} className=\"shrink-0 animate-spin\" />\n {children}\n </span>\n {BAR_WIDTHS.slice(0, bars).map((width) => (\n <SkeletonBar key={width} className={cn('h-3', width)} />\n ))}\n </div>\n )\n}\n\nexport interface CommandPaletteAnswerProps {\n /**\n * The answer's text. A blank line starts a new paragraph, and a number in\n * square brackets \u2014 `[1]` \u2014 is drawn as a small mark pointing at the row of\n * the same number below.\n */\n children: string\n /** A quiet line under the answer \u2014 what a key will do now. */\n note?: string\n}\n\n/** A written answer, with marks that point at the rows it came from. */\nexport function CommandPaletteAnswer({ children, note }: CommandPaletteAnswerProps) {\n const paragraphs = children.split(/\\n\\s*\\n/).filter((p) => p.trim() !== '')\n return (\n <div className=\"flex flex-col gap-2\">\n {paragraphs.map((paragraph, i) => (\n <p key={i} className=\"whitespace-pre-wrap text-body-2 text-text-primary\">\n {/* A mark belongs to the word before it: the space a writer leaves\n before \"[1]\" would stand between them as a gap. */}\n {paragraph.split(/\\s*(\\[\\d+\\])/).map((part, j) =>\n /^\\[\\d+\\]$/.test(part) ? (\n <sup key={j} className=\"ml-0.5 font-mono text-small text-accent-primary\">\n {part.slice(1, -1)}\n </sup>\n ) : (\n part\n ),\n )}\n </p>\n ))}\n {note && <FieldLine>{note}</FieldLine>}\n </div>\n )\n}\n\nexport interface CommandPaletteQuoteProps {\n /** Text written for someone to use \u2014 shown as it will be used, line breaks kept. */\n children: string\n}\n\n/** Text written to be used elsewhere, set off by a rule at its left edge. */\nexport function CommandPaletteQuote({ children }: CommandPaletteQuoteProps) {\n return <p className=\"whitespace-pre-wrap border-l-2 border-border-default pl-3 text-body-2 text-text-primary\">{children}</p>\n}\n", "import { useMemo, useState, type KeyboardEvent, type ReactNode } from 'react'\nimport { Button as BaseButton } from '@base-ui/react/button'\nimport { Combobox } from '@base-ui/react/combobox'\nimport { IconX } from '@tabler/icons-react'\nimport { cn } from './cn'\nimport { MenuItem, MenuPanel } from './Menu'\nimport { ScrollArea } from './ScrollArea'\n\n/* The chip's own look, written once: `InputChip` draws it for a caller who\n wants a chip on its own, and `ChipInput` gives the same classes to Base UI's\n `Combobox.Chip`, which is the one that joins the input's keyboard. */\nconst CHIP_BOX =\n // Curved, not a pill (Katerina, 2026-09-01): the Avatar keeps its own\n // rounded-sm corners \u2014 never a forced circle \u2014 and the chip's corner\n // follows concentrically: 4px face + 2px inset = rounded-md.\n 'inline-flex items-center gap-1.5 bg-bg-elevated border border-border-subtle rounded-md py-0.5 max-h-[24px]'\nconst CHIP_LABEL = 'text-caption font-medium text-text-primary'\nconst CHIP_REMOVE = 'size-4 flex items-center justify-center rounded-full hover:bg-bg-hover text-text-secondary'\n// The padding follows the contents (Katerina, 2026-09-01): a face sits 2px\n// from the edge, a bare label needs 8px of air; the \u2715 brings its own box, so\n// 4px behind it \u2014 8px when there isn't one.\nconst chipPadding = (leading: boolean, removable: boolean) => cn(leading ? 'pl-[2px]' : 'pl-2', removable ? 'pr-1' : 'pr-2')\n\n/**\n * The chip a `ChipInput` is made of: a 24px pill with an optional 16px\n * leading (a face, an icon), a 12px label, and the \u2715 that removes it.\n * Exported on its own (Katerina, 2026-09-01) under a name that promises\n * nothing about people \u2014 a chip like this may one day hold a label, a file,\n * a filter.\n *\n * Inside a `ChipInput` the chip is Base UI's `Combobox.Chip` wearing these\n * same classes, because there it has to answer the arrow keys and Backspace\n * along with the input. This component is for a chip standing alone.\n */\nexport interface InputChipProps {\n label: string\n /** Before the label, 16px \u2014 an Avatar, an icon. */\n leading?: ReactNode\n /** Draws the \u2715; absent, the chip is display-only. */\n onRemove?: () => void\n /**\n * The \u2715's name for a screen reader. `Remove <label>` when not given. A chip\n * whose \u2715 does something other than remove it names what it does \u2014 a\n * launcher's scope chip leaves the scope (\"Leave Ship\").\n */\n removeLabel?: string\n /**\n * Cut a long label with an ellipsis once the chip is capped, by a\n * `max-w-*` on `className`. Off unless asked: cutting clips up to 4px of a\n * letter's soft edge at 1x even when the label fits (measured 2026-09-15),\n * so a chip that is never capped keeps every pixel.\n */\n truncate?: boolean\n className?: string\n}\n\nexport function InputChip({ label, leading, onRemove, removeLabel, truncate, className }: InputChipProps) {\n return (\n <div className={cn(CHIP_BOX, chipPadding(!!leading, !!onRemove), className)}>\n {leading && <span className=\"flex shrink-0 items-center\">{leading}</span>}\n <span className={cn(CHIP_LABEL, truncate && 'min-w-0 truncate')}>{label}</span>\n {/* Base UI's `Button`, as every button in the package is (D6). Base UI\n has no chip of its own \u2014 its only chips are `Combobox.Chip` and\n `ChipRemove`, which throw outside a combobox \u2014 so the \u2715 is the one\n part of a chip standing alone that it has a counterpart for. */}\n {onRemove && (\n <BaseButton\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation()\n onRemove()\n }}\n // In a capped chip the label gives way, never the \u2715.\n className={cn(CHIP_REMOVE, truncate && 'shrink-0')}\n aria-label={removeLabel ?? `Remove ${label}`}\n >\n <IconX size={10} stroke={1.5} />\n </BaseButton>\n )}\n </div>\n )\n}\n\n/**\n * A multi-select input: chips for the chosen, a typeahead for the rest \u2014\n * Peek's PersonChipInput (2026-09-01), generalised on the way in, and on Base\n * UI's `Combobox` since stage 5 of the migration (2026-09-13).\n *\n * **What the part brought, and what it took away from this file.** The list is\n * a real listbox now: the input keeps focus and says which row is highlighted\n * through `aria-activedescendant`, where before the rows were plain buttons in\n * a `<div>` and nothing was announced. With it went the highlight index, the\n * arrow keys, Enter, the filter loop, the blur timeout that kept a click on a\n * row from closing the list under the pointer, the `createPortal`, the\n * measured anchor rect, the resize and scroll listeners that re-measured it,\n * and `fit.ts` \u2014 the flip-up-when-low arithmetic this package carried for one\n * caller (PLAN Finding 22). Base UI's positioner does the flipping, and it\n * does it against the element rather than against a rect read a frame ago.\n *\n * **What this file still decides**, because none of it is the part's business:\n * which options are on offer (the chosen and the excluded are not), that a\n * match is on the label *or* the description, that suggestions appear only\n * once you type \u2014 focusing must not drop the whole directory over the surface\n * below \u2014 and that Backspace on an empty query takes the last chip.\n *\n * Escape clears the query when there is one and bubbles when there is not, so\n * the surface around it (dialog, launcher) can act.\n *\n * Generic over the option type: the objects handed back through `onChange`\n * are the caller's own, extra fields and all \u2014 no re-mapping on the way out.\n */\nexport interface ChipInputOption {\n id: string\n label: string\n /** The suggestion row's second line \u2014 a role, an address. Also searched. */\n description?: string\n}\n\nexport interface ChipInputProps<T extends ChipInputOption = ChipInputOption> {\n value: T[]\n onChange: (next: T[]) => void\n /** The directory the typeahead searches. */\n options: T[]\n placeholder?: string\n autoFocus?: boolean\n /** Option ids excluded from the suggestion list (e.g., the current user). */\n excludeIds?: string[]\n /** Before a chip's label, 16px \u2014 a face, an icon. */\n chipLeading?: (option: T) => ReactNode\n /** Before a suggestion row's label, 32px. */\n rowLeading?: (option: T) => ReactNode\n /** Set by a `Field` with `required`; a caller inside one owes nothing. */\n 'aria-required'?: boolean | 'true' | 'false'\n /**\n * The field's name, for a caller that is not inside a `Field` \u2014 a `Field`'s\n * label names it already. With neither this nor `aria-labelledby`, the\n * placeholder is the name, and it stays the name once a chip is in.\n */\n 'aria-label'?: string\n /** The id of what names the field on the page \u2014 a visible \"To:\", say. */\n 'aria-labelledby'?: string\n}\n\nexport function ChipInput<T extends ChipInputOption = ChipInputOption>({\n value,\n onChange,\n options,\n placeholder = 'Search\u2026',\n autoFocus,\n excludeIds = [],\n chipLeading,\n rowLeading,\n ...aria\n}: ChipInputProps<T>) {\n const [query, setQuery] = useState('')\n /* The box the list hangs from. Base UI hangs a combobox's list from its\n input by default, and the input sits inside this box's 12px of padding\n and its border, so the list came out 26px narrower than the field it\n belongs to (measured: 358 against 384) and started inside it. Held as an\n element, not a measured rect, so the positioner re-measures it. */\n const [box, setBox] = useState<HTMLDivElement | null>(null)\n\n /* What is on offer: never what is already chosen, never what the caller\n excluded. The part filters by the query; which options exist at all is\n this component's question. */\n const available = useMemo(() => {\n const chosen = new Set(value.map((o) => o.id))\n const excluded = new Set(excludeIds)\n return options.filter((o) => !chosen.has(o.id) && !excluded.has(o.id))\n }, [options, value, excludeIds])\n\n /* A match is on the label or the description \u2014 \"who is the engineer\" finds\n the person by their role. Base UI's own filter reads one string per item. */\n const filter = useMemo(\n () => (item: T, q: string) => {\n const needle = q.trim().toLowerCase()\n if (!needle) return true\n return item.label.toLowerCase().includes(needle) || (item.description ?? '').toLowerCase().includes(needle)\n },\n [],\n )\n\n /* Suggestions appear only once you type. Focus \u2014 or a dialog's autoFocus \u2014\n must not drop the whole directory over the surface below, which is why\n the open state is this component's and not the part's. */\n const open = query.trim().length > 0\n\n /* The field's name (PLAN Finding 6). With no chip, Chrome names the field\n by its placeholder; the first chip takes the placeholder away, and the\n field was left with no name at all (measured: \"\"). So the placeholder\n stays on as the name once it is no longer drawn. A caller's own name\n wins, and so does a `Field`'s label: Base UI points `aria-labelledby` at\n it, and a label by reference outranks `aria-label`.\n Only a name that exists is passed. Base UI copies a caller's prop over its\n own even when the value is `undefined`, so an `aria-labelledby` written\n out empty would wipe the `Field`'s. */\n const ariaLabel = aria['aria-label'] ?? (value.length > 0 ? placeholder : undefined)\n const naming = {\n ...(ariaLabel !== undefined && { 'aria-label': ariaLabel }),\n ...(aria['aria-labelledby'] !== undefined && { 'aria-labelledby': aria['aria-labelledby'] }),\n }\n\n function removeLast() {\n if (value.length > 0) onChange(value.slice(0, -1))\n }\n\n function onInputKeyDown(event: KeyboardEvent<HTMLInputElement>) {\n if (event.key === 'Backspace' && query === '' && value.length > 0) {\n // Consumed: removing a chip must not double as the surface's \"back\",\n // and Base UI would otherwise walk focus into the chips first.\n event.preventDefault()\n event.stopPropagation()\n removeLast()\n return\n }\n if (event.key === 'Escape' && query !== '') {\n // Consume it only when there is something to clear; an idle input lets\n // Escape bubble so the surface around it (dialog, launcher) can act.\n event.preventDefault()\n event.stopPropagation()\n setQuery('')\n }\n }\n\n return (\n <Combobox.Root\n multiple\n items={available}\n value={value}\n onValueChange={(next) => {\n onChange(next as T[])\n setQuery('')\n }}\n inputValue={query}\n onInputValueChange={setQuery}\n open={open}\n /* The list is the query's, not the click's \u2014 see `open` above. */\n openOnInputClick={false}\n filter={filter}\n itemToStringLabel={(option) => (option as T).label}\n >\n {/* The box the chips and the input share. `Combobox.Chips` is what makes\n the two one control for the keyboard; the look is what it always was. */}\n <Combobox.Chips ref={setBox} className=\"bg-bg-inset border border-border-default hover:border-border-strong focus-within:border-border-focus focus-within:hover:border-border-focus rounded-lg px-3 py-1.5 flex flex-wrap items-center gap-1.5 transition-colors min-h-[38px] cursor-text signal:transition-shadow signal:focus-within:shadow-focus-ring\">\n {value.map((option) => (\n <Combobox.Chip key={option.id} className={cn(CHIP_BOX, chipPadding(!!chipLeading, true))}>\n {chipLeading && <span className=\"flex shrink-0 items-center\">{chipLeading(option)}</span>}\n <span className={CHIP_LABEL}>{option.label}</span>\n <Combobox.ChipRemove className={CHIP_REMOVE} aria-label={`Remove ${option.label}`}>\n <IconX size={10} stroke={1.5} />\n </Combobox.ChipRemove>\n </Combobox.Chip>\n ))}\n <Combobox.Input\n autoFocus={autoFocus}\n placeholder={value.length === 0 ? placeholder : ''}\n aria-required={aria['aria-required']}\n {...naming}\n onKeyDown={onInputKeyDown}\n className=\"flex-1 min-w-[120px] bg-transparent text-body-2 text-text-primary placeholder:text-text-muted outline-none border-none\"\n />\n </Combobox.Chips>\n\n <Combobox.Portal>\n <Combobox.Positioner\n anchor={box}\n sideOffset={GAP}\n collisionPadding={VIEWPORT_PAD}\n className=\"z-50 data-[anchor-hidden]:hidden\"\n >\n {/* As wide as the box, which is what the hand-measured rect was\n for; `--anchor-width` is the positioner's own answer, and the\n anchor is the box (see `box` above), not the input. The padding\n is on the scrolling content so the bar hugs the panel (D63), and\n 240px is the cap this list has always had. */}\n <Combobox.Popup className=\"w-[var(--anchor-width)] p-0\" render={<MenuPanel />}>\n <ScrollArea viewportClassName=\"max-h-[240px]\" contentClassName=\"flex flex-col p-2\">\n <Combobox.List>\n {(option: T) => (\n <Combobox.Item\n key={option.id}\n value={option}\n render={\n <MenuItem\n size=\"tall\"\n leading={rowLeading?.(option)}\n label={option.label}\n description={option.description}\n />\n }\n />\n )}\n </Combobox.List>\n </ScrollArea>\n </Combobox.Popup>\n </Combobox.Positioner>\n </Combobox.Portal>\n </Combobox.Root>\n )\n}\n\n/** `Menu`'s numbers, because the list hangs the same way a menu does. */\nconst GAP = 4\nconst VIEWPORT_PAD = 8\n", "import { createContext, useContext, type ComponentPropsWithRef, type ReactElement, type ReactNode, type RefObject } from 'react'\nimport { IconChevronRight } from '@tabler/icons-react'\nimport { Menu as BaseMenu } from '@base-ui/react/menu'\nimport { cn } from './cn'\nimport { triggerDisabled } from './triggerDisabled'\nimport { ScrollArea } from './ScrollArea'\nimport { Kbd } from './Kbd'\nimport { SectionLabel } from './SectionLabel'\n\n/**\n * THE menu shell (2026-09-01) \u2014 extracted once, for every menu in every app.\n * On Base UI's `Menu` since stage 4 of the migration (2026-09-07).\n *\n * Peek has no single Menu file; its topic menu, conversation menus, files\n * menu and the top bar's account menu each hand-roll the same thing, and\n * they disagree: some close on Escape, most do not, and row heights and\n * minimum widths drift copy by copy. Ship extracted the shape so it would\n * not become the next copy; the package exists so nobody becomes the one\n * after that.\n *\n * What every menu shares, kept exactly: an elevated container with a\n * hairline border, 8px radius, 8px padding and the large shadow; items that\n * are 8px-radius rows, `px-2 py-1.5` and never shorter than 36px, hover\n * fill, 14px text, destructive ones in the error colour; section headings\n * as a SectionLabel in a 32px row; and the two exits every menu has \u2014 Escape and a click outside.\n *\n * **What stage 4 changed, and it is the first thing a keyboard user notices:\n * the arrow keys walk the rows.** \u2191 and \u2193 move between items and wrap, Home\n * and End jump to the ends, typing a row's first letters jumps to it, and\n * focus moves into the menu when it opens and back out when it closes. None\n * of that existed; a menu was a portalled box of buttons you tabbed through.\n *\n * Deleted with the port: the `createPortal`, the `mousedown` listener on\n * `document`, the Escape listener beside it, the resize and scroll listeners,\n * the provisional hidden render this component did in order to measure\n * itself, and `fit.ts` \u2014 the shared geometry the shell and Select both used.\n * Floating UI places, flips and clamps all of it.\n *\n * The three anchorings are unchanged, and all three now portal (the third\n * did not, which is exactly how the identity menu got covered by a z-indexed\n * panel header in the first place):\n * - `anchor` (an element, or a rect a click handler measured): the menu hangs\n * under it, flips above when the room below is worse, and stays on screen.\n * `align=\"right\"` hangs the menu's right edge from the anchor's.\n * - `position` (viewport coordinates the caller computed): the corner the\n * caller chose, as a virtual anchor, so it still cannot land off screen.\n * - neither: under the element the menu sits in, right-aligned \u2014 what\n * `absolute right-0 top-full mt-1` drew, but portalled, so no ancestor's\n * stacking context or overflow can clip it.\n */\n/**\n * Whether a row is inside a `Menu`.\n *\n * It is load-bearing: `MenuItem` and `MenuSection` are also used\n * inside a bare `MenuPanel`, with no `Menu` around them \u2014 Peek's `@`, `/` and\n * `[` pickers and its compose menu all do that, because a popup inside a text\n * editor cannot have a menu's keyboard: the editor's suggestion plugin already\n * owns it. A Base UI `Menu.Item` outside a `Menu.Root` has no context to read,\n * so those rows stay exactly the buttons they have always been, and only rows\n * inside a real menu become the part.\n */\nconst MenuContext = createContext<{ openOnHover: boolean } | null>(null)\n\n/**\n * The menu's surface, with none of its behaviour \u2014 an elevated box with a\n * hairline border, 8px radius, 8px padding and the large shadow.\n *\n * Split out of `Menu` on 2026-09-05. `Menu` owns Escape, outside-click,\n * placement and now the keyboard, and that is right for a menu opened from a\n * trigger \u2014 but a type-ahead popup inside a text editor cannot have them: the\n * editor's suggestion plugin already owns the keyboard and positions the\n * popup, and a second Escape handler fights it. So Peek's `@`, `/` and `[`\n * menus each drew this box by hand, and the three had already drifted apart.\n *\n * `Menu` renders this, so there is still exactly one definition of the\n * surface \u2014 change it here and every menu in every app follows.\n *\n * Width, height and internal rhythm belong to the caller: a picker that lists\n * people is not the width of one that lists verbs.\n */\nexport interface MenuPanelProps extends Omit<ComponentPropsWithRef<'div'>, 'children'> {\n /** Optional only because Base UI fills it in when this is a `render`\n * target: `Menu.Popup` supplies the children. Every other caller passes\n * them. */\n children?: ReactNode\n}\n\nexport function MenuPanel({ children, className, ...props }: MenuPanelProps) {\n return (\n <div\n className={cn(\n 'flex flex-col rounded-lg border border-border-default bg-bg-elevated p-2 shadow-lg',\n /*\n * A divider in a menu runs the width of the rows it separates.\n *\n * `Divider` is inset 12px each side, which is right in a page and\n * wrong here: measured in a 180px menu, the rule started 21px from the\n * panel's edge where the rows start at 9px \u2014 12px narrower on each side\n * than the things it divides. `IdentityMenu` was already cancelling it\n * by hand with `mx-0`, which is the sign it belonged here (Katerina,\n * 2026-09-08). Direct children only, so a divider a caller puts inside\n * a row keeps its own spacing.\n */\n '[&>[role=separator]]:mx-0',\n className,\n )}\n {...props}\n >\n {children}\n </div>\n )\n}\n\nexport interface MenuProps {\n /**\n * The control that opens the menu. Any element that forwards its ref and\n * spreads its props \u2014 this package's `Button`, `IconButton` and\n * `PersonTrigger` all do.\n *\n * The menu owns it, and that is the point: Base UI can only do the toggle,\n * the placement, the focus return, the first-item highlight and the whole\n * hover choreography if it knows which element opened it.\n */\n trigger: ReactElement\n /** Which of the menu's edges hangs from the trigger's. Default left. */\n align?: 'left' | 'right'\n /**\n * Open on hover, and close shortly after the pointer leaves \u2014 for a control\n * that only appears while the pointer is on a card.\n *\n * Base UI owns the whole choreography, including the diagonal from a row\n * out to a submenu panel. The shell used to hand-write this and got it\n * wrong in two directions.\n */\n openOnHover?: boolean\n /** Controlled, for a caller that must know or must force it. Leave both\n * off and the menu keeps its own state. */\n open?: boolean\n onOpenChange?: (open: boolean) => void\n /** Base UI's imperative handle. `actions.current?.close()` shuts the menu \u2014\n * for a row that must close it before opening what it opens. A `MenuItem`\n * closes the menu by itself, so this is only for content that is not one. */\n actionsRef?: RefObject<{ close: () => void; unmount: () => void } | null>\n children: ReactNode\n /** On the menu's surface \u2014 its width. **Not its padding**: see\n * `contentClassName`. */\n className?: string\n /**\n * The padding around the rows, as a class. Default `p-2`, 8px.\n *\n * It is on the scrolling content, not on the panel, so the scrollbar hugs the\n * panel's edge (D63) \u2014 and so a padding class on `className` adds to it\n * rather than replacing it. Peek's Later menu asked for `p-1` there and got\n * 12px from `0.12.6` on (PLAN Finding 60). Set it here.\n */\n contentClassName?: string\n}\n\n/**\n * Read from `globalThis` rather than as a bare `process`: the declaration\n * build carries no ambient types on purpose, so that a consumer of this\n * package does not inherit Node's. Every bundler still replaces the value.\n */\nconst isProduction = () =>\n (globalThis as { process?: { env?: { NODE_ENV?: string } } }).process?.env?.NODE_ENV === 'production'\n\n/** The 4px between a menu and its trigger, and the 8px it keeps clear of\n * every screen edge \u2014 the two numbers `fit.ts` used. */\nconst GAP = 4\nconst VIEWPORT_PAD = 8\n/** A hover menu opens at once and closes 150ms after the pointer leaves \u2014\n * the two numbers the hand-written version used. */\nconst HOVER_OPEN_DELAY = 0\nconst HOVER_CLOSE_DELAY = 150\n\nexport function Menu({ trigger, align = 'left', openOnHover = false, open, onOpenChange, actionsRef, children, className, contentClassName }: MenuProps) {\n return (\n <BaseMenu.Root\n open={open}\n onOpenChange={onOpenChange ? (next) => onOpenChange(next) : undefined}\n actionsRef={actionsRef}\n /* Non-modal, as every menu here has always been: the page behind keeps\n its scrollbar, so opening a menu never shifts the layout. */\n modal={false}\n >\n <BaseMenu.Trigger\n render={trigger}\n /* The part writes its own disabled state over the button's, so a\n disabled trigger came out `aria-disabled=\"false\"` and opened\n (Finding 39, Ship's adoption, 2026-09-08). It is told what the\n button already knows. */\n disabled={triggerDisabled(trigger)}\n openOnHover={openOnHover}\n delay={HOVER_OPEN_DELAY}\n closeDelay={HOVER_CLOSE_DELAY}\n />\n <BaseMenu.Portal>\n <BaseMenu.Positioner\n side=\"bottom\"\n align={align === 'right' ? 'end' : 'start'}\n sideOffset={GAP}\n collisionPadding={VIEWPORT_PAD}\n className=\"z-50 data-[anchor-hidden]:hidden\"\n >\n <BaseMenu.Popup\n data-interactive\n /* `outline-none`: the popup is a programmatic focus target, not\n something a keyboard user tabs to. Without it Chrome rings the\n WHOLE panel when the menu is opened from the keyboard, which\n reads as \"the menu is one thing\" rather than \"these rows are the\n things\" \u2014 measured, `outline: auto 1px`. The rows keep their own\n highlight. Exactly the fix `DialogShell`'s card needed at stage\n 3, in a second place.\n\n `--available-height` is the room Floating UI found after flipping\n and clamping; the shell passes no cap of its own, so a menu stands\n as tall as it can and scrolls only when the screen truly has no\n room. Select's 288 was never this component's \u2014 the identity\n panel got it by accident once and grew a scrollbar at full\n height. */\n className={cn('min-w-[180px] outline-none p-0', className)}\n render={<MenuPanel />}\n >\n {/* The height cap sits on the box that scrolls \u2014 on the panel it\n let the box grow to its content and nothing scrolled (measured,\n 2026-09-08).\n *\n * And **the padding is on the content, not on the panel** (D63,\n * 2026-09-13). With it on the panel the scrolling box was inset by\n * it, so the bar floated 9px in from the panel's edge where every\n * other scrolling surface in the suite draws it at 1px \u2014\n * `DialogShell` had it right and said so in its own comment, and\n * this is the same arrangement. The rows do not move: the padding\n * that used to be the panel's is now the content's, at the same\n * 8px. The cap loses its `- 1rem` for the same reason \u2014 the\n * padding is inside the scrolling box now, so the panel is exactly\n * as tall as Floating UI allowed.\n *\n * A caller's `className` still lands on the panel, so a caller\n * asking for different padding needs `contentClassName`. That\n * sentence was written at 0.12.6 and the prop was not: a `p-1` on\n * `className` added 4px to these 8px instead of replacing them\n * (PLAN Finding 60). The prop exists now. */}\n <ScrollArea viewportClassName=\"max-h-[var(--available-height)]\" contentClassName={cn('flex flex-col p-2 [&>[role=separator]]:mx-0', contentClassName)}>\n <MenuContext.Provider value={{ openOnHover }}>{children}</MenuContext.Provider>\n </ScrollArea>\n </BaseMenu.Popup>\n </BaseMenu.Positioner>\n </BaseMenu.Portal>\n </BaseMenu.Root>\n )\n}\n\n/**\n * A row that opens another menu beside it \u2014 the submenu two Peek menus\n * hand-rolled before this, one of which dropped the ref its edge-flip\n * measured and shipped a submenu cut off by the screen (2026-09-03).\n *\n * Hover-timed as those were, and as ours was: it opens at once and closes\n * 150ms after the pointer leaves, so the diagonal from row to panel survives.\n * Those two numbers are all that is left of the old implementation \u2014 the\n * placement, the flip at a screen edge, and now the arrow keys (\u2192 opens it,\n * \u2190 closes it) are Base UI's.\n */\nexport interface MenuSubProps {\n /** The trigger row's label. */\n label: string\n leading?: ReactNode\n /** Mark the trigger row as holding a current value. */\n selected?: boolean\n /** The submenu's rows. */\n children: ReactNode\n /** On the submenu panel. */\n className?: string\n}\n\nexport function MenuSub({ label, leading, selected, children, className }: MenuSubProps) {\n /*\n * A submenu inside a hover-opened menu strands it.\n *\n * Measured 2026-09-08: enter the submenu's panel, then leave in any\n * direction that does not cross back over the parent, and neither the\n * submenu nor the menu ever closes again \u2014 at 200ms, 500ms, 1s and 2s.\n * Both triggers hard-code Floating UI's `safePolygon({ blockPointerEvents:\n * true })`, which blocks pointer events while the path from row to panel is\n * live; leaving that way never resolves the polygon. There is no prop.\n *\n * So the rule is: no `openOnHover` on a menu that has one of these. This\n * says so out loud rather than leaving it to the page, because the failure\n * is a menu that will not go away and the cause is two files apart.\n */\n const menu = useContext(MenuContext)\n if (!isProduction() && menu?.openOnHover) {\n console.error(\n '[@estiva-app/ui] Menu: `openOnHover` and `MenuSub` cannot be used together \u2014 ' +\n 'leaving the submenu panel strands the menu open. Open this menu on a press instead. ' +\n 'See Menu.mdx, \"Hover-opened menus\".',\n )\n }\n return (\n <BaseMenu.SubmenuRoot>\n <BaseMenu.SubmenuTrigger\n openOnHover\n delay={0}\n closeDelay={150}\n nativeButton\n render={<button type=\"button\" />}\n className={menuItemClassName({ size: 'default', selected })}\n >\n <MenuItemBody label={label} leading={leading} submenu />\n </BaseMenu.SubmenuTrigger>\n <BaseMenu.Portal>\n <BaseMenu.Positioner\n side=\"inline-end\"\n align=\"start\"\n sideOffset={GAP}\n collisionPadding={VIEWPORT_PAD}\n className=\"z-50 data-[anchor-hidden]:hidden\"\n >\n <BaseMenu.Popup className={cn('w-[160px]', className)} data-interactive render={<MenuPanel />}>\n {children}\n </BaseMenu.Popup>\n </BaseMenu.Positioner>\n </BaseMenu.Portal>\n </BaseMenu.SubmenuRoot>\n )\n}\n\nexport interface MenuItemProps extends Omit<ComponentPropsWithRef<'button'>, 'children'> {\n label?: string\n /** Replaces the label/description block \u2014 for rows whose middle is richer\n * than text (the launcher's form rows). Leading/trailing still apply. */\n children?: ReactNode\n /** `tall` is the picker row \u2014 content height with a 40px floor, px-3,\n * gap-3, room for a 32px face or icon tile and the description line.\n * `default` is the command row. */\n size?: 'default' | 'tall'\n /** A second line under the label \u2014 a role, an address \u2014 12px, secondary, truncating. */\n description?: string\n /** Before the label: a 16px icon (stroke 1.5, secondary), or an Avatar, for rows led by a face. */\n leading?: ReactNode\n /** At the right edge: a hint, a value \u2014 anything. Wins over `shortcut` and `submenu`. */\n trailing?: ReactNode\n /**\n * Shown at the right edge **only while this row is the one you are pointing\n * at or have arrowed onto** \u2014 an `EnterHint`, typically.\n *\n * Pass it unconditionally. Do not do `trailing={active ? <EnterHint/> : undefined}`:\n * that mounts the hint, and a mount is instant while the row's own fill is a\n * 150ms fade, so the hint lands ahead of the highlight on the way in and\n * vanishes ahead of it on the way out (measured 2026-09-05 \u2014 ~7 frames of a\n * chip sitting on an unhighlighted row, and two rows lit at once when\n * sweeping). This slot is always in the DOM and revealed by the *same*\n * `:hover` / `selected` the fill uses, on the same duration and curve, so\n * the two cannot come apart \u2014 and the row does not reflow when it appears.\n *\n * It cross-fades with `trailing`/`shortcut`/`submenu` rather than displacing\n * them, and reserves the wider of the two, so nothing moves either way.\n */\n hint?: ReactNode\n /** A keyboard hint, drawn as the kbd chip. */\n shortcut?: string\n /** The row opens another menu: draws the chevron at the right edge. */\n submenu?: boolean\n destructive?: boolean\n /** The row the menu currently points at (a submenu's chosen value). */\n selected?: boolean\n}\n\n/**\n * The row's class list, written once because `MenuSub`'s trigger row is the\n * same row.\n *\n * `data-highlighted` joins `:hover` here, and that is the whole visible\n * consequence of the port: the fill that followed the pointer now also\n * follows the arrow keys, because Base UI sets one attribute for both.\n */\n/** The row's look, shared inside the package: `MenuItem` draws it, and\n * `Select` puts it on Base UI's `Select.Item`, whose parts (`ItemText`,\n * `ItemIndicator`) have to stay the element's own. One row, two parts. Not\n * exported from the package's index. */\nexport function menuItemClassName({ size, selected, className }: { size: 'default' | 'tall'; selected?: boolean; className?: string }) {\n return cn(\n // shrink-0: a menu is a flex column that scrolls at its max height,\n // and a flex child shrinks before its container does \u2014 so every row\n // in an overflowing menu was squashed to its `min-h`, and a row given\n // an explicit height silently lost it (Peek's `[` menu: h-12 rows\n // measured 40px). The same fix NavItem took on 2026-09-02.\n // `group`: the `hint` slot reveals itself from this row's own :hover,\n // so the hint and the fill are one CSS state change, not two engines.\n //\n // No transition on the fill (Katerina, 2026-09-05). It faded over\n // 150ms, and anything appearing with it had to fade too or arrive\n // ahead of it \u2014 which, sweeping a pointer down a list, read as the\n // hint flickering in and out. Both are instant now: they still change\n // on exactly the same :hover, so they cannot come apart, and a row\n // lights and unlights crisply as the pointer crosses it.\n 'group flex w-full shrink-0 cursor-pointer items-center rounded-lg text-left outline-none hover:bg-bg-hover data-[highlighted]:bg-bg-hover',\n // Both sizes are as tall as their content and never shorter than a\n // floor: 36px for a default row (Katerina, 2026-09-11), 40px for a\n // tall one (Katerina, 2026-09-01). A single-line action row sits at\n // 36, a picker row at 40, a row with a 32px face and a role line\n // comes out at its natural 48. One rule, not a hand-picked height\n // per file \u2014 Peek's Later menu had been forcing `h-9` for exactly\n // this 36px and was the only menu in either app out of step.\n size === 'tall' ? 'min-h-10 gap-3 px-3 py-1.5' : 'min-h-9 gap-2 px-2 py-1.5',\n selected && 'bg-bg-hover',\n className,\n )\n}\n\n/** Everything inside the row \u2014 written once, for the same reason. Shared\n * inside the package like `menuItemClassName`: `CommandPalette` draws it\n * inside Base UI's `Autocomplete.Item`. Not exported from the index. */\nexport function MenuItemBody({ label, children, size = 'default', description, leading, trailing, hint, shortcut, submenu, destructive, selected }: Pick<MenuItemProps, 'label' | 'children' | 'size' | 'description' | 'leading' | 'trailing' | 'hint' | 'shortcut' | 'submenu' | 'destructive' | 'selected'>) {\n const edge =\n trailing ??\n (shortcut ? (\n <Kbd>{shortcut}</Kbd>\n ) : submenu ? (\n <IconChevronRight size={16} stroke={1.5} className=\"shrink-0 text-text-muted\" />\n ) : null)\n return (\n <>\n {leading && <span className=\"flex shrink-0 items-center\">{leading}</span>}\n {/* Sizes are arbitrary values (the body-2 and caption tokens): these\n lists merge with a colour, and tw-merge drops a token size beside a\n colour. Ship's copy said `text-sm`, which was never the ramp. */}\n {children ?? (\n <span className={cn('flex min-w-0 flex-1 flex-col', size === 'tall' && 'gap-[2px]')}>\n <span className={cn('truncate text-body-2', destructive ? 'text-error-default' : 'text-text-primary')}>\n {label}\n </span>\n {description && <span className=\"truncate text-caption text-text-secondary\">{description}</span>}\n </span>\n )}\n {(edge || hint) && (\n // One grid cell holding both, right-aligned: the slot is as wide as\n // the wider of the two and never changes, so revealing the hint moves\n // nothing. No transition here either \u2014 the hint switches on the same\n // :hover / `selected` as the fill, in the same frame.\n <span className=\"grid shrink-0 items-center justify-items-end [&>*]:col-start-1 [&>*]:row-start-1\">\n {edge && (\n <span className={cn('flex items-center', hint && 'group-hover:opacity-0 group-data-[highlighted]:opacity-0', hint && selected && 'opacity-0')}>\n {edge}\n </span>\n )}\n {hint && (\n <span className={cn('flex items-center opacity-0 group-hover:opacity-100 group-data-[highlighted]:opacity-100', selected && 'opacity-100')}>\n {hint}\n </span>\n )}\n </span>\n )}\n </>\n )\n}\n\nexport function MenuItem({ label, children, size = 'default', description, leading, trailing, hint, shortcut, submenu, destructive, selected, className, ...props }: MenuItemProps) {\n const body = (\n <MenuItemBody\n label={label}\n size={size}\n description={description}\n leading={leading}\n trailing={trailing}\n hint={hint}\n shortcut={shortcut}\n submenu={submenu}\n destructive={destructive}\n selected={selected}\n >\n {children}\n </MenuItemBody>\n )\n const rowClassName = menuItemClassName({ size, selected, className })\n\n /*\n * Inside a `Menu` the row is Base UI's `Menu.Item`: it joins the roving\n * focus, answers the typeahead, and closes the menu when it is chosen.\n * `nativeButton` keeps the `<button>`; the part would draw a `<div>`.\n *\n * Outside one \u2014 a bare `MenuPanel` in an editor popup \u2014 it is a plain\n * button, **with no `role=\"menuitem\"`**. It used to carry the role\n * unconditionally, which made an orphan: ARIA requires a `menuitem` to sit\n * inside a `menu` or a `menubar`, and `MenuPanel` is a `<div>` with no role\n * at all. Four Peek files draw rows that way, so four surfaces were telling\n * a screen reader they were menu items of nothing.\n *\n * A button is what these rows actually are. The pickers they sit in are a\n * listbox pattern rather than a menu \u2014 the editor's plugin owns the\n * highlight and the keyboard \u2014 and saying so properly is `ChipInput`'s\n * stage, not this one; claiming the wrong role in the meantime is worse\n * than claiming none.\n */\n if (!useContext(MenuContext)) {\n return (\n <button type=\"button\" className={rowClassName} {...props}>\n {body}\n </button>\n )\n }\n return (\n <BaseMenu.Item\n nativeButton\n render={<button type=\"button\" />}\n className={rowClassName}\n /* `Menu.Item` types its handlers for the `<div>` it would draw by\n default. `render` makes it the `<button>` this row has always been,\n and `nativeButton` tells Base UI so \u2014 but the prop types do not\n follow the render target, so a caller's `onClick` is a button\n handler facing a div signature. The element underneath is a\n `<button>`, which `Menu.test.tsx` pins. */\n {...(props as ComponentPropsWithRef<'div'>)}\n >\n {body}\n </BaseMenu.Item>\n )\n}\n\n/**\n * The keyboard hint a picker row shows while highlighted \u2014 hand-rolled in\n * five files before this (2026-09-01), and drawn as its own thing until\n * 2026-09-05, when it became the `Kbd` chip every other key hint uses. A\n * picker row and a menu row sit in the same menu; they named the same key\n * two ways.\n *\n * `target` is what pressing it gives you, when that is worth saying \u2014 the\n * topic picker's \"\u21A9 Enter #topic\". It sits after the chip, in the picker's\n * own 9px voice, because it is not a key.\n *\n * Katerina, 2026-09-05, told the measurement and asked again: the chip keeps\n * the `\u21A9` character. It is not in Geist Mono \u2014 the browser borrows it, so it\n * advances 8.63px where the font's own characters advance 6 \u2014 and that is a\n * knowing trade for the glyph, not an oversight. If the width ever has to go,\n * Tabler's IconCornerDownLeft draws the same shape.\n */\nexport function EnterHint({ target }: { target?: string }) {\n return (\n <span className=\"flex shrink-0 items-center gap-1.5 text-text-muted\">\n <Kbd>\u21A9 Enter</Kbd>\n {target && (\n <span className=\"text-menu signal:font-mono signal:text-small signal:tracking-wider\">{target}</span>\n )}\n </span>\n )\n}\n\n/**\n * A section heading inside a menu: the 32px row with a SectionLabel, read\n * secondary \u2014 a heading inside a menu labels the rows, it is not one of\n * them (Katerina, 2026-09-01).\n *\n * Inside a `Menu` it is Base UI's `Menu.Group` with the heading as its\n * `GroupLabel`, so the rows under it are announced as a named group rather\n * than as a run of items with a stray line above them. Outside one \u2014 the\n * editor pickers, which draw the surface but not the menu \u2014 it is the two\n * plain elements it has always been.\n */\nexport function MenuSection({ label, children, className }: { label: string; children: ReactNode; /** On the heading row \u2014 a surface whose rows are px-3 aligns its heading with px-3. */ className?: string }) {\n const inMenu = useContext(MenuContext) !== null\n const heading = (\n <div className={cn('flex h-8 items-center px-2', className)}>\n <SectionLabel className=\"text-text-secondary\">{label}</SectionLabel>\n </div>\n )\n if (!inMenu) {\n return (\n <div className=\"flex flex-col\">\n {heading}\n {children}\n </div>\n )\n }\n return (\n <BaseMenu.Group className=\"flex flex-col\">\n <BaseMenu.GroupLabel render={heading} />\n {children}\n </BaseMenu.Group>\n )\n}\n\n/** A non-interactive row inside a menu, at the item's own geometry. */\nexport function MenuRow({ children, className }: { children: ReactNode; className?: string }) {\n return <div className={cn('flex items-center gap-2 px-2 py-1.5', className)}>{children}</div>\n}\n", "import type { ReactElement } from 'react'\n\n/**\n * Whether the element a `Menu` or `Popover` was handed as its trigger is\n * disabled \u2014 by `disabled`, or by a `disabledReason` (which disables the\n * button and keeps it reachable). Base UI's trigger parts keep a disabled\n * state of their own and write it over the rendered button's, so they have to\n * be told (Finding 39, 2026-09-08).\n */\nexport function triggerDisabled(trigger: ReactElement): boolean {\n const props = trigger.props as { disabled?: boolean; disabledReason?: string }\n return !!(props.disabled || props.disabledReason)\n}\n", "import type { ReactNode } from 'react'\nimport { cn } from './cn'\n\n/**\n * THE section-title label \u2014 every section heading (sidebar sections, menu\n * headings, panel titles) renders through this span, so a style change is\n * one edit. Peek's original, verbatim: 12px / 12px / 500, primary text\n * (Peek's ruling 2026-07-23: labels stay primary), the mono uppercase\n * micro-label under Signal. Metrics as arbitrary values for the tw-merge\n * reason the README records.\n *\n * Deliberately NOT the same thing as Property's stacked field label (the\n * 9px `menu`-token one): they were merged for a day and unmerged by\n * Katerina's ruling (2026-09-01) \u2014 a section title and a field label are\n * different voices, at different sizes, on purpose.\n */\nexport function SectionLabel({ children, className }: { children: ReactNode; className?: string }) {\n return (\n <span\n className={cn(\n 'text-h5 leading-3 text-text-primary signal:font-mono signal:text-small signal:uppercase signal:tracking-widest',\n className,\n )}\n >\n {children}\n </span>\n )\n}\n", "import type { ReactNode } from 'react'\nimport { IconMessage2 } from '@tabler/icons-react'\nimport { cn } from './cn'\n\n/**\n * The place where content will be once there is some, in two manners\n * (Katerina, 2026-09-09):\n *\n * - `page` \u2014 the whole page is empty: a 16px icon over a centred line of\n * secondary text, **in the middle of its box both ways** (Katerina,\n * 2026-09-09: it sat near the top). It grows to the room a flex column\n * gives it (`flex-1`) and centres inside that; a caller that draws it\n * straight into a page hands it the height (`className=\"h-full\"`).\n * Peek's EmptyState (2026-08-28) otherwise; the default.\n * - `section` \u2014 one section of a page is empty, and the page has other\n * things on it: the line alone, left-aligned, no icon. A section's\n * emptiness is a line among the page's content, not a stage of its own.\n * It is the quiet line \u2014 caption size in the muted colour, as a Field's\n * helper line is \u2014 so beside rows it reads as \"nothing here\", not as one\n * more row (Katerina, 2026-09-15: \"not that intense\", \"smaller font\").\n *\n * The message is the caller's \u2014 a shared component has no words of its own\n * for what is missing.\n */\nexport interface EmptyStateProps {\n /** 16px, stroke 1.5. A speech bubble when absent. Drawn for a `page` only. */\n icon?: ReactNode\n message: string\n /** `page` when the whole page is empty; `section` when one part of it is. */\n scope?: 'page' | 'section'\n className?: string\n}\n\nexport function EmptyState({ icon, message, scope = 'page', className }: EmptyStateProps) {\n if (scope === 'section') {\n return <p className={cn('text-caption text-text-muted', className)}>{message}</p>\n }\n return (\n <div className={cn('flex flex-1 flex-col items-center justify-center gap-2', className)}>\n <span className=\"text-text-secondary\">{icon ?? <IconMessage2 size={16} stroke={1.5} />}</span>\n <p className=\"text-body-2 text-text-secondary text-center\">{message}</p>\n </div>\n )\n}\n", "import { cloneElement, isValidElement, type ReactElement, type ReactNode } from 'react'\nimport { Field as BaseField } from '@base-ui/react/field'\nimport { cn } from './cn'\n\n/**\n * Peek's Field (2026-08-28): a label over a control, 8px apart, with a red\n * asterisk when required. The label is the `input-label` type token, merged\n * with `cn()` like every other class list here. On Base UI's `Field` since\n * stage 3 of the migration (2026-09-07).\n *\n * The label names its control. It did not once, and the control was a\n * *sibling* of the label with no `htmlFor`, so there was neither an explicit\n * nor an implicit association: a screen reader announced an unlabelled edit\n * box and clicking the label focused nothing (SHA-17). That fix used to be\n * ours \u2014 a context carrying a generated id, which every control had to opt\n * into by calling `useFieldControlId`. **Base UI does it now**, for its own\n * `Input`, `Checkbox`, `Select` and anything rendered through `Field.Control`,\n * so a new control is labelled by construction rather than by remembering.\n *\n * `helper` and `error` are the line under the control, which Ship built by\n * hand in two dialogs (`COMPONENTS-SHIP.md` F14) and Peek in four\n * (`COMPONENTS-PEEK.md` F14), always the same two class lists. **An error\n * replaces the helper rather than joining it** \u2014 that is what those callers\n * did (`pairError ?? 'Leave empty and\u2026'`), and a field that says both at once\n * is asking the reader to work out which one is live.\n *\n * The line is announced: Base UI wires `aria-describedby` for the helper and\n * `aria-invalid` + the error's id for the error, which the hand-built spans\n * never did.\n *\n * `required` is announced too, since 2026-09-08. It drew the asterisk and\n * nothing else \u2014 measured, the control carried neither `required` nor\n * `aria-required`, so the one thing the mark means never reached anybody who\n * could not see it. Base UI's `Field` has no `required` of its own, so this\n * puts `aria-required` on the control itself.\n */\nexport interface FieldProps {\n label: string\n /**\n * Draws the asterisk **and** marks the control `aria-required`, so the mark\n * means something to a reader who cannot see it. It reaches a single control\n * element; a `children` of several elements keeps the asterisk and owes its\n * own `aria-required`.\n */\n required?: boolean\n /**\n * A hint under the control \u2014 what the format is, what happens if it is left\n * empty. `caption`, muted. Hidden while `error` is set.\n */\n helper?: string\n /**\n * What is wrong, in the same place as the helper and in the error colour.\n * Setting it also marks the control invalid, so the caller no longer passes\n * `aria-invalid` itself.\n */\n error?: string\n children: ReactNode\n}\n\n/**\n * The two looks the line under a control wears, in one place so `Field` and\n * `FieldLine` cannot drift apart. `warning` is `FieldLine`'s alone \u2014 see there.\n */\nconst LINE_STYLES = {\n helper: 'text-caption text-text-muted',\n warning: 'text-caption text-warning-default',\n error: 'text-caption text-error-default',\n} as const\n\nexport function Field({ label, required = false, helper, error, children }: FieldProps) {\n\n /*\n The asterisk is a picture of `required`; this is the word for it. Base UI's\n `Field` has no `required` to propagate, so the control is marked here \u2014\n `aria-required` rather than the native attribute, because the native one\n also switches on the browser's own validation bubble, which no field in\n either app uses. A control that already says so keeps what it says.\n */\n const control =\n required && isValidElement(children)\n ? cloneElement(children as ReactElement<{ 'aria-required'?: boolean | 'true' | 'false' }>, {\n 'aria-required': (children as ReactElement<{ 'aria-required'?: boolean | 'true' | 'false' }>).props['aria-required'] ?? true,\n })\n : children\n return (\n <BaseField.Root invalid={!!error} className=\"flex flex-col gap-2\">\n {/* `cn`, like everywhere else. It was a template literal, with a comment\n saying the type token must never be merged \u2014 which stopped being true\n when `cn()` was taught the ramp: `input-label` is in it, and\n `cn.test.ts` pins that. */}\n <BaseField.Label className={cn('text-input-label text-text-primary', required && 'flex items-center')}>\n {label}\n {/* A picture, not a word: `aria-required` on the control says it, and\n without this the control was named \"Title*\" (Finding 38). */}\n {required && (\n <span aria-hidden=\"true\" className=\"text-error-default ml-0.5\">\n *\n </span>\n )}\n </BaseField.Label>\n {/*\n The control sits in the same place whether or not there is a line\n under it. It used to be wrapped only when there was one, and React\n then re-created the control the moment an error appeared or cleared \u2014\n so a person typing into a field whose error clears on input lost\n focus after the first keystroke (found by Ship's adoption, 2026-09-08,\n Finding 38). A one-child flex column draws exactly as the bare control\n did; the 6px stack Ship and Peek both wrote by hand appears only with a\n line.\n */}\n <div className=\"flex flex-col gap-1.5\">\n {control}\n {error != null ? (\n <BaseField.Error match className={LINE_STYLES.error}>\n {error}\n </BaseField.Error>\n ) : helper != null ? (\n <BaseField.Description className={LINE_STYLES.helper}>{helper}</BaseField.Description>\n ) : null}\n </div>\n </BaseField.Root>\n )\n}\n\n/**\n * The line on its own \u2014 the same small line `Field` draws under a control,\n * for the places where there is no single control to draw it under.\n *\n * **Why it is not a `Field`.** Three surfaces in Peek put this line under a\n * *group*: the rename row (a field and two buttons), a Folder's action\n * controls, another app's action controls. Each already has a section heading\n * above it, and `Field` comes with a label of its own \u2014 so wrapping the group\n * in one would either say the heading twice or restyle it. Found at step 4 of\n * Peek's adoption (`ADOPTION.md` B24); until this existed, all three spelled\n * `text-xs text-error-default` by hand.\n *\n * **It announces itself**, which is the half the hand-written spans never had.\n * These lines appear *after* something was done \u2014 a rename that the relay\n * refused, an action that failed \u2014 so a reader who cannot see them is told:\n * `role=\"alert\"` for an error, `role=\"status\"` for the rest. That is `Banner`'s\n * rule (2026-09-02), kept here so the two agree.\n *\n * **`warning` is the tone `Field` has not got**, and deliberately: `Field`'s\n * `error` also marks its control invalid, and a warning is not invalid \u2014 the\n * rename went through, and something about it needs saying. A group has no\n * single control to mark, so the distinction is free here and would not be\n * there.\n *\n * When the line belongs to one control, it is `Field`'s `helper` or `error`:\n * those are wired to the control with `aria-describedby` and `aria-invalid`,\n * which this cannot be.\n */\nexport type FieldLineTone = 'helper' | 'warning' | 'error'\n\nexport interface FieldLineProps {\n /** `helper` (muted) is a hint; `warning` (amber) and `error` (red) are outcomes. Default `helper`. */\n tone?: FieldLineTone\n children: ReactNode\n className?: string\n}\n\nexport function FieldLine({ tone = 'helper', children, className }: FieldLineProps) {\n return (\n <p role={tone === 'error' ? 'alert' : 'status'} className={cn(LINE_STYLES[tone], className)}>\n {children}\n </p>\n )\n}\n", "import type { CSSProperties } from 'react'\nimport { cn } from './cn'\n\n/**\n * Peek's Skeleton (2026-08-28), the generic parts only: the bar, a row\n * shaped like a list row, and a list of them. Peek's placeholders shaped\n * like a conversation card or a huddle card know what those are and stay\n * in Peek, built from the bar.\n *\n * A list reveals after 150ms (`animate-skeleton-in`, in the preset) so a\n * fast load never flashes a skeleton \u2014 Peek's rule.\n */\nexport function SkeletonBar({ className, style }: { className?: string; style?: CSSProperties }) {\n return <div className={cn('bg-bg-inset rounded-sm animate-pulse', className)} style={style} />\n}\n\nconst ROW_BAR_WIDTHS = [150, 100, 170, 120, 90, 140, 110, 160]\n\n/** A 32px row: a 16px square and a bar, like a row with a face and a name. */\nexport function SkeletonRow({ barWidth = 130 }: { barWidth?: number }) {\n return (\n <div className=\"flex items-center gap-2 px-2 h-[32px] rounded-lg\">\n <SkeletonBar className=\"w-4 h-4 shrink-0\" />\n <SkeletonBar className=\"h-3.5\" style={{ width: barWidth }} />\n </div>\n )\n}\n\n/** Rows of varied widths, so the placeholder does not read as a pattern. */\nexport function SkeletonList({ rows = 8, className }: { rows?: number; className?: string }) {\n return (\n <div className={cn('flex flex-col gap-0.5 animate-skeleton-in', className)} aria-hidden>\n {Array.from({ length: rows }, (_, i) => (\n <SkeletonRow key={i} barWidth={ROW_BAR_WIDTHS[i % ROW_BAR_WIDTHS.length]} />\n ))}\n </div>\n )\n}\n", "import type { ComponentPropsWithRef, ReactNode } from 'react'\nimport { cn } from './cn'\n\n/**\n * A word in a sentence that stands for something: a person, a place, a thing\n * written into running text. One line high, level with the words around it.\n *\n * The shape is Peek's inline chip (D67, Peek PR #206, 2026-09-13), class for\n * class, moved into the package so both apps draw one (UIG-27). Three things\n * changed on the way, all ruled by Katerina:\n *\n * - the person tone is `person`, not `mention` \u2014 every chip here is a\n * mention of something;\n * - the alignment is a class (`align-top h-[1.4em]`), not a `style` object,\n * so it is part of the class list like every other size in the package;\n * - a chip that leads somewhere takes `href` and `onClick`, the way `NavItem`\n * does: the package draws an anchor, the app's router takes the click.\n *\n * **Why `align-top` and `1.4em`.** `1.4em` is the body text's line height,\n * so the chip is exactly one line tall; `vertical-align: top` pins it to the\n * line box, which keeps the line at 19.6px and the chip's words level with\n * the sentence's, with or without an icon. Measured in Peek on 2026-09-13:\n * `text-bottom` made the line 21.2px and lifted the chip's words 1.6px;\n * `baseline` lifted them 3.8px once an icon came first.\n */\n\n/** The shape every tone shares. Exported for a rich-text editor that renders chips from strings. */\nexport const INLINE_CHIP_CLASSES =\n 'inline-flex h-[1.4em] items-center gap-1 rounded-sm px-1 mx-0.5 align-top text-body-2 font-normal select-none'\n\n/** What a chip is made of, by what it stands for. */\nexport const INLINE_CHIP_TONE_CLASSES = {\n /** A thing or a place \u2014 anything that is not a person. */\n neutral: 'bg-bg-active text-text-primary',\n /** A person. */\n person: 'bg-accent-muted text-accent-primary',\n /** A person, called urgently. */\n urgent: 'bg-warning-muted text-warning-default',\n /**\n * A reference nobody could resolve \u2014 still a mention, just an anonymous one.\n * Its height is the body line's 19.6px written out, because `1.4em` here is\n * the caption's em: 16.8px, with the letters 1.8px above the sentence's\n * baseline. At 19.6px they sit 0.4px off it (measured 2026-09-14; Peek's\n * copy of this chip still has the 16.8px box).\n */\n quiet: 'h-[19.6px] bg-bg-active text-text-muted font-mono text-caption',\n} as const\n\nexport type InlineChipTone = keyof typeof INLINE_CHIP_TONE_CLASSES\n\n/** The chip's classes for a tone, with anything the caller adds. For renderers that cannot use the component. */\nexport function inlineChipClassName(tone: InlineChipTone, className?: string) {\n return cn(INLINE_CHIP_CLASSES, INLINE_CHIP_TONE_CLASSES[tone], className)\n}\n\nexport interface InlineChipProps extends Omit<ComponentPropsWithRef<'a'>, 'children'> {\n /** What the chip stands for. Default `neutral`. */\n tone?: InlineChipTone\n /** Drawn before the label in a 16px box: a 16px icon, or a 14px one it centres. */\n icon?: ReactNode\n /**\n * Where the chip leads. With it the chip is an anchor; without it, a span.\n * A router app passes `onClick` too, and navigates there itself.\n */\n href?: string\n children: ReactNode\n}\n\nexport function InlineChip({ tone = 'neutral', icon, href, className, children, ...props }: InlineChipProps) {\n const body = (\n <>\n {icon && <span className=\"flex size-4 shrink-0 items-center justify-center text-text-secondary\">{icon}</span>}\n {children}\n </>\n )\n const classes = inlineChipClassName(tone, className)\n // Without somewhere to go it is a label, and a label is not a link.\n if (href === undefined) {\n return (\n <span className={classes} {...(props as ComponentPropsWithRef<'span'>)}>\n {body}\n </span>\n )\n }\n return (\n <a href={href} className={classes} {...props}>\n {body}\n </a>\n )\n}\n", "import { useCallback, useRef, type ReactNode } from 'react'\nimport { cn } from './cn'\nimport { Divider } from './Divider'\nimport { Menu, MenuItem, MenuPanel, MenuRow, MenuSection } from './Menu'\nimport { Person } from './Person'\nimport { PersonTrigger } from './PersonTrigger'\n\n/**\n * Who you are, and where you are \u2014 the top bar's identity trigger and the\n * menu it opens. Ship's IdentityMenu (2026-09-01), moved in whole: every app\n * needs one, and this one is already made of nothing but shared parts.\n *\n * The trigger is a face and a name, never a key (Ship's ruling A4) \u2014 or, with\n * `compact`, the face alone, as Peek's top bar draws it. Anonymous is a\n * silhouette and the word \"Anonymous\": the truth of the situation rather than\n * eight hex characters posing as a name.\n *\n * The menu keeps the two honest sentences, and the workspace line names the\n * relay because on this substrate the relay *is* the workspace. Every section\n * hides when the app cannot fill it: no `relayUrl`, no workspace section; no\n * `onCopyKey`, no copy item \u2014 only offer actions that can succeed. The one\n * place a key appears is as the *result* of \"Copy public key\" \u2014 this\n * component never receives the key, so it cannot show one; the caller\n * copies. Items are text only, no icons (her ruling).\n */\nexport interface Identity {\n /** From `kind:0`. Absent means anonymous. */\n name?: string\n picture?: string\n /** Known to Estiva ID, never published to the relay. */\n email?: string\n}\n\nexport interface IdentityMenuProps {\n me: Identity\n /** Signed in through Estiva ID, as opposed to a browser-held key. */\n signedIn: boolean\n /** Names the workspace; absent hides the workspace section. */\n relayUrl?: string\n /** Estiva ID's base URL when this build offers sign-in; absent = anonymous-only build. */\n idBase?: string\n /** \"Copy public key\" appears only when the app can supply one. */\n onCopyKey?: () => void\n onSignOut?: () => void\n /** Face-only trigger, 36px \u2014 no chevron, no name beside it. */\n compact?: boolean\n className?: string\n /**\n * App-specific rows, drawn as their own group between the workspace\n * section and the actions. A function receives `close`, so a row can\n * shut the menu before opening what it opens.\n */\n children?: ReactNode | ((close: () => void) => ReactNode)\n}\n\n/**\n * The menu alone, as a plain surface \u2014 what the docs canvases draw, and what\n * a surface that wants the rows without the menu can use.\n */\nexport interface IdentityPanelProps extends Omit<IdentityMenuProps, 'compact'> {\n /** Called when a row asks the menu to close. */\n onClose?: () => void\n}\n\nexport function IdentityPanel({ className, onClose = () => {}, ...rest }: IdentityPanelProps) {\n return (\n <MenuPanel className={cn('w-72', className)}>\n <IdentityRows {...rest} onClose={onClose} />\n </MenuPanel>\n )\n}\n\n/** The old name for the same thing. */\nexport const IdentityPanelSurface = IdentityPanel\n\n/**\n * The panel's contents, without the menu around them.\n *\n * Split out at stage 4 for the same reason `MenuPanel` was split out of\n * `Menu`: a real menu portals and places itself against a trigger, so it\n * cannot stand in a docs page, and the canvases must still show the artifact\n * (Katerina, D25). The stories draw these rows on a `MenuPanel`; the app gets\n * them inside a `Menu`. One definition either way.\n *\n * Not exported from the package \u2014 `IdentityMenu` and `IdentityPanel` are the\n * API; this is how they are built.\n */\nexport function IdentityRows({ me, signedIn, relayUrl, idBase, onCopyKey, onSignOut, onClose, children }: Omit<IdentityPanelProps, 'className'> & { onClose: () => void }) {\n const act = (action: () => void) => () => {\n onClose()\n action()\n }\n const appRows = typeof children === 'function' ? children(onClose) : children\n return (\n <>\n <MenuSection label={signedIn ? 'Signed in as' : 'Acting as'}>\n <MenuRow>\n <Person name={me.name} picture={me.picture} fallback=\"Anonymous\" size={28} className=\"text-body-2-strong\" />\n </MenuRow>\n <Note>\n {signedIn\n ? 'Your Estiva ID. The same person you are in every Estiva app.'\n : 'A key held by this browser only. Nobody else knows who you are.'}\n </Note>\n {me.email && <Note>{me.email} \u00B7 known to Estiva, never published to the relay</Note>}\n </MenuSection>\n\n {relayUrl && (\n <>\n <Divider className=\"mx-0 my-2\" />\n <MenuSection label=\"Workspace\">\n <MenuRow>\n <span className=\"break-all font-mono text-caption text-text-primary\">{relayUrl}</span>\n </MenuRow>\n <Note>Everyone on this relay shares this workspace, in every app.</Note>\n </MenuSection>\n </>\n )}\n\n {appRows && (\n <>\n <Divider className=\"mx-0 my-2\" />\n {appRows}\n </>\n )}\n\n {(signedIn && idBase) || onCopyKey || (idBase && onSignOut) ? <Divider className=\"mx-0 my-2\" /> : null}\n\n {signedIn && idBase && (\n <MenuItem\n label=\"Edit your profile in Estiva ID\"\n onClick={act(() => window.open(idBase, '_blank', 'noopener,noreferrer'))}\n />\n )}\n {onCopyKey && <MenuItem label=\"Copy public key\" onClick={act(onCopyKey)} />}\n {idBase && onSignOut && <MenuItem label=\"Sign out\" onClick={act(onSignOut)} />}\n </>\n )\n}\n\nexport function IdentityMenu({ me, signedIn, relayUrl, idBase, onCopyKey, onSignOut, compact = false, className, children }: IdentityMenuProps) {\n /*\n * The menu owns the trigger (stage 4, 2026-09-08). It used to be the other\n * way round \u2014 this component held `open`, and the panel hung from a ref to\n * the wrapper \u2014 and four separate defects came from Base UI not knowing\n * which element opened it: the face stopped closing its own menu, opening\n * from the keyboard highlighted nothing, a hovered submenu row unmounted\n * the panel, and the panel hung from a wrapper the app's layout could\n * stretch. `PLAN.md` \u00A76.2 has the measurements.\n */\n const actions = useRef<{ close: () => void; unmount: () => void } | null>(null)\n const close = useCallback(() => actions.current?.close(), [])\n return (\n /* The wrapper carries the caller's `className` and nothing else. It used\n to be `relative`, because the panel was positioned against it; the panel\n hangs from the trigger and portals now, so a positioning context here\n would only be a lie about what this box does. */\n <div className={className}>\n <Menu\n align=\"right\"\n actionsRef={actions}\n className=\"w-72\"\n trigger={\n <PersonTrigger\n name={me.name}\n picture={me.picture}\n fallback=\"Anonymous\"\n compact={compact}\n size={compact ? 36 : undefined}\n /* The row shape is named by its own text \u2014 the person. Only the\n bare face needs a label; naming the row would override the\n person's name as the accessible name (Ship's tests find the\n trigger by it). */\n aria-label={compact ? 'Account menu' : undefined}\n />\n }\n >\n <IdentityRows\n me={me}\n signedIn={signedIn}\n relayUrl={relayUrl}\n idBase={idBase}\n onCopyKey={onCopyKey}\n onSignOut={onSignOut}\n onClose={close}\n >\n {children}\n </IdentityRows>\n </Menu>\n </div>\n )\n}\n\nfunction Note({ children }: { children: ReactNode }) {\n return <span className=\"px-2 pb-1.5 text-caption text-text-secondary\">{children}</span>\n}\n", "import { Separator } from '@base-ui/react/separator'\nimport { cn } from './cn'\n\n/**\n * Peek's Divider (2026-08-28): a hairline in `border-subtle`, inset 12px each\n * side. Plus what Ship added: `orientation=\"vertical\"` \u2014 the same hairline\n * standing up, stretching to its row's height, no inset.\n *\n * On Base UI's `Separator` since stage 6 of the migration (2026-09-14). It\n * writes the `separator` role and `aria-orientation` this component used to\n * write by hand, and adds `data-orientation`. Nothing it draws changed.\n */\nexport interface DividerProps {\n orientation?: 'horizontal' | 'vertical'\n /**\n * Words in the middle of the line \u2014 \"New since you last read this\", a\n * date. Horizontal only; the line splits around the label.\n */\n label?: string\n /**\n * `warning` for a line that asks for attention: the label in the warning\n * colour, the lines in its wash. Peek and Ship both drew their \"new since\n * you last read\" rule in the accent, which measures 3.3:1 on Ship's\n * background and could not be read (Katerina, 2026-09-08); the warning\n * colour measures 9.6:1 there. Default: the muted text, `border-subtle`\n * lines, for a date.\n */\n tone?: 'default' | 'warning'\n className?: string\n}\n\nexport function Divider({ orientation = 'horizontal', label, tone = 'default', className }: DividerProps) {\n if (label && orientation === 'horizontal') {\n const line = tone === 'warning' ? 'bg-warning-muted' : 'bg-border-subtle'\n return (\n <Separator\n orientation=\"horizontal\"\n aria-label={label}\n className={cn('flex shrink-0 items-center gap-2 mx-3', className)}\n >\n <span aria-hidden=\"true\" className={cn('h-px flex-1', line)} />\n <span className={cn('shrink-0 text-caption', tone === 'warning' ? 'text-warning-default' : 'text-text-muted')}>{label}</span>\n <span aria-hidden=\"true\" className={cn('h-px flex-1', line)} />\n </Separator>\n )\n }\n return (\n <Separator\n orientation={orientation}\n // shrink-0 on both orientations: a 1px flex child in an overflowing\n // column shrinks to nothing, and a hairline that renders 0px tall is a\n // hairline nobody can see \u2014 Peek's `/` menu had been drawing two of\n // them, measured 0px, since it was built (2026-09-05).\n className={cn('shrink-0 bg-border-subtle', orientation === 'horizontal' ? 'h-px mx-3' : 'w-px self-stretch', className)}\n />\n )\n}\n", "import { cn } from './cn'\nimport { Avatar } from './Avatar'\n\n/**\n * A face beside a name. Ship's Person (2026-09-01), which was Peek's minus\n * the projection type it took its profile from: this one is handed the two\n * strings and nothing else, so it cannot be handed a key. Peek keeps a\n * wrapper that resolves the profile, the way its Avatar wrapper resolves the\n * picture.\n *\n * A face beside a name needs no caption (Peek's rulings), and someone nobody\n * has published a name for gets the `fallback` \u2014 an em dash by default, the\n * same mark a property with no value uses \u2014 rather than the key back on\n * screen to say it. Pass \"Anonymous\" where the unnamed person is *you*, and\n * you know who that is.\n *\n * The text size is the caller's, so the face and the words are always set\n * together \u2014 see the Sizes story.\n */\nexport interface PersonProps {\n name?: string\n picture?: string\n /** Shown in place of a missing name. */\n fallback?: string\n size?: number\n className?: string\n}\n\nexport function Person({ name, picture, fallback = '\u2014', size = 20, className }: PersonProps) {\n return (\n // gap-2: the face-to-name distance is one rule (8px) wherever a person\n // appears \u2014 Katerina, 2026-09-01, set on Person so PersonTrigger and every\n // row agree by construction.\n <span className={cn('flex min-w-0 items-center gap-2', className)}>\n <Avatar name={name} src={picture} size={size} />\n <span className={cn('truncate', !name && 'text-text-muted')}>{name ?? fallback}</span>\n </span>\n )\n}\n", "import type { ComponentPropsWithRef } from 'react'\nimport { Button as BaseButton } from '@base-ui/react/button'\nimport { IconChevronDown } from '@tabler/icons-react'\nimport { cn } from './cn'\nimport { Avatar } from './Avatar'\nimport { Person, type PersonProps } from './Person'\n\n/**\n * The person, as the button that opens the account menu. On Base UI's Button\n * since stage 2 of the migration (2026-09-07).\n *\n * Two shapes, one component (Katerina, 2026-09-01):\n *\n * - the row \u2014 face \u00B7 name \u00B7 chevron on a 32px button with a hover fill, as\n * Ship's top bar draws it. The chevron is 14px, the small-control size.\n * - `compact` \u2014 the face alone, as Peek's top bar draws it: no chevron, no\n * padding, and no hover fill, because the face fills the whole control and\n * a fill would have nowhere to show.\n *\n * What it opens \u2014 the menu and everything in it \u2014 stays in the app.\n */\nexport interface PersonTriggerProps\n extends Omit<PersonProps, 'className'>,\n Omit<ComponentPropsWithRef<'button'>, 'children'> {\n /** Whether what it opens is open \u2014 the row shape holds its hover fill while so. */\n open?: boolean\n /** Face only \u2014 no chevron, no hover fill. Defaults the face to 36px; the row shape to 22px. */\n compact?: boolean\n}\n\nexport function PersonTrigger({ name, picture, fallback, size, open = false, compact = false, className, ...props }: PersonTriggerProps) {\n if (compact) {\n return (\n <BaseButton\n type=\"button\"\n aria-haspopup=\"menu\"\n aria-expanded={open}\n /*\n The face alone is an icon-only control, so it owes a name. It has\n one already \u2014 the person \u2014 and taking it from there means no caller\n can forget: measured 2026-09-08, this button announced **\"AD\"**, the\n initials, with no `aria-label` anywhere. A caller that wants to name\n the destination rather than the person still can, and `IdentityMenu`\n does (\"Account menu\").\n */\n aria-label={props['aria-label'] ?? name ?? fallback}\n /*\n No `focus:outline-none` here any more. It removed the browser's ring\n and put nothing in its place, so this control \u2014 the account trigger\n in Peek's top bar \u2014 had **no visible focus at all** (measured\n 2026-09-08: `outline: solid 2px rgba(0,0,0,0)`, no shadow). Left\n alone it wears the same ring `Button`, `IconButton` and a `Tab`\n wear, which is the ring the rest of the package already relies on.\n */\n className={cn('cursor-pointer rounded-full', className)}\n {...props}\n >\n <Avatar name={name} src={picture} size={size ?? 36} />\n </BaseButton>\n )\n }\n return (\n <BaseButton\n type=\"button\"\n aria-haspopup=\"menu\"\n aria-expanded={open}\n className={cn(\n 'flex h-8 cursor-pointer items-center gap-1.5 rounded-md pl-1.5 pr-1.5 text-body-2 text-text-primary transition-colors hover:bg-bg-hover',\n open && 'bg-bg-hover',\n // Under a `Menu` the open state is Base UI's, not a prop: the trigger\n // carries `data-popup-open` while its menu is up, and sets its own\n // `aria-expanded`. Both spellings hold the fill, so this works whether\n // the caller drives it or the menu does.\n 'data-[popup-open]:bg-bg-hover',\n className,\n )}\n {...props}\n >\n <Person name={name} picture={picture} fallback={fallback} size={size ?? 22} />\n <IconChevronDown size={14} stroke={1.5} className=\"shrink-0 text-text-muted\" />\n </BaseButton>\n )\n}\n", "import { IconCheck, IconChevronDown } from '@tabler/icons-react'\nimport { Select as BaseSelect } from '@base-ui/react/select'\nimport type { ReactNode } from 'react'\nimport { cn } from './cn'\nimport { ScrollArea } from './ScrollArea'\nimport { MenuPanel, menuItemClassName } from './Menu'\n\n/**\n * Peek's Select (2026-08-28), verbatim, plus what Ship added: an option may\n * carry a `leading` node \u2014 an avatar beside a person's name \u2014 shown in the\n * trigger and in the list. On Base UI's `Select` since stage 4 of the\n * migration (2026-09-07).\n *\n * A button that shows the choice and opens a listbox under it. **Under it:\n * Base UI would rather lay the list over the trigger so the chosen option's\n * text covers the trigger's, the way macOS does \u2014 `alignItemWithTrigger` is\n * off, because every Select in both apps opens below one today (Katerina,\n * D24).**\n *\n * What the port deleted: `createPortal`, the whole `onKeyDown` switch, the\n * outside-click, resize and scroll listeners, the `aria-activedescendant`\n * bookkeeping, the `scrollIntoView` that kept the highlight visible, and\n * `fit.ts` \u2014 the pure geometry this component grew and then shared with the\n * Menu shell. Floating UI does the fitting; `--available-height` is where the\n * old 288px cap now meets the room the screen actually has.\n *\n * What it gained, and none of it was written here: **typeahead** \u2014 type the\n * first letters of an option and the list jumps to it \u2014 a listbox whose\n * highlight is managed rather than counted, and a value that can be part of a\n * form.\n *\n * Two sizes; `disabled` explains nothing by itself \u2014 wrap it in a tooltip\n * that does.\n */\nexport interface SelectOption {\n value: string\n label: string\n /** Drawn before the label, 16px \u2014 an Avatar, an icon. */\n leading?: ReactNode\n}\n\nexport interface SelectProps {\n value: string\n onChange: (value: string) => void\n options: SelectOption[]\n size?: 'default' | 'small'\n ariaLabel?: string\n /** Set by a `Field` with `required`; a caller inside one owes nothing. */\n 'aria-required'?: boolean | 'true' | 'false'\n placeholder?: string\n disabled?: boolean\n className?: string\n}\n\n/** The 4px between the trigger and the list, and the 8px the list keeps clear\n * of every screen edge \u2014 the two numbers `fitMenu` used. */\nconst GAP = 4\nconst VIEWPORT_PAD = 8\n\nexport function Select({ value, onChange, options, size = 'default', ariaLabel, placeholder = 'Select\u2026', disabled, className, ...aria }: SelectProps) {\n const selected = options.find((o) => o.value === value)\n return (\n <BaseSelect.Root\n value={value}\n onValueChange={(next) => onChange(next as string)}\n disabled={disabled}\n // Non-modal, as it has always been: the page behind stays scrollable and\n // keeps its scrollbar, so opening a select never shifts the layout.\n modal={false}\n >\n <BaseSelect.Trigger\n aria-label={ariaLabel}\n aria-required={aria['aria-required']}\n className={cn(\n /*\n * `min-w-0 max-w-full`: a trigger must never outgrow its container\n * (Katerina, 2026-09-01 \u2014 a long label stretched the files panel's\n * Lead row past its card). In a flex row the default min-width:auto\n * forbids shrinking below the label's width, which is what kept\n * `truncate` from ever engaging; in a block container max-w-full is\n * the cap. Full-width callers are unaffected.\n */\n 'flex w-full min-w-0 max-w-full items-center justify-between gap-2 rounded-lg border bg-bg-inset text-left',\n 'border-border-default text-text-primary outline-none transition-colors',\n // The focused border survives a hover: hover alone strengthens the\n // hairline, but hover while focused must not grey the focus colour \u2014\n // the stacked variant outranks plain hover by specificity.\n 'hover:border-border-strong focus-visible:hover:border-border-focus aria-expanded:hover:border-border-focus disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled',\n 'focus-visible:border-border-focus aria-expanded:border-border-focus',\n 'signal:transition-shadow signal:focus-visible:shadow-focus-ring',\n size === 'default' && 'px-3 py-2 text-input-value',\n size === 'small' && 'h-6 px-2 text-caption',\n className,\n )}\n >\n <span className={cn('flex min-w-0 items-center gap-2', !selected && 'text-text-muted')}>\n {selected?.leading && <span className=\"flex shrink-0 items-center\">{selected.leading}</span>}\n <BaseSelect.Value className=\"truncate\">{() => selected?.label ?? placeholder}</BaseSelect.Value>\n </span>\n <BaseSelect.Icon\n render={<IconChevronDown size={size === 'small' ? 14 : 16} stroke={1.5} className=\"shrink-0 text-text-secondary\" />}\n />\n </BaseSelect.Trigger>\n\n <BaseSelect.Portal>\n <BaseSelect.Positioner\n side=\"bottom\"\n align=\"start\"\n sideOffset={GAP}\n collisionPadding={VIEWPORT_PAD}\n /* D24: hang the list under the trigger, as it always has, rather\n than laying it over the trigger with the chosen option on top. */\n alignItemWithTrigger={false}\n /*\n * The list follows its trigger now rather than closing when the page\n * scrolls \u2014 it was `fixed` to where the trigger had been, so closing\n * was the only way it could avoid being left behind. `anchor-hidden`\n * is the other half of that: when the trigger scrolls out of view\n * entirely the list goes with it, instead of floating over whatever\n * has scrolled into its place.\n */\n className=\"z-50 data-[anchor-hidden]:hidden\"\n >\n <BaseSelect.Popup\n /* `--anchor-width` is the trigger's width and `--available-height`\n the room the list actually has after Floating UI has flipped and\n clamped it \u2014 the two numbers `fitMenu` used to compute here. The\n 288px is the old `max-h-72`, now a ceiling on that room rather\n than a height applied blind. */\n /* The list is the package's one list (Katerina, 2026-09-13: \"i\n thought the type to search menu would be from estiva-ui and be\n the one used in select component\"). The box is `MenuPanel` \u2014\n the same border, fill, radius and shadow this used to spell out\n \u2014 with its padding moved onto the scrolling content (D63). */\n render={<MenuPanel />}\n className=\"min-w-[var(--anchor-width)] p-0\"\n >\n {/* The list scrolls in a ScrollArea: the bar takes no width, so a\n long list is exactly as wide as a short one (Katerina,\n 2026-09-08).\n *\n * **The padding is on the scrolling content, not on the panel**\n * (D63, applied here 2026-09-13). On the panel it inset the\n * scrolling box, so the thumb sat 7px from the panel's edge where\n * DialogShell, Popover, Menu and ChipInput all draw it at 3px \u2014\n * Katerina: \"the position of the scrollbar in select \u2026 not closer\n * to the right side\". The rows keep their 4px inset, because the\n * padding that was the panel's is the content's; and the cap loses\n * its `- 0.5rem`, because that padding is inside the box that\n * scrolls now, so 288px stays 288px. */}\n <ScrollArea viewportClassName=\"max-h-[min(288px,var(--available-height))]\" contentClassName=\"flex flex-col p-2\">\n {options.map((option) => (\n <BaseSelect.Item\n key={option.value}\n value={option.value}\n /* The highlight is Base UI's `data-highlighted` \u2014 pointer and\n keyboard set the same attribute, so what the DOM says and\n what the row looks like cannot disagree. It used to be an\n index this component counted. */\n /* A menu row, exactly (`menuItemClassName`): 36px floor, 8px\n in from a panel padded 8px, so the label lands 17px from the\n panel's edge \u2014 the same pixel as the old 4px + 12px. What\n Select adds is its own: the \u2713 at the end, and the chosen\n row in medium weight. No fade on the highlight, as in every\n menu (Katerina, 2026-09-05). */\n className={cn(menuItemClassName({ size: 'default' }), 'justify-between data-[selected]:font-medium')}\n >\n <span className=\"flex min-w-0 items-center gap-2\">\n {option.leading && <span className=\"flex shrink-0 items-center\">{option.leading}</span>}\n <BaseSelect.ItemText className=\"truncate text-body-2 text-text-primary\">{option.label}</BaseSelect.ItemText>\n </span>\n <BaseSelect.ItemIndicator\n render={<IconCheck size={16} stroke={1.5} className=\"shrink-0 text-text-secondary\" />}\n />\n </BaseSelect.Item>\n ))}\n </ScrollArea>\n </BaseSelect.Popup>\n </BaseSelect.Positioner>\n </BaseSelect.Portal>\n </BaseSelect.Root>\n )\n}\n", "import { forwardRef, type InputHTMLAttributes } from 'react'\nimport { Input } from '@base-ui/react/input'\nimport { cn } from './cn'\n\n/**\n * Peek's TextInput (2026-08-28): the inset field with a 8px radius, 14px\n * text, the focus border in every theme (Katerina, 2026-08-28: Ship's inputs\n * focus like Signal's) \u2014 and Signal's glow on top.\n * Plus a disabled look (Ship's addition): the disabled surface and text,\n * no pointer. On Base UI's `Input` since stage 3 of the migration\n * (2026-09-07).\n *\n * Base UI's `Input` is a native `<input>` that finds a surrounding `Field` on\n * its own, so the id plumbing this used to do \u2014 `useFieldControlId`, and the\n * context behind it \u2014 is gone. Outside a `Field` it behaves as before: the\n * field context has a default, so nothing has to be wrapped.\n */\nexport interface TextInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size'> {\n /**\n * `small` is 24px tall with 12px text \u2014 the small `Select`'s size, so a\n * field can share a dense row with small selects (D67, 2026-09-13: Peek's\n * reference widget hand-built one because this was 38px). Replaces the\n * native `size` attribute, which counts characters and which no caller\n * used; set a width with the layout instead.\n */\n size?: 'default' | 'small'\n}\n\nexport const TextInput = forwardRef<HTMLInputElement, TextInputProps>(function TextInput({ className, type = 'text', size = 'default', ...props }, ref) {\n return (\n <Input\n ref={ref}\n type={type}\n className={cn(\n 'bg-bg-inset border border-border-default focus:border-border-focus rounded-lg',\n // The small size is the small Select's trigger, class for class.\n size === 'default' && 'px-3 py-2 text-input-value',\n size === 'small' && 'h-6 px-2 text-caption',\n 'text-text-primary placeholder:text-text-muted',\n 'outline-none transition-colors',\n 'disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled',\n 'signal:transition-shadow signal:focus:shadow-focus-ring',\n className,\n )}\n {...props}\n />\n )\n})\n", "import { forwardRef, type TextareaHTMLAttributes } from 'react'\nimport { Field as BaseField } from '@base-ui/react/field'\nimport { cn } from './cn'\n\n/**\n * Peek's Textarea (2026-08-28), verbatim: TextInput's look on a textarea\n * that does not resize. Plus a disabled look (Ship's addition). On Base UI's\n * `Field.Control` since stage 3 of the migration (2026-09-07).\n *\n * Base UI has an `Input` part but no textarea, so this is `Field.Control`\n * rendering a `<textarea>` \u2014 the same part `Input` is built on, told which\n * element to be. It finds a surrounding `Field` on its own, which is what\n * retired `useFieldControlId`; outside one it renders a plain textarea, as\n * before.\n */\nexport type TextareaProps = TextareaHTMLAttributes<HTMLTextAreaElement>\n\nexport const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(function Textarea({ className, ...props }, ref) {\n return (\n <BaseField.Control\n ref={ref as React.Ref<HTMLElement>}\n render={<textarea />}\n className={cn(\n 'bg-bg-inset border border-border-default focus:border-border-focus rounded-lg px-3 py-2',\n 'text-input-value text-text-primary placeholder:text-text-muted',\n 'resize-none outline-none transition-colors',\n 'disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled',\n 'signal:transition-shadow signal:focus:shadow-focus-ring',\n className,\n )}\n {...(props as object)}\n />\n )\n})\n", "import { useState, type ReactNode } from 'react'\nimport { Button } from './Button'\nimport { DialogShell } from './DialogShell'\n\n/**\n * A question with two answers, before something a person would want back \u2014\n * Ship's ConfirmDialog (2026-09-01), verbatim. Its own comment always called\n * it a package candidate: nothing here knows what is being confirmed.\n *\n * The confirm button is `destructive` when the action is (a deletion, an\n * archive); `primary` otherwise. While the action runs the buttons wait;\n * an action that resolves `false` keeps the dialog open, so the caller's\n * banner can say why.\n *\n * On Base UI's `AlertDialog` since stage 3 of the migration (2026-09-07),\n * through `DialogShell`'s `alert`. **A press on the backdrop no longer\n * closes it** (Katerina, D20): a destructive question is answered, not\n * clicked away. Escape and the close button still cancel, as they did.\n */\nexport interface ConfirmDialogProps {\n title: string\n /** The body \u2014 plain sentences, body-2. */\n children: ReactNode\n confirmLabel: string\n destructive?: boolean\n onConfirm: () => Promise<boolean | void> | boolean | void\n onClose: () => void\n}\n\nexport function ConfirmDialog({ title, children, confirmLabel, destructive = false, onConfirm, onClose }: ConfirmDialogProps) {\n const [busy, setBusy] = useState(false)\n const confirm = async () => {\n setBusy(true)\n try {\n const ok = await onConfirm()\n if (ok !== false) onClose()\n } finally {\n setBusy(false)\n }\n }\n return (\n <DialogShell\n alert\n title={title}\n onClose={onClose}\n bodyClassName=\"flex flex-col gap-3 text-body-2 text-text-primary\"\n footer={\n <>\n <Button variant=\"muted\" onClick={onClose} disabled={busy}>\n Cancel\n </Button>\n <Button variant={destructive ? 'destructive' : 'primary'} onClick={() => void confirm()} disabled={busy}>\n {confirmLabel}\n </Button>\n </>\n }\n >\n {children}\n </DialogShell>\n )\n}\n", "import { useRef, type ReactNode } from 'react'\nimport { Dialog } from '@base-ui/react/dialog'\nimport { AlertDialog } from '@base-ui/react/alert-dialog'\nimport { IconX } from '@tabler/icons-react'\nimport { cn } from './cn'\nimport { IconButton } from './IconButton'\nimport { ScrollArea } from './ScrollArea'\n\n/**\n * Peek's DialogShell (2026-08-28), verbatim: the portal, the backdrop, the\n * 502px card and its chrome \u2014 a 48px header with the title and a close\n * button, a body, a 48px footer for the buttons. A dialog is just what goes\n * in the three slots. On Base UI's `Dialog` since stage 3 of the migration\n * (2026-09-07).\n *\n * **What that fixed, and this page used to say it did not:** focus is trapped\n * inside the card, so Tab cannot walk out into the page behind it, and it\n * returns to whatever opened the dialog when it closes. Both were missing and\n * both were written down as missing. The portal, the outside press and the\n * Escape key are Base UI's too \u2014 the `keydown` listener this file kept on\n * `document` is gone, and with it the bug where two open dialogs both closed\n * on one Escape.\n *\n * The card is mounted only while it is open, as before: this component has no\n * `open` prop, and a caller renders it or does not. So `open` is `true` and\n * `onOpenChange` reports the ways Base UI closes it \u2014 Escape, a press outside,\n * the close button \u2014 through the one `onClose` the callers already pass.\n *\n * The DOM is the same shape it was, deliberately: a backdrop, then a\n * full-screen flex layer that centres the card. Base UI positions nothing for\n * a dialog, so keeping the layer is what keeps the pixels.\n */\nexport interface DialogShellProps {\n /** Labels the dialog for assistive tech, and renders as the header text\n * unless `headerContent` replaces it. */\n title: string\n onClose: () => void\n /** Replaces the title text in the header \u2014 a back button beside the title,\n * a count chip after it. The close button stays. */\n headerContent?: ReactNode\n /** Absent: no footer row, and the body keeps the card's own bottom edge\n * (a roster that simply ends). */\n footer?: ReactNode\n children: ReactNode\n /** Extra classes on the body: its layout and any padding override (`flex flex-col gap-6`, `p-0 py-2`). */\n bodyClassName?: string\n /**\n * How tall the body may get before it scrolls, as the cap class \u2014\n * `max-h-[400px]`, `max-h-[70vh]`. Setting it makes the body a `ScrollArea`,\n * so a long roster or a tall form scrolls in the package's bar rather than\n * the browser's (D40, ADOPTION B19).\n *\n * Without it the body is what it always was and grows to its content, so no\n * dialog written before 0.12.2 moves a pixel. A `bodyClassName` carrying\n * `overflow-y-auto` and no cap never scrolled anything \u2014 the box grew \u2014 and\n * is the case this replaces.\n */\n bodyMaxHeight?: string\n width?: number\n /**\n * A question that has to be answered rather than clicked away: a press on\n * the backdrop no longer closes it, and it announces itself as an alert.\n * Escape and the close button still work. `ConfirmDialog` is this\n * (Katerina, D20, 2026-09-07); a form or a roster is not.\n */\n alert?: boolean\n}\n\nexport function DialogShell({ title, onClose, headerContent, footer, children, bodyClassName, bodyMaxHeight, width = 502, alert = false }: DialogShellProps) {\n // The two families are the same parts with different dismiss rules, so the\n // chrome below is written once. AlertDialog re-exports Dialog's Backdrop,\n // Popup, Portal and Title types, which is why this substitutes cleanly.\n const Parts = alert ? AlertDialog : Dialog\n const popupRef = useRef<HTMLDivElement>(null)\n return (\n <Parts.Root\n open\n onOpenChange={(open) => {\n if (!open) onClose()\n }}\n >\n <Parts.Portal>\n {/* Backdrop */}\n <Parts.Backdrop className=\"fixed inset-0 z-40 bg-scrim\" />\n\n {/* Dialog */}\n <div className=\"fixed inset-0 z-50 flex items-center justify-center pointer-events-none\">\n <Parts.Popup\n ref={popupRef}\n /*\n Focus the card, not the first control in it.\n\n Base UI's default is the first tabbable element, which here is the\n \u2715 in the header \u2014 so every dialog opened with a visible ring on\n its close button, which the screenshot diff caught as a 28px\n square nobody had asked for. Focusing the card arms the trap and\n lets a screen reader read the dialog, with no control lit up.\n\n A field with `autoFocus` still wins: React focuses it while the\n popup mounts, and Base UI does not move focus that has already\n landed inside. Both dialogs in Ship rely on that.\n */\n initialFocus={popupRef}\n /* The header names it when the header is the title. When\n `headerContent` replaces that text there is nothing to point at,\n so the name is spelled instead \u2014 as this component always did. */\n aria-label={headerContent != null ? title : undefined}\n /* `outline-none` because the card is a programmatic focus target,\n not something a keyboard user tabs to: without it Chrome rings\n the whole 502px card on open, which the diff caught the moment\n `initialFocus` moved off the \u2715. The controls inside keep their\n own focus styling. */\n className=\"bg-bg-elevated border border-border-subtle rounded-lg shadow-lg pointer-events-auto flex flex-col overflow-hidden outline-none\"\n style={{ width }}\n >\n {/* Header */}\n <div className=\"h-12 flex items-center justify-between pl-5 pr-4 border-b border-border-subtle shrink-0\">\n {headerContent ?? (\n <Parts.Title className=\"text-h4 text-text-primary\" render={<span />}>\n {title}\n </Parts.Title>\n )}\n {/*\n The \u2715 IS the `Close` part now, rather than a button that calls\n `onClose` beside one (stage 4, 2026-09-07). Stage 3 could not do\n this: an `IconButton` carrying a `tooltip` returned the tooltip\n wrapper `<div>` as its root, so the part composed onto the\n wrapper and not the button. Porting Tooltip removed the wrapper\n \u2014 the trigger is the button itself \u2014 and the composition works.\n\n What it buys: the dialog closes through its own state machine,\n so the \u2715, Escape and the outside press are one path with one\n reason attached, instead of one of the three going around.\n */}\n <Parts.Close\n render={\n <IconButton tooltip=\"Close\" aria-label=\"Close\">\n <IconX size={16} stroke={1.5} />\n </IconButton>\n }\n />\n </div>\n\n {/* Body. With a cap it scrolls in the package's bar: the cap goes\n on the viewport (ScrollArea's rule \u2014 on the region the viewport\n grows to its content and nothing scrolls), and the padding and\n the caller's layout go on the content, so the bar is drawn over\n the padding rather than beside it. */}\n {bodyMaxHeight ? (\n <ScrollArea\n className={cn(footer != null && 'border-b border-border-subtle')}\n viewportClassName={bodyMaxHeight}\n contentClassName={cn('pl-5 pr-4 py-4', bodyClassName)}\n >\n {children}\n </ScrollArea>\n ) : (\n <div className={cn('pl-5 pr-4 py-4', footer != null && 'border-b border-border-subtle', bodyClassName)}>{children}</div>\n )}\n\n {/* Footer */}\n {footer != null && <div className=\"h-12 flex items-center justify-end gap-2 pl-5 pr-4 shrink-0\">{footer}</div>}\n </Parts.Popup>\n </div>\n </Parts.Portal>\n </Parts.Root>\n )\n}\n", "import { useEffect, useRef, useState, type KeyboardEvent, type ReactNode, type RefObject } from 'react'\nimport { Field as BaseField } from '@base-ui/react/field'\nimport { Input } from '@base-ui/react/input'\nimport { cn } from './cn'\n\n/**\n * Text you click to edit \u2014 Ship's EditableText (2026-09-01), verbatim. Its\n * own comment always called it a package candidate: nothing here knows what\n * is being edited.\n *\n * The editing state is Base UI's `Input` \u2014 or `Field.Control` as a\n * `<textarea>` when multiline \u2014 since stage 3 of the migration (2026-09-07).\n * The read state, the draft, and every rule about committing stay here:\n * Base UI has no opinion about what an edit means, and this component is\n * nothing but that opinion.\n *\n * Reads as text until clicked; then it is a field. Enter commits (Shift+Enter\n * is a new line when multiline), Escape cancels, blur commits. A commit that\n * fails keeps the field open with the text in it, so an edit is never\n * silently lost \u2014 the caller has already said why. An unchanged value is not\n * committed at all. Empty shows the placeholder, muted.\n */\nexport interface EditableTextProps {\n value: string\n placeholder: string\n /** Resolve `true` when saved; `false` (or throw) keeps the field open with the text. */\n onCommit: (value: string) => Promise<boolean> | boolean\n multiline?: boolean\n /** Applied to both the text and the field, so they are the same size. */\n className?: string\n /** The accessible name of the field \u2014 \"Title\", \"Description\". */\n label: string\n /**\n * What to *show* when not editing, when that differs from what is edited \u2014\n * a value carrying a raw reference that the caller renders as a live object\n * below the text, say.\n *\n * It overrides the display branch only. `value` is still what the editor\n * opens with and what a commit compares against, so nothing is silently lost\n * \u2014 which is the failure a naive `value={stripped}` would cause.\n */\n display?: string\n /**\n * What to *draw* when not editing, when the value is structured rather than\n * a line of prose.\n *\n * A node, not a string, because a heading and a bullet are elements. Added\n * for a rich text field (SPEC \u00A713): the app renders the parsed body and\n * hands the result in.\n *\n * `display` stays the **string**, and stays what decides emptiness \u2014 so a\n * blank field still shows its placeholder rather than an empty element. Like\n * `display`, this overrides the display branch only: `value` is what the\n * editor opens with and what a commit compares against, so what is edited is\n * unchanged.\n *\n * `whitespace-pre-wrap` is dropped when this is set. Structured content\n * carries its own line breaks, and preserving the source's as well doubles\n * every one of them.\n */\n displayNode?: ReactNode\n /** Show the value only \u2014 no edit affordance. For a reader who cannot write. */\n readOnly?: boolean\n}\n\nexport function EditableText({ value, display, displayNode, placeholder, onCommit, multiline = false, className, label, readOnly = false }: EditableTextProps) {\n const [editing, setEditing] = useState(false)\n const [draft, setDraft] = useState(value)\n const [busy, setBusy] = useState(false)\n const fieldRef = useRef<HTMLInputElement | HTMLTextAreaElement>(null)\n\n useEffect(() => {\n if (editing) {\n fieldRef.current?.focus()\n fieldRef.current?.select()\n }\n }, [editing])\n\n const open = () => {\n setDraft(value)\n setEditing(true)\n }\n\n const cancel = () => {\n setEditing(false)\n setDraft(value)\n }\n\n const commit = async () => {\n if (busy) return\n const next = draft.trim()\n if (next === value) {\n setEditing(false)\n return\n }\n setBusy(true)\n try {\n const ok = await onCommit(next)\n if (ok) setEditing(false)\n } catch {\n /* the caller has shown why; keep the field open */\n } finally {\n setBusy(false)\n }\n }\n\n const onKeyDown = (event: KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>) => {\n if (event.key === 'Escape') {\n event.preventDefault()\n cancel()\n } else if (event.key === 'Enter' && !(multiline && event.shiftKey)) {\n event.preventDefault()\n void commit()\n }\n }\n\n const fieldClass = cn(\n 'w-full rounded-md border border-border-strong bg-bg-inset px-2 py-1 text-text-primary outline-none',\n multiline && 'resize-none',\n className,\n )\n\n if (readOnly) {\n return (\n /*\n No `aria-label` here. It sat on a bare `<div>`, which has no role, and\n ARIA does not let an author name a generic element \u2014 so the name was\n written and never read. Read-only, this is a line of text: whatever\n names the region around it (a `Field`, a `Property`) names this too.\n */\n <div\n className={cn('w-full px-2 py-1', multiline && !displayNode && 'whitespace-pre-wrap', (display ?? value) ? 'text-text-primary' : 'text-text-muted', className)}\n >\n {(display ?? value) ? (displayNode ?? (display ?? value)) : placeholder}\n </div>\n )\n }\n\n if (editing) {\n return multiline ? (\n <BaseField.Control\n ref={fieldRef as RefObject<HTMLTextAreaElement>}\n render={<textarea rows={4} />}\n aria-label={label}\n value={draft}\n disabled={busy}\n onChange={(event) => setDraft(event.target.value)}\n onKeyDown={onKeyDown}\n onBlur={() => void commit()}\n className={fieldClass}\n />\n ) : (\n <Input\n ref={fieldRef as RefObject<HTMLInputElement>}\n aria-label={label}\n value={draft}\n disabled={busy}\n onChange={(event) => setDraft(event.target.value)}\n onKeyDown={onKeyDown}\n onBlur={() => void commit()}\n className={fieldClass}\n />\n )\n }\n\n return (\n <button\n type=\"button\"\n onClick={open}\n /*\n No `title`. It was the one native browser tooltip left in the package \u2014\n its own timing, its own look, no theme \u2014 saying \"Click to edit\" beside\n an `aria-label` that already says it. The hover border is the\n affordance, and anything that needs a designed hint uses `WithTooltip`.\n */\n aria-label={`Edit ${label.toLowerCase()}`}\n className={cn(\n 'w-full rounded-md border border-transparent px-2 py-1 text-left transition-colors hover:border-border-default',\n multiline && !displayNode && 'whitespace-pre-wrap',\n (display ?? value) ? 'text-text-primary' : 'text-text-muted',\n className,\n )}\n >\n {(display ?? value) ? (displayNode ?? (display ?? value)) : placeholder}\n </button>\n )\n}\n", "import { Progress } from '@base-ui/react/progress'\nimport { cn } from './cn'\n\n/**\n * How much of something is done: a thin rounded track with a success-coloured\n * fill. On Base UI's `Progress`, which owns the `progressbar` role and its\n * numbers; this component writes the look.\n *\n * Both apps hand-built one, each with its own `role=\"progressbar\"` (UIG-27).\n * Their two looks are both kept, because each does a different job:\n *\n * - `default` is Ship's (`ui/ProgressBar.tsx`): 6px, the success colour \u2014 a\n * bar someone reads, with the words printed beside it.\n * - `quiet` is Peek's (`ui/ProjectTickets.tsx`): 4px, the muted success colour\n * \u2014 a glance on the same line as a count, \"not a chart\".\n *\n * The props are Ship's, unchanged. The bar alone says nothing a screen reader\n * can use beyond its numbers, so `label` is required; Base UI reads the value\n * out as a percentage.\n */\nexport type ProgressBarVariant = 'default' | 'quiet'\n\nconst TRACK_CLASSES: Record<ProgressBarVariant, string> = {\n default: 'h-1.5 w-full overflow-hidden rounded-full bg-bg-inset',\n // 4px, Katerina's ruling of 14 September; Peek's own bar is 3px until it takes this one.\n quiet: 'h-1 w-full overflow-hidden rounded-full bg-bg-inset',\n}\n\nconst INDICATOR_CLASSES: Record<ProgressBarVariant, string> = {\n default: 'h-full rounded-full bg-success-default transition-[width]',\n quiet: 'h-full rounded-full bg-success-muted transition-[width] duration-300',\n}\n\nexport interface ProgressBarProps {\n /** How many are done. */\n value: number\n /** How many there are. At `0` the bar is empty. */\n max: number\n /** The accessible name \u2014 \"Items done\". */\n label: string\n /** Default `default`. */\n variant?: ProgressBarVariant\n /** Placement only: a width, a margin, `flex-1` in a row. */\n className?: string\n}\n\nexport function ProgressBar({ value, max, label, variant = 'default', className }: ProgressBarProps) {\n return (\n // `max={0}` needs no guard: Base UI reads the 0/0 share as an empty bar.\n <Progress.Root value={value} max={max} aria-label={label} className={className}>\n <Progress.Track className={TRACK_CLASSES[variant]}>\n <Progress.Indicator className={INDICATOR_CLASSES[variant]} />\n </Progress.Track>\n </Progress.Root>\n )\n}\n", "import { Fragment, useCallback, useLayoutEffect, useRef, useState, type MouseEventHandler, type ReactNode } from 'react'\nimport { cn } from './cn'\nimport { Link } from './Link'\nimport { WithTooltip } from './Tooltip'\n\n/**\n * A trail: \"Documents / Quarterly plan / DOC-12\". Ship's Breadcrumb\n * (2026-09-01), which knows nothing about what the places are.\n *\n * An item with an `href` is a link, even when it is last \u2014 a page may end its\n * trail on somewhere to go. The last item is where you are when it has none,\n * and may be `mono` (a ref, an id) or plain. The separator is a slash, muted,\n * never read aloud.\n *\n * A crumb that truncates shows its whole label in a tooltip on hover\n * (Katerina, 2026-09-01); one that fits shows nothing extra. Truncation is\n * re-measured when the trail resizes, so the tooltip appears and disappears\n * with the room the trail actually has.\n *\n * A crumb with an `href` is a plain anchor, so in a router app a click on it\n * reloads the page. Such an app passes `onClick` per crumb and navigates in\n * place there \u2014 NavItem's rule \u2014 and the `href` stays a real address so the\n * link can still be copied or opened in a new tab. Ship's breadcrumbs were\n * the last links reloading the whole app (ADOPTION S15, 2026-09-08).\n */\nexport interface Crumb {\n label: string\n href?: string\n /** A router app intercepts the click here. Called only on a crumb with an `href`. */\n onClick?: MouseEventHandler<HTMLAnchorElement>\n /** Set the item in the mono face \u2014 a ref, an id. */\n mono?: boolean\n /**\n * 16px, stroke 1.5, before the label \u2014 what kind of place this is, so a\n * container's name is not mistaken for an item's (Katerina, 2026-09-09).\n * Decorative: the label says where you are; the icon says what it is.\n */\n icon?: ReactNode\n /** Quieter \u2014 a label that is not a place. */\n muted?: boolean\n}\n\nexport interface BreadcrumbProps {\n items: Crumb[]\n className?: string\n}\n\nexport function Breadcrumb({ items, className }: BreadcrumbProps) {\n const navRef = useRef<HTMLElement>(null)\n const labelRefs = useRef<(HTMLElement | null)[]>([])\n const [truncated, setTruncated] = useState<ReadonlySet<number>>(new Set())\n\n const measure = useCallback(() => {\n const next = new Set<number>()\n labelRefs.current.forEach((el, index) => {\n if (el && el.scrollWidth > el.clientWidth) next.add(index)\n })\n setTruncated((prev) => (prev.size === next.size && [...next].every((i) => prev.has(i)) ? prev : next))\n }, [])\n\n useLayoutEffect(() => {\n measure()\n const nav = navRef.current\n // jsdom has neither layout nor ResizeObserver; without this guard a\n // consumer app cannot render a page with a trail in its tests.\n if (!nav || typeof ResizeObserver === 'undefined') return\n const observer = new ResizeObserver(measure)\n observer.observe(nav)\n return () => observer.disconnect()\n }, [measure, items])\n\n return (\n <nav ref={navRef} aria-label=\"Breadcrumb\" className={cn('flex min-w-0 items-center gap-1.5 text-body-2', className)}>\n {items.map((item, index) => {\n const last = index === items.length - 1\n // The mono size is an arbitrary value (the caption token) because this\n // string goes through cn() and a token size before a colour class is\n // dropped (the tailwind-merge pitfall).\n const tone = item.muted || (last && item.mono) ? 'text-text-muted' : last ? 'text-text-primary' : 'text-text-secondary'\n const text = cn(\n 'truncate',\n // A mono crumb is a ref \u2014 the identity. It never gives up width to a\n // long name beside it (the LongName story always claimed \"the ref\n // stays\"; flexbox was squeezing it anyway until this line).\n item.mono && 'shrink-0 font-mono text-caption',\n tone,\n )\n const setLabelRef = (el: HTMLElement | null) => {\n labelRefs.current[index] = el\n }\n const crumb = item.href ? (\n // `plain` adds no look of its own: a crumb keeps the trail's size and\n // tone, and brightens on hover. The package's own Link, so a trail\n // follows whatever a link learns to do (UIG-5).\n <Link ref={setLabelRef} variant=\"plain\" href={item.href} onClick={item.onClick} className={cn(text, 'hover:text-text-primary')}>\n {item.label}\n </Link>\n ) : (\n <span ref={setLabelRef} className={text} aria-current={last ? 'page' : undefined}>\n {item.label}\n </span>\n )\n return (\n <Fragment key={`${item.label}-${index}`}>\n {index > 0 && (\n <span aria-hidden=\"true\" className=\"shrink-0 text-text-muted\">\n /\n </span>\n )}\n {/* Beside the crumb, not inside it: the crumb stays the one element\n that truncates and is measured, and the trail's own gap \u2014 6px \u2014\n is the distance between an icon and its name everywhere here. */}\n {item.icon && (\n <span aria-hidden=\"true\" className={cn('flex shrink-0', tone)}>\n {item.icon}\n </span>\n )}\n {truncated.has(index) ? (\n // `min-w-0 shrink` undoes the wrapper's own shrink-0 \u2014 the crumb\n // must keep truncating inside it, or wrapping it would widen the\n // trail and the tooltip would never be needed again.\n <WithTooltip label={item.label} wrapperClassName=\"min-w-0 shrink\">\n {crumb}\n </WithTooltip>\n ) : (\n crumb\n )}\n </Fragment>\n )\n })}\n </nav>\n )\n}\n", "import type { ComponentPropsWithoutRef, ReactNode } from 'react'\nimport { cn } from './cn'\nimport { WithTooltip } from './Tooltip'\n\n/**\n * One row of a sidebar: a label, an optional count, an active state\n * (Ship's NavItem, 2026-09-02, verbatim).\n *\n * A count is a number of ONE thing, and its tooltip says which (\"18\n * open\", \"5 active\") \u2014 the caller passes the number and the words for it,\n * and a zero is not drawn at all. That is this row's rule; a *tab's*\n * count draws its zero (see Tabs) \u2014 both rulings stand, deliberately.\n *\n * Active is the same fill as a selected tab \u2014 `bg-active`, neutral.\n */\nexport interface NavItemProps extends Omit<ComponentPropsWithoutRef<'a'>, 'href'> {\n label: string\n href: string\n /** `0` or absent renders no counter. */\n count?: number\n /** What the count counts, for the tooltip \u2014 \"18 open\", \"5 active\". */\n countLabel?: string\n active?: boolean\n /** 16px, stroke 1.5. */\n icon?: ReactNode\n className?: string\n}\n\nexport function NavItem({ label, href, count, countLabel, active = false, icon, className, ...props }: NavItemProps) {\n return (\n <a\n href={href}\n aria-current={active ? 'page' : undefined}\n className={cn(\n // shrink-0: the row keeps its 32px inside an overflowing flex column\n // \u2014 without it the list compresses instead of scrolling (the missing-\n // shrink-0 family; Katerina, 2026-09-02).\n 'flex h-8 min-w-0 shrink-0 items-center gap-2 rounded-md px-2 text-body-2 transition-colors',\n active ? 'bg-bg-active text-text-primary' : 'text-text-secondary hover:bg-bg-hover hover:text-text-primary',\n className,\n )}\n {...props}\n >\n {icon && <span className=\"flex shrink-0 items-center\">{icon}</span>}\n <span className=\"flex-1 truncate\">{label}</span>\n {count ? (\n <WithTooltip label={countLabel ?? `${count} open`}>\n {/* A 16px box, centred: the slot is an icon's width, so a number\n here sits on the same axis as a SectionHeader's action above it\n \u2014 right edges alone left a digit 4px off a 16px icon (Katerina,\n 2026-09-09). A three-digit count grows the box leftwards. */}\n <span className=\"min-w-4 shrink-0 text-center font-mono text-caption tabular-nums text-text-muted\">{count}</span>\n </WithTooltip>\n ) : null}\n </a>\n )\n}\n", "import { useMemo, type ReactElement, type ReactNode, type RefObject } from 'react'\nimport { Popover as BasePopover } from '@base-ui/react/popover'\nimport { cn } from './cn'\nimport { triggerDisabled } from './triggerDisabled'\nimport { ScrollArea } from './ScrollArea'\nimport { MenuPanel } from './Menu'\n\n/**\n * A floating panel from a trigger \u2014 the same elevated surface a `Menu` uses,\n * with none of a menu's semantics. New at stage 4 (2026-09-07).\n *\n * **It exists because `Menu` was being used for this.** Peek's selection\n * toolbar puts a text field inside one, and its debug panel fills one with\n * toggle rows; neither is a list of actions, and since the menus moved onto\n * Base UI a `Menu` gives its contents roving focus and typeahead, which is\n * wrong for both and fights a text field outright. Beyond those two, Peek\n * hand-writes overlay behaviour in thirteen files \u2014 six `createPortal`, five\n * outside-click listeners, seven position calculations against\n * `window.innerWidth` (`COMPONENTS-PEEK.md` F5). This is what they become.\n *\n * **The API is `Menu`'s**: it takes the `trigger` and owns everything after \u2014\n * the toggle, the placement, the dismissal and the focus return. What differs\n * is inside: a `Popover` announces itself as a dialog, its contents are\n * ordinary content, and Tab walks them in order.\n *\n * The one thing a `Menu` has no use for is the second mode below: a panel with\n * no trigger element at all, hung from a rect the caller measured \u2014 a toolbar\n * over a text selection. That mode is controlled, because there is nothing for\n * Base UI to watch.\n */\nexport interface PopoverProps {\n /**\n * The control that opens the panel. Any element that forwards its ref and\n * spreads its props \u2014 this package's `Button`, `IconButton` and\n * `PersonTrigger` all do.\n *\n * Base UI can only do the toggle, the placement, the dismissal and the focus\n * return if it knows which element opened the panel. Give it the trigger\n * unless there is genuinely no element to give \u2014 see `anchor`.\n */\n trigger?: ReactElement\n /**\n * For a panel with **no trigger element**: an element, or a rect the caller\n * measured \u2014 a text selection's. Pair it with `open`, since there is nothing\n * for Base UI to watch.\n *\n * An anchored panel **does not take focus**, because the person is still in\n * whatever produced it. That also means it cannot be reached by keyboard, so\n * everything in one must be reachable another way.\n *\n * Render it always and toggle `open`; do not mount it only while it is open.\n */\n anchor?: HTMLElement | DOMRect | null\n /**\n * Which of the panel's edges hangs from the trigger's, or `center` to put\n * the panel's middle over the anchor's. Default left.\n *\n * **`center` is what a toolbar over a text selection wants** (Katerina,\n * PEE-19, after Linear): the panel sits over the middle of what you\n * selected, wherever in the line that is. Until 0.12.2 only the two edges\n * were mapped, so Peek's selection toolbar kept the placement arithmetic\n * the move onto this component was meant to delete (ADOPTION B21).\n */\n align?: 'left' | 'center' | 'right'\n /**\n * Which side of the trigger, or of the anchor, the panel prefers. Default\n * `bottom`.\n *\n * **A toolbar over a text selection wants `top`** (Katerina, 2026-09-08):\n * below, it covers the line you are about to read next, and it is the line\n * *after* the selection that tells you what you have selected. It is a\n * preference, not a promise \u2014 Base UI flips it when that side has no room.\n */\n side?: 'top' | 'bottom'\n /** Controlled, for a caller that must know or must force it. Required with\n * `anchor`; with a `trigger`, leave both off and the panel keeps its own. */\n open?: boolean\n onOpenChange?: (open: boolean) => void\n /**\n * Where focus goes when the panel closes. With a `trigger` it goes back to\n * the trigger and this is not needed. An anchored panel never took focus, so\n * this only matters when something inside it did \u2014 a field the person tabbed\n * or clicked into: point it at what they came from, or focus is left on the\n * document body.\n */\n finalFocus?: RefObject<HTMLElement | null>\n /** Base UI's imperative handle. `actions.current?.close()` shuts the panel \u2014\n * for the Cancel and Save buttons a form panel ends with. */\n actionsRef?: RefObject<{ close: () => void; unmount: () => void } | null>\n /** Names the panel for assistive tech. A panel with a visible heading can\n * point at it instead, with `aria-labelledby`. */\n ariaLabel?: string\n children: ReactNode\n /**\n * On the panel's surface \u2014 its width. **Not its padding**: see\n * `contentClassName`.\n */\n className?: string\n /**\n * The padding around the children, as a class. Default `p-2`, 8px \u2014 what a\n * panel of rows or a small form wants. **A toolbar wants `p-1`.**\n *\n * The padding is on the scrolling content, not on the panel, so a scrollbar\n * is drawn over it and hugs the panel's edge (D63). A padding class on\n * `className` therefore does not replace this one \u2014 it adds to it: that is\n * how every toolbar in a `Popover` grew 8px a side at `0.12.6`, measured 13px\n * from the panel's edge to the toolbar where it had been 5px (PLAN Finding\n * 60). Set it here.\n */\n contentClassName?: string\n /**\n * A cap on the scrolling box, as a class \u2014 `max-h-[360px]`. Without one the\n * panel grows to the room the positioner has, which is the right default for\n * a panel that is as tall as its content.\n *\n * **It cannot go on `className`.** That lands on the panel, and the panel's\n * children sit inside a `ScrollArea` whose viewport carries its own cap \u2014 so\n * a `max-h` on the panel is overrun by the viewport and the content draws\n * straight through the panel's border (measured with Peek's `/` menu,\n * 2026-09-12: a 400px panel with 559px of rows hanging out of it). The cap\n * belongs on the viewport, as `DialogShell` takes `bodyMaxHeight`.\n *\n * **When a panel needs one.** A panel taller than the room above *and* below\n * its anchor is not flipped by the positioner \u2014 it is moved to the side of\n * the anchor, which for a type-ahead over a caret is not where it belongs.\n * A cap keeps the choice between above and below.\n *\n * It replaces the available-height cap rather than adding to it, so a caller\n * that sets one owns it.\n */\n maxHeight?: string\n}\n\n/** The 4px between the panel and what it hangs from, and the 8px it keeps\n * clear of every screen edge \u2014 `Menu`'s numbers, because it is the same box. */\nconst GAP = 4\nconst VIEWPORT_PAD = 8\n\nexport function Popover({ trigger, anchor, align = 'left', side = 'bottom', open, onOpenChange, finalFocus, actionsRef, ariaLabel, children, className, contentClassName, maxHeight }: PopoverProps) {\n /* A rect is not an element, so it becomes a virtual anchor \u2014 the one shape\n Floating UI takes besides an element. */\n const anchorTarget = useMemo(() => {\n if (!anchor) return undefined\n if (anchor instanceof Element) return anchor\n return { getBoundingClientRect: () => anchor }\n }, [anchor])\n\n return (\n <BasePopover.Root\n open={open}\n onOpenChange={onOpenChange ? (next) => onOpenChange(next) : undefined}\n actionsRef={actionsRef}\n /* Non-modal: the page behind keeps its scrollbar, so opening a panel\n never shifts the layout, and a toolbar over a text selection must not\n take the page away from the person using it. */\n modal={false}\n >\n {/* Told the button's disabled state, or it writes `aria-disabled=\"false\"`\n over it and opens anyway (Finding 39). */}\n {trigger && <BasePopover.Trigger render={trigger} disabled={triggerDisabled(trigger)} />}\n <BasePopover.Portal>\n <BasePopover.Positioner\n anchor={anchorTarget}\n side={side}\n align={align === 'right' ? 'end' : align === 'center' ? 'center' : 'start'}\n sideOffset={GAP}\n collisionPadding={VIEWPORT_PAD}\n className=\"z-50 data-[anchor-hidden]:hidden\"\n >\n <BasePopover.Popup\n aria-label={ariaLabel}\n /*\n * From a trigger, focus lands on the first thing in the panel \u2014\n * the field, in the panel this component exists for \u2014 and goes\n * back to the trigger when it closes.\n *\n * From an anchor, it does not move at all. A panel with no trigger\n * appeared rather than being asked for, and the person is still in\n * the middle of what produced it: a toolbar over a text selection\n * that took the caret out of the text would end the edit it exists\n * to serve. Measured 2026-09-08: with focus moved into the panel,\n * the first control's tooltip opens on `:focus-visible` and eats\n * the Escape that should have closed the panel, and the caller's\n * re-read of the selection fights the panel's own dismissal \u2014 both\n * intermittently. Neither happens once focus stays put.\n *\n * The cost is stated on the page: an anchored panel cannot be\n * reached by keyboard, so what is in one must also be reachable\n * some other way.\n */\n initialFocus={trigger ? undefined : false}\n finalFocus={finalFocus}\n className={cn('min-w-[180px] outline-none p-0', className)}\n render={<MenuPanel />}\n >\n {/* As in Menu: the cap on the scrolling box, and the padding on the\n content rather than the panel, so the bar is drawn over the\n padding instead of 9px inside it (D63). A caller's `maxHeight`\n replaces the cap, and a caller's `contentClassName` the padding\n \u2014 see the props. */}\n <ScrollArea\n viewportClassName={maxHeight ?? 'max-h-[var(--available-height)]'}\n contentClassName={cn('flex flex-col p-2 [&>[role=separator]]:mx-0', contentClassName)}\n >\n {children}\n </ScrollArea>\n </BasePopover.Popup>\n </BasePopover.Positioner>\n </BasePopover.Portal>\n </BasePopover.Root>\n )\n}\n", "import type { ReactNode, Ref } from 'react'\nimport { Toolbar as BaseToolbar } from '@base-ui/react/toolbar'\nimport { cn } from './cn'\nimport { IconButton, type IconButtonProps } from './IconButton'\nimport { MenuPanel } from './Menu'\nimport { TextInput, type TextInputProps } from './TextInput'\n\n/**\n * A strip of controls that behaves as **one** control.\n *\n * New at Katerina's request, 2026-09-08, pulled forward from stage 6.\n *\n * **It exists because every strip in the suite is as many Tab stops as it has\n * buttons.** Ship's reaction row, Peek's composer strip and the editor's\n * formatting strip are each a hand-rolled `<div class=\"flex\">` of\n * `IconButton`s: eight buttons, eight stops, and no way to get past them but\n * through them. A toolbar is one stop \u2014 Tab in, arrow keys along, Tab out \u2014\n * which is what the `toolbar` role means and what Base UI's part implements.\n *\n * What it is not: a `Menu` (that is a list of *verbs* you open and choose\n * from, and it closes when you do) or a row of unrelated buttons (a form's\n * Cancel and Save are two separate answers, and each deserves its own stop).\n * A toolbar is a set of things you do *to* something that is still there.\n *\n * **It draws the box.** A toolbar floats over what it acts on \u2014 a card, a\n * paragraph, an image \u2014 so it carries the elevated surface that separates it\n * from that: the same `MenuPanel` a `Menu` draws, because a floating strip and\n * a floating list are the same box. Peek had built this twice and the two had\n * already drifted \u2014 `ConversationQuickMenu` is `rounded-sm` with `shadow-sm`\n * and a subtle border, `ReactionPicker` `rounded-lg` with `shadow-lg` and a\n * default one. There is one box now. `surface={false}` for a strip inside\n * something that already draws it.\n *\n * **The gap, stated: Home and End do nothing.** Base UI's composite has them\n * and its `Toolbar` does not switch them on (`enableHomeAndEndKeys`, read in\n * `useCompositeRoot`, not passed by `ToolbarRoot`) \u2014 so this page does not\n * claim them. The arrow keys wrap, which reaches both ends in one press of a\n * strip this size.\n */\nexport interface ToolbarProps {\n /**\n * Names the strip for assistive tech \u2014 \"Formatting\", \"Reactions\". A toolbar\n * is a landmark-ish grouping and is announced as a bare \"toolbar\" without\n * one, which says nothing when a page has two.\n */\n 'aria-label': string\n /** `horizontal` walks with \u2190 \u2192, `vertical` with \u2191 \u2193. Default horizontal. */\n orientation?: 'horizontal' | 'vertical'\n /** Whether the arrow keys wrap at the ends. Default true, as Base UI's. */\n loopFocus?: boolean\n /**\n * The elevated box around the strip. **On by default** \u2014 a toolbar floats,\n * and this is what floating looks like here.\n *\n * Off for a strip inside a surface that already draws one: a `Popover`, a\n * dialog, a card's own panel. Two boxes inside each other is the tell.\n */\n surface?: boolean\n children: ReactNode\n /** The row's own layout \u2014 its gap, its padding, its wrapping. */\n className?: string\n}\n\nexport function Toolbar({ 'aria-label': ariaLabel, orientation = 'horizontal', loopFocus = true, surface = true, children, className }: ToolbarProps) {\n const strip = (\n <BaseToolbar.Root\n aria-label={ariaLabel}\n orientation={orientation}\n loopFocus={loopFocus}\n className={cn('flex items-center gap-1', orientation === 'vertical' && 'flex-col', className)}\n >\n {children}\n </BaseToolbar.Root>\n )\n if (!surface) return strip\n /*\n The box WRAPS the strip rather than being composed onto it. `MenuPanel` is\n a flex COLUMN \u2014 right for a menu's rows, wrong for a row of controls \u2014 and\n Tailwind emits `flex-col` after `flex-row`, so a merged class list would\n stand the toolbar on its end whatever order the classes arrived in. One\n extra element, and the box keeps its single definition.\n\n `p-1` is the tighter of Peek's two paddings and the right one for a strip\n of 24px controls; `MenuPanel`'s own `p-2` is a menu's, where the rows run\n the full width.\n */\n return <MenuPanel className=\"w-fit p-1\">{strip}</MenuPanel>\n}\n\n/**\n * One control in the strip. It is this package's `IconButton` \u2014 the same\n * square, the same variants, the same `tooltip` and `disabledReason` \u2014 joined\n * to the toolbar's roving focus.\n *\n * A disabled button **keeps its place in the walk**: a strip whose controls\n * come and go from the arrow keys as their state changes is a strip you cannot\n * learn. It is also what lets a `disabledReason` be read.\n *\n * **It must be inside a `Toolbar`.** Base UI throws otherwise \u2014\n * \"ToolbarRootContext is missing\" \u2014 because a part with no strip has no walk\n * to join. A control beside a strip is an `IconButton`.\n */\nexport interface ToolbarButtonProps extends IconButtonProps {\n ref?: Ref<HTMLButtonElement>\n}\n\nexport function ToolbarButton({ ref, disabled, disabledReason, ...props }: ToolbarButtonProps) {\n return (\n <BaseToolbar.Button\n ref={ref}\n /*\n The part has to be told, not only the button inside it. The toolbar\n keeps its own map of which items are disabled \u2014 it is what decides\n where an arrow key lands \u2014 and a `Toolbar.Button` that renders a\n disabled `IconButton` without saying so leaves the two disagreeing:\n measured, the element came out `aria-disabled=\"false\"` over a button\n that was disabled.\n */\n disabled={disabled || !!disabledReason}\n /* Keep it in the walk either way. A strip whose controls come and go\n from the arrow keys as their state changes is a strip you cannot\n learn, and a `disabledReason` you cannot reach is a reason nobody\n reads. */\n focusableWhenDisabled\n render={<IconButton disabled={disabled} disabledReason={disabledReason} {...props} />}\n />\n )\n}\n\n/**\n * A text field inside the strip \u2014 the link editor in a selection toolbar.\n *\n * The arrow keys belong to the field while it has focus, which is the whole\n * point: \u2190 and \u2192 move the caret. The toolbar's walk resumes at Tab. That is\n * Base UI's `Toolbar.Input`, and it is why a field in a toolbar cannot just be\n * a `TextInput` dropped in the row.\n */\nexport type ToolbarInputProps = TextInputProps\n\nexport function ToolbarInput({ size, ...props }: ToolbarInputProps) {\n // `size` is TextInput's own (default or small), not the native attribute\n // Base UI's part would take, so it goes to the field it draws.\n return <BaseToolbar.Input render={<TextInput size={size} />} {...props} />\n}\n\n/**\n * The hairline between two groups of controls. It is `Divider`'s rule and\n * `Toolbar.Separator`'s role, so a screen reader hears the grouping a sighted\n * reader sees.\n */\nexport function ToolbarSeparator({ className }: { className?: string }) {\n return (\n <BaseToolbar.Separator\n orientation=\"vertical\"\n /* `shrink-0` on a hairline in a flex row, always: without it a 1px rule\n renders at 0px and nobody notices (the package's own trap table). */\n className={cn('mx-1 h-4 w-px shrink-0 bg-border-default', className)}\n />\n )\n}\n", "import { cn } from './cn'\nimport { Toolbar, ToolbarButton } from './Toolbar'\n\n/**\n * The reactions on offer, to choose one from \u2014 Peek's `ReactionPicker`\n * (2026-09-03), moved in at Katerina's request (2026-09-08).\n *\n * **`Reaction` is the answer; this is the question.** A `Reaction` is a pill\n * that says an emoji, a count and whether it is yours. This is the row you\n * pick from before any of that exists, and both apps need it: Peek draws one\n * from a card's action strip (`ConversationQuickMenu`), and Ship draws the\n * same row in `Reactions.tsx` from its own vocabulary.\n *\n * **It is a `Toolbar`** \u2014 icon buttons holding emoji, on the strip's own\n * elevated box \u2014 so the whole row is one Tab stop and the arrow keys walk it.\n * Peek's was five separate stops.\n *\n * ## What stays with the app\n *\n * **Which emoji, and what each one means.** Peek offers five and names them\n * (\"Makes sense\", \"Agree\", \"Thank you\", \"Let's go!\", \"Congrats\"); Ship will\n * offer its own. A vocabulary is product knowledge, so it arrives as\n * `options` \u2014 and the name is not optional, because the emoji is decorative\n * here for the same reason it is on `Reaction`: a glyph read aloud is noise,\n * and its spoken name differs per screen reader.\n *\n * ## Picking, and nothing else\n *\n * **It does not show which reactions are yours.** That is `Reaction`'s job \u2014\n * the pill with the count and the accent fill \u2014 and the two live in different\n * places: the reactions *made* sit in a row on the card, and this row is what\n * opens over it when you go to add one. A picker that also reported state\n * would be two components wearing one name (Katerina, 2026-09-08).\n */\nexport interface ReactionOption {\n /** The emoji itself. Drawn decoratively \u2014 `label` names the control. */\n emoji: string\n /** What this reaction *means*: \"Makes sense\", \"Congrats\". Required, and it\n * is what a screen reader and the tooltip both say. */\n label: string\n}\n\nexport interface ReactionPickerProps {\n /** What is on offer, in the order it is drawn. The app's vocabulary. */\n options: ReactionOption[]\n onSelect: (emoji: string) => void\n /** Names the row. Default \"Reactions\". */\n 'aria-label'?: string\n /**\n * The elevated box around the row, from `Toolbar`. **On by default**, which\n * is how a picker floats over a card. Off inside a `Popover`, which draws\n * that box already.\n */\n surface?: boolean\n /** The row's layout \u2014 its gap, its padding. */\n className?: string\n}\n\nexport function ReactionPicker({ options, onSelect, 'aria-label': ariaLabel = 'Reactions', surface = true, className }: ReactionPickerProps) {\n return (\n <Toolbar aria-label={ariaLabel} surface={surface} className={cn('gap-0.5', className)}>\n {options.map((option) => (\n /*\n A `ToolbarButton`, not a button in a tooltip wrapper. The wrapper\n would become the toolbar's item and the button inside it would never\n join the walk \u2014 and it is unnecessary since stage 4, because an\n `IconButton` with a `tooltip` IS the trigger.\n\n The tooltip names it for a pointer, `aria-label` for everything else,\n and both say the meaning rather than the glyph.\n */\n <ToolbarButton\n key={option.emoji}\n aria-label={option.label}\n tooltip={option.label}\n onClick={() => onSelect(option.emoji)}\n /* Peek's geometry, verbatim: a 28px square rather than the\n IconButton's 24, and the emoji at 18px \u2014 larger than a `Reaction`\n pill's 16, because here the emoji is the whole control. */\n className=\"size-7 text-h3 leading-none\"\n >\n {/* Decorative, as on `Reaction`: the control is named above. */}\n <span aria-hidden=\"true\">{option.emoji}</span>\n </ToolbarButton>\n ))}\n </Toolbar>\n )\n}\n", "import type { ReactNode } from 'react'\nimport { PreviewCard as BasePreviewCard } from '@base-ui/react/preview-card'\nimport { cn } from './cn'\nimport { ScrollArea } from './ScrollArea'\nimport { MenuPanel } from './Menu'\n\n/**\n * More of a thing, on hover \u2014 a card beside a row that is only a snippet.\n * New at stage 4 (2026-09-07).\n *\n * **It exists because Peek's Screener preview is this, hand-written**: its own\n * `createPortal`, its own \"prefer the right, flip left if it would run off\n * screen\" arithmetic against `window.innerWidth`, and its own clamp against\n * the bottom edge (`ScreenerPreviewCard.tsx`). Floating UI does all three.\n *\n * **Not a Tooltip.** A tooltip is a word for a control, is\n * `pointer-events: none`, and may hold nothing you can reach. A preview card\n * holds *content* \u2014 faces, text, a link \u2014 and you can move the pointer into\n * it, which is what a preview is for. If what you have is a label, use\n * `WithTooltip`.\n *\n * The surface is the package's elevated panel, the same one a `Menu` draws, so\n * there is one definition of that box. Width, height and internal rhythm are\n * the caller's: a preview of a conversation is not the size of a preview of a\n * person.\n */\nexport interface PreviewCardProps {\n /** What the card holds. Rendered only while it is open. */\n content: ReactNode\n /** The row, name or avatar the card previews. */\n children: ReactNode\n /** Which side of the trigger to prefer. It flips when that side has no room. Default right. */\n side?: 'top' | 'bottom' | 'left' | 'right'\n /** Before it opens, in ms. Long enough that crossing a list does not flash a card at every row. */\n delay?: number\n /** After the pointer leaves, in ms \u2014 the grace that lets you cross the gap into the card. */\n closeDelay?: number\n /** On the card's surface: its width, its padding, a max height. */\n className?: string\n /** Extra classes on the trigger wrapper \u2014 e.g. `block w-full` for a row. */\n wrapperClassName?: string\n}\n\n/** The card's own numbers: 12px from the trigger, 8px clear of every screen\n * edge. Peek's hand-written preview used 12 and 12; the 8 is the package's\n * viewport margin, shared with every other floating surface. */\nconst GAP = 12\nconst VIEWPORT_PAD = 8\n/** 350ms, which is Peek's number: `ScreenerItem`'s `HOVER_DELAY_MS`. Long\n * enough not to flash a card at every row while a pointer crosses a list,\n * short enough to feel like an answer. Taken rather than invented, because\n * this component exists to replace that one. */\nconst OPEN_DELAY = 350\n/** The diagonal from the row to the card has to survive. Peek's has no grace\n * at all \u2014 it closes on `mouseleave` \u2014 because its card is\n * `pointer-events: none` and there is nothing to cross to. Which also means\n * **the `overflow-y-auto` on its 300px-capped card can never be scrolled**;\n * here the card can be reached, so it can. */\nconst CLOSE_DELAY = 200\n\nexport function PreviewCard({ content, children, side = 'right', delay = OPEN_DELAY, closeDelay = CLOSE_DELAY, className, wrapperClassName }: PreviewCardProps) {\n return (\n <BasePreviewCard.Root>\n {/* The part renders an `<a>` by default, which a row is not; `render`\n makes it the wrapper this component has always been, and the trigger\n inside keeps whatever element it already is. */}\n <BasePreviewCard.Trigger\n delay={delay}\n closeDelay={closeDelay}\n render={<span className={cn('inline-flex', wrapperClassName)} />}\n >\n {children}\n </BasePreviewCard.Trigger>\n <BasePreviewCard.Portal>\n <BasePreviewCard.Positioner\n side={side}\n align=\"start\"\n sideOffset={GAP}\n collisionPadding={VIEWPORT_PAD}\n className=\"z-50 data-[anchor-hidden]:hidden\"\n >\n <BasePreviewCard.Popup\n className={cn('w-[360px] p-3 outline-none', className)}\n render={<MenuPanel />}\n >\n <ScrollArea viewportClassName=\"max-h-[calc(min(300px,var(--available-height))_-_1.5rem)]\" contentClassName=\"flex flex-col gap-3 [&>*]:shrink-0\">\n {content}\n </ScrollArea>\n </BasePreviewCard.Popup>\n </BasePreviewCard.Positioner>\n </BasePreviewCard.Portal>\n </BasePreviewCard.Root>\n )\n}\n", "import type { ReactNode } from 'react'\nimport { cn } from './cn'\n\n/**\n * The icon rail: a 64px strip of RailItems (the shell of Peek's NavRail,\n * 2026-09-02 \u2014 its entries and their routes stayed in the app).\n *\n * It has no border and no surface of its own \u2014 it stands on the app\n * background, beside whatever card or column the app draws. Collapsing\n * is the caller's: the rail does not know about the burger, it only gets\n * given less room (or none) to stand in. Desktop only.\n */\nexport interface RailProps {\n /** Names the navigation region for assistive tech. */\n 'aria-label'?: string\n children: ReactNode\n className?: string\n}\n\nexport function Rail({ 'aria-label': ariaLabel = 'Navigation', children, className }: RailProps) {\n return (\n <nav aria-label={ariaLabel} className={cn('flex w-16 shrink-0 flex-col items-start gap-2 px-2 py-3', className)}>\n {children}\n </nav>\n )\n}\n", "import type { ComponentPropsWithoutRef, ReactNode } from 'react'\nimport { cn } from './cn'\n\n/**\n * One tile of an icon rail: the icon in its hover tile, a 9px label\n * under it (Peek's NavItem, 2026-09-02, verbatim \u2014 minus the router).\n *\n * The original computed its own active state from the route and rendered\n * a router link; neither belongs in a shared component. This one takes\n * `active` and renders an anchor \u2014 a router app keeps a thin wrapper\n * that does its own matching and intercepts the click.\n *\n * Active is `bg-selected` on the tile \u2014 some themes also give the icon\n * their interactive colour.\n */\nexport interface RailItemProps extends Omit<ComponentPropsWithoutRef<'a'>, 'href'> {\n href: string\n /** 16px, stroke 1.5. */\n icon: ReactNode\n label: string\n active?: boolean\n className?: string\n}\n\nexport function RailItem({ href, icon, label, active = false, className, ...props }: RailItemProps) {\n return (\n <a\n href={href}\n aria-current={active ? 'page' : undefined}\n className={cn('group flex w-full shrink-0 flex-col items-center gap-0.5 px-2 py-0.5', className)}\n {...props}\n >\n <div\n className={cn(\n 'flex items-center justify-center rounded-lg p-2 transition-colors',\n active ? 'bg-bg-selected' : 'group-hover:bg-bg-hover',\n )}\n >\n <span\n className={cn(\n 'flex items-center transition-colors',\n active ? 'text-text-primary signal:text-[color:var(--text-interactive)]' : 'text-text-secondary',\n )}\n >\n {icon}\n </span>\n </div>\n <span className={cn('text-center text-menu', active ? 'text-text-primary' : 'text-text-secondary')}>\n {label}\n </span>\n </a>\n )\n}\n", "import type { ReactNode } from 'react'\nimport { cn } from './cn'\nimport { ScrollArea } from './ScrollArea'\n\n/**\n * The navigation column: 240px, a hairline on its right, the surface\n * behind it, scrolling independently of the content beside it (the shell\n * of Ship's Sidebar, 2026-09-02 \u2014 its contents stayed in the app).\n *\n * What goes inside is the caller's: NavItem rows, a SectionLabel heading\n * in a 32px row over a group. Desktop only \u2014 there is no narrow-screen\n * drawer.\n */\nexport interface SidebarProps {\n /** Names the navigation region for assistive tech. */\n 'aria-label'?: string\n children: ReactNode\n className?: string\n}\n\nexport function Sidebar({ 'aria-label': ariaLabel = 'Workspace', children, className }: SidebarProps) {\n return (\n <nav\n aria-label={ariaLabel}\n className={cn('flex w-60 shrink-0 flex-col border-r border-border-default bg-bg-surface', className)}\n >\n {/* The rows scroll in a ScrollArea: the bar takes no width, so the\n column's padding reads the same with a long list as with a short\n one (Katerina, 2026-09-08). The padding and the gap are the box's. */}\n <ScrollArea className=\"min-h-0 flex-1\" contentClassName=\"flex flex-col gap-px px-2.5 py-3\">\n {children}\n </ScrollArea>\n </nav>\n )\n}\n", "import type { ReactNode } from 'react'\nimport { cn } from './cn'\n\n/**\n * A labelled property row \u2014 \"Status\", \"Lead\", and whatever else a page\n * declares (2026-09-01). Both apps carried one, and they had already\n * drifted: Peek's label was `text-xs` \u2014 a Tailwind default with the wrong\n * line-height, not the caption token \u2014 and Ship's had grown a layout Peek's\n * lacked. One component, both layouts, the token:\n *\n * - `row` \u2014 Peek's: a 68px label column that lines the values up into a\n * column of their own; `text-secondary` rather than muted because a\n * property label is content, not a placeholder.\n * - `stacked` \u2014 Ship's rail: the label above a full-width control. The label\n * is the `menu` token (9px / 115% / 500), uppercase and letter-spaced \u2014\n * its own voice, deliberately not SectionLabel's (unmerged, Katerina\n * 2026-09-01). A literal string, not merged, so the token size survives.\n *\n * **A name and its value, in the markup made for it** (stage 6 of the\n * migration, 2026-09-14). Base UI has no part for this, so it is HTML's own:\n * a `<dl>` holding one `<dt>` (the label) and one `<dd>` (the value), so a\n * screen reader hears the label as the name of what follows rather than as a\n * loose word beside it. Each property is a list of one, which needs no\n * wrapper from the caller.\n */\nexport interface PropertyProps {\n label: string\n layout?: 'row' | 'stacked'\n children: ReactNode\n className?: string\n}\n\n/**\n * The value's `<dd>` draws no box of its own (`display: contents`): whatever\n * the caller passes stays a child of the row or the column, exactly as it was\n * before the `<dd>` existed \u2014 a control that fills the row (`flex-1`) still\n * fills it, and two values still sit the row's gap apart.\n */\nconst VALUE_CLASSES = 'contents'\n\nexport function Property({ label, layout = 'row', children, className }: PropertyProps) {\n if (layout === 'stacked') {\n return (\n // gap-3 \u2014 the one label-above-content distance (Katerina, 2026-09-01):\n // Peek's topic-details sections already sit at 12px, Ship's rail was\n // 6px, and unifying means the bigger, calmer one.\n <dl className={cn('flex flex-col gap-3', className)}>\n <dt className=\"text-menu uppercase tracking-widest text-text-secondary\">{label}</dt>\n <dd className={VALUE_CLASSES}>{children}</dd>\n </dl>\n )\n }\n return (\n <dl className={cn('flex items-center gap-2', className)}>\n <dt className=\"w-[68px] shrink-0 text-caption text-text-secondary\">{label}</dt>\n <dd className={VALUE_CLASSES}>{children}</dd>\n </dl>\n )\n}\n", "import type { ButtonHTMLAttributes, ReactNode } from 'react'\nimport { Toggle } from '@base-ui/react/toggle'\nimport { cn } from './cn'\n\n/**\n * A reaction: an emoji, how many people chose it, and whether you are one of\n * them.\n *\n * **This exists because both apps had already built it, and neither could use\n * the components that were here.** A reaction is a pill with a count that\n * toggles \u2014 `Chip` is a pill and rules itself out for anything clickable,\n * `Button` is clickable and is a 6px-radius rectangle, and `IconButton` has\n * nowhere to put the count. So Peek re-typed Chip's class list with a border\n * added, and Ship reached for a small Button with the count as its label. The\n * two apps' reactions do not look alike today, and neither is what was drawn.\n *\n * ## The one thing it does that nothing else here does\n *\n * **It says the reaction is yours.** That is the state a reaction has and a\n * chip does not: `pressed` fills it with the accent's muted tint and gives it\n * an accent edge, so a glance separates \"two people, one of them me\" from \"two\n * people\". Ship approximated it as `outlined` versus `muted` \u2014 measured at a\n * 1px hairline against no border at all \u2014 which is legible and is not the\n * signal Peek's accent fill gives.\n *\n * On Base UI's `Toggle` since stage 6 of the migration (2026-09-14): a native\n * `<button>` with `aria-pressed` and `data-pressed`, so the state reaches\n * assistive tech as a toggle rather than as a colour. Base UI writes both;\n * this component used to write the first by hand.\n *\n * ## `aria-label` is required, and the reason is specific\n *\n * The emoji is decorative here \u2014 it is `aria-hidden`, because a glyph read\n * aloud is noise and its spoken name differs per screen reader \u2014 and the only\n * visible text is the count. Without a label the control is announced as\n * **\"2\"**, which is what Ship shipped and its own tests caught. Pass the\n * meaning and the count: `\"Makes sense, 2\"`.\n *\n * ## Geometry\n *\n * Chip's pill, at a control's height: fully rounded, 24px to match `Button`\n * `small`, the same 8px horizontal padding, the `chip` type token for the\n * count so it sits at 11px/500 like every other count in the system.\n */\nexport interface ReactionProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'children' | 'type' | 'value'> {\n /** The emoji, drawn decoratively \u2014 name the control with `aria-label`. */\n emoji: ReactNode\n /** How many people reacted. Drawn as it is. A count of `0` is not a reaction: do not render one. */\n count: number\n /**\n * You are one of them: the accent tint and edge, and `aria-pressed`. The\n * caller owns it \u2014 a press calls `onClick` and the pill keeps showing this\n * value until the caller changes it.\n */\n pressed?: boolean\n /**\n * Names the control. **Required** \u2014 the emoji is decorative and the count is\n * the only visible text, so without this it is announced as a bare number.\n */\n 'aria-label': string\n}\n\nexport function Reaction({ emoji, count, pressed = false, className, ...props }: ReactionProps) {\n return (\n <Toggle\n // Controlled: a reaction is yours when the data says so, not when the\n // pill was last pressed \u2014 a write can fail, and another client can\n // change it.\n pressed={pressed}\n className={cn(\n // Chip's pill at a control's height, so a reaction and a status chip\n // read as the same family \u2014 24px matches Button `small`.\n 'inline-flex h-6 items-center justify-center gap-1.5 rounded-full px-2',\n 'border transition-colors',\n 'disabled:cursor-not-allowed disabled:opacity-50',\n pressed\n ? /* In the ship theme the accent on its own wash measures 2.70:1\n (Finding 4), and the count was the thing that vanished\n (Katerina, 2026-09-08). The number reads in the text colour\n there; the accent keeps the edge and the fill. Signal keeps\n Peek's blue-on-wash, which reads. */\n 'border-accent-primary bg-accent-muted text-accent-primary hover:border-accent-hover ship:text-text-primary'\n : 'border-border-default bg-bg-inset text-text-primary hover:border-border-strong hover:bg-bg-hover',\n className,\n )}\n {...props}\n >\n {/* Decorative: the control is named by `aria-label`, and a glyph read\n aloud is noise. 16px so the emoji is legible at chip scale. */}\n <span aria-hidden=\"true\" className=\"shrink-0 text-body-1 leading-none\">\n {emoji}\n </span>\n {/* `text-chip` stands alone here, so there is nothing to merge it with.\n The old note said it must NEVER be merged; that stopped being true\n when `cn()` was taught the ramp \u2014 `chip` is in it, and `cn.test.ts`\n pins that. */}\n <span className=\"text-chip signal:font-mono signal:text-small signal:font-semibold signal:tabular-nums\">\n {count}\n </span>\n </Toggle>\n )\n}\n", "import { type InputHTMLAttributes } from 'react'\nimport { Input } from '@base-ui/react/input'\nimport { cn } from './cn'\nimport { Kbd } from './Kbd'\n\n/**\n * Peek's SearchInput (2026-09-01), verbatim: an inset field with a hairline\n * border that strengthens on focus, and an optional keyboard hint at the\n * right edge \u2014 under Signal, the hint becomes the mono `kbd` chip with the\n * thicker bottom edge.\n *\n * The one product string \u2014 Peek's default placeholder \"Search Peek...\" \u2014\n * stays in Peek; the default here says only \"Search\u2026\".\n *\n * In Peek's top bar this is a launcher affordance rather than a live field:\n * the input is `pointer-events-none` and clicking the surround opens the\n * command launcher. The component is the same either way.\n *\n * On Base UI's `Input` since stage 3 of the migration (2026-09-07), so a\n * search field inside a `Field` is labelled by it without the caller wiring\n * an id. The surround stays ours: the border, the focus-within rule and the\n * `Kbd` hint are this component's, not the input's.\n */\nexport interface SearchInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'className'> {\n /** A keyboard hint drawn at the right edge, e.g. \"Ctrl+K\". */\n shortcut?: string\n className?: string\n}\n\nexport function SearchInput({ shortcut, className, placeholder = 'Search\u2026', ...props }: SearchInputProps) {\n return (\n <div\n className={cn(\n 'flex gap-2 items-center px-3 py-2 rounded-lg',\n 'bg-bg-inset border border-border-default',\n 'focus-within:border-border-strong transition-colors',\n className,\n )}\n >\n <Input\n className=\"flex-1 min-w-0 bg-transparent text-input-value text-text-primary placeholder:text-text-muted outline-none\"\n placeholder={placeholder}\n {...props}\n />\n {shortcut && <Kbd>{shortcut}</Kbd>}\n </div>\n )\n}\n", "import type { ReactNode } from 'react'\nimport { IconChevronRight } from '@tabler/icons-react'\nimport { useRender } from '@base-ui/react/use-render'\nimport { cn } from './cn'\nimport { IconButton } from './IconButton'\nimport { SectionLabel } from './SectionLabel'\n\n/**\n * The 32px row a section starts with (Peek's SectionHeader, 2026-09-01):\n * a SectionLabel, an optional collapse chevron that makes the title the\n * toggle, and actions that appear while the row is hovered or focused, as\n * IconButtons with tooltips.\n *\n * Three things changed on 2026-09-09, for `CollapsibleSection`:\n *\n * - **The title is a button when it toggles.** It was a `div` with an\n * `onClick`, so the keyboard could not open or close a section at all.\n * The button fills the row up to the actions, so the whole row is still\n * the hit target; the actions sit beside it rather than inside it,\n * because a button inside a button is invalid HTML \u2014 and was why every\n * action had to stop its click from also toggling.\n * - **The hover is CSS.** The fill and the actions' reveal were React\n * state (`isHovered`), which the README forbids \u2014 a mount cannot stay in\n * step with a transition. `group-hover` now; the actions are in the row\n * at `opacity-0` until hovered or focused, so a keyboard user reaches\n * them too.\n * - **`render`** lets `CollapsibleSection` hand in Base UI's\n * `Collapsible.Trigger` as the title button \u2014 one header for the\n * hand-held section (`isExpanded` / `onToggle`) and the Base UI one,\n * with no second click handler.\n *\n * One API change from Peek's original, safe because no call site used the\n * old shape yet: the two hard-wired action slots (add, sort) became\n * `actions` \u2014 the caller brings the icon, the tooltip and the handler.\n */\nexport interface SectionAction {\n /** 16px, stroke 1.5. */\n icon: ReactNode\n tooltip: string\n onClick: () => void\n}\n\nexport interface SectionHeaderProps {\n title: string\n /** Collapsible: draws the chevron and makes the title a button that toggles. */\n chevron?: boolean\n isExpanded?: boolean\n onToggle?: () => void\n /**\n * Beside the title and always visible, before the actions \u2014 a count.\n *\n * The actions come and go with the hover; this does not, because a count is\n * information rather than an affordance. Peek's Screener header carries its\n * number as a `Chip` here, and was the last hand-drawn folding header in the\n * app for want of the slot (ADOPTION B23).\n */\n trailing?: ReactNode\n /** Right-aligned, in the order given. */\n actions?: SectionAction[]\n /** `hover` reveals the actions while the row is hovered or focused; `always` keeps them. */\n showActions?: 'hover' | 'always'\n /**\n * What the title renders as, in Base UI's manner. A plain button with\n * `onToggle` by default; `CollapsibleSection` hands in `Collapsible.Trigger`.\n */\n render?: useRender.RenderProp\n className?: string\n}\n\nexport function SectionHeader({ title, chevron = false, isExpanded = true, onToggle, trailing, actions, showActions = 'hover', render, className }: SectionHeaderProps) {\n const titleElement = useRender({\n render: render ?? (chevron ? <button type=\"button\" onClick={onToggle} aria-expanded={isExpanded} /> : <span />),\n props: {\n // `text-left`: a button centres its text. `h-full` and `flex-1`: the\n // whole row up to the actions is the hit target, as it was when the\n // row itself carried the click.\n className: 'flex h-full min-w-0 flex-1 items-center gap-1 text-left',\n children: (\n <>\n {chevron && (\n <IconChevronRight\n size={12}\n stroke={1.5}\n className={cn('shrink-0 text-text-secondary transition-transform duration-150', isExpanded && 'rotate-90')}\n />\n )}\n <SectionLabel>{title}</SectionLabel>\n </>\n ),\n },\n })\n\n return (\n <div\n className={cn(\n 'group flex h-[32px] items-center gap-1 rounded-lg px-2 transition-colors',\n // The fill says \"this does something\": a row with a toggle or actions\n // lights up, a fixed heading over rows does not (2026-09-09, the\n // Sidebar's fixed group).\n (chevron || (actions && actions.length > 0)) && 'hover:bg-bg-hover',\n className,\n )}\n >\n {titleElement}\n {/* Outside the title button, like the actions: a chip inside a button\n would be part of the button's accessible name and part of its hit\n target, and the count is neither. */}\n {trailing != null && <div className=\"flex shrink-0 items-center\">{trailing}</div>}\n {actions && actions.length > 0 && (\n <div\n // `-mr-1`: an IconButton is 24px around a 16px icon, so at the row's\n // `px-2` its icon ended 12px from the edge while a NavItem's number\n // ends 8px from it; pulled 4px, the icon and the number share a\n // right edge (Katerina, 2026-09-09; measured 4px off, then 0).\n className={cn(\n '-mr-1 flex shrink-0 items-center gap-1',\n showActions === 'hover' && 'opacity-0 transition-opacity focus-within:opacity-100 group-hover:opacity-100',\n )}\n >\n {actions.map((action) => (\n <IconButton key={action.tooltip} tooltip={action.tooltip} aria-label={action.tooltip} onClick={action.onClick}>\n {action.icon}\n </IconButton>\n ))}\n </div>\n )}\n </div>\n )\n}\n", "import { useState, type ReactNode } from 'react'\nimport { Collapsible } from '@base-ui/react/collapsible'\nimport { cn } from './cn'\nimport { SectionHeader, type SectionAction } from './SectionHeader'\n\n/**\n * A section that opens and closes: a `SectionHeader` whose title is the\n * toggle, and the rows under it, sliding shut and open (Katerina,\n * 2026-09-09, for the sidebars of both apps). On Base UI's `Collapsible`\n * (D6): the state, the title button's `aria-expanded` and `aria-controls`,\n * Enter and Space, the panel's height for the slide, and `hiddenUntilFound`\n * \u2014 the browser's find-in-page can find a row inside a closed section and\n * open it, because the rows stay in the DOM while closed.\n *\n * Three ways to hold the state, from least to most work for the caller:\n *\n * - none: `defaultOpen` (open unless told otherwise), kept while mounted;\n * - `storageKey`: the same, and this browser remembers it under that key\n * across reloads \u2014 one prop for a sidebar that stays how you left it;\n * - `open` + `onOpenChange`: the caller owns it.\n *\n * A closed section stays closed whatever is selected inside it (her\n * ruling, 2026-09-09) \u2014 the reader closed it.\n */\nexport interface CollapsibleSectionProps {\n title: string\n /** Open unless told otherwise. */\n defaultOpen?: boolean\n /** The caller owns the state. */\n open?: boolean\n onOpenChange?: (open: boolean) => void\n /** Remember open or closed in this browser, under this key. The app prefixes it. */\n storageKey?: string\n /** Beside the title and always visible, before the actions \u2014 a count. `SectionHeader`'s. */\n trailing?: ReactNode\n /** Beside the title, revealed on hover or focus \u2014 `SectionHeader`'s. */\n actions?: SectionAction[]\n showActions?: 'hover' | 'always'\n /** The rows. */\n children: ReactNode\n /** On the section's box: `mt-2` between groups, `shrink-0` in a scrolling column. */\n className?: string\n /** On the box the rows sit in: the gap between them. */\n contentClassName?: string\n}\n\nconst OPEN = 'open'\nconst CLOSED = 'closed'\n\nfunction readStored(key: string | undefined): boolean | undefined {\n if (!key) return undefined\n try {\n const value = window.localStorage.getItem(key)\n return value === OPEN ? true : value === CLOSED ? false : undefined\n } catch {\n return undefined\n }\n}\n\nfunction writeStored(key: string | undefined, open: boolean) {\n if (!key) return\n try {\n window.localStorage.setItem(key, open ? OPEN : CLOSED)\n } catch {\n // A browser that blocks storage still gets a working section; it forgets.\n }\n}\n\nexport function CollapsibleSection({ title, defaultOpen = true, open: openProp, onOpenChange, storageKey, trailing, actions, showActions, children, className, contentClassName }: CollapsibleSectionProps) {\n const [openState, setOpenState] = useState(() => readStored(storageKey) ?? defaultOpen)\n const open = openProp ?? openState\n const setOpen = (next: boolean) => {\n setOpenState(next)\n writeStored(storageKey, next)\n onOpenChange?.(next)\n }\n return (\n <Collapsible.Root open={open} onOpenChange={setOpen} className={cn('flex flex-col', className)}>\n <SectionHeader title={title} chevron isExpanded={open} trailing={trailing} actions={actions} showActions={showActions} className=\"shrink-0\" render={<Collapsible.Trigger />} />\n {/* The slide: Base UI measures the panel and writes its height to a\n variable \u2014 `auto` again once the slide ends, so rows that arrive\n later are not clipped \u2014 and the panel is 0 high on its opening frame\n and its closing frame. The Tooltip's idiom (D26): the data attributes\n are Base UI's, the transition is ours, and none under\n prefers-reduced-motion. */}\n <Collapsible.Panel\n hiddenUntilFound\n className=\"h-[var(--collapsible-panel-height)] overflow-hidden transition-[height] duration-150 ease-out motion-reduce:transition-none data-[starting-style]:h-0 data-[ending-style]:h-0\"\n >\n <div className={cn('flex flex-col', contentClassName)}>{children}</div>\n </Collapsible.Panel>\n </Collapsible.Root>\n )\n}\n", "import type { ReactNode } from 'react'\nimport { Tabs as BaseTabs } from '@base-ui/react/tabs'\nimport { cn } from './cn'\n\n/**\n * A row of tabs. Ship's Tabs (2026-09-01), which was Peek's TopicTabs with\n * the topic-specific ids taken out; on Base UI Tabs since stage 1 of the\n * migration (2026-09-06), which is where the keyboard comes from: one Tab\n * stop for the row, arrow keys between the tabs.\n *\n * A selected tab is a neutral fill (bg-active), not the accent tint \u2014\n * Katerina's ruling (2026-08-27), extended to every app (2026-09-01). Two\n * sizes: `default` is 14px, a little more room; `small` is Peek's own\n * geometry (its inline `fontSize: 12` was the caption token spelled by hand).\n *\n * A tab's count is the sidebar's number, not a chip (Katerina, 2026-09-01,\n * superseding the chip ruling of 2026-08-26): mono, caption size, muted,\n * tabular \u2014 the one way a number sits beside a label everywhere. A count of\n * `0` is still drawn (\"Assigned to me 0\" is an answer); an absent count draws\n * nothing.\n */\nexport interface TabDef<T extends string> {\n id: T\n label: string\n /** Shown as a muted mono number after the label. Absent draws nothing; 0 is drawn. */\n count?: number\n /** 16px, stroke 1.5. */\n icon?: ReactNode\n}\n\nexport interface TabsProps<T extends string> {\n tabs: TabDef<T>[]\n active: T\n onChange: (id: T) => void\n /** `default` 14px; `small` 12px, the denser geometry. */\n size?: 'default' | 'small'\n /**\n * Names the row for assistive tech \u2014 \"Views\", \"Filters\".\n *\n * A `tablist` with no name is announced as a bare \"tab list\", which says\n * nothing and says the same nothing twice on a page with two of them\n * (measured 2026-09-08: no name, and no way to give one). Where a visible\n * heading already names it, point at that with `aria-labelledby` instead.\n */\n 'aria-label'?: string\n 'aria-labelledby'?: string\n /** Lands on the outer box, around the row. */\n className?: string\n}\n\nexport function Tabs<T extends string>({ tabs, active, onChange, size = 'default', className, ...aria }: TabsProps<T>) {\n return (\n <BaseTabs.Root\n value={active}\n onValueChange={(value, details) => {\n // Only a person's choice reaches the caller. Base UI also reports its\n // own fallbacks (an `active` that matches no tab), and those carry\n // `null`, which is not a T.\n if (details.reason === 'none') onChange(value as T)\n }}\n className={className}\n >\n <BaseTabs.List\n activateOnFocus\n aria-label={aria['aria-label']}\n aria-labelledby={aria['aria-labelledby']}\n className=\"flex items-center gap-2\"\n >\n {tabs.map((tab) => (\n <BaseTabs.Tab\n key={tab.id}\n value={tab.id}\n className={(state) =>\n cn(\n 'flex cursor-pointer items-center transition-colors',\n // gap: default is Ship's 6px; small keeps Peek's original 4px, or\n // \"small is Peek's geometry\" stops being true.\n size === 'default' ? 'gap-1.5 rounded-md px-2 py-1 text-body-2' : 'gap-1 rounded px-1.5 py-0.5 text-caption',\n state.active ? 'bg-bg-active text-text-primary' : 'text-text-secondary hover:bg-bg-hover',\n )\n }\n >\n {tab.icon}\n {/* Label and count share a baseline: a smaller text centred as a box\n (items-center) floats above the label's baseline \u2014 the digits\n read as riding high. Baseline alignment is what makes two sizes\n sit on one line. */}\n <span className=\"flex items-baseline\">\n {tab.label}\n {tab.count !== undefined ? (\n <span className=\"ml-2.5 font-mono text-caption tabular-nums text-text-secondary\">{tab.count}</span>\n ) : null}\n </span>\n </BaseTabs.Tab>\n ))}\n </BaseTabs.List>\n </BaseTabs.Root>\n )\n}\n", "import { createContext, useContext, useMemo, type ReactNode } from 'react'\nimport { Toast as BaseToast } from '@base-ui/react/toast'\nimport { IconAlertCircle, IconCircleCheck, IconCircleX } from '@tabler/icons-react'\nimport { Button } from './Button'\nimport { cn } from './cn'\n\n/**\n * Peek's Toast (Figma: Alert) and its provider (2026-09-01), verbatim.\n *\n * The pill: five types \u2014 `success`, `brand`, `neutral`, `warning`, `error` \u2014\n * a leading icon that says which (a check, a `!`, an `\u00D7`), an optional action\n * on the right. Under Signal every toast is\n * the same dark overlay pill; the type lives in the icon's colour and glow,\n * not the surface.\n *\n * The provider sits on Base UI's `Toast` since stage 6 of the migration\n * (2026-09-14). Base UI owns the portal, the timers, the stack and what a\n * screen reader hears; this file owns how a toast looks and where the stack\n * stands (bottom-left). **Up to three show at once** (D7): the newest nearest\n * the corner, a fourth hides the oldest until one of the three closes. Before\n * stage 6 a new toast replaced the standing one, and nothing announced either.\n */\nexport type ToastType = 'success' | 'brand' | 'neutral' | 'warning' | 'error'\n\nexport interface ToastProps {\n label: string\n /**\n * Which of the five this is. It decides the surface in the light themes,\n * and under Signal \u2014 where every pill is the same dark overlay \u2014 the icon\n * and its colour.\n *\n * `warning` and `error` arrived at 0.12.2, for a notice that has to stay up\n * and for one that reports a failure. Until then the set had no way to say\n * either, so a caller with bad news passed `leadingIcon: false` rather than\n * put a tick beside it.\n */\n type?: ToastType\n /** Show the leading icon \u2014 a check, a `!` or an `\u00D7`, per `type`. Defaults to true. */\n leadingIcon?: boolean\n /** When set together with onAction, renders a clickable action on the right side. */\n actionLabel?: string\n onAction?: () => void\n className?: string\n}\n\n// Signal: every toast is the same dark overlay pill (v3) \u2014 the type lives in\n// the icon color + glow, not the surface.\nconst SURFACE_STYLES: Record<ToastType, string> = {\n success: 'bg-success-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-md',\n brand: 'bg-accent-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-md',\n neutral: 'bg-bg-inset border border-border-subtle signal:border-border-default signal:shadow-md',\n warning: 'bg-warning-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-md',\n error: 'bg-error-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-md',\n}\n\n/**\n * The leading icon says which of the five this is (Katerina, 2026-09-10:\n * *\"the icons should be representative, x for error, ! for warning\"*). It was\n * a circle-check on all of them, so a warning and a failure both arrived with\n * a tick beside them \u2014 the reason Peek passed `leadingIcon: false` rather than\n * show one.\n */\nconst ICONS: Record<ToastType, typeof IconCircleCheck> = {\n success: IconCircleCheck,\n brand: IconCircleCheck,\n neutral: IconCircleCheck,\n warning: IconAlertCircle,\n error: IconCircleX,\n}\n\nconst ICON_STYLES: Record<ToastType, string> = {\n success: 'signal:text-success-default signal:drop-shadow-glow-success',\n brand: 'signal:text-text-interactive signal:drop-shadow-glow-accent',\n neutral: 'signal:text-text-secondary',\n warning: 'signal:text-warning-default signal:drop-shadow-glow-warning',\n // No glow: the theme defines `--glow-warning`, `--glow-success` and\n // `--glow-accent` and no error glow, and a colour with no token is not\n // approximated here (D16).\n error: 'signal:text-error-default',\n}\n\nconst LABEL_CLASSES = 'text-body-2 text-text-primary whitespace-nowrap'\n\nconst pillClassName = (type: ToastType, hasAction: boolean, className?: string) =>\n cn(\n 'inline-flex items-center min-h-[32px] pl-2 py-1 rounded-lg shadow-lg',\n // Without an action the label needs real right padding; the action\n // button brings its own edge, so the tight pr-1 only applies there.\n hasAction ? 'pr-1 gap-[46px]' : 'pr-3',\n SURFACE_STYLES[type],\n className,\n )\n\nfunction LeadingIcon({ type }: { type: ToastType }) {\n const Icon = ICONS[type]\n return <Icon size={16} stroke={1.5} className={cn('text-text-primary shrink-0', ICON_STYLES[type])} />\n}\n\n/**\n * One toast, drawn in place. What the provider shows is this pill; draw it\n * yourself only where a page needs a toast's look without its timing \u2014 a\n * story, a picture of the set.\n */\nexport function Toast({ label, type = 'neutral', leadingIcon = true, actionLabel, onAction, className }: ToastProps) {\n const hasAction = !!(actionLabel && onAction)\n return (\n <div className={pillClassName(type, hasAction, className)}>\n <div className=\"flex items-center gap-2 shrink-0\">\n {leadingIcon && <LeadingIcon type={type} />}\n <span className={LABEL_CLASSES}>{label}</span>\n </div>\n {hasAction && (\n <Button variant=\"outlined\" size=\"small\" onClick={onAction}>\n {actionLabel}\n </Button>\n )}\n </div>\n )\n}\n\nexport interface ToastOptions {\n label: string\n /** Which of the five this is \u2014 it decides the surface and the leading icon. Defaults to 'neutral'. */\n type?: ToastType\n /**\n * Renders an action on the right side. Pressing it runs `onAction`, if\n * given, and closes this toast \u2014 so a Dismiss needs only its label.\n */\n actionLabel?: string\n onAction?: () => void\n /** Show the leading icon \u2014 a check, a `!` or an `\u00D7`, per `type`. Defaults to true. */\n leadingIcon?: boolean\n /** Auto-dismiss after this many ms. Defaults to 5000. Pass 0 to disable. */\n durationMs?: number\n}\n\ninterface ToastValue {\n /** Shows a toast and returns its id, for `dismissToast`. */\n showToast: (opts: ToastOptions) => string\n /** Closes the toast with this id; with no id, closes every toast on screen. */\n dismissToast: (id?: string) => void\n}\n\n/** What a toast carries beyond Base UI's own fields. */\ninterface ToastData {\n leadingIcon: boolean\n actionLabel?: string\n onAction?: () => void\n}\n\nconst ToastContext = createContext<ToastValue | null>(null)\n\n/** D7: three on screen at once. */\nconst VISIBLE_TOASTS = 3\n\nexport function ToastProvider({ children }: { children: ReactNode }) {\n // One manager per provider, so `useToast` can add and close without\n // re-rendering every caller each time the list changes.\n const manager = useMemo(() => BaseToast.createToastManager<ToastData>(), [])\n const value = useMemo<ToastValue>(\n () => ({\n showToast: ({ label, type = 'neutral', leadingIcon = true, actionLabel, onAction, durationMs = 5000 }) =>\n manager.add({\n title: label,\n type,\n timeout: durationMs,\n // A failure interrupts; everything else waits for a pause.\n priority: type === 'error' ? 'high' : 'low',\n data: { leadingIcon, actionLabel, onAction },\n }),\n dismissToast: (id) => manager.close(id),\n }),\n [manager],\n )\n\n return (\n <ToastContext.Provider value={value}>\n <BaseToast.Provider toastManager={manager} limit={VISIBLE_TOASTS}>\n {children}\n <BaseToast.Portal>\n <BaseToast.Viewport className=\"fixed bottom-4 left-4 z-[100] flex flex-col-reverse items-start gap-2 pointer-events-none\">\n <ToastList />\n </BaseToast.Viewport>\n </BaseToast.Portal>\n </BaseToast.Provider>\n </ToastContext.Provider>\n )\n}\n\nfunction ToastList() {\n const { toasts, close } = BaseToast.useToastManager<ToastData>()\n // Base UI lists the newest first; the viewport is `flex-col-reverse`, so\n // the newest stands nearest the corner.\n return toasts.map((toast) => {\n const type = (toast.type ?? 'neutral') as ToastType\n const { leadingIcon = true, actionLabel, onAction } = toast.data ?? {}\n return (\n <BaseToast.Root\n key={toast.id}\n toast={toast}\n // Towards the corner it stands in.\n swipeDirection={['left', 'down']}\n // A fourth toast hides the oldest (`data-limited`) until there is room.\n className={pillClassName(type, !!actionLabel, 'pointer-events-auto data-[limited]:hidden')}\n >\n <div className=\"flex items-center gap-2 shrink-0\">\n {leadingIcon && <LeadingIcon type={type} />}\n <BaseToast.Title render={<span />} className={LABEL_CLASSES} />\n </div>\n {actionLabel && (\n <BaseToast.Action\n onClick={() => {\n onAction?.()\n close(toast.id)\n }}\n // The same Button the toast drawn in place uses, told to *be* the\n // Base UI action rather than sit inside one (Katerina, 16 September).\n render={\n <Button variant=\"outlined\" size=\"small\">\n {actionLabel}\n </Button>\n }\n />\n )}\n </BaseToast.Root>\n )\n })\n}\n\nexport function useToast(): ToastValue {\n const ctx = useContext(ToastContext)\n if (!ctx) throw new Error('useToast must be used within ToastProvider')\n return ctx\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["jsx", "jsxs", "jsx", "jsxs", "BaseButton", "jsx", "jsx", "jsx", "jsx", "jsxs", "jsx", "jsx", "jsxs", "BaseButton", "jsx", "jsxs", "jsx", "IconX", "jsx", "jsxs", "IconX", "BaseButton", "jsx", "jsxs", "BaseButton", "jsx", "jsx", "jsxs", "createContext", "useContext", "useLayoutEffect", "useMemo", "useRef", "useState", "IconLoader2", "useState", "BaseButton", "IconX", "jsx", "Fragment", "jsx", "jsxs", "GAP", "VIEWPORT_PAD", "jsx", "jsxs", "BaseButton", "IconX", "useState", "GAP", "VIEWPORT_PAD", "jsx", "jsxs", "jsx", "jsxs", "jsx", "jsxs", "Fragment", "jsx", "jsxs", "createContext", "useContext", "useRef", "useMemo", "useState", "useLayoutEffect", "IconLoader2", "Fragment", "jsx", "jsxs", "useRef", "jsx", "jsxs", "jsx", "jsxs", "BaseButton", "jsx", "jsxs", "BaseButton", "Fragment", "jsx", "jsxs", "jsxs", "Fragment", "jsx", "useRef", "IconCheck", "IconChevronDown", "jsx", "jsxs", "GAP", "VIEWPORT_PAD", "IconChevronDown", "IconCheck", "jsx", "TextInput", "forwardRef", "BaseField", "jsx", "forwardRef", "Textarea", "BaseField", "useState", "useRef", "Dialog", "IconX", "jsx", "jsxs", "Dialog", "useRef", "IconX", "Fragment", "jsx", "jsxs", "useState", "useEffect", "useRef", "useState", "BaseField", "Input", "jsx", "useState", "useRef", "useEffect", "BaseField", "Input", "jsx", "Fragment", "useCallback", "useLayoutEffect", "useRef", "useState", "jsx", "jsxs", "useRef", "useState", "useCallback", "useLayoutEffect", "Fragment", "jsx", "jsxs", "useMemo", "jsx", "jsxs", "GAP", "VIEWPORT_PAD", "useMemo", "jsx", "jsx", "jsx", "jsxs", "GAP", "VIEWPORT_PAD", "OPEN_DELAY", "jsx", "jsx", "jsxs", "jsx", "jsx", "jsxs", "jsx", "jsxs", "Input", "jsx", "jsxs", "Input", "IconChevronRight", "Fragment", "jsx", "jsxs", "IconChevronRight", "useState", "jsx", "jsxs", "useState", "jsx", "jsxs", "createContext", "useContext", "useMemo", "IconAlertCircle", "jsx", "jsxs", "IconAlertCircle", "createContext", "useMemo", "useContext"]
|
|
3
|
+
"sources": ["../src/cn.ts", "../src/ScrollArea.tsx", "../src/TopBar.tsx", "../src/AppShell.tsx", "../src/AttachmentCard.tsx", "../src/Link.tsx", "../src/Card.tsx", "../src/IconButton.tsx", "../src/formBusy.ts", "../src/Tooltip.tsx", "../src/Kbd.tsx", "../src/Avatar.tsx", "../src/AvatarGroup.tsx", "../src/Banner.tsx", "../src/Button.tsx", "../src/Checkbox.tsx", "../src/Chip.tsx", "../src/CommandPalette.tsx", "../src/ChipInput.tsx", "../src/Menu.tsx", "../src/triggerDisabled.ts", "../src/SectionLabel.tsx", "../src/EmptyState.tsx", "../src/Field.tsx", "../src/Form.tsx", "../src/Skeleton.tsx", "../src/InlineChip.tsx", "../src/IdentityMenu.tsx", "../src/Divider.tsx", "../src/Person.tsx", "../src/PersonTrigger.tsx", "../src/FilePicker.tsx", "../src/Select.tsx", "../src/TextInput.tsx", "../src/Textarea.tsx", "../src/ConfirmDialog.tsx", "../src/DialogShell.tsx", "../src/EditableText.tsx", "../src/ProgressBar.tsx", "../src/Breadcrumb.tsx", "../src/NavItem.tsx", "../src/Popover.tsx", "../src/Toolbar.tsx", "../src/ReactionPicker.tsx", "../src/PreviewCard.tsx", "../src/Rail.tsx", "../src/RailItem.tsx", "../src/Sidebar.tsx", "../src/Property.tsx", "../src/Reaction.tsx", "../src/SearchInput.tsx", "../src/SectionHeader.tsx", "../src/CollapsibleSection.tsx", "../src/Tabs.tsx", "../src/Toast.tsx"],
|
|
4
|
+
"sourcesContent": ["import { clsx, type ClassValue } from 'clsx'\nimport { extendTailwindMerge } from 'tailwind-merge'\n\n/**\n * The preset's type ramp, by name, so tailwind-merge can classify the token\n * classes as sizes. Stock tailwind-merge has never heard of `text-body-2`,\n * files it under text-COLOUR, and lets `text-text-primary` in the same\n * merged list knock it out \u2014 the trap Katerina's Tabs review confirmed by\n * measurement (2026-09-01), and the reason shared components used to spell\n * sizes as arbitrary values. Naming the ramp here retires that rule for\n * everything using this cn \u2014 the package and, since the 2026-09-01 dedupe,\n * both apps.\n *\n * Must match `tailwind-preset.js` `fontSize` key for key; `cn.test.ts`\n * asserts it (the preset imports node-only modules, so it cannot be\n * imported here without dragging them into the browser bundle).\n */\nconst FONT_SIZE_TOKENS = [\n 'h1', 'h2', 'h3', 'h4', 'h5',\n 'body-1', 'body-2', 'body-2-strong', 'caption', 'menu',\n 'btn-default', 'btn-small', 'input-label', 'input-value', 'input-helper', 'chip', 'small',\n]\n\n/**\n * The preset's box-shadow and drop-shadow keys, for the same reason: stock\n * tailwind-merge files an unknown `shadow-x` under shadow-COLOUR, so\n * `shadow-focus-ring` beside `shadow-glow-warning` lost one of them, and\n * `shadow-glow-warning` beside `shadow-md` kept both (measured 2026-09-06).\n * `cn.test.ts` pins these to the preset too.\n */\nconst BOX_SHADOW_TOKENS = ['sm', 'md', 'lg', 'focus-ring', 'glow-warning', 'glow-success', 'glow-accent', 'highlight-inset']\nconst DROP_SHADOW_TOKENS = ['glow-success', 'glow-accent', 'glow-warning']\n\nconst twMerge = extendTailwindMerge({\n extend: {\n classGroups: {\n 'font-size': [{ text: FONT_SIZE_TOKENS }],\n shadow: [{ shadow: BOX_SHADOW_TOKENS }],\n 'drop-shadow': [{ 'drop-shadow': DROP_SHADOW_TOKENS }],\n },\n },\n})\n\n/** Merge class names, last-one-wins on conflicting Tailwind utilities. */\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n\nexport { FONT_SIZE_TOKENS as _fontSizeTokens, BOX_SHADOW_TOKENS as _boxShadowTokens, DROP_SHADOW_TOKENS as _dropShadowTokens }\n", "import type { ReactNode, Ref, UIEventHandler } from 'react'\nimport { ScrollArea as BaseScrollArea } from '@base-ui/react/scroll-area'\nimport { cn } from './cn'\n\n/**\n * A region that scrolls without taking width for its scrollbar.\n *\n * A native scrollbar is part of the layout: the moment content overflows, the\n * bar takes its width from the content, the text column narrows, and the\n * right-hand padding looks wider than the left. Katerina saw it in every\n * scrolling surface of both apps (2026-09-08). Base UI's `ScrollArea` hides\n * the native bar and draws its own over the content, so nothing moves when\n * it appears; the bar shows while the pointer is over the region or the\n * content is moving, and fades otherwise.\n *\n * Three boxes, and each class prop lands on one:\n *\n * - the **region** (`className`): its size and placement \u2014 `h-[240px]`, a\n * grid cell, `flex-1 min-h-0`;\n * - the **viewport** (`viewportClassName`): the box that scrolls \u2014 a\n * `max-h-*` cap goes here, and nowhere else, because a cap on the region\n * lets the viewport grow to its content and nothing scrolls (measured,\n * 2026-09-08);\n * - the **content** (`contentClassName`): the box the children sit in \u2014 the\n * padding, the gap, `flex flex-col`. Base UI watches this box for size\n * changes; without it the bar kept showing on Ship's page for overflow that\n * was no longer there (measured, 2026-09-09).\n *\n * The bar is `border-strong` on nothing, 6px wide, inset 2px from the edge \u2014\n * the thin scrollbar both apps had styled by hand in their `index.css`, drawn\n * once here instead.\n *\n * `orientation` says which way the region scrolls; a table that is wider\n * than its box scrolls `horizontal`, a list `vertical` (the default), a board\n * `both`. A vertical region keeps its content no wider than itself, so a\n * long label still truncates.\n */\nexport interface ScrollAreaProps {\n orientation?: 'vertical' | 'horizontal' | 'both'\n /** On the region: its size and placement. */\n className?: string\n /** On the viewport, the box that scrolls: a `max-h-*` cap. */\n viewportClassName?: string\n /** On the content, the box the children sit in: padding, gap, layout. */\n contentClassName?: string\n /**\n * The scrolling box itself, for a region that reads or drives its own\n * scrolling: `ref.current.scrollTop`, `scrollTo`, `scrollHeight`.\n *\n * A conversation is the case this exists for (ADOPTION B20): it arrives at\n * the newest message, keeps its place when older ones load above, and jumps\n * to the bottom when a reply is sent \u2014 none of which the region can do for\n * the caller, and all of which the caller cannot do without the box.\n */\n viewportRef?: Ref<HTMLDivElement>\n /** Fires as the viewport scrolls \u2014 the unread policy's input, beside `viewportRef`. */\n onScroll?: UIEventHandler<HTMLDivElement>\n children: ReactNode\n}\n\nconst BAR = 'flex touch-none select-none rounded-full opacity-0 transition-opacity delay-300 data-[hovering]:opacity-100 data-[hovering]:delay-0 data-[scrolling]:opacity-100 data-[scrolling]:delay-0'\nconst THUMB = 'rounded-full bg-border-strong'\n\nexport function ScrollArea({ orientation = 'vertical', className, viewportClassName, contentClassName, viewportRef, onScroll, children }: ScrollAreaProps) {\n const vertical = orientation !== 'horizontal'\n const horizontal = orientation !== 'vertical'\n return (\n <BaseScrollArea.Root className={cn('relative min-h-0 min-w-0', className)}>\n {/* `overscroll-contain`, per axis and only while that axis has more to\n show: a list that reaches its end does not hand the wheel to the page\n behind it \u2014 the rule every popup here has kept since Peek \u2014 but a\n region with nothing to scroll one way passes that way's wheel through.\n Base UI's viewport is `overflow: scroll` on both axes whatever\n `orientation` says, so a sideways table is also a vertical scroll box\n with no room in it, and an unconditional `contain` stopped the page\n under every table and board (Katerina, 2026-09-09: 0px of page scroll\n with the pointer over Ship's table, 446px without the line; PLAN\n Finding 40). `data-has-overflow-x` / `-y` are Base UI's word for\n \"this axis really overflows\", written on the viewport as it changes. */}\n <BaseScrollArea.Viewport\n ref={viewportRef}\n onScroll={onScroll}\n className={cn(\n 'h-full w-full outline-none data-[has-overflow-x]:overscroll-x-contain data-[has-overflow-y]:overscroll-y-contain',\n viewportClassName,\n )}\n >\n {/* Base UI gives the content box `min-width: fit-content`, which is\n right when the region scrolls sideways and wrong when it does not:\n a row's `truncate` needs a box no wider than the viewport. */}\n <BaseScrollArea.Content className={contentClassName} style={horizontal ? undefined : { minWidth: 0 }}>\n {children}\n </BaseScrollArea.Content>\n </BaseScrollArea.Viewport>\n {vertical && (\n <BaseScrollArea.Scrollbar orientation=\"vertical\" className={cn(BAR, 'w-1.5 justify-center py-0.5 pr-0.5')}>\n <BaseScrollArea.Thumb className={cn(THUMB, 'w-full')} />\n </BaseScrollArea.Scrollbar>\n )}\n {horizontal && (\n <BaseScrollArea.Scrollbar orientation=\"horizontal\" className={cn(BAR, 'h-1.5 flex-col justify-center px-0.5 pb-0.5')}>\n <BaseScrollArea.Thumb className={cn(THUMB, 'h-full')} />\n </BaseScrollArea.Scrollbar>\n )}\n {vertical && horizontal && <BaseScrollArea.Corner />}\n </BaseScrollArea.Root>\n )\n}\n", "import type { ReactNode } from 'react'\nimport { cn } from './cn'\n\n/**\n * The 52px top bar, in two manners:\n *\n * - `solid` \u2014 in flow: a hairline under it, the surface behind it, the\n * frame's first row (2026-09-02, verbatim from the structured app; its\n * product title moved to the caller).\n * - `floating` \u2014 an overlay: absolute over the content, no border and no\n * background, and the bar itself ignores the pointer \u2014 only its clusters\n * catch clicks, so the content underneath stays reachable (2026-09-02,\n * verbatim from the floating app).\n *\n * Geometry both apps agreed on before extraction: 52px tall, `pl-5\n * pr-[26px]`, right cluster `gap-[6px]`. The left region is two slots\n * side by side \u2014 `menu` (the menu button, in an app whose navigation\n * collapses) and `logo` (the mark or name beside it; Katerina,\n * 2026-09-02). This component does not know who you are or what is being\n * searched; it only knows where those go.\n */\nexport interface TopBarProps {\n variant?: 'solid' | 'floating'\n /** Far left: the menu button, in an app whose navigation collapses. Stays the caller's \u2014 the bar only places it. */\n menu?: ReactNode\n /** Beside the menu button: the app's logo mark, or its name as text. */\n logo?: ReactNode\n /** The centre: a search field, or a launcher affordance \u2014 or nothing; the slot holds its place. */\n search?: ReactNode\n /** The right cluster \u2014 an `IdentityMenu` and its neighbours. */\n right?: ReactNode\n className?: string\n}\n\nexport function TopBar({ variant = 'solid', menu, logo, search, right, className }: TopBarProps) {\n const floating = variant === 'floating'\n return (\n <header\n className={cn(\n 'flex h-[52px] items-center pl-5 pr-[26px]',\n floating\n ? 'pointer-events-none absolute left-0 right-0 top-0 z-10'\n : 'shrink-0 gap-4 border-b border-border-default bg-bg-surface',\n className,\n )}\n >\n {(menu || logo) && (\n <div className={cn('flex shrink-0 items-center gap-2', floating && 'pointer-events-auto')}>\n {menu}\n {logo && <div className=\"flex items-center text-body-2-strong text-text-primary\">{logo}</div>}\n </div>\n )}\n <div className={cn('flex min-w-0 flex-1 items-center justify-center', floating && 'pointer-events-auto')}>{search}</div>\n <div className={cn('flex shrink-0 items-center gap-[6px]', floating && 'pointer-events-auto')}>{right}</div>\n </header>\n )\n}\n", "import type { ReactNode } from 'react'\nimport { cn } from './cn'\nimport { ScrollArea } from './ScrollArea'\nimport { TopBar } from './TopBar'\n\n/**\n * The frame of an app, in the two manners that pair with the TopBar's\n * (Katerina, 2026-09-02: the two shells go together \u2014 the solid bar with\n * the sidebar, the floating bar with the rail):\n *\n * - `solid` \u2014 the structured frame: a solid TopBar, the navigation\n * column, the content beside it, sidebar and main scrolling\n * independently (2026-09-02, verbatim from the structured app).\n * - `floating` \u2014 the floating frame: the TopBar floats over the top\n * edge, the rail stands on the app background, and the content lives\n * in the rounded card beside it (2026-09-02, the floating app's frame\n * geometry; its collapse animation and launcher stay in the app).\n *\n * The banner belongs to the content area (her ruling, 2026-09-02): it\n * renders at the top of the main column \u2014 inside the card, in the\n * floating manner \u2014 never across the navigation.\n *\n * Layout only. What goes in each region is the caller's; this component\n * has no data and no routes.\n */\nexport interface AppShellProps {\n variant?: 'solid' | 'floating'\n /** The top bar's menu button, in an app whose navigation collapses. */\n menu?: ReactNode\n /** The top bar's left: the app's logo mark, or its name as text. */\n logo?: ReactNode\n /** The top bar's centre \u2014 a search field, when there is one. */\n search?: ReactNode\n /** The top bar's right cluster \u2014 an `IdentityMenu`. */\n identity?: ReactNode\n /** A `Banner`, or nothing \u2014 drawn at the top of the content area. */\n banner?: ReactNode\n /** The navigation: a `Sidebar` in the solid manner, a `Rail` in the floating one. */\n nav: ReactNode\n children: ReactNode\n}\n\nexport function AppShell({ variant = 'solid', menu, logo, search, identity, banner, nav, children }: AppShellProps) {\n const bar = <TopBar variant={variant} menu={menu} logo={logo} search={search} right={identity} />\n\n if (variant === 'floating') {\n return (\n <div className=\"relative h-full min-h-0 overflow-hidden bg-bg-base\">\n {bar}\n <div className=\"flex h-full pb-4 pr-4 pt-[52px]\">\n {nav}\n <div\n className={cn(\n 'flex min-w-0 flex-1 flex-col overflow-hidden rounded-2xl bg-bg-surface',\n 'signal:border signal:border-border-subtle signal:shadow-highlight-inset',\n )}\n >\n {banner}\n <main className=\"flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden\">{children}</main>\n </div>\n </div>\n </div>\n )\n }\n\n return (\n /* `relative overflow-hidden` is the seal the floating manner already has\n (and the ScrollArea's own root is `relative`, so an absolutely placed\n stray inside a page now belongs to the region and scrolls with it),\n and it takes both halves: an absolutely positioned descendant with no\n positioned ancestor belongs to the *viewport*, so a scroll container\n never clips it and the document itself gains its position as scroll\n range \u2014 the whole page scrolls, navigation and top bar included.\n `relative` claims such strays for the shell; `overflow-hidden` clips\n them at its edge. Either alone seals nothing. */\n <div className=\"relative flex h-full min-h-0 flex-col overflow-hidden bg-bg-base text-text-primary\">\n {bar}\n <div className=\"flex min-h-0 flex-1\">\n {nav}\n <div className=\"flex min-h-0 min-w-0 flex-1 flex-col\">\n {banner}\n {/* The frame owns the page's scrollbar (Katerina, 2026-09-09: the\n Issues page still had a native bar \u2014 D40 said everywhere, and the\n one place every page passes through had been left out). A\n `ScrollArea` here means no page can forget it. The content box is\n at least the viewport's height and grows with a tall page, so Base\n UI sees its size change (the morning's phantom bar was a content\n box it could not watch); `main` fills it as a flex column. The\n page contract (the AppShell page says it): a page is a flex\n child of `main` \u2014 `flex-1` to fill and scroll here; `flex-1\n min-h-0 [contain:size]` to take exactly the frame's height and\n scroll inside itself, as Ship's detail grids do \u2014 and the outer\n region, with nothing to scroll, passes the wheel through. Never\n `h-full`: a box that is *at least* the viewport's height gives a\n percentage nothing to resolve against \u2014 measured, all four Ship\n pages, 2026-09-09. */}\n <ScrollArea className=\"min-h-0 flex-1\" contentClassName=\"flex min-h-full flex-col\">\n <main className=\"flex min-w-0 flex-1 flex-col\">{children}</main>\n </ScrollArea>\n </div>\n </div>\n </div>\n )\n}\n", "import { useLayoutEffect, useRef, useState, type ComponentPropsWithRef, type FC } from 'react'\nimport { Button as BaseButton } from '@base-ui/react/button'\nimport {\n IconAlertCircle,\n IconAlertTriangle,\n IconDownload,\n IconFile,\n IconFileTypeCsv,\n IconFileTypeDocx,\n IconFileTypePdf,\n IconFileTypePpt,\n IconFileTypeTxt,\n IconFileTypeXls,\n IconFileTypeZip,\n IconJson,\n IconLoader2,\n IconMarkdown,\n IconX,\n} from '@tabler/icons-react'\nimport { Card } from './Card'\nimport { IconButton } from './IconButton'\nimport { Link } from './Link'\nimport { WithTooltip } from './Tooltip'\nimport { cn } from './cn'\n\n/**\n * Something attached \u2014 a document or an image \u2014 drawn as a card.\n *\n * Peek's two attachment components, class for class (UIG-27, Katerina's ruling\n * of 14 September that both apps draw one): `FileAttachmentCard`, the card on\n * something already posted, and `PendingAttachmentChip`, the card in a composer\n * waiting to go. What stays in each app is what only the app can do: fetch the\n * bytes with the reader's own permission, open an image full screen, save a\n * file. The card is told the result \u2014 a `src`, an `href`, a `state` \u2014 and hands\n * a click back through `onOpen`, `onDownload` and `onRemove`.\n *\n * Two shapes, decided by the file: an **image** with a picture is a 180px\n * thumbnail you can open; anything else is a 240px **row** \u2014 a type tile, the\n * name, and what it is. Waiting to be sent (`pending`) it is a 200px row with a\n * remove control on its corner.\n *\n * One change from Peek, by ruling: a file that could not be read has a dashed\n * hairline, as every card that cannot be read does.\n */\nexport type AttachmentCardState = 'ready' | 'loading' | 'unreadable' | 'uploading' | 'failed' | 'warning'\n\nconst ICON_BY_EXTENSION: Record<string, FC<{ size?: number; stroke?: number; className?: string }>> = {\n pdf: IconFileTypePdf,\n doc: IconFileTypeDocx,\n docx: IconFileTypeDocx,\n xls: IconFileTypeXls,\n xlsx: IconFileTypeXls,\n ppt: IconFileTypePpt,\n pptx: IconFileTypePpt,\n csv: IconFileTypeCsv,\n txt: IconFileTypeTxt,\n rtf: IconFileTypeTxt,\n zip: IconFileTypeZip,\n json: IconJson,\n md: IconMarkdown,\n}\n\nconst IMAGE_EXTENSIONS = new Set(['png', 'jpg', 'jpeg', 'gif', 'webp', 'heic', 'heif', 'svg'])\n\nconst extensionOf = (name: string) => {\n const dot = name.lastIndexOf('.')\n return dot >= 0 ? name.slice(dot + 1).toLowerCase() : ''\n}\n\nconst isImage = (name: string, contentType?: string) => IMAGE_EXTENSIONS.has(extensionOf(name)) || (contentType?.startsWith('image/') ?? false)\n\n/** `'2.4 MB'`, `'812 KB'`, `'340 bytes'`. */\nconst formatBytes = (bytes: number) => {\n if (bytes < 1024) return `${bytes} bytes`\n const kb = bytes / 1024\n if (kb < 1024) return `${Math.round(kb)} KB`\n return `${(kb / 1024).toFixed(1)} MB`\n}\n\n/** `'PDF'`, `'ZIP'`, `'DOCX'`, `'FILE'`. */\nconst typeLabelOf = (name: string) => extensionOf(name).toUpperCase() || 'FILE'\n\nconst TILE_CLASSES = 'size-9 rounded-md bg-bg-active flex items-center justify-center shrink-0 text-text-secondary'\nconst NAME_CLASSES = 'text-caption font-medium text-text-primary truncate'\nconst NOTE_CLASSES = 'text-small tracking-wide leading-tight truncate'\n\nfunction TypeIcon({ name }: { name: string }) {\n const Icon = ICON_BY_EXTENSION[extensionOf(name)] ?? IconFile\n return <Icon size={20} stroke={1.5} />\n}\n\n/**\n * A line that truncates, with its full words on hover only when they are cut off \u2014 a name that fits, or a size,\n * shows nothing extra (Katerina, 2026-09-14; Breadcrumb's rule, 2026-09-01). `hint`, when given, is always on hover:\n * it says more than the line, not the same words again.\n *\n * Measured like Breadcrumb's crumbs: on mount, when the line resizes, and when the fonts arrive \u2014 a name set in\n * the fallback face can fit and then not, in a line whose box never changes. The wrapper is `flex-col` so the\n * text stretches across it, as wide as it is without one.\n */\nfunction Truncating({ text, hint, className, wrapperClassName }: { text: string; hint?: string; className: string; wrapperClassName?: string }) {\n const ref = useRef<HTMLSpanElement>(null)\n const [cut, setCut] = useState(false)\n useLayoutEffect(() => {\n const el = ref.current\n if (!el) return\n let live = true\n const measure = () => {\n if (live) setCut(el.scrollWidth > el.clientWidth)\n }\n measure()\n void document.fonts?.ready.then(measure)\n // jsdom has neither layout nor ResizeObserver; without this guard an app cannot render a card in its tests.\n if (typeof ResizeObserver === 'undefined') return () => void (live = false)\n const observer = new ResizeObserver(measure)\n observer.observe(el)\n return () => {\n live = false\n observer.disconnect()\n }\n // `cut` re-runs it: wrapping the line mounts a new element, and that one is the one to watch.\n }, [text, cut])\n const line = (\n <span ref={ref} className={className}>\n {text}\n </span>\n )\n if (!hint && !cut) return line\n return (\n <WithTooltip label={hint ?? text} wrapperClassName={cn('min-w-0 flex-col', wrapperClassName)}>\n {line}\n </WithTooltip>\n )\n}\n\nexport interface AttachmentCardProps extends Omit<ComponentPropsWithRef<'div'>, 'children'> {\n /** The name it was attached under, extension included: it decides the icon and the type label. */\n name: string\n /** In bytes. */\n size?: number\n /** Its MIME type, where known: an `image/\u2026` type is an image whatever its name. */\n contentType?: string\n /** Where a document opens, in a new tab. Without it the row is dimmed and opens nothing. */\n href?: string\n /** The picture an image card draws. Without it an image is drawn as a row. */\n src?: string\n /** What a click on an image's picture does \u2014 the app's full-screen view. */\n onOpen?: () => void\n /** Shows a download control on hover or focus, and calls this. */\n onDownload?: () => void\n /** Waiting to be sent: the composer's 200px row, with a remove control on its corner. */\n pending?: boolean\n /** Removes a pending card. */\n onRemove?: () => void\n /**\n * `ready` \u00B7 `loading` (an image on its way) \u00B7 `unreadable` (it could not be read) \u00B7\n * `uploading`, `failed`, `warning` (while pending). Default `ready`.\n */\n state?: AttachmentCardState\n /** The line under the name, in place of its type and size \u2014 why it failed, or what the warning is. */\n note?: string\n /** The note's full words, on hover, where the line is short. */\n noteHint?: string\n}\n\nexport function AttachmentCard({\n name,\n size,\n contentType,\n href,\n src,\n onOpen,\n onDownload,\n pending = false,\n onRemove,\n state = 'ready',\n note,\n noteHint,\n className,\n ...props\n}: AttachmentCardProps) {\n const image = isImage(name, contentType)\n const sizeText = size === undefined ? '' : formatBytes(size)\n\n if (pending) {\n const failed = state === 'failed'\n const warning = state === 'warning'\n return (\n <Card\n fill=\"elevated\"\n className={cn('group relative flex items-center gap-2 w-[200px] p-1.5 pr-3', failed ? 'border-error-default' : warning && 'border-warning-default', className)}\n {...props}\n >\n <div className={cn(TILE_CLASSES, 'overflow-hidden')}>\n {state === 'uploading' ? (\n <IconLoader2 size={16} stroke={1.5} className=\"animate-spin\" />\n ) : failed ? (\n <IconAlertCircle size={16} stroke={1.5} className=\"text-error-default\" />\n ) : warning ? (\n <IconAlertTriangle size={16} stroke={1.5} className=\"text-warning-default\" />\n ) : image && src ? (\n <img src={src} alt={name} className=\"size-full object-cover\" />\n ) : (\n <span className=\"text-menu font-semibold\">{typeLabelOf(name)}</span>\n )}\n </div>\n <div className=\"flex flex-col gap-[1px] min-w-0\">\n <Truncating text={name} className={NAME_CLASSES} />\n <Truncating\n text={(failed || warning ? note : state === 'uploading' ? (note ?? 'Uploading\u2026') : (note ?? sizeText)) ?? ''}\n hint={noteHint}\n className={cn(NOTE_CLASSES, failed ? 'text-error-default' : warning ? 'text-warning-default' : 'text-text-secondary')}\n />\n </div>\n {/* On Base UI's Button, as InputChip's \u2715 is (Katerina, 2026-09-14): IconButton is a 24px square\n that fills on hover, and this is Peek's 20px round badge on the card's corner. */}\n {onRemove && (\n <BaseButton\n type=\"button\"\n aria-label={`Remove ${name}`}\n className=\"absolute -top-1.5 -right-1.5 size-5 rounded-full bg-bg-elevated border border-border-strong flex items-center justify-center text-text-secondary hover:text-text-primary opacity-0 group-hover:opacity-100 transition-opacity\"\n onClick={(event) => {\n event.stopPropagation()\n onRemove()\n }}\n >\n <IconX size={11} stroke={1.75} />\n </BaseButton>\n )}\n </Card>\n )\n }\n\n // Fades in with the card's hover or keyboard focus, and keeps its slot when hidden, so revealing it never shifts the row.\n const download = onDownload ? (\n <IconButton\n variant=\"muted\"\n tooltip=\"Download\"\n aria-label={`Download ${name}`}\n className=\"opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 focus-visible:opacity-100 transition-opacity\"\n onClick={(event) => {\n event.stopPropagation()\n onDownload()\n }}\n >\n <IconDownload size={16} stroke={1.5} />\n </IconButton>\n ) : null\n\n if (state === 'loading') {\n return (\n // `role=\"status\"`: a plain box may not carry a name (axe, aria-prohibited-attr), and a\n // status says what it is doing \u2014 loading \u2014 to a reader that cannot see the pulse.\n <Card role=\"status\" fill=\"inset\" aria-busy=\"true\" aria-label={`Loading ${name}`} className={cn('w-[180px] h-28 animate-pulse', className)} {...props}>\n {null}\n </Card>\n )\n }\n\n if (state === 'unreadable') {\n return (\n <Card fill=\"inset\" unreadable className={cn('flex items-center gap-2 w-[240px] p-1.5 opacity-70', className)} {...props}>\n <div className={TILE_CLASSES}>\n <TypeIcon name={name} />\n </div>\n <div className=\"flex flex-col gap-[1px] min-w-0 text-left\">\n <Truncating text={name} className={NAME_CLASSES} />\n <span className={cn(NOTE_CLASSES, 'text-text-secondary')}>{note ?? 'Could not be loaded'}</span>\n </div>\n </Card>\n )\n }\n\n if (image && src) {\n const picture = <img src={src} alt={name} className=\"w-full h-28 object-cover\" />\n return (\n <Card fill=\"inset\" hover=\"hairline\" className={cn('group relative flex flex-col w-[180px] overflow-hidden', className)} {...props}>\n {onOpen ? (\n <BaseButton\n type=\"button\"\n className=\"block w-full\"\n aria-label={`Preview ${name}`}\n onClick={(event) => {\n event.stopPropagation()\n onOpen()\n }}\n >\n {picture}\n </BaseButton>\n ) : (\n <div className=\"block w-full\">{picture}</div>\n )}\n <div className=\"flex items-center gap-1 pl-2 pr-1 py-1 min-w-0\">\n <Truncating text={name} className=\"flex-1 text-caption text-text-primary truncate\" wrapperClassName=\"flex-1\" />\n {download}\n </div>\n </Card>\n )\n }\n\n const body = (\n <>\n <div className={TILE_CLASSES}>\n <TypeIcon name={name} />\n </div>\n <div className=\"flex flex-col gap-[1px] min-w-0 text-left\">\n <Truncating text={name} className={NAME_CLASSES} />\n <span className={cn(NOTE_CLASSES, 'text-text-secondary')}>{note ?? `${typeLabelOf(name)} \u00B7 ${sizeText}`}</span>\n </div>\n </>\n )\n return (\n <Card fill=\"inset\" hover={href ? 'hairline' : 'none'} className={cn('group flex items-center gap-2 w-[240px] p-1.5 pr-1', !href && 'opacity-70', className)} {...props}>\n {href ? (\n <Link href={href} external variant=\"plain\" className=\"flex items-center gap-2 min-w-0 flex-1 cursor-pointer\" onClick={(event) => event.stopPropagation()}>\n {body}\n </Link>\n ) : (\n <div className=\"flex items-center gap-2 min-w-0 flex-1\">{body}</div>\n )}\n {download}\n </Card>\n )\n}\n", "import type { ComponentPropsWithRef, ReactNode } from 'react'\nimport { cn } from './cn'\n\n/**\n * A link: an anchor with one of four looks, and nothing else of its own.\n *\n * It exists because both apps hand-wrote every link they have \u2014 18 raw `<a>`\n * on 13 September 2026, in three jobs (UIG-27). The looks are the ones those\n * links already had, reduced by Katerina's ruling:\n *\n * - `text` \u2014 a link inside written text: the info colour, always underlined,\n * dimming on hover. The look a link in a body of text already had.\n * - `quiet` \u2014 a title or a timestamp that is also a link. It takes the colour\n * and size of the text it sits in and underlines on hover. Two looks that\n * differed only in their text became this one (her ruling, 13 September).\n * - `underlined` \u2014 a short \"open it elsewhere\" beside a note. It takes the\n * note's colour, is always underlined, and brightens on hover. A dotted\n * and a solid version became this one, solid (her ruling, 13 September).\n * - `plain` \u2014 no look at all: a card or a row that is a link draws itself.\n *\n * **Navigation is the app's.** The package cannot know a router, so like\n * `NavItem` it renders a real anchor and passes every anchor prop through: a\n * router app hands in `onClick`, prevents the default and navigates, and the\n * `href` stays a real address for a modified click or a new tab.\n */\nexport type LinkVariant = 'text' | 'quiet' | 'underlined' | 'plain'\n\nconst VARIANT_CLASSES: Record<LinkVariant, string> = {\n text: 'text-info-default underline underline-offset-2 hover:opacity-80',\n quiet: 'hover:underline',\n underlined: 'underline underline-offset-2 hover:text-text-primary',\n plain: '',\n}\n\nexport interface LinkProps extends ComponentPropsWithRef<'a'> {\n href: string\n /** Default `text`. */\n variant?: LinkVariant\n /**\n * Another site, or another app: opens in a new tab, with `noopener\n * noreferrer` so the page it opens cannot reach back into this one.\n */\n external?: boolean\n children: ReactNode\n}\n\nexport function Link({ href, variant = 'text', external = false, className, children, ...props }: LinkProps) {\n return (\n <a\n href={href}\n {...(external ? { target: '_blank', rel: 'noopener noreferrer' } : {})}\n className={cn(VARIANT_CLASSES[variant], className)}\n {...props}\n >\n {children}\n </a>\n )\n}\n", "import type { ComponentPropsWithRef, ReactNode } from 'react'\nimport { cn } from './cn'\nimport { Link } from './Link'\n\n/**\n * A box that stands for one thing \u2014 a conversation, a project, a file, an\n * object from another app \u2014 drawn as its frame: 8px corners, a fill, a\n * hairline. What goes inside, and the space around it, is the caller's.\n *\n * Both apps drew their cards by hand: 18 of them, sorted by Katerina from\n * photographs on 14 September (UIG-27, docs/GATES.md \u00A70). Her rulings:\n *\n * - **The fill is chosen by what the card sits on**: `surface` on the page,\n * `elevated` on something already filled, `inset` inside a message, `none`\n * for a hairline alone.\n * - **The hairline follows the fill**: the default hairline on `surface` and\n * `elevated`, the subtle one on `inset` and `none`. Every card has 8px\n * corners.\n * - **A card you can pick answers the pointer.** `hairline`: the hairline one\n * step stronger \u2014 what a card that is a link does. `fill`: the card lights\n * up and its hairline shows \u2014 what a card in a feed does, where the light\n * says which one you are on.\n * - **A card that cannot be read has a dashed hairline**, in both apps.\n *\n * No padding of its own: the cards measured 0 to 12px, and a card with none\n * was one whose content pads itself.\n */\nexport type CardFill = 'surface' | 'elevated' | 'inset' | 'none'\nexport type CardHover = 'none' | 'hairline' | 'fill'\nexport type CardAttention = 'accent' | 'warning'\n\nconst FILL_CLASSES: Record<CardFill, string> = {\n surface: 'bg-bg-surface border-border-default',\n elevated: 'bg-bg-elevated border-border-default',\n inset: 'bg-bg-inset border-border-subtle',\n none: 'border-border-subtle',\n}\n\n/** One step stronger than the fill's own hairline. */\nconst HAIRLINE_HOVER_CLASSES: Record<CardFill, string> = {\n surface: 'hover:border-border-strong',\n elevated: 'hover:border-border-strong',\n inset: 'hover:border-border-default',\n none: 'hover:border-border-default',\n}\n\n/** The same step, held by `hovered` rather than by the pointer. */\nconst HAIRLINE_HELD_CLASSES: Record<CardFill, string> = {\n surface: 'border-border-strong',\n elevated: 'border-border-strong',\n inset: 'border-border-default',\n none: 'border-border-default',\n}\n\nconst ATTENTION_CLASSES: Record<CardAttention, string> = {\n accent: 'border-accent-muted hover:border-accent-muted',\n warning: 'border-warning-muted hover:border-warning-muted',\n}\n\nexport interface CardProps extends ComponentPropsWithRef<'div'> {\n /** What the card sits on. Default `surface`. */\n fill?: CardFill\n /** The whole card is a link: an anchor, and its hairline answers the pointer. Navigation stays the app's, as with `Link`. */\n href?: string\n /** How it answers the pointer. Default `hairline` with an `href`, `none` without. */\n hover?: CardHover\n /** No hairline until it is pointed at \u2014 a row in a feed that shows its edge only when you are on it. */\n quietUntilHover?: boolean\n /**\n * Draw the hover look now, whatever the pointer does. For a card whose own menu or picker is open: the menu\n * opens outside the card, so the pointer on it leaves the card, and the card would go dark under its own menu.\n * Ignored while `selected` or `active`, like the pointer.\n */\n hovered?: boolean\n /** The one you are on: the selected fill, a subtle hairline, and no hover. */\n selected?: boolean\n /** Being changed in place: the selected fill with the accent hairline, and no hover. */\n active?: boolean\n /** A hairline that asks to be looked at \u2014 something new (`accent`), or urgent (`warning`). */\n attention?: CardAttention\n /** It stands for something that could not be read: the hairline is dashed. */\n unreadable?: boolean\n children: ReactNode\n}\n\nexport function Card({\n fill = 'surface',\n href,\n hover = href ? 'hairline' : 'none',\n quietUntilHover = false,\n hovered = false,\n selected = false,\n active = false,\n attention,\n unreadable = false,\n className,\n children,\n ...props\n}: CardProps) {\n const still = selected || active\n const classes = cn(\n 'rounded-lg border',\n // A card that never changes has nothing to ease between, and the apps' still cards carry no transition.\n hover !== 'none' && 'transition-colors',\n FILL_CLASSES[fill],\n quietUntilHover && 'border-transparent',\n !still && hover === 'hairline' && HAIRLINE_HOVER_CLASSES[fill],\n !still && hover === 'fill' && 'hover:bg-bg-hover hover:border-border-default',\n !still && hovered && hover === 'hairline' && HAIRLINE_HELD_CLASSES[fill],\n !still && hovered && hover === 'fill' && 'bg-bg-hover border-border-default',\n // The one you are on can still be clicked (it opens again); the one being changed cannot.\n !active && hover !== 'none' && !href && props.onClick && 'cursor-pointer',\n selected && 'bg-bg-selected border-border-subtle',\n active && 'bg-bg-selected border-accent-primary',\n !active && attention && ATTENTION_CLASSES[attention],\n unreadable && 'border-dashed',\n href && 'block',\n className,\n )\n if (href !== undefined) {\n const { ref, ...anchorProps } = props as ComponentPropsWithRef<'a'>\n return (\n <Link href={href} variant=\"plain\" ref={ref} className={classes} {...anchorProps}>\n {children}\n </Link>\n )\n }\n return (\n <div className={classes} {...props}>\n {children}\n </div>\n )\n}\n", "import type { ComponentPropsWithRef, ReactNode } from 'react'\nimport { Button as BaseButton } from '@base-ui/react/button'\nimport { cn } from './cn'\nimport { useFormBusy } from './formBusy'\nimport { TooltipTrigger } from './Tooltip'\n\n/**\n * Peek's IconButton (2026-08-28), verbatim: a square 4px-padded button\n * around a 16px icon, 8px radius, three variants, an optional tooltip that\n * portals above or below. Plus `type=\"button\"` by default (Ship's addition),\n * so it never submits a form by accident. On Base UI's Button since stage 2\n * of the migration (2026-09-07).\n *\n * `disabledReason` disables it, keeps it reachable by keyboard, and shows\n * the reason in place of the tooltip \u2014 see Button.\n */\nexport type IconButtonVariant = 'muted' | 'outlined' | 'primary'\n\n/** `ComponentPropsWithRef` so a `ref` reaches the element \u2014 see Button. */\nexport interface IconButtonProps extends ComponentPropsWithRef<'button'> {\n variant?: IconButtonVariant\n tooltip?: string\n /** A key hint drawn as the `Kbd` chip inside the tooltip \u2014 for a button\n * whose only other affordance is a keyboard shortcut. */\n tooltipShortcut?: string\n tooltipPlacement?: 'top' | 'bottom'\n /** Why the action cannot succeed right now. Disables the button, keeps it\n * reachable by keyboard, and shows the reason as the tooltip. */\n disabledReason?: string\n /** The icon: 16px, stroke 1.5. */\n children: ReactNode\n}\n\nexport function IconButton({\n variant = 'muted',\n className,\n children,\n disabled,\n disabledReason,\n tooltip,\n tooltipShortcut,\n tooltipPlacement,\n type = 'button',\n ...props\n}: IconButtonProps) {\n // Inside a busy Form: switched off, and looking it (formBusy.ts).\n const formBusy = useFormBusy()\n const button = (\n <BaseButton\n type={type}\n disabled={disabled || !!disabledReason || formBusy}\n focusableWhenDisabled={!!disabledReason}\n className={(state) =>\n cn(\n // `shrink-0` stops a flex parent squashing the button; `self-center`\n // stops one stretching it. Two different failures, and both have\n // happened here: a row with no `items-*` drew this 24 wide and 228\n // tall in Peek's TopicMoreMenu story (Katerina, 2026-09-11). A button\n // is the size of its icon and its padding, whatever box it lands in.\n //\n // This is the one of the two that needs it: an IconButton states no\n // height, so its cross size is `auto` and a stretching parent takes\n // it. Measured in the same 260px row on 2026-09-12: 24px with this\n // class, 226px without. `Button` states `h-8`/`h-6`, so it cannot be\n // stretched and carries no `self-center` \u2014 see the note there.\n 'flex items-center justify-center p-1 rounded-lg transition-colors shrink-0 self-center cursor-pointer',\n !state.disabled && variant === 'primary' && 'bg-accent-primary hover:bg-accent-hover text-text-inverse',\n !state.disabled && variant === 'muted' && 'text-text-secondary hover:bg-bg-hover hover:text-text-primary',\n !state.disabled && variant === 'outlined' && 'border border-border-default hover:bg-bg-hover text-text-secondary',\n state.disabled && variant === 'primary' && 'bg-bg-disabled text-text-disabled',\n state.disabled && variant === 'muted' && 'text-text-disabled',\n state.disabled && variant === 'outlined' && 'border border-border-default text-text-disabled',\n // `pointer-events-none` only where the button is truly out of reach:\n // with a `disabledReason` the button IS the tooltip's trigger, and a\n // trigger the pointer cannot land on never opens one. Base UI already\n // swallows the click.\n state.disabled && !disabledReason && 'pointer-events-none',\n state.disabled && 'cursor-not-allowed',\n className,\n )\n }\n {...props}\n >\n {children}\n </BaseButton>\n )\n\n const label = disabledReason ?? tooltip\n if (label) {\n return (\n <TooltipTrigger label={label} shortcut={disabledReason ? undefined : tooltipShortcut} placement={tooltipPlacement}>\n {button}\n </TooltipTrigger>\n )\n }\n return button\n}\n", "import { createContext, useContext } from 'react'\n\n/**\n * Whether a `Form` around this is busy (UIG-7).\n *\n * `Form` switches its children off with a disabled `<fieldset>`, which the\n * browser applies to every native control inside \u2014 but a part that draws its\n * look from Base UI's own `disabled` (Button, IconButton, Checkbox) is not\n * told, so it kept its normal look while it could not be pressed, and a\n * Checkbox, which is a `<span>`, could still be ticked (Katerina saw the busy\n * story's button unchanged, 16 September). Those parts read this and switch\n * off the way their own `disabled` does. Not exported: it is the package's\n * wiring between Form and its parts.\n */\nexport const FormBusyContext = createContext(false)\n\nexport function useFormBusy(): boolean {\n return useContext(FormBusyContext)\n}\n", "import type { ComponentPropsWithRef, ReactElement, ReactNode } from 'react'\nimport { Tooltip as BaseTooltip } from '@base-ui/react/tooltip'\nimport { cn } from './cn'\nimport { Kbd } from './Kbd'\n\n/**\n * Peek's Tooltip and WithTooltip (2026-08-28) \u2014 the same 30px elevated pill,\n * on Base UI's `Tooltip` since stage 4 of the migration (2026-09-07).\n *\n * **What that fixed, and this page used to say it did not: it shows on\n * keyboard focus.** `WithTooltip` was hover-only, which meant the reason a\n * disabled control gives \u2014 the whole point of `disabledReason` \u2014 could not be\n * read without a mouse. Base UI opens it on `:focus-visible` too, so tabbing\n * to a control says what it is.\n *\n * Gone with it: the `createPortal`, the `getBoundingClientRect` arithmetic and\n * the two clamps this file kept against the viewport's edges. Floating UI\n * places and flips it now.\n */\n\n/**\n * How long the pointer must rest on a trigger before the tooltip appears\n * (Katerina, D23, 2026-09-07). It was instant, which flashed a pill per\n * button when sweeping a toolbar; 300ms is long enough to be a decision and\n * short enough not to feel like waiting.\n *\n * Inside a `TooltipProvider` the *neighbours* then open instantly, so a row\n * of icon buttons pauses once and reads freely after that.\n */\nconst OPEN_DELAY = 300\n\nexport interface TooltipProviderProps {\n /** Before the first tooltip in the group opens. Default 300ms (D23). */\n delay?: number\n /** Before one closes. Default: immediately, as it always has. */\n closeDelay?: number\n /** How long after one closes its neighbours still open instantly. */\n timeout?: number\n children: ReactNode\n}\n\n/**\n * Mount once at the top of an app: it makes every tooltip below it share one\n * delay, so the second and third open instantly while the group is warm.\n *\n * Without it each tooltip still waits its own 300ms \u2014 nothing breaks, the row\n * simply pauses on every button instead of once.\n */\nexport function TooltipProvider({ delay = OPEN_DELAY, closeDelay, timeout, children }: TooltipProviderProps) {\n return (\n <BaseTooltip.Provider delay={delay} closeDelay={closeDelay} timeout={timeout}>\n {children}\n </BaseTooltip.Provider>\n )\n}\n\n/** `ComponentPropsWithRef` because the pill is what `Tooltip.Popup` renders \u2014\n * Base UI merges its own props and ref into this element, so both must land. */\nexport interface TooltipProps extends Omit<ComponentPropsWithRef<'div'>, 'children'> {\n label: string\n /** The key that does the same thing, drawn as the `Kbd` chip after the label.\n * Pass it already formatted for the platform \u2014 this renders, it does not\n * decide whether the modifier is a glyph or a word. */\n shortcut?: string\n}\n\nexport function Tooltip({ label, shortcut, className, ...props }: TooltipProps) {\n return (\n <div role=\"tooltip\" className={cn('bg-bg-elevated border border-border-default rounded-lg h-[30px] flex items-center justify-center gap-1.5 px-2 shadow-lg', className)} {...props}>\n <span className=\"text-caption text-text-primary whitespace-nowrap\">{label}</span>\n {shortcut && <Kbd>{shortcut}</Kbd>}\n </div>\n )\n}\n\nexport interface WithTooltipProps {\n label: string\n /** Passed straight to the surface \u2014 see `TooltipProps.shortcut`. */\n shortcut?: string\n placement?: 'top' | 'bottom'\n /** Extra classes on the wrapper \u2014 e.g. `min-w-0 shrink` so a truncating label keeps truncating inside it. */\n wrapperClassName?: string\n children: ReactNode\n}\n\n/** The 6px between the trigger and the pill, and the 8px it keeps clear of\n * every viewport edge \u2014 the two numbers the deleted arithmetic used. */\nconst GAP = 6\nconst VIEWPORT_PAD = 8\n\n/**\n * The portalled surface: everything after the trigger, written once because\n * `WithTooltip` and the two buttons all need it.\n *\n * `pointer-events-none` and `disableHoverablePopup` together keep the pill\n * untouchable, as it has always been \u2014 a tooltip is not somewhere you can put\n * the pointer, and anything you must reach belongs in a Menu or a dialog.\n */\nfunction TooltipSurface({ label, shortcut, placement }: { label: string; shortcut?: string; placement: 'top' | 'bottom' }) {\n return (\n <BaseTooltip.Portal>\n <BaseTooltip.Positioner\n side={placement}\n align=\"center\"\n sideOffset={GAP}\n collisionPadding={VIEWPORT_PAD}\n // `fixed`, as the hand-written one was: the pill is placed against the\n // viewport, so a scroll container between it and the body cannot drag\n // it out of place.\n positionMethod=\"fixed\"\n className=\"pointer-events-none z-[9999]\"\n >\n <BaseTooltip.Popup\n render={\n <Tooltip\n label={label}\n shortcut={shortcut}\n /*\n * The motion (Katerina, D26, 2026-09-07). It had none \u2014 it was\n * mounted and instantly there \u2014 and Base UI is what makes one\n * possible: `data-starting-style` on the opening frame,\n * `data-ending-style` while it closes, and the popup is kept in\n * the DOM until the transition finishes, which is the half a\n * hand-written tooltip cannot do.\n *\n * It fades and travels 4px away from the trigger: from below\n * when it stands above the control (`data-side=top`), from above\n * when it hangs beneath it. 120ms in, 80ms out.\n *\n * `data-instant` is Base UI saying skip it \u2014 the pointer moved\n * to a neighbouring trigger inside the warm delay group, or the\n * tooltip was dismissed. Sweeping a toolbar therefore moves the\n * pill without re-animating it, which is the whole point of the\n * shared delay (D23).\n */\n className={cn(\n 'transition-[opacity,transform] duration-[120ms] ease-out motion-reduce:transition-none',\n 'data-[ending-style]:duration-[80ms] data-[instant]:duration-0',\n 'data-[starting-style]:opacity-0 data-[ending-style]:opacity-0',\n 'data-[side=top]:data-[starting-style]:translate-y-[4px] data-[side=top]:data-[ending-style]:translate-y-[4px]',\n 'data-[side=bottom]:data-[starting-style]:-translate-y-[4px] data-[side=bottom]:data-[ending-style]:-translate-y-[4px]',\n )}\n />\n }\n />\n </BaseTooltip.Positioner>\n </BaseTooltip.Portal>\n )\n}\n\nexport function WithTooltip({ label, shortcut, placement = 'top', wrapperClassName, children }: WithTooltipProps) {\n return (\n <BaseTooltip.Root disableHoverablePopup>\n {/* The wrapper stays the trigger: `WithTooltip` wraps whatever it is\n given \u2014 a raw button, a span, a Chip \u2014 and only an element of its own\n can carry the handlers for all of them. A control that IS the trigger\n (Button, IconButton) composes the part onto itself instead, which is\n what lets a `Dialog.Close` or a `Menu.Trigger` be one of those. */}\n <BaseTooltip.Trigger delay={OPEN_DELAY} render={<div className={cn('inline-flex shrink-0', wrapperClassName)} />}>\n {children}\n </BaseTooltip.Trigger>\n <TooltipSurface label={label} shortcut={shortcut} placement={placement} />\n </BaseTooltip.Root>\n )\n}\n\n/**\n * The tooltip a control wears itself, rather than through a wrapper.\n *\n * `Button` and `IconButton` call this: the Base UI button element becomes the\n * `Tooltip.Trigger`, so the component's root stays the `<button>`. That is\n * what stage 3 ran into and could not fix \u2014 an `IconButton` with a tooltip\n * returned the wrapper `<div>`, so a `Dialog.Close` composed onto the wrapper\n * and the \u2715 could never be the part.\n *\n * Internal: the two buttons' `tooltip` / `disabledReason` props are the API.\n */\nexport function TooltipTrigger({ label, shortcut, placement = 'top', children }: { label: string; shortcut?: string; placement?: 'top' | 'bottom'; children: ReactElement }) {\n return (\n <BaseTooltip.Root disableHoverablePopup>\n {/* `children` is the control itself, so it is what the part renders \u2014\n a `ReactElement`, not a `ReactNode`, because `render` takes one\n element and there is nothing sensible to do with two. */}\n <BaseTooltip.Trigger delay={OPEN_DELAY} render={children} />\n <TooltipSurface label={label} shortcut={shortcut} placement={placement} />\n </BaseTooltip.Root>\n )\n}\n", "import type { ReactNode } from 'react'\nimport { cn } from './cn'\n\n/**\n * A keyboard hint \u2014 the small chip that names the key which does the same thing.\n *\n * **The look is SearchInput's shortcut chip** (Katerina, 2026-09-05), which is\n * the one the apps already show: an inset pill with a hairline border, and under\n * Signal and Ship the keycap treatment \u2014 mono, 10px, a lighter fill and a thicker\n * bottom edge, so it reads as a key rather than a label.\n *\n * It existed before this file did, drawn inline inside `MenuItem` and again\n * inside `SearchInput`. Extracted when `Tooltip` needed a third copy.\n *\n * **Sizes are arbitrary values on purpose.** `text-caption` beside a\n * `text-{color}` is dropped by tailwind-merge, so the chip silently inherited\n * whatever size surrounded it \u2014 10px in a composer, something else elsewhere.\n * `text-[12px]` survives the merge.\n *\n * Content is whatever names the key \u2014 a trigger character (`/`, `@`), a chord\n * (`Cmd+B`, `Ctrl+Alt+1`), or a word (`Esc`). It does not format anything: a caller\n * that knows the platform passes the label it wants.\n */\nexport interface KbdProps {\n children: ReactNode\n className?: string\n}\n\nexport function Kbd({ children, className }: KbdProps) {\n return (\n <kbd\n className={cn(\n 'inline-flex shrink-0 items-center justify-center whitespace-nowrap rounded-sm border border-border-strong bg-bg-inset px-1 py-px font-sans text-caption text-text-secondary',\n 'signal:border-b-2 signal:pt-[2px] signal:pb-px signal:bg-bg-wash signal:font-mono signal:text-small',\n 'ship:border-b-2 ship:pt-[2px] ship:pb-px ship:bg-bg-wash ship:font-mono ship:text-small',\n className,\n )}\n >\n {children}\n </kbd>\n )\n}\n", "import { Avatar as BaseAvatar } from '@base-ui/react/avatar'\nimport { IconUserFilled } from '@tabler/icons-react'\nimport { cn } from './cn'\n\n/**\n * A person's face: their picture, or their initials on a colour that is\n * theirs, or \u2014 when there is no name to take initials from \u2014 a silhouette.\n *\n * Peek's Avatar (2026-08-28), made self-contained: Peek's version found the\n * picture itself, by name, from Peek's data (an upload in Convex, else the\n * demo portrait), and switched the initials on only under its Signal theme.\n * Neither belongs in a shared component, so this one takes the picture URL\n * from its caller and draws the initials in every theme. Peek keeps a\n * three-line wrapper that resolves the picture; Ship passes the relay's\n * `kind:0` picture straight in.\n *\n * On Base UI's `Avatar` since stage 6 of the migration (2026-09-14): Base UI\n * loads the picture and says when it has arrived or failed, and the initials\n * or the silhouette are its fallback. The hand-kept \"broken\" state is gone.\n *\n * Initials come from a *name*, never from a key: a component that accepted a\n * pubkey would eventually show one (Ship's ruling, 2026-08-24). An unnamed\n * person is the silhouette \u2014 `?` on a bright gradient read as an error badge\n * rather than a person (Peek, FEE-1).\n *\n * The hue is a fallback palette, not a token: it is per person, chosen\n * from the name so the same person is the same colour everywhere, and a\n * theme has no say in it. Peek's eight, verbatim.\n */\nconst HUES = ['#56c8ff', '#ff8f6b', '#4ade8c', '#b18cff', '#ffc94d', '#ff7eb0', '#7ea8ff', '#5fdfd6']\n\nexport const hueFor = (key: string) => {\n let h = 0\n for (let i = 0; i < key.length; i++) h = (h * 31 + key.charCodeAt(i)) | 0\n return HUES[Math.abs(h) % HUES.length]\n}\n\n/**\n * Peek's rule, sharpened: the first letter of the first two words \u2014 counting\n * only words that begin with a letter or digit, so \"Claude (steered by\n * Katerina Kelepouri)\" shows \"C\", never \"C(\" (Katerina, 2026-09-01: a name\n * carrying such symbols gets a single letter rather than one of them).\n */\nexport const initialsFor = (name: string) => {\n const words = name.split(/\\s+/).slice(0, 2).filter((w) => /^[\\p{L}\\p{N}]/u.test(w))\n const initials = words.map((w) => w[0]).join('') || name.trim().charAt(0)\n return initials.toUpperCase()\n}\n\nexport interface AvatarProps {\n /** The picture URL, resolved by the caller. Until it arrives, and if it fails, the initials show. */\n src?: string\n /** The person's name \u2014 where the initials and the colour come from. */\n name?: string\n /** Alt text for the picture; the name when absent. Also the initials' source when there is no name. */\n alt?: string\n /** Pixels. The scale: 16 \u00B7 24 \u00B7 32 \u00B7 36 (default). */\n size?: number\n /**\n * **Say the person's name out loud**, for a face that stands on its own \u2014\n * `PersonTrigger`'s compact shape, a stack of members.\n *\n * A face is silent by default, because almost every face in the suite sits\n * beside the name it belongs to: `Person`, a `MenuItem`'s `leading`, a row.\n * A picture that spoke there would double the name \u2014 measured 2026-09-08,\n * before this existed: a `Person` in a button announced **\"AD Ana Duarte\"**,\n * and with a picture **\"Ana Duarte Ana Duarte\"**. The initials are a drawing\n * of a name, not text, and a screen reader was reading them as text.\n */\n label?: string\n className?: string\n}\n\nexport function Avatar({ src, name, alt = '', size = 36, label: spoken, className }: AvatarProps) {\n const label = name || alt\n return (\n <BaseAvatar.Root\n // A div, as it was drawn: Base UI's default is a span.\n render={<div />}\n /* Named or silent, never half of either: with a `label` the tile is one\n image with one name, and everything inside it is that image's pixels;\n without one it is not in the accessibility tree at all. */\n {...(spoken ? { role: 'img', 'aria-label': spoken } : { 'aria-hidden': true })}\n className={cn('rounded-sm overflow-hidden shrink-0 bg-bg-inset', className)}\n style={{ width: size, height: size }}\n >\n {src && (\n <BaseAvatar.Image\n src={src}\n alt=\"\"\n /*\n `keepMounted`: the <img> is in the tile from the first render, as it\n always was, so a picture still loading leaves the tile empty rather\n than flashing initials and swapping them out, and an app's test that\n finds the <img> still finds it. The fallback is rendered after it,\n below the tile's hidden overflow, until the picture arrives (then it\n unmounts) or fails (then the picture is hidden and it moves up).\n */\n keepMounted\n className=\"w-full h-full object-cover data-[error]:hidden\"\n />\n )}\n {label ? (\n <BaseAvatar.Fallback\n render={<div />}\n /*\n `leading-none` is load-bearing (2026-09-03). Centring a flex child\n centres its LINE BOX, and a line box reserves room under the\n baseline for descenders \u2014 which capitals never use \u2014 so initials\n floated above the middle of every tile. It also inherited whatever\n line-height surrounded it, so the same face sat differently in a\n members pill and in a replies row. Measured over six letter pairs\n at 18/24/36px: mean 0.64px high before, 0.06px after.\n\n Per-letter variation stays (a \"Y\" carries its mass up top, a \"ZB\"\n more than an \"AJ\") \u2014 that is the letterform, not the box, and it\n is not something a rule here can flatten.\n */\n className=\"w-full h-full flex items-center justify-center font-semibold leading-none\"\n style={{\n /* eslint-disable no-restricted-syntax -- @estiva-escape: the per-person palette (the note at\n the top): eight hues picked from the name, the one ink that reads on all\n of them, and a size that follows `size`. None of it can be a token. */\n // The one ink colour that reads on all eight hues, which are a\n // palette rather than tokens (see the note at the top) \u2014 so its\n // ink cannot be a token either.\n color: '#08121c',\n fontSize: Math.round(size * 0.36),\n background: `linear-gradient(160deg, color-mix(in srgb, ${hueFor(label)} 92%, #fff) 0%, color-mix(in srgb, ${hueFor(label)} 70%, #0b0d11) 100%)`,\n /* eslint-enable no-restricted-syntax */\n }}\n >\n {/*\n D27 (ruled 2026-09-08, built 2026-09-14): what is centred is the\n capitals' own box, cap height to baseline, not the line box. The\n line box's ascent and descent are rounded to whole pixels before\n it is centred, which put the baseline up to 0.73px from where\n centred capitals put it (at 24px: 14.5 against 15.23); trimmed, it\n lands within 0.06px. On screen, averaged over eight sub-pixel\n positions, the ink leaned 0.66px high at 24px, 0.44px at 28px and\n 0.27px low at 22px; now every size averages within 0.07px of the\n middle. Any one face can still sit up to half a pixel off, because\n the screen rounds text to its pixel grid wherever the tile falls.\n A browser without `text-box` draws the old line box, as before.\n */}\n <span className=\"[text-box:trim-both_cap_alphabetic]\">{initialsFor(label)}</span>\n </BaseAvatar.Fallback>\n ) : (\n <BaseAvatar.Fallback render={<div />} className=\"w-full h-full bg-accent-muted flex items-center justify-center text-text-muted\">\n <IconUserFilled size={Math.round(size * 0.5)} />\n </BaseAvatar.Fallback>\n )}\n </BaseAvatar.Root>\n )\n}\n", "import { Avatar } from './Avatar'\n\n/**\n * Up to three overlapping 24px faces with a `bg-surface` ring \u2014 Peek's member\n * stack (2026-09-01), with one generalisation on the way in: a member is a\n * name plus an optional picture URL, resolved by the caller, exactly as\n * Avatar takes its `src`. Peek's version took bare names because its Avatar\n * wrapper resolves pictures itself; a consumer without such a wrapper could\n * never have shown one.\n *\n * The ring is drawn OUTSIDE the face, as a shadow (2026-09-03). It was a\n * `border` before, which box-sizing takes out of the inside: a 24px avatar\n * showed 20px of face, and at 18px only 14px \u2014 noticeably smaller than the\n * number asked for. A shadow costs the face nothing, so `size` means the\n * size. It replaced an even older wrapper whose `overflow-hidden` clipped\n * 4px of face and the right edge of the initials with it (\"CO\" as \"C(\") \u2014\n * hence the rule this file keeps: nothing that clips ever wraps the face.\n */\nexport interface AvatarGroupMember {\n name: string\n /** Resolved by the caller. Absent draws the initials. */\n picture?: string\n}\n\nexport interface AvatarGroupProps {\n members: AvatarGroupMember[]\n /**\n * Face size in pixels; the overlap follows it. 24 is the members pill,\n * 18 the replies row (Katerina, 2026-09-03) \u2014 that row drew its own stack\n * for two months for want of this one number, and drifted while it did.\n */\n size?: number\n}\n\nexport function AvatarGroup({ members, size = 24 }: AvatarGroupProps) {\n const visible = members.slice(0, 3)\n /*\n A third of the face, so a stack reads the same at any size \u2014 which is\n exactly the ratio both hand-drawn stacks had arrived at independently\n (8px on 24, 6px on 18). The container gives the last face its overlap\n back as padding, so the group measures its true width.\n */\n const overlap = Math.round(size / 3)\n // The ring scales with the face \u2014 2px at 24, 1.5px at 18, which is what both\n // hand-drawn stacks used before this component carried either of them.\n const ring = size / 12\n return (\n <div className=\"flex items-center\" style={{ paddingRight: overlap }}>\n {visible.map((member, i) => (\n /*\n The wrapper carries the overlap and the ring; the face carries\n neither. It must never clip \u2014 see the note at the top of the file \u2014\n so it has the avatar's own radius and no overflow of its own.\n */\n <span\n key={i}\n className=\"relative flex rounded-sm\"\n /* eslint-disable-next-line no-restricted-syntax -- @estiva-escape: the ring's width is the `ring` prop, so it cannot be a class; its colour is the surface token */\n style={{ marginRight: -overlap, boxShadow: `0 0 0 ${ring}px var(--bg-surface)` }}\n >\n {/* Each face says whose it is: a stack stands on its own, with no\n name beside it to borrow. It read as its initials before \u2014\n \"AD BC CD\" \u2014 which is the drawing, not the person. */}\n <Avatar size={size} name={member.name} src={member.picture} label={member.name} />\n </span>\n ))}\n </div>\n )\n}\n", "import { IconX } from '@tabler/icons-react'\nimport { cn } from './cn'\nimport { IconButton } from './IconButton'\n\n/**\n * The strip under the header where the app says something happened \u2014\n * one line, gone when there is nothing to say (Ship's Banner,\n * 2026-09-02, verbatim; `info` and `warning` added on extraction,\n * Katerina's ruling \u2014 the semantic set is four everywhere else).\n *\n * body-2, her ruling (2026-09-01): the caption this once claimed never\n * rendered, so 14px is what it has always been and what she kept.\n * An `error` announces itself (`role=\"alert\"`); the other tones are\n * polite (`role=\"status\"`).\n *\n * `onDismiss` adds an `\u2715` at the right-hand end (Katerina, D21,\n * 2026-09-07). Without it the strip is exactly what it was \u2014 a banner an\n * app removes by not rendering it. With it the row is 40px rather than\n * 36px, because the button is taller than the line of text.\n *\n * **A banner is not a toast.** This is the strip under the header, at the\n * app's full width, for something that stays true. Transient feedback is\n * `Toast`, and something that floats over the page and must not fade is a\n * `Toast` with `durationMs: 0` \u2014 which is what Peek's \"topic deleted\"\n * notice is (D21), not this.\n */\nexport type BannerTone = 'ok' | 'error' | 'info' | 'warning'\n\nexport interface BannerProps {\n tone: BannerTone\n children: string\n /** Adds an `\u2715` at the right-hand end. Absent: the app removes the banner. */\n onDismiss?: () => void\n /** The dismiss button's accessible name. Defaults to \"Dismiss\". */\n dismissLabel?: string\n className?: string\n}\n\nconst TONE_STYLES: Record<BannerTone, string> = {\n ok: 'bg-success-muted text-success-default',\n error: 'bg-error-muted text-error-default',\n info: 'bg-info-muted text-info-default',\n warning: 'bg-warning-muted text-warning-default',\n}\n\nexport function Banner({ tone, children, onDismiss, dismissLabel = 'Dismiss', className }: BannerProps) {\n const role = tone === 'error' ? 'alert' : 'status'\n // No dismiss, no row: every banner that predates the prop keeps the DOM and\n // the height it had.\n if (!onDismiss) {\n return (\n <div role={role} className={cn('px-4 py-2 text-body-2', TONE_STYLES[tone], className)}>\n {children}\n </div>\n )\n }\n return (\n <div role={role} className={cn('flex items-center gap-3 px-4 py-2 text-body-2', TONE_STYLES[tone], className)}>\n <span className=\"min-w-0 flex-1\">{children}</span>\n {/* `text-current` so the \u2715 takes the tone's colour rather than the\n muted grey an IconButton wears on a neutral surface. */}\n <IconButton aria-label={dismissLabel} onClick={onDismiss} className=\"-mr-1 shrink-0 text-current hover:text-current\">\n <IconX size={16} stroke={1.5} />\n </IconButton>\n </div>\n )\n}\n", "import type { ComponentPropsWithRef, ReactNode } from 'react'\nimport { Button as BaseButton } from '@base-ui/react/button'\nimport { cn } from './cn'\nimport { useFormBusy } from './formBusy'\nimport { TooltipTrigger } from './Tooltip'\n\n/**\n * Peek's Button (2026-08-28), verbatim, plus what Ship added and Peek should\n * adopt: a `destructive` variant \u2014 the muted button in the error colour, for\n * \"Delete project\" and its kind; a destructive action is a button like any\n * other, not a dotted link \u2014 and `type=\"button\"` by default, so a button\n * inside a form submits it only when asked to. On Base UI's Button since\n * stage 2 of the migration (2026-09-07).\n *\n * Three variants and two sizes: 32px default / 24px small, 6px radius, 500\n * weight. The sizes are the `btn-default` and `btn-small` type tokens.\n *\n * The primary reads in `text-inverse` on the accent \u2014 a theme decides what\n * that is (dark on Peek's light accents, light on Ship's dark one). Under\n * Signal it is also semibold, as Peek has it.\n *\n * `disabledReason` is the package's own rule, \"only offer actions that can\n * succeed\", done once: the button is disabled, stays reachable by keyboard\n * (Base UI's `focusableWhenDisabled`), and shows the reason as a tooltip on\n * hover. Ship wrote that wrapper by hand six times.\n */\nexport type ButtonVariant = 'primary' | 'outlined' | 'muted' | 'destructive'\nexport type ButtonSize = 'default' | 'small'\n\n/**\n * `ComponentPropsWithRef`, not `ButtonHTMLAttributes`, so a `ref` reaches the\n * element. React 19 passes `ref` to a function component as an ordinary prop,\n * so nothing here forwards it by hand \u2014 it rides in on the spread below. What\n * was missing was only the *type* saying so, which is enough to stop a caller.\n *\n * It matters because a Base UI part composes through `render`: at stage 4 a\n * `Menu.Trigger` or a `Dialog.Close` **is** this Button rather than wrapping\n * one, and a trigger that cannot be measured or focused is not a trigger.\n */\nexport interface ButtonProps extends ComponentPropsWithRef<'button'> {\n variant?: ButtonVariant\n size?: ButtonSize\n /** 16px, stroke 1.5 on the default size; 14px on small. */\n leadingIcon?: ReactNode\n /** Why the action cannot succeed right now. Disables the button, keeps it\n * reachable by keyboard, and shows the reason as a tooltip on hover. */\n disabledReason?: string\n children: ReactNode\n}\n\nexport function Button({\n variant = 'muted',\n size = 'default',\n leadingIcon,\n className,\n children,\n disabled,\n disabledReason,\n type = 'button',\n ...props\n}: ButtonProps) {\n const hasLeadingIcon = !!leadingIcon\n // Inside a busy Form: switched off, and looking it (formBusy.ts).\n const formBusy = useFormBusy()\n const button = (\n <BaseButton\n type={type}\n disabled={disabled || !!disabledReason || formBusy}\n focusableWhenDisabled={!!disabledReason}\n className={(state) =>\n cn(\n // No `self-center` here, deliberately \u2014 `IconButton` needs it and this\n // does not, and the difference is the `h-8` / `h-6` on the next two\n // lines. `align-items: stretch` only stretches a child whose cross\n // size is `auto`; a Button always states its height, so a stretching\n // parent cannot change it. Measured in Chrome on 2026-09-12, in the\n // 260px row of Finding 49: 32px with `self-center` and 32px without.\n // What the class DID do was override the caller on the other axis \u2014\n // in a column it beats the parent's `items-start`, which centred the\n // four actions in Ship's `ProjectRail` and the trigger in five of\n // this package's own story frames, each of which asks for the top.\n // A control does not get to decide where its caller puts it.\n 'inline-flex items-center justify-center gap-1 rounded-md transition-colors font-sans font-medium',\n size === 'default' && 'h-8 text-btn-default',\n size === 'small' && 'h-6 text-btn-small',\n // Extra right padding beside a leading icon, for optical balance.\n size === 'default' && (hasLeadingIcon ? 'pl-2 pr-3' : 'px-2'),\n size === 'small' && (hasLeadingIcon ? 'pl-1.5 pr-2' : 'px-1.5'),\n !state.disabled && variant === 'primary' && 'bg-accent-primary hover:bg-accent-hover text-text-inverse cursor-pointer signal:font-semibold',\n !state.disabled && variant === 'outlined' && 'border border-border-default hover:bg-bg-hover text-text-primary cursor-pointer',\n !state.disabled && variant === 'muted' && 'hover:bg-bg-hover text-text-primary cursor-pointer',\n !state.disabled && variant === 'destructive' && 'hover:bg-error-muted text-error-default cursor-pointer',\n state.disabled && 'bg-bg-disabled text-text-disabled',\n // `pointer-events-none` only where the button is truly out of reach.\n // With a `disabledReason` the button IS the tooltip's trigger, and a\n // trigger the pointer cannot land on never opens one \u2014 Base UI already\n // swallows the click (`focusableWhenDisabled` gives `aria-disabled`\n // and a prevented `onClick`), so nothing else needs it.\n state.disabled && !disabledReason && 'pointer-events-none',\n state.disabled && variant === 'outlined' && 'border border-border-default',\n className,\n )\n }\n {...props}\n >\n {leadingIcon}\n {children}\n </BaseButton>\n )\n return disabledReason ? <TooltipTrigger label={disabledReason}>{button}</TooltipTrigger> : button\n}\n", "import { Checkbox as BaseCheckbox } from '@base-ui/react/checkbox'\nimport { Field as BaseField } from '@base-ui/react/field'\nimport { IconCheck } from '@tabler/icons-react'\nimport { cn } from './cn'\nimport { useFormBusy } from './formBusy'\n\n/**\n * A 16px square that fills with the accent when checked \u2014 Peek's Checkbox\n * (2026-09-01), verbatim; on Base UI Checkbox since stage 1 of the migration\n * (2026-09-06).\n *\n * Controlled only; parents own the state. Two renderings, one class list:\n *\n * - With `onChange` it is the control, as Base UI renders it: a\n * `<span role=\"checkbox\">` with a hidden `<input>` beside it. Space\n * toggles, Enter does not, and its click never reaches the row around it.\n * - Without `onChange` it is a picture of the state, for a row that toggles\n * on its own click. It is hidden from assistive technology and takes no\n * focus, so the row is one control and says the state itself\n * (`aria-pressed`, or `aria-selected` on an option). Two focusable controls\n * nested in one another is what axe's `nested-interactive` rule rejects.\n *\n * The tick is always in the box, hidden when unchecked. A box that is empty\n * in one state and holds an icon in the other hangs on a line of text\n * differently in each, and moved 1px on every click (Katerina, 2026-09-07:\n * \"it should be fixed\"). With the tick always there, Base UI's `<span>` and\n * the `<button>` this used to be land on the same pixel, measured. In a flex\n * row nothing ever moved.\n *\n * With `label`, the words sit beside the box and are part of the target: Base\n * UI's `Field`, its `Field.Label` around the box and the words \u2014 the way its\n * Checkbox page labels a checkbox inside a form. The class list is Peek's\n * Read state panel's, where it was written by hand (UIG-7, 16 September). The\n * words keep their colour when the box is disabled (Katerina: \"no need\").\n *\n * Inside a busy `Form` it is disabled, and looks it (`formBusy.ts`).\n */\nexport interface CheckboxProps {\n checked: boolean\n onChange?: (checked: boolean) => void\n disabled?: boolean\n /**\n * Words beside the box. With `onChange`, clicking them toggles it too, and\n * they name it, so no `aria-label` is needed. `className` stays on the box.\n */\n label?: string\n 'aria-label'?: string\n /** Set by a `Field` with `required`; a caller inside one owes nothing. */\n 'aria-required'?: boolean | 'true' | 'false'\n className?: string\n}\n\nfunction squareClasses(checked: boolean, disabled: boolean, interactive: boolean, className?: string) {\n return cn(\n 'inline-flex items-center justify-center size-4 shrink-0 rounded-sm border transition-colors',\n checked ? 'bg-accent-primary border-accent-primary text-text-inverse' : 'bg-transparent border-border-strong hover:border-text-muted',\n disabled && 'opacity-50 pointer-events-none',\n interactive ? 'cursor-pointer' : 'pointer-events-none',\n className,\n )\n}\n\n/** `flex`, so the icon is a flex item and not an inline box with a line height of its own. */\nconst tickClasses = (checked: boolean) => cn('flex', !checked && 'invisible')\n\nconst WORDS_CLASSES = 'text-body-2 text-text-primary'\n\nexport function Checkbox({ checked, onChange, disabled: ownDisabled = false, label, className, ...aria }: CheckboxProps) {\n const formBusy = useFormBusy()\n const disabled = ownDisabled || formBusy\n if (!onChange) {\n const picture = (\n <span aria-hidden=\"true\" className={squareClasses(checked, disabled, false, className)}>\n <span className={tickClasses(checked)}>\n <IconCheck size={12} stroke={3} />\n </span>\n </span>\n )\n if (label === undefined) return picture\n // The row around it is the control and says the state; the words are only words.\n return (\n <span className=\"flex items-center gap-2\">\n {picture}\n <span className={WORDS_CLASSES}>{label}</span>\n </span>\n )\n }\n const box = (\n <BaseCheckbox.Root\n checked={checked}\n disabled={disabled}\n aria-label={aria['aria-label']}\n aria-required={aria['aria-required']}\n onCheckedChange={(next) => onChange(next)}\n onClick={(e) => e.stopPropagation()}\n className={(state) => squareClasses(state.checked, state.disabled, true, className)}\n >\n <BaseCheckbox.Indicator keepMounted className={(state) => tickClasses(state.checked)}>\n <IconCheck size={12} stroke={3} />\n </BaseCheckbox.Indicator>\n </BaseCheckbox.Root>\n )\n if (label === undefined) return box\n return (\n <BaseField.Root disabled={disabled}>\n {/* No pointer over words that toggle nothing. */}\n <BaseField.Label className={cn('flex items-center gap-2', !disabled && 'cursor-pointer')}>\n {box}\n <span className={WORDS_CLASSES}>{label}</span>\n </BaseField.Label>\n </BaseField.Root>\n )\n}\n", "import type { ReactNode } from 'react'\nimport { cn } from './cn'\n\n/**\n * Peek's Chip (2026-08-28), verbatim: a 20px pill in one of six colour\n * types, with room for a 12px icon either side. Under Signal the label is\n * mono and the coloured types carry a hairline, as Peek has it. The icon\n * slots centre their icon (Ship's addition \u2014 an icon beside text otherwise\n * rides on the baseline).\n *\n * The label is the `chip` type token, 11px / 500. It used to be spelled as a\n * plain class under a note that it must never be merged; `cn()` knows the ramp\n * now, so a token size survives beside a colour and the note is retired.\n */\nexport type ChipType = 'neutral' | 'brand' | 'info' | 'warning' | 'success' | 'error'\n\nexport interface ChipProps {\n type?: ChipType\n label?: string\n leadingIcon?: ReactNode\n trailingIcon?: ReactNode\n className?: string\n}\n\nconst typeStyles: Record<ChipType, string> = {\n neutral: 'bg-bg-inset text-text-primary',\n brand: 'bg-accent-muted text-accent-primary signal:border signal:border-accent-outline',\n info: 'bg-info-muted text-info-default signal:border signal:border-info-outline',\n warning: 'bg-warning-muted text-warning-default signal:border signal:border-warning-outline signal:shadow-glow-warning',\n success: 'bg-success-muted text-success-default signal:border signal:border-success-outline',\n error: 'bg-error-muted text-error-default signal:border signal:border-error-outline',\n}\n\nexport function Chip({ type = 'neutral', label, leadingIcon, trailingIcon, className }: ChipProps) {\n return (\n <div className={cn('inline-flex min-w-0 items-center justify-center gap-1.5 rounded-full max-h-[20px] px-2 py-1', typeStyles[type], className)}>\n {leadingIcon && <span className=\"flex size-3 shrink-0 items-center justify-center\">{leadingIcon}</span>}\n {label && (\n // A chip given a `max-w-*` cuts a long label with an ellipsis instead\n // of growing past it \u2014 Ship's project chip on an issue row\n // (2026-09-09). Clipped sideways only: `overflow-x-clip`, not\n // `truncate`, because `truncate` is `overflow: hidden` on both axes and\n // the label's line box (11px under Signal) is tighter than its glyphs \u2014\n // the screenshot diff caught every descender cut off. `clip` is the\n // one overflow that leaves the other axis visible. A chip with no cap\n // draws exactly as before.\n <span className=\"min-w-0 overflow-x-clip text-ellipsis whitespace-nowrap text-chip signal:font-mono signal:text-small signal:font-semibold signal:tracking-wide signal:tabular-nums\">{label}</span>\n )}\n {trailingIcon && <span className=\"flex size-3 shrink-0 items-center justify-center\">{trailingIcon}</span>}\n </div>\n )\n}\n", "import {\n createContext,\n useContext,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type KeyboardEvent,\n type ReactNode,\n type RefObject,\n} from 'react'\nimport { Dialog } from '@base-ui/react/dialog'\nimport { Autocomplete } from '@base-ui/react/autocomplete'\nimport type { BaseUIEvent } from '@base-ui/react/types'\nimport { IconLoader2, IconSearch } from '@tabler/icons-react'\nimport { cn } from './cn'\nimport { Button } from './Button'\nimport { InputChip } from './ChipInput'\nimport { EmptyState } from './EmptyState'\nimport { FieldLine } from './Field'\nimport { Form } from './Form'\nimport { Kbd } from './Kbd'\nimport { EnterHint, MenuItemBody, menuItemClassName } from './Menu'\nimport { ScrollArea } from './ScrollArea'\nimport { SectionLabel } from './SectionLabel'\nimport { SkeletonBar } from './Skeleton'\n\n/**\n * A window that searches and runs things: a field, rows in groups, and a\n * footer naming the keys that work right now. Built the way Base UI's own\n * command palette example is \u2014 `Dialog` with an `Autocomplete` inside it,\n * the list drawn inline \u2014 which is migration decision D8, brought forward\n * by UIG-29 (Katerina, 15\u201316 September 2026).\n *\n * **Why not `DialogShell`.** A palette has no title bar, no close button and\n * no button row; its top is a field and its bottom is a key footer. Built on\n * the shell it would be the shell with every slot switched off and a second\n * card drawn inside it. `SearchInput` and `ChipInput` do not fit either: the\n * field here drives a list that is always open and never a popup.\n *\n * **What it owns, and what the caller owns** (Katerina, 16 September, P1 and\n * P2). Every key is this component's: the arrows and Enter are Base UI's,\n * and Tab to go in, Ctrl+Backspace to forget, Backspace to go back, Ctrl+Enter\n * to submit and where focus goes are written here, once, so the footer can be\n * written from the same state and never name a key that does nothing. The\n * caller owns the words, which rows exist, what each one does, and the levels\n * \u2014 a palette shows one level at a time, a `CommandPaletteSearch` or a\n * `CommandPaletteForm`, and the caller decides which.\n */\n\n/* \u2500\u2500 The window \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\ninterface PaletteContextValue {\n where?: string\n modKey: string\n popupRef: RefObject<HTMLDivElement | null>\n}\n\nconst PaletteContext = createContext<PaletteContextValue | null>(null)\n\nfunction usePalette(part: string) {\n const palette = useContext(PaletteContext)\n if (!palette) throw new Error(`[@estiva-app/ui] ${part} must be inside a CommandPalette.`)\n return palette\n}\n\nexport interface CommandPaletteProps {\n open: boolean\n onOpenChange: (open: boolean) => void\n /** The window's name for a screen reader \u2014 there is no title on screen. */\n label: string\n /** The footer's left side: where the palette was opened from. */\n where?: string\n /**\n * How the footer spells the modifier in `Ctrl+Backspace` and `Ctrl+Enter` \u2014\n * `Cmd` on a Mac. The keys answer to Ctrl and Cmd either way; the package\n * does not guess the platform, the app says it.\n */\n modKey?: string\n /** One level: a `CommandPaletteSearch` or a `CommandPaletteForm`. */\n children: ReactNode\n}\n\n/** The field a level starts on: the search field, or the form's first control. */\nconst CONTROL = 'input:not([type=\"hidden\"]):not([tabindex=\"-1\"]), textarea, button:not([tabindex=\"-1\"]), [role=\"combobox\"]'\n\nfunction firstControl(popup: HTMLElement | null): HTMLElement | null {\n if (!popup) return null\n const field = popup.querySelector<HTMLElement>('[data-command-palette-field]')\n if (field) return field\n return popup.querySelector<HTMLElement>('[data-command-palette-fields]')?.querySelector<HTMLElement>(CONTROL) ?? null\n}\n\nexport function CommandPalette({ open, onOpenChange, label, where, modKey = 'Ctrl', children }: CommandPaletteProps) {\n const popupRef = useRef<HTMLDivElement>(null)\n\n /*\n Focus never falls out of a level.\n\n Measured in the prototype: when Enter picks a row that then leaves the\n list, or a level is swapped for another, the element that had focus is\n gone, and focus lands on the page or on the dialog's own box. Every key\n after that goes nowhere \u2014 Esc still closes, and nothing else works. So\n after every render, a frame later, focus that is nowhere goes to the\n level's first control. Focus that is somewhere \u2014 a list opened from a\n field, which is portalled out of this box \u2014 is left alone.\n */\n useEffect(() => {\n if (!open) return\n const frame = requestAnimationFrame(() => {\n const popup = popupRef.current\n const active = document.activeElement\n if (popup && (!active || active === document.body || active === popup)) firstControl(popup)?.focus()\n })\n return () => cancelAnimationFrame(frame)\n })\n\n const context = useMemo(() => ({ where, modKey, popupRef }), [where, modKey])\n\n return (\n <Dialog.Root open={open} onOpenChange={(next) => onOpenChange(next)}>\n <Dialog.Portal>\n <Dialog.Backdrop className=\"fixed inset-0 z-40 bg-scrim\" />\n <Dialog.Viewport className=\"fixed inset-0 z-50 flex items-start justify-center pt-[16vh]\">\n <Dialog.Popup\n ref={popupRef}\n aria-label={label}\n initialFocus={() => firstControl(popupRef.current) ?? true}\n onFocus={(e) => {\n if (e.target === popupRef.current) firstControl(popupRef.current)?.focus()\n }}\n /* `outline-none`: the box is a programmatic focus target, not a\n Tab stop \u2014 the same reason as DialogShell's card. */\n className=\"flex w-[658px] max-w-[calc(100vw-32px)] flex-col overflow-hidden rounded-lg border border-border-default bg-bg-elevated shadow-lg outline-none\"\n >\n <PaletteContext.Provider value={context}>{children}</PaletteContext.Provider>\n </Dialog.Popup>\n </Dialog.Viewport>\n </Dialog.Portal>\n </Dialog.Root>\n )\n}\n\n/* \u2500\u2500 The parts every level shares \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n/**\n * The level you are in, drawn as a chip before the field. Removing it goes\n * back, and so does Backspace at the start of the field \u2014 one meaning for the\n * chip, whichever way you reach it.\n */\nexport interface CommandPaletteChip {\n label: string\n /** Before the label, 16px \u2014 an icon, a mark. */\n leading?: ReactNode\n onBack: () => void\n}\n\nfunction LevelChip({ chip, onBack }: { chip: CommandPaletteChip; onBack: () => void }) {\n return <InputChip label={chip.label} leading={chip.leading} onRemove={onBack} removeLabel={`Leave ${chip.label}`} truncate className=\"max-w-[272px] shrink-0\" />\n}\n\n/** Going back leaves focus in the level you land on, whichever way you went. */\nfunction useBack(chip: CommandPaletteChip | undefined, popupRef: RefObject<HTMLDivElement | null>) {\n return () => {\n if (!chip) return\n chip.onBack()\n requestAnimationFrame(() => firstControl(popupRef.current)?.focus())\n }\n}\n\ntype Key = [key: string, word: string]\n\nfunction Footer({ keys }: { keys: Key[] }) {\n const { where } = usePalette('The footer')\n return (\n <div className=\"flex h-9 shrink-0 items-center gap-4 border-t border-border-subtle px-5 text-caption text-text-secondary signal:font-mono signal:text-small signal:text-text-muted\">\n <span className=\"min-w-0 flex-1 truncate\">{where}</span>\n {keys.map(([key, word]) => (\n <span key={key} className=\"flex shrink-0 items-center gap-1.5\">\n <Kbd>{key}</Kbd> {word}\n </span>\n ))}\n </div>\n )\n}\n\nconst lowerFirst = (s: string) => s.charAt(0).toLowerCase() + s.slice(1)\n\n/* \u2500\u2500 A level of rows \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nexport interface CommandPaletteRow {\n /** Unique in the level. The footer follows the lit row by it. */\n id: string\n label: string\n /** The second line \u2014 where it is, who said it, when. */\n description?: string\n /** A 16px icon, drawn on the row's 32px tile. */\n icon?: ReactNode\n /** Instead of `icon`: something with a look of its own \u2014 a face, a status mark \u2014 in the same 32px space, with no tile. */\n leading?: ReactNode\n /** Enter, or a click. */\n onSelect: () => void\n /** The row leads to more rows: Tab, or \u2192 at the end of the text, goes in, and the row shows a chevron. */\n onGoIn?: () => void\n /** The row is the person's own history: Ctrl+Backspace forgets it. */\n onForget?: () => void\n}\n\nexport interface CommandPaletteGroup {\n /** The heading over the rows. A group with no rows is not drawn. */\n label: string\n rows: CommandPaletteRow[]\n}\n\nexport interface CommandPaletteSearchProps {\n query: string\n onQueryChange: (query: string) => void\n placeholder: string\n groups: CommandPaletteGroup[]\n /** The level you are in. Without one, this is the first level and there is nowhere to go back to. */\n chip?: CommandPaletteChip\n /** A line under the rows while more are on their way \u2014 \"Searching\u2026\". */\n pending?: string\n /**\n * Quiet lines under the rows, for what a person should know about them and\n * cannot act on \u2014 \"2 more where you cannot open them\", \"the search was\n * refused, so this list is incomplete\". Plain lines, never rows: the arrows\n * do not stop on them.\n */\n notes?: string[]\n /** The line when there are no rows. Leave it out while rows are still on their way. */\n empty?: string\n /** Above the rows: a `CommandPaletteWorking`, `CommandPaletteAnswer` or `CommandPaletteQuote`. */\n children?: ReactNode\n}\n\ntype ListGroup = { value: string; items: CommandPaletteRow[] }\n\nexport function CommandPaletteSearch({ query, onQueryChange, placeholder, groups, chip, pending, notes = [], empty, children }: CommandPaletteSearchProps) {\n const { modKey, popupRef } = usePalette('CommandPaletteSearch')\n const back = useBack(chip, popupRef)\n const items = useMemo<ListGroup[]>(() => groups.filter((g) => g.rows.length > 0).map((g) => ({ value: g.label, items: g.rows })), [groups])\n const rows = items.flatMap((g) => g.items)\n\n /*\n The lit row, followed by id rather than held as an object: a caller that\n builds its rows again on every render hands Base UI new objects with the\n same ids, and a key must run the handler of the row on screen now, not\n of the one that was lit a render ago.\n */\n const [litId, setLitId] = useState<string | undefined>()\n const lit = litId === undefined ? undefined : rows.find((r) => r.id === litId)\n\n /*\n The lit row stays lit when rows arrive above it (F7).\n\n Base UI keeps the highlight's *position*, not its row: measured, with the\n second row lit, two rows arriving above it handed the highlight to\n whatever now sat second, and Enter would have opened that. Base UI has no\n public way to set the highlight, so the palette walks it back with the\n arrow keys, exactly as a person would. It does so only when Base UI moved\n the highlight by itself (\"none\") \u2014 never after an arrow, the pointer, or\n typing, which starts again from the first row.\n\n And only once the person has moved the highlight on this level. Until then\n the lit row is simply the first one, and a row that arrives above it \u2014 a\n thread's own rows, a moment after opening \u2014 becomes the first one.\n */\n const inputRef = useRef<HTMLInputElement>(null)\n const reported = useRef<{ id?: string; reason?: string }>({})\n const settled = useRef<{ id?: string; query: string; level?: string } | null>(null)\n /** The person has moved the highlight since the text or the level last changed. */\n const moved = useRef(false)\n useLayoutEffect(() => {\n const before = settled.current\n const now = reported.current\n const sameLevel = before?.query === query && before?.level === chip?.label\n if (!sameLevel) moved.current = false\n if (moved.current && before?.id && now.reason === 'none' && now.id !== before.id && sameLevel) {\n const ids = rows.map((r) => r.id)\n const want = ids.indexOf(before.id)\n const at = now.id === undefined ? -1 : ids.indexOf(now.id)\n const input = inputRef.current\n if (input && want !== -1 && at !== -1) {\n const key = want > at ? 'ArrowDown' : 'ArrowUp'\n for (let step = 0; step < Math.abs(want - at); step++) {\n input.dispatchEvent(new KeyboardEvent('keydown', { key, bubbles: true, cancelable: true }))\n }\n }\n }\n settled.current = { id: reported.current.id, query, level: chip?.label }\n })\n\n const onKeyDown = (e: BaseUIEvent<KeyboardEvent<HTMLInputElement>>) => {\n if (e.key === 'Home' || e.key === 'End') {\n // Home and End move the text cursor and nothing else (the key list).\n // Base UI also sends the highlight to the first or last row (measured),\n // so its handling stops here and the field's own takes over.\n e.preventBaseUIHandler()\n return\n }\n const field = e.currentTarget\n const collapsed = field.selectionStart === field.selectionEnd\n const atStart = collapsed && field.selectionStart === 0\n const atEnd = collapsed && field.selectionEnd === field.value.length\n if (e.key === 'Backspace' && (e.ctrlKey || e.metaKey)) {\n // Forgetting is for a row that can be forgotten; anywhere else the key\n // deletes a word, as it does in any field.\n if (lit?.onForget) {\n e.preventDefault()\n lit.onForget()\n }\n return\n }\n if (e.key === 'Backspace' && atStart && chip && !e.shiftKey && !e.altKey) {\n e.preventDefault()\n back()\n return\n }\n if (e.key === 'Tab' && !e.shiftKey) {\n // Tab never walks out of the field at a level of rows: it goes in, or\n // it does nothing (the key list).\n e.preventDefault()\n lit?.onGoIn?.()\n return\n }\n if (e.key === 'ArrowRight' && atEnd && lit?.onGoIn && !e.shiftKey) {\n e.preventDefault()\n lit.onGoIn()\n }\n }\n\n const keys: Key[] = [\n ...(rows.length > 1 ? ([['\u2191\u2193', 'move']] as Key[]) : []),\n ...(lit ? ([lit.onGoIn ? ['Tab', 'go in'] : ['Enter', 'open']] as Key[]) : []),\n ...(lit?.onForget ? ([[`${modKey}+Backspace`, 'forget']] as Key[]) : []),\n // Backspace goes back from the start of the field; it is named while the\n // field is empty, when that is what it will do.\n ...(chip && query === '' ? ([['Backspace', 'back']] as Key[]) : []),\n ['Esc', 'close'],\n ]\n\n return (\n <Autocomplete.Root\n items={items}\n mode=\"none\"\n inline\n open\n value={query}\n onValueChange={(value, details) => {\n // Pressing a row would write the row's name into the field.\n if (details.reason === 'item-press') return\n onQueryChange(value)\n }}\n itemToStringValue={(row) => (row as CommandPaletteRow).label}\n /* The first row is always lit, so Enter always has something to do;\n the highlight stays where it is when the pointer leaves; and the\n arrows stop at the ends rather than wrapping (the key list). */\n autoHighlight=\"always\"\n keepHighlight\n loopFocus={false}\n onItemHighlighted={(row, details) => {\n const id = (row as CommandPaletteRow | undefined)?.id\n reported.current = { id, reason: details.reason }\n if (details.reason !== 'none') moved.current = true\n setLitId(id)\n }}\n >\n <div className=\"flex h-12 shrink-0 items-center gap-3 border-b border-border-subtle px-5\">\n <IconSearch size={16} stroke={1.5} className=\"shrink-0 text-text-secondary\" />\n {chip && <LevelChip chip={chip} onBack={back} />}\n <Autocomplete.Input\n ref={inputRef}\n data-command-palette-field=\"\"\n placeholder={placeholder}\n /* Named by what it asks for. Chrome would fall back to the\n placeholder by itself; said outright, the name does not depend\n on a fallback (ChipInput's field lost its name to one, Finding 6). */\n aria-label={placeholder}\n onKeyDown={onKeyDown}\n className=\"min-w-0 flex-1 bg-transparent text-input-value text-text-primary outline-none placeholder:text-text-muted\"\n />\n </div>\n\n {/* The rows' box is a menu's (P4, Katerina 16 September): 8px in from\n the edge, so the lit row's fill sits where it sits in every menu,\n and the tiles line up with the magnifier at 21px. */}\n <ScrollArea viewportClassName=\"max-h-[420px]\" contentClassName=\"flex flex-col p-2\">\n {children != null && <div className=\"flex flex-col gap-2 px-3 pb-2 pt-3\">{children}</div>}\n\n <Autocomplete.List className=\"flex flex-col\">\n {(group: ListGroup) => (\n <Autocomplete.Group key={group.value} items={group.items} className=\"flex flex-col\">\n {/* A heading labels the rows, it is not one of them: read\n secondary, as in a menu (MenuSection). */}\n <Autocomplete.GroupLabel className=\"flex h-7 shrink-0 items-center px-3\">\n <SectionLabel className=\"text-text-secondary\">{group.value}</SectionLabel>\n </Autocomplete.GroupLabel>\n <Autocomplete.Collection>\n {(row: CommandPaletteRow) => (\n /* The menu row, as the list's own option \u2014 the way Select\n puts it on `Select.Item`. One row, two parts. */\n <Autocomplete.Item key={row.id} value={row} onClick={() => row.onSelect()} className={menuItemClassName({ size: 'tall' })}>\n <MenuItemBody\n size=\"tall\"\n label={row.label}\n description={row.description}\n leading={<RowLeading row={row} />}\n submenu={!!row.onGoIn}\n hint={row.onGoIn ? <Kbd>Tab</Kbd> : <EnterHint />}\n />\n </Autocomplete.Item>\n )}\n </Autocomplete.Collection>\n </Autocomplete.Group>\n )}\n </Autocomplete.List>\n\n {/* Both lines stay mounted, as Base UI asks, so a screen reader hears\n them change; they take no room while they say nothing. */}\n <Autocomplete.Status className=\"flex items-center gap-2 px-3 [&:not(:empty)]:h-8\">\n {pending && (\n <>\n <IconLoader2 size={12} stroke={1.5} className=\"shrink-0 animate-spin text-text-muted\" />\n <span className=\"text-caption text-text-muted\">{pending}</span>\n </>\n )}\n </Autocomplete.Status>\n {notes.map((note) => (\n <div key={note} className=\"flex min-h-8 items-center px-3\">\n <FieldLine>{note}</FieldLine>\n </div>\n ))}\n <Autocomplete.Empty className=\"flex items-center px-3 [&:not(:empty)]:min-h-10\">\n {empty && <EmptyState scope=\"section\" message={empty} />}\n </Autocomplete.Empty>\n </ScrollArea>\n\n <Footer keys={keys} />\n </Autocomplete.Root>\n )\n}\n\n/** Every row leads with the same 32px space, so labels line up whatever leads them. */\nfunction RowLeading({ row }: { row: CommandPaletteRow }) {\n if (row.icon != null) {\n return <span className=\"flex size-8 items-center justify-center rounded-sm bg-bg-inset text-text-secondary\">{row.icon}</span>\n }\n return <span className=\"flex size-8 items-center justify-center\">{row.leading}</span>\n}\n\n/* \u2500\u2500 A level that is a form \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nexport interface CommandPaletteFormProps {\n /** Names what the form makes, and goes back when removed. */\n chip: CommandPaletteChip\n /** Before the chip, 16px \u2014 the mark of whatever the form writes to. */\n icon?: ReactNode\n /** The button's word, and the footer's beside Ctrl+Enter. */\n submitLabel: string\n onSubmit: () => void\n /** Why submitting has to wait \u2014 nothing changed yet. The button says it on hover, and Ctrl+Enter does nothing. */\n submitWaits?: string\n /** While the thing is being made: the fields lock, the button reads `button`, and `line` says what is happening. */\n working?: { button: string; line: string }\n /** What went wrong, beside the button. */\n error?: string\n /** The fields, each in a `Field`. */\n children: ReactNode\n}\n\nconst TEXT_TYPES = new Set(['', 'text', 'search', 'email', 'url', 'tel', 'password', 'number'])\n\n/** A field you type into \u2014 not a list, and not a chip field, whose Backspace takes a chip. */\nfunction isTextField(el: Element | null): el is HTMLInputElement | HTMLTextAreaElement {\n if (el instanceof HTMLTextAreaElement) return true\n return el instanceof HTMLInputElement && TEXT_TYPES.has(el.getAttribute('type') ?? '') && el.getAttribute('role') !== 'combobox' && el.tabIndex >= 0\n}\n\nexport function CommandPaletteForm({ chip, icon, submitLabel, onSubmit, submitWaits, working, error, children }: CommandPaletteFormProps) {\n const { modKey, popupRef } = usePalette('CommandPaletteForm')\n const back = useBack(chip, popupRef)\n const frameRef = useRef<HTMLDivElement>(null)\n const busy = !!working\n const busyRef = useRef(busy)\n busyRef.current = busy\n\n /** A field that says it needs something: Base UI marks the `Field` itself. */\n const firstInvalid = () => frameRef.current?.querySelector<HTMLElement>('[data-invalid]')?.querySelector<HTMLElement>(CONTROL) ?? null\n\n /*\n The form's one way in: its button (type=\"submit\") and Ctrl+Enter both\n arrive here through the package Form's submit, so Base UI's field check\n runs for both. The guard stays: a waiting button is focusable while\n disabled, so it is not the browser that stops it.\n */\n const submit = () => {\n if (busyRef.current || submitWaits) return\n onSubmit()\n // If the caller marked fields instead of starting, the first of them\n // takes focus: the key list's \"focus goes to the first\".\n requestAnimationFrame(() => {\n if (!busyRef.current) firstInvalid()?.focus()\n })\n }\n\n /*\n The lock must not lose focus \u2014 measured in the prototype, where after\n Ctrl+Enter no key but Esc ever worked again. The package Form does it now\n (UIG-7): while it works, focus sits on the form, where its keys still\n arrive; when it stops, focus goes to the first field that needs something,\n or back where it was, or to the first field.\n */\n\n /*\n Backspace goes back only where nothing typed is lost: from an empty text\n field, or from anywhere in a form that has no text field at all (a form\n that is one list). In a list inside a form with text fields it does\n nothing, so walking the form with Tab never throws the draft away.\n */\n const backWorksFrom = (el: Element | null) => {\n const frame = frameRef.current\n if (!frame || busy || !el || !frame.contains(el)) return false\n if (isTextField(el)) return el.value === ''\n return ![...frame.querySelectorAll('input, textarea')].some(isTextField)\n }\n\n // The footer names Backspace only where it works, so it follows focus and typing.\n const [backNamed, setBackNamed] = useState(false)\n const measure = () => setBackNamed(backWorksFrom(document.activeElement))\n useLayoutEffect(measure)\n\n // Ctrl+Enter is the Form's; plain Enter in a field sends nothing here (`enterSends={false}`).\n const onKeyDown = (e: KeyboardEvent<HTMLDivElement>) => {\n if (e.key === 'Backspace' && !e.ctrlKey && !e.metaKey && !e.altKey && backWorksFrom(e.target as Element)) {\n e.preventDefault()\n back()\n }\n }\n\n const keys: Key[] = [\n ...(!busy && !submitWaits ? ([[`${modKey}+Enter`, lowerFirst(submitLabel)]] as Key[]) : []),\n ...(backNamed ? ([['Backspace', 'back']] as Key[]) : []),\n ['Esc', 'close'],\n ]\n\n return (\n <div ref={frameRef} tabIndex={-1} onKeyDown={onKeyDown} onFocus={measure} onBlur={measure} onInput={measure} className=\"flex min-h-0 flex-col outline-none\">\n <div className=\"flex h-12 shrink-0 items-center gap-3 border-b border-border-subtle px-5\">\n {icon != null && <span className=\"flex shrink-0 items-center text-text-secondary\">{icon}</span>}\n <LevelChip chip={chip} onBack={back} />\n </div>\n {/* The package Form, from the fields to the button (UIG-7). The chip row\n stays outside it, so its \u2715 still goes back while the form works; the\n button row is inside it, so focus that was on the button is held.\n The fields are found through data-command-palette-fields. */}\n <Form data-command-palette-fields=\"\" busy={busy} enterSends={false} onSubmit={submit} className=\"flex min-h-0 flex-col\">\n <ScrollArea viewportClassName=\"max-h-[420px]\" contentClassName=\"flex flex-col px-5 py-4\">\n <div className=\"flex flex-col gap-4\">{children}</div>\n </ScrollArea>\n <div className=\"flex shrink-0 items-center gap-3 px-5 pb-4\">\n <div className=\"min-w-0 flex-1\">\n {working ? <FieldLine>{working.line}</FieldLine> : error ? <FieldLine tone=\"error\">{error}</FieldLine> : null}\n </div>\n <Button\n type=\"submit\"\n variant=\"primary\"\n disabled={busy}\n disabledReason={busy ? undefined : submitWaits}\n leadingIcon={busy ? <IconLoader2 size={16} stroke={1.5} className=\"animate-spin\" /> : undefined}\n >\n {working ? working.button : submitLabel}\n </Button>\n </div>\n </Form>\n <Footer keys={keys} />\n </div>\n )\n}\n\n/* \u2500\u2500 What sits above the rows \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nconst BAR_WIDTHS = ['w-4/5', 'w-3/5', 'w-2/3']\n\nexport interface CommandPaletteWorkingProps {\n /** What is happening \u2014 \"Reading 12 messages\u2026\". */\n children: ReactNode\n /** Grey bars under the line, where the result will be. Default 2. */\n bars?: 0 | 1 | 2 | 3\n}\n\n/** A line saying what is being worked on, with grey bars where the result will land. */\nexport function CommandPaletteWorking({ children, bars = 2 }: CommandPaletteWorkingProps) {\n return (\n <div role=\"status\" className=\"flex flex-col gap-2\">\n <span className=\"flex items-center gap-2 text-caption text-text-secondary\">\n <IconLoader2 size={14} stroke={1.5} className=\"shrink-0 animate-spin\" />\n {children}\n </span>\n {BAR_WIDTHS.slice(0, bars).map((width) => (\n <SkeletonBar key={width} className={cn('h-3', width)} />\n ))}\n </div>\n )\n}\n\nexport interface CommandPaletteAnswerProps {\n /**\n * The answer's text. A blank line starts a new paragraph, and a number in\n * square brackets \u2014 `[1]` \u2014 is drawn as a small mark pointing at the row of\n * the same number below.\n */\n children: string\n /** A quiet line under the answer \u2014 what a key will do now. */\n note?: string\n}\n\n/** A written answer, with marks that point at the rows it came from. */\nexport function CommandPaletteAnswer({ children, note }: CommandPaletteAnswerProps) {\n const paragraphs = children.split(/\\n\\s*\\n/).filter((p) => p.trim() !== '')\n return (\n <div className=\"flex flex-col gap-2\">\n {paragraphs.map((paragraph, i) => (\n <p key={i} className=\"whitespace-pre-wrap text-body-2 text-text-primary\">\n {/* A mark belongs to the word before it: the space a writer leaves\n before \"[1]\" would stand between them as a gap. */}\n {paragraph.split(/\\s*(\\[\\d+\\])/).map((part, j) =>\n /^\\[\\d+\\]$/.test(part) ? (\n <sup key={j} className=\"ml-0.5 font-mono text-small text-accent-primary\">\n {part.slice(1, -1)}\n </sup>\n ) : (\n part\n ),\n )}\n </p>\n ))}\n {note && <FieldLine>{note}</FieldLine>}\n </div>\n )\n}\n\nexport interface CommandPaletteQuoteProps {\n /** Text written for someone to use \u2014 shown as it will be used, line breaks kept. */\n children: string\n}\n\n/** Text written to be used elsewhere, set off by a rule at its left edge. */\nexport function CommandPaletteQuote({ children }: CommandPaletteQuoteProps) {\n return <p className=\"whitespace-pre-wrap border-l-2 border-border-default pl-3 text-body-2 text-text-primary\">{children}</p>\n}\n", "import { useMemo, useState, type KeyboardEvent, type ReactNode } from 'react'\nimport { Button as BaseButton } from '@base-ui/react/button'\nimport { Combobox } from '@base-ui/react/combobox'\nimport { IconX } from '@tabler/icons-react'\nimport { cn } from './cn'\nimport { MenuItem, MenuPanel } from './Menu'\nimport { ScrollArea } from './ScrollArea'\n\n/* The chip's own look, written once: `InputChip` draws it for a caller who\n wants a chip on its own, and `ChipInput` gives the same classes to Base UI's\n `Combobox.Chip`, which is the one that joins the input's keyboard. */\nconst CHIP_BOX =\n // Curved, not a pill (Katerina, 2026-09-01): the Avatar keeps its own\n // rounded-sm corners \u2014 never a forced circle \u2014 and the chip's corner\n // follows concentrically: 4px face + 2px inset = rounded-md.\n 'inline-flex items-center gap-1.5 bg-bg-elevated border border-border-subtle rounded-md py-0.5 max-h-[24px]'\nconst CHIP_LABEL = 'text-caption font-medium text-text-primary'\nconst CHIP_REMOVE = 'size-4 flex items-center justify-center rounded-full hover:bg-bg-hover text-text-secondary'\n// The padding follows the contents (Katerina, 2026-09-01): a face sits 2px\n// from the edge, a bare label needs 8px of air; the \u2715 brings its own box, so\n// 4px behind it \u2014 8px when there isn't one.\nconst chipPadding = (leading: boolean, removable: boolean) => cn(leading ? 'pl-[2px]' : 'pl-2', removable ? 'pr-1' : 'pr-2')\n\n/**\n * The chip a `ChipInput` is made of: a 24px pill with an optional 16px\n * leading (a face, an icon), a 12px label, and the \u2715 that removes it.\n * Exported on its own (Katerina, 2026-09-01) under a name that promises\n * nothing about people \u2014 a chip like this may one day hold a label, a file,\n * a filter.\n *\n * Inside a `ChipInput` the chip is Base UI's `Combobox.Chip` wearing these\n * same classes, because there it has to answer the arrow keys and Backspace\n * along with the input. This component is for a chip standing alone.\n */\nexport interface InputChipProps {\n label: string\n /** Before the label, 16px \u2014 an Avatar, an icon. */\n leading?: ReactNode\n /** Draws the \u2715; absent, the chip is display-only. */\n onRemove?: () => void\n /**\n * The \u2715's name for a screen reader. `Remove <label>` when not given. A chip\n * whose \u2715 does something other than remove it names what it does \u2014 a\n * launcher's scope chip leaves the scope (\"Leave Ship\").\n */\n removeLabel?: string\n /**\n * Cut a long label with an ellipsis once the chip is capped, by a\n * `max-w-*` on `className`. Off unless asked: cutting clips up to 4px of a\n * letter's soft edge at 1x even when the label fits (measured 2026-09-15),\n * so a chip that is never capped keeps every pixel.\n */\n truncate?: boolean\n className?: string\n}\n\nexport function InputChip({ label, leading, onRemove, removeLabel, truncate, className }: InputChipProps) {\n return (\n <div className={cn(CHIP_BOX, chipPadding(!!leading, !!onRemove), className)}>\n {leading && <span className=\"flex shrink-0 items-center\">{leading}</span>}\n <span className={cn(CHIP_LABEL, truncate && 'min-w-0 truncate')}>{label}</span>\n {/* Base UI's `Button`, as every button in the package is (D6). Base UI\n has no chip of its own \u2014 its only chips are `Combobox.Chip` and\n `ChipRemove`, which throw outside a combobox \u2014 so the \u2715 is the one\n part of a chip standing alone that it has a counterpart for. */}\n {onRemove && (\n <BaseButton\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation()\n onRemove()\n }}\n // In a capped chip the label gives way, never the \u2715.\n className={cn(CHIP_REMOVE, truncate && 'shrink-0')}\n aria-label={removeLabel ?? `Remove ${label}`}\n >\n <IconX size={10} stroke={1.5} />\n </BaseButton>\n )}\n </div>\n )\n}\n\n/**\n * A multi-select input: chips for the chosen, a typeahead for the rest \u2014\n * Peek's PersonChipInput (2026-09-01), generalised on the way in, and on Base\n * UI's `Combobox` since stage 5 of the migration (2026-09-13).\n *\n * **What the part brought, and what it took away from this file.** The list is\n * a real listbox now: the input keeps focus and says which row is highlighted\n * through `aria-activedescendant`, where before the rows were plain buttons in\n * a `<div>` and nothing was announced. With it went the highlight index, the\n * arrow keys, Enter, the filter loop, the blur timeout that kept a click on a\n * row from closing the list under the pointer, the `createPortal`, the\n * measured anchor rect, the resize and scroll listeners that re-measured it,\n * and `fit.ts` \u2014 the flip-up-when-low arithmetic this package carried for one\n * caller (PLAN Finding 22). Base UI's positioner does the flipping, and it\n * does it against the element rather than against a rect read a frame ago.\n *\n * **What this file still decides**, because none of it is the part's business:\n * which options are on offer (the chosen and the excluded are not), that a\n * match is on the label *or* the description, that suggestions appear only\n * once you type \u2014 focusing must not drop the whole directory over the surface\n * below \u2014 and that Backspace on an empty query takes the last chip.\n *\n * Escape clears the query when there is one and bubbles when there is not, so\n * the surface around it (dialog, launcher) can act.\n *\n * Generic over the option type: the objects handed back through `onChange`\n * are the caller's own, extra fields and all \u2014 no re-mapping on the way out.\n */\nexport interface ChipInputOption {\n id: string\n label: string\n /** The suggestion row's second line \u2014 a role, an address. Also searched. */\n description?: string\n}\n\nexport interface ChipInputProps<T extends ChipInputOption = ChipInputOption> {\n value: T[]\n onChange: (next: T[]) => void\n /** The directory the typeahead searches. */\n options: T[]\n placeholder?: string\n autoFocus?: boolean\n /** Option ids excluded from the suggestion list (e.g., the current user). */\n excludeIds?: string[]\n /** Before a chip's label, 16px \u2014 a face, an icon. */\n chipLeading?: (option: T) => ReactNode\n /** Before a suggestion row's label, 32px. */\n rowLeading?: (option: T) => ReactNode\n /** Set by a `Field` with `required`; a caller inside one owes nothing. */\n 'aria-required'?: boolean | 'true' | 'false'\n /**\n * The field's name, for a caller that is not inside a `Field` \u2014 a `Field`'s\n * label names it already. With neither this nor `aria-labelledby`, the\n * placeholder is the name, and it stays the name once a chip is in.\n */\n 'aria-label'?: string\n /** The id of what names the field on the page \u2014 a visible \"To:\", say. */\n 'aria-labelledby'?: string\n}\n\nexport function ChipInput<T extends ChipInputOption = ChipInputOption>({\n value,\n onChange,\n options,\n placeholder = 'Search\u2026',\n autoFocus,\n excludeIds = [],\n chipLeading,\n rowLeading,\n ...aria\n}: ChipInputProps<T>) {\n const [query, setQuery] = useState('')\n /* The box the list hangs from. Base UI hangs a combobox's list from its\n input by default, and the input sits inside this box's 12px of padding\n and its border, so the list came out 26px narrower than the field it\n belongs to (measured: 358 against 384) and started inside it. Held as an\n element, not a measured rect, so the positioner re-measures it. */\n const [box, setBox] = useState<HTMLDivElement | null>(null)\n\n /* What is on offer: never what is already chosen, never what the caller\n excluded. The part filters by the query; which options exist at all is\n this component's question. */\n const available = useMemo(() => {\n const chosen = new Set(value.map((o) => o.id))\n const excluded = new Set(excludeIds)\n return options.filter((o) => !chosen.has(o.id) && !excluded.has(o.id))\n }, [options, value, excludeIds])\n\n /* A match is on the label or the description \u2014 \"who is the engineer\" finds\n the person by their role. Base UI's own filter reads one string per item. */\n const filter = useMemo(\n () => (item: T, q: string) => {\n const needle = q.trim().toLowerCase()\n if (!needle) return true\n return item.label.toLowerCase().includes(needle) || (item.description ?? '').toLowerCase().includes(needle)\n },\n [],\n )\n\n /* Suggestions appear only once you type. Focus \u2014 or a dialog's autoFocus \u2014\n must not drop the whole directory over the surface below, which is why\n the open state is this component's and not the part's. */\n const open = query.trim().length > 0\n\n /* The field's name (PLAN Finding 6). With no chip, Chrome names the field\n by its placeholder; the first chip takes the placeholder away, and the\n field was left with no name at all (measured: \"\"). So the placeholder\n stays on as the name once it is no longer drawn. A caller's own name\n wins, and so does a `Field`'s label: Base UI points `aria-labelledby` at\n it, and a label by reference outranks `aria-label`.\n Only a name that exists is passed. Base UI copies a caller's prop over its\n own even when the value is `undefined`, so an `aria-labelledby` written\n out empty would wipe the `Field`'s. */\n const ariaLabel = aria['aria-label'] ?? (value.length > 0 ? placeholder : undefined)\n const naming = {\n ...(ariaLabel !== undefined && { 'aria-label': ariaLabel }),\n ...(aria['aria-labelledby'] !== undefined && { 'aria-labelledby': aria['aria-labelledby'] }),\n }\n\n function removeLast() {\n if (value.length > 0) onChange(value.slice(0, -1))\n }\n\n function onInputKeyDown(event: KeyboardEvent<HTMLInputElement>) {\n if (event.key === 'Backspace' && query === '' && value.length > 0) {\n // Consumed: removing a chip must not double as the surface's \"back\",\n // and Base UI would otherwise walk focus into the chips first.\n event.preventDefault()\n event.stopPropagation()\n removeLast()\n return\n }\n if (event.key === 'Escape' && query !== '') {\n // Consume it only when there is something to clear; an idle input lets\n // Escape bubble so the surface around it (dialog, launcher) can act.\n event.preventDefault()\n event.stopPropagation()\n setQuery('')\n }\n }\n\n return (\n <Combobox.Root\n multiple\n items={available}\n value={value}\n onValueChange={(next) => {\n onChange(next as T[])\n setQuery('')\n }}\n inputValue={query}\n onInputValueChange={setQuery}\n open={open}\n /* The list is the query's, not the click's \u2014 see `open` above. */\n openOnInputClick={false}\n filter={filter}\n itemToStringLabel={(option) => (option as T).label}\n >\n {/* The box the chips and the input share. `Combobox.Chips` is what makes\n the two one control for the keyboard; the look is what it always was. */}\n <Combobox.Chips ref={setBox} className=\"bg-bg-inset border border-border-default hover:border-border-strong focus-within:border-border-focus focus-within:hover:border-border-focus rounded-lg px-3 py-1.5 flex flex-wrap items-center gap-1.5 transition-colors min-h-[38px] cursor-text signal:transition-shadow signal:focus-within:shadow-focus-ring\">\n {value.map((option) => (\n <Combobox.Chip key={option.id} className={cn(CHIP_BOX, chipPadding(!!chipLeading, true))}>\n {chipLeading && <span className=\"flex shrink-0 items-center\">{chipLeading(option)}</span>}\n <span className={CHIP_LABEL}>{option.label}</span>\n <Combobox.ChipRemove className={CHIP_REMOVE} aria-label={`Remove ${option.label}`}>\n <IconX size={10} stroke={1.5} />\n </Combobox.ChipRemove>\n </Combobox.Chip>\n ))}\n <Combobox.Input\n autoFocus={autoFocus}\n placeholder={value.length === 0 ? placeholder : ''}\n aria-required={aria['aria-required']}\n {...naming}\n onKeyDown={onInputKeyDown}\n className=\"flex-1 min-w-[120px] bg-transparent text-body-2 text-text-primary placeholder:text-text-muted outline-none border-none\"\n />\n </Combobox.Chips>\n\n <Combobox.Portal>\n <Combobox.Positioner\n anchor={box}\n sideOffset={GAP}\n collisionPadding={VIEWPORT_PAD}\n className=\"z-50 data-[anchor-hidden]:hidden\"\n >\n {/* As wide as the box, which is what the hand-measured rect was\n for; `--anchor-width` is the positioner's own answer, and the\n anchor is the box (see `box` above), not the input. The padding\n is on the scrolling content so the bar hugs the panel (D63), and\n 240px is the cap this list has always had. */}\n <Combobox.Popup className=\"w-[var(--anchor-width)] p-0\" render={<MenuPanel />}>\n <ScrollArea viewportClassName=\"max-h-[240px]\" contentClassName=\"flex flex-col p-2\">\n <Combobox.List>\n {(option: T) => (\n <Combobox.Item\n key={option.id}\n value={option}\n render={\n <MenuItem\n size=\"tall\"\n leading={rowLeading?.(option)}\n label={option.label}\n description={option.description}\n />\n }\n />\n )}\n </Combobox.List>\n </ScrollArea>\n </Combobox.Popup>\n </Combobox.Positioner>\n </Combobox.Portal>\n </Combobox.Root>\n )\n}\n\n/** `Menu`'s numbers, because the list hangs the same way a menu does. */\nconst GAP = 4\nconst VIEWPORT_PAD = 8\n", "import { createContext, useContext, type ComponentPropsWithRef, type ReactElement, type ReactNode, type RefObject } from 'react'\nimport { IconChevronRight } from '@tabler/icons-react'\nimport { Menu as BaseMenu } from '@base-ui/react/menu'\nimport { cn } from './cn'\nimport { triggerDisabled } from './triggerDisabled'\nimport { ScrollArea } from './ScrollArea'\nimport { Kbd } from './Kbd'\nimport { SectionLabel } from './SectionLabel'\n\n/**\n * THE menu shell (2026-09-01) \u2014 extracted once, for every menu in every app.\n * On Base UI's `Menu` since stage 4 of the migration (2026-09-07).\n *\n * Peek has no single Menu file; its topic menu, conversation menus, files\n * menu and the top bar's account menu each hand-roll the same thing, and\n * they disagree: some close on Escape, most do not, and row heights and\n * minimum widths drift copy by copy. Ship extracted the shape so it would\n * not become the next copy; the package exists so nobody becomes the one\n * after that.\n *\n * What every menu shares, kept exactly: an elevated container with a\n * hairline border, 8px radius, 8px padding and the large shadow; items that\n * are 8px-radius rows, `px-2 py-1.5` and never shorter than 36px, hover\n * fill, 14px text, destructive ones in the error colour; section headings\n * as a SectionLabel in a 32px row; and the two exits every menu has \u2014 Escape and a click outside.\n *\n * **What stage 4 changed, and it is the first thing a keyboard user notices:\n * the arrow keys walk the rows.** \u2191 and \u2193 move between items and wrap, Home\n * and End jump to the ends, typing a row's first letters jumps to it, and\n * focus moves into the menu when it opens and back out when it closes. None\n * of that existed; a menu was a portalled box of buttons you tabbed through.\n *\n * Deleted with the port: the `createPortal`, the `mousedown` listener on\n * `document`, the Escape listener beside it, the resize and scroll listeners,\n * the provisional hidden render this component did in order to measure\n * itself, and `fit.ts` \u2014 the shared geometry the shell and Select both used.\n * Floating UI places, flips and clamps all of it.\n *\n * The three anchorings are unchanged, and all three now portal (the third\n * did not, which is exactly how the identity menu got covered by a z-indexed\n * panel header in the first place):\n * - `anchor` (an element, or a rect a click handler measured): the menu hangs\n * under it, flips above when the room below is worse, and stays on screen.\n * `align=\"right\"` hangs the menu's right edge from the anchor's.\n * - `position` (viewport coordinates the caller computed): the corner the\n * caller chose, as a virtual anchor, so it still cannot land off screen.\n * - neither: under the element the menu sits in, right-aligned \u2014 what\n * `absolute right-0 top-full mt-1` drew, but portalled, so no ancestor's\n * stacking context or overflow can clip it.\n */\n/**\n * Whether a row is inside a `Menu`.\n *\n * It is load-bearing: `MenuItem` and `MenuSection` are also used\n * inside a bare `MenuPanel`, with no `Menu` around them \u2014 Peek's `@`, `/` and\n * `[` pickers and its compose menu all do that, because a popup inside a text\n * editor cannot have a menu's keyboard: the editor's suggestion plugin already\n * owns it. A Base UI `Menu.Item` outside a `Menu.Root` has no context to read,\n * so those rows stay exactly the buttons they have always been, and only rows\n * inside a real menu become the part.\n */\nconst MenuContext = createContext<{ openOnHover: boolean } | null>(null)\n\n/**\n * The menu's surface, with none of its behaviour \u2014 an elevated box with a\n * hairline border, 8px radius, 8px padding and the large shadow.\n *\n * Split out of `Menu` on 2026-09-05. `Menu` owns Escape, outside-click,\n * placement and now the keyboard, and that is right for a menu opened from a\n * trigger \u2014 but a type-ahead popup inside a text editor cannot have them: the\n * editor's suggestion plugin already owns the keyboard and positions the\n * popup, and a second Escape handler fights it. So Peek's `@`, `/` and `[`\n * menus each drew this box by hand, and the three had already drifted apart.\n *\n * `Menu` renders this, so there is still exactly one definition of the\n * surface \u2014 change it here and every menu in every app follows.\n *\n * Width, height and internal rhythm belong to the caller: a picker that lists\n * people is not the width of one that lists verbs.\n */\nexport interface MenuPanelProps extends Omit<ComponentPropsWithRef<'div'>, 'children'> {\n /** Optional only because Base UI fills it in when this is a `render`\n * target: `Menu.Popup` supplies the children. Every other caller passes\n * them. */\n children?: ReactNode\n}\n\nexport function MenuPanel({ children, className, ...props }: MenuPanelProps) {\n return (\n <div\n className={cn(\n 'flex flex-col rounded-lg border border-border-default bg-bg-elevated p-2 shadow-lg',\n /*\n * A divider in a menu runs the width of the rows it separates.\n *\n * `Divider` is inset 12px each side, which is right in a page and\n * wrong here: measured in a 180px menu, the rule started 21px from the\n * panel's edge where the rows start at 9px \u2014 12px narrower on each side\n * than the things it divides. `IdentityMenu` was already cancelling it\n * by hand with `mx-0`, which is the sign it belonged here (Katerina,\n * 2026-09-08). Direct children only, so a divider a caller puts inside\n * a row keeps its own spacing.\n */\n '[&>[role=separator]]:mx-0',\n className,\n )}\n {...props}\n >\n {children}\n </div>\n )\n}\n\nexport interface MenuProps {\n /**\n * The control that opens the menu. Any element that forwards its ref and\n * spreads its props \u2014 this package's `Button`, `IconButton` and\n * `PersonTrigger` all do.\n *\n * The menu owns it, and that is the point: Base UI can only do the toggle,\n * the placement, the focus return, the first-item highlight and the whole\n * hover choreography if it knows which element opened it.\n */\n trigger: ReactElement\n /** Which of the menu's edges hangs from the trigger's. Default left. */\n align?: 'left' | 'right'\n /**\n * Open on hover, and close shortly after the pointer leaves \u2014 for a control\n * that only appears while the pointer is on a card.\n *\n * Base UI owns the whole choreography, including the diagonal from a row\n * out to a submenu panel. The shell used to hand-write this and got it\n * wrong in two directions.\n */\n openOnHover?: boolean\n /** Controlled, for a caller that must know or must force it. Leave both\n * off and the menu keeps its own state. */\n open?: boolean\n onOpenChange?: (open: boolean) => void\n /** Base UI's imperative handle. `actions.current?.close()` shuts the menu \u2014\n * for a row that must close it before opening what it opens. A `MenuItem`\n * closes the menu by itself, so this is only for content that is not one. */\n actionsRef?: RefObject<{ close: () => void; unmount: () => void } | null>\n children: ReactNode\n /** On the menu's surface \u2014 its width. **Not its padding**: see\n * `contentClassName`. */\n className?: string\n /**\n * The padding around the rows, as a class. Default `p-2`, 8px.\n *\n * It is on the scrolling content, not on the panel, so the scrollbar hugs the\n * panel's edge (D63) \u2014 and so a padding class on `className` adds to it\n * rather than replacing it. Peek's Later menu asked for `p-1` there and got\n * 12px from `0.12.6` on (PLAN Finding 60). Set it here.\n */\n contentClassName?: string\n}\n\n/**\n * Read from `globalThis` rather than as a bare `process`: the declaration\n * build carries no ambient types on purpose, so that a consumer of this\n * package does not inherit Node's. Every bundler still replaces the value.\n */\nconst isProduction = () =>\n (globalThis as { process?: { env?: { NODE_ENV?: string } } }).process?.env?.NODE_ENV === 'production'\n\n/** The 4px between a menu and its trigger, and the 8px it keeps clear of\n * every screen edge \u2014 the two numbers `fit.ts` used. */\nconst GAP = 4\nconst VIEWPORT_PAD = 8\n/** A hover menu opens at once and closes 150ms after the pointer leaves \u2014\n * the two numbers the hand-written version used. */\nconst HOVER_OPEN_DELAY = 0\nconst HOVER_CLOSE_DELAY = 150\n\nexport function Menu({ trigger, align = 'left', openOnHover = false, open, onOpenChange, actionsRef, children, className, contentClassName }: MenuProps) {\n return (\n <BaseMenu.Root\n open={open}\n onOpenChange={onOpenChange ? (next) => onOpenChange(next) : undefined}\n actionsRef={actionsRef}\n /* Non-modal, as every menu here has always been: the page behind keeps\n its scrollbar, so opening a menu never shifts the layout. */\n modal={false}\n >\n <BaseMenu.Trigger\n render={trigger}\n /* The part writes its own disabled state over the button's, so a\n disabled trigger came out `aria-disabled=\"false\"` and opened\n (Finding 39, Ship's adoption, 2026-09-08). It is told what the\n button already knows. */\n disabled={triggerDisabled(trigger)}\n openOnHover={openOnHover}\n delay={HOVER_OPEN_DELAY}\n closeDelay={HOVER_CLOSE_DELAY}\n />\n <BaseMenu.Portal>\n <BaseMenu.Positioner\n side=\"bottom\"\n align={align === 'right' ? 'end' : 'start'}\n sideOffset={GAP}\n collisionPadding={VIEWPORT_PAD}\n className=\"z-50 data-[anchor-hidden]:hidden\"\n >\n <BaseMenu.Popup\n data-interactive\n /* `outline-none`: the popup is a programmatic focus target, not\n something a keyboard user tabs to. Without it Chrome rings the\n WHOLE panel when the menu is opened from the keyboard, which\n reads as \"the menu is one thing\" rather than \"these rows are the\n things\" \u2014 measured, `outline: auto 1px`. The rows keep their own\n highlight. Exactly the fix `DialogShell`'s card needed at stage\n 3, in a second place.\n\n `--available-height` is the room Floating UI found after flipping\n and clamping; the shell passes no cap of its own, so a menu stands\n as tall as it can and scrolls only when the screen truly has no\n room. Select's 288 was never this component's \u2014 the identity\n panel got it by accident once and grew a scrollbar at full\n height. */\n className={cn('min-w-[180px] outline-none p-0', className)}\n render={<MenuPanel />}\n >\n {/* The height cap sits on the box that scrolls \u2014 on the panel it\n let the box grow to its content and nothing scrolled (measured,\n 2026-09-08).\n *\n * And **the padding is on the content, not on the panel** (D63,\n * 2026-09-13). With it on the panel the scrolling box was inset by\n * it, so the bar floated 9px in from the panel's edge where every\n * other scrolling surface in the suite draws it at 1px \u2014\n * `DialogShell` had it right and said so in its own comment, and\n * this is the same arrangement. The rows do not move: the padding\n * that used to be the panel's is now the content's, at the same\n * 8px. The cap loses its `- 1rem` for the same reason \u2014 the\n * padding is inside the scrolling box now, so the panel is exactly\n * as tall as Floating UI allowed.\n *\n * A caller's `className` still lands on the panel, so a caller\n * asking for different padding needs `contentClassName`. That\n * sentence was written at 0.12.6 and the prop was not: a `p-1` on\n * `className` added 4px to these 8px instead of replacing them\n * (PLAN Finding 60). The prop exists now. */}\n <ScrollArea viewportClassName=\"max-h-[var(--available-height)]\" contentClassName={cn('flex flex-col p-2 [&>[role=separator]]:mx-0', contentClassName)}>\n <MenuContext.Provider value={{ openOnHover }}>{children}</MenuContext.Provider>\n </ScrollArea>\n </BaseMenu.Popup>\n </BaseMenu.Positioner>\n </BaseMenu.Portal>\n </BaseMenu.Root>\n )\n}\n\n/**\n * A row that opens another menu beside it \u2014 the submenu two Peek menus\n * hand-rolled before this, one of which dropped the ref its edge-flip\n * measured and shipped a submenu cut off by the screen (2026-09-03).\n *\n * Hover-timed as those were, and as ours was: it opens at once and closes\n * 150ms after the pointer leaves, so the diagonal from row to panel survives.\n * Those two numbers are all that is left of the old implementation \u2014 the\n * placement, the flip at a screen edge, and now the arrow keys (\u2192 opens it,\n * \u2190 closes it) are Base UI's.\n */\nexport interface MenuSubProps {\n /** The trigger row's label. */\n label: string\n leading?: ReactNode\n /** Mark the trigger row as holding a current value. */\n selected?: boolean\n /** The submenu's rows. */\n children: ReactNode\n /** On the submenu panel. */\n className?: string\n}\n\nexport function MenuSub({ label, leading, selected, children, className }: MenuSubProps) {\n /*\n * A submenu inside a hover-opened menu strands it.\n *\n * Measured 2026-09-08: enter the submenu's panel, then leave in any\n * direction that does not cross back over the parent, and neither the\n * submenu nor the menu ever closes again \u2014 at 200ms, 500ms, 1s and 2s.\n * Both triggers hard-code Floating UI's `safePolygon({ blockPointerEvents:\n * true })`, which blocks pointer events while the path from row to panel is\n * live; leaving that way never resolves the polygon. There is no prop.\n *\n * So the rule is: no `openOnHover` on a menu that has one of these. This\n * says so out loud rather than leaving it to the page, because the failure\n * is a menu that will not go away and the cause is two files apart.\n */\n const menu = useContext(MenuContext)\n if (!isProduction() && menu?.openOnHover) {\n console.error(\n '[@estiva-app/ui] Menu: `openOnHover` and `MenuSub` cannot be used together \u2014 ' +\n 'leaving the submenu panel strands the menu open. Open this menu on a press instead. ' +\n 'See Menu.mdx, \"Hover-opened menus\".',\n )\n }\n return (\n <BaseMenu.SubmenuRoot>\n <BaseMenu.SubmenuTrigger\n openOnHover\n delay={0}\n closeDelay={150}\n nativeButton\n render={<button type=\"button\" />}\n className={menuItemClassName({ size: 'default', selected })}\n >\n <MenuItemBody label={label} leading={leading} submenu />\n </BaseMenu.SubmenuTrigger>\n <BaseMenu.Portal>\n <BaseMenu.Positioner\n side=\"inline-end\"\n align=\"start\"\n sideOffset={GAP}\n collisionPadding={VIEWPORT_PAD}\n className=\"z-50 data-[anchor-hidden]:hidden\"\n >\n <BaseMenu.Popup className={cn('w-[160px]', className)} data-interactive render={<MenuPanel />}>\n {children}\n </BaseMenu.Popup>\n </BaseMenu.Positioner>\n </BaseMenu.Portal>\n </BaseMenu.SubmenuRoot>\n )\n}\n\nexport interface MenuItemProps extends Omit<ComponentPropsWithRef<'button'>, 'children'> {\n label?: string\n /** Replaces the label/description block \u2014 for rows whose middle is richer\n * than text (the launcher's form rows). Leading/trailing still apply. */\n children?: ReactNode\n /** `tall` is the picker row \u2014 content height with a 40px floor, px-3,\n * gap-3, room for a 32px face or icon tile and the description line.\n * `default` is the command row. */\n size?: 'default' | 'tall'\n /** A second line under the label \u2014 a role, an address \u2014 12px, secondary, truncating. */\n description?: string\n /** Before the label: a 16px icon (stroke 1.5, secondary), or an Avatar, for rows led by a face. */\n leading?: ReactNode\n /** At the right edge: a hint, a value \u2014 anything. Wins over `shortcut` and `submenu`. */\n trailing?: ReactNode\n /**\n * Shown at the right edge **only while this row is the one you are pointing\n * at or have arrowed onto** \u2014 an `EnterHint`, typically.\n *\n * Pass it unconditionally. Do not do `trailing={active ? <EnterHint/> : undefined}`:\n * that mounts the hint, and a mount is instant while the row's own fill is a\n * 150ms fade, so the hint lands ahead of the highlight on the way in and\n * vanishes ahead of it on the way out (measured 2026-09-05 \u2014 ~7 frames of a\n * chip sitting on an unhighlighted row, and two rows lit at once when\n * sweeping). This slot is always in the DOM and revealed by the *same*\n * `:hover` / `selected` the fill uses, on the same duration and curve, so\n * the two cannot come apart \u2014 and the row does not reflow when it appears.\n *\n * It cross-fades with `trailing`/`shortcut`/`submenu` rather than displacing\n * them, and reserves the wider of the two, so nothing moves either way.\n */\n hint?: ReactNode\n /** A keyboard hint, drawn as the kbd chip. */\n shortcut?: string\n /** The row opens another menu: draws the chevron at the right edge. */\n submenu?: boolean\n destructive?: boolean\n /** The row the menu currently points at (a submenu's chosen value). */\n selected?: boolean\n}\n\n/**\n * The row's class list, written once because `MenuSub`'s trigger row is the\n * same row.\n *\n * `data-highlighted` joins `:hover` here, and that is the whole visible\n * consequence of the port: the fill that followed the pointer now also\n * follows the arrow keys, because Base UI sets one attribute for both.\n */\n/** The row's look, shared inside the package: `MenuItem` draws it, and\n * `Select` puts it on Base UI's `Select.Item`, whose parts (`ItemText`,\n * `ItemIndicator`) have to stay the element's own. One row, two parts. Not\n * exported from the package's index. */\nexport function menuItemClassName({ size, selected, className }: { size: 'default' | 'tall'; selected?: boolean; className?: string }) {\n return cn(\n // shrink-0: a menu is a flex column that scrolls at its max height,\n // and a flex child shrinks before its container does \u2014 so every row\n // in an overflowing menu was squashed to its `min-h`, and a row given\n // an explicit height silently lost it (Peek's `[` menu: h-12 rows\n // measured 40px). The same fix NavItem took on 2026-09-02.\n // `group`: the `hint` slot reveals itself from this row's own :hover,\n // so the hint and the fill are one CSS state change, not two engines.\n //\n // No transition on the fill (Katerina, 2026-09-05). It faded over\n // 150ms, and anything appearing with it had to fade too or arrive\n // ahead of it \u2014 which, sweeping a pointer down a list, read as the\n // hint flickering in and out. Both are instant now: they still change\n // on exactly the same :hover, so they cannot come apart, and a row\n // lights and unlights crisply as the pointer crosses it.\n 'group flex w-full shrink-0 cursor-pointer items-center rounded-lg text-left outline-none hover:bg-bg-hover data-[highlighted]:bg-bg-hover',\n // Both sizes are as tall as their content and never shorter than a\n // floor: 36px for a default row (Katerina, 2026-09-11), 40px for a\n // tall one (Katerina, 2026-09-01). A single-line action row sits at\n // 36, a picker row at 40, a row with a 32px face and a role line\n // comes out at its natural 48. One rule, not a hand-picked height\n // per file \u2014 Peek's Later menu had been forcing `h-9` for exactly\n // this 36px and was the only menu in either app out of step.\n size === 'tall' ? 'min-h-10 gap-3 px-3 py-1.5' : 'min-h-9 gap-2 px-2 py-1.5',\n selected && 'bg-bg-hover',\n className,\n )\n}\n\n/** Everything inside the row \u2014 written once, for the same reason. Shared\n * inside the package like `menuItemClassName`: `CommandPalette` draws it\n * inside Base UI's `Autocomplete.Item`. Not exported from the index. */\nexport function MenuItemBody({ label, children, size = 'default', description, leading, trailing, hint, shortcut, submenu, destructive, selected }: Pick<MenuItemProps, 'label' | 'children' | 'size' | 'description' | 'leading' | 'trailing' | 'hint' | 'shortcut' | 'submenu' | 'destructive' | 'selected'>) {\n const edge =\n trailing ??\n (shortcut ? (\n <Kbd>{shortcut}</Kbd>\n ) : submenu ? (\n <IconChevronRight size={16} stroke={1.5} className=\"shrink-0 text-text-muted\" />\n ) : null)\n return (\n <>\n {leading && <span className=\"flex shrink-0 items-center\">{leading}</span>}\n {/* Sizes are arbitrary values (the body-2 and caption tokens): these\n lists merge with a colour, and tw-merge drops a token size beside a\n colour. Ship's copy said `text-sm`, which was never the ramp. */}\n {children ?? (\n <span className={cn('flex min-w-0 flex-1 flex-col', size === 'tall' && 'gap-[2px]')}>\n <span className={cn('truncate text-body-2', destructive ? 'text-error-default' : 'text-text-primary')}>\n {label}\n </span>\n {description && <span className=\"truncate text-caption text-text-secondary\">{description}</span>}\n </span>\n )}\n {(edge || hint) && (\n // One grid cell holding both, right-aligned: the slot is as wide as\n // the wider of the two and never changes, so revealing the hint moves\n // nothing. No transition here either \u2014 the hint switches on the same\n // :hover / `selected` as the fill, in the same frame.\n <span className=\"grid shrink-0 items-center justify-items-end [&>*]:col-start-1 [&>*]:row-start-1\">\n {edge && (\n <span className={cn('flex items-center', hint && 'group-hover:opacity-0 group-data-[highlighted]:opacity-0', hint && selected && 'opacity-0')}>\n {edge}\n </span>\n )}\n {hint && (\n <span className={cn('flex items-center opacity-0 group-hover:opacity-100 group-data-[highlighted]:opacity-100', selected && 'opacity-100')}>\n {hint}\n </span>\n )}\n </span>\n )}\n </>\n )\n}\n\nexport function MenuItem({ label, children, size = 'default', description, leading, trailing, hint, shortcut, submenu, destructive, selected, className, ...props }: MenuItemProps) {\n const body = (\n <MenuItemBody\n label={label}\n size={size}\n description={description}\n leading={leading}\n trailing={trailing}\n hint={hint}\n shortcut={shortcut}\n submenu={submenu}\n destructive={destructive}\n selected={selected}\n >\n {children}\n </MenuItemBody>\n )\n const rowClassName = menuItemClassName({ size, selected, className })\n\n /*\n * Inside a `Menu` the row is Base UI's `Menu.Item`: it joins the roving\n * focus, answers the typeahead, and closes the menu when it is chosen.\n * `nativeButton` keeps the `<button>`; the part would draw a `<div>`.\n *\n * Outside one \u2014 a bare `MenuPanel` in an editor popup \u2014 it is a plain\n * button, **with no `role=\"menuitem\"`**. It used to carry the role\n * unconditionally, which made an orphan: ARIA requires a `menuitem` to sit\n * inside a `menu` or a `menubar`, and `MenuPanel` is a `<div>` with no role\n * at all. Four Peek files draw rows that way, so four surfaces were telling\n * a screen reader they were menu items of nothing.\n *\n * A button is what these rows actually are. The pickers they sit in are a\n * listbox pattern rather than a menu \u2014 the editor's plugin owns the\n * highlight and the keyboard \u2014 and saying so properly is `ChipInput`'s\n * stage, not this one; claiming the wrong role in the meantime is worse\n * than claiming none.\n */\n if (!useContext(MenuContext)) {\n return (\n <button type=\"button\" className={rowClassName} {...props}>\n {body}\n </button>\n )\n }\n return (\n <BaseMenu.Item\n nativeButton\n render={<button type=\"button\" />}\n className={rowClassName}\n /* `Menu.Item` types its handlers for the `<div>` it would draw by\n default. `render` makes it the `<button>` this row has always been,\n and `nativeButton` tells Base UI so \u2014 but the prop types do not\n follow the render target, so a caller's `onClick` is a button\n handler facing a div signature. The element underneath is a\n `<button>`, which `Menu.test.tsx` pins. */\n {...(props as ComponentPropsWithRef<'div'>)}\n >\n {body}\n </BaseMenu.Item>\n )\n}\n\n/**\n * The keyboard hint a picker row shows while highlighted \u2014 hand-rolled in\n * five files before this (2026-09-01), and drawn as its own thing until\n * 2026-09-05, when it became the `Kbd` chip every other key hint uses. A\n * picker row and a menu row sit in the same menu; they named the same key\n * two ways.\n *\n * `target` is what pressing it gives you, when that is worth saying \u2014 the\n * topic picker's \"\u21A9 Enter #topic\". It sits after the chip, in the picker's\n * own 9px voice, because it is not a key.\n *\n * Katerina, 2026-09-05, told the measurement and asked again: the chip keeps\n * the `\u21A9` character. It is not in Geist Mono \u2014 the browser borrows it, so it\n * advances 8.63px where the font's own characters advance 6 \u2014 and that is a\n * knowing trade for the glyph, not an oversight. If the width ever has to go,\n * Tabler's IconCornerDownLeft draws the same shape.\n */\nexport function EnterHint({ target }: { target?: string }) {\n return (\n <span className=\"flex shrink-0 items-center gap-1.5 text-text-muted\">\n <Kbd>\u21A9 Enter</Kbd>\n {target && (\n <span className=\"text-menu signal:font-mono signal:text-small signal:tracking-wider\">{target}</span>\n )}\n </span>\n )\n}\n\n/**\n * A section heading inside a menu: the 32px row with a SectionLabel, read\n * secondary \u2014 a heading inside a menu labels the rows, it is not one of\n * them (Katerina, 2026-09-01).\n *\n * Inside a `Menu` it is Base UI's `Menu.Group` with the heading as its\n * `GroupLabel`, so the rows under it are announced as a named group rather\n * than as a run of items with a stray line above them. Outside one \u2014 the\n * editor pickers, which draw the surface but not the menu \u2014 it is the two\n * plain elements it has always been.\n */\nexport function MenuSection({ label, children, className }: { label: string; children: ReactNode; /** On the heading row \u2014 a surface whose rows are px-3 aligns its heading with px-3. */ className?: string }) {\n const inMenu = useContext(MenuContext) !== null\n const heading = (\n <div className={cn('flex h-8 items-center px-2', className)}>\n <SectionLabel className=\"text-text-secondary\">{label}</SectionLabel>\n </div>\n )\n if (!inMenu) {\n return (\n <div className=\"flex flex-col\">\n {heading}\n {children}\n </div>\n )\n }\n return (\n <BaseMenu.Group className=\"flex flex-col\">\n <BaseMenu.GroupLabel render={heading} />\n {children}\n </BaseMenu.Group>\n )\n}\n\n/** A non-interactive row inside a menu, at the item's own geometry. */\nexport function MenuRow({ children, className }: { children: ReactNode; className?: string }) {\n return <div className={cn('flex items-center gap-2 px-2 py-1.5', className)}>{children}</div>\n}\n", "import type { ReactElement } from 'react'\n\n/**\n * Whether the element a `Menu` or `Popover` was handed as its trigger is\n * disabled \u2014 by `disabled`, or by a `disabledReason` (which disables the\n * button and keeps it reachable). Base UI's trigger parts keep a disabled\n * state of their own and write it over the rendered button's, so they have to\n * be told (Finding 39, 2026-09-08).\n */\nexport function triggerDisabled(trigger: ReactElement): boolean {\n const props = trigger.props as { disabled?: boolean; disabledReason?: string }\n return !!(props.disabled || props.disabledReason)\n}\n", "import type { ReactNode } from 'react'\nimport { cn } from './cn'\n\n/**\n * THE section-title label \u2014 every section heading (sidebar sections, menu\n * headings, panel titles) renders through this span, so a style change is\n * one edit. Peek's original, verbatim: 12px / 12px / 500, primary text\n * (Peek's ruling 2026-07-23: labels stay primary), the mono uppercase\n * micro-label under Signal. Metrics as arbitrary values for the tw-merge\n * reason the README records.\n *\n * Deliberately NOT the same thing as Property's stacked field label (the\n * 9px `menu`-token one): they were merged for a day and unmerged by\n * Katerina's ruling (2026-09-01) \u2014 a section title and a field label are\n * different voices, at different sizes, on purpose.\n */\nexport function SectionLabel({ children, className }: { children: ReactNode; className?: string }) {\n return (\n <span\n className={cn(\n 'text-h5 leading-3 text-text-primary signal:font-mono signal:text-small signal:uppercase signal:tracking-widest',\n className,\n )}\n >\n {children}\n </span>\n )\n}\n", "import type { ReactNode } from 'react'\nimport { IconMessage2 } from '@tabler/icons-react'\nimport { cn } from './cn'\n\n/**\n * The place where content will be once there is some, in two manners\n * (Katerina, 2026-09-09):\n *\n * - `page` \u2014 the whole page is empty: a 16px icon over a centred line of\n * secondary text, **in the middle of its box both ways** (Katerina,\n * 2026-09-09: it sat near the top). It grows to the room a flex column\n * gives it (`flex-1`) and centres inside that; a caller that draws it\n * straight into a page hands it the height (`className=\"h-full\"`).\n * Peek's EmptyState (2026-08-28) otherwise; the default.\n * - `section` \u2014 one section of a page is empty, and the page has other\n * things on it: the line alone, left-aligned, no icon. A section's\n * emptiness is a line among the page's content, not a stage of its own.\n * It is the quiet line \u2014 caption size in the muted colour, as a Field's\n * helper line is \u2014 so beside rows it reads as \"nothing here\", not as one\n * more row (Katerina, 2026-09-15: \"not that intense\", \"smaller font\").\n *\n * The message is the caller's \u2014 a shared component has no words of its own\n * for what is missing.\n */\nexport interface EmptyStateProps {\n /** 16px, stroke 1.5. A speech bubble when absent. Drawn for a `page` only. */\n icon?: ReactNode\n message: string\n /** `page` when the whole page is empty; `section` when one part of it is. */\n scope?: 'page' | 'section'\n className?: string\n}\n\nexport function EmptyState({ icon, message, scope = 'page', className }: EmptyStateProps) {\n if (scope === 'section') {\n return <p className={cn('text-caption text-text-muted', className)}>{message}</p>\n }\n return (\n <div className={cn('flex flex-1 flex-col items-center justify-center gap-2', className)}>\n <span className=\"text-text-secondary\">{icon ?? <IconMessage2 size={16} stroke={1.5} />}</span>\n <p className=\"text-body-2 text-text-secondary text-center\">{message}</p>\n </div>\n )\n}\n", "import { cloneElement, isValidElement, type ReactElement, type ReactNode } from 'react'\nimport { Field as BaseField } from '@base-ui/react/field'\nimport { cn } from './cn'\n\n/**\n * Peek's Field (2026-08-28): a label over a control, 8px apart, with a red\n * asterisk when required. The label is the `input-label` type token, merged\n * with `cn()` like every other class list here. On Base UI's `Field` since\n * stage 3 of the migration (2026-09-07).\n *\n * The label names its control. It did not once, and the control was a\n * *sibling* of the label with no `htmlFor`, so there was neither an explicit\n * nor an implicit association: a screen reader announced an unlabelled edit\n * box and clicking the label focused nothing (SHA-17). That fix used to be\n * ours \u2014 a context carrying a generated id, which every control had to opt\n * into by calling `useFieldControlId`. **Base UI does it now**, for its own\n * `Input`, `Checkbox`, `Select` and anything rendered through `Field.Control`,\n * so a new control is labelled by construction rather than by remembering.\n *\n * `helper` and `error` are the line under the control, which Ship built by\n * hand in two dialogs (`COMPONENTS-SHIP.md` F14) and Peek in four\n * (`COMPONENTS-PEEK.md` F14), always the same two class lists. **An error\n * replaces the helper rather than joining it** \u2014 that is what those callers\n * did (`pairError ?? 'Leave empty and\u2026'`), and a field that says both at once\n * is asking the reader to work out which one is live.\n *\n * The line is announced: Base UI wires `aria-describedby` for the helper and\n * `aria-invalid` + the error's id for the error, which the hand-built spans\n * never did.\n *\n * `required` is announced too, since 2026-09-08. It drew the asterisk and\n * nothing else \u2014 measured, the control carried neither `required` nor\n * `aria-required`, so the one thing the mark means never reached anybody who\n * could not see it. Base UI's `Field` has no `required` of its own, so this\n * puts `aria-required` on the control itself.\n */\nexport interface FieldProps {\n label: string\n /**\n * Draws the asterisk **and** marks the control `aria-required`, so the mark\n * means something to a reader who cannot see it. It reaches a single control\n * element; a `children` of several elements keeps the asterisk and owes its\n * own `aria-required`.\n */\n required?: boolean\n /**\n * A hint under the control \u2014 what the format is, what happens if it is left\n * empty. `caption`, muted. Hidden while `error` is set.\n */\n helper?: string\n /**\n * What is wrong, in the same place as the helper and in the error colour.\n * Setting it also marks the control invalid, so the caller no longer passes\n * `aria-invalid` itself.\n */\n error?: string\n children: ReactNode\n}\n\n/**\n * The two looks the line under a control wears, in one place so `Field` and\n * `FieldLine` cannot drift apart. `warning` is `FieldLine`'s alone \u2014 see there.\n */\nconst LINE_STYLES = {\n helper: 'text-caption text-text-muted',\n warning: 'text-caption text-warning-default',\n error: 'text-caption text-error-default',\n} as const\n\nexport function Field({ label, required = false, helper, error, children }: FieldProps) {\n\n /*\n The asterisk is a picture of `required`; this is the word for it. Base UI's\n `Field` has no `required` to propagate, so the control is marked here \u2014\n `aria-required` rather than the native attribute, because the native one\n also switches on the browser's own validation bubble, which no field in\n either app uses. A control that already says so keeps what it says.\n */\n const control =\n required && isValidElement(children)\n ? cloneElement(children as ReactElement<{ 'aria-required'?: boolean | 'true' | 'false' }>, {\n 'aria-required': (children as ReactElement<{ 'aria-required'?: boolean | 'true' | 'false' }>).props['aria-required'] ?? true,\n })\n : children\n return (\n <BaseField.Root invalid={!!error} className=\"flex flex-col gap-2\">\n {/* `cn`, like everywhere else. It was a template literal, with a comment\n saying the type token must never be merged \u2014 which stopped being true\n when `cn()` was taught the ramp: `input-label` is in it, and\n `cn.test.ts` pins that. */}\n <BaseField.Label className={cn('text-input-label text-text-primary', required && 'flex items-center')}>\n {label}\n {/* A picture, not a word: `aria-required` on the control says it, and\n without this the control was named \"Title*\" (Finding 38). */}\n {required && (\n <span aria-hidden=\"true\" className=\"text-error-default ml-0.5\">\n *\n </span>\n )}\n </BaseField.Label>\n {/*\n The control sits in the same place whether or not there is a line\n under it. It used to be wrapped only when there was one, and React\n then re-created the control the moment an error appeared or cleared \u2014\n so a person typing into a field whose error clears on input lost\n focus after the first keystroke (found by Ship's adoption, 2026-09-08,\n Finding 38). A one-child flex column draws exactly as the bare control\n did; the 6px stack Ship and Peek both wrote by hand appears only with a\n line.\n */}\n <div className=\"flex flex-col gap-1.5\">\n {control}\n {error != null ? (\n <BaseField.Error match className={LINE_STYLES.error}>\n {error}\n </BaseField.Error>\n ) : helper != null ? (\n <BaseField.Description className={LINE_STYLES.helper}>{helper}</BaseField.Description>\n ) : null}\n </div>\n </BaseField.Root>\n )\n}\n\n/**\n * The line on its own \u2014 the same small line `Field` draws under a control,\n * for the places where there is no single control to draw it under.\n *\n * **Why it is not a `Field`.** Three surfaces in Peek put this line under a\n * *group*: the rename row (a field and two buttons), a Folder's action\n * controls, another app's action controls. Each already has a section heading\n * above it, and `Field` comes with a label of its own \u2014 so wrapping the group\n * in one would either say the heading twice or restyle it. Found at step 4 of\n * Peek's adoption (`ADOPTION.md` B24); until this existed, all three spelled\n * `text-xs text-error-default` by hand.\n *\n * **It announces itself**, which is the half the hand-written spans never had.\n * These lines appear *after* something was done \u2014 a rename that the relay\n * refused, an action that failed \u2014 so a reader who cannot see them is told:\n * `role=\"alert\"` for an error, `role=\"status\"` for the rest. That is `Banner`'s\n * rule (2026-09-02), kept here so the two agree.\n *\n * **`warning` is the tone `Field` has not got**, and deliberately: `Field`'s\n * `error` also marks its control invalid, and a warning is not invalid \u2014 the\n * rename went through, and something about it needs saying. A group has no\n * single control to mark, so the distinction is free here and would not be\n * there.\n *\n * When the line belongs to one control, it is `Field`'s `helper` or `error`:\n * those are wired to the control with `aria-describedby` and `aria-invalid`,\n * which this cannot be.\n */\nexport type FieldLineTone = 'helper' | 'warning' | 'error'\n\nexport interface FieldLineProps {\n /** `helper` (muted) is a hint; `warning` (amber) and `error` (red) are outcomes. Default `helper`. */\n tone?: FieldLineTone\n children: ReactNode\n className?: string\n}\n\nexport function FieldLine({ tone = 'helper', children, className }: FieldLineProps) {\n return (\n <p role={tone === 'error' ? 'alert' : 'status'} className={cn(LINE_STYLES[tone], className)}>\n {children}\n </p>\n )\n}\n", "import { useLayoutEffect, useRef, type FormHTMLAttributes, type KeyboardEvent, type ReactNode } from 'react'\nimport { Fieldset } from '@base-ui/react/fieldset'\nimport { Form as BaseForm } from '@base-ui/react/form'\nimport { cn } from './cn'\nimport { FormBusyContext, useFormBusy } from './formBusy'\n\n/**\n * A form: Enter in a field, or a submit button, sends it, and the page never\n * reloads. On Base UI's `Form` (UIG-7, 16 September), which the apps' five\n * forms had each written around a plain `<form>`: `preventDefault`, a busy\n * flag, and `disabled={busy}` on every field one by one.\n *\n * Base UI's part, as it renders it: a `<form noValidate>` \u2014 the browser's own\n * validation bubbles are off, which no field in either app used \u2014 that, on\n * submit, checks every `Field` inside and moves focus to the first one that is\n * invalid instead of sending. `Field`'s `error` is still how an error shows.\n *\n * `busy` is ours. While it is on, the children sit in a disabled\n * `<fieldset>` (Base UI's `Fieldset`, with no box of its own), so every field\n * and button inside is switched off at once \u2014 the package's Button,\n * IconButton and Checkbox wearing their own switched-off look, which a\n * fieldset alone does not give them (`formBusy.ts`) \u2014 and focus waits on the form\n * rather than falling to the page. Chrome drops focus to `<body>` the moment\n * the focused field is disabled, before any effect runs, and jsdom does not \u2014\n * so the form remembers the last element inside it that had focus, and takes\n * focus back from the page on its behalf (measured in Chrome, 16 September).\n * When `busy` ends, focus goes to the first invalid field, else to what sent\n * the form, else to the first control. That is `CommandPalette`'s order\n * too, so the two read the same (UIG-29).\n *\n * The keys are ours, the same in every form (Katerina, 16 September): Enter in\n * a one-line field sends; Enter in a text area is a new line; Enter in a list\n * or a people picker picks; Ctrl+Enter (Cmd+Enter) sends from anywhere inside.\n * The form sends on Enter itself rather than leaving it to the browser, whose\n * implicit submission depends on whether the form has a submit button and how\n * many fields it holds. `enterSends={false}` keeps Enter in a one-line field\n * from sending, for a form where only Ctrl+Enter may send (`CommandPalette`).\n * Every way of sending goes through the form's submit, so Base UI's field\n * check runs for each.\n */\nexport interface FormProps extends Omit<FormHTMLAttributes<HTMLFormElement>, 'onSubmit' | 'children' | 'noValidate'> {\n /** Enter in a field, or a submit button. The page's own submit is already prevented. */\n onSubmit: () => void | Promise<void>\n /** While sending: every field and button inside is switched off, and focus waits on the form. */\n busy?: boolean\n /**\n * Whether Enter in a one-line field sends. On by default. Off where only\n * Ctrl+Enter may send \u2014 a form inside `CommandPalette`. Ctrl+Enter sends either way.\n */\n enterSends?: boolean\n children: ReactNode\n}\n\nconst CONTROL = 'input:not([type=\"hidden\"]), textarea, select, button, [role=\"checkbox\"], [role=\"combobox\"], [tabindex]:not([tabindex=\"-1\"])'\n\n/** The inputs a person types one line into; a picker's input (`role=\"combobox\"`) is not one. */\nconst ONE_LINE = new Set(['', 'text', 'search', 'email', 'url', 'tel', 'password', 'number'])\n\nfunction usable(element: Element | null): element is HTMLElement {\n return element instanceof HTMLElement && element.isConnected && element.matches(CONTROL) && !element.matches(':disabled') && element.getAttribute('aria-disabled') !== 'true'\n}\n\n/** A marked field's own control: Base UI marks the control and the `Field` around it. */\nfunction firstInvalid(form: HTMLElement): HTMLElement | undefined {\n for (const marked of form.querySelectorAll('[data-invalid]')) {\n if (usable(marked)) return marked\n const inside = Array.from(marked.querySelectorAll(CONTROL)).find(usable)\n if (inside) return inside\n }\n return undefined\n}\n\nexport function Form({ onSubmit, busy: ownBusy = false, enterSends = true, className, children, ...props }: FormProps) {\n // A form inside a busy form is busy too.\n const outerBusy = useFormBusy()\n const busy = ownBusy || outerBusy\n const form = useRef<HTMLFormElement>(null)\n // Read at submit time: a form that is sending does not send again (Ctrl+Enter reaches it while busy).\n const busyNow = useRef(busy)\n busyNow.current = busy\n // What had focus when the form went busy, and whether the form took focus from it.\n const sender = useRef<Element | null>(null)\n const holding = useRef(false)\n // The last element inside the form that had focus, until focus moves to something outside it.\n const lastInside = useRef<Element | null>(null)\n\n useLayoutEffect(() => {\n const element = form.current\n if (!element) return\n if (busy) {\n const active = document.activeElement\n // Focus is still inside (jsdom, or a field that stayed enabled), or the\n // browser has already dropped it to the page from a field inside (Chrome).\n const from =\n active && active !== element && element.contains(active)\n ? active\n : !active || active === document.body\n ? lastInside.current\n : null\n if (from?.isConnected) {\n sender.current = from\n holding.current = true\n element.focus()\n }\n return\n }\n if (!holding.current) return\n holding.current = false\n // Someone who moved focus on while waiting keeps it where they put it.\n if (document.activeElement !== element && document.activeElement !== document.body) return\n const target = firstInvalid(element) ?? (usable(sender.current) ? sender.current : Array.from(element.querySelectorAll(CONTROL)).find(usable))\n sender.current = null\n target?.focus()\n }, [busy])\n\n /*\n Bubble phase, after the field's own handler: a picker picks on Enter and\n says so by preventing it, and a field that handles Enter itself does too.\n */\n const onKeyDown = (event: KeyboardEvent<HTMLFormElement>) => {\n props.onKeyDown?.(event)\n if (event.key !== 'Enter' || event.defaultPrevented || event.nativeEvent.isComposing) return\n const target = event.target\n const inInput = target instanceof HTMLInputElement\n /*\n In an input the browser sends a form on its own \u2014 on Enter with Shift or\n Alt too (measured in Chrome: Shift+Enter sent Peek's comment box, 16\n September) \u2014 so the form stops that every time and sends only by these\n rules. A text area's Enter is a new line and a button's Enter presses it:\n those are left to the browser.\n */\n if (inInput) event.preventDefault()\n if (event.altKey || event.shiftKey) return\n if (event.ctrlKey || event.metaKey) {\n event.preventDefault()\n form.current?.requestSubmit()\n return\n }\n if (!inInput) return\n const oneLine = ONE_LINE.has((target.getAttribute('type') ?? '').toLowerCase()) && target.getAttribute('role') !== 'combobox'\n if (enterSends && oneLine) form.current?.requestSubmit()\n }\n\n return (\n <BaseForm\n ref={form}\n {...props}\n // Focus waits here only while busy; the rest of the time a click between two fields must not land on the form.\n tabIndex={busy ? -1 : props.tabIndex}\n // A form that holds focus for a moment must not draw a focus ring.\n className={cn('outline-none', className)}\n onFocus={(event) => {\n if (event.target !== form.current) lastInside.current = event.target\n props.onFocus?.(event)\n }}\n onBlur={(event) => {\n const next = event.relatedTarget\n if (next && !form.current?.contains(next)) lastInside.current = null\n props.onBlur?.(event)\n }}\n onKeyDown={onKeyDown}\n onSubmit={(event) => {\n event.preventDefault()\n if (busyNow.current) return\n void onSubmit()\n }}\n >\n <Fieldset.Root disabled={busy} className=\"contents\">\n <FormBusyContext.Provider value={busy}>{children}</FormBusyContext.Provider>\n </Fieldset.Root>\n </BaseForm>\n )\n}\n", "import type { CSSProperties } from 'react'\nimport { cn } from './cn'\n\n/**\n * Peek's Skeleton (2026-08-28), the generic parts only: the bar, a row\n * shaped like a list row, and a list of them. Peek's placeholders shaped\n * like a conversation card or a huddle card know what those are and stay\n * in Peek, built from the bar.\n *\n * A list reveals after 150ms (`animate-skeleton-in`, in the preset) so a\n * fast load never flashes a skeleton \u2014 Peek's rule.\n */\nexport function SkeletonBar({ className, style }: { className?: string; style?: CSSProperties }) {\n return <div className={cn('bg-bg-inset rounded-sm animate-pulse', className)} style={style} />\n}\n\nconst ROW_BAR_WIDTHS = [150, 100, 170, 120, 90, 140, 110, 160]\n\n/** A 32px row: a 16px square and a bar, like a row with a face and a name. */\nexport function SkeletonRow({ barWidth = 130 }: { barWidth?: number }) {\n return (\n <div className=\"flex items-center gap-2 px-2 h-[32px] rounded-lg\">\n <SkeletonBar className=\"w-4 h-4 shrink-0\" />\n <SkeletonBar className=\"h-3.5\" style={{ width: barWidth }} />\n </div>\n )\n}\n\n/** Rows of varied widths, so the placeholder does not read as a pattern. */\nexport function SkeletonList({ rows = 8, className }: { rows?: number; className?: string }) {\n return (\n <div className={cn('flex flex-col gap-0.5 animate-skeleton-in', className)} aria-hidden>\n {Array.from({ length: rows }, (_, i) => (\n <SkeletonRow key={i} barWidth={ROW_BAR_WIDTHS[i % ROW_BAR_WIDTHS.length]} />\n ))}\n </div>\n )\n}\n", "import type { ComponentPropsWithRef, ReactNode } from 'react'\nimport { cn } from './cn'\n\n/**\n * A word in a sentence that stands for something: a person, a place, a thing\n * written into running text. One line high, level with the words around it.\n *\n * The shape is Peek's inline chip (D67, Peek PR #206, 2026-09-13), class for\n * class, moved into the package so both apps draw one (UIG-27). Three things\n * changed on the way, all ruled by Katerina:\n *\n * - the person tone is `person`, not `mention` \u2014 every chip here is a\n * mention of something;\n * - the alignment is a class (`align-top h-[1.4em]`), not a `style` object,\n * so it is part of the class list like every other size in the package;\n * - a chip that leads somewhere takes `href` and `onClick`, the way `NavItem`\n * does: the package draws an anchor, the app's router takes the click.\n *\n * **Why `align-top` and `1.4em`.** `1.4em` is the body text's line height,\n * so the chip is exactly one line tall; `vertical-align: top` pins it to the\n * line box, which keeps the line at 19.6px and the chip's words level with\n * the sentence's, with or without an icon. Measured in Peek on 2026-09-13:\n * `text-bottom` made the line 21.2px and lifted the chip's words 1.6px;\n * `baseline` lifted them 3.8px once an icon came first.\n */\n\n/** The shape every tone shares. Exported for a rich-text editor that renders chips from strings. */\nexport const INLINE_CHIP_CLASSES =\n 'inline-flex h-[1.4em] items-center gap-1 rounded-sm px-1 mx-0.5 align-top text-body-2 font-normal select-none'\n\n/** What a chip is made of, by what it stands for. */\nexport const INLINE_CHIP_TONE_CLASSES = {\n /** A thing or a place \u2014 anything that is not a person. */\n neutral: 'bg-bg-active text-text-primary',\n /** A person. */\n person: 'bg-accent-muted text-accent-primary',\n /** A person, called urgently. */\n urgent: 'bg-warning-muted text-warning-default',\n /**\n * A reference nobody could resolve \u2014 still a mention, just an anonymous one.\n * Its height is the body line's 19.6px written out, because `1.4em` here is\n * the caption's em: 16.8px, with the letters 1.8px above the sentence's\n * baseline. At 19.6px they sit 0.4px off it (measured 2026-09-14; Peek's\n * copy of this chip still has the 16.8px box).\n */\n quiet: 'h-[19.6px] bg-bg-active text-text-muted font-mono text-caption',\n} as const\n\nexport type InlineChipTone = keyof typeof INLINE_CHIP_TONE_CLASSES\n\n/** The chip's classes for a tone, with anything the caller adds. For renderers that cannot use the component. */\nexport function inlineChipClassName(tone: InlineChipTone, className?: string) {\n return cn(INLINE_CHIP_CLASSES, INLINE_CHIP_TONE_CLASSES[tone], className)\n}\n\nexport interface InlineChipProps extends Omit<ComponentPropsWithRef<'a'>, 'children'> {\n /** What the chip stands for. Default `neutral`. */\n tone?: InlineChipTone\n /** Drawn before the label in a 16px box: a 16px icon, or a 14px one it centres. */\n icon?: ReactNode\n /**\n * Where the chip leads. With it the chip is an anchor; without it, a span.\n * A router app passes `onClick` too, and navigates there itself.\n */\n href?: string\n children: ReactNode\n}\n\nexport function InlineChip({ tone = 'neutral', icon, href, className, children, ...props }: InlineChipProps) {\n const body = (\n <>\n {icon && <span className=\"flex size-4 shrink-0 items-center justify-center text-text-secondary\">{icon}</span>}\n {children}\n </>\n )\n const classes = inlineChipClassName(tone, className)\n // Without somewhere to go it is a label, and a label is not a link.\n if (href === undefined) {\n return (\n <span className={classes} {...(props as ComponentPropsWithRef<'span'>)}>\n {body}\n </span>\n )\n }\n return (\n <a href={href} className={classes} {...props}>\n {body}\n </a>\n )\n}\n", "import { useCallback, useRef, type ReactNode } from 'react'\nimport { cn } from './cn'\nimport { Divider } from './Divider'\nimport { Menu, MenuItem, MenuPanel, MenuRow, MenuSection } from './Menu'\nimport { Person } from './Person'\nimport { PersonTrigger } from './PersonTrigger'\n\n/**\n * Who you are, and where you are \u2014 the top bar's identity trigger and the\n * menu it opens. Ship's IdentityMenu (2026-09-01), moved in whole: every app\n * needs one, and this one is already made of nothing but shared parts.\n *\n * The trigger is a face and a name, never a key (Ship's ruling A4) \u2014 or, with\n * `compact`, the face alone, as Peek's top bar draws it. Anonymous is a\n * silhouette and the word \"Anonymous\": the truth of the situation rather than\n * eight hex characters posing as a name.\n *\n * The menu keeps the two honest sentences, and the workspace line names the\n * relay because on this substrate the relay *is* the workspace. Every section\n * hides when the app cannot fill it: no `relayUrl`, no workspace section; no\n * `onCopyKey`, no copy item \u2014 only offer actions that can succeed. The one\n * place a key appears is as the *result* of \"Copy public key\" \u2014 this\n * component never receives the key, so it cannot show one; the caller\n * copies. Items are text only, no icons (her ruling).\n */\nexport interface Identity {\n /** From `kind:0`. Absent means anonymous. */\n name?: string\n picture?: string\n /** Known to Estiva ID, never published to the relay. */\n email?: string\n}\n\nexport interface IdentityMenuProps {\n me: Identity\n /** Signed in through Estiva ID, as opposed to a browser-held key. */\n signedIn: boolean\n /** Names the workspace; absent hides the workspace section. */\n relayUrl?: string\n /** Estiva ID's base URL when this build offers sign-in; absent = anonymous-only build. */\n idBase?: string\n /** \"Copy public key\" appears only when the app can supply one. */\n onCopyKey?: () => void\n onSignOut?: () => void\n /** Face-only trigger, 36px \u2014 no chevron, no name beside it. */\n compact?: boolean\n className?: string\n /**\n * App-specific rows, drawn as their own group between the workspace\n * section and the actions. A function receives `close`, so a row can\n * shut the menu before opening what it opens.\n */\n children?: ReactNode | ((close: () => void) => ReactNode)\n}\n\n/**\n * The menu alone, as a plain surface \u2014 what the docs canvases draw, and what\n * a surface that wants the rows without the menu can use.\n */\nexport interface IdentityPanelProps extends Omit<IdentityMenuProps, 'compact'> {\n /** Called when a row asks the menu to close. */\n onClose?: () => void\n}\n\nexport function IdentityPanel({ className, onClose = () => {}, ...rest }: IdentityPanelProps) {\n return (\n <MenuPanel className={cn('w-72', className)}>\n <IdentityRows {...rest} onClose={onClose} />\n </MenuPanel>\n )\n}\n\n/** The old name for the same thing. */\nexport const IdentityPanelSurface = IdentityPanel\n\n/**\n * The panel's contents, without the menu around them.\n *\n * Split out at stage 4 for the same reason `MenuPanel` was split out of\n * `Menu`: a real menu portals and places itself against a trigger, so it\n * cannot stand in a docs page, and the canvases must still show the artifact\n * (Katerina, D25). The stories draw these rows on a `MenuPanel`; the app gets\n * them inside a `Menu`. One definition either way.\n *\n * Not exported from the package \u2014 `IdentityMenu` and `IdentityPanel` are the\n * API; this is how they are built.\n */\nexport function IdentityRows({ me, signedIn, relayUrl, idBase, onCopyKey, onSignOut, onClose, children }: Omit<IdentityPanelProps, 'className'> & { onClose: () => void }) {\n const act = (action: () => void) => () => {\n onClose()\n action()\n }\n const appRows = typeof children === 'function' ? children(onClose) : children\n return (\n <>\n <MenuSection label={signedIn ? 'Signed in as' : 'Acting as'}>\n <MenuRow>\n <Person name={me.name} picture={me.picture} fallback=\"Anonymous\" size={28} className=\"text-body-2-strong\" />\n </MenuRow>\n <Note>\n {signedIn\n ? 'Your Estiva ID. The same person you are in every Estiva app.'\n : 'A key held by this browser only. Nobody else knows who you are.'}\n </Note>\n {me.email && <Note>{me.email} \u00B7 known to Estiva, never published to the relay</Note>}\n </MenuSection>\n\n {relayUrl && (\n <>\n <Divider className=\"mx-0 my-2\" />\n <MenuSection label=\"Workspace\">\n <MenuRow>\n <span className=\"break-all font-mono text-caption text-text-primary\">{relayUrl}</span>\n </MenuRow>\n <Note>Everyone on this relay shares this workspace, in every app.</Note>\n </MenuSection>\n </>\n )}\n\n {appRows && (\n <>\n <Divider className=\"mx-0 my-2\" />\n {appRows}\n </>\n )}\n\n {(signedIn && idBase) || onCopyKey || (idBase && onSignOut) ? <Divider className=\"mx-0 my-2\" /> : null}\n\n {signedIn && idBase && (\n <MenuItem\n label=\"Edit your profile in Estiva ID\"\n onClick={act(() => window.open(idBase, '_blank', 'noopener,noreferrer'))}\n />\n )}\n {onCopyKey && <MenuItem label=\"Copy public key\" onClick={act(onCopyKey)} />}\n {idBase && onSignOut && <MenuItem label=\"Sign out\" onClick={act(onSignOut)} />}\n </>\n )\n}\n\nexport function IdentityMenu({ me, signedIn, relayUrl, idBase, onCopyKey, onSignOut, compact = false, className, children }: IdentityMenuProps) {\n /*\n * The menu owns the trigger (stage 4, 2026-09-08). It used to be the other\n * way round \u2014 this component held `open`, and the panel hung from a ref to\n * the wrapper \u2014 and four separate defects came from Base UI not knowing\n * which element opened it: the face stopped closing its own menu, opening\n * from the keyboard highlighted nothing, a hovered submenu row unmounted\n * the panel, and the panel hung from a wrapper the app's layout could\n * stretch. `PLAN.md` \u00A76.2 has the measurements.\n */\n const actions = useRef<{ close: () => void; unmount: () => void } | null>(null)\n const close = useCallback(() => actions.current?.close(), [])\n return (\n /* The wrapper carries the caller's `className` and nothing else. It used\n to be `relative`, because the panel was positioned against it; the panel\n hangs from the trigger and portals now, so a positioning context here\n would only be a lie about what this box does. */\n <div className={className}>\n <Menu\n align=\"right\"\n actionsRef={actions}\n className=\"w-72\"\n trigger={\n <PersonTrigger\n name={me.name}\n picture={me.picture}\n fallback=\"Anonymous\"\n compact={compact}\n size={compact ? 36 : undefined}\n /* The row shape is named by its own text \u2014 the person. Only the\n bare face needs a label; naming the row would override the\n person's name as the accessible name (Ship's tests find the\n trigger by it). */\n aria-label={compact ? 'Account menu' : undefined}\n />\n }\n >\n <IdentityRows\n me={me}\n signedIn={signedIn}\n relayUrl={relayUrl}\n idBase={idBase}\n onCopyKey={onCopyKey}\n onSignOut={onSignOut}\n onClose={close}\n >\n {children}\n </IdentityRows>\n </Menu>\n </div>\n )\n}\n\nfunction Note({ children }: { children: ReactNode }) {\n return <span className=\"px-2 pb-1.5 text-caption text-text-secondary\">{children}</span>\n}\n", "import { Separator } from '@base-ui/react/separator'\nimport { cn } from './cn'\n\n/**\n * Peek's Divider (2026-08-28): a hairline in `border-subtle`, inset 12px each\n * side. Plus what Ship added: `orientation=\"vertical\"` \u2014 the same hairline\n * standing up, stretching to its row's height, no inset.\n *\n * On Base UI's `Separator` since stage 6 of the migration (2026-09-14). It\n * writes the `separator` role and `aria-orientation` this component used to\n * write by hand, and adds `data-orientation`. Nothing it draws changed.\n */\nexport interface DividerProps {\n orientation?: 'horizontal' | 'vertical'\n /**\n * Words in the middle of the line \u2014 \"New since you last read this\", a\n * date. Horizontal only; the line splits around the label.\n */\n label?: string\n /**\n * `warning` for a line that asks for attention: the label in the warning\n * colour, the lines in its wash. Peek and Ship both drew their \"new since\n * you last read\" rule in the accent, which measures 3.3:1 on Ship's\n * background and could not be read (Katerina, 2026-09-08); the warning\n * colour measures 9.6:1 there. Default: the muted text, `border-subtle`\n * lines, for a date.\n */\n tone?: 'default' | 'warning'\n className?: string\n}\n\nexport function Divider({ orientation = 'horizontal', label, tone = 'default', className }: DividerProps) {\n if (label && orientation === 'horizontal') {\n const line = tone === 'warning' ? 'bg-warning-muted' : 'bg-border-subtle'\n return (\n <Separator\n orientation=\"horizontal\"\n aria-label={label}\n className={cn('flex shrink-0 items-center gap-2 mx-3', className)}\n >\n <span aria-hidden=\"true\" className={cn('h-px flex-1', line)} />\n <span className={cn('shrink-0 text-caption', tone === 'warning' ? 'text-warning-default' : 'text-text-muted')}>{label}</span>\n <span aria-hidden=\"true\" className={cn('h-px flex-1', line)} />\n </Separator>\n )\n }\n return (\n <Separator\n orientation={orientation}\n // shrink-0 on both orientations: a 1px flex child in an overflowing\n // column shrinks to nothing, and a hairline that renders 0px tall is a\n // hairline nobody can see \u2014 Peek's `/` menu had been drawing two of\n // them, measured 0px, since it was built (2026-09-05).\n className={cn('shrink-0 bg-border-subtle', orientation === 'horizontal' ? 'h-px mx-3' : 'w-px self-stretch', className)}\n />\n )\n}\n", "import { cn } from './cn'\nimport { Avatar } from './Avatar'\n\n/**\n * A face beside a name. Ship's Person (2026-09-01), which was Peek's minus\n * the projection type it took its profile from: this one is handed the two\n * strings and nothing else, so it cannot be handed a key. Peek keeps a\n * wrapper that resolves the profile, the way its Avatar wrapper resolves the\n * picture.\n *\n * A face beside a name needs no caption (Peek's rulings), and someone nobody\n * has published a name for gets the `fallback` \u2014 an em dash by default, the\n * same mark a property with no value uses \u2014 rather than the key back on\n * screen to say it. Pass \"Anonymous\" where the unnamed person is *you*, and\n * you know who that is.\n *\n * The text size is the caller's, so the face and the words are always set\n * together \u2014 see the Sizes story.\n */\nexport interface PersonProps {\n name?: string\n picture?: string\n /** Shown in place of a missing name. */\n fallback?: string\n size?: number\n className?: string\n}\n\nexport function Person({ name, picture, fallback = '\u2014', size = 20, className }: PersonProps) {\n return (\n // gap-2: the face-to-name distance is one rule (8px) wherever a person\n // appears \u2014 Katerina, 2026-09-01, set on Person so PersonTrigger and every\n // row agree by construction.\n <span className={cn('flex min-w-0 items-center gap-2', className)}>\n <Avatar name={name} src={picture} size={size} />\n <span className={cn('truncate', !name && 'text-text-muted')}>{name ?? fallback}</span>\n </span>\n )\n}\n", "import type { ComponentPropsWithRef } from 'react'\nimport { Button as BaseButton } from '@base-ui/react/button'\nimport { IconChevronDown } from '@tabler/icons-react'\nimport { cn } from './cn'\nimport { Avatar } from './Avatar'\nimport { Person, type PersonProps } from './Person'\n\n/**\n * The person, as the button that opens the account menu. On Base UI's Button\n * since stage 2 of the migration (2026-09-07).\n *\n * Two shapes, one component (Katerina, 2026-09-01):\n *\n * - the row \u2014 face \u00B7 name \u00B7 chevron on a 32px button with a hover fill, as\n * Ship's top bar draws it. The chevron is 14px, the small-control size.\n * - `compact` \u2014 the face alone, as Peek's top bar draws it: no chevron, no\n * padding, and no hover fill, because the face fills the whole control and\n * a fill would have nowhere to show.\n *\n * What it opens \u2014 the menu and everything in it \u2014 stays in the app.\n */\nexport interface PersonTriggerProps\n extends Omit<PersonProps, 'className'>,\n Omit<ComponentPropsWithRef<'button'>, 'children'> {\n /** Whether what it opens is open \u2014 the row shape holds its hover fill while so. */\n open?: boolean\n /** Face only \u2014 no chevron, no hover fill. Defaults the face to 36px; the row shape to 22px. */\n compact?: boolean\n}\n\nexport function PersonTrigger({ name, picture, fallback, size, open = false, compact = false, className, ...props }: PersonTriggerProps) {\n if (compact) {\n return (\n <BaseButton\n type=\"button\"\n aria-haspopup=\"menu\"\n aria-expanded={open}\n /*\n The face alone is an icon-only control, so it owes a name. It has\n one already \u2014 the person \u2014 and taking it from there means no caller\n can forget: measured 2026-09-08, this button announced **\"AD\"**, the\n initials, with no `aria-label` anywhere. A caller that wants to name\n the destination rather than the person still can, and `IdentityMenu`\n does (\"Account menu\").\n */\n aria-label={props['aria-label'] ?? name ?? fallback}\n /*\n No `focus:outline-none` here any more. It removed the browser's ring\n and put nothing in its place, so this control \u2014 the account trigger\n in Peek's top bar \u2014 had **no visible focus at all** (measured\n 2026-09-08: `outline: solid 2px rgba(0,0,0,0)`, no shadow). Left\n alone it wears the same ring `Button`, `IconButton` and a `Tab`\n wear, which is the ring the rest of the package already relies on.\n */\n className={cn('cursor-pointer rounded-full', className)}\n {...props}\n >\n <Avatar name={name} src={picture} size={size ?? 36} />\n </BaseButton>\n )\n }\n return (\n <BaseButton\n type=\"button\"\n aria-haspopup=\"menu\"\n aria-expanded={open}\n className={cn(\n 'flex h-8 cursor-pointer items-center gap-1.5 rounded-md pl-1.5 pr-1.5 text-body-2 text-text-primary transition-colors hover:bg-bg-hover',\n open && 'bg-bg-hover',\n // Under a `Menu` the open state is Base UI's, not a prop: the trigger\n // carries `data-popup-open` while its menu is up, and sets its own\n // `aria-expanded`. Both spellings hold the fill, so this works whether\n // the caller drives it or the menu does.\n 'data-[popup-open]:bg-bg-hover',\n className,\n )}\n {...props}\n >\n <Person name={name} picture={picture} fallback={fallback} size={size ?? 22} />\n <IconChevronDown size={14} stroke={1.5} className=\"shrink-0 text-text-muted\" />\n </BaseButton>\n )\n}\n", "import { forwardRef, type InputHTMLAttributes } from 'react'\n\n/**\n * The browser's file picker, with nothing drawn: a hidden `<input type=\"file\">`\n * that the caller's own button opens with `ref.current.click()` (UIG-7,\n * 16 September). Base UI has no part for it.\n *\n * Peek's paperclip and Ship's two wrote this by hand, each with the same two\n * details \u2014 the files handed over as they were chosen, and the value cleared\n * so the same file can be chosen again after a failure or after it was\n * removed. Those are here once.\n *\n * Hidden from the accessibility tree and from Tab: the button that opens it is\n * the control, and two things both named \"Attach\" is one more than a screen\n * reader should find. A `display: none` input still opens its picker on a\n * scripted click.\n */\nexport interface FilePickerProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'onChange' | 'value' | 'defaultValue' | 'className' | 'style' | 'children'> {\n /** The chosen files, in the order the picker gave them. Not called when the picker is closed with nothing chosen. */\n onPick: (files: File[]) => void\n}\n\nexport const FilePicker = forwardRef<HTMLInputElement, FilePickerProps>(function FilePicker({ onPick, ...props }, ref) {\n return (\n <input\n ref={ref}\n type=\"file\"\n aria-hidden=\"true\"\n tabIndex={-1}\n className=\"hidden\"\n {...props}\n onChange={(event) => {\n const files = Array.from(event.target.files ?? [])\n // Cleared before anything else runs, so the same file can be chosen again.\n event.target.value = ''\n if (files.length > 0) onPick(files)\n }}\n />\n )\n})\n", "import { IconCheck, IconChevronDown } from '@tabler/icons-react'\nimport { Select as BaseSelect } from '@base-ui/react/select'\nimport type { ReactNode } from 'react'\nimport { cn } from './cn'\nimport { ScrollArea } from './ScrollArea'\nimport { MenuPanel, menuItemClassName } from './Menu'\n\n/**\n * Peek's Select (2026-08-28), verbatim, plus what Ship added: an option may\n * carry a `leading` node \u2014 an avatar beside a person's name \u2014 shown in the\n * trigger and in the list. On Base UI's `Select` since stage 4 of the\n * migration (2026-09-07).\n *\n * A button that shows the choice and opens a listbox under it. **Under it:\n * Base UI would rather lay the list over the trigger so the chosen option's\n * text covers the trigger's, the way macOS does \u2014 `alignItemWithTrigger` is\n * off, because every Select in both apps opens below one today (Katerina,\n * D24).**\n *\n * What the port deleted: `createPortal`, the whole `onKeyDown` switch, the\n * outside-click, resize and scroll listeners, the `aria-activedescendant`\n * bookkeeping, the `scrollIntoView` that kept the highlight visible, and\n * `fit.ts` \u2014 the pure geometry this component grew and then shared with the\n * Menu shell. Floating UI does the fitting; `--available-height` is where the\n * old 288px cap now meets the room the screen actually has.\n *\n * What it gained, and none of it was written here: **typeahead** \u2014 type the\n * first letters of an option and the list jumps to it \u2014 a listbox whose\n * highlight is managed rather than counted, and a value that can be part of a\n * form.\n *\n * Two sizes; `disabled` explains nothing by itself \u2014 wrap it in a tooltip\n * that does.\n */\nexport interface SelectOption {\n value: string\n label: string\n /** Drawn before the label, 16px \u2014 an Avatar, an icon. */\n leading?: ReactNode\n}\n\nexport interface SelectProps {\n value: string\n onChange: (value: string) => void\n options: SelectOption[]\n size?: 'default' | 'small'\n ariaLabel?: string\n /** Set by a `Field` with `required`; a caller inside one owes nothing. */\n 'aria-required'?: boolean | 'true' | 'false'\n placeholder?: string\n disabled?: boolean\n className?: string\n}\n\n/** The 4px between the trigger and the list, and the 8px the list keeps clear\n * of every screen edge \u2014 the two numbers `fitMenu` used. */\nconst GAP = 4\nconst VIEWPORT_PAD = 8\n\nexport function Select({ value, onChange, options, size = 'default', ariaLabel, placeholder = 'Select\u2026', disabled, className, ...aria }: SelectProps) {\n const selected = options.find((o) => o.value === value)\n return (\n <BaseSelect.Root\n value={value}\n onValueChange={(next) => onChange(next as string)}\n disabled={disabled}\n // Non-modal, as it has always been: the page behind stays scrollable and\n // keeps its scrollbar, so opening a select never shifts the layout.\n modal={false}\n >\n <BaseSelect.Trigger\n aria-label={ariaLabel}\n aria-required={aria['aria-required']}\n className={cn(\n /*\n * `min-w-0 max-w-full`: a trigger must never outgrow its container\n * (Katerina, 2026-09-01 \u2014 a long label stretched the files panel's\n * Lead row past its card). In a flex row the default min-width:auto\n * forbids shrinking below the label's width, which is what kept\n * `truncate` from ever engaging; in a block container max-w-full is\n * the cap. Full-width callers are unaffected.\n */\n 'flex w-full min-w-0 max-w-full items-center justify-between gap-2 rounded-lg border bg-bg-inset text-left',\n 'border-border-default text-text-primary outline-none transition-colors',\n // The focused border survives a hover: hover alone strengthens the\n // hairline, but hover while focused must not grey the focus colour \u2014\n // the stacked variant outranks plain hover by specificity.\n 'hover:border-border-strong focus-visible:hover:border-border-focus aria-expanded:hover:border-border-focus disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled',\n 'focus-visible:border-border-focus aria-expanded:border-border-focus',\n 'signal:transition-shadow signal:focus-visible:shadow-focus-ring',\n size === 'default' && 'px-3 py-2 text-input-value',\n size === 'small' && 'h-6 px-2 text-caption',\n className,\n )}\n >\n <span className={cn('flex min-w-0 items-center gap-2', !selected && 'text-text-muted')}>\n {selected?.leading && <span className=\"flex shrink-0 items-center\">{selected.leading}</span>}\n <BaseSelect.Value className=\"truncate\">{() => selected?.label ?? placeholder}</BaseSelect.Value>\n </span>\n <BaseSelect.Icon\n render={<IconChevronDown size={size === 'small' ? 14 : 16} stroke={1.5} className=\"shrink-0 text-text-secondary\" />}\n />\n </BaseSelect.Trigger>\n\n <BaseSelect.Portal>\n <BaseSelect.Positioner\n side=\"bottom\"\n align=\"start\"\n sideOffset={GAP}\n collisionPadding={VIEWPORT_PAD}\n /* D24: hang the list under the trigger, as it always has, rather\n than laying it over the trigger with the chosen option on top. */\n alignItemWithTrigger={false}\n /*\n * The list follows its trigger now rather than closing when the page\n * scrolls \u2014 it was `fixed` to where the trigger had been, so closing\n * was the only way it could avoid being left behind. `anchor-hidden`\n * is the other half of that: when the trigger scrolls out of view\n * entirely the list goes with it, instead of floating over whatever\n * has scrolled into its place.\n */\n className=\"z-50 data-[anchor-hidden]:hidden\"\n >\n <BaseSelect.Popup\n /* `--anchor-width` is the trigger's width and `--available-height`\n the room the list actually has after Floating UI has flipped and\n clamped it \u2014 the two numbers `fitMenu` used to compute here. The\n 288px is the old `max-h-72`, now a ceiling on that room rather\n than a height applied blind. */\n /* The list is the package's one list (Katerina, 2026-09-13: \"i\n thought the type to search menu would be from estiva-ui and be\n the one used in select component\"). The box is `MenuPanel` \u2014\n the same border, fill, radius and shadow this used to spell out\n \u2014 with its padding moved onto the scrolling content (D63). */\n render={<MenuPanel />}\n className=\"min-w-[var(--anchor-width)] p-0\"\n >\n {/* The list scrolls in a ScrollArea: the bar takes no width, so a\n long list is exactly as wide as a short one (Katerina,\n 2026-09-08).\n *\n * **The padding is on the scrolling content, not on the panel**\n * (D63, applied here 2026-09-13). On the panel it inset the\n * scrolling box, so the thumb sat 7px from the panel's edge where\n * DialogShell, Popover, Menu and ChipInput all draw it at 3px \u2014\n * Katerina: \"the position of the scrollbar in select \u2026 not closer\n * to the right side\". The rows keep their 4px inset, because the\n * padding that was the panel's is the content's; and the cap loses\n * its `- 0.5rem`, because that padding is inside the box that\n * scrolls now, so 288px stays 288px. */}\n <ScrollArea viewportClassName=\"max-h-[min(288px,var(--available-height))]\" contentClassName=\"flex flex-col p-2\">\n {options.map((option) => (\n <BaseSelect.Item\n key={option.value}\n value={option.value}\n /* The highlight is Base UI's `data-highlighted` \u2014 pointer and\n keyboard set the same attribute, so what the DOM says and\n what the row looks like cannot disagree. It used to be an\n index this component counted. */\n /* A menu row, exactly (`menuItemClassName`): 36px floor, 8px\n in from a panel padded 8px, so the label lands 17px from the\n panel's edge \u2014 the same pixel as the old 4px + 12px. What\n Select adds is its own: the \u2713 at the end, and the chosen\n row in medium weight. No fade on the highlight, as in every\n menu (Katerina, 2026-09-05). */\n className={cn(menuItemClassName({ size: 'default' }), 'justify-between data-[selected]:font-medium')}\n >\n <span className=\"flex min-w-0 items-center gap-2\">\n {option.leading && <span className=\"flex shrink-0 items-center\">{option.leading}</span>}\n <BaseSelect.ItemText className=\"truncate text-body-2 text-text-primary\">{option.label}</BaseSelect.ItemText>\n </span>\n <BaseSelect.ItemIndicator\n render={<IconCheck size={16} stroke={1.5} className=\"shrink-0 text-text-secondary\" />}\n />\n </BaseSelect.Item>\n ))}\n </ScrollArea>\n </BaseSelect.Popup>\n </BaseSelect.Positioner>\n </BaseSelect.Portal>\n </BaseSelect.Root>\n )\n}\n", "import { forwardRef, type InputHTMLAttributes } from 'react'\nimport { Input } from '@base-ui/react/input'\nimport { cn } from './cn'\n\n/**\n * Peek's TextInput (2026-08-28): the inset field with a 8px radius, 14px\n * text, the focus border in every theme (Katerina, 2026-08-28: Ship's inputs\n * focus like Signal's) \u2014 and Signal's glow on top.\n * Plus a disabled look (Ship's addition): the disabled surface and text,\n * no pointer. On Base UI's `Input` since stage 3 of the migration\n * (2026-09-07).\n *\n * Base UI's `Input` is a native `<input>` that finds a surrounding `Field` on\n * its own, so the id plumbing this used to do \u2014 `useFieldControlId`, and the\n * context behind it \u2014 is gone. Outside a `Field` it behaves as before: the\n * field context has a default, so nothing has to be wrapped.\n */\nexport interface TextInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size'> {\n /**\n * `small` is 24px tall with 12px text \u2014 the small `Select`'s size, so a\n * field can share a dense row with small selects (D67, 2026-09-13: Peek's\n * reference widget hand-built one because this was 38px). Replaces the\n * native `size` attribute, which counts characters and which no caller\n * used; set a width with the layout instead.\n */\n size?: 'default' | 'small'\n}\n\nexport const TextInput = forwardRef<HTMLInputElement, TextInputProps>(function TextInput({ className, type = 'text', size = 'default', ...props }, ref) {\n return (\n <Input\n ref={ref}\n type={type}\n className={cn(\n // The border strengthens on hover, as Select's and ChipInput's do (Katerina, 16 September:\n // \"aren't there hover states in text input and text area?\"). Focus comes after hover in\n // Tailwind's order, so a focused field keeps the focus border under the pointer.\n 'bg-bg-inset border border-border-default hover:border-border-strong focus:border-border-focus rounded-lg',\n // The small size is the small Select's trigger, class for class.\n size === 'default' && 'px-3 py-2 text-input-value',\n size === 'small' && 'h-6 px-2 text-caption',\n 'text-text-primary placeholder:text-text-muted',\n 'outline-none transition-colors',\n 'disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled',\n 'signal:transition-shadow signal:focus:shadow-focus-ring',\n className,\n )}\n {...props}\n />\n )\n})\n", "import { forwardRef, type TextareaHTMLAttributes } from 'react'\nimport { Field as BaseField } from '@base-ui/react/field'\nimport { cn } from './cn'\n\n/**\n * Peek's Textarea (2026-08-28), verbatim: TextInput's look on a textarea\n * that does not resize. Plus a disabled look (Ship's addition). On Base UI's\n * `Field.Control` since stage 3 of the migration (2026-09-07).\n *\n * Base UI has an `Input` part but no textarea, so this is `Field.Control`\n * rendering a `<textarea>` \u2014 the same part `Input` is built on, told which\n * element to be. It finds a surrounding `Field` on its own, which is what\n * retired `useFieldControlId`; outside one it renders a plain textarea, as\n * before.\n */\nexport type TextareaProps = TextareaHTMLAttributes<HTMLTextAreaElement>\n\nexport const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(function Textarea({ className, ...props }, ref) {\n return (\n <BaseField.Control\n ref={ref as React.Ref<HTMLElement>}\n render={<textarea />}\n className={cn(\n // The border strengthens on hover, as Select's and ChipInput's do (Katerina, 16 September:\n // \"aren't there hover states in text input and text area?\"). Focus comes after hover in\n // Tailwind's order, so a focused field keeps the focus border under the pointer.\n 'bg-bg-inset border border-border-default hover:border-border-strong focus:border-border-focus rounded-lg px-3 py-2',\n 'text-input-value text-text-primary placeholder:text-text-muted',\n 'resize-none outline-none transition-colors',\n 'disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled',\n 'signal:transition-shadow signal:focus:shadow-focus-ring',\n className,\n )}\n {...(props as object)}\n />\n )\n})\n", "import { useState, type ReactNode } from 'react'\nimport { Button } from './Button'\nimport { DialogShell } from './DialogShell'\n\n/**\n * A question with two answers, before something a person would want back \u2014\n * Ship's ConfirmDialog (2026-09-01), verbatim. Its own comment always called\n * it a package candidate: nothing here knows what is being confirmed.\n *\n * The confirm button is `destructive` when the action is (a deletion, an\n * archive); `primary` otherwise. While the action runs the buttons wait;\n * an action that resolves `false` keeps the dialog open, so the caller's\n * banner can say why.\n *\n * On Base UI's `AlertDialog` since stage 3 of the migration (2026-09-07),\n * through `DialogShell`'s `alert`. **A press on the backdrop no longer\n * closes it** (Katerina, D20): a destructive question is answered, not\n * clicked away. Escape and the close button still cancel, as they did.\n */\nexport interface ConfirmDialogProps {\n title: string\n /** The body \u2014 plain sentences, body-2. */\n children: ReactNode\n confirmLabel: string\n destructive?: boolean\n onConfirm: () => Promise<boolean | void> | boolean | void\n onClose: () => void\n}\n\nexport function ConfirmDialog({ title, children, confirmLabel, destructive = false, onConfirm, onClose }: ConfirmDialogProps) {\n const [busy, setBusy] = useState(false)\n const confirm = async () => {\n setBusy(true)\n try {\n const ok = await onConfirm()\n if (ok !== false) onClose()\n } finally {\n setBusy(false)\n }\n }\n return (\n <DialogShell\n alert\n title={title}\n onClose={onClose}\n bodyClassName=\"flex flex-col gap-3 text-body-2 text-text-primary\"\n footer={\n <>\n <Button variant=\"muted\" onClick={onClose} disabled={busy}>\n Cancel\n </Button>\n <Button variant={destructive ? 'destructive' : 'primary'} onClick={() => void confirm()} disabled={busy}>\n {confirmLabel}\n </Button>\n </>\n }\n >\n {children}\n </DialogShell>\n )\n}\n", "import { useRef, type ReactNode } from 'react'\nimport { Dialog } from '@base-ui/react/dialog'\nimport { AlertDialog } from '@base-ui/react/alert-dialog'\nimport { IconX } from '@tabler/icons-react'\nimport { cn } from './cn'\nimport { IconButton } from './IconButton'\nimport { ScrollArea } from './ScrollArea'\n\n/**\n * Peek's DialogShell (2026-08-28), verbatim: the portal, the backdrop, the\n * 502px card and its chrome \u2014 a 48px header with the title and a close\n * button, a body, a 48px footer for the buttons. A dialog is just what goes\n * in the three slots. On Base UI's `Dialog` since stage 3 of the migration\n * (2026-09-07).\n *\n * **What that fixed, and this page used to say it did not:** focus is trapped\n * inside the card, so Tab cannot walk out into the page behind it, and it\n * returns to whatever opened the dialog when it closes. Both were missing and\n * both were written down as missing. The portal, the outside press and the\n * Escape key are Base UI's too \u2014 the `keydown` listener this file kept on\n * `document` is gone, and with it the bug where two open dialogs both closed\n * on one Escape.\n *\n * The card is mounted only while it is open, as before: this component has no\n * `open` prop, and a caller renders it or does not. So `open` is `true` and\n * `onOpenChange` reports the ways Base UI closes it \u2014 Escape, a press outside,\n * the close button \u2014 through the one `onClose` the callers already pass.\n *\n * The DOM is the same shape it was, deliberately: a backdrop, then a\n * full-screen flex layer that centres the card. Base UI positions nothing for\n * a dialog, so keeping the layer is what keeps the pixels.\n */\nexport interface DialogShellProps {\n /** Labels the dialog for assistive tech, and renders as the header text\n * unless `headerContent` replaces it. */\n title: string\n onClose: () => void\n /** Replaces the title text in the header \u2014 a back button beside the title,\n * a count chip after it. The close button stays. */\n headerContent?: ReactNode\n /** Absent: no footer row, and the body keeps the card's own bottom edge\n * (a roster that simply ends). */\n footer?: ReactNode\n children: ReactNode\n /** Extra classes on the body: its layout and any padding override (`flex flex-col gap-6`, `p-0 py-2`). */\n bodyClassName?: string\n /**\n * How tall the body may get before it scrolls, as the cap class \u2014\n * `max-h-[400px]`, `max-h-[70vh]`. Setting it makes the body a `ScrollArea`,\n * so a long roster or a tall form scrolls in the package's bar rather than\n * the browser's (D40, ADOPTION B19).\n *\n * Without it the body is what it always was and grows to its content, so no\n * dialog written before 0.12.2 moves a pixel. A `bodyClassName` carrying\n * `overflow-y-auto` and no cap never scrolled anything \u2014 the box grew \u2014 and\n * is the case this replaces.\n */\n bodyMaxHeight?: string\n width?: number\n /**\n * A question that has to be answered rather than clicked away: a press on\n * the backdrop no longer closes it, and it announces itself as an alert.\n * Escape and the close button still work. `ConfirmDialog` is this\n * (Katerina, D20, 2026-09-07); a form or a roster is not.\n */\n alert?: boolean\n}\n\nexport function DialogShell({ title, onClose, headerContent, footer, children, bodyClassName, bodyMaxHeight, width = 502, alert = false }: DialogShellProps) {\n // The two families are the same parts with different dismiss rules, so the\n // chrome below is written once. AlertDialog re-exports Dialog's Backdrop,\n // Popup, Portal and Title types, which is why this substitutes cleanly.\n const Parts = alert ? AlertDialog : Dialog\n const popupRef = useRef<HTMLDivElement>(null)\n return (\n <Parts.Root\n open\n onOpenChange={(open) => {\n if (!open) onClose()\n }}\n >\n <Parts.Portal>\n {/* Backdrop */}\n <Parts.Backdrop className=\"fixed inset-0 z-40 bg-scrim\" />\n\n {/* Dialog */}\n <div className=\"fixed inset-0 z-50 flex items-center justify-center pointer-events-none\">\n <Parts.Popup\n ref={popupRef}\n /*\n Focus the card, not the first control in it.\n\n Base UI's default is the first tabbable element, which here is the\n \u2715 in the header \u2014 so every dialog opened with a visible ring on\n its close button, which the screenshot diff caught as a 28px\n square nobody had asked for. Focusing the card arms the trap and\n lets a screen reader read the dialog, with no control lit up.\n\n A field with `autoFocus` still wins: React focuses it while the\n popup mounts, and Base UI does not move focus that has already\n landed inside. Both dialogs in Ship rely on that.\n */\n initialFocus={popupRef}\n /* The header names it when the header is the title. When\n `headerContent` replaces that text there is nothing to point at,\n so the name is spelled instead \u2014 as this component always did. */\n aria-label={headerContent != null ? title : undefined}\n /* `outline-none` because the card is a programmatic focus target,\n not something a keyboard user tabs to: without it Chrome rings\n the whole 502px card on open, which the diff caught the moment\n `initialFocus` moved off the \u2715. The controls inside keep their\n own focus styling. */\n className=\"bg-bg-elevated border border-border-subtle rounded-lg shadow-lg pointer-events-auto flex flex-col overflow-hidden outline-none\"\n style={{ width }}\n >\n {/* Header */}\n <div className=\"h-12 flex items-center justify-between pl-5 pr-4 border-b border-border-subtle shrink-0\">\n {headerContent ?? (\n <Parts.Title className=\"text-h4 text-text-primary\" render={<span />}>\n {title}\n </Parts.Title>\n )}\n {/*\n The \u2715 IS the `Close` part now, rather than a button that calls\n `onClose` beside one (stage 4, 2026-09-07). Stage 3 could not do\n this: an `IconButton` carrying a `tooltip` returned the tooltip\n wrapper `<div>` as its root, so the part composed onto the\n wrapper and not the button. Porting Tooltip removed the wrapper\n \u2014 the trigger is the button itself \u2014 and the composition works.\n\n What it buys: the dialog closes through its own state machine,\n so the \u2715, Escape and the outside press are one path with one\n reason attached, instead of one of the three going around.\n */}\n <Parts.Close\n render={\n <IconButton tooltip=\"Close\" aria-label=\"Close\">\n <IconX size={16} stroke={1.5} />\n </IconButton>\n }\n />\n </div>\n\n {/* Body. With a cap it scrolls in the package's bar: the cap goes\n on the viewport (ScrollArea's rule \u2014 on the region the viewport\n grows to its content and nothing scrolls), and the padding and\n the caller's layout go on the content, so the bar is drawn over\n the padding rather than beside it. */}\n {bodyMaxHeight ? (\n <ScrollArea\n className={cn(footer != null && 'border-b border-border-subtle')}\n viewportClassName={bodyMaxHeight}\n contentClassName={cn('pl-5 pr-4 py-4', bodyClassName)}\n >\n {children}\n </ScrollArea>\n ) : (\n <div className={cn('pl-5 pr-4 py-4', footer != null && 'border-b border-border-subtle', bodyClassName)}>{children}</div>\n )}\n\n {/* Footer */}\n {footer != null && <div className=\"h-12 flex items-center justify-end gap-2 pl-5 pr-4 shrink-0\">{footer}</div>}\n </Parts.Popup>\n </div>\n </Parts.Portal>\n </Parts.Root>\n )\n}\n", "import { useEffect, useRef, useState, type KeyboardEvent, type ReactNode, type RefObject } from 'react'\nimport { Field as BaseField } from '@base-ui/react/field'\nimport { Input } from '@base-ui/react/input'\nimport { cn } from './cn'\n\n/**\n * Text you click to edit \u2014 Ship's EditableText (2026-09-01), verbatim. Its\n * own comment always called it a package candidate: nothing here knows what\n * is being edited.\n *\n * The editing state is Base UI's `Input` \u2014 or `Field.Control` as a\n * `<textarea>` when multiline \u2014 since stage 3 of the migration (2026-09-07).\n * The read state, the draft, and every rule about committing stay here:\n * Base UI has no opinion about what an edit means, and this component is\n * nothing but that opinion.\n *\n * Reads as text until clicked; then it is a field. Enter commits (Shift+Enter\n * is a new line when multiline), Escape cancels, blur commits. A commit that\n * fails keeps the field open with the text in it, so an edit is never\n * silently lost \u2014 the caller has already said why. An unchanged value is not\n * committed at all. Empty shows the placeholder, muted.\n */\nexport interface EditableTextProps {\n value: string\n placeholder: string\n /** Resolve `true` when saved; `false` (or throw) keeps the field open with the text. */\n onCommit: (value: string) => Promise<boolean> | boolean\n multiline?: boolean\n /** Applied to both the text and the field, so they are the same size. */\n className?: string\n /** The accessible name of the field \u2014 \"Title\", \"Description\". */\n label: string\n /**\n * What to *show* when not editing, when that differs from what is edited \u2014\n * a value carrying a raw reference that the caller renders as a live object\n * below the text, say.\n *\n * It overrides the display branch only. `value` is still what the editor\n * opens with and what a commit compares against, so nothing is silently lost\n * \u2014 which is the failure a naive `value={stripped}` would cause.\n */\n display?: string\n /**\n * What to *draw* when not editing, when the value is structured rather than\n * a line of prose.\n *\n * A node, not a string, because a heading and a bullet are elements. Added\n * for a rich text field (SPEC \u00A713): the app renders the parsed body and\n * hands the result in.\n *\n * `display` stays the **string**, and stays what decides emptiness \u2014 so a\n * blank field still shows its placeholder rather than an empty element. Like\n * `display`, this overrides the display branch only: `value` is what the\n * editor opens with and what a commit compares against, so what is edited is\n * unchanged.\n *\n * `whitespace-pre-wrap` is dropped when this is set. Structured content\n * carries its own line breaks, and preserving the source's as well doubles\n * every one of them.\n */\n displayNode?: ReactNode\n /** Show the value only \u2014 no edit affordance. For a reader who cannot write. */\n readOnly?: boolean\n}\n\nexport function EditableText({ value, display, displayNode, placeholder, onCommit, multiline = false, className, label, readOnly = false }: EditableTextProps) {\n const [editing, setEditing] = useState(false)\n const [draft, setDraft] = useState(value)\n const [busy, setBusy] = useState(false)\n const fieldRef = useRef<HTMLInputElement | HTMLTextAreaElement>(null)\n\n useEffect(() => {\n if (editing) {\n fieldRef.current?.focus()\n fieldRef.current?.select()\n }\n }, [editing])\n\n const open = () => {\n setDraft(value)\n setEditing(true)\n }\n\n const cancel = () => {\n setEditing(false)\n setDraft(value)\n }\n\n const commit = async () => {\n if (busy) return\n const next = draft.trim()\n if (next === value) {\n setEditing(false)\n return\n }\n setBusy(true)\n try {\n const ok = await onCommit(next)\n if (ok) setEditing(false)\n } catch {\n /* the caller has shown why; keep the field open */\n } finally {\n setBusy(false)\n }\n }\n\n const onKeyDown = (event: KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>) => {\n if (event.key === 'Escape') {\n event.preventDefault()\n cancel()\n } else if (event.key === 'Enter' && !(multiline && event.shiftKey)) {\n event.preventDefault()\n void commit()\n }\n }\n\n const fieldClass = cn(\n 'w-full rounded-md border border-border-strong bg-bg-inset px-2 py-1 text-text-primary outline-none',\n multiline && 'resize-none',\n className,\n )\n\n if (readOnly) {\n return (\n /*\n No `aria-label` here. It sat on a bare `<div>`, which has no role, and\n ARIA does not let an author name a generic element \u2014 so the name was\n written and never read. Read-only, this is a line of text: whatever\n names the region around it (a `Field`, a `Property`) names this too.\n */\n <div\n className={cn('w-full px-2 py-1', multiline && !displayNode && 'whitespace-pre-wrap', (display ?? value) ? 'text-text-primary' : 'text-text-muted', className)}\n >\n {(display ?? value) ? (displayNode ?? (display ?? value)) : placeholder}\n </div>\n )\n }\n\n if (editing) {\n return multiline ? (\n <BaseField.Control\n ref={fieldRef as RefObject<HTMLTextAreaElement>}\n render={<textarea rows={4} />}\n aria-label={label}\n value={draft}\n disabled={busy}\n onChange={(event) => setDraft(event.target.value)}\n onKeyDown={onKeyDown}\n onBlur={() => void commit()}\n className={fieldClass}\n />\n ) : (\n <Input\n ref={fieldRef as RefObject<HTMLInputElement>}\n aria-label={label}\n value={draft}\n disabled={busy}\n onChange={(event) => setDraft(event.target.value)}\n onKeyDown={onKeyDown}\n onBlur={() => void commit()}\n className={fieldClass}\n />\n )\n }\n\n return (\n <button\n type=\"button\"\n onClick={open}\n /*\n No `title`. It was the one native browser tooltip left in the package \u2014\n its own timing, its own look, no theme \u2014 saying \"Click to edit\" beside\n an `aria-label` that already says it. The hover border is the\n affordance, and anything that needs a designed hint uses `WithTooltip`.\n */\n aria-label={`Edit ${label.toLowerCase()}`}\n className={cn(\n 'w-full rounded-md border border-transparent px-2 py-1 text-left transition-colors hover:border-border-default',\n multiline && !displayNode && 'whitespace-pre-wrap',\n (display ?? value) ? 'text-text-primary' : 'text-text-muted',\n className,\n )}\n >\n {(display ?? value) ? (displayNode ?? (display ?? value)) : placeholder}\n </button>\n )\n}\n", "import { Progress } from '@base-ui/react/progress'\nimport { cn } from './cn'\n\n/**\n * How much of something is done: a thin rounded track with a success-coloured\n * fill. On Base UI's `Progress`, which owns the `progressbar` role and its\n * numbers; this component writes the look.\n *\n * Both apps hand-built one, each with its own `role=\"progressbar\"` (UIG-27).\n * Their two looks are both kept, because each does a different job:\n *\n * - `default` is Ship's (`ui/ProgressBar.tsx`): 6px, the success colour \u2014 a\n * bar someone reads, with the words printed beside it.\n * - `quiet` is Peek's (`ui/ProjectTickets.tsx`): 4px, the muted success colour\n * \u2014 a glance on the same line as a count, \"not a chart\".\n *\n * The props are Ship's, unchanged. The bar alone says nothing a screen reader\n * can use beyond its numbers, so `label` is required; Base UI reads the value\n * out as a percentage.\n */\nexport type ProgressBarVariant = 'default' | 'quiet'\n\nconst TRACK_CLASSES: Record<ProgressBarVariant, string> = {\n default: 'h-1.5 w-full overflow-hidden rounded-full bg-bg-inset',\n // 4px, Katerina's ruling of 14 September; Peek's own bar is 3px until it takes this one.\n quiet: 'h-1 w-full overflow-hidden rounded-full bg-bg-inset',\n}\n\nconst INDICATOR_CLASSES: Record<ProgressBarVariant, string> = {\n default: 'h-full rounded-full bg-success-default transition-[width]',\n quiet: 'h-full rounded-full bg-success-muted transition-[width] duration-300',\n}\n\nexport interface ProgressBarProps {\n /** How many are done. */\n value: number\n /** How many there are. At `0` the bar is empty. */\n max: number\n /** The accessible name \u2014 \"Items done\". */\n label: string\n /** Default `default`. */\n variant?: ProgressBarVariant\n /** Placement only: a width, a margin, `flex-1` in a row. */\n className?: string\n}\n\nexport function ProgressBar({ value, max, label, variant = 'default', className }: ProgressBarProps) {\n return (\n // `max={0}` needs no guard: Base UI reads the 0/0 share as an empty bar.\n <Progress.Root value={value} max={max} aria-label={label} className={className}>\n <Progress.Track className={TRACK_CLASSES[variant]}>\n <Progress.Indicator className={INDICATOR_CLASSES[variant]} />\n </Progress.Track>\n </Progress.Root>\n )\n}\n", "import { Fragment, useCallback, useLayoutEffect, useRef, useState, type MouseEventHandler, type ReactNode } from 'react'\nimport { cn } from './cn'\nimport { Link } from './Link'\nimport { WithTooltip } from './Tooltip'\n\n/**\n * A trail: \"Documents / Quarterly plan / DOC-12\". Ship's Breadcrumb\n * (2026-09-01), which knows nothing about what the places are.\n *\n * An item with an `href` is a link, even when it is last \u2014 a page may end its\n * trail on somewhere to go. The last item is where you are when it has none,\n * and may be `mono` (a ref, an id) or plain. The separator is a slash, muted,\n * never read aloud.\n *\n * A crumb that truncates shows its whole label in a tooltip on hover\n * (Katerina, 2026-09-01); one that fits shows nothing extra. Truncation is\n * re-measured when the trail resizes, so the tooltip appears and disappears\n * with the room the trail actually has.\n *\n * A crumb with an `href` is a plain anchor, so in a router app a click on it\n * reloads the page. Such an app passes `onClick` per crumb and navigates in\n * place there \u2014 NavItem's rule \u2014 and the `href` stays a real address so the\n * link can still be copied or opened in a new tab. Ship's breadcrumbs were\n * the last links reloading the whole app (ADOPTION S15, 2026-09-08).\n */\nexport interface Crumb {\n label: string\n href?: string\n /** A router app intercepts the click here. Called only on a crumb with an `href`. */\n onClick?: MouseEventHandler<HTMLAnchorElement>\n /** Set the item in the mono face \u2014 a ref, an id. */\n mono?: boolean\n /**\n * 16px, stroke 1.5, before the label \u2014 what kind of place this is, so a\n * container's name is not mistaken for an item's (Katerina, 2026-09-09).\n * Decorative: the label says where you are; the icon says what it is.\n */\n icon?: ReactNode\n /** Quieter \u2014 a label that is not a place. */\n muted?: boolean\n}\n\nexport interface BreadcrumbProps {\n items: Crumb[]\n className?: string\n}\n\nexport function Breadcrumb({ items, className }: BreadcrumbProps) {\n const navRef = useRef<HTMLElement>(null)\n const labelRefs = useRef<(HTMLElement | null)[]>([])\n const [truncated, setTruncated] = useState<ReadonlySet<number>>(new Set())\n\n const measure = useCallback(() => {\n const next = new Set<number>()\n labelRefs.current.forEach((el, index) => {\n if (el && el.scrollWidth > el.clientWidth) next.add(index)\n })\n setTruncated((prev) => (prev.size === next.size && [...next].every((i) => prev.has(i)) ? prev : next))\n }, [])\n\n useLayoutEffect(() => {\n measure()\n const nav = navRef.current\n // jsdom has neither layout nor ResizeObserver; without this guard a\n // consumer app cannot render a page with a trail in its tests.\n if (!nav || typeof ResizeObserver === 'undefined') return\n const observer = new ResizeObserver(measure)\n observer.observe(nav)\n return () => observer.disconnect()\n }, [measure, items])\n\n return (\n <nav ref={navRef} aria-label=\"Breadcrumb\" className={cn('flex min-w-0 items-center gap-1.5 text-body-2', className)}>\n {items.map((item, index) => {\n const last = index === items.length - 1\n // The mono size is an arbitrary value (the caption token) because this\n // string goes through cn() and a token size before a colour class is\n // dropped (the tailwind-merge pitfall).\n const tone = item.muted || (last && item.mono) ? 'text-text-muted' : last ? 'text-text-primary' : 'text-text-secondary'\n const text = cn(\n 'truncate',\n // A mono crumb is a ref \u2014 the identity. It never gives up width to a\n // long name beside it (the LongName story always claimed \"the ref\n // stays\"; flexbox was squeezing it anyway until this line).\n item.mono && 'shrink-0 font-mono text-caption',\n tone,\n )\n const setLabelRef = (el: HTMLElement | null) => {\n labelRefs.current[index] = el\n }\n const crumb = item.href ? (\n // `plain` adds no look of its own: a crumb keeps the trail's size and\n // tone, and brightens on hover. The package's own Link, so a trail\n // follows whatever a link learns to do (UIG-5).\n <Link ref={setLabelRef} variant=\"plain\" href={item.href} onClick={item.onClick} className={cn(text, 'hover:text-text-primary')}>\n {item.label}\n </Link>\n ) : (\n <span ref={setLabelRef} className={text} aria-current={last ? 'page' : undefined}>\n {item.label}\n </span>\n )\n return (\n <Fragment key={`${item.label}-${index}`}>\n {index > 0 && (\n <span aria-hidden=\"true\" className=\"shrink-0 text-text-muted\">\n /\n </span>\n )}\n {/* Beside the crumb, not inside it: the crumb stays the one element\n that truncates and is measured, and the trail's own gap \u2014 6px \u2014\n is the distance between an icon and its name everywhere here. */}\n {item.icon && (\n <span aria-hidden=\"true\" className={cn('flex shrink-0', tone)}>\n {item.icon}\n </span>\n )}\n {truncated.has(index) ? (\n // `min-w-0 shrink` undoes the wrapper's own shrink-0 \u2014 the crumb\n // must keep truncating inside it, or wrapping it would widen the\n // trail and the tooltip would never be needed again.\n <WithTooltip label={item.label} wrapperClassName=\"min-w-0 shrink\">\n {crumb}\n </WithTooltip>\n ) : (\n crumb\n )}\n </Fragment>\n )\n })}\n </nav>\n )\n}\n", "import type { ComponentPropsWithoutRef, ReactNode } from 'react'\nimport { cn } from './cn'\nimport { WithTooltip } from './Tooltip'\n\n/**\n * One row of a sidebar: a label, an optional count, an active state\n * (Ship's NavItem, 2026-09-02, verbatim).\n *\n * A count is a number of ONE thing, and its tooltip says which (\"18\n * open\", \"5 active\") \u2014 the caller passes the number and the words for it,\n * and a zero is not drawn at all. That is this row's rule; a *tab's*\n * count draws its zero (see Tabs) \u2014 both rulings stand, deliberately.\n *\n * Active is the same fill as a selected tab \u2014 `bg-active`, neutral.\n */\nexport interface NavItemProps extends Omit<ComponentPropsWithoutRef<'a'>, 'href'> {\n label: string\n href: string\n /** `0` or absent renders no counter. */\n count?: number\n /** What the count counts, for the tooltip \u2014 \"18 open\", \"5 active\". */\n countLabel?: string\n active?: boolean\n /** 16px, stroke 1.5. */\n icon?: ReactNode\n className?: string\n}\n\nexport function NavItem({ label, href, count, countLabel, active = false, icon, className, ...props }: NavItemProps) {\n return (\n <a\n href={href}\n aria-current={active ? 'page' : undefined}\n className={cn(\n // shrink-0: the row keeps its 32px inside an overflowing flex column\n // \u2014 without it the list compresses instead of scrolling (the missing-\n // shrink-0 family; Katerina, 2026-09-02).\n 'flex h-8 min-w-0 shrink-0 items-center gap-2 rounded-md px-2 text-body-2 transition-colors',\n active ? 'bg-bg-active text-text-primary' : 'text-text-secondary hover:bg-bg-hover hover:text-text-primary',\n className,\n )}\n {...props}\n >\n {icon && <span className=\"flex shrink-0 items-center\">{icon}</span>}\n <span className=\"flex-1 truncate\">{label}</span>\n {count ? (\n <WithTooltip label={countLabel ?? `${count} open`}>\n {/* A 16px box, centred: the slot is an icon's width, so a number\n here sits on the same axis as a SectionHeader's action above it\n \u2014 right edges alone left a digit 4px off a 16px icon (Katerina,\n 2026-09-09). A three-digit count grows the box leftwards. */}\n <span className=\"min-w-4 shrink-0 text-center font-mono text-caption tabular-nums text-text-muted\">{count}</span>\n </WithTooltip>\n ) : null}\n </a>\n )\n}\n", "import { useMemo, type ReactElement, type ReactNode, type RefObject } from 'react'\nimport { Popover as BasePopover } from '@base-ui/react/popover'\nimport { cn } from './cn'\nimport { triggerDisabled } from './triggerDisabled'\nimport { ScrollArea } from './ScrollArea'\nimport { MenuPanel } from './Menu'\n\n/**\n * A floating panel from a trigger \u2014 the same elevated surface a `Menu` uses,\n * with none of a menu's semantics. New at stage 4 (2026-09-07).\n *\n * **It exists because `Menu` was being used for this.** Peek's selection\n * toolbar puts a text field inside one, and its debug panel fills one with\n * toggle rows; neither is a list of actions, and since the menus moved onto\n * Base UI a `Menu` gives its contents roving focus and typeahead, which is\n * wrong for both and fights a text field outright. Beyond those two, Peek\n * hand-writes overlay behaviour in thirteen files \u2014 six `createPortal`, five\n * outside-click listeners, seven position calculations against\n * `window.innerWidth` (`COMPONENTS-PEEK.md` F5). This is what they become.\n *\n * **The API is `Menu`'s**: it takes the `trigger` and owns everything after \u2014\n * the toggle, the placement, the dismissal and the focus return. What differs\n * is inside: a `Popover` announces itself as a dialog, its contents are\n * ordinary content, and Tab walks them in order.\n *\n * The one thing a `Menu` has no use for is the second mode below: a panel with\n * no trigger element at all, hung from a rect the caller measured \u2014 a toolbar\n * over a text selection. That mode is controlled, because there is nothing for\n * Base UI to watch.\n */\nexport interface PopoverProps {\n /**\n * The control that opens the panel. Any element that forwards its ref and\n * spreads its props \u2014 this package's `Button`, `IconButton` and\n * `PersonTrigger` all do.\n *\n * Base UI can only do the toggle, the placement, the dismissal and the focus\n * return if it knows which element opened the panel. Give it the trigger\n * unless there is genuinely no element to give \u2014 see `anchor`.\n */\n trigger?: ReactElement\n /**\n * For a panel with **no trigger element**: an element, or a rect the caller\n * measured \u2014 a text selection's. Pair it with `open`, since there is nothing\n * for Base UI to watch.\n *\n * An anchored panel **does not take focus**, because the person is still in\n * whatever produced it. That also means it cannot be reached by keyboard, so\n * everything in one must be reachable another way.\n *\n * Render it always and toggle `open`; do not mount it only while it is open.\n */\n anchor?: HTMLElement | DOMRect | null\n /**\n * Which of the panel's edges hangs from the trigger's, or `center` to put\n * the panel's middle over the anchor's. Default left.\n *\n * **`center` is what a toolbar over a text selection wants** (Katerina,\n * PEE-19, after Linear): the panel sits over the middle of what you\n * selected, wherever in the line that is. Until 0.12.2 only the two edges\n * were mapped, so Peek's selection toolbar kept the placement arithmetic\n * the move onto this component was meant to delete (ADOPTION B21).\n */\n align?: 'left' | 'center' | 'right'\n /**\n * Which side of the trigger, or of the anchor, the panel prefers. Default\n * `bottom`.\n *\n * **A toolbar over a text selection wants `top`** (Katerina, 2026-09-08):\n * below, it covers the line you are about to read next, and it is the line\n * *after* the selection that tells you what you have selected. It is a\n * preference, not a promise \u2014 Base UI flips it when that side has no room.\n */\n side?: 'top' | 'bottom'\n /** Controlled, for a caller that must know or must force it. Required with\n * `anchor`; with a `trigger`, leave both off and the panel keeps its own. */\n open?: boolean\n onOpenChange?: (open: boolean) => void\n /**\n * Where focus goes when the panel closes. With a `trigger` it goes back to\n * the trigger and this is not needed. An anchored panel never took focus, so\n * this only matters when something inside it did \u2014 a field the person tabbed\n * or clicked into: point it at what they came from, or focus is left on the\n * document body.\n */\n finalFocus?: RefObject<HTMLElement | null>\n /** Base UI's imperative handle. `actions.current?.close()` shuts the panel \u2014\n * for the Cancel and Save buttons a form panel ends with. */\n actionsRef?: RefObject<{ close: () => void; unmount: () => void } | null>\n /** Names the panel for assistive tech. A panel with a visible heading can\n * point at it instead, with `aria-labelledby`. */\n ariaLabel?: string\n children: ReactNode\n /**\n * On the panel's surface \u2014 its width. **Not its padding**: see\n * `contentClassName`.\n */\n className?: string\n /**\n * The padding around the children, as a class. Default `p-2`, 8px \u2014 what a\n * panel of rows or a small form wants. **A toolbar wants `p-1`.**\n *\n * The padding is on the scrolling content, not on the panel, so a scrollbar\n * is drawn over it and hugs the panel's edge (D63). A padding class on\n * `className` therefore does not replace this one \u2014 it adds to it: that is\n * how every toolbar in a `Popover` grew 8px a side at `0.12.6`, measured 13px\n * from the panel's edge to the toolbar where it had been 5px (PLAN Finding\n * 60). Set it here.\n */\n contentClassName?: string\n /**\n * A cap on the scrolling box, as a class \u2014 `max-h-[360px]`. Without one the\n * panel grows to the room the positioner has, which is the right default for\n * a panel that is as tall as its content.\n *\n * **It cannot go on `className`.** That lands on the panel, and the panel's\n * children sit inside a `ScrollArea` whose viewport carries its own cap \u2014 so\n * a `max-h` on the panel is overrun by the viewport and the content draws\n * straight through the panel's border (measured with Peek's `/` menu,\n * 2026-09-12: a 400px panel with 559px of rows hanging out of it). The cap\n * belongs on the viewport, as `DialogShell` takes `bodyMaxHeight`.\n *\n * **When a panel needs one.** A panel taller than the room above *and* below\n * its anchor is not flipped by the positioner \u2014 it is moved to the side of\n * the anchor, which for a type-ahead over a caret is not where it belongs.\n * A cap keeps the choice between above and below.\n *\n * It replaces the available-height cap rather than adding to it, so a caller\n * that sets one owns it.\n */\n maxHeight?: string\n}\n\n/** The 4px between the panel and what it hangs from, and the 8px it keeps\n * clear of every screen edge \u2014 `Menu`'s numbers, because it is the same box. */\nconst GAP = 4\nconst VIEWPORT_PAD = 8\n\nexport function Popover({ trigger, anchor, align = 'left', side = 'bottom', open, onOpenChange, finalFocus, actionsRef, ariaLabel, children, className, contentClassName, maxHeight }: PopoverProps) {\n /* A rect is not an element, so it becomes a virtual anchor \u2014 the one shape\n Floating UI takes besides an element. */\n const anchorTarget = useMemo(() => {\n if (!anchor) return undefined\n if (anchor instanceof Element) return anchor\n return { getBoundingClientRect: () => anchor }\n }, [anchor])\n\n return (\n <BasePopover.Root\n open={open}\n onOpenChange={onOpenChange ? (next) => onOpenChange(next) : undefined}\n actionsRef={actionsRef}\n /* Non-modal: the page behind keeps its scrollbar, so opening a panel\n never shifts the layout, and a toolbar over a text selection must not\n take the page away from the person using it. */\n modal={false}\n >\n {/* Told the button's disabled state, or it writes `aria-disabled=\"false\"`\n over it and opens anyway (Finding 39). */}\n {trigger && <BasePopover.Trigger render={trigger} disabled={triggerDisabled(trigger)} />}\n <BasePopover.Portal>\n <BasePopover.Positioner\n anchor={anchorTarget}\n side={side}\n align={align === 'right' ? 'end' : align === 'center' ? 'center' : 'start'}\n sideOffset={GAP}\n collisionPadding={VIEWPORT_PAD}\n className=\"z-50 data-[anchor-hidden]:hidden\"\n >\n <BasePopover.Popup\n aria-label={ariaLabel}\n /*\n * From a trigger, focus lands on the first thing in the panel \u2014\n * the field, in the panel this component exists for \u2014 and goes\n * back to the trigger when it closes.\n *\n * From an anchor, it does not move at all. A panel with no trigger\n * appeared rather than being asked for, and the person is still in\n * the middle of what produced it: a toolbar over a text selection\n * that took the caret out of the text would end the edit it exists\n * to serve. Measured 2026-09-08: with focus moved into the panel,\n * the first control's tooltip opens on `:focus-visible` and eats\n * the Escape that should have closed the panel, and the caller's\n * re-read of the selection fights the panel's own dismissal \u2014 both\n * intermittently. Neither happens once focus stays put.\n *\n * The cost is stated on the page: an anchored panel cannot be\n * reached by keyboard, so what is in one must also be reachable\n * some other way.\n */\n initialFocus={trigger ? undefined : false}\n finalFocus={finalFocus}\n className={cn('min-w-[180px] outline-none p-0', className)}\n render={<MenuPanel />}\n >\n {/* As in Menu: the cap on the scrolling box, and the padding on the\n content rather than the panel, so the bar is drawn over the\n padding instead of 9px inside it (D63). A caller's `maxHeight`\n replaces the cap, and a caller's `contentClassName` the padding\n \u2014 see the props. */}\n <ScrollArea\n viewportClassName={maxHeight ?? 'max-h-[var(--available-height)]'}\n contentClassName={cn('flex flex-col p-2 [&>[role=separator]]:mx-0', contentClassName)}\n >\n {children}\n </ScrollArea>\n </BasePopover.Popup>\n </BasePopover.Positioner>\n </BasePopover.Portal>\n </BasePopover.Root>\n )\n}\n", "import type { ReactNode, Ref } from 'react'\nimport { Toolbar as BaseToolbar } from '@base-ui/react/toolbar'\nimport { cn } from './cn'\nimport { IconButton, type IconButtonProps } from './IconButton'\nimport { MenuPanel } from './Menu'\nimport { TextInput, type TextInputProps } from './TextInput'\n\n/**\n * A strip of controls that behaves as **one** control.\n *\n * New at Katerina's request, 2026-09-08, pulled forward from stage 6.\n *\n * **It exists because every strip in the suite is as many Tab stops as it has\n * buttons.** Ship's reaction row, Peek's composer strip and the editor's\n * formatting strip are each a hand-rolled `<div class=\"flex\">` of\n * `IconButton`s: eight buttons, eight stops, and no way to get past them but\n * through them. A toolbar is one stop \u2014 Tab in, arrow keys along, Tab out \u2014\n * which is what the `toolbar` role means and what Base UI's part implements.\n *\n * What it is not: a `Menu` (that is a list of *verbs* you open and choose\n * from, and it closes when you do) or a row of unrelated buttons (a form's\n * Cancel and Save are two separate answers, and each deserves its own stop).\n * A toolbar is a set of things you do *to* something that is still there.\n *\n * **It draws the box.** A toolbar floats over what it acts on \u2014 a card, a\n * paragraph, an image \u2014 so it carries the elevated surface that separates it\n * from that: the same `MenuPanel` a `Menu` draws, because a floating strip and\n * a floating list are the same box. Peek had built this twice and the two had\n * already drifted \u2014 `ConversationQuickMenu` is `rounded-sm` with `shadow-sm`\n * and a subtle border, `ReactionPicker` `rounded-lg` with `shadow-lg` and a\n * default one. There is one box now. `surface={false}` for a strip inside\n * something that already draws it.\n *\n * **The gap, stated: Home and End do nothing.** Base UI's composite has them\n * and its `Toolbar` does not switch them on (`enableHomeAndEndKeys`, read in\n * `useCompositeRoot`, not passed by `ToolbarRoot`) \u2014 so this page does not\n * claim them. The arrow keys wrap, which reaches both ends in one press of a\n * strip this size.\n */\nexport interface ToolbarProps {\n /**\n * Names the strip for assistive tech \u2014 \"Formatting\", \"Reactions\". A toolbar\n * is a landmark-ish grouping and is announced as a bare \"toolbar\" without\n * one, which says nothing when a page has two.\n */\n 'aria-label': string\n /** `horizontal` walks with \u2190 \u2192, `vertical` with \u2191 \u2193. Default horizontal. */\n orientation?: 'horizontal' | 'vertical'\n /** Whether the arrow keys wrap at the ends. Default true, as Base UI's. */\n loopFocus?: boolean\n /**\n * The elevated box around the strip. **On by default** \u2014 a toolbar floats,\n * and this is what floating looks like here.\n *\n * Off for a strip inside a surface that already draws one: a `Popover`, a\n * dialog, a card's own panel. Two boxes inside each other is the tell.\n */\n surface?: boolean\n children: ReactNode\n /** The row's own layout \u2014 its gap, its padding, its wrapping. */\n className?: string\n}\n\nexport function Toolbar({ 'aria-label': ariaLabel, orientation = 'horizontal', loopFocus = true, surface = true, children, className }: ToolbarProps) {\n const strip = (\n <BaseToolbar.Root\n aria-label={ariaLabel}\n orientation={orientation}\n loopFocus={loopFocus}\n className={cn('flex items-center gap-1', orientation === 'vertical' && 'flex-col', className)}\n >\n {children}\n </BaseToolbar.Root>\n )\n if (!surface) return strip\n /*\n The box WRAPS the strip rather than being composed onto it. `MenuPanel` is\n a flex COLUMN \u2014 right for a menu's rows, wrong for a row of controls \u2014 and\n Tailwind emits `flex-col` after `flex-row`, so a merged class list would\n stand the toolbar on its end whatever order the classes arrived in. One\n extra element, and the box keeps its single definition.\n\n `p-1` is the tighter of Peek's two paddings and the right one for a strip\n of 24px controls; `MenuPanel`'s own `p-2` is a menu's, where the rows run\n the full width.\n */\n return <MenuPanel className=\"w-fit p-1\">{strip}</MenuPanel>\n}\n\n/**\n * One control in the strip. It is this package's `IconButton` \u2014 the same\n * square, the same variants, the same `tooltip` and `disabledReason` \u2014 joined\n * to the toolbar's roving focus.\n *\n * A disabled button **keeps its place in the walk**: a strip whose controls\n * come and go from the arrow keys as their state changes is a strip you cannot\n * learn. It is also what lets a `disabledReason` be read.\n *\n * **It must be inside a `Toolbar`.** Base UI throws otherwise \u2014\n * \"ToolbarRootContext is missing\" \u2014 because a part with no strip has no walk\n * to join. A control beside a strip is an `IconButton`.\n */\nexport interface ToolbarButtonProps extends IconButtonProps {\n ref?: Ref<HTMLButtonElement>\n}\n\nexport function ToolbarButton({ ref, disabled, disabledReason, ...props }: ToolbarButtonProps) {\n return (\n <BaseToolbar.Button\n ref={ref}\n /*\n The part has to be told, not only the button inside it. The toolbar\n keeps its own map of which items are disabled \u2014 it is what decides\n where an arrow key lands \u2014 and a `Toolbar.Button` that renders a\n disabled `IconButton` without saying so leaves the two disagreeing:\n measured, the element came out `aria-disabled=\"false\"` over a button\n that was disabled.\n */\n disabled={disabled || !!disabledReason}\n /* Keep it in the walk either way. A strip whose controls come and go\n from the arrow keys as their state changes is a strip you cannot\n learn, and a `disabledReason` you cannot reach is a reason nobody\n reads. */\n focusableWhenDisabled\n render={<IconButton disabled={disabled} disabledReason={disabledReason} {...props} />}\n />\n )\n}\n\n/**\n * A text field inside the strip \u2014 the link editor in a selection toolbar.\n *\n * The arrow keys belong to the field while it has focus, which is the whole\n * point: \u2190 and \u2192 move the caret. The toolbar's walk resumes at Tab. That is\n * Base UI's `Toolbar.Input`, and it is why a field in a toolbar cannot just be\n * a `TextInput` dropped in the row.\n */\nexport type ToolbarInputProps = TextInputProps\n\nexport function ToolbarInput({ size, ...props }: ToolbarInputProps) {\n // `size` is TextInput's own (default or small), not the native attribute\n // Base UI's part would take, so it goes to the field it draws.\n return <BaseToolbar.Input render={<TextInput size={size} />} {...props} />\n}\n\n/**\n * The hairline between two groups of controls. It is `Divider`'s rule and\n * `Toolbar.Separator`'s role, so a screen reader hears the grouping a sighted\n * reader sees.\n */\nexport function ToolbarSeparator({ className }: { className?: string }) {\n return (\n <BaseToolbar.Separator\n orientation=\"vertical\"\n /* `shrink-0` on a hairline in a flex row, always: without it a 1px rule\n renders at 0px and nobody notices (the package's own trap table). */\n className={cn('mx-1 h-4 w-px shrink-0 bg-border-default', className)}\n />\n )\n}\n", "import { cn } from './cn'\nimport { Toolbar, ToolbarButton } from './Toolbar'\n\n/**\n * The reactions on offer, to choose one from \u2014 Peek's `ReactionPicker`\n * (2026-09-03), moved in at Katerina's request (2026-09-08).\n *\n * **`Reaction` is the answer; this is the question.** A `Reaction` is a pill\n * that says an emoji, a count and whether it is yours. This is the row you\n * pick from before any of that exists, and both apps need it: Peek draws one\n * from a card's action strip (`ConversationQuickMenu`), and Ship draws the\n * same row in `Reactions.tsx` from its own vocabulary.\n *\n * **It is a `Toolbar`** \u2014 icon buttons holding emoji, on the strip's own\n * elevated box \u2014 so the whole row is one Tab stop and the arrow keys walk it.\n * Peek's was five separate stops.\n *\n * ## What stays with the app\n *\n * **Which emoji, and what each one means.** Peek offers five and names them\n * (\"Makes sense\", \"Agree\", \"Thank you\", \"Let's go!\", \"Congrats\"); Ship will\n * offer its own. A vocabulary is product knowledge, so it arrives as\n * `options` \u2014 and the name is not optional, because the emoji is decorative\n * here for the same reason it is on `Reaction`: a glyph read aloud is noise,\n * and its spoken name differs per screen reader.\n *\n * ## Picking, and nothing else\n *\n * **It does not show which reactions are yours.** That is `Reaction`'s job \u2014\n * the pill with the count and the accent fill \u2014 and the two live in different\n * places: the reactions *made* sit in a row on the card, and this row is what\n * opens over it when you go to add one. A picker that also reported state\n * would be two components wearing one name (Katerina, 2026-09-08).\n */\nexport interface ReactionOption {\n /** The emoji itself. Drawn decoratively \u2014 `label` names the control. */\n emoji: string\n /** What this reaction *means*: \"Makes sense\", \"Congrats\". Required, and it\n * is what a screen reader and the tooltip both say. */\n label: string\n}\n\nexport interface ReactionPickerProps {\n /** What is on offer, in the order it is drawn. The app's vocabulary. */\n options: ReactionOption[]\n onSelect: (emoji: string) => void\n /** Names the row. Default \"Reactions\". */\n 'aria-label'?: string\n /**\n * The elevated box around the row, from `Toolbar`. **On by default**, which\n * is how a picker floats over a card. Off inside a `Popover`, which draws\n * that box already.\n */\n surface?: boolean\n /** The row's layout \u2014 its gap, its padding. */\n className?: string\n}\n\nexport function ReactionPicker({ options, onSelect, 'aria-label': ariaLabel = 'Reactions', surface = true, className }: ReactionPickerProps) {\n return (\n <Toolbar aria-label={ariaLabel} surface={surface} className={cn('gap-0.5', className)}>\n {options.map((option) => (\n /*\n A `ToolbarButton`, not a button in a tooltip wrapper. The wrapper\n would become the toolbar's item and the button inside it would never\n join the walk \u2014 and it is unnecessary since stage 4, because an\n `IconButton` with a `tooltip` IS the trigger.\n\n The tooltip names it for a pointer, `aria-label` for everything else,\n and both say the meaning rather than the glyph.\n */\n <ToolbarButton\n key={option.emoji}\n aria-label={option.label}\n tooltip={option.label}\n onClick={() => onSelect(option.emoji)}\n /* Peek's geometry, verbatim: a 28px square rather than the\n IconButton's 24, and the emoji at 18px \u2014 larger than a `Reaction`\n pill's 16, because here the emoji is the whole control. */\n className=\"size-7 text-h3 leading-none\"\n >\n {/* Decorative, as on `Reaction`: the control is named above. */}\n <span aria-hidden=\"true\">{option.emoji}</span>\n </ToolbarButton>\n ))}\n </Toolbar>\n )\n}\n", "import type { ReactNode } from 'react'\nimport { PreviewCard as BasePreviewCard } from '@base-ui/react/preview-card'\nimport { cn } from './cn'\nimport { ScrollArea } from './ScrollArea'\nimport { MenuPanel } from './Menu'\n\n/**\n * More of a thing, on hover \u2014 a card beside a row that is only a snippet.\n * New at stage 4 (2026-09-07).\n *\n * **It exists because Peek's Screener preview is this, hand-written**: its own\n * `createPortal`, its own \"prefer the right, flip left if it would run off\n * screen\" arithmetic against `window.innerWidth`, and its own clamp against\n * the bottom edge (`ScreenerPreviewCard.tsx`). Floating UI does all three.\n *\n * **Not a Tooltip.** A tooltip is a word for a control, is\n * `pointer-events: none`, and may hold nothing you can reach. A preview card\n * holds *content* \u2014 faces, text, a link \u2014 and you can move the pointer into\n * it, which is what a preview is for. If what you have is a label, use\n * `WithTooltip`.\n *\n * The surface is the package's elevated panel, the same one a `Menu` draws, so\n * there is one definition of that box. Width, height and internal rhythm are\n * the caller's: a preview of a conversation is not the size of a preview of a\n * person.\n */\nexport interface PreviewCardProps {\n /** What the card holds. Rendered only while it is open. */\n content: ReactNode\n /** The row, name or avatar the card previews. */\n children: ReactNode\n /** Which side of the trigger to prefer. It flips when that side has no room. Default right. */\n side?: 'top' | 'bottom' | 'left' | 'right'\n /** Before it opens, in ms. Long enough that crossing a list does not flash a card at every row. */\n delay?: number\n /** After the pointer leaves, in ms \u2014 the grace that lets you cross the gap into the card. */\n closeDelay?: number\n /** On the card's surface: its width, its padding, a max height. */\n className?: string\n /** Extra classes on the trigger wrapper \u2014 e.g. `block w-full` for a row. */\n wrapperClassName?: string\n}\n\n/** The card's own numbers: 12px from the trigger, 8px clear of every screen\n * edge. Peek's hand-written preview used 12 and 12; the 8 is the package's\n * viewport margin, shared with every other floating surface. */\nconst GAP = 12\nconst VIEWPORT_PAD = 8\n/** 350ms, which is Peek's number: `ScreenerItem`'s `HOVER_DELAY_MS`. Long\n * enough not to flash a card at every row while a pointer crosses a list,\n * short enough to feel like an answer. Taken rather than invented, because\n * this component exists to replace that one. */\nconst OPEN_DELAY = 350\n/** The diagonal from the row to the card has to survive. Peek's has no grace\n * at all \u2014 it closes on `mouseleave` \u2014 because its card is\n * `pointer-events: none` and there is nothing to cross to. Which also means\n * **the `overflow-y-auto` on its 300px-capped card can never be scrolled**;\n * here the card can be reached, so it can. */\nconst CLOSE_DELAY = 200\n\nexport function PreviewCard({ content, children, side = 'right', delay = OPEN_DELAY, closeDelay = CLOSE_DELAY, className, wrapperClassName }: PreviewCardProps) {\n return (\n <BasePreviewCard.Root>\n {/* The part renders an `<a>` by default, which a row is not; `render`\n makes it the wrapper this component has always been, and the trigger\n inside keeps whatever element it already is. */}\n <BasePreviewCard.Trigger\n delay={delay}\n closeDelay={closeDelay}\n render={<span className={cn('inline-flex', wrapperClassName)} />}\n >\n {children}\n </BasePreviewCard.Trigger>\n <BasePreviewCard.Portal>\n <BasePreviewCard.Positioner\n side={side}\n align=\"start\"\n sideOffset={GAP}\n collisionPadding={VIEWPORT_PAD}\n className=\"z-50 data-[anchor-hidden]:hidden\"\n >\n <BasePreviewCard.Popup\n className={cn('w-[360px] p-3 outline-none', className)}\n render={<MenuPanel />}\n >\n <ScrollArea viewportClassName=\"max-h-[calc(min(300px,var(--available-height))_-_1.5rem)]\" contentClassName=\"flex flex-col gap-3 [&>*]:shrink-0\">\n {content}\n </ScrollArea>\n </BasePreviewCard.Popup>\n </BasePreviewCard.Positioner>\n </BasePreviewCard.Portal>\n </BasePreviewCard.Root>\n )\n}\n", "import type { ReactNode } from 'react'\nimport { cn } from './cn'\n\n/**\n * The icon rail: a 64px strip of RailItems (the shell of Peek's NavRail,\n * 2026-09-02 \u2014 its entries and their routes stayed in the app).\n *\n * It has no border and no surface of its own \u2014 it stands on the app\n * background, beside whatever card or column the app draws. Collapsing\n * is the caller's: the rail does not know about the burger, it only gets\n * given less room (or none) to stand in. Desktop only.\n */\nexport interface RailProps {\n /** Names the navigation region for assistive tech. */\n 'aria-label'?: string\n children: ReactNode\n className?: string\n}\n\nexport function Rail({ 'aria-label': ariaLabel = 'Navigation', children, className }: RailProps) {\n return (\n <nav aria-label={ariaLabel} className={cn('flex w-16 shrink-0 flex-col items-start gap-2 px-2 py-3', className)}>\n {children}\n </nav>\n )\n}\n", "import type { ComponentPropsWithoutRef, ReactNode } from 'react'\nimport { cn } from './cn'\n\n/**\n * One tile of an icon rail: the icon in its hover tile, a 9px label\n * under it (Peek's NavItem, 2026-09-02, verbatim \u2014 minus the router).\n *\n * The original computed its own active state from the route and rendered\n * a router link; neither belongs in a shared component. This one takes\n * `active` and renders an anchor \u2014 a router app keeps a thin wrapper\n * that does its own matching and intercepts the click.\n *\n * Active is `bg-selected` on the tile \u2014 some themes also give the icon\n * their interactive colour.\n */\nexport interface RailItemProps extends Omit<ComponentPropsWithoutRef<'a'>, 'href'> {\n href: string\n /** 16px, stroke 1.5. */\n icon: ReactNode\n label: string\n active?: boolean\n className?: string\n}\n\nexport function RailItem({ href, icon, label, active = false, className, ...props }: RailItemProps) {\n return (\n <a\n href={href}\n aria-current={active ? 'page' : undefined}\n className={cn('group flex w-full shrink-0 flex-col items-center gap-0.5 px-2 py-0.5', className)}\n {...props}\n >\n <div\n className={cn(\n 'flex items-center justify-center rounded-lg p-2 transition-colors',\n active ? 'bg-bg-selected' : 'group-hover:bg-bg-hover',\n )}\n >\n <span\n className={cn(\n 'flex items-center transition-colors',\n active ? 'text-text-primary signal:text-[color:var(--text-interactive)]' : 'text-text-secondary',\n )}\n >\n {icon}\n </span>\n </div>\n <span className={cn('text-center text-menu', active ? 'text-text-primary' : 'text-text-secondary')}>\n {label}\n </span>\n </a>\n )\n}\n", "import type { ReactNode } from 'react'\nimport { cn } from './cn'\nimport { ScrollArea } from './ScrollArea'\n\n/**\n * The navigation column: 240px, a hairline on its right, the surface\n * behind it, scrolling independently of the content beside it (the shell\n * of Ship's Sidebar, 2026-09-02 \u2014 its contents stayed in the app).\n *\n * What goes inside is the caller's: NavItem rows, a SectionLabel heading\n * in a 32px row over a group. Desktop only \u2014 there is no narrow-screen\n * drawer.\n */\nexport interface SidebarProps {\n /** Names the navigation region for assistive tech. */\n 'aria-label'?: string\n children: ReactNode\n className?: string\n}\n\nexport function Sidebar({ 'aria-label': ariaLabel = 'Workspace', children, className }: SidebarProps) {\n return (\n <nav\n aria-label={ariaLabel}\n className={cn('flex w-60 shrink-0 flex-col border-r border-border-default bg-bg-surface', className)}\n >\n {/* The rows scroll in a ScrollArea: the bar takes no width, so the\n column's padding reads the same with a long list as with a short\n one (Katerina, 2026-09-08). The padding and the gap are the box's. */}\n <ScrollArea className=\"min-h-0 flex-1\" contentClassName=\"flex flex-col gap-px px-2.5 py-3\">\n {children}\n </ScrollArea>\n </nav>\n )\n}\n", "import type { ReactNode } from 'react'\nimport { cn } from './cn'\n\n/**\n * A labelled property row \u2014 \"Status\", \"Lead\", and whatever else a page\n * declares (2026-09-01). Both apps carried one, and they had already\n * drifted: Peek's label was `text-xs` \u2014 a Tailwind default with the wrong\n * line-height, not the caption token \u2014 and Ship's had grown a layout Peek's\n * lacked. One component, both layouts, the token:\n *\n * - `row` \u2014 Peek's: a 68px label column that lines the values up into a\n * column of their own; `text-secondary` rather than muted because a\n * property label is content, not a placeholder.\n * - `stacked` \u2014 Ship's rail: the label above a full-width control. The label\n * is the `menu` token (9px / 115% / 500), uppercase and letter-spaced \u2014\n * its own voice, deliberately not SectionLabel's (unmerged, Katerina\n * 2026-09-01). A literal string, not merged, so the token size survives.\n *\n * **A name and its value, in the markup made for it** (stage 6 of the\n * migration, 2026-09-14). Base UI has no part for this, so it is HTML's own:\n * a `<dl>` holding one `<dt>` (the label) and one `<dd>` (the value), so a\n * screen reader hears the label as the name of what follows rather than as a\n * loose word beside it. Each property is a list of one, which needs no\n * wrapper from the caller.\n */\nexport interface PropertyProps {\n label: string\n layout?: 'row' | 'stacked'\n children: ReactNode\n className?: string\n}\n\n/**\n * The value's `<dd>` draws no box of its own (`display: contents`): whatever\n * the caller passes stays a child of the row or the column, exactly as it was\n * before the `<dd>` existed \u2014 a control that fills the row (`flex-1`) still\n * fills it, and two values still sit the row's gap apart.\n */\nconst VALUE_CLASSES = 'contents'\n\nexport function Property({ label, layout = 'row', children, className }: PropertyProps) {\n if (layout === 'stacked') {\n return (\n // gap-3 \u2014 the one label-above-content distance (Katerina, 2026-09-01):\n // Peek's topic-details sections already sit at 12px, Ship's rail was\n // 6px, and unifying means the bigger, calmer one.\n <dl className={cn('flex flex-col gap-3', className)}>\n <dt className=\"text-menu uppercase tracking-widest text-text-secondary\">{label}</dt>\n <dd className={VALUE_CLASSES}>{children}</dd>\n </dl>\n )\n }\n return (\n <dl className={cn('flex items-center gap-2', className)}>\n <dt className=\"w-[68px] shrink-0 text-caption text-text-secondary\">{label}</dt>\n <dd className={VALUE_CLASSES}>{children}</dd>\n </dl>\n )\n}\n", "import type { ButtonHTMLAttributes, ReactNode } from 'react'\nimport { Toggle } from '@base-ui/react/toggle'\nimport { cn } from './cn'\n\n/**\n * A reaction: an emoji, how many people chose it, and whether you are one of\n * them.\n *\n * **This exists because both apps had already built it, and neither could use\n * the components that were here.** A reaction is a pill with a count that\n * toggles \u2014 `Chip` is a pill and rules itself out for anything clickable,\n * `Button` is clickable and is a 6px-radius rectangle, and `IconButton` has\n * nowhere to put the count. So Peek re-typed Chip's class list with a border\n * added, and Ship reached for a small Button with the count as its label. The\n * two apps' reactions do not look alike today, and neither is what was drawn.\n *\n * ## The one thing it does that nothing else here does\n *\n * **It says the reaction is yours.** That is the state a reaction has and a\n * chip does not: `pressed` fills it with the accent's muted tint and gives it\n * an accent edge, so a glance separates \"two people, one of them me\" from \"two\n * people\". Ship approximated it as `outlined` versus `muted` \u2014 measured at a\n * 1px hairline against no border at all \u2014 which is legible and is not the\n * signal Peek's accent fill gives.\n *\n * On Base UI's `Toggle` since stage 6 of the migration (2026-09-14): a native\n * `<button>` with `aria-pressed` and `data-pressed`, so the state reaches\n * assistive tech as a toggle rather than as a colour. Base UI writes both;\n * this component used to write the first by hand.\n *\n * ## `aria-label` is required, and the reason is specific\n *\n * The emoji is decorative here \u2014 it is `aria-hidden`, because a glyph read\n * aloud is noise and its spoken name differs per screen reader \u2014 and the only\n * visible text is the count. Without a label the control is announced as\n * **\"2\"**, which is what Ship shipped and its own tests caught. Pass the\n * meaning and the count: `\"Makes sense, 2\"`.\n *\n * ## Geometry\n *\n * Chip's pill, at a control's height: fully rounded, 24px to match `Button`\n * `small`, the same 8px horizontal padding, the `chip` type token for the\n * count so it sits at 11px/500 like every other count in the system.\n */\nexport interface ReactionProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'children' | 'type' | 'value'> {\n /** The emoji, drawn decoratively \u2014 name the control with `aria-label`. */\n emoji: ReactNode\n /** How many people reacted. Drawn as it is. A count of `0` is not a reaction: do not render one. */\n count: number\n /**\n * You are one of them: the accent tint and edge, and `aria-pressed`. The\n * caller owns it \u2014 a press calls `onClick` and the pill keeps showing this\n * value until the caller changes it.\n */\n pressed?: boolean\n /**\n * Names the control. **Required** \u2014 the emoji is decorative and the count is\n * the only visible text, so without this it is announced as a bare number.\n */\n 'aria-label': string\n}\n\nexport function Reaction({ emoji, count, pressed = false, className, ...props }: ReactionProps) {\n return (\n <Toggle\n // Controlled: a reaction is yours when the data says so, not when the\n // pill was last pressed \u2014 a write can fail, and another client can\n // change it.\n pressed={pressed}\n className={cn(\n // Chip's pill at a control's height, so a reaction and a status chip\n // read as the same family \u2014 24px matches Button `small`.\n 'inline-flex h-6 items-center justify-center gap-1.5 rounded-full px-2',\n 'border transition-colors',\n 'disabled:cursor-not-allowed disabled:opacity-50',\n pressed\n ? /* In the ship theme the accent on its own wash measures 2.70:1\n (Finding 4), and the count was the thing that vanished\n (Katerina, 2026-09-08). The number reads in the text colour\n there; the accent keeps the edge and the fill. Signal keeps\n Peek's blue-on-wash, which reads. */\n 'border-accent-primary bg-accent-muted text-accent-primary hover:border-accent-hover ship:text-text-primary'\n : 'border-border-default bg-bg-inset text-text-primary hover:border-border-strong hover:bg-bg-hover',\n className,\n )}\n {...props}\n >\n {/* Decorative: the control is named by `aria-label`, and a glyph read\n aloud is noise. 16px so the emoji is legible at chip scale. */}\n <span aria-hidden=\"true\" className=\"shrink-0 text-body-1 leading-none\">\n {emoji}\n </span>\n {/* `text-chip` stands alone here, so there is nothing to merge it with.\n The old note said it must NEVER be merged; that stopped being true\n when `cn()` was taught the ramp \u2014 `chip` is in it, and `cn.test.ts`\n pins that. */}\n <span className=\"text-chip signal:font-mono signal:text-small signal:font-semibold signal:tabular-nums\">\n {count}\n </span>\n </Toggle>\n )\n}\n", "import { type InputHTMLAttributes } from 'react'\nimport { Input } from '@base-ui/react/input'\nimport { cn } from './cn'\nimport { Kbd } from './Kbd'\n\n/**\n * Peek's SearchInput (2026-09-01), verbatim: an inset field with a hairline\n * border that strengthens on focus, and an optional keyboard hint at the\n * right edge \u2014 under Signal, the hint becomes the mono `kbd` chip with the\n * thicker bottom edge.\n *\n * The one product string \u2014 Peek's default placeholder \"Search Peek...\" \u2014\n * stays in Peek; the default here says only \"Search\u2026\".\n *\n * In Peek's top bar this is a launcher affordance rather than a live field:\n * the input is `pointer-events-none` and clicking the surround opens the\n * command launcher. The component is the same either way.\n *\n * On Base UI's `Input` since stage 3 of the migration (2026-09-07), so a\n * search field inside a `Field` is labelled by it without the caller wiring\n * an id. The surround stays ours: the border, the focus-within rule and the\n * `Kbd` hint are this component's, not the input's.\n */\nexport interface SearchInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'className'> {\n /** A keyboard hint drawn at the right edge, e.g. \"Ctrl+K\". */\n shortcut?: string\n className?: string\n}\n\nexport function SearchInput({ shortcut, className, placeholder = 'Search\u2026', ...props }: SearchInputProps) {\n return (\n <div\n className={cn(\n 'flex gap-2 items-center px-3 py-2 rounded-lg',\n 'bg-bg-inset border border-border-default',\n // Hover strengthens the border as it does on every other field (16 September). Here that\n // is also the focus look, which was the stronger border before hover existed.\n 'hover:border-border-strong focus-within:border-border-strong transition-colors',\n className,\n )}\n >\n <Input\n className=\"flex-1 min-w-0 bg-transparent text-input-value text-text-primary placeholder:text-text-muted outline-none\"\n placeholder={placeholder}\n {...props}\n />\n {shortcut && <Kbd>{shortcut}</Kbd>}\n </div>\n )\n}\n", "import type { ReactNode } from 'react'\nimport { IconChevronRight } from '@tabler/icons-react'\nimport { useRender } from '@base-ui/react/use-render'\nimport { cn } from './cn'\nimport { IconButton } from './IconButton'\nimport { SectionLabel } from './SectionLabel'\n\n/**\n * The 32px row a section starts with (Peek's SectionHeader, 2026-09-01):\n * a SectionLabel, an optional collapse chevron that makes the title the\n * toggle, and actions that appear while the row is hovered or focused, as\n * IconButtons with tooltips.\n *\n * Three things changed on 2026-09-09, for `CollapsibleSection`:\n *\n * - **The title is a button when it toggles.** It was a `div` with an\n * `onClick`, so the keyboard could not open or close a section at all.\n * The button fills the row up to the actions, so the whole row is still\n * the hit target; the actions sit beside it rather than inside it,\n * because a button inside a button is invalid HTML \u2014 and was why every\n * action had to stop its click from also toggling.\n * - **The hover is CSS.** The fill and the actions' reveal were React\n * state (`isHovered`), which the README forbids \u2014 a mount cannot stay in\n * step with a transition. `group-hover` now; the actions are in the row\n * at `opacity-0` until hovered or focused, so a keyboard user reaches\n * them too.\n * - **`render`** lets `CollapsibleSection` hand in Base UI's\n * `Collapsible.Trigger` as the title button \u2014 one header for the\n * hand-held section (`isExpanded` / `onToggle`) and the Base UI one,\n * with no second click handler.\n *\n * One API change from Peek's original, safe because no call site used the\n * old shape yet: the two hard-wired action slots (add, sort) became\n * `actions` \u2014 the caller brings the icon, the tooltip and the handler.\n */\nexport interface SectionAction {\n /** 16px, stroke 1.5. */\n icon: ReactNode\n tooltip: string\n onClick: () => void\n}\n\nexport interface SectionHeaderProps {\n title: string\n /** Collapsible: draws the chevron and makes the title a button that toggles. */\n chevron?: boolean\n isExpanded?: boolean\n onToggle?: () => void\n /**\n * Beside the title and always visible, before the actions \u2014 a count.\n *\n * The actions come and go with the hover; this does not, because a count is\n * information rather than an affordance. Peek's Screener header carries its\n * number as a `Chip` here, and was the last hand-drawn folding header in the\n * app for want of the slot (ADOPTION B23).\n */\n trailing?: ReactNode\n /** Right-aligned, in the order given. */\n actions?: SectionAction[]\n /** `hover` reveals the actions while the row is hovered or focused; `always` keeps them. */\n showActions?: 'hover' | 'always'\n /**\n * What the title renders as, in Base UI's manner. A plain button with\n * `onToggle` by default; `CollapsibleSection` hands in `Collapsible.Trigger`.\n */\n render?: useRender.RenderProp\n className?: string\n}\n\nexport function SectionHeader({ title, chevron = false, isExpanded = true, onToggle, trailing, actions, showActions = 'hover', render, className }: SectionHeaderProps) {\n const titleElement = useRender({\n render: render ?? (chevron ? <button type=\"button\" onClick={onToggle} aria-expanded={isExpanded} /> : <span />),\n props: {\n // `text-left`: a button centres its text. `h-full` and `flex-1`: the\n // whole row up to the actions is the hit target, as it was when the\n // row itself carried the click.\n className: 'flex h-full min-w-0 flex-1 items-center gap-1 text-left',\n children: (\n <>\n {chevron && (\n <IconChevronRight\n size={12}\n stroke={1.5}\n className={cn('shrink-0 text-text-secondary transition-transform duration-150', isExpanded && 'rotate-90')}\n />\n )}\n <SectionLabel>{title}</SectionLabel>\n </>\n ),\n },\n })\n\n return (\n <div\n className={cn(\n 'group flex h-[32px] items-center gap-1 rounded-lg px-2 transition-colors',\n // The fill says \"this does something\": a row with a toggle or actions\n // lights up, a fixed heading over rows does not (2026-09-09, the\n // Sidebar's fixed group).\n (chevron || (actions && actions.length > 0)) && 'hover:bg-bg-hover',\n className,\n )}\n >\n {titleElement}\n {/* Outside the title button, like the actions: a chip inside a button\n would be part of the button's accessible name and part of its hit\n target, and the count is neither. */}\n {trailing != null && <div className=\"flex shrink-0 items-center\">{trailing}</div>}\n {actions && actions.length > 0 && (\n <div\n // `-mr-1`: an IconButton is 24px around a 16px icon, so at the row's\n // `px-2` its icon ended 12px from the edge while a NavItem's number\n // ends 8px from it; pulled 4px, the icon and the number share a\n // right edge (Katerina, 2026-09-09; measured 4px off, then 0).\n className={cn(\n '-mr-1 flex shrink-0 items-center gap-1',\n showActions === 'hover' && 'opacity-0 transition-opacity focus-within:opacity-100 group-hover:opacity-100',\n )}\n >\n {actions.map((action) => (\n <IconButton key={action.tooltip} tooltip={action.tooltip} aria-label={action.tooltip} onClick={action.onClick}>\n {action.icon}\n </IconButton>\n ))}\n </div>\n )}\n </div>\n )\n}\n", "import { useState, type ReactNode } from 'react'\nimport { Collapsible } from '@base-ui/react/collapsible'\nimport { cn } from './cn'\nimport { SectionHeader, type SectionAction } from './SectionHeader'\n\n/**\n * A section that opens and closes: a `SectionHeader` whose title is the\n * toggle, and the rows under it, sliding shut and open (Katerina,\n * 2026-09-09, for the sidebars of both apps). On Base UI's `Collapsible`\n * (D6): the state, the title button's `aria-expanded` and `aria-controls`,\n * Enter and Space, the panel's height for the slide, and `hiddenUntilFound`\n * \u2014 the browser's find-in-page can find a row inside a closed section and\n * open it, because the rows stay in the DOM while closed.\n *\n * Three ways to hold the state, from least to most work for the caller:\n *\n * - none: `defaultOpen` (open unless told otherwise), kept while mounted;\n * - `storageKey`: the same, and this browser remembers it under that key\n * across reloads \u2014 one prop for a sidebar that stays how you left it;\n * - `open` + `onOpenChange`: the caller owns it.\n *\n * A closed section stays closed whatever is selected inside it (her\n * ruling, 2026-09-09) \u2014 the reader closed it.\n */\nexport interface CollapsibleSectionProps {\n title: string\n /** Open unless told otherwise. */\n defaultOpen?: boolean\n /** The caller owns the state. */\n open?: boolean\n onOpenChange?: (open: boolean) => void\n /** Remember open or closed in this browser, under this key. The app prefixes it. */\n storageKey?: string\n /** Beside the title and always visible, before the actions \u2014 a count. `SectionHeader`'s. */\n trailing?: ReactNode\n /** Beside the title, revealed on hover or focus \u2014 `SectionHeader`'s. */\n actions?: SectionAction[]\n showActions?: 'hover' | 'always'\n /** The rows. */\n children: ReactNode\n /** On the section's box: `mt-2` between groups, `shrink-0` in a scrolling column. */\n className?: string\n /** On the box the rows sit in: the gap between them. */\n contentClassName?: string\n}\n\nconst OPEN = 'open'\nconst CLOSED = 'closed'\n\nfunction readStored(key: string | undefined): boolean | undefined {\n if (!key) return undefined\n try {\n const value = window.localStorage.getItem(key)\n return value === OPEN ? true : value === CLOSED ? false : undefined\n } catch {\n return undefined\n }\n}\n\nfunction writeStored(key: string | undefined, open: boolean) {\n if (!key) return\n try {\n window.localStorage.setItem(key, open ? OPEN : CLOSED)\n } catch {\n // A browser that blocks storage still gets a working section; it forgets.\n }\n}\n\nexport function CollapsibleSection({ title, defaultOpen = true, open: openProp, onOpenChange, storageKey, trailing, actions, showActions, children, className, contentClassName }: CollapsibleSectionProps) {\n const [openState, setOpenState] = useState(() => readStored(storageKey) ?? defaultOpen)\n const open = openProp ?? openState\n const setOpen = (next: boolean) => {\n setOpenState(next)\n writeStored(storageKey, next)\n onOpenChange?.(next)\n }\n return (\n <Collapsible.Root open={open} onOpenChange={setOpen} className={cn('flex flex-col', className)}>\n <SectionHeader title={title} chevron isExpanded={open} trailing={trailing} actions={actions} showActions={showActions} className=\"shrink-0\" render={<Collapsible.Trigger />} />\n {/* The slide: Base UI measures the panel and writes its height to a\n variable \u2014 `auto` again once the slide ends, so rows that arrive\n later are not clipped \u2014 and the panel is 0 high on its opening frame\n and its closing frame. The Tooltip's idiom (D26): the data attributes\n are Base UI's, the transition is ours, and none under\n prefers-reduced-motion. */}\n <Collapsible.Panel\n hiddenUntilFound\n className=\"h-[var(--collapsible-panel-height)] overflow-hidden transition-[height] duration-150 ease-out motion-reduce:transition-none data-[starting-style]:h-0 data-[ending-style]:h-0\"\n >\n <div className={cn('flex flex-col', contentClassName)}>{children}</div>\n </Collapsible.Panel>\n </Collapsible.Root>\n )\n}\n", "import type { ReactNode } from 'react'\nimport { Tabs as BaseTabs } from '@base-ui/react/tabs'\nimport { cn } from './cn'\n\n/**\n * A row of tabs. Ship's Tabs (2026-09-01), which was Peek's TopicTabs with\n * the topic-specific ids taken out; on Base UI Tabs since stage 1 of the\n * migration (2026-09-06), which is where the keyboard comes from: one Tab\n * stop for the row, arrow keys between the tabs.\n *\n * A selected tab is a neutral fill (bg-active), not the accent tint \u2014\n * Katerina's ruling (2026-08-27), extended to every app (2026-09-01). Two\n * sizes: `default` is 14px, a little more room; `small` is Peek's own\n * geometry (its inline `fontSize: 12` was the caption token spelled by hand).\n *\n * A tab's count is the sidebar's number, not a chip (Katerina, 2026-09-01,\n * superseding the chip ruling of 2026-08-26): mono, caption size, muted,\n * tabular \u2014 the one way a number sits beside a label everywhere. A count of\n * `0` is still drawn (\"Assigned to me 0\" is an answer); an absent count draws\n * nothing.\n */\nexport interface TabDef<T extends string> {\n id: T\n label: string\n /** Shown as a muted mono number after the label. Absent draws nothing; 0 is drawn. */\n count?: number\n /** 16px, stroke 1.5. */\n icon?: ReactNode\n}\n\nexport interface TabsProps<T extends string> {\n tabs: TabDef<T>[]\n active: T\n onChange: (id: T) => void\n /** `default` 14px; `small` 12px, the denser geometry. */\n size?: 'default' | 'small'\n /**\n * Names the row for assistive tech \u2014 \"Views\", \"Filters\".\n *\n * A `tablist` with no name is announced as a bare \"tab list\", which says\n * nothing and says the same nothing twice on a page with two of them\n * (measured 2026-09-08: no name, and no way to give one). Where a visible\n * heading already names it, point at that with `aria-labelledby` instead.\n */\n 'aria-label'?: string\n 'aria-labelledby'?: string\n /** Lands on the outer box, around the row. */\n className?: string\n}\n\nexport function Tabs<T extends string>({ tabs, active, onChange, size = 'default', className, ...aria }: TabsProps<T>) {\n return (\n <BaseTabs.Root\n value={active}\n onValueChange={(value, details) => {\n // Only a person's choice reaches the caller. Base UI also reports its\n // own fallbacks (an `active` that matches no tab), and those carry\n // `null`, which is not a T.\n if (details.reason === 'none') onChange(value as T)\n }}\n className={className}\n >\n <BaseTabs.List\n activateOnFocus\n aria-label={aria['aria-label']}\n aria-labelledby={aria['aria-labelledby']}\n className=\"flex items-center gap-2\"\n >\n {tabs.map((tab) => (\n <BaseTabs.Tab\n key={tab.id}\n value={tab.id}\n className={(state) =>\n cn(\n 'flex cursor-pointer items-center transition-colors',\n // gap: default is Ship's 6px; small keeps Peek's original 4px, or\n // \"small is Peek's geometry\" stops being true.\n size === 'default' ? 'gap-1.5 rounded-md px-2 py-1 text-body-2' : 'gap-1 rounded px-1.5 py-0.5 text-caption',\n state.active ? 'bg-bg-active text-text-primary' : 'text-text-secondary hover:bg-bg-hover',\n )\n }\n >\n {tab.icon}\n {/* Label and count share a baseline: a smaller text centred as a box\n (items-center) floats above the label's baseline \u2014 the digits\n read as riding high. Baseline alignment is what makes two sizes\n sit on one line. */}\n <span className=\"flex items-baseline\">\n {tab.label}\n {tab.count !== undefined ? (\n <span className=\"ml-2.5 font-mono text-caption tabular-nums text-text-secondary\">{tab.count}</span>\n ) : null}\n </span>\n </BaseTabs.Tab>\n ))}\n </BaseTabs.List>\n </BaseTabs.Root>\n )\n}\n", "import { createContext, useContext, useMemo, type ReactNode } from 'react'\nimport { Toast as BaseToast } from '@base-ui/react/toast'\nimport { IconAlertCircle, IconCircleCheck, IconCircleX } from '@tabler/icons-react'\nimport { Button } from './Button'\nimport { cn } from './cn'\n\n/**\n * Peek's Toast (Figma: Alert) and its provider (2026-09-01), verbatim.\n *\n * The pill: five types \u2014 `success`, `brand`, `neutral`, `warning`, `error` \u2014\n * a leading icon that says which (a check, a `!`, an `\u00D7`), an optional action\n * on the right. Under Signal every toast is\n * the same dark overlay pill; the type lives in the icon's colour and glow,\n * not the surface.\n *\n * The provider sits on Base UI's `Toast` since stage 6 of the migration\n * (2026-09-14). Base UI owns the portal, the timers, the stack and what a\n * screen reader hears; this file owns how a toast looks and where the stack\n * stands (bottom-left). **Up to three show at once** (D7): the newest nearest\n * the corner, a fourth hides the oldest until one of the three closes. Before\n * stage 6 a new toast replaced the standing one, and nothing announced either.\n */\nexport type ToastType = 'success' | 'brand' | 'neutral' | 'warning' | 'error'\n\nexport interface ToastProps {\n label: string\n /**\n * Which of the five this is. It decides the surface in the light themes,\n * and under Signal \u2014 where every pill is the same dark overlay \u2014 the icon\n * and its colour.\n *\n * `warning` and `error` arrived at 0.12.2, for a notice that has to stay up\n * and for one that reports a failure. Until then the set had no way to say\n * either, so a caller with bad news passed `leadingIcon: false` rather than\n * put a tick beside it.\n */\n type?: ToastType\n /** Show the leading icon \u2014 a check, a `!` or an `\u00D7`, per `type`. Defaults to true. */\n leadingIcon?: boolean\n /** When set together with onAction, renders a clickable action on the right side. */\n actionLabel?: string\n onAction?: () => void\n className?: string\n}\n\n// Signal: every toast is the same dark overlay pill (v3) \u2014 the type lives in\n// the icon color + glow, not the surface.\nconst SURFACE_STYLES: Record<ToastType, string> = {\n success: 'bg-success-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-md',\n brand: 'bg-accent-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-md',\n neutral: 'bg-bg-inset border border-border-subtle signal:border-border-default signal:shadow-md',\n warning: 'bg-warning-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-md',\n error: 'bg-error-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-md',\n}\n\n/**\n * The leading icon says which of the five this is (Katerina, 2026-09-10:\n * *\"the icons should be representative, x for error, ! for warning\"*). It was\n * a circle-check on all of them, so a warning and a failure both arrived with\n * a tick beside them \u2014 the reason Peek passed `leadingIcon: false` rather than\n * show one.\n */\nconst ICONS: Record<ToastType, typeof IconCircleCheck> = {\n success: IconCircleCheck,\n brand: IconCircleCheck,\n neutral: IconCircleCheck,\n warning: IconAlertCircle,\n error: IconCircleX,\n}\n\nconst ICON_STYLES: Record<ToastType, string> = {\n success: 'signal:text-success-default signal:drop-shadow-glow-success',\n brand: 'signal:text-text-interactive signal:drop-shadow-glow-accent',\n neutral: 'signal:text-text-secondary',\n warning: 'signal:text-warning-default signal:drop-shadow-glow-warning',\n // No glow: the theme defines `--glow-warning`, `--glow-success` and\n // `--glow-accent` and no error glow, and a colour with no token is not\n // approximated here (D16).\n error: 'signal:text-error-default',\n}\n\nconst LABEL_CLASSES = 'text-body-2 text-text-primary whitespace-nowrap'\n\nconst pillClassName = (type: ToastType, hasAction: boolean, className?: string) =>\n cn(\n 'inline-flex items-center min-h-[32px] pl-2 py-1 rounded-lg shadow-lg',\n // Without an action the label needs real right padding; the action\n // button brings its own edge, so the tight pr-1 only applies there.\n hasAction ? 'pr-1 gap-[46px]' : 'pr-3',\n SURFACE_STYLES[type],\n className,\n )\n\nfunction LeadingIcon({ type }: { type: ToastType }) {\n const Icon = ICONS[type]\n return <Icon size={16} stroke={1.5} className={cn('text-text-primary shrink-0', ICON_STYLES[type])} />\n}\n\n/**\n * One toast, drawn in place. What the provider shows is this pill; draw it\n * yourself only where a page needs a toast's look without its timing \u2014 a\n * story, a picture of the set.\n */\nexport function Toast({ label, type = 'neutral', leadingIcon = true, actionLabel, onAction, className }: ToastProps) {\n const hasAction = !!(actionLabel && onAction)\n return (\n <div className={pillClassName(type, hasAction, className)}>\n <div className=\"flex items-center gap-2 shrink-0\">\n {leadingIcon && <LeadingIcon type={type} />}\n <span className={LABEL_CLASSES}>{label}</span>\n </div>\n {hasAction && (\n <Button variant=\"outlined\" size=\"small\" onClick={onAction}>\n {actionLabel}\n </Button>\n )}\n </div>\n )\n}\n\nexport interface ToastOptions {\n label: string\n /** Which of the five this is \u2014 it decides the surface and the leading icon. Defaults to 'neutral'. */\n type?: ToastType\n /**\n * Renders an action on the right side. Pressing it runs `onAction`, if\n * given, and closes this toast \u2014 so a Dismiss needs only its label.\n */\n actionLabel?: string\n onAction?: () => void\n /** Show the leading icon \u2014 a check, a `!` or an `\u00D7`, per `type`. Defaults to true. */\n leadingIcon?: boolean\n /** Auto-dismiss after this many ms. Defaults to 5000. Pass 0 to disable. */\n durationMs?: number\n}\n\ninterface ToastValue {\n /** Shows a toast and returns its id, for `dismissToast`. */\n showToast: (opts: ToastOptions) => string\n /** Closes the toast with this id; with no id, closes every toast on screen. */\n dismissToast: (id?: string) => void\n}\n\n/** What a toast carries beyond Base UI's own fields. */\ninterface ToastData {\n leadingIcon: boolean\n actionLabel?: string\n onAction?: () => void\n}\n\nconst ToastContext = createContext<ToastValue | null>(null)\n\n/** D7: three on screen at once. */\nconst VISIBLE_TOASTS = 3\n\nexport function ToastProvider({ children }: { children: ReactNode }) {\n // One manager per provider, so `useToast` can add and close without\n // re-rendering every caller each time the list changes.\n const manager = useMemo(() => BaseToast.createToastManager<ToastData>(), [])\n const value = useMemo<ToastValue>(\n () => ({\n showToast: ({ label, type = 'neutral', leadingIcon = true, actionLabel, onAction, durationMs = 5000 }) =>\n manager.add({\n title: label,\n type,\n timeout: durationMs,\n // A failure interrupts; everything else waits for a pause.\n priority: type === 'error' ? 'high' : 'low',\n data: { leadingIcon, actionLabel, onAction },\n }),\n dismissToast: (id) => manager.close(id),\n }),\n [manager],\n )\n\n return (\n <ToastContext.Provider value={value}>\n <BaseToast.Provider toastManager={manager} limit={VISIBLE_TOASTS}>\n {children}\n <BaseToast.Portal>\n <BaseToast.Viewport className=\"fixed bottom-4 left-4 z-[100] flex flex-col-reverse items-start gap-2 pointer-events-none\">\n <ToastList />\n </BaseToast.Viewport>\n </BaseToast.Portal>\n </BaseToast.Provider>\n </ToastContext.Provider>\n )\n}\n\nfunction ToastList() {\n const { toasts, close } = BaseToast.useToastManager<ToastData>()\n // Base UI lists the newest first; the viewport is `flex-col-reverse`, so\n // the newest stands nearest the corner.\n return toasts.map((toast) => {\n const type = (toast.type ?? 'neutral') as ToastType\n const { leadingIcon = true, actionLabel, onAction } = toast.data ?? {}\n return (\n <BaseToast.Root\n key={toast.id}\n toast={toast}\n // Towards the corner it stands in.\n swipeDirection={['left', 'down']}\n // A fourth toast hides the oldest (`data-limited`) until there is room.\n className={pillClassName(type, !!actionLabel, 'pointer-events-auto data-[limited]:hidden')}\n >\n <div className=\"flex items-center gap-2 shrink-0\">\n {leadingIcon && <LeadingIcon type={type} />}\n <BaseToast.Title render={<span />} className={LABEL_CLASSES} />\n </div>\n {actionLabel && (\n <BaseToast.Action\n onClick={() => {\n onAction?.()\n close(toast.id)\n }}\n // The same Button the toast drawn in place uses, told to *be* the\n // Base UI action rather than sit inside one (Katerina, 16 September).\n render={\n <Button variant=\"outlined\" size=\"small\">\n {actionLabel}\n </Button>\n }\n />\n )}\n </BaseToast.Root>\n )\n })\n}\n\nexport function useToast(): ToastValue {\n const ctx = useContext(ToastContext)\n if (!ctx) throw new Error('useToast must be used within ToastProvider')\n return ctx\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["jsx", "jsxs", "jsx", "jsxs", "BaseButton", "jsx", "jsx", "jsx", "jsx", "jsxs", "jsx", "jsx", "jsxs", "BaseButton", "jsx", "jsxs", "jsx", "IconX", "jsx", "jsxs", "IconX", "BaseButton", "jsx", "jsxs", "BaseButton", "jsx", "jsxs", "jsx", "jsxs", "createContext", "useContext", "useLayoutEffect", "useMemo", "useRef", "useState", "IconLoader2", "useState", "BaseButton", "IconX", "createContext", "useContext", "jsx", "Fragment", "jsx", "jsxs", "createContext", "GAP", "VIEWPORT_PAD", "useContext", "jsx", "jsxs", "BaseButton", "IconX", "useState", "GAP", "VIEWPORT_PAD", "jsx", "jsxs", "BaseField", "jsx", "jsxs", "BaseField", "useLayoutEffect", "useRef", "jsx", "useRef", "useLayoutEffect", "jsx", "jsxs", "Fragment", "jsx", "jsxs", "createContext", "useContext", "CONTROL", "useRef", "useMemo", "useState", "useLayoutEffect", "IconLoader2", "firstInvalid", "Fragment", "jsx", "jsxs", "useRef", "jsx", "jsxs", "jsx", "jsxs", "BaseButton", "jsx", "jsxs", "BaseButton", "Fragment", "jsx", "jsxs", "jsxs", "Fragment", "jsx", "useRef", "jsx", "FilePicker", "IconCheck", "IconChevronDown", "jsx", "jsxs", "GAP", "VIEWPORT_PAD", "IconChevronDown", "IconCheck", "forwardRef", "jsx", "forwardRef", "TextInput", "forwardRef", "BaseField", "jsx", "forwardRef", "Textarea", "BaseField", "useState", "useRef", "Dialog", "IconX", "jsx", "jsxs", "Dialog", "useRef", "IconX", "Fragment", "jsx", "jsxs", "useState", "useEffect", "useRef", "useState", "BaseField", "Input", "jsx", "useState", "useRef", "useEffect", "BaseField", "Input", "jsx", "Fragment", "useCallback", "useLayoutEffect", "useRef", "useState", "jsx", "jsxs", "useRef", "useState", "useCallback", "useLayoutEffect", "Fragment", "jsx", "jsxs", "useMemo", "jsx", "jsxs", "GAP", "VIEWPORT_PAD", "useMemo", "jsx", "jsx", "jsx", "jsxs", "GAP", "VIEWPORT_PAD", "OPEN_DELAY", "jsx", "jsx", "jsxs", "jsx", "jsx", "jsxs", "jsx", "jsxs", "Input", "jsx", "jsxs", "Input", "IconChevronRight", "Fragment", "jsx", "jsxs", "IconChevronRight", "useState", "jsx", "jsxs", "useState", "jsx", "jsxs", "createContext", "useContext", "useMemo", "IconAlertCircle", "jsx", "jsxs", "IconAlertCircle", "createContext", "useMemo", "useContext"]
|
|
7
7
|
}
|