Skip to content

Commit f4a3dda

Browse files
authored
feat: update thread component, sync registry, and fix LangGraph API (#9)
1 parent d82db44 commit f4a3dda

File tree

8 files changed

+1123
-724
lines changed

8 files changed

+1123
-724
lines changed

app/globals.css

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -128,31 +128,4 @@
128128
body {
129129
@apply bg-background text-foreground;
130130
}
131-
132-
/* Scrollbar styling */
133-
::-webkit-scrollbar {
134-
width: 12px;
135-
}
136-
137-
::-webkit-scrollbar-thumb {
138-
@apply bg-muted-foreground/20;
139-
border-radius: 6px;
140-
border: 2px solid transparent;
141-
background-clip: padding-box;
142-
}
143-
144-
::-webkit-scrollbar-thumb:hover {
145-
@apply bg-muted-foreground/30;
146-
}
147-
148-
/* Firefox only scrollbar styling */
149-
@-moz-document url-prefix() {
150-
* {
151-
scrollbar-color: oklch(0.552 0.016 285.938 / 25%) transparent;
152-
}
153-
154-
.dark * {
155-
scrollbar-color: oklch(0.705 0.015 286.067 / 25%) transparent;
156-
}
157-
}
158131
}

components/MyAssistant.tsx

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

3-
import { useRef } from "react";
43
import { AssistantRuntimeProvider } from "@assistant-ui/react";
54
import { useLangGraphRuntime } from "@assistant-ui/react-langgraph";
65

76
import { createThread, getThreadState, sendMessage } from "@/lib/chatApi";
87
import { Thread } from "@/components/assistant-ui/thread";
98

109
export function MyAssistant() {
11-
const threadIdRef = useRef<string | undefined>(undefined);
1210
const runtime = useLangGraphRuntime({
13-
threadId: threadIdRef.current,
14-
stream: async (messages, { command }) => {
15-
if (!threadIdRef.current) {
16-
const { thread_id } = await createThread();
17-
threadIdRef.current = thread_id;
18-
}
19-
const threadId = threadIdRef.current;
20-
return sendMessage({
21-
threadId,
11+
stream: async function* (messages, { initialize, command }) {
12+
const { externalId } = await initialize();
13+
if (!externalId) throw new Error("Thread not found");
14+
15+
const generator = await sendMessage({
16+
threadId: externalId,
2217
messages,
2318
command,
2419
});
20+
21+
yield* generator;
2522
},
26-
onSwitchToNewThread: async () => {
23+
create: async () => {
2724
const { thread_id } = await createThread();
28-
threadIdRef.current = thread_id;
25+
return { externalId: thread_id };
2926
},
30-
onSwitchToThread: async (threadId) => {
31-
const state = await getThreadState(threadId);
32-
threadIdRef.current = threadId;
33-
return { messages: state.values.messages };
27+
load: async (externalId) => {
28+
const state = await getThreadState(externalId);
29+
return {
30+
messages: state.values.messages,
31+
};
3432
},
3533
});
3634

components/assistant-ui/thread.tsx

Lines changed: 51 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,3 @@
1-
import {
2-
ActionBarPrimitive,
3-
BranchPickerPrimitive,
4-
ComposerPrimitive,
5-
ErrorPrimitive,
6-
MessagePrimitive,
7-
ThreadPrimitive,
8-
} from "@assistant-ui/react";
91
import {
102
ArrowDownIcon,
113
ArrowUpIcon,
@@ -17,20 +9,31 @@ import {
179
RefreshCwIcon,
1810
Square,
1911
} from "lucide-react";
12+
13+
import {
14+
ActionBarPrimitive,
15+
BranchPickerPrimitive,
16+
ComposerPrimitive,
17+
ErrorPrimitive,
18+
MessagePrimitive,
19+
ThreadPrimitive,
20+
} from "@assistant-ui/react";
21+
2022
import type { FC } from "react";
23+
import { LazyMotion, MotionConfig, domAnimation } from "motion/react";
24+
import * as m from "motion/react-m";
2125

26+
import { Button } from "@/components/ui/button";
27+
import { MarkdownText } from "@/components/assistant-ui/markdown-text";
28+
import { ToolFallback } from "@/components/assistant-ui/tool-fallback";
29+
import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
2230
import {
2331
ComposerAddAttachment,
2432
ComposerAttachments,
2533
UserMessageAttachments,
2634
} from "@/components/assistant-ui/attachment";
27-
import { MarkdownText } from "@/components/assistant-ui/markdown-text";
28-
import { ToolFallback } from "@/components/assistant-ui/tool-fallback";
29-
import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
30-
import { Button } from "@/components/ui/button";
35+
3136
import { cn } from "@/lib/utils";
32-
import { LazyMotion, MotionConfig, domAnimation } from "motion/react";
33-
import * as m from "motion/react-m";
3437

3538
export const Thread: FC = () => {
3639
return (
@@ -43,7 +46,9 @@ export const Thread: FC = () => {
4346
}}
4447
>
4548
<ThreadPrimitive.Viewport className="aui-thread-viewport relative flex flex-1 flex-col overflow-x-auto overflow-y-scroll px-4">
46-
<ThreadWelcome />
49+
<ThreadPrimitive.If empty>
50+
<ThreadWelcome />
51+
</ThreadPrimitive.If>
4752

4853
<ThreadPrimitive.Messages
4954
components={{
@@ -52,9 +57,11 @@ export const Thread: FC = () => {
5257
AssistantMessage,
5358
}}
5459
/>
60+
5561
<ThreadPrimitive.If empty={false}>
5662
<div className="aui-thread-viewport-spacer min-h-8 grow" />
5763
</ThreadPrimitive.If>
64+
5865
<Composer />
5966
</ThreadPrimitive.Viewport>
6067
</ThreadPrimitive.Root>
@@ -79,37 +86,36 @@ const ThreadScrollToBottom: FC = () => {
7986

8087
const ThreadWelcome: FC = () => {
8188
return (
82-
<ThreadPrimitive.Empty>
83-
<div className="aui-thread-welcome-root mx-auto my-auto flex w-full max-w-[var(--thread-max-width)] flex-grow flex-col">
84-
<div className="aui-thread-welcome-center flex w-full flex-grow flex-col items-center justify-center">
85-
<div className="aui-thread-welcome-message flex size-full flex-col justify-center px-8">
86-
<m.div
87-
initial={{ opacity: 0, y: 10 }}
88-
animate={{ opacity: 1, y: 0 }}
89-
exit={{ opacity: 0, y: 10 }}
90-
className="aui-thread-welcome-message-motion-1 text-2xl font-semibold"
91-
>
92-
Hello there!
93-
</m.div>
94-
<m.div
95-
initial={{ opacity: 0, y: 10 }}
96-
animate={{ opacity: 1, y: 0 }}
97-
exit={{ opacity: 0, y: 10 }}
98-
transition={{ delay: 0.1 }}
99-
className="aui-thread-welcome-message-motion-2 text-2xl text-muted-foreground/65"
100-
>
101-
How can I help you today?
102-
</m.div>
103-
</div>
89+
<div className="aui-thread-welcome-root mx-auto my-auto flex w-full max-w-[var(--thread-max-width)] flex-grow flex-col">
90+
<div className="aui-thread-welcome-center flex w-full flex-grow flex-col items-center justify-center">
91+
<div className="aui-thread-welcome-message flex size-full flex-col justify-center px-8">
92+
<m.div
93+
initial={{ opacity: 0, y: 10 }}
94+
animate={{ opacity: 1, y: 0 }}
95+
exit={{ opacity: 0, y: 10 }}
96+
className="aui-thread-welcome-message-motion-1 text-2xl font-semibold"
97+
>
98+
Hello there!
99+
</m.div>
100+
<m.div
101+
initial={{ opacity: 0, y: 10 }}
102+
animate={{ opacity: 1, y: 0 }}
103+
exit={{ opacity: 0, y: 10 }}
104+
transition={{ delay: 0.1 }}
105+
className="aui-thread-welcome-message-motion-2 text-2xl text-muted-foreground/65"
106+
>
107+
How can I help you today?
108+
</m.div>
104109
</div>
105110
</div>
106-
</ThreadPrimitive.Empty>
111+
<ThreadSuggestions />
112+
</div>
107113
);
108114
};
109115

110-
const ThreadWelcomeSuggestions: FC = () => {
116+
const ThreadSuggestions: FC = () => {
111117
return (
112-
<div className="aui-thread-welcome-suggestions grid w-full gap-2 @md:grid-cols-2">
118+
<div className="aui-thread-welcome-suggestions grid w-full gap-2 pb-4 @md:grid-cols-2">
113119
{[
114120
{
115121
title: "What's the weather",
@@ -142,8 +148,7 @@ const ThreadWelcomeSuggestions: FC = () => {
142148
>
143149
<ThreadPrimitive.Suggestion
144150
prompt={suggestedAction.action}
145-
method="replace"
146-
autoSend
151+
send
147152
asChild
148153
>
149154
<Button
@@ -169,14 +174,11 @@ const Composer: FC = () => {
169174
return (
170175
<div className="aui-composer-wrapper sticky bottom-0 mx-auto flex w-full max-w-[var(--thread-max-width)] flex-col gap-4 overflow-visible rounded-t-3xl bg-background pb-4 md:pb-6">
171176
<ThreadScrollToBottom />
172-
<ThreadPrimitive.Empty>
173-
<ThreadWelcomeSuggestions />
174-
</ThreadPrimitive.Empty>
175-
<ComposerPrimitive.Root className="aui-composer-root relative flex w-full flex-col rounded-3xl border border-border bg-muted px-1 pt-2 shadow-[0_9px_9px_0px_rgba(0,0,0,0.01),0_2px_5px_0px_rgba(0,0,0,0.06)] dark:border-muted-foreground/15">
177+
<ComposerPrimitive.Root className="aui-composer-root group/input-group relative flex w-full flex-col rounded-3xl border border-input bg-background px-1 pt-2 shadow-xs transition-[color,box-shadow] outline-none has-[textarea:focus-visible]:border-ring has-[textarea:focus-visible]:ring-[3px] has-[textarea:focus-visible]:ring-ring/50 dark:bg-background">
176178
<ComposerAttachments />
177179
<ComposerPrimitive.Input
178180
placeholder="Send a message..."
179-
className="aui-composer-input mb-1 max-h-32 min-h-16 w-full resize-none bg-transparent px-3.5 pt-1.5 pb-3 text-base outline-none placeholder:text-muted-foreground focus:outline-primary"
181+
className="aui-composer-input mb-1 max-h-32 min-h-16 w-full resize-none bg-transparent px-3.5 pt-1.5 pb-3 text-base outline-none placeholder:text-muted-foreground focus-visible:ring-0"
180182
rows={1}
181183
autoFocus
182184
aria-label="Message input"
@@ -239,7 +241,7 @@ const AssistantMessage: FC = () => {
239241
return (
240242
<MessagePrimitive.Root asChild>
241243
<div
242-
className="aui-assistant-message-root relative mx-auto w-full max-w-[var(--thread-max-width)] animate-in py-4 duration-200 fade-in slide-in-from-bottom-1 last:mb-24"
244+
className="aui-assistant-message-root relative mx-auto w-full max-w-[var(--thread-max-width)] animate-in py-4 duration-150 ease-out fade-in slide-in-from-bottom-1 last:mb-24"
243245
data-role="assistant"
244246
>
245247
<div className="aui-assistant-message-content mx-2 leading-7 break-words text-foreground">
@@ -292,7 +294,7 @@ const UserMessage: FC = () => {
292294
return (
293295
<MessagePrimitive.Root asChild>
294296
<div
295-
className="aui-user-message-root mx-auto grid w-full max-w-[var(--thread-max-width)] animate-in auto-rows-auto grid-cols-[minmax(72px,1fr)_auto] gap-y-2 px-2 py-4 duration-200 fade-in slide-in-from-bottom-1 first:mt-3 last:mb-5 [&:where(>*)]:col-start-2"
297+
className="aui-user-message-root mx-auto grid w-full max-w-[var(--thread-max-width)] animate-in auto-rows-auto grid-cols-[minmax(72px,1fr)_auto] gap-y-2 px-2 py-4 duration-150 ease-out fade-in slide-in-from-bottom-1 first:mt-3 last:mb-5 [&:where(>*)]:col-start-2"
296298
data-role="user"
297299
>
298300
<UserMessageAttachments />

components/ui/button.tsx

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,21 @@
1-
import * as React from "react"
2-
import { Slot } from "@radix-ui/react-slot"
3-
import { cva, type VariantProps } from "class-variance-authority"
1+
import * as React from "react";
2+
import { Slot } from "@radix-ui/react-slot";
3+
import { cva, type VariantProps } from "class-variance-authority";
44

5-
import { cn } from "@/lib/utils"
5+
import { cn } from "@/lib/utils";
66

77
const buttonVariants = cva(
88
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
99
{
1010
variants: {
1111
variant: {
12-
default:
13-
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
12+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
1413
destructive:
15-
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
14+
"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
1615
outline:
1716
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
1817
secondary:
19-
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
18+
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
2019
ghost:
2120
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
2221
link: "text-primary underline-offset-4 hover:underline",
@@ -26,6 +25,8 @@ const buttonVariants = cva(
2625
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
2726
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
2827
icon: "size-9",
28+
"icon-sm": "size-8",
29+
"icon-lg": "size-10",
2930
},
3031
},
3132
defaultVariants: {
@@ -43,17 +44,17 @@ function Button({
4344
...props
4445
}: React.ComponentProps<"button"> &
4546
VariantProps<typeof buttonVariants> & {
46-
asChild?: boolean
47+
asChild?: boolean;
4748
}) {
48-
const Comp = asChild ? Slot : "button"
49+
const Comp = asChild ? Slot : "button";
4950

5051
return (
5152
<Comp
5253
data-slot="button"
5354
className={cn(buttonVariants({ variant, size, className }))}
5455
{...props}
5556
/>
56-
)
57+
);
5758
}
5859

59-
export { Button, buttonVariants }
60+
export { Button, buttonVariants };

components/ui/tooltip.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,13 @@ function TooltipContent({
4646
data-slot="tooltip-content"
4747
sideOffset={sideOffset}
4848
className={cn(
49-
"z-50 w-fit origin-(--radix-tooltip-content-transform-origin) animate-in rounded-md bg-primary px-3 py-1.5 text-xs text-balance text-primary-foreground fade-in-0 zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
49+
"z-50 w-fit origin-(--radix-tooltip-content-transform-origin) animate-in rounded-md bg-foreground px-3 py-1.5 text-xs text-balance text-background fade-in-0 zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
5050
className,
5151
)}
5252
{...props}
5353
>
5454
{children}
55-
<TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-primary fill-primary" />
55+
<TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground" />
5656
</TooltipPrimitive.Content>
5757
</TooltipPrimitive.Portal>
5858
);

package.json

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,26 @@
1717
"tailwindStylesheet": "app/globals.css"
1818
},
1919
"dependencies": {
20-
"@assistant-ui/react": "^0.11.15",
21-
"@assistant-ui/react-langgraph": "^0.6.9",
22-
"@assistant-ui/react-markdown": "^0.11.0",
23-
"@langchain/langgraph-sdk": "^0.1.6",
24-
"@radix-ui/react-avatar": "^1.1.10",
20+
"@assistant-ui/react": "^0.11.39",
21+
"@assistant-ui/react-langgraph": "^0.7.7",
22+
"@assistant-ui/react-markdown": "^0.11.4",
23+
"@langchain/langgraph-sdk": "^1.0.0",
24+
"@radix-ui/react-avatar": "^1.1.11",
25+
"@radix-ui/react-collapsible": "^1.1.12",
2526
"@radix-ui/react-dialog": "^1.1.15",
26-
"@radix-ui/react-separator": "^1.1.7",
27-
"@radix-ui/react-slot": "^1.2.3",
27+
"@radix-ui/react-separator": "^1.1.8",
28+
"@radix-ui/react-slot": "^1.2.4",
2829
"@radix-ui/react-tooltip": "^1.2.8",
2930
"class-variance-authority": "^0.7.1",
3031
"clsx": "^2.1.1",
31-
"lucide-react": "^0.544.0",
32-
"motion": "^12.23.19",
33-
"next": "15.5.4",
34-
"react": "^19.1.1",
35-
"react-dom": "^19.1.1",
32+
"lucide-react": "^0.554.0",
33+
"motion": "^12.23.24",
34+
"next": "16.0.3",
35+
"react": "^19.2.0",
36+
"react-dom": "^19.2.0",
3637
"remark-gfm": "^4.0.1",
37-
"tailwind-merge": "^3.3.1",
38-
"tw-animate-css": "^1.3.8",
38+
"tailwind-merge": "^3.4.0",
39+
"tw-animate-css": "^1.4.0",
3940
"zustand": "^5.0.8"
4041
},
4142
"devDependencies": {
@@ -45,9 +46,9 @@
4546
"@types/react": "^19",
4647
"@types/react-dom": "^19",
4748
"eslint": "^9",
48-
"eslint-config-next": "15.5.4",
49+
"eslint-config-next": "16.0.3",
4950
"prettier": "^3.6.2",
50-
"prettier-plugin-tailwindcss": "^0.6.14",
51+
"prettier-plugin-tailwindcss": "^0.7.1",
5152
"tailwindcss": "^4",
5253
"typescript": "^5"
5354
}

0 commit comments

Comments
 (0)