# Personal taste core — serafim

> Transferable invariants confirmed by edit requests in ≥2 projects (21st-private-1, eval-hub-app). Read together with the project taste.md; on conflict the project wins, except antipatterns — those are never overridden.

Taste axis: classical (order, density, restraint) — 14/15 core rules on the classical axis.

## Antipatterns (permanent)
- Hide actions/buttons when the action is impossible in the current context
- Remove decorative or unnecessary icons
- Don't show empty, null or placeholder content — hide the element when there is no data _(confidence: medium)_
- Remove redundant labels/headers when meaning is obvious from context _(confidence: medium)_

## Craft norms (violation = bug)
- Match the rest of the app's established style and design system for any component
- Keep elements properly aligned; crooked/misaligned layout reads as a bug
- Prevent layout shifts; skeleton loaders must match the final rendered state
- Match the design mock exactly _(confidence: medium)_
- Keep text and element sizing consistent across the interface _(confidence: medium)_
- Group and structure content for fast readability instead of a wall of text _(confidence: low)_

## Personal preferences
- Default to a meaningful view/content rather than an empty or 'all' state _(confidence: medium)_
- Use muted text for body content and highlight only meaningful elements like links _(confidence: medium)_
- Place confirm buttons and limit values inside the input field on the right _(confidence: medium)_

## Meta-preferences
- Reuse existing components and patterns instead of creating new ones
- Animate state changes and transitions with smooth morphing, consistent with the rest of the app


---

# taste.md — 21st-private-1 (v7)

> Project style profile. Read TOGETHER with the personal core `~/.taste/core.md`: the core holds the person's invariants, this file holds this repo's style. Conflicts resolve in favor of the project, except core antipatterns.

<!-- taste:auto:start -->
## Style identity
Build on a near-white canvas (`#f9fafb`/95.7% gray) with deep charcoal foreground; honor the explicit light/dark `--tl-background` overrides and keep a single electric-blue primary (228° 100%) as the only saturated accent. Keep density high and compact: lean on `text-sm`/`text-xs` down to `text-[10px]`, `h-7`/`h-8` controls, and tight `gap-1`–`gap-2` spacing. Default radii to `rounded-md`, reserving `rounded-full` for pills and avatars. Mute body text via `muted-foreground` and let only links and primary actions carry color. Animate state with `transition-colors`/`transition-all` at `duration-150`–`200`, never abruptly.

## Directives — owner's words, source of truth
These override code statistics. Code adoption may lag; stats below only fact-check.
- **Style destructive/distractive buttons like the diff view 'Discard changes' button, and keep this rule documented in claude.md** — owner, 2026-01-27: «Проверь Deleta чтобы было в стили Discard changes и добавь в claude.md. добавь праивло как мы делаем distractive кнопки…» _(scope: global; restated 4×)_
- **In magic-chat, reuse the existing agents components (layout, sidebar, tools) instead of inventing new ones** — owner, 2026-02-15: «Я просил тебя использовать сущетсвубщий блять компонент как мы испольузем в agents…» _(scope: zone; restated 6×)_
- **Use only the local project icons from apps/web/app/(alpha)/agents/{features}/ui/icons (same as canvas/agents-sidebar) — never lucide or custom inline SVG** — owner, 2026-02-20: «сделай чтобы из apps/web/app/(alpha)/agents/{features}/ui/icons были все иконки…» _(scope: global; restated 4×; pins agents/{features}/ui/icons)_
- **For switches use the existing canvas switch component apps/web/app/(alpha)/canvas/{components}/ui/switch.tsx** — owner, 2026-02-20: «используй сука apps/web/app/(alpha)/canvas/[id/{components}/ui/switch.tsx]…» _(scope: global; restated 2×; pins canvas/{components}/ui/switch.tsx)_
- **Everywhere use the toast styles from /agents and /an, not the old canvas toast overrides** — owner, 2026-03-04: «сделтаь чтобы теперь везде исопльзвоались как в /agetns /an а не как в канвасе…» _(scope: global; pins agents/an toasts)_
- **Tooltips must follow the agents-sidebar style with the kbd shortcut shown on the left bottom row** — owner, 2026-03-06: «в тултипах слева должно быть в нижнем ряду kbd символ - как в agents-sidebar например…» _(scope: global; restated 4×)_
- **For code use the proper code blocks like in the agents/docs documentation** — owner, 2026-04-01: «для кода надо использовать нормальные кодовые блоки как в документации…» _(scope: global; restated 3×; pins agents/docs code blocks)_
- **In community, style template/agent cards exactly like the component cards** — owner, 2026-04-10: «сделай агентам как в карточке компонентов один в один…» _(scope: zone; restated 5×)_

## Direction (code velocity)
- New design system lives under `{components}/ui/` (canvas); prefer it for new work, legacy `components/ui/` is being phased out
- popover: now `apps/web/components/ui/popover.tsx`, migrating away from `apps/web/app/(alpha)/canvas/[id]/{components}/ui/popover.tsx`
- tooltip: now `apps/web/components/ui/tooltip.tsx`, migrating away from `apps/web/app/(alpha)/canvas/[id]/{components}/ui/tooltip.tsx`
- pinned to `apps/web/app/(alpha)/canvas/[id]/{components}/ui/` by owner directive (2026-02-20): icons, switch — code adoption lags, migration in progress

## Tokens (from code)
- apps/web/app/globals.css: `--color-background: #f9fafb`, `--color-background: var(--tl-background-dark) !important`, `--color-background: var(--tl-background) !important`, `--background: 0 0% 100%`, `--foreground: 240 10% 3.9%`, `--card-foreground: 240 10% 3.9%`, `--popover-foreground: 240 10% 3.9%`, `--primary: 228 100% 50%`, `--primary-foreground: 0 0% 100%`, `--primary-gradient-start: 228 100% 50%`, `--primary-gradient-end: 228 100% 48%`, `--secondary-foreground: 240 5.9% 10%`, `--muted: 0 0% 92%`, `--tl-background: 0 0% 95.7%`, `--muted-foreground: 240 3.8% 46.1%`, `--accent: 240 4.8% 95.9%`

## Grammar (utility frequencies from code)
- Typography: `text-sm`×1511, `text-xs`×1135, `text-[11px]`×395, `text-[10px]`×278
- Radii: `rounded-md`×800, `rounded-full`×618, `rounded-lg`×481, `rounded`×424
- Control heights: `h-4`×925, `h-7`×379, `h-8`×352, `h-3`×250
- Gaps/paddings: `gap-2`×1137, `px-2`×445, `gap-1.5`×427, `px-3`×396, `gap-1`×383
- Animation: `transition-colors`×707, `transition-all`×307, `duration-200`×275, `duration-150`×246

## Key entities

### sonner (`apps/web/components/ui/sonner.tsx`, imports: 288)
**Component**: `Toaster` wrapper around the `sonner` library's `Sonner` component, exported as a named export and marked `"use client"`. **Theming**: Reads `resolvedTheme` via `useTheme()` from `next-themes` and passes it as the `theme` prop (`"light" | "dark" | "system"`), so toasts automatically m
- full spec: `.taste/facts/sonner.md`

### button (`apps/web/components/ui/button.tsx`, imports: 256)
**Base styles**: `inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors`. Focus uses `outline-offset-2 focus-visible:outline focus-visible:outline-2 focus-visible:outline-primary/70`; disabled state applies `disabled:opacity-50 disabled:pointer-ev
- variants: variant(default|brand|destructive|outline|secondary|ghost|link), size(sm|default|lg|icon), defaultVariants(variant|size)
- owner pattern (60 msgs): When the owner asks about buttons, he usually wants them styled and behaving consistently with existing reference buttons elsewhere in the app (submit button, sidebar, publish, agent onboarding), with proper hover/scale/loader animations — and he wants…
- full ask-pattern: `.taste/topics/buttons.md`
- full spec: `.taste/facts/button.md`

### icons (`apps/web/app/(alpha)/canvas/[id]/{components}/ui/icons.tsx`, imports: 104)
**Icons component** — A collection of inline SVG icon React components (one named export per icon) for a Claude/Anthropic-style UI. **API/props**: Each icon accepts `IconProps = React.SVGProps<SVGSVGElement> & { className?: string }`, spread onto the root `<svg>` so consumers can override size, colo
- user rule: Style icons consistently with the component style in the dialog preview
- owner pattern (60 msgs): When the owner mentions icons he usually wants the RIGHT icon used (reusing existing ones from the codebase/sidebar/context menu, not invented ones), positioned/sized/colored correctly, and consistent with the rest of the UI. He often supplies exact SVGs and…
- full ask-pattern: `.taste/topics/icons.md`
- full spec: `.taste/facts/icons.md`

### input (`apps/web/components/ui/input.tsx`, imports: 107)
**Element**: A `forwardRef` `<input>` (displayName `Input`) accepting all native input props plus `className`; merges classes via `cn()`. No sub-components. **Base look**: `flex h-9 w-full rounded-lg border border-input bg-background px-3 py-2 text-sm text-foreground shadow-sm shadow-black/5 transit
- full spec: `.taste/facts/input.md`

### badge (`apps/web/components/ui/badge.tsx`, imports: 80)
**Layout & Base Style**: Renders a `<div>` with `inline-flex items-center`, rounded corners (`rounded-md`), `border`, padding `px-2 py-[1px]`, font size `text-[12px]`, and `transition-colors` for smooth color changes. **Variants** (via `class-variance-authority`, default = `default`):
- variants: variant(default|secondary|destructive|outline), defaultVariants(variant)
- full spec: `.taste/facts/badge.md`

### canvas-page-wrapper (`apps/web/app/(alpha)/canvas/{components}/canvas-page-wrapper.tsx`, imports: 73)
**Layout**: Full-viewport wrapper using `h-svh` (svh height unit), `bg-tl-background` themed background, `overflow-hidden` to clip content; carries `data-canvas-page` attribute for targeting. **Inner container**: Nested `div` with `h-full flex flex-col relative overflow-hidden bg-tl-background` — es
- full spec: `.taste/facts/canvas-page-wrapper.md`

### label (`apps/web/components/ui/label.tsx`, imports: 72)
**Appearance**: Renders a `<label>` via Radix `LabelPrimitive.Root`. Base classes: `text-[12px] font-medium leading-none` — 12px font size, medium weight (500), tight line-height (1). **States**: Responds to a sibling `peer` element's disabled state via `peer-disabled:cursor-not-allowed peer-disable
- full spec: `.taste/facts/label.md`

### dialog (`apps/web/components/ui/dialog.tsx`, imports: 57)
**Architecture**: Built on `@radix-ui/react-dialog`. Exports primitives (`Dialog`/Root, `DialogTrigger`, `DialogPortal`, `DialogClose`) plus styled sub-components. Uses `Cross2Icon` from `@radix-ui/react-icons` for close buttons and a `cn()` util for class merging. **Overlay**: `fixed inset-0 z-50 b
- user rule: Show add-file dialogs with a path input field in a single form
- user rule: The dialog should display the same as the custom workspace title rename dialog
- full spec: `.taste/facts/dialog.md`

### skeleton (`apps/web/components/ui/skeleton.tsx`, imports: 65)
**Element**: Single `<div>` rendering a placeholder block; spreads all `React.HTMLAttributes<HTMLDivElement>` props (sizing/shape controlled via passed `className`). **Base classes**: `animate-pulse rounded-md bg-muted` — continuous pulsing opacity animation, medium rounded corners, muted theme back
- owner pattern (60 msgs): When the owner mentions loading, he almost always wants a consistent, branded loader/skeleton that matches the final rendered content and is correctly positioned. He prefers skeleton loaders over spinners, demands the AN/logo spinner be reused everywhere, and…
- full ask-pattern: `.taste/topics/loading.md`
- full spec: `.taste/facts/skeleton.md`

### card (`apps/web/components/ui/card.tsx`, imports: 53)
**Card** (root): `div` with `rounded-lg border bg-card text-card-foreground shadow-sm`; forwards ref and all HTML div attrs, accepts `className` merged via `cn`. **CardHeader**: `div` with `flex flex-col space-y-1.5 p-6` — vertical stack, 6-unit padding, 1.5 gap between children.
- user rule: Hide unfilled or irrelevant cards instead of showing placeholders
- owner pattern (60 msgs): When the owner talks about cards he almost always wants them to look and behave EXACTLY like the existing community 'component card' (карточка компонента) — that's his reference standard for borders, shadows, rounding, hover, icons and tags. He keeps pushing…
- full ask-pattern: `.taste/topics/cards.md`
- full spec: `.taste/facts/card.md`

### loading-spinner (`apps/web/components/ui/loading-spinner.tsx`, imports: 42)
**LoadingSpinner**: Centered loader in a `w-full h-full flex items-center justify-center` wrapper (`data-test="logo-loading-spinner"`); applies `animate-pulse` while not ready/unmounted. Inner box sized via `size` prop — `xs` = `w-6 h-6` (1.5rem), `sm` = `w-8 h-8` (2rem, default) — `flex items-cente
- user rule: Remove the long loading with a spinner — either preload data or show content immediately without waiting
- full spec: `.taste/facts/loading-spinner.md`

### select (`apps/web/components/ui/select.tsx`, imports: 40)
**Sub-components**: Built on Radix `@radix-ui/react-select`. Exports `Select` (Root), `SelectGroup`, `SelectValue`, `SelectTrigger`, `SelectContent`, `SelectLabel`, `SelectItem`, `SelectSeparator`, `SelectScrollUpButton`, `SelectScrollDownButton`. Icons from `@radix-ui/react-icons` (`CheckIcon`, `Ch
- user rule: Make the scope selection more prominent and intuitive visually
- full spec: `.taste/facts/select.md`

### text-shimmer (`apps/web/components/ui/text-shimmer.tsx`, imports: 46)
**Appearance**: Renders an inline-block text element (default `<p>`, configurable via `as` prop) with a shimmering light-sweep effect. Base text color is gray (`--base-color:#a1a1aa` light / `#71717a` dark), with a moving gradient highlight (`--base-gradient-color:#000` light / `#ffffff` dark) that 
- full spec: `.taste/facts/text-shimmer.md`

### tooltip (`apps/web/components/ui/tooltip.tsx`, imports: 45)
**Sub-components**: Exports `TooltipProvider`, `Tooltip` (Root), `TooltipTrigger`, and `TooltipContent` — thin wrappers over Radix UI `@radix-ui/react-tooltip`. **TooltipContent appearance**: Rendered in a Portal; `relative z-50 max-w-[280px] flex flex-col items-start gap-0.5 rounded-md border borde
- owner pattern (60 msgs): When the owner mentions tooltips, he almost always wants them to match the canonical agents-sidebar / details-sidebar style: dark, with kbd hotkey hints (kbd in the bottom-left), correct text, imported from the right place, and behaving correctly (centered,…
- full ask-pattern: `.taste/topics/tooltips.md`
- full spec: `.taste/facts/tooltip.md`

### table (`apps/web/components/ui/table.tsx`, imports: 35)
**Structure & sub-components**: Exports 8 composable, `forwardRef` components — `Table` (wraps `<table>` in a `<div className="relative w-full overflow-auto">` for horizontal scrolling), `TableHeader` (`<thead>`), `TableBody` (`<tbody>`), `TableFooter` (`<tfoot>`), `TableRow` (`<tr>`), `TableHead` (
- user rule: Avoid blue tones in the table background
- owner pattern (60 msgs): When the owner talks about tables he almost always wants a clean, consistent, dense table style copied from his reference pages (an/overview, agents/threads, agents/deployments) and his mock designs — uniform 0.5px borders, no doubled borders, no big empty…
- full ask-pattern: `.taste/topics/tables.md`
- full spec: `.taste/facts/table.md`

### section-header (`apps/web/components/features/agents/landing/section-header.tsx`, imports: 32)
**SectionHeader** — Animated section heading block (overline + heading + optional subtext), uses `motion/react`. Props: `overline` (string), `heading` (string), `subtext?` (string), `maxHeadingWidth?` (default `"24ch"`). **Overline**: `motion.div` with classes `flex items-center text-xs font-medium 
- user rule: Use for sidebar headings the typography used in the old publication popover
- full spec: `.taste/facts/section-header.md`

### dropdown-menu (`apps/web/app/(alpha)/canvas/[id]/{components}/ui/dropdown-menu.tsx`, imports: 40)
**Built on Radix UI** (`@radix-ui/react-dropdown-menu`) with `cn` util for class merging. Exports: `DropdownMenu` (Root), `DropdownMenuTrigger`, `DropdownMenuContent`, `DropdownMenuItem`, `DropdownMenuCheckboxItem`, `DropdownMenuRadioItem`, `DropdownMenuLabel`, `DropdownMenuSeparator`, `DropdownMenu
- owner pattern (60 msgs): When the owner mentions menus (dropdowns, context menus, command menus, popovers), he almost always wants them to look and behave EXACTLY like the established 'correct' pattern used in docs theme selector / agents model selector / sidebar header, with…
- full ask-pattern: `.taste/topics/menus.md`
- full spec: `.taste/facts/dropdown-menu.md`

### logo (`apps/web/app/(alpha)/canvas/[id]/{components}/ui/logo.tsx`, imports: 39)
**Logo component**: Renders an SVG-based "21st" logo wrapped in a `<div>` with classes `w-full h-full relative` merged via `cn()` with an optional `className` prop. **SVG**: `width="100%"` `height="100%"`, `viewBox="0 0 400 400"`, `fill="none"`, `aria-label="21st logo"`; scales to fill its parent co
- full spec: `.taste/facts/logo.md`

### avatar (`apps/web/components/ui/avatar.tsx`, imports: 39)
**Avatar (Root):** `relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full` — a 40×40px circular container that clips overflow; accepts `className` override and forwards all Radix props/ref. **AvatarImage:** `aspect-square h-full w-full` — fills the root as a square image; shown when the sour
- full spec: `.taste/facts/avatar.md`

### textarea (`apps/web/components/ui/textarea.tsx`, imports: 38)
**Element**: A single `Textarea` component (`React.forwardRef` wrapping a native `<textarea>`), accepting all standard `React.ComponentProps<"textarea">` and merging a custom `className` via the `cn()` utility. No sub-components or icons. **Appearance**: `flex min-h-[80px] w-full rounded-lg border b
- full spec: `.taste/facts/textarea.md`

### command (`apps/web/app/(alpha)/canvas/[id]/{components}/ui/command.tsx`, imports: 35)
**Root `Command`**: Flex column container, `h-full w-full overflow-hidden rounded-[10px] text-popover-foreground bg-popover`. Accepts `mode` prop (`"dark"`|`"light"`, default `"dark"`) — applies `dark` class when dark. Built on cmdk `CommandPrimitive`. **`CommandDialog`**: Wraps `Command` inside `Di
- full spec: `.taste/facts/command.md`

### agents-link (`apps/web/components/agents-link.tsx`, imports: 33)
**Purpose**: A thin wrapper around Next.js `Link` that automatically transforms string `href` values through the `agentsHref()` utility (prepending/rewriting paths for the agents context); non-string hrefs (object form) pass through untouched. **Rendering**: Renders a single Next.js `<Link>` element
- full spec: `.taste/facts/agents-link.md`

### spinner (`apps/web/components/icons/spinner.tsx`, imports: 32)
**Element**: A single inline `<svg>` (xmlns set, `viewBox="0 0 24 24"`, `fill="none"`) sized by `width`/`height` props (default `20`×`20`px); applies class `animate-spin` plus any passed `className` (continuous rotation animation). **Background ring**: `<circle>` at `cx=12 cy=12 r=10`, `stroke={colo
- full spec: `.taste/facts/spinner.md`

### tabs (`apps/web/components/ui/tabs.tsx`, imports: 30)
**Composition**: Built on Radix UI Tabs; exports `Tabs` (Root), `TabsList`, `TabsTrigger`, and `TabsContent`. All inner parts use `forwardRef` and inherit Radix display names. **TabsList**: `inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground` — a 40px-tall rou
- owner pattern (60 msgs): When the owner mentions tabs/tables, he usually wants either a clean data table matching a reference mock/page or consistent tab-list switchers (theme-builder/agents style) with correct reordering, spacing, focus outlines and tab-order. He constantly…
- full ask-pattern: `.taste/topics/tabs.md`
- full spec: `.taste/facts/tabs.md`

### skeletons (`apps/web/components/ui/skeletons.tsx`, imports: 28)
A collection of pulsing loading-placeholder components (`animate-pulse`) built with `bg-muted` blocks, `shadow-base`, and rounded corners. All use Tailwind classes; icons from `lucide-react`. **ComponentCardSkeleton**: `<li>` wrapper `w-full flex flex-col items-center justify-center p-5 animate-puls
- full spec: `.taste/facts/skeletons.md`

### scroll-area (`apps/web/components/ui/scroll-area.tsx`, imports: 25)
**ScrollArea (Root)**: Wraps content with `relative overflow-hidden` plus optional `className`; renders a `Viewport`, `ScrollBar`, and Radix `Corner`. Built on `@radix-ui/react-scroll-area`. **Viewport**: `data-scroll-viewport="true"`, classes `h-full w-full rounded-[inherit] [&>div]:!block isolate 
- full spec: `.taste/facts/scroll-area.md`

### context-menu (`apps/web/app/(alpha)/canvas/[id]/{components}/ui/context-menu.tsx`, imports: 24)
**Composition**: Radix-based context menu exporting `ContextMenu` (Root), `ContextMenuTrigger`, `ContextMenuContent`, `ContextMenuItem`, `ContextMenuCheckboxItem`, `ContextMenuRadioItem`, `ContextMenuLabel`, `ContextMenuSeparator`, `ContextMenuShortcut`, `ContextMenuGroup`, `ContextMenuPortal`, `Con
- full spec: `.taste/facts/context-menu.md`

### copy-markdown-button (`apps/web/components/features/agents/an/docs/copy-markdown-button.tsx`, imports: 23)
**CopyMarkdownButton** — A client component button that converts the page's `<main>` DOM into markdown and copies it to the clipboard. **Layout & variants**: Renders two icon-only buttons wrapped in a `TooltipProvider` (delayDuration 150). Desktop variant (`hidden md:inline-flex`, `size-[34px]`) and
- full spec: `.taste/facts/copy-markdown-button.md`

### form (`apps/web/components/ui/form.tsx`, imports: 15)
**Composition**: A set of compound components built on `react-hook-form` and Radix primitives. Exports `Form` (alias for `FormProvider`), `FormField`, `FormItem`, `FormLabel`, `FormControl`, `FormDescription`, `FormMessage`, and the `useFormField` hook. `FormField` wraps RHF's `Controller` inside a 
- user rule: Use the color scheme and patterns from the /agents pages for forms with fields and settings blocks
- full spec: `.taste/facts/form.md`

### code-block (`apps/web/components/features/agents/an/docs/code-block.tsx`, imports: 22)
**CodeBlock** — Syntax-highlighted code container with Shiki (themes: `vesper` for dark, `github-light` for light with custom color overrides injected via `<style>`). Supports langs: json, typescript, tsx, bash, python, go, plaintext. Auto-detects language from filename extension (ts→typescript, tsx
- full spec: `.taste/facts/code-block.md`

### popover (`apps/web/components/ui/popover.tsx`, imports: 14)
**Sub-components**: Re-exports Radix Popover primitives — `Popover` (Root), `PopoverTrigger`, `PopoverAnchor`, `PopoverClose`, plus a custom `PopoverContent` wrapped in `PopoverPrimitive.Portal`. **PopoverContent appearance**: `z-50 w-72` (288px wide), `overflow-auto`, `rounded-[10px]`, `border bord
- user rule: All popovers should be in dark theme as in the sidebar
- full spec: `.taste/facts/popover.md`

### an-logo-spinner (`apps/web/components/features/agents/an/dashboard/an-logo-spinner.tsx`, imports: 21)
**SVG logo spinner** rendered at fixed `width="20" height="20"` with `viewBox="0 0 400 400"`, `fill="none"`, `aria-label="Loading"` for accessibility. **Classes**: `animate-pulse text-muted-foreground` plus any optional `className` prop appended (defaults to empty string).
- full spec: `.taste/facts/an-logo-spinner.md`

### alert-dialog (`apps/web/components/ui/alert-dialog.tsx`, imports: 20)
**Structure & Sub-components**: Built on Radix UI `@radix-ui/react-alert-dialog`. Exports: `AlertDialog` (Root), `AlertDialogTrigger`, `AlertDialogPortal`, `AlertDialogOverlay`, `AlertDialogContent`, `AlertDialogHeader`, `AlertDialogFooter`, `AlertDialogTitle`, `AlertDialogDescription`, `AlertDialog
- full spec: `.taste/facts/alert-dialog.md`

### dashed-divider (`apps/web/components/features/agents/landing/dashed-divider.tsx`, imports: 19)
**DashedDivider & DashedBox** — SVG-based border/divider components using `currentColor` with `text-border` color token. **`DashedDivider`**: Full-width horizontal dashed line rendered as an SVG (`w-full h-px text-border`, `preserveAspectRatio="none"`). Contains a `<line>` from x1=0,y1=0.5 to x2=100
- full spec: `.taste/facts/dashed-divider.md`

### chat (`apps/web/app/(alpha)/canvas/[id]/{components}/chat.tsx`, imports: 11)
**Chat Component** — A canvas-based AI chat interface (`"use client"`) using `useChat` from `ai-latest-react`, rendering messages, input, and context hints in a scrollable container. **Layout & sub-components**: Composed of `ChatContainer` (scroll wrapper), `MessageItem` (per-message render), `ChatI
- user rule: Message history without a backdrop, styled like the info boxes at the top, with the heading 'Chat History' instead of 'Messages'
- full spec: `.taste/facts/chat.md`

### checkbox (`apps/web/components/ui/checkbox.tsx`, imports: 19)
**Structure**: Built on `@radix-ui/react-checkbox` with `CheckboxPrimitive.Root` wrapping a `CheckboxPrimitive.Indicator`. Exported as `Checkbox`, forwards ref to the Root element, accepts all Radix Root props plus `className` (merged via `cn`). **Appearance**: A `size-4` (16×16px) square with `roun
- full spec: `.taste/facts/checkbox.md`

### preview-container (`apps/web/components/features/agents/an/showcase/components/preview-container.tsx`, imports: 17)
**PreviewContainer**: A vertical flex wrapper (`flex flex-col`) rendering an optional uppercase label above a themed preview box; props: `theme` (AnTheme), `colorMode` ('light'|'dark', default 'light'), `children`, `className`, `label`, `height` (default 'auto'). **Label** (optional): rendered only 
- full spec: `.taste/facts/preview-container.md`

### showcase-layout (`apps/web/components/features/agents/an/showcase/components/showcase-layout.tsx`, imports: 16)
**Container**: Full-height scrollable wrapper (`h-full overflow-y-auto`) with inner content area `p-6 max-w-6xl mx-auto` (centered, max-width 6xl, 24px padding). **Header block** (`mb-6`): Title as `<h1>` (`text-xl font-semibold text-gray-900`); description `<p>` (`text-sm text-gray-500 mt-1`); sour
- full spec: `.taste/facts/showcase-layout.md`

### main-layout (`apps/web/components/features/main-page/main-layout.tsx`, imports: 8)
**Structure**: Renders a `<main>` wrapper containing a `Container` sub-component that holds `children`. Top-level layout primitive for page content. **Styling**: `<main>` uses classes `min-h-screen w-full` (full viewport height, full width), merged with optional `className` prop via `cn()` utility f
- user rule: Show real content (main) instead of a placeholder; hide the loader after completion; smoothly transition results without jumps or recounts; results appear in sync with the timeline, not earlier
- full spec: `.taste/facts/main-layout.md`

### header.client (`apps/web/components/ui/header.client.tsx`, imports: 16)
**Header (`header.client`)** — fixed top navigation bar, client component with Clerk auth, Jotai state, and React Query for user data. **Layout**: Fixed bar `flex fixed top-0 left-0 right-0 h-14 z-50 items-center px-4 py-3 text-foreground`; background `bg-muted` unless `variant==='publish'`. Left se
- full spec: `.taste/facts/header.client.md`

### sidebar (`apps/web/app/(alpha)/canvas/{components}/ui/sidebar.tsx`, imports: 7)
**Sidebar** — A composable, collapsible sidebar system (shadcn-style) built on React Context, with desktop/mobile responsiveness, keyboard shortcut, and cookie-persisted state. **Layout & sizing**: Widths via CSS vars — `--sidebar-width: 16rem`, `--sidebar-width-icon: 3rem`, mobile `18rem`. Uses `bg
- variants: variant(default|outline), size(default|sm|lg), defaultVariants(variant|size)
- user rule: Style the editing sidebar according to the component style with a close icon and action buttons
- user rule: Left-align sidebar headings, as shown in the icon
- owner pattern (60 msgs): When the owner talks about the sidebar, he usually wants visual/style consistency (focus rings, outlines, headers, group titles) matching the main sidebar, correct sub-sidebar behavior (open/close, scroll, footer persistence), and adding/moving navigation…
- full ask-pattern: `.taste/topics/sidebar.md`
- full spec: `.taste/facts/sidebar.md`

### resizable-sidebar (`apps/web/app/(alpha)/canvas/[id]/{components}/resizable-sidebar.tsx`, imports: 15)
**ResizableSidebar** — A draggable, animated collapsible panel that docks to `left`, `right`, or `bottom` (vertical), with width/height controlled by a Jotai atom. **Layout/appearance**: Container is `bg-transparent flex flex-col text-xs shrink-0 relative` plus `w-full` (bottom) or `h-full` (left/ri
- full spec: `.taste/facts/resizable-sidebar.md`

### switch (`apps/web/app/(alpha)/canvas/[id]/{components}/ui/switch.tsx`, imports: 15)
**Structure**: Radix `SwitchPrimitives.Root` (data-slot="switch") wrapping `SwitchPrimitives.Thumb` (data-slot="switch-thumb"); forwardRef, displayName from Radix Root, merges `className` via `cn`. **Track (Root)**: `peer inline-flex h-5 w-11 shrink-0 cursor-pointer items-center rounded-full border-
- full spec: `.taste/facts/switch.md`

### user-avatar (`apps/web/components/ui/user-avatar.tsx`, imports: 15)
**UserAvatar** — Avatar component with optional hover-card profile preview. **Base avatar**: Renders `<Avatar>` with classes `group shadow-base` (adds `cursor-pointer` when `isClickable`); inline style `width/height = size` (default 40px). Contains `<AvatarImage>` (src fallback `/placeholder.svg`, a
- full spec: `.taste/facts/user-avatar.md`

### agent-card (`apps/web/components/features/agents-registry/agent-card.tsx`, imports: 14)
**Layout & Card Body**: Renders an `<li>` wrapper (`group/card w-full flex items-center justify-center p-5 overflow-hidden`) containing a clickable `<a>` card linking to `/community/agents/{username}/{slug}`. Card is `rounded-[12px] p-4 h-[156px]`, flex-column, `bg-tl-background`, with inset/shadow 
- full spec: `.taste/facts/agent-card.md`

### chart (`apps/web/components/ui/chart.tsx`, imports: 12)
**Purpose & Structure**: A composable Recharts wrapper providing themeable charts. Exports `ChartContainer`, `ChartTooltip`, `ChartTooltipContent`, `ChartLegend`, `ChartLegendContent`, and `ChartStyle`. Uses React Context (`ChartContext`) to share a `config` object (`ChartConfig`) keyed by data seri
- owner pattern (25 msgs): When the owner asks about charts, he usually wants clean, working data visualizations on the Observability/analytics pages — correctly styled, with proper tooltips, empty/skeleton states, and sensible layout. He also uses mermaid diagrams for content…
- full ask-pattern: `.taste/topics/charts.md`
- full spec: `.taste/facts/chart.md`

### toggle-switch (`apps/web/components/features/agents/an/chat-preview/components/input/toggle-switch.tsx`, imports: 2)
**Layout**: A pill-shaped track (`relative shrink-0 box-content h-[14px] w-[26px] rounded-full p-[2px]`) containing an absolutely-positioned circular knob (`absolute top-[2px] left-[2px] w-[14px] h-[14px] rounded-full bg-white`). Track is 26×14px with 2px padding; knob is 14×14px white circle. **Pro
- user rule: Style toggles in accordance with the overall app style
- full spec: `.taste/facts/toggle-switch.md`

### link-preview (`apps/web/components/ui/link-preview.tsx`, imports: 2)
**Layout/look**: Inline hover-preview link built on `@radix-ui/react-hover-card`. Trigger renders `children` as a clickable link (`text-black dark:text-white` + custom `className`) pointing to `url`. On hover, a floating preview card appears above (`side="top"`, `align="center"`, `sideOffset={10}`) 
- user rule: Move the element into the header or remove it
- full spec: `.taste/facts/link-preview.md`

### message-actions (`apps/web/app/(alpha)/canvas/[id]/{components}/message-actions.tsx`, imports: 2)
A memoized component rendering contextual action buttons over a chat message, absolutely positioned at `bottom-2 right-2` in a flex row with `gap-1`. Container uses `transition-opacity pointer-events-auto`; visible (`opacity-100`) when dropdown is open or restoring, otherwise `opacity-0 group-hover/
- user rule: Use blue (#0084FF or similar) for your own messages in the chat
- user rule: Rename the 'Play' step to 'Test'; add visual feedback on message send (spinner, optimistic trace generation) until the server responds
- full spec: `.taste/facts/message-actions.md`

## How the owner usually asks (cross-cutting)
- **filters** (39 msgs): When the owner talks about filters he usually wants a filter UI placed in a header/sidebar (popover or replaced-search sidebar) for inboxes, threads, logs and community/templates — consistently styled like the existing a → `.taste/topics/filters.md`
- **search** (60 msgs): When the owner mentions search, he usually wants a single unified search experience embedded in the sidebar (same layout/style as components/agents), where clicking search turns a button into an input that slides down wi → `.taste/topics/search.md`
- **layout** (60 msgs): When the owner mentions layout, he almost always means the header ("шапка") — and wants it consistent with an existing reference (usually /agents, the sidebar, or quickstart): centered main content with breadcrumbs, corr → `.taste/topics/layout.md`
- **animation** (60 msgs): When the owner talks about animation he's usually fine-tuning existing transitions — controlling exact timing, sequencing (what appears when), entry/exit smoothness, and consistency across the app. He also frequently wan → `.taste/topics/animation.md`
- **typography** (60 msgs): When the owner asks about typography, he usually wants font sizes, weights, and font families to be consistent across the whole app and matching existing conventions — readable sizes, no random weight changes on selectio → `.taste/topics/typography.md`
- **empty-states** (12 msgs): When the owner mentions empty states, he usually wants empty/no-data cases handled gracefully — no wasted blank space, no ugly 'nothing here' placeholders, just hide/skip empty content or show a clean empty graph/skeleto → `.taste/topics/empty-states.md`

## Exemplar pages
- /studio/serafimcloud (8×)
- /news/post/3 (4×)
- /news/submit (3×)
- /home (3×)
- /agents/internal/admin (3×)
- /agents/docs/deploy/deploy (3×)
- /studio/serafimcloud/registries/0e9f5dc2-7fce-4e1f-97ef-a5fa (2×)
- /studio/serafimcloud/registries (2×)
<!-- taste:auto:end -->
