Rewrite the 'Cutting a release' + 'CI publish flow' sections to describe
the aimock-pattern flow (hand-bump + PR-merge + self-gated CI publish)
and the new multi-bullet --summary/--type CLI. Retain the Open VSX
one-time-setup block, the transient-failure handling notes, verification
steps, rollback guidance, and ADO PAT retirement note.
Rewrite vscode-extension-release.sh to cut an aimock-style release commit
(bump package.json + prepend CHANGELOG.md section) without tagging or
pushing — CI now owns tag/release creation on main-push. Takes one or
more --summary bullets grouped by --type (Added/Changed/Fixed/Removed/
Deprecated/Security, Keep-a-Changelog style). Supports --dry-run.
Seed packages/vscode-extension/CHANGELOG.md with a retroactive v0.1.0
entry covering the three launch surfaces (A2UI Preview, Hook Explorer,
AG-UI Inspector) so the first real release has prior history to anchor
against and the GH Release step has notes to pull from.
Wrap both registry publish steps in a bash retry helper that retries up
to 5 times with staggered backoff (10s/20s/40s/60s/90s) on transient
conditions (5xx, timeouts, connection resets, DNS). Auth and validation
errors still fail fast with no retry.
Critically, 'version already exists' is treated as idempotent success:
if attempt N-1 landed on the registry but its response was lost to a
502 after commit, attempt N sees the already-published version and
short-circuits rather than failing the job.
Motivated by Open VSX /publish returning intermittent 502 Bad Gateway
errors from Eclipse Foundation infra. Each attempt is wrapped in
::group:: markers so per-attempt logs are collapsible in the Actions
UI. Reconciliation step updated to reflect retry semantics and to
tell the operator to rerun the job (not bump the version) on exhausted
retries. RELEASING.md gets a 'Transient registry failures' section
documenting the behavior for both CI and manual publish paths.
Covers prerequisites (VSCE_PAT + OVSX_PAT as production env secrets),
the one-time Open VSX namespace claim, how to cut a release via the
local helper script, the CI publish flow, verification URLs, rollback
guidance (registries are immutable, ship forward), and a note on the
2026-12-01 Azure DevOps PAT sunset.
Widens CopilotChatView's autoScroll prop to accept "pin-to-bottom" |
"pin-to-send" | "none" | boolean. "pin-to-send" scrolls the latest
user message to ~16px from the top on send and maintains a dynamic
bottom spacer so the viewport doesn't chase the streaming response.
Boolean back-compat: true -> "pin-to-bottom", false -> "none".
## Summary
Three connected features land together so the CopilotKit VS Code
extension becomes a coherent debugger/preview surface:
1. **Hook Explorer** — every V1 + V2 render hook can be discovered and
previewed live with auto-generated controls, an inline `▶️ Preview
Component` CodeLens, and a sidebar that lists every captured site.
2. **AG-UI Event Inspector** — live SSE debug stream of all AG-UI
events, filterable and color-coded, in a sidebar view + editor panel.
3. **A2UI Catalog sidebar → webview** — the last native TreeView gets
replaced with a Tailwind-styled webview that matches the other two, now
with a proper **Go to source** action on components and fixtures.
## Hook Explorer
### Discovery + preview
- oxc-based scanner walks the workspace and finds every call-site of any
hook in the registry (17 across V1 + V2, render + data).
- Preview panel bundles the user's source via Rolldown (IIFE format,
React externalized, CSS collected per `@copilotkit/a2ui-renderer`
pattern), executes it in the webview with a capture-only **stub** for
`@copilotkit/react-core` (+ `/v2`), and mounts the user's component just
long enough to record each hook's config.
- Auto-generated form on the left/top drives the `render` prop's
args/parameters/state/event live. V1 parameter arrays and V2 Zod /
Standard Schema all map through a unified `FormSchema` derived at
runtime from the captured config.
- `useCopilotAction`, `useCopilotAuthenticatedAction_c`,
`useCoAgentStateRender`, `useLangGraphInterrupt`, `useRenderTool`,
`useRenderToolCall`, `useDefaultRenderTool`, `useLazyToolRenderer`,
`useRenderCustomMessages`, `useRenderActivityMessage`,
`useHumanInTheLoop`, `useInterrupt`, `useFrontendTool`, `useComponent`,
`useDefaultTool` all previewable.
- Inline `▶️ Preview Component` CodeLens above every render-hook call
site, backed by the same `copilotkit.hooks.preview` command as the
sidebar.
- Imported render components work: rolldown walks transitive imports
from the hook's `render` prop through any number of sibling files.
- Cross-file hook switches are robust: controls are reset on load,
Harness only mounts once the real HostRoot is ready, a top-level error
boundary auto-recovers when you pick a different hook.
### Why the stub approach
Bundling the real `@copilotkit/react-core` through rolldown's IIFE
output hit a `__commonJSMin` TDZ chain (`require_clipboard`,
`require_graphql`, `require_context_helpers`, …) because the
chat/runtime-client/markdown graph has circular imports. Externalizing
react-core + routing to a Proxy-backed stub that captures hook configs
avoids the whole CJS wrapping problem, shrinks the preview bundle from
~24 MB to ~1.3 KB, and keeps the preview runtime path completely
runnable without a live CopilotKit backend. Tradeoff documented in
`copilotkit-stubs.ts`.
### Weather-themed fixtures
14+ fixtures under `packages/vscode-extension/test-workspace/hooks`,
each a distinct visual scenario (forecast card, severity-palette alerts
with imported CSS, forecast strip, live radar grid, conic-gradient
precipitation gauge, air-quality badge with imported render, pollen
report with a 2-hop import graph, HITL evacuation confirm,
sunrise/sunset gradient, etc.). Used both as regression fixtures and as
the demo surface for video.
### Styling
- Tailwind-via-CDN + VS Code CSS variables for theme-aware chrome.
- User-provided CSS imports collected by rolldown and injected as a
`<style>` tag per load.
- Controls + form fields converted to Tailwind; textarea matches input
styling.
- Framed "Rendered output" card so the render prop is visually
unmistakable.
## AG-UI Event Inspector
### Runtime (`@copilotkit/runtime` + `@copilotkit/shared`)
- `DebugEventBus` — in-memory pub/sub on `BaseCopilotRuntime`, only
instantiated when `NODE_ENV != production`.
- Event tap in `createSseEventResponse` broadcasts every AG-UI event
with metadata (agentId, threadId, runId, timestamp).
- `GET /debug-events` SSE endpoint — returns 404 in production, streams
`DebugEventEnvelope` JSON to connected clients, initial `: connected`
comment flushes headers immediately.
### VSCode Extension
- `DebugStream` — Node SSE client with auto-reconnect, exponential
backoff, URL validation, error surfacing.
- `InspectorPanel` (editor panel, command `CopilotKit: Open AG-UI
Inspector`) and `InspectorViewProvider` (sidebar view) both use a shared
`DebugStream` instance — events persist when switching tabs.
- Inspector React app: `ConnectionBar`, `FilterBar`, `EventList`,
`EventDetail`.
- Color scheme: purple (lifecycle), red (errors), blue (text), orange
(tools), green (reasoning), teal (state), yellow (activity), gray
(unknown).
## A2UI Catalog → webview
- Replaces `ComponentPreviewProvider` (native TreeDataProvider) with
`CatalogListViewProvider` (WebviewViewProvider), matching the Hooks and
Inspector sidebars.
- New React webview with refresh header, component rows (name + relative
path + `auto` badge when no fixture), expandable fixtures list.
- Click a component row → preview (or toggle if it has fixtures); click
a fixture row → preview that fixture.
- Hover action buttons: `▷` preview + `</>` go-to-source on every row.
- "Go to source" opens the component file for component rows; for
fixture rows it opens the fixture file and jumps the cursor to the named
fixture key.
## Test coverage
- Runtime: DebugEventBus unit tests (8), handleDebugEvents endpoint (5),
fetch-router routes (4), integration across Express/Hono/Node/Fetch (9).
- Hooks: scanner + 16 fixture bundle-smoke test, regression guard
against `node_<builtin>` self-references, CSS collector test, stub-based
capture E2E, cross-kind controls remount, FormRenderer defensive
rendering.
- Inspector + webview: DebugStream reconnect (10), inspector components
(17), colors (9).
- Total: **178 tests** passing for the vscode-extension package; runtime
suite unchanged.
## Test plan
- [ ] `pnpm nx run copilotkit-vscode-extension:build` and `pnpm nx run
copilotkit-vscode-extension:test` both green
- [ ] F5 launches the Extension Dev Host with `test-workspace` open
- [ ] Hooks sidebar lists every fixture hook; click a row → preview
opens; `</>` button opens the source
- [ ] `▶️ Preview Component` CodeLens shows above every render hook in a
`.tsx` file; clicking it opens the preview
- [ ] Form controls drive the render live; cross-kind hook switches
(action ↔ custom-message) don't crash; a forced render-prop throw
recovers when a different hook is picked
- [ ] Imported-render fixtures (`ImportedAirQuality`,
`ImportedPollenReport`) bundle and preview correctly
- [ ] A2UI Catalog sidebar is the new webview, refresh works, `</>` on a
fixture opens the fixture file and reveals the named key
- [ ] AG-UI Inspector connects to `GET /debug-events`, filters + detail
work, events survive sidebar/panel switch, invalid URL shows red error
- ThreadStoreRegistry.register() now fires onThreadStoreUnregistered
before overwriting an existing store so subscribers (web inspector)
don't stay subscribed to stale stores on replacement; test extended
to verify both events fire in order
- Wire handleClearThreads to POST /threads/clear route; add RouteInfo
variant, router pattern, and fetch-handler case so the inspector can
actually call it
- Add handleGetThreadMessages tool-call mapping test using properly
typed Message objects (role as const, type as const) — exercises the
real mapping code path without mocking getThreadMessages
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Excludes showcase/shell-docs and showcase/shell-dojo demo-content.json from
the size check in lefthook.yml — these data files were added by main but
weren't in the exemption list, causing the pre-commit hook to reject them.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove all demo/scripted animation code (demoMode, demoThreads,
demoConversation, startDemoAnimation, etc.) from the clean PR branch.
Also fix duplicate onAgentRunStarted handler, remove unused
announcementMarkdown field, and remove dead getSelectedMenu method.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- middleware SKILL: beforeRequestMiddleware runs AFTER hooks.onRequest (not before); cite fetch-handler.ts:136-147 for exact order
- agent-runners/in-memory ref: delete globalThis[Symbol] leaves module-captured reference intact; use in-place Map.clear() on data.stores + data.historicRunsBackup; flag missing official reset helper as follow-up
- intelligence-mode SKILL: identifyUser does NOT forward thrown Response (resolveIntelligenceUser converts to generic 500); gate auth rejection in hooks.onRequest which does forward Responses
- built-in-agent/factory-modes ref: custom factory example now checks abortSignal.aborted at entry and between yields; note that agent.abortRun() only flips the flag — generator must consult it
- setup-endpoint Express delegate: stream SSE via Readable.fromWeb().pipe(res) instead of buffering via arrayBuffer(); applies to both the recommended pattern and the CRITICAL "Correct" example. Add Node 18.17+ note.
- built-in-agent frontmatter: maxSteps defaults to undefined (AI SDK stops after one generation), not 1.
- built-in-agent examples: replace bare anthropic("claude-sonnet-4") / anthropicText("claude-sonnet-4") with the dated id "claude-sonnet-4-5-20250929" (AnthropicMessagesModelId in @ai-sdk/anthropic@3.0.49 does not accept the bare form).
- 0-to-working-chat Next.js: align catch-all with migration-playbook — use [[...slug]] so the bare /api/copilotkit basePath also routes.
- migration-playbook v1->v2 parameters: v1 entries default to required: true; only map to .optional() when required: false was explicit. Prevents silently flipping the contract.
- switching-agents Recipe 2 (Tabs): hold activeAgent in a ref so the onAgentsChanged subscribe effect only re-binds on copilotkit change, not on every tab click.
- a2ui-rendering custom-catalog example: use the real createCatalog(definitions, renderers) signature with Zod schemas; remove the bogus extractSchema<T>() generic (types erase at runtime).
- spa-without-runtime clipboard handler: correct the comment — returning a structured error does NOT trigger onError with tool_handler_failed; only thrown handlers do.
- agent-runners: agent_thread_locked fires only on Intelligence 409, not SSE;
SSE surfaces "Thread already running" as a plain 500 body
- agent-runners: SqliteAgentRunner install-hint is unreachable — the import of
@copilotkit/sqlite-runner fails at module load if better-sqlite3 is missing
- agent-runners: soften "CopilotIntelligenceRuntimeOptions explicitly excludes
runner" — the field is simply not declared; excess-property checks flag it
- agent-runners/sqlite: mirror real schema (CREATE INDEX IF NOT EXISTS)
- middleware: consistent afterRequestMiddleware signature across Wrong/Correct
examples; document the full { runtime, response, path, messages?, threadId?,
runId? } shape
- server-side-tools: Factory Mode example now uses AI SDK + the real converter
(convertToolDefinitionsToVercelAITools); TanStack caveat called out since
defineTool output is not a TanStack tool and there is no built-in converter
- setup-endpoint: Cloudflare Workers example hoists runtime/handler to module
scope behind a lazy getter; openaiText signature corrected to createOpenaiChat
when passing an explicit API key (openaiText config omits apiKey)
- transcription: JSON payload mode works in both multi-route and single-endpoint
— dispatch is by Content-Type, not endpoint mode
- wiring-external-agents/mastra: getLocalAgents requires resourceId; remote
Mastra section now points at HttpAgent instead of misshaping getRemoteAgents
- package.json: add per-condition types to every exports entry so bundler and
moduleResolution: "bundler"/"node16" both pick up the matching .d.mts/.d.cts
- useAgent: isRunning is not on the return type; sourced from agent
- addContext: agentId is not accepted; remove "per-agent escape hatch"
- provider-setup: publicApiKey is canonical, publicLicenseKey is alias
- chat-components/attachments: add missing copilotkit.runAgent call
- attachments: map Attachment[] to InputContent[] before spreading
- rendering-activity-messages: fix Rules-of-Hooks violation in "Correct" example
- custom-message-renderers: guard runId.slice against missing-run-id fallback
- human-in-the-loop: abort-on-unmount effect was capturing stale isRunning
- client-side-tools: Skeleton imports from @/components/ui/skeleton
- threads: CopilotKitIntelligence ships on @copilotkit/runtime/v2 with apiUrl/wsUrl/apiKey/organizationId config
- provider-setup: remove stale-token useMemo lead example
- package.json: add per-condition types to exports map
- Wrap the loading-skeleton provider example in children (was self-closing
while every other example wraps CopilotChat).
- Declare `const theme` in the custom catalog snippet so the `a2ui={{ theme,
catalog }}` spread isn't referencing an undefined identifier.
- Mirror the real built-in bridge's `if (copilotkit.properties)` guard in
the try/finally cleanup example so the pattern doesn't throw
`TypeError: Cannot destructure null` and mask the original runAgent error.
- Drop the "Explicit renderer" subsection: construction-only example
contradicted the adjacent "do NOT pass to renderActivityMessages"
anti-pattern and had no legitimate usage snippet.
- Clarify frontmatter: `createA2UIMessageRenderer` ships from
`@copilotkit/react-core/v2`; low-level primitives ship from
`@copilotkit/a2ui-renderer`. Removed stale `@ag-ui/a2ui-middleware`
reference since no example imports it.
- Wrap Python examples in `async def agent_generator():` and mark as
pseudocode (surface_id kwarg shape varies by A2UI SDK version).
- Soften the "only @copilotkitnext/angular" blanket claim to describe the
specific mistake instead.
- package.json: add per-condition `types` to the `exports` map so
`attw --profile node16` and TS `moduleResolution: node16/nodenext/bundler`
consumers resolve types correctly via both import and require.
intent edit-package-json replaced "files": ["skills"] instead of appending, excluding dist/ from runtime and react-core published packages. publint caught it (pkg.exports.[].import/require -> file not published). Restoring dist + skills.
a2ui-renderer was unaffected (had existing "files": ["dist"] which got appended correctly).
- package.json: remove the view/title binding that put
'Open AG-UI Inspector' on the A2UI Catalog view — the inspector is
its own sidebar tab now, so adding that action to the catalog title
bar was redundant noise. Inspector view keeps its own binding.
- Both sidebar webviews (hook-list + catalog-list) had an in-webview
refresh button that duplicated the native VS Code title-bar refresh
icon already registered under view/title. Drop the React buttons
and rely on the native icons; single source of truth and cleaner
header.
- Keep deletion of sdk-python/copilotkit/langgraph_agent.py (deprecated LangGraphAgent
removed in this PR; main's unrelated bug fixes are superseded by our removal)
- Resolve poetry.lock conflict by taking main's ag_ui_langgraph 0.0.33
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Review-loop fixes on the VS Code extension surface:
Scanner / lens
- hook-scanner.ts: extract scanContent(filePath, content) so callers
with the live editor buffer can parse that instead of re-reading the
last-saved on-disk version. scanFile keeps the same on-disk semantics
via delegation.
- hook-scanner.ts: scanWorkspace now returns {sites, capped, filesScanned}
so MAX_FILES_SCANNED truncation is observable instead of silent.
- activate-hook-explorer.ts: when the cap trips, log the detail to the
Hook Explorer output channel and surface a user-facing warning toast
pointing at it. Tighten the save-path catch to log unexpected throws
instead of swallowing them.
- hook-lens-provider.ts: consume document.getText() via scanContent so
lenses follow unsaved edits. Output channel is now required and the
scanContent-throw path logs to it.
Panels (bounded queues + durable error trails)
- preview-panel.ts + hooks/panel.ts: pendingMessages now capped
(1000 and 32 respectively). A wedged webview (CSP violation, crashed
bundle, never-ready) used to grow these buffers unbounded on every
save. Latched pendingCapLogged logs one breadcrumb per wedged
episode, resets on 'ready' (and on dispose for hooks/panel) so a
recovered session can re-arm the warning.
- preview-panel.ts: removed the misleading type:'error' message on
successful .fixture.ts[x] bundles — a valid fixture is not an error.
- activate.ts: catalog onOpenSource now wraps openTextDocument in a
try/catch that appends the full stack to a dedicated
'CopilotKit A2UI Catalog' output channel in addition to the toast,
so open-source failures leave a durable trail instead of a 5-second
flash.
- activate.ts: revert the dead startOffset param added to
findValuePosition — the plumbing through validateFixtureMessages
wasn't done in the same PR, leaving unused API surface. Kept the
limitation comment as documentation.
Catalog wire protocol
- catalog-list-view-provider.ts: normalize fixtureName (?? undefined)
at the wire boundary so a JSON null can't slip through into callback
code that declares 'string | undefined'. Exhaustive 'never' guard
also logs an unknown-variant warning for version-skew scenarios
(older host receives a newer webview's message).
Review-loop fixes on the runtime surface:
- sse-response.ts: wrap DebugEventBus.broadcast in try/catch inside the
observer's next — a buggy subscriber used to propagate an exception
that RxJS routed to `error`, killing the SSE stream for an unrelated
reason. Logged via the existing logError helper instead.
- sse-response.ts: non-abort write failures no longer fall through
silently. Previously streamClosed stayed false and the next event
re-attempted a broken writer. Now logs + sets streamClosed.
- sse-connect-agent-id.test.ts: new unit test that feeds a synthetic
observable into handleSseConnect and asserts the emitted envelope
carries the route-resolved agentId ('weather-agent') rather than
the literal 'connect'. Regression guard for the fix in 7f3a8c27c
that the /run-based integration tests didn't cover.
- debug-events.suite.ts: replaced a version of this assertion that
couldn't reliably drive /connect in the integration runtime with a
pointer to the unit test above.
- fetch-router.ts: clarify the cpk-debug-events comment — the prefix
is what guards against agent-name collision, not the ordering of
router branches.
- debug-stream.ts (extension client): include the underlying JSON parse
error message in the emitted error so a truncated SSE frame can be
distinguished from a runtime/protocol version mismatch.
Puts the two most-used views up top and demotes the inspector to last.
No functional change; just the "views.copilotkit" ordering in the
extension manifest.
Dropping 'blob:' from script-src in the prior review-response commit
broke the A2UI catalog preview: webview/App.tsx intentionally
URL.createObjectURLs the bundled IIFE and loads it via
'<script src=blob:…>', so the browser blocked every catalog load with
'Loading the script blob:vscode-webview://… violates Content Security
Policy directive'.
Re-add 'blob:' to preview-panel's script-src with a documented
rationale: the blob content originates from the trusted extension host
over postMessage, not from external input, so the general concern that
'blob: enables arbitrary-string JS' doesn't apply to this threat
model. The hook-preview panel still excludes 'blob:' because it uses
an inline nonce-gated <script> instead.
Per PR review suggestion (14) — a cpk- prefix makes collision with a
user-named agent essentially impossible. The earlier change only
documented the route as reserved; renaming it removes the hazard.
Rename scope:
- URL path segment (/debug-events → /cpk-debug-events) in the router,
handler switch arms, method-not-allowed check, and integration suite.
- RouteInfo union discriminator ('debug-events' → 'cpk-debug-events')
in hooks.ts and its two consumers in fetch-handler.ts.
- VS Code DebugStream client URL.
- Docs page (event-inspector.mdx) — both the Callout and the Steps copy.
Left unchanged (internal names that describe the feature but aren't the
public route):
- packages/runtime/src/v2/runtime/handlers/handle-debug-events.ts
- packages/runtime/src/v2/runtime/core/debug-event-bus.ts
- packages/shared/src/debug-event-envelope.ts (and the DebugEventEnvelope
type)
- 'debug-event' (singular) message type in the VS Code webview bridge —
that's the extension host ↔ webview protocol, not the HTTP route.
Critical
- scripts/hooks/check-binaries.sh: restore showcase data-file exclusions
(demo-content / search-index / starter-content >1 MB) that the inline
refactor dropped; add 'set -eu' so silent shell failures don't hide
policy violations.
- packages/vscode-extension/src/extension/preview-panel.ts: drop 'blob:'
from CSP script-src — it lets arbitrary-string JS execute via Blob URL
and defeats most of CSP's XSS protection.
- packages/vscode-extension/package.json: set private: true. The extension
ships as a .vsix via vsce, not npm, and workspace:* devDependencies
would break an accidental 'npm publish'.
Important
- runtime: forward the real agentId from handleConnectAgent into
handleSseConnect / createSseEventResponse so DebugEventBus envelopes
on /connect carry the actual agent name instead of the literal
'connect'. Updates handle-connect.ts, sse/connect.ts.
- hooks/panel.ts CSP: narrow connect-src from 'https:' to just the
Tailwind CDN. The preview path never drives a real CopilotKit runtime
— all hook calls route through the stub — so there's no legitimate
https: fetch to allow from inside bundled user code.
- extension/utils.ts getNonce(): switch to crypto.randomBytes. Math.random()
is not acceptable for a value that gates inline-script execution.
- .github/workflows/vscode-extension.yml:
* Build step uses 'nx run copilotkit-vscode-extension:build' instead
of 'pnpm run build' (targeted build with Nx caching, not full
monorepo rebuild).
* Added explicit 'Type check' step (tsc --noEmit).
* Added Lint step gated with continue-on-error until the Nx target
exists, so a missing target doesn't break the pipeline.
* Publish job now queries the Marketplace for the current published
version and skips 'vsce publish' when the local package.json
version matches — stops every docs/CI-only push to main from
failing on duplicate-version errors.
- hooks/hook-scanner.ts: bound the synchronous walk at 20 000 files so a
pathologically large workspace can't freeze the extension host; flag
kept in module-level constant with a rationale comment.
- sse-response.ts: document that debugEventBus.broadcast intentionally
runs before the stream-closed gate so debug subscribers see trailing
events even after the SSE client disconnects.
- inspector-panel.ts: subscribe to DebugStream lazily on show() rather
than in the constructor; unsubscribe on panel dispose. Avoids firing
the event callback on every envelope when no panel is open.
Suggestions
- fetch-router.ts: document 'debug-events' as a reserved route so it
can't be shadowed by an agent literally named 'debug-events'.
- activate.ts findValuePosition: add optional startOffset parameter,
document the first-occurrence limitation + the follow-up path for
per-fixture precision.
- activate-hook-explorer.ts isInsideWorkspace: fix JSDoc to reflect the
code (root itself is excluded).
- Remove the '⟵ render() →' chip on the preview card border; the blue
frame + 'Rendered output' label above already carry that meaning
clearly enough.
- Caption now says 'The controls on the left/top drive its props live.'
so it still makes sense at narrow widths where the split collapses
and the controls stack above the render column.
Label
- CodeLens now reads just '▶️ Preview Component' regardless of hook
kind / identity — the sidebar already carries the hook name and the
tooltip still has the full detail when you hover.
Fixtures
- ImportedAirQuality.tsx (V1, useCopilotAction) — render is an imported
reference to AirQualityBadge, defined in
test-workspace/hooks/shared/AirQualityBadge.tsx. Confirms the preview
bundler walks the import when the render prop is a component symbol
rather than an inline arrow.
- ImportedPollenReport.tsx (V2, useRenderTool) — render is an imported
PollenReport, which itself imports from ./shared/pollen-copy.ts.
Exercises a two-hop import graph rooted at the hook's render prop.
Scanner's prefilter skips shared/*.tsx automatically because neither
shared file calls a hook or references @copilotkit/*, so the sidebar
stays tidy — only the two new hook fixtures show up.
Integration test expectations updated: showAirQuality + pollenReport
added to the scan list, and both new files included in the bundle-smoke
loop.
Renders a '▶️ Preview <hook>(<name>)' CodeLens above every render-hook
call-site in .ts / .tsx files. Clicking the lens fires the existing
copilotkit.hooks.preview command with the resolved site — same flow as
the sidebar, just available inline where the hook is called.
- Only render-category hooks (useCopilotAction, useRenderTool,
useLangGraphInterrupt, useHumanInTheLoop, ...) get a lens; data hooks
have nothing to preview and are skipped.
- Provider parses on demand via the same oxc scanner as the sidebar, so
lens positions stay in sync without a separate cache.
- Debounced per-file save handler already rescans sites; we now also
call lensProvider.refresh() from there so lenses update on save.
The catalog sidebar was the last view still rendered via a native
TreeDataProvider while inspector + hooks had already moved to webviews.
Replace it with a matching webview so all three sidebars share the same
Tailwind-driven look and interaction model, and add a 'go to source' action
on every row (component and fixture).
Changes
- New CatalogListViewProvider mirrors HookListViewProvider: buffer
messages until the webview posts 'ready', then flush init +
components. Refresh / preview / openSource actions forwarded to
injected callbacks.
- New React webview at src/webview/catalog-list:
- Sticky 'A2UI Catalog' header with a refresh button
- One row per discovered component (name + relative path + 'auto'
badge when no fixture file exists)
- Expandable fixtures list (click the chevron / row to toggle)
- Hover-reveal actions: ▷ preview (component-only) + </> go-to-source
- Fixture rows preview via click, with a </> button that opens the
fixture file and reveals the named fixture key
- 'Go to source' for components opens the catalog file; for fixtures
opens the fixture file and selects the "<name>": key so the user
lands right on the edit point.
- New bridge types + new tsdown entry for the catalog-list bundle.
- activate.ts: drop the tree provider, own the scan results locally,
hand them to the webview, keep component registry seeding in sync on
file-change rescans.
- package.json: componentPreview view is now type: "webview", renamed
"A2UI Catalog". Removed the TreeView-only view/item/context binding.
- Deleted sidebar/view-provider.ts — no longer referenced.
All three sidebars (Inspector, Hooks, Catalog) now use the same webview
pattern, same go-to-source UX, and the same Tailwind styling.