Skip to content

Commit bbe23e6

Browse files
BenTaylorDevclaude
andcommitted
fix(threads): skip /connect for absent threads, stabilize switch UX (ENT-314)
- Skip copilotkit.connectAgent when CopilotChat lacks a caller-supplied threadId — a locally-minted UUID has no backend record, so /connect would always 404 on the intelligence platform. - Suppress the welcome screen while a connect is in flight and unconditionally when the caller has supplied a threadId (hasExplicitThreadId). Prevents the "How can I help you today?" flash on thread switch. - Gate suggestions on !isConnecting && !isRunning to avoid painting them against a mid-replay message tree. - Defer the isConnecting release by one animation frame so trailing bootstrap renders commit before the flag flips. - Reserve room for the "Powered by CopilotKit" license badge via a new --copilotkit-license-banner-offset CSS var published by the banner on mount; chat input consumes it only when bottom-anchored. - Sort and display threads by lastRunAt (fallback to updatedAt → createdAt) so metadata-only actions like archive/rename don't reshuffle the list. - useThreads waits for runtimeConnectionStatus === Connected before dispatching the store context, eliminating the speculative /threads fetch that fired before /info returned wsUrl. Threads example polish: restore button + tooltips on archive/restore/delete, segmented Active/All filter, graceful error state, skeleton rows on initial load, stable scrollbar gutter, pre-paint dark-mode class, logo position stable across app/chat modes, drop dynamic-import drawer wrapper that caused null first paint, archived-row dimming via child colors instead of opacity. Tests: - CopilotChat.absentThreadConnect: connect is skipped without a threadId, fires when supplied via prop or config. - CopilotChatView.connectingGate: isConnecting suppresses welcome; hasExplicitThreadId suppresses welcome on empty chat. - threads (core): lastRunAt sort fallback ordering. - use-threads: Connecting-state gate defers /threads until Connected. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent ae20d6b commit bbe23e6

16 files changed

Lines changed: 14177 additions & 203 deletions

File tree

examples/integrations/langgraph-python-threads/apps/app/index.html

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,21 @@
55
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
66
<link rel="icon" type="image/svg+xml" href="/copilotkit-logo-mark.svg" />
77
<title>CopilotKit + LangGraph</title>
8+
<script>
9+
// Apply the theme class before first paint so the page doesn't flash
10+
// white when the OS prefers dark. Mirrors the logic in ThemeProvider;
11+
// its later useEffect is a no-op when the class is already correct.
12+
(function () {
13+
try {
14+
var prefersDark = window.matchMedia(
15+
"(prefers-color-scheme: dark)",
16+
).matches;
17+
document.documentElement.classList.add(
18+
prefersDark ? "dark" : "light",
19+
);
20+
} catch (_) {}
21+
})();
22+
</script>
823
</head>
924
<body>
1025
<div id="root"></div>

examples/integrations/langgraph-python-threads/apps/app/src/components/example-layout/index.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ export function ExampleLayout({ chatContent, appContent }: ExampleLayoutProps) {
3737
<div
3838
className={`max-h-full flex flex-col ${
3939
mode === "app"
40-
? "w-1/3 px-6 max-lg:hidden" // Hide on mobile in app mode
41-
: "flex-1 max-lg:px-4"
40+
? "w-1/3 max-lg:hidden" // Hide on mobile in app mode
41+
: "flex-1"
4242
}`}
4343
>
4444
<div className="shrink-0 pt-6 pl-6 pb-2 max-lg:pl-4 max-lg:pt-4">
@@ -48,7 +48,13 @@ export function ExampleLayout({ chatContent, appContent }: ExampleLayoutProps) {
4848
className="h-7 dark:invert"
4949
/>
5050
</div>
51-
<div className="flex-1 min-h-0 overflow-y-auto">{chatContent}</div>
51+
<div
52+
className={`flex-1 min-h-0 overflow-y-auto ${
53+
mode === "app" ? "px-6" : "max-lg:px-4"
54+
}`}
55+
>
56+
{chatContent}
57+
</div>
5258
</div>
5359

5460
{/* State Panel */}
Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,4 @@
11
"use client";
22

3-
import { useState, useEffect, type ComponentProps } from "react";
4-
import type ThreadsDrawerComponent from "./threads-drawer";
5-
6-
type Props = ComponentProps<typeof ThreadsDrawerComponent>;
7-
3+
export { default as ThreadsDrawer } from "./threads-drawer";
84
export type { ThreadsDrawerProps } from "./threads-drawer";
9-
10-
export function ThreadsDrawer(props: Props) {
11-
const [Component, setComponent] = useState<
12-
typeof ThreadsDrawerComponent | null
13-
>(null);
14-
15-
useEffect(() => {
16-
import("./threads-drawer").then((m) => setComponent(() => m.default));
17-
}, []);
18-
19-
if (!Component) return null;
20-
return <Component {...props} />;
21-
}

0 commit comments

Comments
 (0)