feat(drawer): CopilotDrawer host theming, mobile launcher, action icons + thread-management/tooltip fixes - #5707
Conversation
📣 Social Copy GeneratorGenerate social media copies (Twitter/X, LinkedIn, Blog Post) for this PR using Claude.
|
@copilotkit/a2ui-renderer
@copilotkit/agentcore-runner
@copilotkit/angular
@copilotkit/bot
@copilotkit/bot-discord
@copilotkit/bot-slack
@copilotkit/bot-store-postgres
@copilotkit/bot-store-redis
@copilotkit/bot-teams
@copilotkit/bot-telegram
@copilotkit/bot-ui
@copilotkit/bot-whatsapp
@copilotkit/core
@copilotkit/react-core
@copilotkit/react-native
@copilotkit/react-textarea
@copilotkit/react-ui
@copilotkit/runtime
@copilotkit/runtime-client-gql
@copilotkit/sdk-js
@copilotkit/shared
@copilotkit/sqlite-runner
@copilotkit/voice
@copilotkit/vue
@copilotkit/web-components
@copilotkit/web-inspector
commit: |
|
Size Change: +1.44 kB (+0.13%) Total Size: 1.1 MB 📦 View Changed
ℹ️ View Unchanged
|
MikeRyanDev
left a comment
There was a problem hiding this comment.
Found one blocking issue in the clipped-name tooltip change.
In packages/web-components/src/drawer/styles.ts, the stacking fix is written as .row.name-clipped:hover { z-index: 2; }, but _syncNameClipping() toggles name-clipped on the .row-name element, not on the surrounding li.row. That selector never matches, so the intended z-lift does not run. Because each row has its own transform-created stacking context, clipped-name tooltips can still paint under later rows, which is the exact visual issue this PR says it fixes.
Please apply the clipped state to the row (or otherwise make the z-index rule target the actual row stacking context) and add coverage/assertion for that class/selector contract.
…ons, self-owned mobile launcher, and UX fixes Polish on top of the shipped <copilotkit-drawer> (#5701), surfaced by the first real de-fork pilot (langgraph-js): - Theming follows the host app's light/dark theme: each token resolves override -> host theme var (--background/--card/...) -> built-in light default, instead of baking light-only values (drawer was light on a dark app). - Row actions are icon buttons (inline lucide-style SVG, currentColor) with instant tooltips. Native title is replaced by a CSS tooltip (matches the react components' delayDuration:0; title's show-delay is browser-fixed) and is positioned to the side so the list's scroll-overflow never clips it. - Archived rows are muted, no longer struck through. - A refetch (e.g. Active<->All filter toggle) keeps the known list visible; the full loading state shows only on the initial empty fetch. - The reserved footer region hides when nothing is slotted into it (it rendered as an empty box at the bottom); mirrors the memories region. - Mobile gets a self-owned floating launcher so the drawer is always openable with no host header wiring. Its icon is swappable via a launcher-icon slot and its position via --cpk-drawer-launcher-top/left (host can center it on its own header controls); exposes part="launcher". Adds element tests for icons+tooltips, archived styling, refetch-preserves-list, footer hide-when-empty, and the mobile launcher (render + open + desktop-absent).
…lotKit>
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.
Also, surfaced during the langgraph-js de-fork validation:
- CopilotChat clears messages when switching to a fresh non-explicit thread
("+ New") so the welcome screen shows instead of the prior thread — including
when a still-loading /connect is superseded by the switch.
- 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.
…tandard CPK tooltip Row-action tooltips used a surface background + border, which read as a button. Restyle to the standard CopilotKit tooltip: primary bubble, primary-foreground text, an arrow, no border. Also suppress the tooltip while the delete-confirm dialog is open — the clicked trash button keeps :focus-visible, which otherwise left its "Delete" tooltip floating over the dialog.
…n mobile The in-header thread-drawer launcher 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 appeared to do nothing on click). Gate it on a reactive mobile viewport check so it 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; nothing masquerades as a chat control.
A long thread name is clipped with an ellipsis; expose the full text via the same instant primary-bubble tooltip as the row actions (not the native `title`), shown only when the name is actually truncated. The name text moved to an inner span so the outer `.row-name` can host the un-clipped bubble, and the hovered row is z-lifted so the bubble isn't painted under later rows (each row is its own stacking context from the entry-animation transform).
4100e69 to
31f31ee
Compare
|
Fixed in edbf98034 — thanks @MikeRyanDev, good catch.
Fix: also stamp the flag on the owning Added a red-green contract test ( |
…ift fires _syncNameClipping toggled name-clipped on .row-name, but the stacking fix in styles.ts targets .row.name-clipped:hover — a different element — so the z-index lift never matched and clipped-name tooltips still painted under later rows (each row is its own transform stacking context). Stamp the flag on the owning .row too; the tooltip bubble stays scoped to .row-name:hover so a row-action hover never surfaces it. Adds a red-green contract test asserting the class lands on both the row-name and the row. Addresses MikeRyanDev review on #5707.
|
(Note: the branch was force-rebased onto latest main between my comment and push, so the fix landed as |
MikeRyanDev
left a comment
There was a problem hiding this comment.
Re-reviewed after the clipped-name tooltip fix. The previous blocker is addressed: name-clipped is now stamped on both .row-name and the owning .row, and the contract test covers set/clear behavior.
Verified locally with:
pnpm exec nx run @copilotkit/web-components:test(57 tests passed)pnpm exec nx run @copilotkit/react-core:test(107 files / 1375 Vitest tests passed, plus 2 node test script tests passed)
## What
The **Angular `<CopilotDrawer>` feature** for the CopilotKit SDK — a
ready, usable threads drawer for Angular apps, plus the active-thread
foundation it sits on. (This PR merges the originally-staged PR1+PR2 so
it ships a working feature, not just plumbing.)
**Foundation — active-thread coordination (no new store):**
1. **`CopilotChatConfiguration`** — an injectable, signal-based service
mirroring React's `CopilotChatConfigurationProvider`: owns
`agentId`/`threadId` resolution (controlled prop → override → minted
fallback) + the `hasExplicitThreadId` welcome flag; `setActiveThreadId`
/ `startNewThread` setters that no-op when host-controlled;
single-instance via `useExisting`.
2. **`connectActiveThread`** (internal connector) — reactively pins the
resolved thread onto `agent.threadId` and owns the connect lifecycle
(per-run `AbortController` on `HttpAgent`s, a single caught connect
chain, a staleness-guarded loading cursor, abort+`detachActiveRun()`
teardown), mirroring the standalone `connectToAgent` path. Clears
messages only on a genuine new-thread transition (never on
mount/agent-swap).
3. **`CopilotChat`** consumes the ambient config when present
(input-first precedence: `[agentId]`/`[threadId]` win), seeds the config
from a set `[threadId]`; standalone `[threadId]` usage unchanged when no
provider is present.
**The drawer — `<copilot-drawer>`:**
4. A standalone `OnPush` wrapper around the framework-agnostic
`copilotkit-drawer` Lit element (`@copilotkit/web-components`). Events
bind declaratively; element properties are set imperatively via
`viewChild`+`effect` (the `a2ui-activity-renderer` precedent). Routes
the element's events
(`thread-selected`/`new-thread`/`archive`/`unarchive`/`delete`/`filter-change`/`retry`)
to the config + `injectThreads` mutations (delete-of-active resets to a
fresh thread).
5. **`CopilotDrawerRow`** directive for per-row custom content
(`slot="row:{id}"`), `onThreadSelect`/`onNewThread` host escape-hatch
callbacks, and `<ng-content>` slot passthrough
(`launcher-icon`/`memories`).
6. **`listError`** added to `injectThreads` — a filtered error (genuine
list/mutation errors only, excluding developer/config errors like
"Runtime URL is not configured") so the drawer's error panel never shows
dev strings to end users. Mirrors react-core's `useThreads`.
**Always-licensed:** Angular SDK licensing is no longer a thing, so the
wrapper does not wire the element's `licensed`/`upsell` — no upsell
path. **Inline-chat-only:** no Layer-2 open-state coordination (the
element self-provides its mobile launcher).
## Why
Angular had no active-thread provider (only `injectThreads`) and no
drawer component. This lands both so Angular apps get thread switching +
a usable threads drawer at parity with the React vertical, reinterpreted
for Angular's inline-chat-only surface.
## Testing
`@copilotkit/angular`: tsc clean, `oxlint` 0 errors, `ng-packagr` build
green, **152 tests pass** (full package suite). New coverage: config
precedence/controlled-vs-uncontrolled + single-instance identity;
connector connect-on-switch / clear-only-on-real-transition / cursor
staleness / no-unhandled-rejection / abort+detach teardown (verified
against a real `HttpAgent`); drawer prop binding, all 7 event routings
(incl. delete-active reset), escape-hatch overrides, `renderRow`
projection, `<ng-content>` slot passthrough, and the `listError`
dev-error-exclusion.
Hardened through a full `cr-loop` (foundation: 5 rounds; wrapper: 2
rounds) — the wrapper CR caught + fixed a real bug (the error panel was
leaking dev/config errors; fixed via the filtered `listError`).
## Dependency / release gate
Depends on `@copilotkit/web-components` (the `copilotkit-drawer`
element) being published — the same release gate as the React drawer
work (#5707). `packages/angular` publishes alongside.
## Follow-ups (out of scope, tracked)
- A demo `routes/threads` in `examples/v2/angular/demo` (kept separate
to keep this a clean SDK-only change).
- Pre-existing OSS packaging: `zod` is in `devDependencies` but imported
by production source — should move to `dependencies`/`peerDependencies`.
- `packages/angular/src/lib/threads.spec.ts` uses `describe/it` vs the
flat-`test` convention of sibling specs (pre-existing).
- Minor polish backlog from CR (renderRow multi-row tests, a docblock
tidy) — non-blocking.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
…K CopilotDrawer Replace the hand-rolled threads-drawer fork in every threads-enabled integration example with the SDK <CopilotDrawer> (uncontrolled CopilotChatConfigurationProvider + reserved-column layout + theme no-flash where applicable). 16 examples; all browser/build-validated locally. DRAFT — depends on #5707 and the subsequent npm release; not mergeable until the SDK publishes @copilotkit/web-components and react-core bumps. Pre-merge TODOs in the PR description.
…K CopilotDrawer Replace the hand-rolled threads-drawer fork in every threads-enabled integration example with the SDK <CopilotDrawer> (uncontrolled CopilotChatConfigurationProvider + reserved-column layout + theme no-flash where applicable). 16 examples; all browser/build-validated locally. DRAFT — depends on #5707 and the subsequent npm release; not mergeable until the SDK publishes @copilotkit/web-components and react-core bumps. Pre-merge TODOs in the PR description.
…K CopilotDrawer Replace the hand-rolled threads-drawer fork in every threads-enabled integration example with the SDK <CopilotDrawer> (uncontrolled CopilotChatConfigurationProvider + reserved-column layout + theme no-flash where applicable). 16 examples; all browser/build-validated locally. DRAFT — depends on #5707 and the subsequent npm release; not mergeable until the SDK publishes @copilotkit/web-components and react-core bumps. Pre-merge TODOs in the PR description.
CopilotDrawer — SDK polish + thread-management fixes
Polish and fixes on top of the shipped
<copilotkit-drawer>(#5701), surfaced by real de-fork pilots run against a locally-built SDK + the hosted Intelligence runtime: langgraph-js (rich canvas / inlineCopilotChat/ dark theme) and mastra (the proverbs CoAgents demo /CopilotSidebar/ light theme / controlled→uncontrolled provider). Spans@copilotkit/web-componentsand@copilotkit/react-core— no example/de-fork changes here (those wait on a release that publishes web-components + areact-corebump that includesCopilotDrawer). Captured as Phase-5 acceptance requirements in the design doc.react-core— thread management<CopilotKit>.CopilotChatConfigurationProvidertreated anythreadIdprop as controlled, so the auto-minted, non-explicit threadId that<CopilotKit>'s v1 bridge seeds blocked the drawer's imperativesetActiveThreadId/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.CopilotChatclears 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/connectis superseded by the switch (the aborted connect's stale snapshot is dropped instead of repopulating the view).CopilotDrawertreats a pending (null) license status as loading, not unlicensed, so the upgrade upsell no longer flashes (or sticks) before the runtime reports the license.CopilotModalHeader.DrawerLaunchertogglesdrawerOpen, which only drives the off-canvas mobile drawer; on desktop the drawer is a persistent in-flow panel that ignoresopen, 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 lucideX) is the consistent pair.web-components— theming, launcher, icons, tooltips--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.currentColor). Nativetitleis replaced by an instant CSS tooltip (matches the React components' RadixdelayDuration: 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-visibleotherwise, leaving its "Delete" tooltip floating over the dialog).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).footerregion hides (it rendered as a stray bordered box at the bottom). Driven by aslotchangelistener, mirroring thememoriesregion.launcher-iconslot, 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 exposespart="launcher". The richer in-headerCopilotModalHeader.DrawerLauncherremains an optional integration.Tests
CopilotChatConfigurationProvider, the "+ New" message reset onCopilotChat, license-pending gating onCopilotDrawer, and the mobile-only / desktop-hidden launcher onCopilotModalHeader. New regression tests verified red-green; chat + provider suites green,tsc --noEmitclean, 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.)data-tooltip+ placeholder), archived styling, refetch-preserves-list, footer hide-when-empty, and mobile launcher render/open + desktop-absent. Drawer suite green,tsc --noEmitclean, build +es-checkgreen underCI=true. (Truncation detection + tooltip stacking are layout/CSS — verified live, not in jsdom.)🤖 Generated with Claude Code