diff --git a/.github/workflows/claude-review.yml b/.github/workflows/claude-review.yml
index 8ed710c29..0c35c758c 100644
--- a/.github/workflows/claude-review.yml
+++ b/.github/workflows/claude-review.yml
@@ -27,6 +27,7 @@ jobs:
- name: Claude review
uses: anthropics/claude-code-action@806af32823ef69c8ef357086c573a902af641307 # v1
+ continue-on-error: true
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
# Post as the workflow's own GITHUB_TOKEN. Without this the action
@@ -51,10 +52,17 @@ jobs:
- Public API / DX regressions on the published `@threadplane/*` surface
- Missing or weak test coverage for the change
+ For docs-heavy PRs, skip generated or bulky derived files unless
+ they are the only evidence for a problem. In particular, do not
+ spend review budget on `api-docs.json`, generated public context
+ files, build artifacts, or lockfile-sized output. Prefer reviewing
+ the source templates, generators, route code, docs config, and MDX
+ pages that produce or consume those artifacts.
+
Post a concise top-level summary via `gh pr comment`. Post specific
issues as inline comments. Be brief; skip nitpicks and style unless
they affect correctness. If the PR looks good, say so briefly.
claude_args: |
--model claude-sonnet-4-6
- --max-turns 15
+ --max-turns 30
--allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*)"
diff --git a/apps/website/content/docs/chat/api/api-docs.json b/apps/website/content/docs/chat/api/api-docs.json
index 94e7a6123..ad77d918f 100644
--- a/apps/website/content/docs/chat/api/api-docs.json
+++ b/apps/website/content/docs/chat/api/api-docs.json
@@ -1672,7 +1672,7 @@
{
"name": "currentAssistantStreaming",
"type": "Signal",
- "description": "True iff there's a current (last-index) assistant message that's\nstill streaming. The bubble's own caret already signals loading;\nwe suppress the floor typing-indicator in that case so the user\ndoesn't see two loading affordances at once.\n\nMatches the same `streaming + current` condition the bubble uses\nto enable `.chat-message__caret`:\n `agent().isLoading() && i === agent().messages().length - 1`\n `i === agent().messages().length - 1`\n\nRestricted to assistant role because the caret only renders on\nassistant bubbles (`:host([data-role=\"assistant\"][data-current=...\n ][data-streaming=...])`).",
+ "description": "True iff there's a current (last-index) assistant message that's\nstill streaming. The bubble's own caret already signals loading;\nwe suppress the floor typing-indicator in that case so the user\ndoesn't see two loading affordances at once.\n\nMatches the same `streaming + current` condition the bubble uses\nto enable `.chat-message__caret`:\n `this.agent().isLoading() && i === this.agent().messages().length - 1`\n `i === this.agent().messages().length - 1`\n\nRestricted to assistant role because the caret only renders on\nassistant bubbles (`:host([data-role=\"assistant\"][data-current=...\n ][data-streaming=...])`).",
"optional": false
},
{
diff --git a/apps/website/content/docs/chat/getting-started/installation.mdx b/apps/website/content/docs/chat/getting-started/installation.mdx
index 56b2b2919..e9434cfc8 100644
--- a/apps/website/content/docs/chat/getting-started/installation.mdx
+++ b/apps/website/content/docs/chat/getting-started/installation.mdx
@@ -101,7 +101,7 @@ For me, this is the lowest-friction path for a small team — the tradeoff is th
## 3. Wire `provideChat()` and `provideAgent()`
-Register both providers in your application config. Below is a minimal `app.config.ts` for a LangGraph backend (swap `@threadplane/langgraph` for `@threadplane/ag-ui` if you picked that adapter — the API surface is identical).
+Register both providers in your application config. Below is a minimal `app.config.ts` for a LangGraph backend. If you picked AG-UI, component calls stay the same, but the provider import and config shape change (`apiUrl`/`assistantId` for LangGraph, `url` for AG-UI).
```ts
// src/app/app.config.ts
diff --git a/apps/website/content/docs/chat/guides/error-handling.mdx b/apps/website/content/docs/chat/guides/error-handling.mdx
index c56c141e3..ccf83a39c 100644
--- a/apps/website/content/docs/chat/guides/error-handling.mdx
+++ b/apps/website/content/docs/chat/guides/error-handling.mdx
@@ -55,7 +55,7 @@ if (err) {
```
-The Retry button calls `agent().retry()` for you. For non-retryable failures (e.g. `auth`), no button is shown — retrying the same bad credential would just fail again.
+The Retry button calls the bound Agent's `retry()` method for you. For non-retryable failures (e.g. `auth`), no button is shown — retrying the same bad credential would just fail again.
## Custom error UI
diff --git a/apps/website/content/docs/choosing-an-adapter/index.mdx b/apps/website/content/docs/choosing-an-adapter/index.mdx
index ad19215c1..dae8ac329 100644
--- a/apps/website/content/docs/choosing-an-adapter/index.mdx
+++ b/apps/website/content/docs/choosing-an-adapter/index.mdx
@@ -16,7 +16,7 @@ Threadplane ships two adapters that connect a backend agent runtime to ``
## Code comparison
-The two adapters share the same public surface — `provideAgent`, `injectAgent`, `AgentConfig`. Swapping is a one-line import change.
+The two adapters share the same public surface — `provideAgent`, `injectAgent`, and an adapter-specific `AgentConfig`. Component code stays the same; the provider import and config shape change.
### LangGraph adapter
@@ -92,3 +92,9 @@ The `Agent` contract from `@threadplane/chat` is intentionally runtime-neutral.
- `@threadplane/ag-ui` consumes the AG-UI event protocol. Any backend that emits AG-UI events plugs in, regardless of what graph engine sits behind it.
Both adapters are MIT-licensed and live in the same monorepo.
+
+## Next steps
+
+- [LangGraph quickstart](/docs/langgraph/getting-started/quickstart) if your backend is LangGraph Platform and you want native streaming, checkpoints, threads, interrupts, and history APIs.
+- [AG-UI quickstart](/docs/ag-ui/getting-started/quickstart) if your backend emits the AG-UI protocol from CrewAI, Mastra, CopilotKit runtime, or another compatible runtime.
+- [Chat quickstart](/docs/chat/getting-started/quickstart) if you want to start from the drop-in `` surface and wire the adapter afterward.
diff --git a/apps/website/content/docs/langgraph/api/api-docs.json b/apps/website/content/docs/langgraph/api/api-docs.json
index ab7622bd1..d8c23adfb 100644
--- a/apps/website/content/docs/langgraph/api/api-docs.json
+++ b/apps/website/content/docs/langgraph/api/api-docs.json
@@ -2,7 +2,7 @@
{
"name": "AgentLifecycleRegistry",
"kind": "class",
- "description": "Optional registry that collects per-instance agent lifecycles within\nan Angular injection context. External instrumentation packages\n(e.g. cockpit-telemetry) provide this token and read from it.\n\n`@threadplane/langgraph` does NOT provide this itself — `agent()` writes to\nthe registry only when an external consumer has provided it.",
+ "description": "Optional registry that collects per-instance agent lifecycles within\nan Angular injection context. External instrumentation packages\n(e.g. cockpit-telemetry) provide this token and read from it.\n\n`@threadplane/langgraph` does NOT provide this itself — the configured agent\ninstance writes to the registry only when an external consumer has provided it.",
"params": [],
"examples": [],
"properties": [
diff --git a/apps/website/content/prompts/getting-started.md b/apps/website/content/prompts/getting-started.md
index 23ba06f3c..77b8ad6a5 100644
--- a/apps/website/content/prompts/getting-started.md
+++ b/apps/website/content/prompts/getting-started.md
@@ -1,12 +1,12 @@
Add angular to my Angular 20+ application.
-Install: npm install @threadplane/langgraph@latest
+Install: npm install @threadplane/chat @threadplane/langgraph
1. In app.config.ts, add provideAgent({ apiUrl: 'http://localhost:2024', assistantId: 'chat' }) to the providers array. Import it from '@threadplane/langgraph'.
-2. Create a ChatComponent that calls injectAgent() in the constructor or as a field initializer. injectAgent() MUST be called inside an Angular injection context — constructor or field initializer is correct; ngOnInit is not.
+2. Create a component that imports ChatComponent from '@threadplane/chat' and calls injectAgent() in the constructor or as a field initializer. injectAgent() MUST be called inside an Angular injection context — constructor or field initializer is correct; ngOnInit is not.
-3. The component template should loop over chat.messages() using @for and render each message's content. Add an input field and a button that calls chat.submit({ message: inputValue }).
+3. The component template can start with . For custom layouts, loop over chat.messages() with @for and call chat.submit({ message: inputValue }) from your input.
4. In app.config.ts provideAgent call, the apiUrl should point to the LangGraph server. For local dev this is http://localhost:2024. For production use the LangGraph Platform URL from environment.ts.
diff --git a/apps/website/content/prompts/testing.md b/apps/website/content/prompts/testing.md
index 1e87508e9..2c9a3910a 100644
--- a/apps/website/content/prompts/testing.md
+++ b/apps/website/content/prompts/testing.md
@@ -7,6 +7,8 @@ const transport = new MockAgentTransport();
providers: [provideAgent({ transport, assistantId: 'test', apiUrl: '' })]
const chat = injectAgent();
+In Angular tests, put the provider in TestBed.configureTestingModule({ providers: [...] }) or the component's providers array, then read chat with injectAgent() from an Angular injection context.
+
To emit a streaming response:
transport.emit([
{ type: 'values', values: { messages: [] } },
diff --git a/apps/website/content/prompts/thread-persistence.md b/apps/website/content/prompts/thread-persistence.md
index c76cce173..816a6faa5 100644
--- a/apps/website/content/prompts/thread-persistence.md
+++ b/apps/website/content/prompts/thread-persistence.md
@@ -1,13 +1,13 @@
Add thread persistence to my Angular component that uses angular, so conversations survive page refresh.
-1. On component init, read the stored thread ID: const storedId = localStorage.getItem('chat-thread-id').
+1. Put thread persistence in an injectable service so provider config can read it from Angular DI.
-2. Create a signal: threadId = signal(storedId).
+2. In that service, create a signal from storage: threadId = signal(localStorage.getItem('chat-thread-id')).
-3. Pass it to provideAgent: provideAgent({ assistantId: 'chat', threadId: this.threadId, onThreadId: (id) => { this.threadId.set(id); localStorage.setItem('chat-thread-id', id); } }).
+3. Configure the agent with a provider factory: provideAgent(() => { const memory = inject(ThreadMemory); return { assistantId: 'chat', threadId: memory.threadId, onThreadId: (id) => memory.remember(id) }; }).
4. The onThreadId callback fires once when the server creates a new thread. After that, the same thread ID is reused and the full conversation history is restored from the LangGraph server.
-5. To start a new conversation, call this.threadId.set(null) — this causes the injected agent to create a fresh thread on the next submit.
+5. To start a new conversation, call memory.threadId.set(null) and clear storage — this causes the injected agent to create a fresh thread on the next submit.
No changes to the template are needed.
diff --git a/apps/website/scripts/generate-whitepaper.ts b/apps/website/scripts/generate-whitepaper.ts
index ad8e3a25c..2d9562e00 100644
--- a/apps/website/scripts/generate-whitepaper.ts
+++ b/apps/website/scripts/generate-whitepaper.ts
@@ -85,9 +85,9 @@ Context: Angular teams building AI agent applications must wire streaming transp
Cover:
- Why streaming state is hard in Angular (zone.js, change detection, timing)
-- The Angular signals approach: how agent() exposes messages() as Signal
+- The Angular signals approach: how injectAgent() exposes messages() as Signal
- How isLoading() lets developers drive loading UI without polling
-- Code example: minimal agent() setup (TypeScript snippet, 8-12 lines)
+- Code example: minimal provideAgent() plus injectAgent() setup (TypeScript snippet, 8-12 lines)
- Production checklist item: "Are your message signals OnPush-compatible?"
Tone: Direct, technical, peer-to-peer. No fluff. Audience is senior Angular engineers.`,
@@ -106,7 +106,7 @@ Cover:
- The threadId signal and onThreadId callback pattern
- How to persist threadId to localStorage and restore on mount
- Thread list UI and switching between conversations
-- Code example: provideAgent() plus agent({ threadId }) with thread persistence (8-12 lines)
+- Code example: provideAgent() with a threadId signal plus injectAgent() in the component (8-12 lines)
- Production checklist item: "Does your agent UI resume threads correctly after a browser refresh?"
Tone: Direct, technical, peer-to-peer. No fluff. Audience is senior Angular engineers.`,
@@ -142,7 +142,7 @@ Context: Production agents that take consequential actions — sending emails, d
Cover:
- The LangGraph interrupt() and Command.RESUME pattern
- Why polling and custom websocket approaches are brittle
-- The interrupt() signal in agent() and how it maps to approval state
+- The interrupt() signal returned by injectAgent() and how it maps to approval state
- headless and prebuilt
- The three approval actions: approve, edit, cancel — and how each maps to a resume command
- Code example: interrupt signal binding (8-12 lines)
@@ -221,20 +221,20 @@ Tone: Direct, technical, peer-to-peer. No fluff. Audience is senior Angular engi
},
{
id: 'agent-api',
- title: 'The agent() API',
+ title: 'The injectAgent() API',
prompt: `Write a 400-600 word chapter for an engineering white paper titled "The Enterprise Guide to Agent UI in Angular".
-Chapter topic: The agent() API
+Chapter topic: The injectAgent() API
-Context: @threadplane/langgraph exposes an Angular signals-based API for connecting LangGraph agents to Angular components. The core primitive is agent() — a function that returns reactive signals wired directly to the agent stream, with no manual subscription management, no zone-patching, and no token accumulation logic.
+Context: @threadplane/langgraph exposes an Angular signals-based API for connecting LangGraph agents to Angular components. The core primitive is injectAgent() — an Angular DI helper that returns reactive signals wired directly to the configured agent stream, with no manual subscription management, no zone-patching, and no token accumulation logic.
Cover:
-- How agent() returns a LangGraphAgent with typed signals: messages(), isLoading(), error(), interrupt(), and langGraph* raw signals
+- How injectAgent() returns a LangGraphAgent with typed signals: messages(), isLoading(), error(), interrupt(), and langGraph* raw signals
- The provideAgent() provider and how it configures the agent endpoint and stream transport
- Why the Angular signals design works with OnPush change detection out of the box
- How to bind agent state directly in Angular templates without async pipe or manual subscriptions
-- Code example: minimal agent() setup with template binding (10-14 lines)
-- The contrast: what the equivalent hand-rolled code looks like vs. agent() in 3 lines
+- Code example: minimal provideAgent() setup with injectAgent() template binding (10-14 lines)
+- The contrast: what the equivalent hand-rolled code looks like vs. injectAgent() in 3 lines
Tone: Direct, technical, peer-to-peer. No fluff. Audience is senior Angular engineers.`,
},
diff --git a/apps/website/src/app/ag-ui/page.tsx b/apps/website/src/app/ag-ui/page.tsx
index a1ad7cede..b0b2d2aab 100644
--- a/apps/website/src/app/ag-ui/page.tsx
+++ b/apps/website/src/app/ag-ui/page.tsx
@@ -51,7 +51,7 @@ export default async function AgUiPage() {
maxWidth: 680,
}}
>
- Build an Angular agent UI on any AG-UI-compatible runtime — LangGraph, CrewAI, Mastra, Microsoft Agent Framework, AG2, Pydantic AI, AWS Strands, CopilotKit. Same primitives, same chat surface, same testing story.
+ Build an Angular agent UI on any AG-UI-compatible runtime — CrewAI, Mastra, Microsoft Agent Framework, AG2, Pydantic AI, AWS Strands, CopilotKit, or LangGraph fronted by AG-UI. Same Agent contract and chat surface; runtime-specific history and checkpoint behavior stays with the backend.
@@ -111,10 +111,10 @@ export default async function AgUiPage() {
id="primitives"
eyebrow="Same primitives"
headline="Drop-in for everything @threadplane/chat ships."
- body="provideAgent registers an AG-UI client and exposes the same Agent contract that @threadplane/langgraph provides. Every chat primitive — durable threads, interrupts, subagents, generative UI, citations — works against any AG-UI runtime."
+ body="provideAgent registers an AG-UI client and exposes the same Agent contract that @threadplane/langgraph provides. Chat rendering, status, tool calls, generative UI, and citations use the same Angular primitives; durable checkpointed threads and history depend on the backend protocol, so use @threadplane/langgraph when you need the native LangGraph thread API."
bullets={[
'provideAgent + injectAgent — same names across adapters',
- 'Identical Agent contract: messages() / status() / interrupt() / reload()',
+ 'Shared Agent contract: messages() / status() / reload()',
'Same A2UI surface, themes, and citations rendering',
'MockAgentTransport works the same way for tests',
]}
diff --git a/apps/website/src/app/docs/page.tsx b/apps/website/src/app/docs/page.tsx
index ee3369d43..049bcf737 100644
--- a/apps/website/src/app/docs/page.tsx
+++ b/apps/website/src/app/docs/page.tsx
@@ -30,7 +30,7 @@ const BACKENDS: Backend[] = [
{
title: 'LangGraph',
blurb: 'For LangChain & LangGraph backends.',
- install: 'npm i @threadplane/langgraph',
+ install: 'npm i @threadplane/chat @threadplane/langgraph',
href: '/docs/langgraph/getting-started/quickstart',
logoSrc: '/logos/langgraph.svg',
attribution: 'LangChain',
@@ -38,7 +38,7 @@ const BACKENDS: Backend[] = [
{
title: 'AG-UI',
blurb: 'For CrewAI, Mastra, Pydantic AI, Strands, and more.',
- install: 'npm i @threadplane/ag-ui',
+ install: 'npm i @threadplane/chat @threadplane/ag-ui @ag-ui/client @ag-ui/core',
href: '/docs/ag-ui/getting-started/quickstart',
logoSrc: '/logos/runtimes/copilotkit.svg',
attribution: 'AG-UI · CopilotKit',
@@ -347,8 +347,9 @@ export default function DocsLandingPage() {
maxWidth: '52ch',
}}
>
- A suite of MIT-licensed libraries for streaming agent interfaces.
- Pick your backend to get started.
+ Streaming agent interfaces with runtime adapters, a shared Agent contract,
+ and a drop-in chat surface. Most packages are MIT; @threadplane/chat is
+ dual-licensed for noncommercial/evaluation and commercial production use.
@@ -374,7 +375,7 @@ export default function DocsLandingPage() {
{b.install}
- Quickstart →
+ Adapter quickstart →
))}
@@ -384,6 +385,10 @@ export default function DocsLandingPage() {
Choosing an adapter →
+ {' '}Want the drop-in UI first?{' '}
+
+ Chat quickstart →
+
diff --git a/apps/website/src/app/llms.txt/route.ts b/apps/website/src/app/llms.txt/route.ts
index 92c2ccea2..958d255c9 100644
--- a/apps/website/src/app/llms.txt/route.ts
+++ b/apps/website/src/app/llms.txt/route.ts
@@ -26,14 +26,17 @@ function buildLlmsTxt(): string {
'- @threadplane/ag-ui — adapter for any AG-UI-compatible backend (CrewAI, Mastra, Microsoft AF, AG2, Pydantic AI, AWS Strands, CopilotKit runtime)',
'- @threadplane/render — generative UI runtime (Vercel json-render + Google A2UI)',
'- @threadplane/a2ui — protocol types, JSONL parser, dynamic value resolver, and pointer helpers for A2UI streams',
+ '- @threadplane/middleware — backend LangGraph helpers for browser-executed client tools',
'- @threadplane/licensing — offline license verification and non-blocking package check helpers',
'- @threadplane/telemetry — browser, Node, and shared telemetry helpers with privacy controls',
'',
'## Install',
'# LangGraph backend:',
'npm install @threadplane/chat @threadplane/langgraph',
+ '# LangGraph backend with browser client tools:',
+ 'npm install @threadplane/chat @threadplane/langgraph @threadplane/middleware',
'# AG-UI backend:',
- 'npm install @threadplane/chat @threadplane/ag-ui',
+ 'npm install @threadplane/chat @threadplane/ag-ui @ag-ui/client @ag-ui/core',
'',
'## Key API (symmetric across adapters)',
'- provideAgent(config) — wires the adapter into Angular DI. Same name across @threadplane/langgraph and @threadplane/ag-ui.',
@@ -59,7 +62,7 @@ function buildLlmsTxt(): string {
'// Template: ',
'',
'## License',
- 'MIT — free for any use, commercial or noncommercial.',
+ 'Most packages are MIT. @threadplane/chat is free for noncommercial/evaluation use and commercially licensed for production use.',
'',
'## Full reference',
'https://threadplane.ai/llms-full.txt',
diff --git a/apps/website/src/app/page.tsx b/apps/website/src/app/page.tsx
index daa0f5cf2..1537e9f4a 100644
--- a/apps/website/src/app/page.tsx
+++ b/apps/website/src/app/page.tsx
@@ -43,7 +43,7 @@ export default async function HomePage() {
headline="Build the Angular UI layer for production agents."
body={
<>
- provideAgent + agent() give you headless chat, durable threads, interrupts, tool progress, and generative UI. LangGraph and AG-UI adapters share the contract, so teams can swap runtimes without rewriting the Angular surface.
+ provideAgent + injectAgent() give you headless chat, durable threads, interrupts, tool progress, and generative UI. LangGraph and AG-UI adapters share the contract, so teams can swap runtimes without rewriting the Angular surface.
>
}
bullets={[
@@ -53,9 +53,9 @@ export default async function HomePage() {
'One Angular UI layer, swappable runtimes',
]}
supportingCards={[
- { title: 'provideAgent', description: 'Wire the agent into your app.config.ts.' },
- { title: 'AgUiAdapter', description: 'Any AG-UI compliant backend.' },
- { title: 'LangGraphAdapter', description: 'Native LangGraph streaming.' },
+ { title: 'provideAgent', description: 'Wire the agent into Angular DI.' },
+ { title: '@threadplane/ag-ui', description: 'Any AG-UI compliant backend.' },
+ { title: '@threadplane/langgraph', description: 'Native LangGraph streaming.' },
]}
cta={{ label: 'Read the streaming guide', href: '/docs/langgraph/guides/streaming' }}
visual={
@@ -108,12 +108,12 @@ export default async function HomePage() {
id="ship"
eyebrow="Ship"
headline="Patterns built for production, not demos."
- body="Error boundaries, observability hooks, fallback strategies — the stuff that turns a demo into a real app. MIT-licensed, so the code is yours forever."
+ body="Error boundaries, observability hooks, fallback strategies — the stuff that turns a demo into a real app. Most packages are MIT; the drop-in chat package is commercially licensed for production use."
bullets={[
'error() / status() / reload() signals',
'Readiness gate + per-component fallback',
'Thread persistence patterns',
- 'MIT licensed — own it forever',
+ 'Clear package licensing',
]}
supportingCards={[
{ title: 'error/status/reload', description: 'Boundary signals for every agent.' },
diff --git a/apps/website/src/components/docs/DocsSearch.tsx b/apps/website/src/components/docs/DocsSearch.tsx
index 608b8786a..618869b60 100644
--- a/apps/website/src/components/docs/DocsSearch.tsx
+++ b/apps/website/src/components/docs/DocsSearch.tsx
@@ -1,28 +1,39 @@
'use client';
import { useState, useEffect, useRef, useCallback } from 'react';
import { useRouter } from 'next/navigation';
-import { docsConfig, type LibraryId } from '../../lib/docs-config';
+import { docsConfig, specialDocsPages, type LibraryId } from '../../lib/docs-config';
import { tokens } from '@threadplane/design-tokens';
import { analyticsEvents } from '../../lib/analytics/events';
import { track } from '../../lib/analytics/client';
interface SearchablePage {
title: string;
- slug: string;
- section: string;
- library: LibraryId;
+ description?: string;
+ href: string;
+ slug?: string;
+ section?: string;
+ library?: LibraryId;
libraryTitle: string;
}
-const allSearchablePages: SearchablePage[] = docsConfig.flatMap((lib) =>
- lib.sections.flatMap((s) =>
- s.pages.map((p) => ({
- ...p,
- library: lib.id,
- libraryTitle: lib.title,
- }))
- )
-);
+const allSearchablePages: SearchablePage[] = [
+ ...specialDocsPages.map((page) => ({
+ title: page.title,
+ description: page.description,
+ href: page.path,
+ libraryTitle: 'Start here',
+ })),
+ ...docsConfig.flatMap((lib) =>
+ lib.sections.flatMap((s) =>
+ s.pages.map((p) => ({
+ ...p,
+ href: `/docs/${lib.id}/${p.section}/${p.slug}`,
+ library: lib.id,
+ libraryTitle: lib.title,
+ }))
+ )
+ ),
+];
export function DocsSearch({ library }: { library?: LibraryId }) {
const [open, setOpen] = useState(false);
@@ -34,11 +45,12 @@ export function DocsSearch({ library }: { library?: LibraryId }) {
const results = query.length > 0
? allSearchablePages.filter((p) =>
p.title.toLowerCase().includes(query.toLowerCase()) ||
- p.slug.toLowerCase().includes(query.toLowerCase()) ||
- p.section.toLowerCase().includes(query.toLowerCase()) ||
+ p.description?.toLowerCase().includes(query.toLowerCase()) ||
+ p.slug?.toLowerCase().includes(query.toLowerCase()) ||
+ p.section?.toLowerCase().includes(query.toLowerCase()) ||
p.libraryTitle.toLowerCase().includes(query.toLowerCase())
).slice(0, 8)
- : allSearchablePages.filter((p) => !library || p.library === library).slice(0, 6);
+ : allSearchablePages.filter((p) => !library || !p.library || p.library === library).slice(0, 6);
const handleKeyDown = useCallback((e: KeyboardEvent) => {
if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
@@ -64,12 +76,12 @@ export function DocsSearch({ library }: { library?: LibraryId }) {
const navigate = (page: SearchablePage) => {
track(analyticsEvents.docsSearchResultClick, {
surface: 'docs',
- destination_url: `/docs/${page.library}/${page.section}/${page.slug}`,
+ destination_url: page.href,
library: page.library === 'langgraph' || page.library === 'render' || page.library === 'chat' ? page.library : 'unknown',
query_length: query.length,
result_count: results.length,
});
- router.push(`/docs/${page.library}/${page.section}/${page.slug}`);
+ router.push(page.href);
setOpen(false);
};
@@ -125,7 +137,7 @@ export function DocsSearch({ library }: { library?: LibraryId }) {