@estiva-app/ui 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Reaction.d.ts +54 -0
- package/dist/Reaction.d.ts.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +50 -23
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
- package/src/Reaction.mdx +49 -0
- package/src/Reaction.stories.tsx +49 -0
- package/src/Reaction.tsx +86 -0
- package/src/index.ts +1 -0
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/cn.ts", "../src/TopBar.tsx", "../src/AppShell.tsx", "../src/Avatar.tsx", "../src/AvatarGroup.tsx", "../src/Banner.tsx", "../src/Button.tsx", "../src/Checkbox.tsx", "../src/Chip.tsx", "../src/ChipInput.tsx", "../src/fit.ts", "../src/Menu.tsx", "../src/Kbd.tsx", "../src/SectionLabel.tsx", "../src/Tooltip.tsx", "../src/IconButton.tsx", "../src/IdentityMenu.tsx", "../src/Divider.tsx", "../src/Person.tsx", "../src/PersonTrigger.tsx", "../src/Field.tsx", "../src/Select.tsx", "../src/TextInput.tsx", "../src/Textarea.tsx", "../src/ConfirmDialog.tsx", "../src/DialogShell.tsx", "../src/EditableText.tsx", "../src/EmptyState.tsx", "../src/Skeleton.tsx", "../src/Breadcrumb.tsx", "../src/NavItem.tsx", "../src/Rail.tsx", "../src/RailItem.tsx", "../src/Sidebar.tsx", "../src/Property.tsx", "../src/SearchInput.tsx", "../src/SectionHeader.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',\n]\n\nconst twMerge = extendTailwindMerge({\n extend: { classGroups: { 'font-size': [{ text: FONT_SIZE_TOKENS }] } },\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 }\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 { 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-[inset_0_1px_0_rgba(255,255,255,0.035)]',\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 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 <main className=\"min-w-0 flex-1 overflow-y-auto\">{children}</main>\n </div>\n </div>\n </div>\n )\n}\n", "import { useState } from 'react'\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 * 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. A picture that fails to load falls back to the initials. */\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 className?: string\n}\n\nexport function Avatar({ src, name, alt = '', size = 36, className }: AvatarProps) {\n const [broken, setBroken] = useState(false)\n const label = name || alt\n const picture = src && !broken ? src : undefined\n return (\n <div className={cn('rounded-sm overflow-hidden shrink-0 bg-bg-inset', className)} style={{ width: size, height: size }}>\n {picture ? (\n <img src={picture} alt={alt || name || ''} className=\"w-full h-full object-cover\" onError={() => setBroken(true)} />\n ) : label ? (\n <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 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 }}\n >\n {initialsFor(label)}\n </div>\n ) : (\n <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 </div>\n )}\n </div>\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 style={{ marginRight: -overlap, boxShadow: `0 0 0 ${ring}px var(--bg-surface)` }}\n >\n <Avatar size={size} name={member.name} src={member.picture} alt={member.name} />\n </span>\n ))}\n </div>\n )\n}\n", "import { cn } from './cn'\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 */\nexport type BannerTone = 'ok' | 'error' | 'info' | 'warning'\n\nexport interface BannerProps {\n tone: BannerTone\n children: string\n className?: string\n}\n\nconst TONE: 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, className }: BannerProps) {\n return (\n <div role={tone === 'error' ? 'alert' : 'status'} className={cn('px-4 py-2 text-body-2', TONE[tone], className)}>\n {children}\n </div>\n )\n}\n", "import type { ButtonHTMLAttributes, ReactNode } from 'react'\nimport { cn } from './cn'\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.\n *\n * Three variants and two sizes: 32px default / 24px small, 6px radius, 500\n * weight. Sizes are spelled as arbitrary values for the reason the README\n * records: tailwind-merge drops a custom `text-{size}` that is followed by a\n * `text-{colour}`.\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 */\nexport type ButtonVariant = 'primary' | 'outlined' | 'muted' | 'destructive'\nexport type ButtonSize = 'default' | 'small'\n\nexport interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n variant?: ButtonVariant\n size?: ButtonSize\n /** 16px, stroke 1.5 on the default size; 14px on small. */\n leadingIcon?: ReactNode\n children: ReactNode\n}\n\nexport function Button({\n variant = 'muted',\n size = 'default',\n leadingIcon,\n className,\n children,\n disabled,\n type = 'button',\n ...props\n}: ButtonProps) {\n const hasLeadingIcon = !!leadingIcon\n return (\n <button\n type={type}\n className={cn(\n 'inline-flex items-center justify-center gap-1 rounded-md transition-colors font-sans font-medium',\n size === 'default' && 'h-8 text-[14px] leading-[14px]',\n size === 'small' && 'h-6 text-[12px] leading-[12px]',\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 !disabled && variant === 'primary' && 'bg-accent-primary hover:bg-accent-hover text-text-inverse cursor-pointer signal:font-semibold',\n !disabled && variant === 'outlined' && 'border border-border-default hover:bg-bg-hover text-text-primary cursor-pointer',\n !disabled && variant === 'muted' && 'hover:bg-bg-hover text-text-primary cursor-pointer',\n !disabled && variant === 'destructive' && 'hover:bg-error-muted text-error-default cursor-pointer',\n disabled && 'bg-bg-disabled text-text-disabled pointer-events-none',\n disabled && variant === 'outlined' && 'border border-border-default',\n className,\n )}\n disabled={disabled}\n {...props}\n >\n {leadingIcon}\n {children}\n </button>\n )\n}\n", "import { 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.\n *\n * Controlled only; parents own the state. Rendered as a button with the\n * checkbox role so it works standalone or inside clickable rows: a caller\n * that toggles on the row's own click passes no `onChange`, and the square\n * goes inert rather than stealing the click.\n */\nexport interface CheckboxProps {\n checked: boolean\n onChange?: (checked: boolean) => void\n disabled?: boolean\n 'aria-label'?: string\n className?: string\n}\n\nexport function Checkbox({ checked, onChange, disabled = false, className, ...aria }: CheckboxProps) {\n return (\n <button\n type=\"button\"\n role=\"checkbox\"\n aria-checked={checked}\n aria-label={aria['aria-label']}\n disabled={disabled}\n onClick={(e) => {\n e.stopPropagation()\n onChange?.(!checked)\n }}\n className={cn(\n 'inline-flex items-center justify-center size-4 shrink-0 rounded-[4px] border transition-colors',\n checked\n ? 'bg-accent-primary border-accent-primary text-text-inverse'\n : 'bg-transparent border-border-strong hover:border-text-muted',\n disabled && 'opacity-50 pointer-events-none',\n onChange ? 'cursor-pointer' : 'pointer-events-none',\n className,\n )}\n >\n {checked && <IconCheck size={12} stroke={3} />}\n </button>\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 is a plain class here,\n * never merged, so tailwind-merge cannot drop it.\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-[rgba(86,200,255,0.3)]',\n info: 'bg-info-muted text-info-default signal:border signal:border-[rgba(86,200,255,0.3)]',\n warning: 'bg-warning-muted text-warning-default signal:border signal:border-[rgba(255,176,32,0.3)] signal:shadow-[shadow:0_0_5px_rgba(255,176,32,0.4)]',\n success: 'bg-success-muted text-success-default signal:border signal:border-[rgba(63,222,140,0.3)]',\n error: 'bg-error-muted text-error-default signal:border signal:border-[rgba(255,107,107,0.3)]',\n}\n\nexport function Chip({ type = 'neutral', label, leadingIcon, trailingIcon, className }: ChipProps) {\n return (\n <div className={cn('inline-flex items-center justify-center gap-1.5 rounded-full max-h-[20px] min-w-[16px] 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 <span className=\"text-chip whitespace-nowrap signal:font-mono signal:text-[10px] signal:font-semibold signal:tracking-[0.02em] 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 { useState, useRef, useMemo, useEffect, useLayoutEffect, type KeyboardEvent, type ReactNode } from 'react'\nimport { createPortal } from 'react-dom'\nimport { IconX } from '@tabler/icons-react'\nimport { cn } from './cn'\nimport { fitMenu } from './fit'\nimport { MenuItem } from './Menu'\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 */\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 className?: string\n}\n\nexport function InputChip({ label, leading, onRemove, className }: InputChipProps) {\n return (\n <div\n className={cn(\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]',\n // The padding follows the contents (Katerina, 2026-09-01): a face\n // sits 2px from the edge, a bare label needs 8px of air; the \u2715\n // brings its own box, so 4px behind it \u2014 8px when there isn't one.\n leading ? 'pl-[2px]' : 'pl-2',\n onRemove ? 'pr-1' : 'pr-2',\n className,\n )}\n >\n {leading && <span className=\"flex shrink-0 items-center\">{leading}</span>}\n <span className=\"text-caption font-medium text-text-primary\">{label}</span>\n {onRemove && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation()\n onRemove()\n }}\n className=\"size-4 flex items-center justify-center rounded-full hover:bg-bg-hover text-text-secondary\"\n aria-label={`Remove ${label}`}\n >\n <IconX size={10} stroke={1.5} />\n </button>\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. Peek's\n * version knew it was picking people: it read the directory from Peek's own\n * data layer and drew every face itself. Here the caller hands in `options`,\n * and \u2014 when the entries have faces or icons \u2014 the two leading slots: 16px\n * in a chip, 32px in a suggestion row. Nothing in this file knows what is\n * being picked.\n *\n * Suggestions appear only once the user types \u2014 focusing (or auto-focus on\n * dialog open) must not drop the full directory over the surface below.\n * Backspace on an empty query removes the last chip; Escape clears the query\n * when there is one and bubbles when there is not, so the surface around it\n * (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}\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}: ChipInputProps<T>) {\n const [query, setQuery] = useState('')\n const [highlight, setHighlight] = useState(0)\n const [isFocused, setIsFocused] = useState(false)\n const [anchorRect, setAnchorRect] = useState<DOMRect | null>(null)\n const wrapperRef = useRef<HTMLDivElement>(null)\n const inputRef = useRef<HTMLInputElement>(null)\n\n const matches = useMemo(() => {\n const selectedIds = new Set(value.map((o) => o.id))\n const excludedIds = new Set(excludeIds)\n const q = query.trim().toLowerCase()\n return options.filter((o) => {\n if (selectedIds.has(o.id)) return false\n if (excludedIds.has(o.id)) return false\n if (!q) return true\n return o.label.toLowerCase().includes(q) || (o.description ?? '').toLowerCase().includes(q)\n })\n }, [query, value, excludeIds, options])\n\n useEffect(() => {\n setHighlight(0)\n }, [query, matches.length])\n\n const showDropdown = isFocused && query.trim().length > 0 && matches.length > 0\n\n useLayoutEffect(() => {\n if (!showDropdown) return\n const update = () => {\n if (wrapperRef.current) setAnchorRect(wrapperRef.current.getBoundingClientRect())\n }\n update()\n window.addEventListener('resize', update)\n window.addEventListener('scroll', update, true)\n return () => {\n window.removeEventListener('resize', update)\n window.removeEventListener('scroll', update, true)\n }\n }, [showDropdown, value.length])\n\n function addOption(option: T) {\n onChange([...value, option])\n setQuery('')\n inputRef.current?.focus()\n }\n\n function removeOption(id: string) {\n onChange(value.filter((o) => o.id !== id))\n }\n\n function handleKeyDown(e: KeyboardEvent<HTMLInputElement>) {\n if (e.key === 'Backspace' && query === '' && value.length > 0) {\n // Consumed: removing a chip must not double as the surface's \"back\".\n e.preventDefault()\n removeOption(value[value.length - 1].id)\n return\n }\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n setHighlight((h) => Math.min(h + 1, Math.max(0, matches.length - 1)))\n return\n }\n if (e.key === 'ArrowUp') {\n e.preventDefault()\n setHighlight((h) => Math.max(h - 1, 0))\n return\n }\n if (e.key === 'Enter') {\n e.preventDefault()\n const target = matches[highlight]\n if (target) addOption(target)\n return\n }\n if (e.key === 'Escape') {\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 if (query !== '') {\n e.preventDefault()\n setQuery('')\n }\n }\n }\n\n return (\n <div className=\"relative\">\n <div\n ref={wrapperRef}\n 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 onClick={() => inputRef.current?.focus()}\n >\n {value.map((o) => (\n <InputChip key={o.id} label={o.label} leading={chipLeading?.(o)} onRemove={() => removeOption(o.id)} />\n ))}\n\n <input\n ref={inputRef}\n autoFocus={autoFocus}\n type=\"text\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n onKeyDown={handleKeyDown}\n onFocus={() => setIsFocused(true)}\n onBlur={() => {\n setTimeout(() => setIsFocused(false), 150)\n }}\n placeholder={value.length === 0 ? placeholder : ''}\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 </div>\n\n {showDropdown && anchorRect && createPortal(\n <div\n className=\"fixed z-[60] overflow-y-auto bg-bg-elevated border border-border-default rounded-lg shadow-lg\"\n /*\n Placed by fitMenu (2026-09-03), not hung blindly below: an input\n low on the screen flips its list upward instead of running the\n tail past the bottom edge. The rows are a fixed 48px, so the\n content height is arithmetic and needs no second render pass;\n the 240px cap is the old max-h-[240px].\n */\n style={{\n ...fitMenu({\n anchor: { left: anchorRect.left, top: anchorRect.top, bottom: anchorRect.bottom },\n menu: { width: anchorRect.width, contentHeight: matches.length * 48 },\n viewport: { width: window.innerWidth, height: window.innerHeight },\n cap: 240,\n }),\n width: anchorRect.width,\n }}\n >\n {matches.map((o, i) => (\n <MenuItem\n key={o.id}\n size=\"tall\"\n className=\"h-12 rounded-none\"\n leading={rowLeading?.(o)}\n label={o.label}\n description={o.description}\n selected={i === highlight}\n onMouseEnter={() => setHighlight(i)}\n onMouseDown={(e) => {\n e.preventDefault()\n addOption(o)\n }}\n />\n ))}\n </div>,\n document.body\n )}\n </div>\n )\n}\n", "/**\n * Viewport geometry for everything that floats \u2014 pure, so the decisions are\n * testable without a browser.\n *\n * One module, because the failure it prevents keeps recurring one surface at\n * a time: the files-panel picker was cut off at the right edge (fixed in\n * Select, 2026-09-01), then a reply menu's highlight submenu was cut off the\n * same way and the identity menu vanished under a z-indexed header\n * (2026-09-03). A menu that hand-rolls its own geometry is a menu waiting to\n * be the next screenshot; the shell calls these instead.\n */\n\nconst MARGIN = 8\nconst GAP = 4\n\n/**\n * Where a menu of this size goes, given its anchor and the viewport.\n *\n * Left is clamped inside the viewport with an 8px margin. Height is capped\n * at `cap` \u2014 Select's option list keeps the old `max-h-72` (288), a menu\n * panel passes none and uses all the room it opens into, so it scrolls only\n * when the screen truly has no space (the identity panel got Select's cap\n * by accident and grew a scrollbar at full height, 2026-09-03) \u2014 but never\n * taller than that room; when the room below the anchor is smaller than\n * both the content and the room above, the menu opens UPWARD (anchored to\n * the trigger's top via `bottom`). The 120px floor keeps a menu usable even\n * in a cramped corner \u2014 scrollable beats invisible.\n */\nexport function fitMenu({\n anchor,\n menu,\n viewport,\n cap = Number.POSITIVE_INFINITY,\n}: {\n anchor: { left: number; top: number; bottom: number }\n menu: { width: number; contentHeight: number }\n viewport: { width: number; height: number }\n /** Tallest the menu may stand even with room to spare. Absent: the room is the only limit. */\n cap?: number\n}): { left: number; top?: number; bottom?: number; maxHeight: number } {\n const left = Math.max(MARGIN, Math.min(anchor.left, viewport.width - menu.width - MARGIN))\n const below = viewport.height - anchor.bottom - GAP - MARGIN\n const above = anchor.top - GAP - MARGIN\n const openUp = below < Math.min(menu.contentHeight, cap) && above > below\n const maxHeight = Math.max(Math.min(cap, openUp ? above : below), 120)\n return openUp\n ? { left, bottom: viewport.height - anchor.top + GAP, maxHeight }\n : { left, top: anchor.bottom + GAP, maxHeight }\n}\n\n/**\n * Keep a box a caller has already placed fully on screen.\n *\n * For the menus whose caller measured a rect and chose the corner itself\n * (`position`): the caller's math stays authoritative, but its result can no\n * longer land off screen \u2014 it is slid inside the viewport, never flipped,\n * and capped to the viewport's height with the same 120px floor as fitMenu.\n */\nexport function clampBox({\n box,\n viewport,\n}: {\n box: { left: number; top: number; width: number; height: number }\n viewport: { width: number; height: number }\n}): { left: number; top: number; maxHeight: number } {\n const left = Math.max(MARGIN, Math.min(box.left, viewport.width - box.width - MARGIN))\n const maxHeight = Math.max(Math.min(box.height, viewport.height - 2 * MARGIN), 120)\n const top = Math.max(MARGIN, Math.min(box.top, viewport.height - maxHeight - MARGIN))\n return { left, top, maxHeight }\n}\n\n/**\n * Where a submenu goes, given its trigger row and the viewport.\n *\n * To the RIGHT of the row when it fits, flipped to the LEFT when it does not\n * \u2014 the measured flip two menus hand-rolled and one of them broke (the copy\n * dropped the ref its measurement read, so it measured nothing and always\n * opened rightward, off the screen). Top-aligned with the row, slid up when\n * the tail would run past the bottom edge.\n */\nexport function fitSubmenu({\n row,\n panel,\n viewport,\n}: {\n row: { left: number; right: number; top: number }\n panel: { width: number; height: number }\n viewport: { width: number; height: number }\n}): { left: number; top: number } {\n const fitsRight = row.right + GAP + panel.width <= viewport.width - MARGIN\n const left = Math.max(MARGIN, fitsRight ? row.right + GAP : row.left - GAP - panel.width)\n const top = Math.max(MARGIN, Math.min(row.top, viewport.height - panel.height - MARGIN))\n return { left, top }\n}\n", "import { createContext, useCallback, useContext, useEffect, useLayoutEffect, useRef, useState, type ComponentPropsWithRef, type CSSProperties, type ReactNode } from 'react'\nimport { IconChevronRight } from '@tabler/icons-react'\nimport { createPortal } from 'react-dom'\nimport { cn } from './cn'\nimport { Kbd } from './Kbd'\nimport { clampBox, fitMenu, fitSubmenu } from './fit'\nimport { SectionLabel } from './SectionLabel'\n\n/**\n * THE menu shell (2026-09-01) \u2014 extracted once, for every menu in every app.\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`, hover fill, 14px text, destructive\n * ones in the error colour; section headings as a SectionLabel in a 32px\n * row; and the two exits every menu has \u2014 Escape and a click outside \u2014\n * owned by the menu, never copied into a caller.\n *\n * Where it goes is owned here too (2026-09-03), because the two ways a menu\n * goes wrong are both placement: it opens inside a stacking context or a\n * scroll container and something covers or clips it, or it opens near an\n * edge and runs off the screen. Both shipped \u2014 the identity menu vanished\n * under a z-indexed panel header, a submenu was cut by the right edge \u2014 so\n * the shell now portals to the body (nothing in an app can cover the body's\n * last child at z-50) and fits itself to the viewport (fit.ts, the same\n * measured geometry Select uses). A caller hands over a trigger, not\n * coordinates.\n *\n * Three anchorings, in order of preference:\n * - `anchor` (an element, usually the trigger): portalled, measured, and\n * placed by `fitMenu` \u2014 under the anchor, flipped above when the room\n * below is worse, clamped inside the viewport, height-capped with its own\n * scrollbar. `align=\"right\"` hangs the menu's right edge from the\n * anchor's. Closes on resize and on any page scroll, because both move\n * the anchor out from under it.\n * - `position` (viewport coordinates the caller computed): portalled and\n * clamped (`clampBox`) \u2014 the caller's corner survives, but can no longer\n * land off screen.\n * - neither: in-flow under a `relative` wrapper, right-aligned. For stories\n * and static surfaces only \u2014 inside an app this mode inherits every\n * ancestor's stacking context and clip, which is how the identity menu\n * got covered.\n */\nexport interface MenuProps {\n onClose: () => void\n /** The trigger \u2014 an element, or the rect a click handler already measured.\n * The menu portals to the body and places itself against it. */\n anchor?: HTMLElement | DOMRect | null\n /** With `anchor`: which of the menu's edges hangs from the anchor's. Default left. */\n align?: 'left' | 'right'\n /** Viewport coordinates; the menu is portalled, hung from `top`, aligned to whichever edge is given, and clamped on screen. */\n position?: { top: number; right: number } | { top: number; left: number }\n /** Close 150ms after the pointer leaves the menu \u2014 the hover-flow menus\n * (quick-menu cards) dismiss this way. The grace period is shared with any\n * open MenuSub panel, so crossing into a portalled submenu never counts as\n * leaving. */\n closeOnLeave?: boolean\n children: ReactNode\n className?: string\n}\n\n/** MenuSub reports its hover into the enclosing Menu's leave-grace timer, so\n * a `closeOnLeave` menu survives the pointer crossing into a portalled\n * submenu panel \u2014 the one hover region the old inline submenus had for free. */\nconst MenuHoverContext = createContext<{ hold: () => void; release: () => void } | 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 and\n * placement, and that is right for a menu opened from a trigger \u2014 but a\n * type-ahead popup inside a text editor cannot have them: the editor's\n * suggestion plugin already owns the keyboard and positions the popup, and a\n * second Escape handler fights it. So Peek's `@`, `/` and `[` menus each drew\n * 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 children: ReactNode\n}\n\nexport function MenuPanel({ children, className, ...props }: MenuPanelProps) {\n return (\n <div\n className={cn('flex flex-col rounded-lg border border-border-default bg-bg-elevated p-2 shadow-lg', className)}\n {...props}\n >\n {children}\n </div>\n )\n}\n\nexport function Menu({ onClose, anchor, align = 'left', position, closeOnLeave = false, children, className }: MenuProps) {\n const ref = useRef<HTMLDivElement>(null)\n const leaveTimer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n const hold = useCallback(() => clearTimeout(leaveTimer.current), [])\n const release = useCallback(() => {\n if (!closeOnLeave) return\n clearTimeout(leaveTimer.current)\n leaveTimer.current = setTimeout(onClose, 150)\n }, [closeOnLeave, onClose])\n useEffect(() => () => clearTimeout(leaveTimer.current), [])\n const portalled = Boolean(anchor || position)\n /** Where the menu actually goes \u2014 measured against the viewport after a\n * hidden provisional render, so it is never covered and never cut off. */\n const [placed, setPlaced] = useState<{ left: number; top?: number; bottom?: number; maxHeight: number } | null>(null)\n\n useEffect(() => {\n const onDown = (event: MouseEvent) => {\n if (!ref.current?.contains(event.target as Node)) onClose()\n }\n const onKey = (event: KeyboardEvent) => {\n if (event.key === 'Escape') onClose()\n }\n document.addEventListener('mousedown', onDown)\n document.addEventListener('keydown', onKey)\n return () => {\n document.removeEventListener('mousedown', onDown)\n document.removeEventListener('keydown', onKey)\n }\n }, [onClose])\n\n // Callers build `position` inline every render; depending on its\n // coordinates rather than the object keeps the effect from re-running\n // (and re-placing) on every parent render.\n const posLeft = position && 'left' in position ? position.left : undefined\n const posRight = position && 'right' in position ? position.right : undefined\n const posTop = position?.top\n\n useLayoutEffect(() => {\n if (!portalled || !ref.current) return\n const viewport = { width: window.innerWidth, height: window.innerHeight }\n const menu = ref.current\n if (anchor) {\n const rect = anchor instanceof Element ? anchor.getBoundingClientRect() : anchor\n const left = align === 'right' ? rect.right - menu.offsetWidth : rect.left\n setPlaced(\n fitMenu({\n anchor: { left, top: rect.top, bottom: rect.bottom },\n menu: { width: menu.offsetWidth, contentHeight: menu.scrollHeight },\n viewport,\n }),\n )\n } else if (posTop !== undefined) {\n const left = posLeft ?? viewport.width - (posRight ?? 0) - menu.offsetWidth\n setPlaced(clampBox({ box: { left, top: posTop, width: menu.offsetWidth, height: menu.offsetHeight }, viewport }))\n }\n }, [portalled, anchor, align, posLeft, posRight, posTop])\n\n /* An anchored menu is placed against its trigger's rect, and a resize or a\n page scroll moves the trigger out from under it \u2014 close, as Select does.\n A scroll INSIDE the menu is its own capped list working; leave those. */\n useEffect(() => {\n if (!anchor) return\n const onScroll = (event: Event) => {\n if (event.target instanceof Node && ref.current?.contains(event.target)) return\n onClose()\n }\n window.addEventListener('resize', onClose)\n window.addEventListener('scroll', onScroll, true)\n return () => {\n window.removeEventListener('resize', onClose)\n window.removeEventListener('scroll', onScroll, true)\n }\n }, [anchor, onClose])\n\n const style: CSSProperties | undefined = portalled\n ? placed\n ? { left: placed.left, top: placed.top, bottom: placed.bottom, maxHeight: placed.maxHeight }\n : // The provisional render: measured by the layout effect, never seen.\n { left: 0, top: 0, visibility: 'hidden' }\n : undefined\n const node = (\n <MenuHoverContext.Provider value={{ hold, release }}>\n <MenuPanel\n ref={ref}\n role=\"menu\"\n data-interactive\n className={cn(\n 'z-50 min-w-[180px]',\n portalled ? 'fixed overflow-y-auto' : 'absolute right-0 top-full mt-1',\n className,\n )}\n style={style}\n onClick={(event) => event.stopPropagation()}\n onMouseEnter={closeOnLeave ? hold : undefined}\n onMouseLeave={closeOnLeave ? release : undefined}\n >\n {children}\n </MenuPanel>\n </MenuHoverContext.Provider>\n )\n return portalled ? createPortal(node, document.body) : node\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 like those were: opens at once, closes 150ms after the\n * pointer leaves row and panel both, so the diagonal from row to panel\n * survives. The panel portals to the body and is placed by `fitSubmenu` \u2014\n * right of the row when it fits, left when it does not, never past an edge\n * \u2014 so it also escapes whatever container its menu happens to be in.\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 const [open, setOpen] = useState(false)\n const rowRef = useRef<HTMLDivElement>(null)\n const panelRef = useRef<HTMLDivElement>(null)\n const closeTimer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n const [placed, setPlaced] = useState<{ left: number; top: number } | null>(null)\n // The panel portals out of the menu's DOM, so hovering it would read as\n // \"left the menu\" to a closeOnLeave shell \u2014 report hover upward instead.\n const menuHover = useContext(MenuHoverContext)\n\n const enter = () => {\n clearTimeout(closeTimer.current)\n menuHover?.hold()\n setOpen(true)\n }\n const leave = () => {\n closeTimer.current = setTimeout(() => setOpen(false), 150)\n menuHover?.release()\n }\n useEffect(() => () => clearTimeout(closeTimer.current), [])\n\n useLayoutEffect(() => {\n if (!open || !rowRef.current || !panelRef.current) {\n setPlaced(null)\n return\n }\n const row = rowRef.current.getBoundingClientRect()\n setPlaced(\n fitSubmenu({\n row: { left: row.left, right: row.right, top: row.top },\n panel: { width: panelRef.current.offsetWidth, height: panelRef.current.offsetHeight },\n viewport: { width: window.innerWidth, height: window.innerHeight },\n }),\n )\n }, [open])\n\n return (\n <div ref={rowRef} onMouseEnter={enter} onMouseLeave={leave}>\n <MenuItem label={label} leading={leading} selected={selected} submenu />\n {open &&\n createPortal(\n <MenuPanel\n ref={panelRef}\n role=\"menu\"\n data-interactive\n className={cn('fixed z-50 w-[160px]', className)}\n style={placed ?? { left: 0, top: 0, visibility: 'hidden' }}\n onMouseEnter={enter}\n onMouseLeave={leave}\n >\n {children}\n </MenuPanel>,\n document.body,\n )}\n </div>\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\nexport function MenuItem({ label, children, size = 'default', description, leading, trailing, hint, shortcut, submenu, destructive, selected, className, ...props }: MenuItemProps) {\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 <button\n type=\"button\"\n role=\"menuitem\"\n className={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 hover:bg-bg-hover',\n // tall: as tall as its content, never shorter than 40px (Katerina,\n // 2026-09-01) \u2014 a single-line picker row sits at 40, a row with a\n // 32px face and a role line comes out at its natural 48. One rule,\n // not a hand-picked height per file.\n size === 'tall' ? 'min-h-10 gap-3 px-3 py-1.5' : 'gap-2 px-2 py-1.5',\n selected && 'bg-bg-hover',\n className,\n )}\n {...props}\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-[14px] leading-[140%]', destructive ? 'text-error-default' : 'text-text-primary')}>\n {label}\n </span>\n {description && <span className=\"truncate text-[12px] leading-[120%] 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', hint && selected && 'opacity-0')}>\n {edge}\n </span>\n )}\n {hint && (\n <span className={cn('flex items-center opacity-0 group-hover:opacity-100', selected && 'opacity-100')}>\n {hint}\n </span>\n )}\n </span>\n )}\n </button>\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-[9px] font-medium leading-[115%] signal:font-mono signal:text-[9.5px] signal:tracking-[0.04em]\">{target}</span>\n )}\n </span>\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). */\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 return (\n <div className=\"flex flex-col\">\n <div className={cn('flex h-8 items-center px-2', className)}>\n <SectionLabel className=\"text-text-secondary\">{label}</SectionLabel>\n </div>\n {children}\n </div>\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 { 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-[12px] leading-[120%] font-normal text-text-secondary',\n 'signal:border-b-2 signal:pt-[2px] signal:pb-px signal:bg-[rgba(255,255,255,.05)] signal:font-mono signal:text-[10px]',\n 'ship:border-b-2 ship:pt-[2px] ship:pb-px ship:bg-[rgba(255,255,255,.05)] ship:font-mono ship:text-[10px]',\n className,\n )}\n >\n {children}\n </kbd>\n )\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-[12px] leading-[12px] font-medium text-text-primary signal:font-mono signal:text-[10px] signal:uppercase signal:tracking-[0.14em]',\n className,\n )}\n >\n {children}\n </span>\n )\n}\n", "import { useCallback, useLayoutEffect, useRef, useState, type CSSProperties, type ReactNode } from 'react'\nimport { createPortal } from 'react-dom'\nimport { cn } from './cn'\nimport { Kbd } from './Kbd'\n\n/**\n * Peek's Tooltip and WithTooltip (2026-08-28), verbatim, in one file.\n *\n * `Tooltip` is the surface: a 30px elevated pill with a caption. `WithTooltip`\n * wraps a trigger and portals the surface above or below it on hover, fixed\n * to the viewport and kept 8px inside it. The wrapper is `inline-flex` and\n * shrinks nothing \u2014 wrap a control, never a block (a form inside it\n * collapses to its content width; Ship learnt that).\n */\nexport interface TooltipProps {\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 className?: string\n}\n\nexport function Tooltip({ label, shortcut, className }: 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)}>\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\nconst GAP = 6\nconst VIEWPORT_PAD = 8\n\nexport function WithTooltip({ label, shortcut, placement = 'top', wrapperClassName, children }: WithTooltipProps) {\n const [show, setShow] = useState(false)\n const ref = useRef<HTMLDivElement>(null)\n const tooltipRef = useRef<HTMLDivElement>(null)\n const [style, setStyle] = useState<CSSProperties>({ position: 'fixed', zIndex: 9999, pointerEvents: 'none', visibility: 'hidden' })\n\n const reposition = useCallback(() => {\n const trigger = ref.current\n const tip = tooltipRef.current\n if (!trigger || !tip) return\n const triggerRect = trigger.getBoundingClientRect()\n const tipRect = tip.getBoundingClientRect()\n let top = placement === 'bottom' ? triggerRect.bottom + GAP : triggerRect.top - tipRect.height - GAP\n // The vertical axis flips and clamps like the horizontal one always has\n // (2026-09-03): a `top` tooltip on a control near the viewport's top edge\n // was the one floating surface left that could leave the screen.\n if (placement === 'top' && top < VIEWPORT_PAD) top = triggerRect.bottom + GAP\n else if (placement === 'bottom' && top + tipRect.height > window.innerHeight - VIEWPORT_PAD) top = triggerRect.top - tipRect.height - GAP\n top = Math.max(VIEWPORT_PAD, Math.min(top, window.innerHeight - tipRect.height - VIEWPORT_PAD))\n let left = triggerRect.left + triggerRect.width / 2 - tipRect.width / 2\n left = Math.max(VIEWPORT_PAD, Math.min(left, window.innerWidth - tipRect.width - VIEWPORT_PAD))\n setStyle({ position: 'fixed', top, left, zIndex: 9999, pointerEvents: 'none', visibility: 'visible' })\n }, [placement])\n\n useLayoutEffect(() => {\n if (show) reposition()\n }, [show, reposition])\n\n return (\n <div ref={ref} className={cn('inline-flex shrink-0', wrapperClassName)} onMouseEnter={() => setShow(true)} onMouseLeave={() => setShow(false)}>\n {children}\n {show &&\n createPortal(\n <div ref={tooltipRef} style={style}>\n <Tooltip label={label} shortcut={shortcut} />\n </div>,\n document.body,\n )}\n </div>\n )\n}\n", "import type { ButtonHTMLAttributes, ReactNode } from 'react'\nimport { cn } from './cn'\nimport { WithTooltip } 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.\n */\nexport type IconButtonVariant = 'muted' | 'outlined' | 'primary'\n\nexport interface IconButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\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 /** The icon: 16px, stroke 1.5. */\n children: ReactNode\n}\n\nexport function IconButton({\n variant = 'muted',\n className,\n children,\n disabled,\n tooltip,\n tooltipShortcut,\n tooltipPlacement,\n type = 'button',\n ...props\n}: IconButtonProps) {\n const button = (\n <button\n type={type}\n className={cn(\n 'flex items-center justify-center p-1 rounded-lg transition-colors shrink-0 cursor-pointer',\n !disabled && variant === 'primary' && 'bg-accent-primary hover:bg-accent-hover text-text-inverse',\n !disabled && variant === 'muted' && 'text-text-secondary hover:bg-bg-hover hover:text-text-primary',\n !disabled && variant === 'outlined' && 'border border-border-default hover:bg-bg-hover text-text-secondary',\n disabled && variant === 'primary' && 'bg-bg-disabled text-text-disabled',\n disabled && variant === 'muted' && 'text-text-disabled',\n disabled && variant === 'outlined' && 'border border-border-default text-text-disabled',\n disabled && 'pointer-events-none cursor-not-allowed',\n className,\n )}\n disabled={disabled}\n {...props}\n >\n {children}\n </button>\n )\n\n if (tooltip) {\n return (\n <WithTooltip label={tooltip} shortcut={tooltipShortcut} placement={tooltipPlacement}>\n {button}\n </WithTooltip>\n )\n }\n return button\n}\n", "import { useRef, useState, type ReactNode } from 'react'\nimport { cn } from './cn'\nimport { Divider } from './Divider'\nimport { Menu, MenuItem, 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\nexport interface IdentityPanelProps extends Omit<IdentityMenuProps, 'compact' | 'className'> {\n onClose: () => void\n /** The trigger to hang from \u2014 the panel portals to the body and fits the\n * viewport, so no header, sidebar or scroll container can cover it (the\n * z-10 floating top bar trapped the old in-flow panel under a z-20 panel\n * header, 2026-09-03). Absent, the panel stands in flow \u2014 the stories. */\n anchor?: HTMLElement | null\n /** On the Menu surface \u2014 the stories pass `static` to stand it in flow. */\n className?: string\n}\n\n/** The menu alone \u2014 what `IdentityMenu` opens. Exported so the stories show\n * the designed artifact rather than a closed trigger, and for any surface\n * that wants the panel without the trigger. */\nexport function IdentityPanel({ me, signedIn, relayUrl, idBase, onCopyKey, onSignOut, onClose, anchor, className, children }: IdentityPanelProps) {\n const act = (action: () => void) => () => {\n onClose()\n action()\n }\n const appRows = typeof children === 'function' ? children(onClose) : children\n return (\n <Menu onClose={onClose} anchor={anchor} align=\"right\" className={cn('w-72', className)}>\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 </Menu>\n )\n}\n\nexport function IdentityMenu({ me, signedIn, relayUrl, idBase, onCopyKey, onSignOut, compact = false, className, children }: IdentityMenuProps) {\n const [open, setOpen] = useState(false)\n /* The wrapper is the anchor: the panel hangs its right edge from this\n div's, exactly where the old in-flow `absolute right-0` put it \u2014 but\n portalled, so nothing z-indexed in the app can cover it. */\n const anchorRef = useRef<HTMLDivElement>(null)\n\n return (\n <div ref={anchorRef} className={cn('relative', className)}>\n <PersonTrigger\n name={me.name}\n picture={me.picture}\n fallback=\"Anonymous\"\n compact={compact}\n size={compact ? 36 : undefined}\n open={open}\n // Swallowed so the menu's outside-mousedown dismiss does not fire\n // first and turn the toggle into a close-then-reopen flicker.\n onMouseDown={(event) => event.stopPropagation()}\n onClick={() => setOpen((value) => !value)}\n // The row shape is named by its own text \u2014 the person. Only the bare\n // face needs a label; naming the row would override the person's name\n // as the accessible name (Ship's tests find the trigger by it).\n aria-label={compact ? 'Account menu' : undefined}\n />\n\n {open && (\n <IdentityPanel\n me={me}\n signedIn={signedIn}\n relayUrl={relayUrl}\n idBase={idBase}\n onCopyKey={onCopyKey}\n onSignOut={onSignOut}\n onClose={() => setOpen(false)}\n anchor={anchorRef.current}\n >\n {children}\n </IdentityPanel>\n )}\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 { 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 \u2014 and the separator\n * role for assistive tech.\n */\nexport interface DividerProps {\n orientation?: 'horizontal' | 'vertical'\n className?: string\n}\n\nexport function Divider({ orientation = 'horizontal', className }: DividerProps) {\n return (\n <div\n role=\"separator\"\n aria-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 { 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.\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 <button\n type=\"button\"\n aria-haspopup=\"menu\"\n aria-expanded={open}\n className={cn('cursor-pointer rounded-full focus:outline-none', className)}\n {...props}\n >\n <Avatar name={name} src={picture} size={size ?? 36} />\n </button>\n )\n }\n return (\n <button\n type=\"button\"\n aria-haspopup=\"menu\"\n aria-expanded={open}\n className={cn(\n // The size is an arbitrary value (the body-2 token): this list goes through\n // cn(), and tw-merge silently drops a custom text-{size} once a\n // text-{colour} follows it. Measured: the name rendered 16px.\n 'flex h-8 cursor-pointer items-center gap-1.5 rounded-md pl-1.5 pr-1.5 text-[14px] leading-[140%] text-text-primary transition-colors hover:bg-bg-hover',\n 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 </button>\n )\n}\n", "import { createContext, useContext, useId, type ReactNode } from 'react'\n\n/**\n * The id of the control this Field labels.\n *\n * **A control has to opt in by calling `useFieldControlId`.** The automatic\n * alternative is nesting the control inside the `<label>`, which associates\n * anything by construction and needs no cooperation \u2014 and it is not used here,\n * because a control that is *both* nested in a label and named by its `htmlFor`\n * can receive two activations from one click. That is a real hazard for a\n * checkbox and a latent one for everything else, and this library has a\n * `Checkbox`.\n *\n * So: one explicit mechanism, and a test that pins it for every primitive that\n * uses it (`Field.test.tsx`). A new primitive that renders a labelable element\n * calls this hook and spreads the result; one that does not is unlabelled, and\n * the test is where that gets noticed.\n */\nconst FieldControlIdContext = createContext<string | undefined>(undefined)\n\n/**\n * The id a surrounding `Field` wants this control to have, falling back to the\n * caller's own outside one.\n *\n * **Inside a Field, the Field wins**, which is the opposite of what I wrote\n * first and the test caught within the minute. Letting a control's own `id`\n * take precedence leaves the label's `htmlFor` pointing at the id the Field\n * generated and the control answering to a different one \u2014 which is this\n * ticket's defect exactly, reproduced by the fix for it, and it fails with the\n * same message: *\"Found a label with the text of: Title, however no form\n * control was found associated to that label.\"*\n *\n * A caller who needs to choose the id names it on the Field (`htmlFor`), which\n * is the one place that can set both halves. Outside a Field there is nothing\n * to disagree with, so the caller's id is used.\n */\nexport function useFieldControlId(ownId?: string): string | undefined {\n const fromField = useContext(FieldControlIdContext)\n return fromField ?? ownId\n}\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 as a plain\n * class, never merged.\n *\n * The label names its control (SHA-17). It did not, 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.\n */\nexport interface FieldProps {\n label: string\n required?: boolean\n /**\n * Override the generated id. Only needed when something outside has to name\n * the control \u2014 an `aria-describedby` elsewhere, or a form library.\n */\n htmlFor?: string\n children: ReactNode\n}\n\nexport function Field({ label, required = false, htmlFor, children }: FieldProps) {\n const generated = useId()\n const id = htmlFor ?? generated\n return (\n <div className=\"flex flex-col gap-2\">\n <label\n htmlFor={id}\n className={`text-input-label text-text-primary${required ? ' flex items-center' : ''}`}\n >\n {label}\n {required && <span className=\"text-error-default ml-0.5\">*</span>}\n </label>\n <FieldControlIdContext.Provider value={id}>{children}</FieldControlIdContext.Provider>\n </div>\n )\n}\n", "import { IconCheck, IconChevronDown } from '@tabler/icons-react'\nimport { useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, type KeyboardEvent, type ReactNode } from 'react'\nimport { createPortal } from 'react-dom'\nimport { cn } from './cn'\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.\n *\n * A button that opens a portalled listbox under itself: arrows move (and keep\n * the active option scrolled into view), Home and End jump, Enter and Space\n * pick, Escape closes and returns focus to the trigger, Tab closes, a click\n * outside closes, a PAGE scroll or resize closes (the list is fixed to where\n * the trigger was) \u2014 a scroll *inside* the list is the list's own business\n * and must not dismiss it. The menu keeps itself on screen: clamped to the\n * viewport's sides, height capped to the space it actually has, opening\n * upward when the room below is worse than the room above (Katerina,\n * 2026-09-01 \u2014 the files-panel picker was cut right and bottom, and its own\n * scroll closed it). Two sizes; `disabled` explains nothing by itself \u2014 wrap\n * it in a tooltip 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 placeholder?: string\n disabled?: boolean\n className?: string\n}\n\n// The geometry lives in fit.ts now (2026-09-03) \u2014 shared with the Menu\n// shell, so a cut-off surface is fixed once. Re-exported because this is\n// where it grew up and its test still names it by this address.\nimport { fitMenu } from './fit'\nexport { fitMenu }\n\nexport function Select({ value, onChange, options, size = 'default', ariaLabel, placeholder = 'Select\u2026', disabled, className }: SelectProps) {\n const id = useId()\n const triggerRef = useRef<HTMLButtonElement>(null)\n const menuRef = useRef<HTMLDivElement>(null)\n const [rect, setRect] = useState<DOMRect | null>(null)\n /** Where the menu actually goes \u2014 measured against the viewport after the\n * provisional render, so it is never cut off by an edge. */\n const [placement, setPlacement] = useState<{\n left: number\n top?: number\n bottom?: number\n maxHeight: number\n } | null>(null)\n const open = rect !== null\n\n const selectedIndex = useMemo(() => Math.max(0, options.findIndex((o) => o.value === value)), [options, value])\n const [activeIndex, setActiveIndex] = useState(selectedIndex)\n\n const close = useCallback((refocus: boolean) => {\n setRect(null)\n if (refocus) triggerRef.current?.focus()\n }, [])\n\n const openMenu = useCallback(() => {\n setActiveIndex(selectedIndex)\n setRect(triggerRef.current?.getBoundingClientRect() ?? null)\n }, [selectedIndex])\n\n /*\n * Fit the menu to the viewport, before paint.\n *\n * The provisional render sits at the trigger's corner and is invisible;\n * this measures it and decides the real box: left clamped inside the\n * viewport, height capped to the space available, and the whole thing\n * opening UPWARD when the room below is smaller than both the content and\n * the room above. A menu that is always fully on screen is also the only\n * kind whose scrollbar can actually be used.\n */\n useLayoutEffect(() => {\n if (!rect || !menuRef.current) {\n setPlacement(null)\n return\n }\n setPlacement(\n fitMenu({\n anchor: { left: rect.left, top: rect.top, bottom: rect.bottom },\n menu: { width: menuRef.current.offsetWidth, contentHeight: menuRef.current.scrollHeight },\n viewport: { width: window.innerWidth, height: window.innerHeight },\n // The option list keeps its classic height (the old max-h-72); a menu\n // panel passes no cap and stands as tall as the room it opens into.\n cap: 288,\n }),\n )\n }, [rect, options.length])\n\n // Keyboard follows the highlight: without this, arrowing past the fold\n // moved `activeIndex` into rows the capped menu never showed.\n useEffect(() => {\n if (!open) return\n // Optional call: jsdom implements neither scrolling nor this method, and\n // a consumer's component tests should not crash for a scroll nicety.\n document.getElementById(`${id}-${activeIndex}`)?.scrollIntoView?.({ block: 'nearest' })\n }, [open, activeIndex, id])\n\n // Outside click and Escape \u2014 the two exits every menu has. `mousedown`\n // rather than `click`, so a press that starts outside dismisses at once.\n useEffect(() => {\n if (!open) return\n const onDown = (e: MouseEvent) => {\n if (menuRef.current?.contains(e.target as Node)) return\n if (triggerRef.current?.contains(e.target as Node)) return\n setRect(null)\n }\n const onResize = () => setRect(null)\n /*\n * A PAGE scroll moves the anchor out from under the fixed menu, so it\n * closes. A scroll INSIDE the menu is the menu working as designed \u2014\n * capture phase sees those too, and closing on them made the list\n * impossible to scroll at all (the bug this comment survives to prevent).\n */\n const onScroll = (e: Event) => {\n if (e.target instanceof Node && menuRef.current?.contains(e.target)) return\n setRect(null)\n }\n document.addEventListener('mousedown', onDown)\n window.addEventListener('resize', onResize)\n window.addEventListener('scroll', onScroll, true)\n return () => {\n document.removeEventListener('mousedown', onDown)\n window.removeEventListener('resize', onResize)\n window.removeEventListener('scroll', onScroll, true)\n }\n }, [open])\n\n const pick = (index: number) => {\n const option = options[index]\n if (!option) return\n onChange(option.value)\n close(true)\n }\n\n const onKeyDown = (e: KeyboardEvent) => {\n if (!open) {\n if (['ArrowDown', 'ArrowUp', 'Enter', ' '].includes(e.key)) {\n e.preventDefault()\n openMenu()\n }\n return\n }\n switch (e.key) {\n case 'Escape':\n e.preventDefault()\n close(true)\n break\n case 'ArrowDown':\n e.preventDefault()\n setActiveIndex((i) => Math.min(options.length - 1, i + 1))\n break\n case 'ArrowUp':\n e.preventDefault()\n setActiveIndex((i) => Math.max(0, i - 1))\n break\n case 'Home':\n e.preventDefault()\n setActiveIndex(0)\n break\n case 'End':\n e.preventDefault()\n setActiveIndex(options.length - 1)\n break\n case 'Enter':\n case ' ':\n e.preventDefault()\n pick(activeIndex)\n break\n case 'Tab':\n close(false)\n break\n }\n }\n\n const selected = options.find((o) => o.value === value)\n\n return (\n <>\n <button\n ref={triggerRef}\n type=\"button\"\n disabled={disabled}\n aria-haspopup=\"listbox\"\n aria-expanded={open}\n aria-label={ariaLabel}\n onClick={() => (open ? close(false) : openMenu())}\n onKeyDown={onKeyDown}\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-[14px] leading-[1.4] font-normal',\n size === 'small' && 'h-6 px-2 text-[12px] leading-[1.4] font-normal',\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 <span className=\"truncate\">{selected?.label ?? placeholder}</span>\n </span>\n <IconChevronDown size={size === 'small' ? 14 : 16} stroke={1.5} className=\"shrink-0 text-text-secondary\" />\n </button>\n\n {open &&\n rect &&\n createPortal(\n <div\n ref={menuRef}\n role=\"listbox\"\n aria-activedescendant={`${id}-${activeIndex}`}\n tabIndex={-1}\n style={\n placement\n ? { ...placement, minWidth: rect.width }\n : // Provisional frame: measured by the layout effect above,\n // replaced before paint. Hidden so a cut-off position is\n // never visible, not even for a frame.\n { top: rect.bottom + 4, left: rect.left, minWidth: rect.width, visibility: 'hidden' }\n }\n className=\"fixed z-50 overflow-y-auto rounded-lg border border-border-default bg-bg-elevated p-1 shadow-lg\"\n >\n {options.map((option, index) => (\n <div\n key={option.value}\n id={`${id}-${index}`}\n role=\"option\"\n aria-selected={option.value === value}\n onMouseEnter={() => setActiveIndex(index)}\n onMouseDown={(e) => {\n // The document-level dismiss also listens on mousedown.\n e.preventDefault()\n pick(index)\n }}\n className={cn(\n 'flex h-9 cursor-pointer items-center justify-between gap-2 rounded-lg px-3 transition-colors',\n 'text-[14px] font-normal leading-[1.4] text-text-primary',\n index === activeIndex && 'bg-bg-hover',\n option.value === value && 'font-medium',\n )}\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 <span className=\"truncate\">{option.label}</span>\n </span>\n {option.value === value && <IconCheck size={16} stroke={1.5} className=\"shrink-0 text-text-secondary\" />}\n </div>\n ))}\n </div>,\n document.body,\n )}\n </>\n )\n}\n", "import { forwardRef, type InputHTMLAttributes } from 'react'\nimport { cn } from './cn'\nimport { useFieldControlId } from './Field'\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.\n */\nexport type TextInputProps = InputHTMLAttributes<HTMLInputElement>\n\nexport const TextInput = forwardRef<HTMLInputElement, TextInputProps>(function TextInput({ className, type = 'text', id, ...props }, ref) {\n // A surrounding Field names this control (SHA-17); an explicit id still wins.\n const controlId = useFieldControlId(id)\n return (\n <input\n ref={ref}\n id={controlId}\n type={type}\n className={cn(\n 'bg-bg-inset border border-border-default focus:border-border-focus rounded-lg px-3 py-2',\n 'text-[14px] leading-[1.4] font-normal 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 { cn } from './cn'\nimport { useFieldControlId } from './Field'\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).\n */\nexport type TextareaProps = TextareaHTMLAttributes<HTMLTextAreaElement>\n\nexport const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(function Textarea({ className, id, ...props }, ref) {\n // A surrounding Field names this control (SHA-17); an explicit id still wins.\n const controlId = useFieldControlId(id)\n return (\n <textarea\n ref={ref}\n id={controlId}\n className={cn(\n 'bg-bg-inset border border-border-default focus:border-border-focus rounded-lg px-3 py-2',\n 'text-[14px] leading-[1.4] font-normal 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}\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 */\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 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 { useEffect, type ReactNode } from 'react'\nimport { createPortal } from 'react-dom'\nimport { IconX } from '@tabler/icons-react'\nimport { cn } from './cn'\nimport { IconButton } from './IconButton'\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. Plus what Ship added: Escape closes it, and the card\n * says what it is to assistive tech.\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 (e.g. `flex flex-col gap-6`, or a max height with `overflow-y-auto`). */\n bodyClassName?: string\n width?: number\n}\n\nexport function DialogShell({ title, onClose, headerContent, footer, children, bodyClassName, width = 502 }: DialogShellProps) {\n useEffect(() => {\n const onKey = (event: KeyboardEvent) => {\n if (event.key === 'Escape') onClose()\n }\n document.addEventListener('keydown', onKey)\n return () => document.removeEventListener('keydown', onKey)\n }, [onClose])\n\n return createPortal(\n <>\n {/* Backdrop */}\n <div className=\"fixed inset-0 z-40 bg-black/50\" onClick={onClose} />\n\n {/* Dialog */}\n <div className=\"fixed inset-0 z-50 flex items-center justify-center pointer-events-none\">\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={title}\n className=\"bg-bg-elevated border border-border-subtle rounded-lg shadow-lg pointer-events-auto flex flex-col overflow-hidden\"\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 ?? <span className=\"text-h4 text-text-primary\">{title}</span>}\n <IconButton tooltip=\"Close\" aria-label=\"Close\" onClick={onClose}>\n <IconX size={16} stroke={1.5} />\n </IconButton>\n </div>\n\n {/* Body */}\n <div className={cn('pl-5 pr-4 py-4', footer != null && 'border-b border-border-subtle', bodyClassName)}>{children}</div>\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 </div>\n </div>\n </>,\n document.body,\n )\n}\n", "import { useEffect, useRef, useState, type KeyboardEvent, type ReactNode, type RefObject } from 'react'\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 * 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 <div\n aria-label={label}\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 <textarea\n ref={fieldRef as RefObject<HTMLTextAreaElement>}\n aria-label={label}\n value={draft}\n rows={4}\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 title=\"Click to edit\"\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 type { ReactNode } from 'react'\nimport { IconMessage2 } from '@tabler/icons-react'\nimport { cn } from './cn'\n\n/**\n * Peek's EmptyState (2026-08-28), verbatim: a 16px icon over a centred line\n * of secondary text. The message is the caller's \u2014 a shared component has no\n * words of its own for what is missing.\n */\nexport interface EmptyStateProps {\n /** 16px, stroke 1.5. A speech bubble when absent. */\n icon?: ReactNode\n message: string\n className?: string\n}\n\nexport function EmptyState({ icon, message, className }: EmptyStateProps) {\n return (\n <div className={cn('flex flex-col gap-2 items-center', 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 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 { Fragment, useCallback, useLayoutEffect, useRef, useState } from 'react'\nimport { cn } from './cn'\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 */\nexport interface Crumb {\n label: string\n href?: string\n /** Set the item in the mono face \u2014 a ref, an id. */\n mono?: boolean\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-[14px] leading-[140%]', 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 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-[12px] leading-[120%]',\n item.muted || (last && item.mono) ? 'text-text-muted' : last ? 'text-text-primary' : 'text-text-secondary',\n )\n const setLabelRef = (el: HTMLElement | null) => {\n labelRefs.current[index] = el\n }\n const crumb = item.href ? (\n <a ref={setLabelRef} href={item.href} className={cn(text, 'hover:text-text-primary')}>\n {item.label}\n </a>\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 {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 <span className=\"shrink-0 font-mono text-caption tabular-nums text-text-muted\">{count}</span>\n </WithTooltip>\n ) : null}\n </a>\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-[9px] font-medium leading-[115%]', 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'\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(\n 'flex w-60 shrink-0 flex-col gap-px overflow-y-auto border-r border-border-default bg-bg-surface px-2.5 py-3',\n className,\n )}\n >\n {children}\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 */\nexport interface PropertyProps {\n label: string\n layout?: 'row' | 'stacked'\n children: ReactNode\n className?: string\n}\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 <div className={cn('flex flex-col gap-3', className)}>\n <span className=\"text-menu uppercase tracking-[0.08em] text-text-secondary\">{label}</span>\n {children}\n </div>\n )\n }\n return (\n <div className={cn('flex items-center gap-2', className)}>\n <span className=\"w-[68px] shrink-0 text-caption text-text-secondary\">{label}</span>\n {children}\n </div>\n )\n}\n", "import { type InputHTMLAttributes } from 'react'\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 */\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 { useState, type ReactNode } from 'react'\nimport { IconChevronRight } from '@tabler/icons-react'\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 whole row the\n * toggle, and actions that appear only while the row is hovered, as\n * IconButtons with tooltips.\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; the\n * header owns the hover reveal and stops the click from also toggling the\n * section. Peek's add/sort pair is the WithActions story, in its original\n * order.\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 whole row the toggle. */\n chevron?: boolean\n isExpanded?: boolean\n onToggle?: () => void\n /** Right-aligned, in the order given. */\n actions?: SectionAction[]\n /** `hover` reveals the actions only while the row is hovered; `always` keeps them. */\n showActions?: 'hover' | 'always'\n className?: string\n}\n\nexport function SectionHeader({ title, chevron = false, isExpanded = true, onToggle, actions, showActions = 'hover', className }: SectionHeaderProps) {\n const [isHovered, setIsHovered] = useState(false)\n\n return (\n <div\n className={cn(\n 'flex h-[32px] items-center justify-between rounded-lg px-2 transition-colors',\n isHovered && 'bg-bg-hover',\n chevron && 'cursor-pointer',\n className,\n )}\n onClick={chevron ? onToggle : undefined}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n >\n <div className=\"flex shrink-0 items-center gap-1\">\n {chevron && (\n <IconChevronRight\n size={12}\n stroke={1.5}\n className={cn('text-text-secondary transition-transform duration-150', isExpanded && 'rotate-90')}\n />\n )}\n <SectionLabel>{title}</SectionLabel>\n </div>\n\n {(showActions === 'always' || isHovered) && actions && actions.length > 0 && (\n <div className=\"flex items-center gap-1\">\n {actions.map((action) => (\n <IconButton\n key={action.tooltip}\n tooltip={action.tooltip}\n aria-label={action.tooltip}\n onClick={(event) => {\n event.stopPropagation()\n action.onClick()\n }}\n >\n {action.icon}\n </IconButton>\n ))}\n </div>\n )}\n </div>\n )\n}\n", "import type { ReactNode } from 'react'\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.\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 className?: string\n}\n\nexport function Tabs<T extends string>({ tabs, active, onChange, size = 'default', className }: TabsProps<T>) {\n return (\n <div role=\"tablist\" className={cn('flex items-center gap-2', className)}>\n {tabs.map((tab) => (\n <button\n key={tab.id}\n type=\"button\"\n role=\"tab\"\n aria-selected={active === tab.id}\n onClick={() => onChange(tab.id)}\n className={cn(\n 'flex cursor-pointer items-center transition-colors',\n // Arbitrary sizes (the body-2 and caption tokens): the colour branch below\n // follows them through cn(), and tw-merge drops a custom text-{size}\n // once a text-{colour} lands after it. Measured: tabs rendered 16px.\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-[14px] leading-[140%]' : 'gap-1 rounded px-1.5 py-0.5 text-[12px] leading-[120%]',\n active === tab.id ? 'bg-bg-active text-text-primary' : 'text-text-secondary hover:bg-bg-hover',\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 </button>\n ))}\n </div>\n )\n}\n", "import { createContext, useCallback, useContext, useEffect, useMemo, useState, type ReactNode } from 'react'\nimport { createPortal } from 'react-dom'\nimport { IconCircleCheck } from '@tabler/icons-react'\nimport { cn } from './cn'\n\n/**\n * Peek's Toast (Figma: Alert) and its provider (2026-09-01), verbatim.\n *\n * The pill: three types \u2014 `success`, `brand`, `neutral` \u2014 an optional leading\n * circle-check, an optional action 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 owns positioning (bottom-left), the portal, and auto-dismiss\n * (5 s; pass `durationMs: 0` to keep one up). One toast at a time \u2014 a new\n * one replaces the standing one, it does not queue.\n */\nexport type ToastType = 'success' | 'brand' | 'neutral'\n\nexport interface ToastProps {\n label: string\n /** Visual variant per Figma (Alert component): success, brand, or neutral. */\n type?: ToastType\n /** Show the leading circle-check icon. 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_BY_TYPE: Record<ToastType, string> = {\n success: 'bg-success-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-[shadow:var(--shadow-md)]',\n brand: 'bg-accent-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-[shadow:var(--shadow-md)]',\n neutral: 'bg-bg-inset border border-border-subtle signal:border-border-default signal:shadow-[shadow:var(--shadow-md)]',\n}\n\nconst ICON_BY_TYPE: Record<ToastType, string> = {\n success: 'signal:text-success-default signal:drop-shadow-[0_0_5px_rgba(63,222,140,0.7)]',\n brand: 'signal:text-text-interactive signal:drop-shadow-[0_0_5px_rgba(86,200,255,0.6)]',\n neutral: 'signal:text-text-secondary',\n}\n\nconst ACTION_BORDER_BY_TYPE: Record<ToastType, string> = {\n success: 'signal:border signal:border-border-default signal:hover:border-border-strong',\n brand: 'signal:border signal:border-border-default signal:hover:border-border-strong',\n neutral: 'border border-border-default',\n}\n\nexport function Toast({ label, type = 'neutral', leadingIcon = true, actionLabel, onAction, className }: ToastProps) {\n const hasAction = !!(actionLabel && onAction)\n return (\n <div\n className={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_BY_TYPE[type],\n className,\n )}\n >\n <div className=\"flex items-center gap-2 shrink-0\">\n {leadingIcon && (\n <IconCircleCheck size={16} stroke={1.5} className={cn('text-text-primary shrink-0', ICON_BY_TYPE[type])} />\n )}\n <span className=\"font-normal text-[14px] leading-[1.4] text-text-primary whitespace-nowrap\">{label}</span>\n </div>\n {hasAction && (\n <button\n type=\"button\"\n onClick={onAction}\n className={cn(\n 'h-6 flex items-center justify-center gap-1 px-1 py-1 rounded-md shrink-0 transition-colors',\n ACTION_BORDER_BY_TYPE[type],\n type === 'neutral' ? 'hover:border-border-strong' : 'hover:opacity-80',\n )}\n >\n <span className=\"font-medium text-[12px] leading-[12px] text-text-primary whitespace-nowrap\">{actionLabel}</span>\n </button>\n )}\n </div>\n )\n}\n\nexport interface ToastOptions {\n label: string\n /** Visual variant per Figma (Alert component). Defaults to 'neutral'. */\n type?: ToastType\n /** When set, renders a clickable action on the right side. */\n actionLabel?: string\n onAction?: () => void\n /** Show the leading circle-check icon. 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 ActiveToast extends ToastOptions {\n id: number\n}\n\ninterface ToastValue {\n showToast: (opts: ToastOptions) => void\n dismissToast: () => void\n}\n\nconst ToastContext = createContext<ToastValue | null>(null)\n\nlet toastSeq = 0\n\nexport function ToastProvider({ children }: { children: ReactNode }) {\n const [toast, setToast] = useState<ActiveToast | null>(null)\n\n const showToast = useCallback((opts: ToastOptions) => {\n setToast({ ...opts, id: ++toastSeq })\n }, [])\n\n const dismissToast = useCallback(() => {\n setToast(null)\n }, [])\n\n useEffect(() => {\n if (!toast) return\n const duration = toast.durationMs ?? 5000\n if (duration <= 0) return\n const timer = setTimeout(() => {\n setToast((current) => (current?.id === toast.id ? null : current))\n }, duration)\n return () => clearTimeout(timer)\n }, [toast])\n\n const value = useMemo<ToastValue>(() => ({ showToast, dismissToast }), [showToast, dismissToast])\n\n return (\n <ToastContext.Provider value={value}>\n {children}\n {toast &&\n createPortal(\n <div className=\"fixed bottom-4 left-4 z-[100] pointer-events-none\">\n <Toast\n className=\"pointer-events-auto\"\n label={toast.label}\n type={toast.type ?? 'neutral'}\n leadingIcon={toast.leadingIcon ?? true}\n actionLabel={toast.actionLabel}\n onAction={\n toast.onAction\n ? () => {\n toast.onAction?.()\n dismissToast()\n }\n : undefined\n }\n />\n </div>,\n document.body,\n )}\n </ToastContext.Provider>\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", "jsx", "jsx", "jsxs", "jsx", "jsx", "jsxs", "useState", "useRef", "useEffect", "useLayoutEffect", "createPortal", "useState", "jsx", "jsx", "jsx", "jsxs", "useState", "jsx", "jsxs", "useState", "useRef", "useEffect", "useLayoutEffect", "createPortal", "useCallback", "useLayoutEffect", "useRef", "useState", "createPortal", "jsx", "jsxs", "GAP", "useState", "useRef", "useCallback", "useLayoutEffect", "createPortal", "jsx", "useRef", "useState", "jsx", "jsx", "jsxs", "jsx", "jsxs", "jsx", "jsxs", "useState", "useRef", "createContext", "useContext", "jsx", "jsxs", "IconCheck", "IconChevronDown", "useCallback", "useEffect", "useId", "useLayoutEffect", "useMemo", "useRef", "useState", "createPortal", "Fragment", "jsx", "jsxs", "useId", "useRef", "useState", "useMemo", "useCallback", "useLayoutEffect", "useEffect", "jsxs", "Fragment", "jsx", "IconChevronDown", "createPortal", "IconCheck", "jsx", "TextInput", "forwardRef", "jsx", "forwardRef", "Textarea", "useState", "useEffect", "createPortal", "IconX", "Fragment", "jsx", "jsxs", "useEffect", "createPortal", "IconX", "Fragment", "jsx", "jsxs", "useState", "useEffect", "useRef", "useState", "jsx", "useState", "useRef", "useEffect", "jsx", "jsxs", "jsx", "jsxs", "Fragment", "useCallback", "useLayoutEffect", "useRef", "useState", "jsx", "jsxs", "useRef", "useState", "useCallback", "useLayoutEffect", "Fragment", "jsx", "jsxs", "jsx", "jsx", "jsxs", "jsx", "jsx", "jsxs", "jsx", "jsxs", "useState", "IconChevronRight", "jsx", "jsxs", "useState", "IconChevronRight", "jsx", "jsxs", "createContext", "useCallback", "useContext", "useEffect", "useMemo", "useState", "createPortal", "jsx", "jsxs", "createContext", "useState", "useCallback", "useEffect", "useMemo", "createPortal", "useContext"]
|
|
3
|
+
"sources": ["../src/cn.ts", "../src/TopBar.tsx", "../src/AppShell.tsx", "../src/Avatar.tsx", "../src/AvatarGroup.tsx", "../src/Banner.tsx", "../src/Button.tsx", "../src/Checkbox.tsx", "../src/Chip.tsx", "../src/ChipInput.tsx", "../src/fit.ts", "../src/Menu.tsx", "../src/Kbd.tsx", "../src/SectionLabel.tsx", "../src/Tooltip.tsx", "../src/IconButton.tsx", "../src/IdentityMenu.tsx", "../src/Divider.tsx", "../src/Person.tsx", "../src/PersonTrigger.tsx", "../src/Field.tsx", "../src/Select.tsx", "../src/TextInput.tsx", "../src/Textarea.tsx", "../src/ConfirmDialog.tsx", "../src/DialogShell.tsx", "../src/EditableText.tsx", "../src/EmptyState.tsx", "../src/Skeleton.tsx", "../src/Breadcrumb.tsx", "../src/NavItem.tsx", "../src/Rail.tsx", "../src/RailItem.tsx", "../src/Sidebar.tsx", "../src/Property.tsx", "../src/Reaction.tsx", "../src/SearchInput.tsx", "../src/SectionHeader.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',\n]\n\nconst twMerge = extendTailwindMerge({\n extend: { classGroups: { 'font-size': [{ text: FONT_SIZE_TOKENS }] } },\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 }\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 { 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-[inset_0_1px_0_rgba(255,255,255,0.035)]',\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 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 <main className=\"min-w-0 flex-1 overflow-y-auto\">{children}</main>\n </div>\n </div>\n </div>\n )\n}\n", "import { useState } from 'react'\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 * 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. A picture that fails to load falls back to the initials. */\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 className?: string\n}\n\nexport function Avatar({ src, name, alt = '', size = 36, className }: AvatarProps) {\n const [broken, setBroken] = useState(false)\n const label = name || alt\n const picture = src && !broken ? src : undefined\n return (\n <div className={cn('rounded-sm overflow-hidden shrink-0 bg-bg-inset', className)} style={{ width: size, height: size }}>\n {picture ? (\n <img src={picture} alt={alt || name || ''} className=\"w-full h-full object-cover\" onError={() => setBroken(true)} />\n ) : label ? (\n <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 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 }}\n >\n {initialsFor(label)}\n </div>\n ) : (\n <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 </div>\n )}\n </div>\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 style={{ marginRight: -overlap, boxShadow: `0 0 0 ${ring}px var(--bg-surface)` }}\n >\n <Avatar size={size} name={member.name} src={member.picture} alt={member.name} />\n </span>\n ))}\n </div>\n )\n}\n", "import { cn } from './cn'\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 */\nexport type BannerTone = 'ok' | 'error' | 'info' | 'warning'\n\nexport interface BannerProps {\n tone: BannerTone\n children: string\n className?: string\n}\n\nconst TONE: 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, className }: BannerProps) {\n return (\n <div role={tone === 'error' ? 'alert' : 'status'} className={cn('px-4 py-2 text-body-2', TONE[tone], className)}>\n {children}\n </div>\n )\n}\n", "import type { ButtonHTMLAttributes, ReactNode } from 'react'\nimport { cn } from './cn'\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.\n *\n * Three variants and two sizes: 32px default / 24px small, 6px radius, 500\n * weight. Sizes are spelled as arbitrary values for the reason the README\n * records: tailwind-merge drops a custom `text-{size}` that is followed by a\n * `text-{colour}`.\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 */\nexport type ButtonVariant = 'primary' | 'outlined' | 'muted' | 'destructive'\nexport type ButtonSize = 'default' | 'small'\n\nexport interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n variant?: ButtonVariant\n size?: ButtonSize\n /** 16px, stroke 1.5 on the default size; 14px on small. */\n leadingIcon?: ReactNode\n children: ReactNode\n}\n\nexport function Button({\n variant = 'muted',\n size = 'default',\n leadingIcon,\n className,\n children,\n disabled,\n type = 'button',\n ...props\n}: ButtonProps) {\n const hasLeadingIcon = !!leadingIcon\n return (\n <button\n type={type}\n className={cn(\n 'inline-flex items-center justify-center gap-1 rounded-md transition-colors font-sans font-medium',\n size === 'default' && 'h-8 text-[14px] leading-[14px]',\n size === 'small' && 'h-6 text-[12px] leading-[12px]',\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 !disabled && variant === 'primary' && 'bg-accent-primary hover:bg-accent-hover text-text-inverse cursor-pointer signal:font-semibold',\n !disabled && variant === 'outlined' && 'border border-border-default hover:bg-bg-hover text-text-primary cursor-pointer',\n !disabled && variant === 'muted' && 'hover:bg-bg-hover text-text-primary cursor-pointer',\n !disabled && variant === 'destructive' && 'hover:bg-error-muted text-error-default cursor-pointer',\n disabled && 'bg-bg-disabled text-text-disabled pointer-events-none',\n disabled && variant === 'outlined' && 'border border-border-default',\n className,\n )}\n disabled={disabled}\n {...props}\n >\n {leadingIcon}\n {children}\n </button>\n )\n}\n", "import { 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.\n *\n * Controlled only; parents own the state. Rendered as a button with the\n * checkbox role so it works standalone or inside clickable rows: a caller\n * that toggles on the row's own click passes no `onChange`, and the square\n * goes inert rather than stealing the click.\n */\nexport interface CheckboxProps {\n checked: boolean\n onChange?: (checked: boolean) => void\n disabled?: boolean\n 'aria-label'?: string\n className?: string\n}\n\nexport function Checkbox({ checked, onChange, disabled = false, className, ...aria }: CheckboxProps) {\n return (\n <button\n type=\"button\"\n role=\"checkbox\"\n aria-checked={checked}\n aria-label={aria['aria-label']}\n disabled={disabled}\n onClick={(e) => {\n e.stopPropagation()\n onChange?.(!checked)\n }}\n className={cn(\n 'inline-flex items-center justify-center size-4 shrink-0 rounded-[4px] border transition-colors',\n checked\n ? 'bg-accent-primary border-accent-primary text-text-inverse'\n : 'bg-transparent border-border-strong hover:border-text-muted',\n disabled && 'opacity-50 pointer-events-none',\n onChange ? 'cursor-pointer' : 'pointer-events-none',\n className,\n )}\n >\n {checked && <IconCheck size={12} stroke={3} />}\n </button>\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 is a plain class here,\n * never merged, so tailwind-merge cannot drop it.\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-[rgba(86,200,255,0.3)]',\n info: 'bg-info-muted text-info-default signal:border signal:border-[rgba(86,200,255,0.3)]',\n warning: 'bg-warning-muted text-warning-default signal:border signal:border-[rgba(255,176,32,0.3)] signal:shadow-[shadow:0_0_5px_rgba(255,176,32,0.4)]',\n success: 'bg-success-muted text-success-default signal:border signal:border-[rgba(63,222,140,0.3)]',\n error: 'bg-error-muted text-error-default signal:border signal:border-[rgba(255,107,107,0.3)]',\n}\n\nexport function Chip({ type = 'neutral', label, leadingIcon, trailingIcon, className }: ChipProps) {\n return (\n <div className={cn('inline-flex items-center justify-center gap-1.5 rounded-full max-h-[20px] min-w-[16px] 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 <span className=\"text-chip whitespace-nowrap signal:font-mono signal:text-[10px] signal:font-semibold signal:tracking-[0.02em] 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 { useState, useRef, useMemo, useEffect, useLayoutEffect, type KeyboardEvent, type ReactNode } from 'react'\nimport { createPortal } from 'react-dom'\nimport { IconX } from '@tabler/icons-react'\nimport { cn } from './cn'\nimport { fitMenu } from './fit'\nimport { MenuItem } from './Menu'\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 */\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 className?: string\n}\n\nexport function InputChip({ label, leading, onRemove, className }: InputChipProps) {\n return (\n <div\n className={cn(\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]',\n // The padding follows the contents (Katerina, 2026-09-01): a face\n // sits 2px from the edge, a bare label needs 8px of air; the \u2715\n // brings its own box, so 4px behind it \u2014 8px when there isn't one.\n leading ? 'pl-[2px]' : 'pl-2',\n onRemove ? 'pr-1' : 'pr-2',\n className,\n )}\n >\n {leading && <span className=\"flex shrink-0 items-center\">{leading}</span>}\n <span className=\"text-caption font-medium text-text-primary\">{label}</span>\n {onRemove && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation()\n onRemove()\n }}\n className=\"size-4 flex items-center justify-center rounded-full hover:bg-bg-hover text-text-secondary\"\n aria-label={`Remove ${label}`}\n >\n <IconX size={10} stroke={1.5} />\n </button>\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. Peek's\n * version knew it was picking people: it read the directory from Peek's own\n * data layer and drew every face itself. Here the caller hands in `options`,\n * and \u2014 when the entries have faces or icons \u2014 the two leading slots: 16px\n * in a chip, 32px in a suggestion row. Nothing in this file knows what is\n * being picked.\n *\n * Suggestions appear only once the user types \u2014 focusing (or auto-focus on\n * dialog open) must not drop the full directory over the surface below.\n * Backspace on an empty query removes the last chip; Escape clears the query\n * when there is one and bubbles when there is not, so the surface around it\n * (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}\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}: ChipInputProps<T>) {\n const [query, setQuery] = useState('')\n const [highlight, setHighlight] = useState(0)\n const [isFocused, setIsFocused] = useState(false)\n const [anchorRect, setAnchorRect] = useState<DOMRect | null>(null)\n const wrapperRef = useRef<HTMLDivElement>(null)\n const inputRef = useRef<HTMLInputElement>(null)\n\n const matches = useMemo(() => {\n const selectedIds = new Set(value.map((o) => o.id))\n const excludedIds = new Set(excludeIds)\n const q = query.trim().toLowerCase()\n return options.filter((o) => {\n if (selectedIds.has(o.id)) return false\n if (excludedIds.has(o.id)) return false\n if (!q) return true\n return o.label.toLowerCase().includes(q) || (o.description ?? '').toLowerCase().includes(q)\n })\n }, [query, value, excludeIds, options])\n\n useEffect(() => {\n setHighlight(0)\n }, [query, matches.length])\n\n const showDropdown = isFocused && query.trim().length > 0 && matches.length > 0\n\n useLayoutEffect(() => {\n if (!showDropdown) return\n const update = () => {\n if (wrapperRef.current) setAnchorRect(wrapperRef.current.getBoundingClientRect())\n }\n update()\n window.addEventListener('resize', update)\n window.addEventListener('scroll', update, true)\n return () => {\n window.removeEventListener('resize', update)\n window.removeEventListener('scroll', update, true)\n }\n }, [showDropdown, value.length])\n\n function addOption(option: T) {\n onChange([...value, option])\n setQuery('')\n inputRef.current?.focus()\n }\n\n function removeOption(id: string) {\n onChange(value.filter((o) => o.id !== id))\n }\n\n function handleKeyDown(e: KeyboardEvent<HTMLInputElement>) {\n if (e.key === 'Backspace' && query === '' && value.length > 0) {\n // Consumed: removing a chip must not double as the surface's \"back\".\n e.preventDefault()\n removeOption(value[value.length - 1].id)\n return\n }\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n setHighlight((h) => Math.min(h + 1, Math.max(0, matches.length - 1)))\n return\n }\n if (e.key === 'ArrowUp') {\n e.preventDefault()\n setHighlight((h) => Math.max(h - 1, 0))\n return\n }\n if (e.key === 'Enter') {\n e.preventDefault()\n const target = matches[highlight]\n if (target) addOption(target)\n return\n }\n if (e.key === 'Escape') {\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 if (query !== '') {\n e.preventDefault()\n setQuery('')\n }\n }\n }\n\n return (\n <div className=\"relative\">\n <div\n ref={wrapperRef}\n 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 onClick={() => inputRef.current?.focus()}\n >\n {value.map((o) => (\n <InputChip key={o.id} label={o.label} leading={chipLeading?.(o)} onRemove={() => removeOption(o.id)} />\n ))}\n\n <input\n ref={inputRef}\n autoFocus={autoFocus}\n type=\"text\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n onKeyDown={handleKeyDown}\n onFocus={() => setIsFocused(true)}\n onBlur={() => {\n setTimeout(() => setIsFocused(false), 150)\n }}\n placeholder={value.length === 0 ? placeholder : ''}\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 </div>\n\n {showDropdown && anchorRect && createPortal(\n <div\n className=\"fixed z-[60] overflow-y-auto bg-bg-elevated border border-border-default rounded-lg shadow-lg\"\n /*\n Placed by fitMenu (2026-09-03), not hung blindly below: an input\n low on the screen flips its list upward instead of running the\n tail past the bottom edge. The rows are a fixed 48px, so the\n content height is arithmetic and needs no second render pass;\n the 240px cap is the old max-h-[240px].\n */\n style={{\n ...fitMenu({\n anchor: { left: anchorRect.left, top: anchorRect.top, bottom: anchorRect.bottom },\n menu: { width: anchorRect.width, contentHeight: matches.length * 48 },\n viewport: { width: window.innerWidth, height: window.innerHeight },\n cap: 240,\n }),\n width: anchorRect.width,\n }}\n >\n {matches.map((o, i) => (\n <MenuItem\n key={o.id}\n size=\"tall\"\n className=\"h-12 rounded-none\"\n leading={rowLeading?.(o)}\n label={o.label}\n description={o.description}\n selected={i === highlight}\n onMouseEnter={() => setHighlight(i)}\n onMouseDown={(e) => {\n e.preventDefault()\n addOption(o)\n }}\n />\n ))}\n </div>,\n document.body\n )}\n </div>\n )\n}\n", "/**\n * Viewport geometry for everything that floats \u2014 pure, so the decisions are\n * testable without a browser.\n *\n * One module, because the failure it prevents keeps recurring one surface at\n * a time: the files-panel picker was cut off at the right edge (fixed in\n * Select, 2026-09-01), then a reply menu's highlight submenu was cut off the\n * same way and the identity menu vanished under a z-indexed header\n * (2026-09-03). A menu that hand-rolls its own geometry is a menu waiting to\n * be the next screenshot; the shell calls these instead.\n */\n\nconst MARGIN = 8\nconst GAP = 4\n\n/**\n * Where a menu of this size goes, given its anchor and the viewport.\n *\n * Left is clamped inside the viewport with an 8px margin. Height is capped\n * at `cap` \u2014 Select's option list keeps the old `max-h-72` (288), a menu\n * panel passes none and uses all the room it opens into, so it scrolls only\n * when the screen truly has no space (the identity panel got Select's cap\n * by accident and grew a scrollbar at full height, 2026-09-03) \u2014 but never\n * taller than that room; when the room below the anchor is smaller than\n * both the content and the room above, the menu opens UPWARD (anchored to\n * the trigger's top via `bottom`). The 120px floor keeps a menu usable even\n * in a cramped corner \u2014 scrollable beats invisible.\n */\nexport function fitMenu({\n anchor,\n menu,\n viewport,\n cap = Number.POSITIVE_INFINITY,\n}: {\n anchor: { left: number; top: number; bottom: number }\n menu: { width: number; contentHeight: number }\n viewport: { width: number; height: number }\n /** Tallest the menu may stand even with room to spare. Absent: the room is the only limit. */\n cap?: number\n}): { left: number; top?: number; bottom?: number; maxHeight: number } {\n const left = Math.max(MARGIN, Math.min(anchor.left, viewport.width - menu.width - MARGIN))\n const below = viewport.height - anchor.bottom - GAP - MARGIN\n const above = anchor.top - GAP - MARGIN\n const openUp = below < Math.min(menu.contentHeight, cap) && above > below\n const maxHeight = Math.max(Math.min(cap, openUp ? above : below), 120)\n return openUp\n ? { left, bottom: viewport.height - anchor.top + GAP, maxHeight }\n : { left, top: anchor.bottom + GAP, maxHeight }\n}\n\n/**\n * Keep a box a caller has already placed fully on screen.\n *\n * For the menus whose caller measured a rect and chose the corner itself\n * (`position`): the caller's math stays authoritative, but its result can no\n * longer land off screen \u2014 it is slid inside the viewport, never flipped,\n * and capped to the viewport's height with the same 120px floor as fitMenu.\n */\nexport function clampBox({\n box,\n viewport,\n}: {\n box: { left: number; top: number; width: number; height: number }\n viewport: { width: number; height: number }\n}): { left: number; top: number; maxHeight: number } {\n const left = Math.max(MARGIN, Math.min(box.left, viewport.width - box.width - MARGIN))\n const maxHeight = Math.max(Math.min(box.height, viewport.height - 2 * MARGIN), 120)\n const top = Math.max(MARGIN, Math.min(box.top, viewport.height - maxHeight - MARGIN))\n return { left, top, maxHeight }\n}\n\n/**\n * Where a submenu goes, given its trigger row and the viewport.\n *\n * To the RIGHT of the row when it fits, flipped to the LEFT when it does not\n * \u2014 the measured flip two menus hand-rolled and one of them broke (the copy\n * dropped the ref its measurement read, so it measured nothing and always\n * opened rightward, off the screen). Top-aligned with the row, slid up when\n * the tail would run past the bottom edge.\n */\nexport function fitSubmenu({\n row,\n panel,\n viewport,\n}: {\n row: { left: number; right: number; top: number }\n panel: { width: number; height: number }\n viewport: { width: number; height: number }\n}): { left: number; top: number } {\n const fitsRight = row.right + GAP + panel.width <= viewport.width - MARGIN\n const left = Math.max(MARGIN, fitsRight ? row.right + GAP : row.left - GAP - panel.width)\n const top = Math.max(MARGIN, Math.min(row.top, viewport.height - panel.height - MARGIN))\n return { left, top }\n}\n", "import { createContext, useCallback, useContext, useEffect, useLayoutEffect, useRef, useState, type ComponentPropsWithRef, type CSSProperties, type ReactNode } from 'react'\nimport { IconChevronRight } from '@tabler/icons-react'\nimport { createPortal } from 'react-dom'\nimport { cn } from './cn'\nimport { Kbd } from './Kbd'\nimport { clampBox, fitMenu, fitSubmenu } from './fit'\nimport { SectionLabel } from './SectionLabel'\n\n/**\n * THE menu shell (2026-09-01) \u2014 extracted once, for every menu in every app.\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`, hover fill, 14px text, destructive\n * ones in the error colour; section headings as a SectionLabel in a 32px\n * row; and the two exits every menu has \u2014 Escape and a click outside \u2014\n * owned by the menu, never copied into a caller.\n *\n * Where it goes is owned here too (2026-09-03), because the two ways a menu\n * goes wrong are both placement: it opens inside a stacking context or a\n * scroll container and something covers or clips it, or it opens near an\n * edge and runs off the screen. Both shipped \u2014 the identity menu vanished\n * under a z-indexed panel header, a submenu was cut by the right edge \u2014 so\n * the shell now portals to the body (nothing in an app can cover the body's\n * last child at z-50) and fits itself to the viewport (fit.ts, the same\n * measured geometry Select uses). A caller hands over a trigger, not\n * coordinates.\n *\n * Three anchorings, in order of preference:\n * - `anchor` (an element, usually the trigger): portalled, measured, and\n * placed by `fitMenu` \u2014 under the anchor, flipped above when the room\n * below is worse, clamped inside the viewport, height-capped with its own\n * scrollbar. `align=\"right\"` hangs the menu's right edge from the\n * anchor's. Closes on resize and on any page scroll, because both move\n * the anchor out from under it.\n * - `position` (viewport coordinates the caller computed): portalled and\n * clamped (`clampBox`) \u2014 the caller's corner survives, but can no longer\n * land off screen.\n * - neither: in-flow under a `relative` wrapper, right-aligned. For stories\n * and static surfaces only \u2014 inside an app this mode inherits every\n * ancestor's stacking context and clip, which is how the identity menu\n * got covered.\n */\nexport interface MenuProps {\n onClose: () => void\n /** The trigger \u2014 an element, or the rect a click handler already measured.\n * The menu portals to the body and places itself against it. */\n anchor?: HTMLElement | DOMRect | null\n /** With `anchor`: which of the menu's edges hangs from the anchor's. Default left. */\n align?: 'left' | 'right'\n /** Viewport coordinates; the menu is portalled, hung from `top`, aligned to whichever edge is given, and clamped on screen. */\n position?: { top: number; right: number } | { top: number; left: number }\n /** Close 150ms after the pointer leaves the menu \u2014 the hover-flow menus\n * (quick-menu cards) dismiss this way. The grace period is shared with any\n * open MenuSub panel, so crossing into a portalled submenu never counts as\n * leaving. */\n closeOnLeave?: boolean\n children: ReactNode\n className?: string\n}\n\n/** MenuSub reports its hover into the enclosing Menu's leave-grace timer, so\n * a `closeOnLeave` menu survives the pointer crossing into a portalled\n * submenu panel \u2014 the one hover region the old inline submenus had for free. */\nconst MenuHoverContext = createContext<{ hold: () => void; release: () => void } | 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 and\n * placement, and that is right for a menu opened from a trigger \u2014 but a\n * type-ahead popup inside a text editor cannot have them: the editor's\n * suggestion plugin already owns the keyboard and positions the popup, and a\n * second Escape handler fights it. So Peek's `@`, `/` and `[` menus each drew\n * 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 children: ReactNode\n}\n\nexport function MenuPanel({ children, className, ...props }: MenuPanelProps) {\n return (\n <div\n className={cn('flex flex-col rounded-lg border border-border-default bg-bg-elevated p-2 shadow-lg', className)}\n {...props}\n >\n {children}\n </div>\n )\n}\n\nexport function Menu({ onClose, anchor, align = 'left', position, closeOnLeave = false, children, className }: MenuProps) {\n const ref = useRef<HTMLDivElement>(null)\n const leaveTimer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n const hold = useCallback(() => clearTimeout(leaveTimer.current), [])\n const release = useCallback(() => {\n if (!closeOnLeave) return\n clearTimeout(leaveTimer.current)\n leaveTimer.current = setTimeout(onClose, 150)\n }, [closeOnLeave, onClose])\n useEffect(() => () => clearTimeout(leaveTimer.current), [])\n const portalled = Boolean(anchor || position)\n /** Where the menu actually goes \u2014 measured against the viewport after a\n * hidden provisional render, so it is never covered and never cut off. */\n const [placed, setPlaced] = useState<{ left: number; top?: number; bottom?: number; maxHeight: number } | null>(null)\n\n useEffect(() => {\n const onDown = (event: MouseEvent) => {\n if (!ref.current?.contains(event.target as Node)) onClose()\n }\n const onKey = (event: KeyboardEvent) => {\n if (event.key === 'Escape') onClose()\n }\n document.addEventListener('mousedown', onDown)\n document.addEventListener('keydown', onKey)\n return () => {\n document.removeEventListener('mousedown', onDown)\n document.removeEventListener('keydown', onKey)\n }\n }, [onClose])\n\n // Callers build `position` inline every render; depending on its\n // coordinates rather than the object keeps the effect from re-running\n // (and re-placing) on every parent render.\n const posLeft = position && 'left' in position ? position.left : undefined\n const posRight = position && 'right' in position ? position.right : undefined\n const posTop = position?.top\n\n useLayoutEffect(() => {\n if (!portalled || !ref.current) return\n const viewport = { width: window.innerWidth, height: window.innerHeight }\n const menu = ref.current\n if (anchor) {\n const rect = anchor instanceof Element ? anchor.getBoundingClientRect() : anchor\n const left = align === 'right' ? rect.right - menu.offsetWidth : rect.left\n setPlaced(\n fitMenu({\n anchor: { left, top: rect.top, bottom: rect.bottom },\n menu: { width: menu.offsetWidth, contentHeight: menu.scrollHeight },\n viewport,\n }),\n )\n } else if (posTop !== undefined) {\n const left = posLeft ?? viewport.width - (posRight ?? 0) - menu.offsetWidth\n setPlaced(clampBox({ box: { left, top: posTop, width: menu.offsetWidth, height: menu.offsetHeight }, viewport }))\n }\n }, [portalled, anchor, align, posLeft, posRight, posTop])\n\n /* An anchored menu is placed against its trigger's rect, and a resize or a\n page scroll moves the trigger out from under it \u2014 close, as Select does.\n A scroll INSIDE the menu is its own capped list working; leave those. */\n useEffect(() => {\n if (!anchor) return\n const onScroll = (event: Event) => {\n if (event.target instanceof Node && ref.current?.contains(event.target)) return\n onClose()\n }\n window.addEventListener('resize', onClose)\n window.addEventListener('scroll', onScroll, true)\n return () => {\n window.removeEventListener('resize', onClose)\n window.removeEventListener('scroll', onScroll, true)\n }\n }, [anchor, onClose])\n\n const style: CSSProperties | undefined = portalled\n ? placed\n ? { left: placed.left, top: placed.top, bottom: placed.bottom, maxHeight: placed.maxHeight }\n : // The provisional render: measured by the layout effect, never seen.\n { left: 0, top: 0, visibility: 'hidden' }\n : undefined\n const node = (\n <MenuHoverContext.Provider value={{ hold, release }}>\n <MenuPanel\n ref={ref}\n role=\"menu\"\n data-interactive\n className={cn(\n 'z-50 min-w-[180px]',\n portalled ? 'fixed overflow-y-auto' : 'absolute right-0 top-full mt-1',\n className,\n )}\n style={style}\n onClick={(event) => event.stopPropagation()}\n onMouseEnter={closeOnLeave ? hold : undefined}\n onMouseLeave={closeOnLeave ? release : undefined}\n >\n {children}\n </MenuPanel>\n </MenuHoverContext.Provider>\n )\n return portalled ? createPortal(node, document.body) : node\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 like those were: opens at once, closes 150ms after the\n * pointer leaves row and panel both, so the diagonal from row to panel\n * survives. The panel portals to the body and is placed by `fitSubmenu` \u2014\n * right of the row when it fits, left when it does not, never past an edge\n * \u2014 so it also escapes whatever container its menu happens to be in.\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 const [open, setOpen] = useState(false)\n const rowRef = useRef<HTMLDivElement>(null)\n const panelRef = useRef<HTMLDivElement>(null)\n const closeTimer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n const [placed, setPlaced] = useState<{ left: number; top: number } | null>(null)\n // The panel portals out of the menu's DOM, so hovering it would read as\n // \"left the menu\" to a closeOnLeave shell \u2014 report hover upward instead.\n const menuHover = useContext(MenuHoverContext)\n\n const enter = () => {\n clearTimeout(closeTimer.current)\n menuHover?.hold()\n setOpen(true)\n }\n const leave = () => {\n closeTimer.current = setTimeout(() => setOpen(false), 150)\n menuHover?.release()\n }\n useEffect(() => () => clearTimeout(closeTimer.current), [])\n\n useLayoutEffect(() => {\n if (!open || !rowRef.current || !panelRef.current) {\n setPlaced(null)\n return\n }\n const row = rowRef.current.getBoundingClientRect()\n setPlaced(\n fitSubmenu({\n row: { left: row.left, right: row.right, top: row.top },\n panel: { width: panelRef.current.offsetWidth, height: panelRef.current.offsetHeight },\n viewport: { width: window.innerWidth, height: window.innerHeight },\n }),\n )\n }, [open])\n\n return (\n <div ref={rowRef} onMouseEnter={enter} onMouseLeave={leave}>\n <MenuItem label={label} leading={leading} selected={selected} submenu />\n {open &&\n createPortal(\n <MenuPanel\n ref={panelRef}\n role=\"menu\"\n data-interactive\n className={cn('fixed z-50 w-[160px]', className)}\n style={placed ?? { left: 0, top: 0, visibility: 'hidden' }}\n onMouseEnter={enter}\n onMouseLeave={leave}\n >\n {children}\n </MenuPanel>,\n document.body,\n )}\n </div>\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\nexport function MenuItem({ label, children, size = 'default', description, leading, trailing, hint, shortcut, submenu, destructive, selected, className, ...props }: MenuItemProps) {\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 <button\n type=\"button\"\n role=\"menuitem\"\n className={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 hover:bg-bg-hover',\n // tall: as tall as its content, never shorter than 40px (Katerina,\n // 2026-09-01) \u2014 a single-line picker row sits at 40, a row with a\n // 32px face and a role line comes out at its natural 48. One rule,\n // not a hand-picked height per file.\n size === 'tall' ? 'min-h-10 gap-3 px-3 py-1.5' : 'gap-2 px-2 py-1.5',\n selected && 'bg-bg-hover',\n className,\n )}\n {...props}\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-[14px] leading-[140%]', destructive ? 'text-error-default' : 'text-text-primary')}>\n {label}\n </span>\n {description && <span className=\"truncate text-[12px] leading-[120%] 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', hint && selected && 'opacity-0')}>\n {edge}\n </span>\n )}\n {hint && (\n <span className={cn('flex items-center opacity-0 group-hover:opacity-100', selected && 'opacity-100')}>\n {hint}\n </span>\n )}\n </span>\n )}\n </button>\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-[9px] font-medium leading-[115%] signal:font-mono signal:text-[9.5px] signal:tracking-[0.04em]\">{target}</span>\n )}\n </span>\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). */\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 return (\n <div className=\"flex flex-col\">\n <div className={cn('flex h-8 items-center px-2', className)}>\n <SectionLabel className=\"text-text-secondary\">{label}</SectionLabel>\n </div>\n {children}\n </div>\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 { 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-[12px] leading-[120%] font-normal text-text-secondary',\n 'signal:border-b-2 signal:pt-[2px] signal:pb-px signal:bg-[rgba(255,255,255,.05)] signal:font-mono signal:text-[10px]',\n 'ship:border-b-2 ship:pt-[2px] ship:pb-px ship:bg-[rgba(255,255,255,.05)] ship:font-mono ship:text-[10px]',\n className,\n )}\n >\n {children}\n </kbd>\n )\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-[12px] leading-[12px] font-medium text-text-primary signal:font-mono signal:text-[10px] signal:uppercase signal:tracking-[0.14em]',\n className,\n )}\n >\n {children}\n </span>\n )\n}\n", "import { useCallback, useLayoutEffect, useRef, useState, type CSSProperties, type ReactNode } from 'react'\nimport { createPortal } from 'react-dom'\nimport { cn } from './cn'\nimport { Kbd } from './Kbd'\n\n/**\n * Peek's Tooltip and WithTooltip (2026-08-28), verbatim, in one file.\n *\n * `Tooltip` is the surface: a 30px elevated pill with a caption. `WithTooltip`\n * wraps a trigger and portals the surface above or below it on hover, fixed\n * to the viewport and kept 8px inside it. The wrapper is `inline-flex` and\n * shrinks nothing \u2014 wrap a control, never a block (a form inside it\n * collapses to its content width; Ship learnt that).\n */\nexport interface TooltipProps {\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 className?: string\n}\n\nexport function Tooltip({ label, shortcut, className }: 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)}>\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\nconst GAP = 6\nconst VIEWPORT_PAD = 8\n\nexport function WithTooltip({ label, shortcut, placement = 'top', wrapperClassName, children }: WithTooltipProps) {\n const [show, setShow] = useState(false)\n const ref = useRef<HTMLDivElement>(null)\n const tooltipRef = useRef<HTMLDivElement>(null)\n const [style, setStyle] = useState<CSSProperties>({ position: 'fixed', zIndex: 9999, pointerEvents: 'none', visibility: 'hidden' })\n\n const reposition = useCallback(() => {\n const trigger = ref.current\n const tip = tooltipRef.current\n if (!trigger || !tip) return\n const triggerRect = trigger.getBoundingClientRect()\n const tipRect = tip.getBoundingClientRect()\n let top = placement === 'bottom' ? triggerRect.bottom + GAP : triggerRect.top - tipRect.height - GAP\n // The vertical axis flips and clamps like the horizontal one always has\n // (2026-09-03): a `top` tooltip on a control near the viewport's top edge\n // was the one floating surface left that could leave the screen.\n if (placement === 'top' && top < VIEWPORT_PAD) top = triggerRect.bottom + GAP\n else if (placement === 'bottom' && top + tipRect.height > window.innerHeight - VIEWPORT_PAD) top = triggerRect.top - tipRect.height - GAP\n top = Math.max(VIEWPORT_PAD, Math.min(top, window.innerHeight - tipRect.height - VIEWPORT_PAD))\n let left = triggerRect.left + triggerRect.width / 2 - tipRect.width / 2\n left = Math.max(VIEWPORT_PAD, Math.min(left, window.innerWidth - tipRect.width - VIEWPORT_PAD))\n setStyle({ position: 'fixed', top, left, zIndex: 9999, pointerEvents: 'none', visibility: 'visible' })\n }, [placement])\n\n useLayoutEffect(() => {\n if (show) reposition()\n }, [show, reposition])\n\n return (\n <div ref={ref} className={cn('inline-flex shrink-0', wrapperClassName)} onMouseEnter={() => setShow(true)} onMouseLeave={() => setShow(false)}>\n {children}\n {show &&\n createPortal(\n <div ref={tooltipRef} style={style}>\n <Tooltip label={label} shortcut={shortcut} />\n </div>,\n document.body,\n )}\n </div>\n )\n}\n", "import type { ButtonHTMLAttributes, ReactNode } from 'react'\nimport { cn } from './cn'\nimport { WithTooltip } 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.\n */\nexport type IconButtonVariant = 'muted' | 'outlined' | 'primary'\n\nexport interface IconButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\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 /** The icon: 16px, stroke 1.5. */\n children: ReactNode\n}\n\nexport function IconButton({\n variant = 'muted',\n className,\n children,\n disabled,\n tooltip,\n tooltipShortcut,\n tooltipPlacement,\n type = 'button',\n ...props\n}: IconButtonProps) {\n const button = (\n <button\n type={type}\n className={cn(\n 'flex items-center justify-center p-1 rounded-lg transition-colors shrink-0 cursor-pointer',\n !disabled && variant === 'primary' && 'bg-accent-primary hover:bg-accent-hover text-text-inverse',\n !disabled && variant === 'muted' && 'text-text-secondary hover:bg-bg-hover hover:text-text-primary',\n !disabled && variant === 'outlined' && 'border border-border-default hover:bg-bg-hover text-text-secondary',\n disabled && variant === 'primary' && 'bg-bg-disabled text-text-disabled',\n disabled && variant === 'muted' && 'text-text-disabled',\n disabled && variant === 'outlined' && 'border border-border-default text-text-disabled',\n disabled && 'pointer-events-none cursor-not-allowed',\n className,\n )}\n disabled={disabled}\n {...props}\n >\n {children}\n </button>\n )\n\n if (tooltip) {\n return (\n <WithTooltip label={tooltip} shortcut={tooltipShortcut} placement={tooltipPlacement}>\n {button}\n </WithTooltip>\n )\n }\n return button\n}\n", "import { useRef, useState, type ReactNode } from 'react'\nimport { cn } from './cn'\nimport { Divider } from './Divider'\nimport { Menu, MenuItem, 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\nexport interface IdentityPanelProps extends Omit<IdentityMenuProps, 'compact' | 'className'> {\n onClose: () => void\n /** The trigger to hang from \u2014 the panel portals to the body and fits the\n * viewport, so no header, sidebar or scroll container can cover it (the\n * z-10 floating top bar trapped the old in-flow panel under a z-20 panel\n * header, 2026-09-03). Absent, the panel stands in flow \u2014 the stories. */\n anchor?: HTMLElement | null\n /** On the Menu surface \u2014 the stories pass `static` to stand it in flow. */\n className?: string\n}\n\n/** The menu alone \u2014 what `IdentityMenu` opens. Exported so the stories show\n * the designed artifact rather than a closed trigger, and for any surface\n * that wants the panel without the trigger. */\nexport function IdentityPanel({ me, signedIn, relayUrl, idBase, onCopyKey, onSignOut, onClose, anchor, className, children }: IdentityPanelProps) {\n const act = (action: () => void) => () => {\n onClose()\n action()\n }\n const appRows = typeof children === 'function' ? children(onClose) : children\n return (\n <Menu onClose={onClose} anchor={anchor} align=\"right\" className={cn('w-72', className)}>\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 </Menu>\n )\n}\n\nexport function IdentityMenu({ me, signedIn, relayUrl, idBase, onCopyKey, onSignOut, compact = false, className, children }: IdentityMenuProps) {\n const [open, setOpen] = useState(false)\n /* The wrapper is the anchor: the panel hangs its right edge from this\n div's, exactly where the old in-flow `absolute right-0` put it \u2014 but\n portalled, so nothing z-indexed in the app can cover it. */\n const anchorRef = useRef<HTMLDivElement>(null)\n\n return (\n <div ref={anchorRef} className={cn('relative', className)}>\n <PersonTrigger\n name={me.name}\n picture={me.picture}\n fallback=\"Anonymous\"\n compact={compact}\n size={compact ? 36 : undefined}\n open={open}\n // Swallowed so the menu's outside-mousedown dismiss does not fire\n // first and turn the toggle into a close-then-reopen flicker.\n onMouseDown={(event) => event.stopPropagation()}\n onClick={() => setOpen((value) => !value)}\n // The row shape is named by its own text \u2014 the person. Only the bare\n // face needs a label; naming the row would override the person's name\n // as the accessible name (Ship's tests find the trigger by it).\n aria-label={compact ? 'Account menu' : undefined}\n />\n\n {open && (\n <IdentityPanel\n me={me}\n signedIn={signedIn}\n relayUrl={relayUrl}\n idBase={idBase}\n onCopyKey={onCopyKey}\n onSignOut={onSignOut}\n onClose={() => setOpen(false)}\n anchor={anchorRef.current}\n >\n {children}\n </IdentityPanel>\n )}\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 { 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 \u2014 and the separator\n * role for assistive tech.\n */\nexport interface DividerProps {\n orientation?: 'horizontal' | 'vertical'\n className?: string\n}\n\nexport function Divider({ orientation = 'horizontal', className }: DividerProps) {\n return (\n <div\n role=\"separator\"\n aria-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 { 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.\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 <button\n type=\"button\"\n aria-haspopup=\"menu\"\n aria-expanded={open}\n className={cn('cursor-pointer rounded-full focus:outline-none', className)}\n {...props}\n >\n <Avatar name={name} src={picture} size={size ?? 36} />\n </button>\n )\n }\n return (\n <button\n type=\"button\"\n aria-haspopup=\"menu\"\n aria-expanded={open}\n className={cn(\n // The size is an arbitrary value (the body-2 token): this list goes through\n // cn(), and tw-merge silently drops a custom text-{size} once a\n // text-{colour} follows it. Measured: the name rendered 16px.\n 'flex h-8 cursor-pointer items-center gap-1.5 rounded-md pl-1.5 pr-1.5 text-[14px] leading-[140%] text-text-primary transition-colors hover:bg-bg-hover',\n 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 </button>\n )\n}\n", "import { createContext, useContext, useId, type ReactNode } from 'react'\n\n/**\n * The id of the control this Field labels.\n *\n * **A control has to opt in by calling `useFieldControlId`.** The automatic\n * alternative is nesting the control inside the `<label>`, which associates\n * anything by construction and needs no cooperation \u2014 and it is not used here,\n * because a control that is *both* nested in a label and named by its `htmlFor`\n * can receive two activations from one click. That is a real hazard for a\n * checkbox and a latent one for everything else, and this library has a\n * `Checkbox`.\n *\n * So: one explicit mechanism, and a test that pins it for every primitive that\n * uses it (`Field.test.tsx`). A new primitive that renders a labelable element\n * calls this hook and spreads the result; one that does not is unlabelled, and\n * the test is where that gets noticed.\n */\nconst FieldControlIdContext = createContext<string | undefined>(undefined)\n\n/**\n * The id a surrounding `Field` wants this control to have, falling back to the\n * caller's own outside one.\n *\n * **Inside a Field, the Field wins**, which is the opposite of what I wrote\n * first and the test caught within the minute. Letting a control's own `id`\n * take precedence leaves the label's `htmlFor` pointing at the id the Field\n * generated and the control answering to a different one \u2014 which is this\n * ticket's defect exactly, reproduced by the fix for it, and it fails with the\n * same message: *\"Found a label with the text of: Title, however no form\n * control was found associated to that label.\"*\n *\n * A caller who needs to choose the id names it on the Field (`htmlFor`), which\n * is the one place that can set both halves. Outside a Field there is nothing\n * to disagree with, so the caller's id is used.\n */\nexport function useFieldControlId(ownId?: string): string | undefined {\n const fromField = useContext(FieldControlIdContext)\n return fromField ?? ownId\n}\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 as a plain\n * class, never merged.\n *\n * The label names its control (SHA-17). It did not, 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.\n */\nexport interface FieldProps {\n label: string\n required?: boolean\n /**\n * Override the generated id. Only needed when something outside has to name\n * the control \u2014 an `aria-describedby` elsewhere, or a form library.\n */\n htmlFor?: string\n children: ReactNode\n}\n\nexport function Field({ label, required = false, htmlFor, children }: FieldProps) {\n const generated = useId()\n const id = htmlFor ?? generated\n return (\n <div className=\"flex flex-col gap-2\">\n <label\n htmlFor={id}\n className={`text-input-label text-text-primary${required ? ' flex items-center' : ''}`}\n >\n {label}\n {required && <span className=\"text-error-default ml-0.5\">*</span>}\n </label>\n <FieldControlIdContext.Provider value={id}>{children}</FieldControlIdContext.Provider>\n </div>\n )\n}\n", "import { IconCheck, IconChevronDown } from '@tabler/icons-react'\nimport { useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, type KeyboardEvent, type ReactNode } from 'react'\nimport { createPortal } from 'react-dom'\nimport { cn } from './cn'\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.\n *\n * A button that opens a portalled listbox under itself: arrows move (and keep\n * the active option scrolled into view), Home and End jump, Enter and Space\n * pick, Escape closes and returns focus to the trigger, Tab closes, a click\n * outside closes, a PAGE scroll or resize closes (the list is fixed to where\n * the trigger was) \u2014 a scroll *inside* the list is the list's own business\n * and must not dismiss it. The menu keeps itself on screen: clamped to the\n * viewport's sides, height capped to the space it actually has, opening\n * upward when the room below is worse than the room above (Katerina,\n * 2026-09-01 \u2014 the files-panel picker was cut right and bottom, and its own\n * scroll closed it). Two sizes; `disabled` explains nothing by itself \u2014 wrap\n * it in a tooltip 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 placeholder?: string\n disabled?: boolean\n className?: string\n}\n\n// The geometry lives in fit.ts now (2026-09-03) \u2014 shared with the Menu\n// shell, so a cut-off surface is fixed once. Re-exported because this is\n// where it grew up and its test still names it by this address.\nimport { fitMenu } from './fit'\nexport { fitMenu }\n\nexport function Select({ value, onChange, options, size = 'default', ariaLabel, placeholder = 'Select\u2026', disabled, className }: SelectProps) {\n const id = useId()\n const triggerRef = useRef<HTMLButtonElement>(null)\n const menuRef = useRef<HTMLDivElement>(null)\n const [rect, setRect] = useState<DOMRect | null>(null)\n /** Where the menu actually goes \u2014 measured against the viewport after the\n * provisional render, so it is never cut off by an edge. */\n const [placement, setPlacement] = useState<{\n left: number\n top?: number\n bottom?: number\n maxHeight: number\n } | null>(null)\n const open = rect !== null\n\n const selectedIndex = useMemo(() => Math.max(0, options.findIndex((o) => o.value === value)), [options, value])\n const [activeIndex, setActiveIndex] = useState(selectedIndex)\n\n const close = useCallback((refocus: boolean) => {\n setRect(null)\n if (refocus) triggerRef.current?.focus()\n }, [])\n\n const openMenu = useCallback(() => {\n setActiveIndex(selectedIndex)\n setRect(triggerRef.current?.getBoundingClientRect() ?? null)\n }, [selectedIndex])\n\n /*\n * Fit the menu to the viewport, before paint.\n *\n * The provisional render sits at the trigger's corner and is invisible;\n * this measures it and decides the real box: left clamped inside the\n * viewport, height capped to the space available, and the whole thing\n * opening UPWARD when the room below is smaller than both the content and\n * the room above. A menu that is always fully on screen is also the only\n * kind whose scrollbar can actually be used.\n */\n useLayoutEffect(() => {\n if (!rect || !menuRef.current) {\n setPlacement(null)\n return\n }\n setPlacement(\n fitMenu({\n anchor: { left: rect.left, top: rect.top, bottom: rect.bottom },\n menu: { width: menuRef.current.offsetWidth, contentHeight: menuRef.current.scrollHeight },\n viewport: { width: window.innerWidth, height: window.innerHeight },\n // The option list keeps its classic height (the old max-h-72); a menu\n // panel passes no cap and stands as tall as the room it opens into.\n cap: 288,\n }),\n )\n }, [rect, options.length])\n\n // Keyboard follows the highlight: without this, arrowing past the fold\n // moved `activeIndex` into rows the capped menu never showed.\n useEffect(() => {\n if (!open) return\n // Optional call: jsdom implements neither scrolling nor this method, and\n // a consumer's component tests should not crash for a scroll nicety.\n document.getElementById(`${id}-${activeIndex}`)?.scrollIntoView?.({ block: 'nearest' })\n }, [open, activeIndex, id])\n\n // Outside click and Escape \u2014 the two exits every menu has. `mousedown`\n // rather than `click`, so a press that starts outside dismisses at once.\n useEffect(() => {\n if (!open) return\n const onDown = (e: MouseEvent) => {\n if (menuRef.current?.contains(e.target as Node)) return\n if (triggerRef.current?.contains(e.target as Node)) return\n setRect(null)\n }\n const onResize = () => setRect(null)\n /*\n * A PAGE scroll moves the anchor out from under the fixed menu, so it\n * closes. A scroll INSIDE the menu is the menu working as designed \u2014\n * capture phase sees those too, and closing on them made the list\n * impossible to scroll at all (the bug this comment survives to prevent).\n */\n const onScroll = (e: Event) => {\n if (e.target instanceof Node && menuRef.current?.contains(e.target)) return\n setRect(null)\n }\n document.addEventListener('mousedown', onDown)\n window.addEventListener('resize', onResize)\n window.addEventListener('scroll', onScroll, true)\n return () => {\n document.removeEventListener('mousedown', onDown)\n window.removeEventListener('resize', onResize)\n window.removeEventListener('scroll', onScroll, true)\n }\n }, [open])\n\n const pick = (index: number) => {\n const option = options[index]\n if (!option) return\n onChange(option.value)\n close(true)\n }\n\n const onKeyDown = (e: KeyboardEvent) => {\n if (!open) {\n if (['ArrowDown', 'ArrowUp', 'Enter', ' '].includes(e.key)) {\n e.preventDefault()\n openMenu()\n }\n return\n }\n switch (e.key) {\n case 'Escape':\n e.preventDefault()\n close(true)\n break\n case 'ArrowDown':\n e.preventDefault()\n setActiveIndex((i) => Math.min(options.length - 1, i + 1))\n break\n case 'ArrowUp':\n e.preventDefault()\n setActiveIndex((i) => Math.max(0, i - 1))\n break\n case 'Home':\n e.preventDefault()\n setActiveIndex(0)\n break\n case 'End':\n e.preventDefault()\n setActiveIndex(options.length - 1)\n break\n case 'Enter':\n case ' ':\n e.preventDefault()\n pick(activeIndex)\n break\n case 'Tab':\n close(false)\n break\n }\n }\n\n const selected = options.find((o) => o.value === value)\n\n return (\n <>\n <button\n ref={triggerRef}\n type=\"button\"\n disabled={disabled}\n aria-haspopup=\"listbox\"\n aria-expanded={open}\n aria-label={ariaLabel}\n onClick={() => (open ? close(false) : openMenu())}\n onKeyDown={onKeyDown}\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-[14px] leading-[1.4] font-normal',\n size === 'small' && 'h-6 px-2 text-[12px] leading-[1.4] font-normal',\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 <span className=\"truncate\">{selected?.label ?? placeholder}</span>\n </span>\n <IconChevronDown size={size === 'small' ? 14 : 16} stroke={1.5} className=\"shrink-0 text-text-secondary\" />\n </button>\n\n {open &&\n rect &&\n createPortal(\n <div\n ref={menuRef}\n role=\"listbox\"\n aria-activedescendant={`${id}-${activeIndex}`}\n tabIndex={-1}\n style={\n placement\n ? { ...placement, minWidth: rect.width }\n : // Provisional frame: measured by the layout effect above,\n // replaced before paint. Hidden so a cut-off position is\n // never visible, not even for a frame.\n { top: rect.bottom + 4, left: rect.left, minWidth: rect.width, visibility: 'hidden' }\n }\n className=\"fixed z-50 overflow-y-auto rounded-lg border border-border-default bg-bg-elevated p-1 shadow-lg\"\n >\n {options.map((option, index) => (\n <div\n key={option.value}\n id={`${id}-${index}`}\n role=\"option\"\n aria-selected={option.value === value}\n onMouseEnter={() => setActiveIndex(index)}\n onMouseDown={(e) => {\n // The document-level dismiss also listens on mousedown.\n e.preventDefault()\n pick(index)\n }}\n className={cn(\n 'flex h-9 cursor-pointer items-center justify-between gap-2 rounded-lg px-3 transition-colors',\n 'text-[14px] font-normal leading-[1.4] text-text-primary',\n index === activeIndex && 'bg-bg-hover',\n option.value === value && 'font-medium',\n )}\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 <span className=\"truncate\">{option.label}</span>\n </span>\n {option.value === value && <IconCheck size={16} stroke={1.5} className=\"shrink-0 text-text-secondary\" />}\n </div>\n ))}\n </div>,\n document.body,\n )}\n </>\n )\n}\n", "import { forwardRef, type InputHTMLAttributes } from 'react'\nimport { cn } from './cn'\nimport { useFieldControlId } from './Field'\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.\n */\nexport type TextInputProps = InputHTMLAttributes<HTMLInputElement>\n\nexport const TextInput = forwardRef<HTMLInputElement, TextInputProps>(function TextInput({ className, type = 'text', id, ...props }, ref) {\n // A surrounding Field names this control (SHA-17); an explicit id still wins.\n const controlId = useFieldControlId(id)\n return (\n <input\n ref={ref}\n id={controlId}\n type={type}\n className={cn(\n 'bg-bg-inset border border-border-default focus:border-border-focus rounded-lg px-3 py-2',\n 'text-[14px] leading-[1.4] font-normal 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 { cn } from './cn'\nimport { useFieldControlId } from './Field'\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).\n */\nexport type TextareaProps = TextareaHTMLAttributes<HTMLTextAreaElement>\n\nexport const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(function Textarea({ className, id, ...props }, ref) {\n // A surrounding Field names this control (SHA-17); an explicit id still wins.\n const controlId = useFieldControlId(id)\n return (\n <textarea\n ref={ref}\n id={controlId}\n className={cn(\n 'bg-bg-inset border border-border-default focus:border-border-focus rounded-lg px-3 py-2',\n 'text-[14px] leading-[1.4] font-normal 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}\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 */\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 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 { useEffect, type ReactNode } from 'react'\nimport { createPortal } from 'react-dom'\nimport { IconX } from '@tabler/icons-react'\nimport { cn } from './cn'\nimport { IconButton } from './IconButton'\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. Plus what Ship added: Escape closes it, and the card\n * says what it is to assistive tech.\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 (e.g. `flex flex-col gap-6`, or a max height with `overflow-y-auto`). */\n bodyClassName?: string\n width?: number\n}\n\nexport function DialogShell({ title, onClose, headerContent, footer, children, bodyClassName, width = 502 }: DialogShellProps) {\n useEffect(() => {\n const onKey = (event: KeyboardEvent) => {\n if (event.key === 'Escape') onClose()\n }\n document.addEventListener('keydown', onKey)\n return () => document.removeEventListener('keydown', onKey)\n }, [onClose])\n\n return createPortal(\n <>\n {/* Backdrop */}\n <div className=\"fixed inset-0 z-40 bg-black/50\" onClick={onClose} />\n\n {/* Dialog */}\n <div className=\"fixed inset-0 z-50 flex items-center justify-center pointer-events-none\">\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={title}\n className=\"bg-bg-elevated border border-border-subtle rounded-lg shadow-lg pointer-events-auto flex flex-col overflow-hidden\"\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 ?? <span className=\"text-h4 text-text-primary\">{title}</span>}\n <IconButton tooltip=\"Close\" aria-label=\"Close\" onClick={onClose}>\n <IconX size={16} stroke={1.5} />\n </IconButton>\n </div>\n\n {/* Body */}\n <div className={cn('pl-5 pr-4 py-4', footer != null && 'border-b border-border-subtle', bodyClassName)}>{children}</div>\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 </div>\n </div>\n </>,\n document.body,\n )\n}\n", "import { useEffect, useRef, useState, type KeyboardEvent, type ReactNode, type RefObject } from 'react'\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 * 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 <div\n aria-label={label}\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 <textarea\n ref={fieldRef as RefObject<HTMLTextAreaElement>}\n aria-label={label}\n value={draft}\n rows={4}\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 title=\"Click to edit\"\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 type { ReactNode } from 'react'\nimport { IconMessage2 } from '@tabler/icons-react'\nimport { cn } from './cn'\n\n/**\n * Peek's EmptyState (2026-08-28), verbatim: a 16px icon over a centred line\n * of secondary text. The message is the caller's \u2014 a shared component has no\n * words of its own for what is missing.\n */\nexport interface EmptyStateProps {\n /** 16px, stroke 1.5. A speech bubble when absent. */\n icon?: ReactNode\n message: string\n className?: string\n}\n\nexport function EmptyState({ icon, message, className }: EmptyStateProps) {\n return (\n <div className={cn('flex flex-col gap-2 items-center', 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 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 { Fragment, useCallback, useLayoutEffect, useRef, useState } from 'react'\nimport { cn } from './cn'\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 */\nexport interface Crumb {\n label: string\n href?: string\n /** Set the item in the mono face \u2014 a ref, an id. */\n mono?: boolean\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-[14px] leading-[140%]', 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 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-[12px] leading-[120%]',\n item.muted || (last && item.mono) ? 'text-text-muted' : last ? 'text-text-primary' : 'text-text-secondary',\n )\n const setLabelRef = (el: HTMLElement | null) => {\n labelRefs.current[index] = el\n }\n const crumb = item.href ? (\n <a ref={setLabelRef} href={item.href} className={cn(text, 'hover:text-text-primary')}>\n {item.label}\n </a>\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 {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 <span className=\"shrink-0 font-mono text-caption tabular-nums text-text-muted\">{count}</span>\n </WithTooltip>\n ) : null}\n </a>\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-[9px] font-medium leading-[115%]', 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'\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(\n 'flex w-60 shrink-0 flex-col gap-px overflow-y-auto border-r border-border-default bg-bg-surface px-2.5 py-3',\n className,\n )}\n >\n {children}\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 */\nexport interface PropertyProps {\n label: string\n layout?: 'row' | 'stacked'\n children: ReactNode\n className?: string\n}\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 <div className={cn('flex flex-col gap-3', className)}>\n <span className=\"text-menu uppercase tracking-[0.08em] text-text-secondary\">{label}</span>\n {children}\n </div>\n )\n }\n return (\n <div className={cn('flex items-center gap-2', className)}>\n <span className=\"w-[68px] shrink-0 text-caption text-text-secondary\">{label}</span>\n {children}\n </div>\n )\n}\n", "import type { ButtonHTMLAttributes, ReactNode } from 'react'\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 * It is a real `<button>` with `aria-pressed`, so the state reaches assistive\n * tech as a toggle rather than as a colour.\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'> {\n /** The emoji, drawn decoratively \u2014 name the control with `aria-label`. */\n emoji: ReactNode\n /** How many people reacted. Drawn as-is; `0` is not a reaction and is not drawn. */\n count: number\n /** You are one of them: the accent tint and edge, and `aria-pressed`. */\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, type, ...props }: ReactionProps) {\n return (\n <button\n type={type ?? 'button'}\n aria-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 ? 'border-accent-primary bg-accent-muted text-accent-primary hover:border-accent-hover'\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-[16px] leading-none\">\n {emoji}\n </span>\n {/* `text-chip` is a plain class, never merged \u2014 the same guard Chip uses,\n because tailwind-merge drops a token size that follows a text colour. */}\n <span className=\"text-chip signal:font-mono signal:text-[10px] signal:font-semibold signal:tabular-nums\">\n {count}\n </span>\n </button>\n )\n}\n", "import { type InputHTMLAttributes } from 'react'\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 */\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 { useState, type ReactNode } from 'react'\nimport { IconChevronRight } from '@tabler/icons-react'\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 whole row the\n * toggle, and actions that appear only while the row is hovered, as\n * IconButtons with tooltips.\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; the\n * header owns the hover reveal and stops the click from also toggling the\n * section. Peek's add/sort pair is the WithActions story, in its original\n * order.\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 whole row the toggle. */\n chevron?: boolean\n isExpanded?: boolean\n onToggle?: () => void\n /** Right-aligned, in the order given. */\n actions?: SectionAction[]\n /** `hover` reveals the actions only while the row is hovered; `always` keeps them. */\n showActions?: 'hover' | 'always'\n className?: string\n}\n\nexport function SectionHeader({ title, chevron = false, isExpanded = true, onToggle, actions, showActions = 'hover', className }: SectionHeaderProps) {\n const [isHovered, setIsHovered] = useState(false)\n\n return (\n <div\n className={cn(\n 'flex h-[32px] items-center justify-between rounded-lg px-2 transition-colors',\n isHovered && 'bg-bg-hover',\n chevron && 'cursor-pointer',\n className,\n )}\n onClick={chevron ? onToggle : undefined}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n >\n <div className=\"flex shrink-0 items-center gap-1\">\n {chevron && (\n <IconChevronRight\n size={12}\n stroke={1.5}\n className={cn('text-text-secondary transition-transform duration-150', isExpanded && 'rotate-90')}\n />\n )}\n <SectionLabel>{title}</SectionLabel>\n </div>\n\n {(showActions === 'always' || isHovered) && actions && actions.length > 0 && (\n <div className=\"flex items-center gap-1\">\n {actions.map((action) => (\n <IconButton\n key={action.tooltip}\n tooltip={action.tooltip}\n aria-label={action.tooltip}\n onClick={(event) => {\n event.stopPropagation()\n action.onClick()\n }}\n >\n {action.icon}\n </IconButton>\n ))}\n </div>\n )}\n </div>\n )\n}\n", "import type { ReactNode } from 'react'\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.\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 className?: string\n}\n\nexport function Tabs<T extends string>({ tabs, active, onChange, size = 'default', className }: TabsProps<T>) {\n return (\n <div role=\"tablist\" className={cn('flex items-center gap-2', className)}>\n {tabs.map((tab) => (\n <button\n key={tab.id}\n type=\"button\"\n role=\"tab\"\n aria-selected={active === tab.id}\n onClick={() => onChange(tab.id)}\n className={cn(\n 'flex cursor-pointer items-center transition-colors',\n // Arbitrary sizes (the body-2 and caption tokens): the colour branch below\n // follows them through cn(), and tw-merge drops a custom text-{size}\n // once a text-{colour} lands after it. Measured: tabs rendered 16px.\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-[14px] leading-[140%]' : 'gap-1 rounded px-1.5 py-0.5 text-[12px] leading-[120%]',\n active === tab.id ? 'bg-bg-active text-text-primary' : 'text-text-secondary hover:bg-bg-hover',\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 </button>\n ))}\n </div>\n )\n}\n", "import { createContext, useCallback, useContext, useEffect, useMemo, useState, type ReactNode } from 'react'\nimport { createPortal } from 'react-dom'\nimport { IconCircleCheck } from '@tabler/icons-react'\nimport { cn } from './cn'\n\n/**\n * Peek's Toast (Figma: Alert) and its provider (2026-09-01), verbatim.\n *\n * The pill: three types \u2014 `success`, `brand`, `neutral` \u2014 an optional leading\n * circle-check, an optional action 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 owns positioning (bottom-left), the portal, and auto-dismiss\n * (5 s; pass `durationMs: 0` to keep one up). One toast at a time \u2014 a new\n * one replaces the standing one, it does not queue.\n */\nexport type ToastType = 'success' | 'brand' | 'neutral'\n\nexport interface ToastProps {\n label: string\n /** Visual variant per Figma (Alert component): success, brand, or neutral. */\n type?: ToastType\n /** Show the leading circle-check icon. 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_BY_TYPE: Record<ToastType, string> = {\n success: 'bg-success-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-[shadow:var(--shadow-md)]',\n brand: 'bg-accent-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-[shadow:var(--shadow-md)]',\n neutral: 'bg-bg-inset border border-border-subtle signal:border-border-default signal:shadow-[shadow:var(--shadow-md)]',\n}\n\nconst ICON_BY_TYPE: Record<ToastType, string> = {\n success: 'signal:text-success-default signal:drop-shadow-[0_0_5px_rgba(63,222,140,0.7)]',\n brand: 'signal:text-text-interactive signal:drop-shadow-[0_0_5px_rgba(86,200,255,0.6)]',\n neutral: 'signal:text-text-secondary',\n}\n\nconst ACTION_BORDER_BY_TYPE: Record<ToastType, string> = {\n success: 'signal:border signal:border-border-default signal:hover:border-border-strong',\n brand: 'signal:border signal:border-border-default signal:hover:border-border-strong',\n neutral: 'border border-border-default',\n}\n\nexport function Toast({ label, type = 'neutral', leadingIcon = true, actionLabel, onAction, className }: ToastProps) {\n const hasAction = !!(actionLabel && onAction)\n return (\n <div\n className={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_BY_TYPE[type],\n className,\n )}\n >\n <div className=\"flex items-center gap-2 shrink-0\">\n {leadingIcon && (\n <IconCircleCheck size={16} stroke={1.5} className={cn('text-text-primary shrink-0', ICON_BY_TYPE[type])} />\n )}\n <span className=\"font-normal text-[14px] leading-[1.4] text-text-primary whitespace-nowrap\">{label}</span>\n </div>\n {hasAction && (\n <button\n type=\"button\"\n onClick={onAction}\n className={cn(\n 'h-6 flex items-center justify-center gap-1 px-1 py-1 rounded-md shrink-0 transition-colors',\n ACTION_BORDER_BY_TYPE[type],\n type === 'neutral' ? 'hover:border-border-strong' : 'hover:opacity-80',\n )}\n >\n <span className=\"font-medium text-[12px] leading-[12px] text-text-primary whitespace-nowrap\">{actionLabel}</span>\n </button>\n )}\n </div>\n )\n}\n\nexport interface ToastOptions {\n label: string\n /** Visual variant per Figma (Alert component). Defaults to 'neutral'. */\n type?: ToastType\n /** When set, renders a clickable action on the right side. */\n actionLabel?: string\n onAction?: () => void\n /** Show the leading circle-check icon. 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 ActiveToast extends ToastOptions {\n id: number\n}\n\ninterface ToastValue {\n showToast: (opts: ToastOptions) => void\n dismissToast: () => void\n}\n\nconst ToastContext = createContext<ToastValue | null>(null)\n\nlet toastSeq = 0\n\nexport function ToastProvider({ children }: { children: ReactNode }) {\n const [toast, setToast] = useState<ActiveToast | null>(null)\n\n const showToast = useCallback((opts: ToastOptions) => {\n setToast({ ...opts, id: ++toastSeq })\n }, [])\n\n const dismissToast = useCallback(() => {\n setToast(null)\n }, [])\n\n useEffect(() => {\n if (!toast) return\n const duration = toast.durationMs ?? 5000\n if (duration <= 0) return\n const timer = setTimeout(() => {\n setToast((current) => (current?.id === toast.id ? null : current))\n }, duration)\n return () => clearTimeout(timer)\n }, [toast])\n\n const value = useMemo<ToastValue>(() => ({ showToast, dismissToast }), [showToast, dismissToast])\n\n return (\n <ToastContext.Provider value={value}>\n {children}\n {toast &&\n createPortal(\n <div className=\"fixed bottom-4 left-4 z-[100] pointer-events-none\">\n <Toast\n className=\"pointer-events-auto\"\n label={toast.label}\n type={toast.type ?? 'neutral'}\n leadingIcon={toast.leadingIcon ?? true}\n actionLabel={toast.actionLabel}\n onAction={\n toast.onAction\n ? () => {\n toast.onAction?.()\n dismissToast()\n }\n : undefined\n }\n />\n </div>,\n document.body,\n )}\n </ToastContext.Provider>\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", "jsx", "jsx", "jsxs", "jsx", "jsx", "jsxs", "useState", "useRef", "useEffect", "useLayoutEffect", "createPortal", "useState", "jsx", "jsx", "jsx", "jsxs", "useState", "jsx", "jsxs", "useState", "useRef", "useEffect", "useLayoutEffect", "createPortal", "useCallback", "useLayoutEffect", "useRef", "useState", "createPortal", "jsx", "jsxs", "GAP", "useState", "useRef", "useCallback", "useLayoutEffect", "createPortal", "jsx", "useRef", "useState", "jsx", "jsx", "jsxs", "jsx", "jsxs", "jsx", "jsxs", "useState", "useRef", "createContext", "useContext", "jsx", "jsxs", "IconCheck", "IconChevronDown", "useCallback", "useEffect", "useId", "useLayoutEffect", "useMemo", "useRef", "useState", "createPortal", "Fragment", "jsx", "jsxs", "useId", "useRef", "useState", "useMemo", "useCallback", "useLayoutEffect", "useEffect", "jsxs", "Fragment", "jsx", "IconChevronDown", "createPortal", "IconCheck", "jsx", "TextInput", "forwardRef", "jsx", "forwardRef", "Textarea", "useState", "useEffect", "createPortal", "IconX", "Fragment", "jsx", "jsxs", "useEffect", "createPortal", "IconX", "Fragment", "jsx", "jsxs", "useState", "useEffect", "useRef", "useState", "jsx", "useState", "useRef", "useEffect", "jsx", "jsxs", "jsx", "jsxs", "Fragment", "useCallback", "useLayoutEffect", "useRef", "useState", "jsx", "jsxs", "useRef", "useState", "useCallback", "useLayoutEffect", "Fragment", "jsx", "jsxs", "jsx", "jsx", "jsxs", "jsx", "jsx", "jsxs", "jsx", "jsxs", "jsx", "jsxs", "useState", "IconChevronRight", "jsx", "jsxs", "useState", "IconChevronRight", "jsx", "jsxs", "createContext", "useCallback", "useContext", "useEffect", "useMemo", "useState", "createPortal", "jsx", "jsxs", "createContext", "useState", "useCallback", "useEffect", "useMemo", "createPortal", "useContext"]
|
|
7
7
|
}
|