# Changelog

All notable changes to `@timbal-ai/timbal-react` are documented here.

## [4.2.2] — 2026-07-09

### Added

- **New lint rules `page-missing-inset` and `card-flush-content`.** A
  `PageHeader` without a page inset contract (`PageBody`, `AppShell` /
  `RoutedAppShell`, `Page`, or lateral `px-*` on the root) is now an error —
  the classic failure is titles and tables running flush to the shell/card
  edge. A `Card` or `CardContent` with `p-0` / `px-0` holding headings,
  forms, or tables is also blocked. New `HOUSE_RULES` entries:
  `page-inset-required`, `card-flush-content`.

## [4.2.1] — 2026-07-08

### Added

- **New lint rule `button-custom-fill`.** A hand-painted background on a
  `Button` (`bg-success`, `bg-primary`, gradients, arbitrary values — on the
  opening tag, including multi-line tags) is now an error. Buttons come from
  the variant system only (default / secondary / outline / ghost /
  destructive / link), whose label colors are contrast-gated by the DNA
  compiler; custom fills are how agents produce green buttons with
  unreadable labels. State-scoped tints (`hover:bg-destructive/10`,
  `data-[state=open]:bg-accent`) and fills on button *children* (status
  dots) stay allowed. New `HOUSE_RULES` entry: `button-variants-only`.

### Fixed

- **Gray band behind the chat composer on white pages.** The thread's sticky
  composer footer hardcoded `bg-background`. With DNA panel surfaces,
  `--background` is the light-gray desktop canvas — so any chat mounted on a
  white (`--card`) page painted the old gray band behind the input. The band
  now paints `var(--thread-canvas, var(--card))`: white by default, and the
  studio shells (`TimbalChatShell`, `TimbalStudioShell`) set
  `--thread-canvas: var(--background)` because their playground gradient
  bottoms out there. Hosts with a custom canvas can set `--thread-canvas` on
  any ancestor. (Both the Tailwind class and the baked `styles.css` rule
  changed.)
- **Composer displaced below the fold while streaming.** When a host mounted
  `TimbalChat` in a container without a bounded height, the thread root's
  `h-full` resolved to auto, so the column grew with every streamed token and
  the sticky composer travelled below the viewport. The thread root now also
  carries `max-h-dvh` (a no-op in correctly bounded shells), which keeps the
  internal message viewport as the scroller regardless of host layout.
  `TimbalChatShell` / `TimbalStudioShell` roots additionally gained
  `max-h-full` so they clamp to a height-constrained pane instead of
  overflowing it.

## [4.2.0] — 2026-07-08

### Added

- **DNA `color.selection` — selection-control accent.** `dna.json` accepts
  `color.selection` (hex / rgb() / oklch()); the compiler emits
  `--selection` / `--selection-foreground` in both modes and maps them into
  `@theme inline` (`bg-selection`, `text-selection-foreground`, …). This is
  the checked/active fill for checkboxes, radios, and similar binary
  controls. Defaults to the status set's info blue; explicit values are kept
  verbatim (the glyph foreground is contrast-gated at 3:1 — WCAG 1.4.11
  non-text — so vivid accents survive).

### Changed

- **Neutrals default to PURE gray (compiler v1.3.0).** Previously the
  neutral ladder inherited the brand hue with a small default chroma, so a
  chromatic brand (e.g. blue) washed the whole app — tinted canvas
  gradient, tinted dropdown/menu hovers, tinted sidebar active items,
  tinted mobile sheets. All ladder `defaultChroma` values are now `0`:
  surfaces are white/gray/dark and hovers are the default gray unless a
  project explicitly opts in via `color.neutrals.chroma`. The
  `--playground-*` canvas gradient follows the neutrals verbatim (no
  chroma floor).
- **`color.accent` no longer tints the hover surface.** `--accent` (and
  `--sidebar-accent`) are always the neutral gray ladder step; the field
  is still validated but is reserved for decorative use. Brand-tinted
  hover surfaces were the main source of the "blue-washed UI" failure.
- **New lint rule `status-fill-foreground`.** A solid status/selection
  fill (`bg-success`, `hover:bg-destructive`, …) without its
  contrast-gated `text-<tone>-foreground` pair is now an error — the
  label would inherit the page foreground and can be unreadable on the
  saturated fill (e.g. near-black text on solid green). Tinted chips
  (`bg-success/15`), subtle surfaces (`bg-success-subtle`), and textless
  `rounded-full` indicator dots are exempt.

## [4.1.0] — 2026-07-08

### Added

- **DNA `finish` field — the classic Timbal look is the default again.**
  `dna.json` gains a top-level `finish: "timbal" | "flat"` (default
  `"timbal"`). The compiler (v1.1.0) now derives the signature chrome from
  brand + neutrals in both modes: the `--playground-from/via/to` canvas
  gradient, `--elevated-*` / `--modal-*` surface grades, `--primary-fill-*` /
  `--secondary-fill-*` / `--ghost-fill-*` control-fill gradient stops, and
  skeuomorphic `shadow-control` / `shadow-control-bordered` inset-highlight
  shadows — all mapped into `@theme inline` so `from-primary-fill-from`,
  `shadow-control`, etc. work as utilities. `finish: "flat"` emits the same
  token names with degenerate stops (from == to, hairline shadows), so
  fork-first component source never branches on the finish: one `button.tsx`
  renders the classic gradient + inset chrome under `timbal` and plain flat
  shadcn-style controls under `flat`. Rebranding via `color.brand` keeps the
  finish. New exported type `DnaFinish`; `finish` participates in the
  fingerprint so `timbal-dna check` catches drift.

- **Draggable copilot trigger** — the floating **Assistant** pill can now be
  dragged anywhere on screen when it covers underlying UI. The position
  persists across reloads (`localStorage`, stored as viewport fractions so it
  survives resizes and always re-clamps on screen), and dropping the pill near
  its home corner snaps it back to the default spot. New `AppCopilot` /
  `CopilotOverlay` prop `triggerDraggable` (default `true`), plus
  `triggerPosition` / `setTriggerPosition` / `resetTriggerPosition` on
  `CopilotControls` (exported `CopilotTriggerPosition` type) for programmatic
  control — e.g. a host-app "Reset assistant position" action via
  `useCopilot()?.resetTriggerPosition()`.

## [4.0.0] — 2026-07-07

Major release for the fork-first UI generation stack: per-project **Design DNA**
(`dna.json` → `tokens.css`) replaces ad-hoc theming for blueprint v2 projects,
and **ui-lint v2** keeps only correctness errors — taste moves to the screenshot
critique rubric.

### Added

- **Design DNA engine** — `parseDna`, `compileDna`, `DnaValidationError`, and
  `DNA_COMPILER_VERSION` exported from `@timbal-ai/timbal-react` and `/app`.
  A validated `dna.json` compiles deterministically to a complete light + dark
  `tokens.css` with WCAG contrast checks, curated font pairings, status sets,
  elevation ladders, density specs, motion presets, and chart recipes.
- **`timbal-dna` CLI** (new `bin`) — `compile`, `check` (byte-compare drift
  detection), `validate`, and `registries` (curated menus for agents). Default
  paths: `src/design/dna.json` → `src/design/tokens.css`.
- **Curated registries** — `FONT_PAIRINGS`, `STATUS_SETS`, `MOTION_PRESETS`,
  `ELEVATION_LADDERS`, `DENSITY_SPECS` plus lookup helpers (`getFontPairing`,
  `getStatusSet`, …) exported for tooling and agent prompts.
- **`HouseRule.enforcement`** — `"lint"` vs `"prompt-only"` so vocabulary and
  linter policy stay in sync.

### Changed

- **ui-lint v2 — correctness only.** The linter now emits **errors only** for
  patterns that break at runtime or punch through the theme system: raw palette
  colors, color literals, `hsl(var(--token))` wrapping, unsafe chart dataKeys,
  inline style colors, hand-authored theme tokens / `forcedTheme`, and chat
  surfaces wrapped in bordered containers. **13 taste rules** (`no-icon-spam`,
  `no-bold-metric`, `no-glow`, `no-table-in-card`, hand-rolled shell chrome,
  etc.) are annotated `enforcement: "prompt-only"` in `HOUSE_RULES` and judged
  on rendered screenshots via the critique rubric — not blocked by lint.
- **`strict` / `--strict` is a no-op in v2** — accepted for backward
  compatibility but has no effect because no warn-tier findings are emitted.
  Fork-first projects own component source; taste bans would flag the design
  system itself.
- **`reviewGeneratedUi`** — unchanged API; revision prompts now reference only
  correctness findings.

### Migration

- **Blueprint v2 / fork-first projects:** author `src/design/dna.json`, run
  `timbal-dna compile`, import `src/design/tokens.css` before kit styles, and
  own UI primitives under `src/components/`. Use `timbal-dna check` in CI to
  catch hand-edited `tokens.css`.
- **Legacy package-consumed projects:** `createTimbalTheme` / presets remain
  available; ui-lint is less opinionated (warnings no longer block unless you
  relied on `strict: true` for taste — move those checks to visual critique).
- **Codegen pipelines:** replace taste-blocking `lintGeneratedUi(…, { strict:
  true })` gates with `timbal-dna check` + screenshot critique for fork-first
  repos; keep lint for hard errors only.

## [3.2.0] — 2026-07-03

Dashboard chrome gets a flush sidebar by default, codegen guardrails re-tier taste
vs correctness, and `AppShell topbar` is a supported layout again.

### Added

- **`StudioSidebar` `variant` prop** (`"flush"` | `"floating"`, default `"flush"`)
  and exported `StudioSidebarVariant` type from `@timbal-ai/timbal-react/studio`.

### Changed

- **`StudioSidebar` redesigned — flat nav rows + flush rail default.** Nav items
  are now quiet text rows with a soft `sidebar-accent` tint on hover/selection
  (no more raised bordered-button chrome per row), and the high-level
  `StudioSidebar` defaults to a **full-height flush rail** with a single hairline
  border — the standard product-dashboard look. Pass `variant="floating"` for
  the previous rounded studio-style card; `TimbalStudioShell` (which drives
  `StudioSidebarPanel` directly) keeps the floating card unchanged. The footer
  user menu now uses the token-styled overlay (`DropdownMenuItem
  variant="destructive"` for sign out) instead of hand-rolled dark-glass chrome,
  so it follows light/dark and rebranding. `--sidebar-accent` (light) nudged to
  `oklch(0.955 0.004 260)` so the flat selected state reads clearly.
- **Lint severities re-tiered: errors = correctness + theming integrity,
  warnings = taste.** `no-glow`, `no-uppercase-heading`, `no-table-in-card`,
  the custom-heading half of `no-chat-wrapping`, and `no-custom-shell-chrome`
  (hand-rolled rails) are now **warnings** — they block only under
  `strict: true`, which becomes the per-caller knob (run strict for cheap/fast
  generation models, lenient for frontier models). Raw colors, color literals,
  `hsl(var(--token))` wrapping, unsafe chart dataKeys, theme-token bypasses,
  and chat-wrapping layout breaks remain hard errors. The policy is documented
  in `ui-lint.ts`, `HOUSE_RULES`, and the skill; a severity-policy test pins
  each tier.
- **`no-custom-shell-chrome` no longer rejects the `AppShell topbar={…}` slot or
  `AppShellSidebarTrigger`** — a global topbar (brand + nav links + search +
  account) is a supported layout again, composed through the shell's `topbar`
  prop with `<AppShellSidebarTrigger />` placed inside it when a sidebar drawer
  also exists. The rule still flags hand-rolled `<nav>`/`<aside>` rails built
  outside the shell's slots. `HOUSE_RULES`, `APP_KIT_AGENT_INSTRUCTIONS`, and
  the catalog entry for `AppShell` were updated to document the topbar
  archetype instead of forbidding it.
- **`AppShell` main inset** defaults to the flush rail width on first render so
  mount doesn't flash a slide for the common `variant="flush"` sidebar.
- **`Page` fill layout** re-adds bottom padding inside `overflow-y-auto` scroll
  panes (not the shell footer) so the last row isn't flush against the viewport
  edge.
- **`CopilotOverlay` trigger pill** — toned-down liquid-glass displacement and
  aberration plus a container scrim/ring so the floating pill doesn't smear dark
  fringes on busy backgrounds.

## [3.1.0] — 2026-07-02

### Added

- **`neutrals` intent** — full neutral-canvas personality from one hue
  (`neutrals: { hue: 85, chroma: 0.016, lightness: 0.975 }` = cream paper):
  derives page background, cards, muted surfaces, sidebar, and borders in both
  modes, with hue independent of `brand` (unlike `tintNeutrals`, which it wins
  over). Makes cream-editorial / greige-enterprise / warm-consumer canvases
  expressible without any token overrides. `folio` preset now ships cream
  neutrals so the range is visible in the gallery.
- **`typography.display` now reaches every heading** — `styles.css` routes
  `h1`–`h3` through `var(--font-display, var(--font-sans, inherit))`, so a
  serif/mono display stack re-skins kit-rendered titles (Page, Section,
  dialogs) with no wrapper spans. No-op when `display` is unset.
- **Tiered agent prompts** — `APP_KIT_CORE_INSTRUCTIONS` (new export): the
  compact always-injected tier (~6.5k chars, size test-enforced) carrying the
  surface/archetype decision, every house rule (rendered from `HOUSE_RULES`,
  zero drift), the retry-killing API gotchas, and a routing table to on-demand
  layers. The full instruction strings now also ship as **readable files** —
  `dist/prompts/{core,appkit,theme,reference}.md` (exports map `./prompts/*`)
  — so agents can pull detail per task instead of consuming the 56k-char
  monolith up front (which remains exported unchanged).
- **`REFERENCE_AGENT_INSTRUCTIONS` (new export)** — the compact
  screenshot-to-plan protocol for reference-matching agents: signal→intent
  extraction table, block/invention planning, upfront non-goals
  (house-rule conflicts), and the iterate-on-intent loop. Contract-tested so it
  can only name intent fields that exist.
- **`invention-lane` house rule** — bespoke components are documented as
  legitimate when no catalog block fits, with the substrate rules (kit
  primitives + semantic tokens, zero literals, same lint) and the
  second-use-extract discipline; rendered into `APP_KIT_AGENT_INSTRUCTIONS`
  alongside a dedicated section.

- **`createTimbalTheme` intent grew four fields** so reference-matching a design
  never requires hand-authored tokens:
  - `overrides` — one-off token overrides, **token-referential only**
    (`var(--token)` / `color-mix(in oklab, …)`; literal colors throw with a
    teaching error). Flat map applies to both modes; `{ light, dark, root }`
    form for deliberate asymmetry. Overrides pass the `timbal-ui-lint` gate by
    construction.
  - `chartPalette` — up to 6 intent colors mapped to `--chart-1..6`, adapted
    per mode (dark series brightened for contrast). Exempt from the
    `color-literal` lint rule like `brand`/`accent`, including
    formatter-wrapped multi-line arrays.
  - `surfaces: "panel" | "console"` — `"console"` flattens the sidebar into the
    background, brand-tints the active nav item, points `--chart-1` at the
    brand, and defaults shadows to hairline.
  - `defaultMode: "light" | "dark"` — dark-first intent, passed through on the
    returned tokens; wire as `defaultTheme={theme.defaultMode ?? "light"}`.
- **`--sidebar-active` / `--sidebar-active-foreground` tokens** — the
  `StudioSidebar` active nav item (expanded and collapsed rail) now renders
  through these tokens (default preserves the shipped elevated-gradient look).
  Overriding `--sidebar-active: var(--sidebar-accent)` yields a flat accent
  fill — no more `nav[aria-label]` CSS hooks to restyle the active state.
- New exported types: `TimbalThemeOverrides`, `ThemeSurfaces`, `ThemeMode`.

### Changed

- `color-literal` / `theme-via-generator` / `forcedTheme` lint messages now
  teach the sanctioned escape hatches (intent fields, `overrides`,
  `var()`/`color-mix()`) instead of only naming the violation, and warn against
  pasting `themeToCss` output into gated app CSS.
- `THEME_AGENT_INSTRUCTIONS` rewritten around the richer intent object: the
  golden rule is now "literals only as intent", runtime `applyTimbalTheme` is
  the default apply path, and dark-first wiring goes through `defaultMode`.

## [3.0.0] — 2026-06-30

One button for the whole package. `TimbalV2Button` is gone — every chat, studio,
and app-kit surface now renders the single `Button` component, so generated apps
can no longer accidentally mix two button systems (the bug that produced
mismatched "Cancel" / "Save" footers).

### Breaking

- **Removed `TimbalV2Button`** (and its internal `TimbalV2ButtonProps`). It is no
  longer exported from the package root or `/ui`. Migrate to `Button`:
  - `variant="primary"` → `color="primary"` (or `variant="default"`)
  - `variant="secondary"` → `variant="secondary"`
  - `variant="ghost"` → `variant="ghost"`
  - `variant="destructive"` → `color="primary-destructive"`
  - `isIconOnly` + `size="sm"` → `size="icon-sm"` (use `icon-xs` / `icon` / `icon-lg` for other sizes)
  - `fullWidth` → `className="w-full"`
  - For the fully-rounded pill look (chat/studio chrome), add `shape="pill"`.
  `Button` keeps `isLoading`, `asChild`, `iconLeading`/`iconTrailing`, and the
  legacy `variant` aliases.

### Changed

- **All internal `TimbalV2Button` usages migrated to `Button`** with `shape="pill"`
  to preserve the rounded chat/studio chrome: composer send/cancel and the
  message edit-composer, the studio mode toggle and mobile menu trigger,
  `AppShellChatTrigger`, `AppConfirmDialog`, the multi-select question artifact
  confirm, and `TooltipIconButton`.
- **`TooltipIconButton`** now wraps `Button` (as a `size="icon-sm"`,
  `shape="pill"` button) and exposes a stable `variant` union
  (`TooltipIconButtonVariant`: `primary | secondary | ghost | informative |
  destructive`) mapped onto `Button`'s color system — existing
  `variant="primary"` / `"secondary"` call sites keep working.

### Kept

- The `TIMBAL_V2_*` **surface** token records (modal/card/switch/avatar chrome,
  e.g. `TIMBAL_V2_MODAL_SURFACE`, `TIMBAL_V2_ELEVATED_SURFACE`,
  `TIMBAL_V2_SECONDARY_PILL_ROOT`) remain public — only the button component was
  removed. They are still used by `Dialog`, `Card`, `Switch`, `Avatar`, and the
  integration/catalog surfaces.

## [2.0.1] — 2026-06-30

Codegen guardrails and agent guidance to stop recurring dashboard anti-patterns
(hand-rolled topbars, coach panels, and broken chart colors), plus a Sheet
mobile-scroll polish.

### Added

- **`no-custom-shell-chrome` now rejects `AppShell topbar={…}`** — the linter
  previously only caught hand-rolled `<nav>`/`<aside>` rails and
  `AppShellSidebarTrigger`. Passing a global topbar (e.g. an "AI Coach" button +
  theme toggle) now hard-fails with a message pointing to `Page.actions` and
  `<AppCopilot>`.
- **`chart-data-key` lint rule** — flags a chart series `dataKey` containing a
  space or `%`. The chart layer maps each `dataKey` to `--color-<dataKey>`, so an
  unsafe key (`"Water %"`) produces invalid CSS and the series renders black. The
  fix is a safe identifier key + a separate `label`
  (`{ dataKey: "waterPct", label: "Water %" }`). Added as a `HOUSE_RULE`, so it
  also appears in `APP_KIT_AGENT_INSTRUCTIONS` / `UI_REVIEW_AGENT_INSTRUCTIONS`.
- **Agent-instruction guards for recurring runtime bugs** — `APP_KIT_AGENT_INSTRUCTIONS`
  now documents: import-subpath verification (app-kit surfaces like `StatusBadge`
  are `/app`, not `/ui` — a wrong subpath is a runtime blank-page crash);
  the `AppCopilotProvider` prop contract (`value`, not `context`, and redundant
  when `<AppCopilot>` is already mounted); the chart `dataKey` safe-identifier
  rule; and a "never swallow fetch errors / verify queries return rows" data-loading
  note.
- **`APP_KIT_AGENT_INSTRUCTIONS` — copilot + chart recipes** — documents the
  self-mounting `<AppCopilot>` pattern (`CopilotProvider` + `useCopilot()` +
  `hideTrigger`, `suggestions` for quick-action chips) so agents stop hand-rolling
  `Sheet` + `TimbalChat` coach panels; adds dual-metric/correlation chart
  guidance (multi-series `ChartArtifact` / `LineAreaChart`, normalize + tooltip —
  not raw recharts dual axes); and surfaces the **chart color-token contract**
  (`var(--chart-N)` directly — never `hsl(var(--chart-N))`, which renders
  black/empty charts).
- **`APP_KIT_CATALOG` — `AppShell` description** no longer advertises a topbar.

### Changed

- **Topbar guidance is now "never"** — agent instructions and catalog prose
  aligned with the linter: global actions belong in `Page.actions` or the
  sidebar; an in-app assistant is `<AppCopilot>`, not a topbar button.

### Fixed

- **`SheetContent` mobile scroll** — drawer body is now `min-h-0 flex-col
  overflow-y-auto` with scrollbar chrome hidden on small viewports so touch
  scrolling works without a visible track.

## [2.0.0] — 2026-06-30

A clarity + agent-reuse cleanup. The assistant is now a **self-contained drop-in
component**, common sections ship as **importable, forkable blocks** indexed by a
**machine-readable catalog**, and the root export is a **deterministic mirror of
the subpaths** (no more hand-maintained drift). Chat auto-scroll no longer fights
the composer. Breaking changes are limited to the copilot's `AppShell` coupling;
deprecated aliases ease the migration.

### Breaking

- **`AppShell` is layout-only — all `chat*` props removed.** The copilot is no
  longer wired through `AppShell`. Removed props: `chat`, `chatTriggerLabel`,
  `chatCollapsible`, `chatWidth`, `chatHeight`, `chatExpanded`,
  `defaultChatExpanded`, `onChatExpandedChange`, `hideChatTrigger`, and related.
  Render `<AppCopilot />` anywhere instead (see migration below).

### Added

- **`<AppCopilot>` — self-contained floating copilot** (`src/app/copilot/`). Drop
  it anywhere; it `createPortal`s its own fixed glass panel + SiriWave trigger to
  `document.body`, owns its open/expand state (or accepts controlled
  `open`/`onOpenChange`/`expanded` props), takes a `context` prop for agent
  tooling, and mounts the runtime — no `AppShell` wiring. `CopilotProvider` +
  `useCopilot()` drive custom triggers anywhere in the tree. Also exports
  `CopilotPanel`, `CopilotOverlay`, `SiriWave`.
- **Importable, forkable blocks** (`src/app/blocks/`, exported from `/app`):
  `FilteredDataTable`, `StatGrid`, `IntegrationsGrid`, `ResourceGallery`,
  `SettingsLayout` — prop-driven sections promoted from the best recipes, each
  with a `source` ref in the catalog so agents can fork.
- **`APP_KIT_CATALOG` + `getCatalogEntry`** (`src/app/catalog.ts`) — a
  machine-readable index of every primitive + block with an exact `importFrom`
  path, `exports`, and (for blocks) `composedOf` + `source`. The block/primitive
  listing inside `APP_KIT_AGENT_INSTRUCTIONS` is now **generated from this
  catalog**, so import paths can't drift from the prose. A contract test asserts
  every entry resolves to a real export.
- **Root export gaps closed** — `AlertCard`, `CatalogCard`, the density API
  (`AppDensityProvider`, `useAppDensity`, `appDensityClass`, …), the app layout
  class helpers (`appPageColumnClass`, …), the `/ui` chart primitives
  (`ChartContainer`, `ChartTooltip`, …), and `TimbalV2Button` are now reachable
  from the package root, matching the subpaths.
- **Thread scroll knobs** — `Thread`, `TimbalChat`, and `CopilotPanel` pass
  through `autoScroll`, `scrollToBottomOnRunStart`, `scrollToBottomOnInitialize`,
  and `scrollToBottomOnThreadSwitch` to the underlying viewport. Defaults are
  unchanged (auto-follow on), so this is an opt-in escape hatch.

### Changed

- **Deterministic root export** — `src/index.ts` now `export *`s each subpath
  barrel (`./ui`, `./chat`, `./studio`, `./app`, `./site`, `./artifacts`) plus
  root-only modules, with a small collision-overrides block pinning the names
  `/app` re-exports from `/ui` · `/chat` · `/artifacts` to a single source
  (`BreadcrumbItem` stays the `/ui` component; `/app`'s data type is
  `AppBreadcrumbItem`). To expose a new public symbol, add it to its
  `src/<area>/index.ts` — the root picks it up automatically.
- **`UI_REVIEW_AGENT_INSTRUCTIONS` + the `reviewGeneratedUi` revision prompt** are
  now generated from `HOUSE_RULES` (single source of truth), like the anti-slop
  checklist in `APP_KIT_AGENT_INSTRUCTIONS`.
- **`SiriWave` moved into the package** (`src/app/copilot/siri-wave.tsx`) — it
  previously lived outside `src/` (`components/ui/`). Nothing public lives outside
  `src/` anymore.
- **Docs/guidance consolidated** — README copilot section rewritten around
  `<AppCopilot>` (no topbar contradiction); recipe paths corrected to
  `examples/app-kit/src/recipes/`; SKILL.md documents the copilot/blocks/catalog
  subsystems + the root-as-subpaths export strategy; AGENTS.md points at the
  catalog + blocks; the generic `spacing-system` skill was removed in favor of a
  Timbal-token spacing note in SKILL.md.
- **Examples migrated** — `copilot-overlay` recipe and the `operations-dashboard`
  reference use `<AppCopilot>`.

### Fixed

- **Chat auto-scroll no longer fights the composer (P6).** The composer textarea
  grows with CSS `field-sizing: content` instead of JS-driven
  `react-textarea-autosize`, so typing/resizing it produces no JS height writes
  for the viewport's resize/mutation observers to react to — the "scroll yanks to
  bottom while typing" behavior is gone, with no layout change. Apps no longer
  need a `useChatScrollLock`-style workaround.
- **Upstream `useThreadViewportAutoScroll` patch** (`patches/`) — scrolling **up**
  now cancels a pending auto-scroll (`scrollingToBottomBehaviorRef`) instead of
  ignoring the scroll while one is in flight, so reading back through history no
  longer snaps you to the bottom. Shipped as a local `bun` patch for dev/tests;
  the same diff is intended for upstream (`@assistant-ui/react` is a peer
  dependency).

### Deprecated (removed next major)

- `AppChatPanel` → use `CopilotPanel` (or just `<AppCopilot />`).
- `useAppShellChat` → use `useCopilot`.
- `AppShellChatControls` type → `CopilotControls`.
- `AppCopilotProvider` page-context provider still works; for open/expand state
  prefer `<CopilotProvider>` / `<AppCopilot context={…} />`.

### Migration (1.x → 2.0)

Replace the `AppShell` `chat*` wiring with a sibling `<AppCopilot>`:

```diff
- <AppCopilotProvider value={{ page: "Operations" }}>
-   <AppShell
-     sidebar={<StudioSidebar … />}
-     chat={<AppChatPanel workforceId="ops" suggestions={…} />}
-     chatTriggerLabel="Assistant"
-     chatCollapsible
-   >
-     <Page title="Operations">{/* … */}</Page>
-   </AppShell>
- </AppCopilotProvider>
+ <>
+   <AppShell sidebar={<StudioSidebar … />}>
+     <Page title="Operations" actions={<ModeToggle />}>{/* … */}</Page>
+   </AppShell>
+   <AppCopilot
+     workforceId="ops"
+     context={{ page: "Operations" }}
+     triggerLabel="Assistant"
+     suggestions={…}
+   />
+ </>
```

- Custom triggers: wrap in `<CopilotProvider>` and call `useCopilot()?.setOpen(true)`,
  or drive `<AppCopilot open onOpenChange hideTrigger />` directly.
- Global actions that used a topbar move to `Page.actions`.
- Imports keep working via deprecated aliases for one major; switch
  `AppChatPanel`→`CopilotPanel` and `useAppShellChat`→`useCopilot` at your leisure.

## [1.9.1] — 2026-06-29

### Fixed

- **Assistant action-bar “More” menu** — the “Export as Markdown” dropdown rendered behind the floating copilot panel (`z-50` vs shell `z-[70]`). Raised the menu to `z-[80]` so the option is visible when opened.

## [1.9.0] — 2026-06-29

API-naming reconciliation so codegen agents stop hitting `tsc` retry loops on the
shapes the docs already describe. All three changes are additive and backward
compatible.

### Added

- **`StudioSidebar` `items` prop** — the canonical nav prop is now `items` (`StudioSidebarItem[]`), matching `APP_KIT_AGENT_INSTRUCTIONS` and the skill docs. `workforces` is retained as a **deprecated alias** (it still works; `items` wins when both are passed), reflecting that the sidebar is general route nav, not only a workforce picker.
- **`Section` `actions` slot** — `Section` now accepts a right-aligned `actions` node on its header row (e.g. a "Refresh" button), mirroring `Page` `actions`. Previously this content had to be placed inline in the body.
- **`useLiveQuery().refresh`** — alias of `refetch`, matching the `refresh` naming used by the other data hooks (`useWorkforces`, `useConversations`, `useConversation`). Both names are returned.

### Changed

- **`APP_KIT_AGENT_INSTRUCTIONS`** — documents `StudioSidebar items` (with `workforces` noted as deprecated) and the `Section actions` slot; the embedded example uses `items`.

## [1.8.0] — 2026-06-28

Conversation history for app runs, a glass copilot shell, and sidebar + anti-slop
hardening. Host apps can list past threads, reopen one in `<Thread>`, and continue
it; `AppChatPanel` / `AppShell` ship an Apple-style liquid-glass copilot with a
built-in history menu. Sidebar icons and four new lint gates close the gap that let
codegen agents ship hand-rolled neon dashboards.

### Added

- **Conversation history (app runs)** — data layer + hooks to list thread roots and hydrate a full conversation from stored run traces:
  - `listRuns`, `getRun`, `orderRunsForThread`, `isRootRun`, `runParentId` (`src/runtime/conversations.ts`)
  - `runTraceToMessages`, `conversationRunsToMessages`, `normalizeContentToText` (`src/runtime/trace-to-messages.ts`) — rebuild text, thinking, tool calls (with results), and attachments per turn
  - `useConversations` — paginated thread-root list scoped to a `workforceId`
  - `useConversation` — fetch every turn in a thread, hydrate traces, return `<Thread>`-ready `ChatMessage[]` (with `truncated` when over `maxTurns`)
  - `useTimbalRuntime().loadMessages(messages)` — replace runtime messages to reopen a stored thread; the last assistant `runId` becomes the parent for the next send
  - Exported from the package root and `./chat`; README documents the host `{baseUrl}/runs` proxy requirement
- **`AppChatPanel` conversation menu** — lists past threads for the panel's `workforceId`, reopens one by hydrating its traces into the live runtime, and starts a fresh thread via `clear`. Glass-styled composer (attachment chips, collapsible suggestions, send/cancel controls) tuned for the dark copilot panel.
- **Liquid-glass copilot shell (`AppShell`)** — floating panel uses a vibrancy gradient + backdrop blur; expand/collapse via `chatExpanded` / `defaultChatExpanded` / `onChatExpandedChange` and `useAppShellChat().setExpanded`. Launcher trigger is a `liquid-glass-react` pill with an animated `SiriWave` glyph (no MessageSquare icon).
- **`StudioSidebar` nav items take an optional `icon`** — items are now `{ id, name, icon? }` (new exported `StudioSidebarItem` type). The icon renders inline when expanded and as the rail glyph when collapsed (falling back to the initial). Fully backward compatible — existing `WorkforceItem[]` callers are unchanged.
- **Five hard-error lint rules** (`lintGeneratedUi`), each mirrored in `HOUSE_RULES`:
  - `no-glow` — neon/glow shadows (`shadow-[0_0_…]`, `drop-shadow-[0_0_…]`). Offset drop shadows and `shadow-card` are unaffected.
  - `no-custom-shell-chrome` — a hand-rolled topbar (`AppShellSidebarTrigger` is unnecessary) or a hand-rolled `<nav>`/`<aside>` rail. Use `AppShell sidebar={<StudioSidebar … />}`.
  - `no-uppercase-heading` — `uppercase` on `<h1>`–`<h3>` or large text. Small `text-xs uppercase tracking-wide` eyebrows are allowed.
  - `theme-via-generator` — `forcedTheme` or hand-authored theme token values. Brand via `createTimbalTheme({ brand })` instead.
  - `no-chat-wrapping` — `TimbalChat` / `AppChatPanel` wrapped in `Card`, `Section`, or custom bordered containers (or redundant heading/status chrome above the chat).

### Changed

- **`APP_KIT_AGENT_INSTRUCTIONS` rewired around the shell** — new **Shell & navigation** section; documents the no-topbar default, `StudioSidebar` icon nav, and the chat-wrapping rule.
- **Recipes + reference model the pattern** — `sidebar-dashboard` recipe, the `operations-dashboard` reference, and the blueprint `AppKitDemo` now pass icon nav items (and a `brand`).
- **`StudioSidebar` user menu** — profile dropdown restyled to match the glass history menu pattern.
- **README** — simplified `AppShell` + `StudioSidebar` mobile-nav example (no manual `useAppShellNav` wiring); new **Conversation history** section with hook examples and lower-level API table.

### Dependencies

- **`liquid-glass-react`** — used for the copilot launcher pill (`AppShell`).

### Tooling

- `trace-to-messages.test.ts` — turn + thread reconstruction from sample traces.
- Anti-drift test asserts `StudioSidebarItem` accepts `{ id, name, icon }` at the type level.

## [1.7.0] — 2026-06-28

Hardening release: make UI generation bulletproof for codegen agents by letting
the package speak for itself (types, defaults, JSDoc-with-examples, fail-loud
guardrails) instead of leaning on prose instructions.

### Added

- **`AppShell` auto mobile-nav hamburger (no topbar required)** — when an `AppShell` has a `sidebar` but no `topbar`, it now renders its own floating hamburger (`md:hidden`, top-left) that opens the sidebar drawer. New `mobileSidebarTrigger?: "auto" | "topbar" | "none"` (default `"auto"`). A sidebar dashboard now works on mobile with **no topbar and no wiring** — detaching the long-standing "inject a topbar just for the mobile menu" coupling.
- **`StudioSidebar` auto-syncs to the shell** — inside `AppShell`, the drawer reads the shell's mobile-nav controls automatically when `mobileOpen` isn't passed (via a neutral `src/layout/shell-nav-context.tsx` channel, mirroring the inset channel). No more manual `mobileOpen` / `onMobileOpenChange` / `StudioSidebarBackdrop` plumbing.
- **Chart-color lint rule (`chart-token-color-fn`, error)** — flags wrapping an OKLCH token in a color function (`hsl(var(--chart-1))`, `rgb(var(--primary))`, …), the silent "empty chart that still builds" bug. New `chart-token-color` entry in `HOUSE_RULES`.
- **JSDoc `@example` on the hot prop surface** — `LineAreaChart`, `PieChart`, `RadialChart`, `RadarChart`, `DataTable`, `StatusBadge`, `StatusDot`, `MetricRow`, `Button`, `ChartPanel`/`MetricChartCard`, `FieldSelect` now carry copyable examples + disambiguation in their `.d.ts`, killing the "grep `dist/*.d.ts` to reverse-engineer props" loop.
- **New recipes** — `sidebar-dashboard` (canonical no-topbar sidebar dashboard) and `chat-with-drawer` (chat-first + right-side Sheet drawer with a bounded scroll region) in `examples/app-kit`, registered in the blocks catalog.

### Fixed

- **`SheetDescription` / `DialogDescription` no longer cause hydration errors** — they render as a `<div>` (via `asChild`) instead of Radix's default `<p>`, so block-level children (badges, pill rows) are valid HTML.
- **`lintGeneratedUi` / `formatLintReport` fail loud on misuse** — passing a non-string source (e.g. `{ filename, source }`) or the whole `LintResult` to `formatLintReport` now throws a `TypeError` whose message states the correct signature, instead of silently mis-running.

### Tooling

- Anti-drift test (`src/anti-drift.test.ts`) asserts the `/app`, `/ui`, `/studio` export surface the examples/blueprint depend on (and that `Tabs` / `AppShellTopbar` are NOT exported), so API drift fails a test instead of surfacing as a codegen error.
- `docs/agent-environment.md` captures the environmental gotchas (`bun x tsc -b` not `npx`, preview vs EFS `node_modules` paths, stale `*.tsbuildinfo`) once, out of the prompt strings.

## [1.6.1] — 2026-06-27

### Fixed

- **recharts charts no longer white-screen under React 19** — recharts 3.6+ stores React elements inside a Redux-Toolkit/`immer` store, and `immer` **11.0.0** froze React 19's Fiber internals, so chart routes crashed with `Cannot assign to read only property 'lanes'`. The crash was always a transitive-dependency mismatch (a stale lockfile pinning `immer@11.0.0`), not a problem with the chart components — `LineAreaChart`, `PieChart`, `RadialChart`, and `RadarChart` are unchanged. The package now forces `immer` to `>=11.0.1` via `overrides`/`resolutions`, deduping to a single safe copy.

### Changed

- **recharts pinned to an exact tested version (`3.8.1`)** so the recharts/Redux-Toolkit/`immer` matrix the package validates against can't float into an untested 3.x range.
- **`APP_KIT_AGENT_INSTRUCTIONS` charts section** now tells codegen agents that the React 19 chart crash is a dependency override (`"overrides": { "immer": ">=11.0.1" }`), **not** a code change — and to keep using the chart components instead of hand-rolling SVG/CSS charts as a workaround.

### Tooling

- **`check:deps` preflight guard** (`scripts/check-immer.mjs`, wired into CI) fails the build if any installed `immer` falls in the broken `[11.0.0, 11.0.1)` window, turning a silent runtime white-screen into a loud, actionable error. Example apps (`examples/app-kit`, `examples/mock-ui`) carry the same `immer` override.

## [1.6.0] — 2026-06-26

### Changed

- **Condensed layout density across all UI primitives** — heights, paddings, and gaps were stepped down one notch library-wide for a tighter, more data-dense default. Driven from the shared tokens (`CONTROL_SIZE` h-10→h-9 / h-9→h-8 in `src/design/control-surface.ts`, `TOPBAR_HEIGHT_PX` 48→44 and `PILL_HEIGHT_PX` 40→36 in `src/design/tokens.ts`, `TIMBAL_V2_SIZE_HEIGHT`/`SIZE_ICON`/`SIZE_LABEL_PX` in `src/design/button-tokens.ts`, segmented-control paddings in `src/design/pill-segmented-classes.ts`) and applied consistently to `Button`, `UntitledButton`, `Card`, `Table`, `Alert`, `Dialog`, `Sheet`, `Popover`, `Accordion`, `Select`, `DropdownMenu`, `Menubar`, `Command`, `TagInput`, `Toolbar`, `Toast`, `Avatar`, `Calendar`, `Textarea`, `CopyButton`, `Snippet`, `InputOTP`, and `Breadcrumb`. Non-breaking: all component APIs and size variant names are unchanged.

### Fixed

- **Badges no longer stretch inside flex-column layouts** — `StatusBadge` (`src/app/surfaces/StatusBadge.tsx`) and the artifact `badge` node (`src/artifacts/ui/nodes.tsx`) now carry `w-fit shrink-0`, so a badge placed in a vertical flex container (e.g. a `Kanban` card) hugs its label instead of expanding to the full column width.

## [1.5.0] — 2026-06-26

### Added

- **`Kanban` board primitive** (`src/ui/kanban.tsx`) — A fully-featured, highly accessible Kanban board with column customization, drag-and-drop capabilities powered by `@dnd-kit`, and a read-only mode for static board rendering.
- **App density system** (`src/app/layout/app-density-context.tsx`, `src/design/app-density.ts`) — Context-driven density controls supporting "standard" and "compact" layouts. Compact mode cascades tighter padding, smaller text, and lower default heights to descendant components (pages, charts, tables).
- **Advanced filtering** (`src/app/data/FilterDropdown.tsx`, `src/app/data/FilterField.tsx`) — `FilterDropdown` is a **data-driven** multi-facet filter popover: pass `fields` describing your actual columns (`multiselect` / `text` / `daterange` / `numeric`) and it adapts to the table's content. State is keyed by field `id`, controlled (`value` + `onChange`) or uncontrolled (`defaultValue`); every control is on the shared control-surface contract. It renders **removable active-filter pills** (with "Clear all") next to the trigger by default (`showActiveChips`). `FilterField` is the labeled single-control wrapper for `FilterBar`.
- **Site/Marketing primitives** (`src/site/`) — A brand new `./site` subpath export containing five high-quality interactive and marketing-grade components:
  - **`Magnetic`** — magnetic hover effect that pulls elements toward the cursor.
  - **`Marquee`** — high-performance, infinite scrolling marquee.
  - **`Parallax`** — scroll-driven parallax container with custom speed factors.
  - **`Reveal`** — scroll or entrance fade-in reveal with custom directions and delays.
  - **`TextReveal`** — character-by-character or word-by-word scroll-reveal animations.

### Agent instructions & exports

- **`SITE_AGENT_INSTRUCTIONS`** — a new codegen prompt (exported from `./site` and the package root) documenting the `/site` motion primitives, their props, reduced-motion/SSR guarantees, and dosing/anti-overuse guidance.
- **`/site` primitives re-exported from the package root** — `Reveal`, `TextReveal`, `Parallax`, `Marquee`, `Magnetic` (plus the `EASE` / `DURATION` / `SPRING` motion tokens) are now reachable from both `@timbal-ai/timbal-react` and `@timbal-ai/timbal-react/site`, matching `/studio`, `/app`, and `/ui`.
- **`APP_KIT_AGENT_INSTRUCTIONS` expanded** — documents `FilterDropdown` and surfaces the new dependency-free `/ui` primitives (`Stepper`, `Rating`, `NumberField`, `TagInput`, `AvatarGroup`, `CircularProgress`, `CopyButton`, `Snippet`) so codegen agents discover them.

## [1.4.0] — 2026-06-05

### Added

- **`AppShell contentFill`** — makes the content region a bounded, non-scrolling flex column instead of the default padded scroll area. For full-bleed pages that own their own scroll (a full-page chat, a canvas, an editor, a split master–detail view): a `h-full` / `flex-1 min-h-0` child now fills exactly and a pinned footer (the chat composer) stays put instead of riding down on scroll. No `mainClassName` surgery and no `h-[calc(100dvh-…)]` guesses required.
- **`Page fill`** — makes a `Page` a `min-h-0 flex-1` flex column (pair with `AppShell contentFill` for full-height content). Give the fill child `min-h-0 flex-1`.
- **Headerless `Page`** — `Page.title` (and `PageHeader.title`) is now optional. Omit it to render a page with no `<h1>` header row (and no header padding), instead of dropping `Page` entirely to lose a heading.
- **New layout recipes** (`examples/app-kit/src/recipes/`): **full-page chat** (`contentFill` + headerless `fill` + `TimbalChat`), **split view** (master–detail two-pane), and **bento dashboard** (asymmetric `SurfaceCard`/`ChartPanel`/`StatTile` grid) — surfaced in the Blocks gallery and `APP_KIT_AGENT_INSTRUCTIONS` as distinct layout archetypes so generated UIs vary beyond the single sidebar+topbar+MetricRow+table shape.

### Changed

- **`AppShell` `main` is now a bounded flex column by default** (`flex min-h-0 flex-col`, keeping `pb-8 md:pb-10`). Previously the content `main` was content-sized, so every full-height child had to pass `mainClassName="flex min-h-0 flex-col"` to undo a layout deficiency; `h-full` / `flex-1` children now resolve a height out of the box. Scrolling `Page` content is unaffected (the outer scroll region still scrolls).
- **`APP_KIT_AGENT_INSTRUCTIONS`** documents a **Layout archetypes** menu (sidebar dashboard, focused, bento overview, split master–detail, full-page chat/canvas, copilot overlay, section-switcher) and the full-height contract (`contentFill` / `fill` / headerless `Page`), steering codegen away from shipping the same layout every time and away from `h-[calc(100dvh-…)]` / `min-h-[…]` sizing hacks.

### Fixed

- **`FieldSwitch` / `FieldSelect` id collision** — these derived their input id from `id ?? name ?? "switch"`, so multiple unlabeled instances all rendered `id="switch"` and toggling one flipped the others (and labels mis-associated). They now fall back to a `useId()`-generated id, so every instance is unique by default. `FieldInput` / `FieldTextarea` get the same `useId()` fallback (fixes silent label/control mis-association when no `name` is set).

## [1.3.0] — 2026-06-04

### Added

- **Ten new `/ui` primitives** — all dependency-free, styled with the design tokens and (where applicable) the control-surface contract:
  - **`AvatarGroup`** — overlapping avatar stack with an optional `+N` overflow chip (`max`, `spacing`).
  - **`Stepper`** — ordered step indicator for wizards/onboarding; horizontal or vertical, with complete/active/upcoming states.
  - **`Timeline`** — vertical event rail with per-item `title` / `description` / `meta` and tones (activity logs, audit history).
  - **`Rating`** — star rating, interactive (keyboard + hover preview) or `readOnly`; controlled or uncontrolled.
  - **`NumberField`** — numeric input with −/+ steppers on the shared control surface; clamps to `min`/`max`, steps by `step`.
  - **`TagInput`** — chips/token input on the control surface; commits on Enter/comma, removes on Backspace, optional `dedupe`/`max`.
  - **`Banner`** — page-level announcement bar with tones, optional icon/actions, and a dismiss button (use `Alert` for in-flow messages).
  - **`CopyButton`** — click-to-copy with a transient check confirmation; icon-only or with a label.
  - **`Snippet`** — single-line code/command on the elevated surface with a built-in copy button.
  - **`CircularProgress`** — lightweight SVG progress ring, determinate (with optional center label) or indeterminate.

### Fixed

- **`Calendar` rebuilt for `react-day-picker` v10** — the previous build mixed v8-era `flex` classes onto v10's table-based grid, which broke alignment and left nav/day buttons unstyled (`buttonVariants` carries layout only in this package). The calendar now uses v10's native table layout with the correct class keys, roomy `size-10` cells, spaced rows, a clear weekday header, and on-token selected / range / today states. Fixes both the inline `Calendar` and the `DatePicker` popover.

## [1.2.0] — 2026-06-03

### Changed

- **App-kit charts now run on [recharts](https://recharts.org)** (native shadcn chart layer). `LineAreaChart`, `PieChart`, `RadialChart`, and `RadarChart` keep the same public props but render via recharts under the shadcn `ChartContainer` / `ChartTooltipContent` / `ChartLegendContent` chrome — so tooltips, hover states, and animation match shadcn exactly across every chart kind. Series colors still flow from the theme `--chart-1..6` tokens.
- **Flush dashboard charts are tooltip-first** — `ChartPanel`, `MetricChartCard`, and cartesian `ChartArtifact` views use `layout="flush"` with **axes hidden by default**; category labels and formatted values appear in shadcn tooltips on hover. Fixes clipped axis ticks and edge-cropped bars/lines in card shells.
- **`LineAreaChart` margins and scales** — symmetric plot inset when axes are off; `no-gap` / zero bar gap only when category axes are explicitly shown; horizontal bars use band category scale (no misaligned Y ticks).
- **Linked Vite dev** — `timbalReactLocalDev()` always aliases `file:../timbal-react` installs to `src/` so gallery apps pick up source edits without rebuilding `dist/` first.
- Polish: in-card legends no longer clip outside the card; line/area charts use a thin crosshair cursor; tooltips animate in; radial/radar charts have hover tooltips.

### Added

- **shadcn chart primitives exported from `/ui`**: `ChartContainer`, `ChartTooltip`, `ChartTooltipContent`, `ChartLegend`, `ChartLegendContent`, `ChartStyle`, `useChart`, and the `ChartConfig` type.
- **`ChartArtifact.showAxes`** — opt-in axis ticks on flush cartesian charts (default off).
- **`resolveChartMargin`**, **`resolveTooltipCategory`**, and related helpers exported from the charts entry for tests and custom wrappers.

### Dependencies

- Adds **`recharts`** as a dependency and **`react-is`** as a peer dependency (`react-is` must match your React version).

## [1.1.0] — 2026-06-03

### Added

- **`DataTable` scales to real datasets** — three additive props (all backward-compatible):
  - **`pageSize`** — built-in client-side pagination with a compact footer pager (row range + prev/next). Controlled via `pageIndex` / `onPageChange`, or uncontrolled with `defaultPageIndex`; out-of-range pages snap back after filtering.
  - **`selectable`** + **`selectedKeys`** / **`defaultSelectedKeys`** / **`onSelectionChange`** — a leading checkbox column with header select-all (indeterminate state) for bulk actions (acknowledge/resolve/export). Toggling a checkbox never triggers `onRowClick`.
  - **`loading`** (+ **`loadingRows`**) — shaped skeleton rows that preserve the header/columns so the table doesn't jump when data arrives.
- **Loading states across the data kit** — `MetricRow`, `MetricChartCard`, and `ChartPanel` accept **`loading`**, rendering skeleton tiles / plot-height skeletons. Every async dashboard gets a consistent pending state without re-inventing one.
- **`useLiveQuery` / `useInterval`** (root + `/app`) — poll an async source on an interval for live dashboards (alerts, metrics, logs). Handles loading vs. background `refreshing`, drops stale/post-unmount responses, pauses while the tab is hidden + refetches on focus, and exposes `lastUpdated` + a manual `refetch`. Pairs with `authFetch`.
- **`AppShell` owns the mobile nav** — `AppShell` manages the drawer open-state (`navOpen` / `defaultNavOpen` / `onNavOpenChange`), renders the mobile backdrop automatically, and provides **`useAppShellNav()`** + **`AppShellSidebarTrigger`** (an `md:hidden` hamburger). Wiring a responsive `StudioSidebar` drops from ~30 lines of `isMobile`/resize/backdrop boilerplate to passing `mobileOpen={nav.open}` / `onMobileOpenChange={nav.setOpen}`.
- **`StatusBadge` `tone="danger"`** — a destructive/red tone for critical severity, so error/critical states aren't forced onto the brand `primary` tone.

## [1.0.0] — 2026-06-02

First stable release. The `@timbal-ai/timbal-react/ui` primitive layer, the control-surface contract, and the app-kit are now a settled public surface.

### Added

- **Built-in motion engine** — Dialog, AlertDialog, Sheet, Popover, DropdownMenu, Select, Tooltip, Toast, NavigationMenu, and Accordion / Collapsible now animate (fade / zoom / slide / height) out of the box. The `animate-in` / `animate-out` / `fade-*` / `zoom-*` / `slide-*` utilities and the `accordion` / `collapsible` / `caret-blink` keyframes are **inlined in `styles.css`** — no `tailwindcss-animate` / `tw-animate-css` dependency and no consumer config. Duration flows from any `duration-*` utility via `--tw-duration` (default 150ms). Compose `overlayAnimationClass` for custom overlays.
- **Complete vendored primitive catalog** on the control-surface contract: `Form`, `AspectRatio`, `Toolbar`, `Menubar`, `NavigationMenu`, `Breadcrumb`, `Pagination`, `Command` (cmdk), `Calendar` (react-day-picker), `Combobox`, `DatePicker`, `InputOTP`, `Kbd`, `Spinner`, `InputGroup` — alongside the existing `Input`, `Select`, `Dialog`, `Sheet`, `AlertDialog`, `Table`, `Toast`, `Card`, `Badge`, `Slider`, `Progress`, and more.
- **`overlayListPanelClass`** + **`PopoverContent` `variant="list"`** — one shared listbox/menu chrome so `Select`, `DropdownMenu`, `Combobox`, and `Command` panels are visually identical. `Combobox` uses `ComboboxTrigger` (control skin), not a `Button`.
- **App-kit organized into two libraries** (example): a **UI primitives** catalog (per-family audit, sidebar) and a **Blocks** library (composed sections — Project settings, Settings form, Metrics, Analytics, Charts, Table, Integrations, Resources, Confirm & destructive, Detail sheet, Empty states, Sign-in). Driven by `primitives-catalog.ts` / `blocks-catalog.ts`.

### Changed

- `overlayAnimationClass` is now exported from `@timbal-ai/timbal-react/ui` and the root for composing custom overlays.

### Removed (breaking)

- **`Tabs` / `TabsList` / `TabsTrigger` / `TabsContent` are no longer exported.** Radix/shadcn Tabs are intentionally not part of this package. Use **`PillSegmentedTabs`** (`trackVariant="flush"`) or the app-kit **`SubNav`** for section bars; switch panel content with local state or the router. Use `Accordion` / `Collapsible` for expand-collapse.

---

## [0.8.2] — 2026-06-02

### Added

- **Full theme personalities** — extend programmatic theming beyond brand color to **roundness, shadows, and fonts** in one intent object:
  - **`createTimbalTheme`** — new options: `shadow` (`none` | `hairline` | `soft` | `medium` | `strong`), `typography` (`sans`, optional `display`/`mono`, `importUrl` for web fonts). `radius` now also sets `--radius-2xl` (composer shell). Returns `fontFamily` + `fontImportUrl` for runtime font loading.
  - **`themeToCss(theme, { scope?, includeFontImport? })`** — emits a `font-family: var(--font-sans)` rule when the theme carries a font; optional `@import` for standalone stylesheets.
  - **`ensureThemeFontLink(url)`** — inject/remove a managed font `<link>` in `<head>`.
  - **`applyTimbalTheme` / `TimbalThemeStyle`** — auto-load preset/custom web fonts via `<link>`.
  - **`TIMBAL_THEME_PRESETS`** — each preset is now a full personality (color + radius + shadow + font), not color-only. New presets: **`folio`** (Fraunces serif, sharp corners), **`carbon`** (JetBrains Mono, green accent). Existing presets (`indigo`, `violet`, …) ship distinct fonts and radii.
  - **`ThemePresetGallery`** — shows font name on each swatch card.
  - **`THEME_AGENT_INSTRUCTIONS`** — documents typography, shadow, and font-loading rules for UI-generation agents.

### Changed

- **`TimbalThemePreset`** — adds optional `font` label for pickers.

---

## [0.8.1] — 2026-06-02

### Added

- **Programmatic theming** — derive a complete, paired light + dark palette from a single brand color so apps and UI-generation agents never hand-author OKLCH or risk a light-only override:
  - **`createTimbalTheme({ brand, accent?, radius?, tintNeutrals? })`** — owns the OKLCH math for `--primary`, its foreground, `--ring`, the full primary button gradient, and the soft playground tint. Returns paired `{ light, dark, root }` token maps.
  - **`themeToCss(theme, { scope? })`** — serialize to a paired `:root` / `.dark` (or `[data-timbal-theme]`-scoped) CSS string for build-time/SSR.
  - **`applyTimbalTheme(theme)` / `clearTimbalTheme()`** — runtime apply via a managed `<style>` (works with the `.dark` toggle); returns a disposer.
  - **`TimbalThemeStyle`** — render a generated theme (or `preset`) as a `<style>` near the app root.
  - **`TIMBAL_THEME_PRESETS`** + **`getThemePreset`**, **`applyThemePreset`**, **`getStoredThemePreset`** — a closed catalog (`platform`, `indigo`, `violet`, `forest`, `warm`, `slate`) to offer styles by stable id; persists to `STORAGE_KEYS.themePreset` (`timbal-theme-preset`).
  - **`ThemePresetGallery`** — preview + pick presets with real-component swatches, scoped so the live app doesn't change until selection.
  - **`THEME_AGENT_INSTRUCTIONS`** — system-prompt text directing UI-generation agents to theme via these APIs, never raw OKLCH.

---

## [0.8.0] — 2026-06-02

### Added

- **Headless UI primitives** (`@timbal-ai/timbal-react/ui`, also re-exported from the root) — token-styled Radix wrappers so apps stop hand-rolling raw HTML:
  - **`DropdownMenu`** family (`DropdownMenu`, `DropdownMenuTrigger`, `DropdownMenuContent`, `DropdownMenuItem`, `DropdownMenuCheckboxItem`, `DropdownMenuRadioGroup`/`DropdownMenuRadioItem`, `DropdownMenuLabel`, `DropdownMenuSeparator`, `DropdownMenuShortcut`, `DropdownMenuGroup`, `DropdownMenuSub`/`DropdownMenuSubTrigger`/`DropdownMenuSubContent`) — row actions and overflow menus.
  - **`Popover`** family (`Popover`, `PopoverTrigger`, `PopoverContent`, `PopoverAnchor`) — filters, pickers, lightweight floating panels.
  - **`Select`** family (`Select`, `SelectTrigger`, `SelectValue`, `SelectContent`, `SelectItem`, `SelectGroup`, `SelectLabel`, `SelectSeparator`, scroll buttons) — rich select beyond the native `FieldSelect`.
- **`DialogHeader`**, **`DialogFooter`**, **`DialogDescription`** added to the `Dialog` family so dialog layout no longer has to be hand-rolled.

All new primitives use the package design tokens (`bg-popover`, `border-border`, `shadow-card`, `focus:bg-accent`, …), portal above dialogs (`z-[80]`), and require no extra dependencies — `radix-ui` and `tw-animate-css` are already in the stack.

---

## [0.7.1] — 2026-06-02

### Changed

- **Opaque elevated surfaces** — `--elevated-from` / `--elevated-to` no longer use alpha in light or dark themes, preserving the signature vertical gradient with no background bleed-through.

### Added

- **`TIMBAL_V2_ELEVATED_SURFACE`** — elevated card/list surface (gradient + border + `shadow-card`) without hover/active fill shifts, for catalog cards and connection lists.
- **`TIMBAL_V2_LOGO_TILE`** — logo/integration mark tile pinned to the light plate in both themes so dark provider logos stay legible.
- **`STORAGE_KEYS.theme`** — localStorage key used by `ModeToggle` in uncontrolled mode.

### Fixed

- **`StatTile`** typography aligned with platform metrics (normal weight, compact labels, tabular numerals).

---

## [0.7.0] — 2026-05-29

### Added

- **App kit premade components** under `@timbal-ai/timbal-react/app`:
  - **Metrics:** `MetricRow`, `MetricChartCard`, `MetricTile` (platform-style KPI strip + flush chart)
  - **Integrations:** `IntegrationCard`, `ConnectionRow`, `ConnectionRowList`, `IntegrationsEmptyState`, `PlanBadge`
  - **Settings:** `SettingsSection`, `FieldRow`, `DangerZone`, `FloatingUnsavedChangesBar`
  - **Surfaces:** `InfoCard`, `DescriptionList`, `ExpandableSection`, `ResourceCard`, `StatusDot`
- **Charts engine** (`LineAreaChart`, `Sparkline`, `CHART_PALETTE`) — dependency-free SVG + motion; shared by app kit and chart artifacts
- **`APP_KIT_AGENT_INSTRUCTIONS`** — codegen system-prompt text (component menu, recipes, accessibility)
- **`TIMBAL_V2_ELEVATED_SURFACE`** and **`TIMBAL_V2_LOGO_TILE`** design tokens for catalog/resource cards
- **`@timbal-ai/timbal-react/vite`** — `timbalReactLocalDev()` plugin: linked-package dev, `dist/` watch, src alias when `dist/` is missing
- **`examples/app-kit`** recipe gallery and reference dashboard
- **`AGENTS.md`** section for app kit + recipes

### Changed

- **`ChartPanel`** — same shell as `MetricChartCard` (title row + flush plot); `ChartArtifactView` supports `embedded` mode
- **Chart artifacts** — area/line/bar route through `LineAreaChart` with flush layout and improved styling
- **`Button`** — pill shape by default; re-exported from `/app` for catalog CTAs
- **`StatTile`** — typography aligned with platform metrics (normal weight, compact labels)
- **Integration / resource / metric cards** — elevated secondary chrome; no background flash on card click (reserved for `Button`)

### Fixed

- Local dev **404 on `*.esm.js`** when `dist/` is empty — vite plugin falls back to `src/` with a console warning

---

## [0.6.1]

Subpath exports (`/chat`, `/studio`, `/ui`, `/app`), layered package architecture, initial app kit (`AppShell`, `Page`, `DataTable`, …).
