mirror of
https://github.com/CopilotKit/CopilotKit.git
synced 2026-09-14 16:26:20 +08:00
ca28191042
## 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)