Files
Ben Taylor ca28191042 feat(drawer): CopilotDrawer host theming, mobile launcher, action icons + thread-management/tooltip fixes (#5707)
## CopilotDrawer — SDK polish + thread-management fixes

Polish and fixes on top of the shipped `<copilotkit-drawer>`
([#5701](https://github.com/CopilotKit/CopilotKit/pull/5701)), surfaced
by real de-fork pilots run against a locally-built SDK + the hosted
Intelligence runtime: **langgraph-js** (rich canvas / inline
`CopilotChat` / dark theme) and **mastra** (the proverbs CoAgents demo /
`CopilotSidebar` / light theme / controlled→uncontrolled provider).
Spans **`@copilotkit/web-components`** and **`@copilotkit/react-core`**
— no example/de-fork changes here (those wait on a release that
publishes web-components + a `react-core` bump that includes
`CopilotDrawer`). Captured as Phase-5 acceptance requirements in the
[design doc](https://app.notion.com/p/3883aa3818528190b5d1f9b6ba26dca0).

### `react-core` — thread management
- **Thread switching works under `<CopilotKit>`.**
`CopilotChatConfigurationProvider` treated any `threadId` prop as
controlled, so the auto-minted, non-explicit threadId that
`<CopilotKit>`'s v1 bridge seeds blocked the drawer's imperative
`setActiveThreadId` / `startNewThread` — thread switching and "+ New"
were dead in every app wrapped in `<CopilotKit>`. A non-explicit
(`hasExplicitThreadId={false}`) threadId is now overridable; a genuine
caller-supplied threadId stays controlled.
- **"+ New" resets the chat.** `CopilotChat` clears messages when
switching to a fresh non-explicit thread so the welcome screen shows
instead of the prior thread's messages — including when a still-loading
`/connect` is superseded by the switch (the aborted connect's stale
snapshot is dropped instead of repopulating the view).
- **No upsell flash/stick.** `CopilotDrawer` treats a pending (`null`)
license status as loading, not unlicensed, so the upgrade upsell no
longer flashes (or sticks) before the runtime reports the license.
- **In-header drawer launcher is mobile-only.**
`CopilotModalHeader.DrawerLauncher` toggles `drawerOpen`, which only
drives the off-canvas *mobile* drawer; on desktop the drawer is a
persistent in-flow panel that ignores `open`, so the launcher was a dead
no-op there. It now renders only at ≤767px. On desktop the chat's own
open/close (the toggle FAB + the header close, both lucide `X`) is the
consistent pair.

### `web-components` — theming, launcher, icons, tooltips
- **Host-theme inheritance (incl. dark mode).** Each token now resolves
`--cpk-drawer-* override → host theme var
(`--background`/`--card`/`--foreground`/`--border`/…) → built-in light
default`. The drawer follows the host app's light/dark theme by
inheritance, instead of baking light-only values (it showed a light
drawer on a dark app). Custom properties aren't reset by the `:host {
all: initial }` guard, so the standalone fallback still holds.
- **Row actions are icon buttons with instant, on-brand tooltips.**
Archive / Unarchive / Delete render inline lucide-style SVGs
(`currentColor`). Native `title` is replaced by an instant CSS tooltip
(matches the React components' Radix `delayDuration: 0`; `title`'s
show-delay is browser-fixed at ~1.5s), styled to match the standard
CopilotKit tooltip — a primary-colored bubble with an arrow, not a
surface/bordered box — and positioned to the side so the list's
scroll-overflow never clips it. The tooltip is suppressed while the
delete-confirm dialog is open (the clicked trash button keeps
`:focus-visible` otherwise, leaving its "Delete" tooltip floating over
the dialog).
- **Clipped thread names get a tooltip.** A truncated name exposes its
full text via the same instant primary-bubble tooltip as the row actions
(not the native `title`), shown only when actually clipped. The name
text moved to an inner span (so the outer can host the un-clipped
bubble) and the hovered row is z-lifted (each row is its own stacking
context from the entry-animation transform, which otherwise painted the
bubble under later rows).
- **Archived rows are muted**, no longer struck through.
- **Refetch preserves the list.** A refetch (e.g. Active↔All toggle)
keeps the known threads visible; the full loading state only renders on
the initial empty fetch.
- **Empty `footer` region hides** (it rendered as a stray bordered box
at the bottom). Driven by a `slotchange` listener, mirroring the
`memories` region.
- **Self-owned mobile launcher.** When the drawer is a closed off-canvas
mobile modal it renders its own floating "open threads" launcher, so
it's always openable without the host wiring a header button. The icon
is swappable via a `launcher-icon` **slot**, position is themeable via
`--cpk-drawer-launcher-top` / `--cpk-drawer-launcher-left` (so a host
can center it on its own header controls), and it exposes
`part="launcher"`. The richer in-header
`CopilotModalHeader.DrawerLauncher` remains an optional integration.

### Tests
- **react-core**: thread-control + non-explicit-override cases on
`CopilotChatConfigurationProvider`, the "+ New" message reset on
`CopilotChat`, license-pending gating on `CopilotDrawer`, and the
mobile-only / desktop-hidden launcher on `CopilotModalHeader`. New
regression tests verified red-green; chat + provider suites green, `tsc
--noEmit` clean, build green. (The superseded-connect *race* guard is
covered by live verification + its synchronous sibling test — an
async-timing unit test would be flaky, which the repo treats as a bug.)
- **web-components**: element tests for the icon tooltips +
confirm-dialog suppression, the clipped-name tooltip (`data-tooltip` +
placeholder), archived styling, refetch-preserves-list, footer
hide-when-empty, and mobile launcher render/open + desktop-absent.
Drawer suite green, `tsc --noEmit` clean, build + `es-check` green under
`CI=true`. (Truncation detection + tooltip stacking are layout/CSS —
verified live, not in jsdom.)

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-06-28 08:44:06 -05:00
..
2026-06-26 21:15:26 +00:00
2026-06-26 21:15:26 +00:00
2026-06-25 07:54:33 +00:00
2026-06-25 07:54:33 +00:00
2026-06-25 07:54:33 +00:00
2026-06-25 07:54:33 +00:00
2026-06-25 07:54:33 +00:00
2026-06-25 07:54:33 +00:00