Skip to content

Commit 2f6816f

Browse files
authored
feat(showcase/langgraph-python): align chat surfaces + shadcn recipe overhaul (CopilotKit#4753)
## Summary Four LangGraph Python showcase demos updated: - **HITL In-app**: `CopilotChat` → `CopilotPopup` (`defaultOpen={true}`). Tickets panel fills the viewport; chat is a popup in the corner. Approval dialog still portaled to `<body>`. - **Shared State: Streaming**: `CopilotChat` (custom aside) → `CopilotSidebar`. Document view fills the page; sidebar opens by default. - **Shared State: Read + Write**: `CopilotPopup` → `CopilotSidebar`. Card grid breakpoint bumped from `lg:` to `xl:` so cards stack instead of pinching when the sidebar consumes ~480px on mid-size laptops; `overflow-y-auto` on the wrapper keeps the page scrollable. - **Shared State: Read (Recipe)**: visual overhaul with shadcn primitives (`Card`, `Input`, `Select`, `Textarea`, `Badge`, `Button`, `Spinner`, `Separator`). All `data-testid` attributes and QA-doc-asserted strings preserved verbatim ("Make Your Recipe", "AI Recipe Assistant", "+ Add Ingredient", "+ Add Step", "Improve with AI", default ingredients/instructions, all 7 dietary preference labels, all 5 cooking-time labels, all 3 suggestions, etc.). React state-sync logic preserved verbatim (no behavior change). ## Why The chat surfaces were inconsistent across demos — HITL used a full-pane chat where the demo's premise is "approval modals appear *outside* the chat surface", and the shared-state demos mixed `CopilotChat` and `CopilotPopup` instead of using the prebuilt `CopilotSidebar`. The Recipe demo was raw Tailwind while the rest of the suite uses shadcn primitives. ## Plumbing fixes that fell out of the work - **`clsx` + `tailwind-merge` added to `package.json`** as direct deps. They were imported by `src/lib/utils.ts` (the `cn` helper used by all shadcn primitives) but only transitively resolvable, which broke `next dev --turbopack`'s stricter module resolution. - **`globals.css` rule**: `body[data-scroll-locked] { padding-right: 0 !important }`. Radix overlays (Select, Dialog, Popover) use `react-remove-scroll-bar`, which measures the gap between viewport and body inner width to detect scrollbar size — and our body uses `margin-inline-end: 480px` to make room for `<CopilotSidebar />`. The library mis-reads that 480px as scrollbar width and "compensates" with a matching `padding-right`, which shrinks the content area and shifts the centered card every time a dropdown opens. We have no real scrollbar (`body { overflow: hidden }`), so the compensation is unnecessary — neutralize it. Comment in the diff explains the rationale. ## Test plan - [x] `next build` clean across all 54 routes - [x] `oxlint` reports zero new warnings (only pre-existing patterns the diff preserved verbatim) - [x] Manually verified `/demos/hitl-in-app`, `/demos/shared-state-streaming`, `/demos/shared-state-read-write`, `/demos/shared-state-read` in browser (Chrome, dev server) - [x] Verified the Recipe page's Select dropdowns no longer shift the card when opened (the original motivation for the `globals.css` rule) - [ ] HITL e2e suite (`tests/e2e/hitl-in-app.spec.ts`) — selectors preserved (`getByPlaceholder("Type a message")`, suggestion-pill testids, approval-dialog-* testids); CopilotPopup's `defaultOpen={true}` mounts the chat input on first paint - [ ] Streaming and Read e2e suites are stale pre-PR (reference content that doesn't exist in source — "AI Document Editor", "Sales Pipeline") and remain in the same state ## CR loop Ran `cr-loop` (Round 1, 7 agents). Reclassified one finding to bucket (d) per user direction (npm/pnpm lockfile-convention concern is a different PR's subject — pre-existing across all `showcase/integrations/*` and out of scope here). Procedure 3 audit promoted no items to bucket (a); 24 bucket (c) items confirmed as pre-existing and routed to follow-up backlog. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
2 parents 19494c0 + 3d47397 commit 2f6816f

9 files changed

Lines changed: 1558 additions & 618 deletions

File tree

showcase/integrations/langgraph-python/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"@radix-ui/react-checkbox": "^1.3.3",
2323
"@radix-ui/react-separator": "^1.1.8",
2424
"class-variance-authority": "^0.7.1",
25+
"clsx": "^2.1.1",
2526
"cmdk": "^0.2.1",
2627
"embla-carousel-react": "^8.6.0",
2728
"lucide-react": "^1.14.0",
@@ -33,6 +34,7 @@
3334
"react-markdown": "^10.1.0",
3435
"recharts": "^2.15.0",
3536
"remark-gfm": "^4.0.1",
37+
"tailwind-merge": "^3.5.0",
3638
"yaml": "^2.8.4",
3739
"zod": "^3.24.0"
3840
},

showcase/integrations/langgraph-python/pnpm-lock.yaml

Lines changed: 1260 additions & 390 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

showcase/integrations/langgraph-python/qa/hitl-in-app.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@
1010

1111
### 1. Basic Functionality
1212

13-
- [ ] Navigate to `/demos/hitl-in-app`; verify the page renders within 3s as a two-column layout — left column is the "Support Inbox" panel, right column is the `CopilotChat` surface (bordered, `w-[420px]`)
13+
- [ ] Navigate to `/demos/hitl-in-app`; verify the page renders within 3s with the "Support Inbox" panel filling the viewport and the `CopilotPopup` open by default in the bottom-right corner
1414
- [ ] Verify the left header shows the eyebrow "Support Inbox", heading "Open tickets", and the instruction text mentioning "approval dialog here in the app — outside the chat"
1515
- [ ] Verify exactly 3 ticket cards render with test ids `ticket-12345`, `ticket-12346`, `ticket-12347`, each showing the customer name (Jordan Rivera / Priya Shah / Morgan Lee), subject line, and status pill ("Open" green, "Escalating" amber)
1616
- [ ] Verify ticket #12345 displays "Disputed amount: $50.00"
17-
- [ ] Verify the `CopilotChat` input placeholder is visible and no approval dialog is rendered on initial load
17+
- [ ] Verify the `CopilotPopup` chat input placeholder is visible (popup opens by default) and no approval dialog is rendered on initial load
1818
- [ ] Send "Hello" and verify the agent responds within 10s
1919

2020
### 2. Feature-Specific Checks
@@ -30,7 +30,7 @@
3030

3131
- [ ] Click the "Approve refund for #12345" suggestion (or type the equivalent prompt)
3232
- [ ] Within 15s verify an approval modal appears with `data-testid="approval-dialog-overlay"` (fullscreen fixed backdrop with `backdrop-blur-sm`) and `data-testid="approval-dialog"` (centered card with `role="dialog"` and `aria-modal="true"`)
33-
- [ ] Verify the modal is rendered at the document root (portaled via `createPortal(content, document.body)`) — confirm in DevTools that `approval-dialog-overlay` is a direct descendant of `<body>`, NOT nested inside the `CopilotChat` container
33+
- [ ] Verify the modal is rendered at the document root (portaled via `createPortal(content, document.body)`) — confirm in DevTools that `approval-dialog-overlay` is a direct descendant of `<body>`, NOT nested inside the `CopilotPopup` container
3434
- [ ] Verify the modal shows the eyebrow "Action requires your approval", a heading containing the action summary (with concrete numbers such as "$50" and "#12345"), and optional context block below
3535
- [ ] Verify the textarea `data-testid="approval-dialog-reason"` is present with placeholder "Add a short note the assistant will see…"
3636
- [ ] Type a short note (e.g. "Verified duplicate charge") into the reason textarea
@@ -55,7 +55,7 @@
5555

5656
#### Modal Is Outside the Chat (Contract Check)
5757

58-
- [ ] While the modal is open, verify the chat transcript is still scrollable / visible on the right and does NOT contain an inline copy of the approval UI
58+
- [ ] While the modal is open, verify the chat transcript inside the popup is still scrollable / visible and does NOT contain an inline copy of the approval UI
5959
- [ ] Confirm that closing the modal via Approve/Reject is the only resolution path — there is no inline "approve" button rendered inside a chat bubble
6060

6161
### 3. Error Handling

showcase/integrations/langgraph-python/qa/shared-state-read-write.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
### 1. Basic Functionality
1111

12-
- [ ] Navigate to `/demos/shared-state-read-write`; verify the page renders within 3s with the left sidebar (preferences + notes cards) and the right-side `CopilotChat` pane
12+
- [ ] Navigate to `/demos/shared-state-read-write`; verify the page renders within 3s with the preferences + notes cards in the main column and the `CopilotSidebar` open by default on the right (cards stack vertically once the viewport drops below `xl` / 1280px)
1313
- [ ] Verify `data-testid="preferences-card"` is visible with heading "Your preferences"
1414
- [ ] Verify `data-testid="notes-card"` is visible with heading "Agent notes" and empty-state `data-testid="notes-empty"` reading "No notes yet. Ask the agent to remember something."
1515
- [ ] Verify the chat input placeholder is "Chat with the agent..."

showcase/integrations/langgraph-python/src/app/demos/hitl-in-app/page.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
import { useState } from "react";
44
import {
5-
CopilotChat,
65
CopilotKit,
6+
CopilotPopup,
77
useFrontendTool,
88
} from "@copilotkit/react-core/v2";
99
import { z } from "zod";
@@ -86,11 +86,15 @@ function Layout() {
8686
};
8787

8888
return (
89-
<div className="grid h-screen grid-cols-[1fr_420px] bg-gray-50">
89+
<div className="h-screen bg-gray-50">
9090
<TicketsPanel />
91-
<div className="border-l border-gray-200 bg-white">
92-
<CopilotChat agentId="hitl-in-app" className="h-full" />
93-
</div>
91+
<CopilotPopup
92+
agentId="hitl-in-app"
93+
defaultOpen={true}
94+
labels={{
95+
chatInputPlaceholder: "Type a message",
96+
}}
97+
/>
9498
{dialog.open && (
9599
<ApprovalDialog pending={dialog.pending} onResolve={handleResolve} />
96100
)}

showcase/integrations/langgraph-python/src/app/demos/shared-state-read-write/demo-layout.tsx

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"use client";
22

33
import React from "react";
4-
import { CopilotPopup } from "@copilotkit/react-core/v2";
4+
import { CopilotSidebar } from "@copilotkit/react-core/v2";
55

66
import { PreferencesCard, Preferences } from "./preferences-card";
77
import { NotesCard } from "./notes-card";
@@ -20,26 +20,33 @@ export function DemoLayout({
2020
onClearNotes,
2121
}: DemoLayoutProps) {
2222
return (
23-
<div className="min-h-screen w-full bg-gray-50">
23+
<div className="min-h-screen w-full overflow-y-auto bg-gray-50">
2424
<main className="mx-auto max-w-6xl p-6 md:p-10">
2525
<header className="mb-8 space-y-2">
2626
<h1 className="text-3xl font-semibold text-[#010507]">
2727
Shared state — read &amp; write
2828
</h1>
2929
<p className="text-sm text-[#57575B] max-w-2xl">
3030
The UI writes preferences into agent state and reads back the
31-
agent&apos;s scratch pad. Open the chat popup in the corner and ask
32-
the agent to remember something.
31+
agent&apos;s scratch pad. Open the chat sidebar and ask the agent to
32+
remember something.
3333
</p>
3434
</header>
3535

36-
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 items-start">
37-
<PreferencesCard value={preferences} onChange={onPreferencesChange} />
38-
<NotesCard notes={notes} onClear={onClearNotes} />
36+
<div className="grid grid-cols-1 xl:grid-cols-2 gap-6 items-start">
37+
<div className="min-w-0">
38+
<PreferencesCard
39+
value={preferences}
40+
onChange={onPreferencesChange}
41+
/>
42+
</div>
43+
<div className="min-w-0">
44+
<NotesCard notes={notes} onClear={onClearNotes} />
45+
</div>
3946
</div>
4047
</main>
4148

42-
<CopilotPopup
49+
<CopilotSidebar
4350
agentId="shared-state-read-write"
4451
defaultOpen={true}
4552
labels={{

0 commit comments

Comments
 (0)