Skip to content

Commit 87f7242

Browse files
committed
feat: tweaks
1 parent 87be0fe commit 87f7242

File tree

5 files changed

+63
-63
lines changed

5 files changed

+63
-63
lines changed

apps/web/src/components/chat/ChatInput.tsx

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import {
22
AgentNotFoundError,
33
LEGACY_API_SERVER_URL,
4-
listTools,
54
MODELS,
65
useAgent,
76
useWriteFile,
@@ -46,7 +45,7 @@ ChatInput.UI = (
4645
const {
4746
agentRoot,
4847
chat: { stop, input, handleInputChange, handleSubmit, status },
49-
setStreamTools,
48+
setMentions,
5049
} = useChatContext();
5150
const [isUploading, setIsUploading] = useState(false);
5251
const [files, setFiles] = useState<FileList | undefined>(undefined);
@@ -71,35 +70,14 @@ ChatInput.UI = (
7170

7271
const writeFileMutation = useWriteFile();
7372

74-
const handleRichTextChange = async (
73+
const handleRichTextChange = (
7574
markdown: string,
76-
mentions?: MentionItem[],
75+
mentions: MentionItem[] | null,
7776
) => {
7877
handleInputChange(
7978
{ target: { value: markdown } } as React.ChangeEvent<HTMLTextAreaElement>,
8079
);
81-
82-
if (mentions?.length) {
83-
const tools = await mentions.reduce(async (promise, mention) => {
84-
const acc = await promise;
85-
const integrationTools = await listTools(mention.connection);
86-
if (integrationTools.tools.length > 0) {
87-
const integrationId = mention.id;
88-
acc[integrationId] = integrationTools.tools.map((tool) => tool.name);
89-
}
90-
return acc;
91-
}, Promise.resolve({} as Record<string, string[]>));
92-
93-
// Only update stream tools if we have actual tools
94-
if (Object.keys(tools).length > 0) {
95-
setStreamTools(tools);
96-
} else {
97-
setStreamTools(null);
98-
}
99-
} else {
100-
// If no mentions, remove the tools
101-
setStreamTools(null);
102-
}
80+
setMentions(mentions);
10381
};
10482

10583
// Auto-focus when loading state changes from true to false

apps/web/src/components/chat/RichText.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { NoNewLine } from "./extensions/NoNewLine.ts";
1414

1515
interface RichTextAreaProps {
1616
value: string;
17-
onChange: (markdown: string, mentions?: MentionItem[]) => void;
17+
onChange: (markdown: string, mentions: MentionItem[] | null) => void;
1818
onKeyDown?: (event: React.KeyboardEvent<HTMLDivElement>) => void;
1919
onKeyUp?: (event: React.KeyboardEvent<HTMLDivElement>) => void;
2020
onPaste?: (event: React.ClipboardEvent) => void;

apps/web/src/components/chat/components/MentionList.tsx

Lines changed: 46 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
1-
import { forwardRef, useEffect, useImperativeHandle, useState } from "react";
21
import type { Integration } from "@deco/sdk";
2+
import { Button } from "@deco/ui/components/button.tsx";
3+
import {
4+
forwardRef,
5+
useEffect,
6+
useImperativeHandle,
7+
useRef,
8+
useState,
9+
} from "react";
310
import { IntegrationIcon } from "../../integrations/list/common.tsx";
4-
511
export interface MentionListRef {
612
onKeyDown: (props: { event: KeyboardEvent }) => boolean;
713
}
@@ -14,6 +20,7 @@ interface MentionListProps {
1420
export const MentionList = forwardRef<MentionListRef, MentionListProps>(
1521
({ items, command }, ref) => {
1622
const [selectedIndex, setSelectedIndex] = useState(0);
23+
const itemRefs = useRef<(HTMLDivElement | null)[]>([]);
1724

1825
const selectItem = (index: number) => {
1926
const item = items[index];
@@ -26,6 +33,13 @@ export const MentionList = forwardRef<MentionListRef, MentionListProps>(
2633
setSelectedIndex(0);
2734
}, [items]);
2835

36+
useEffect(() => {
37+
const ref = itemRefs.current[selectedIndex];
38+
if (ref) {
39+
ref.scrollIntoView({ block: "nearest" });
40+
}
41+
}, [selectedIndex]);
42+
2943
useImperativeHandle(ref, () => ({
3044
onKeyDown: ({ event }) => {
3145
if (event.key === "ArrowUp") {
@@ -55,31 +69,38 @@ export const MentionList = forwardRef<MentionListRef, MentionListProps>(
5569
{items.length
5670
? (
5771
items.map((item, index) => (
58-
<button
59-
className={`flex items-center gap-2 w-full p-2 text-left rounded-md text-sm ${
60-
index === selectedIndex
61-
? "bg-slate-100"
62-
: "hover:bg-slate-50"
63-
}`}
72+
<div
6473
key={item.id}
65-
onClick={() => selectItem(index)}
74+
ref={(el) => {
75+
itemRefs.current[index] = el;
76+
}}
6677
>
67-
<div className="w-8 h-8 flex-shrink-0">
68-
<IntegrationIcon
69-
icon={item.icon}
70-
name={item.name}
71-
className="w-full h-full"
72-
/>
73-
</div>
74-
<div className="flex flex-col min-w-0">
75-
<span className="font-medium truncate">{item.name}</span>
76-
{item.description && (
77-
<span className="text-xs text-slate-500 truncate">
78-
{item.description}
79-
</span>
80-
)}
81-
</div>
82-
</button>
78+
<Button
79+
variant="ghost"
80+
className={`h-auto flex justify-start items-center gap-2 w-full py-2 px-2 text-left rounded-md text-sm ${
81+
index === selectedIndex
82+
? "bg-slate-100"
83+
: "hover:bg-slate-50"
84+
}`}
85+
onClick={() => selectItem(index)}
86+
>
87+
<div className="w-8 h-8 flex-shrink-0">
88+
<IntegrationIcon
89+
icon={item.icon}
90+
name={item.name}
91+
className="w-full h-full"
92+
/>
93+
</div>
94+
<div className="flex flex-col min-w-0">
95+
<span className="font-medium truncate">{item.name}</span>
96+
{item.description && (
97+
<span className="text-xs text-slate-500 truncate">
98+
{item.description}
99+
</span>
100+
)}
101+
</div>
102+
</Button>
103+
</div>
83104
))
84105
)
85106
: (

apps/web/src/components/chat/context.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
import { trackEvent } from "../../hooks/analytics.ts";
1919
import { getAgentOverrides } from "../../hooks/useAgentOverrides.ts";
2020
import { useSelectedModel } from "../../hooks/useSelectedModel.ts";
21+
import { MentionItem } from "./extensions/Mention.ts";
2122
import { IMAGE_REGEXP, openPreviewPanel } from "./utils/preview.ts";
2223

2324
const LAST_MESSAGES_COUNT = 10;
@@ -52,7 +53,7 @@ type IContext = {
5253
isAutoScrollEnabled: (e: HTMLDivElement | null) => boolean;
5354
retry: (context?: string[]) => void;
5455
select: (toolCallId: string, selectedValue: string) => Promise<void>;
55-
setStreamTools: (tools: Record<string, string[]> | null) => void;
56+
setMentions: (tools: MentionItem[] | null) => void;
5657
};
5758

5859
const Context = createContext<IContext | null>(null);
@@ -88,9 +89,7 @@ export function ChatProvider({
8889
const agentRoot = useAgentRoot(agentId);
8990
const selectedModel = useSelectedModel();
9091
const invalidateAll = useInvalidateAll();
91-
const [streamTools, setStreamTools] = useState<
92-
Record<string, string[]> | null
93-
>(null);
92+
const [mentions, setMentions] = useState<MentionItem[] | null>(null);
9493
const {
9594
addOptimisticThread,
9695
} = useAddOptimisticThread();
@@ -134,6 +133,8 @@ export function ChatProvider({
134133
const searchParams = new URLSearchParams(globalThis.location.search);
135134
const bypassOpenRouter = searchParams.get("openRouter") === "false";
136135

136+
const integrations = mentions?.map((tool) => tool.id);
137+
137138
const overrides = getAgentOverrides(agentId);
138139
return {
139140
args: [allMessages, {
@@ -142,11 +143,11 @@ export function ChatProvider({
142143
bypassOpenRouter,
143144
lastMessages: 0,
144145
sendReasoning: true,
146+
integrations,
145147
smoothStream: {
146148
delayInMs: 20,
147149
chunk: "word",
148150
},
149-
tools: streamTools,
150151
}],
151152
metadata: { threadId: threadId ?? agentId },
152153
};
@@ -266,7 +267,7 @@ export function ChatProvider({
266267
isAutoScrollEnabled,
267268
retry: handleRetry,
268269
select: handlePickerSelect,
269-
setStreamTools,
270+
setMentions,
270271
}}
271272
>
272273
{children}

apps/web/src/components/chat/tools/HandoffResponse.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ export function HandoffResponse({ agentId, threadId }: HandoffResponseProps) {
3030
<Collapsible open={isOpen} onOpenChange={setIsOpen}>
3131
<div className="rounded-xl border border-slate-200 overflow-hidden">
3232
{/* Agent Header */}
33-
<CollapsibleTrigger asChild>
34-
<button className="w-full bg-slate-50 p-4 flex items-center gap-3 border-b border-slate-200 hover:bg-slate-100 transition-colors">
33+
<CollapsibleTrigger className="cursor-pointer">
34+
<div className="w-full bg-slate-50 p-4 flex items-center gap-3 border-b border-slate-200 hover:bg-slate-100 transition-colors">
3535
<Avatar className="h-8 w-8">
3636
<AvatarImage
3737
src={agent?.avatar}
@@ -54,14 +54,14 @@ export function HandoffResponse({ agentId, threadId }: HandoffResponseProps) {
5454
isOpen && "rotate-90",
5555
)}
5656
/>
57-
</button>
57+
</div>
5858
</CollapsibleTrigger>
5959

6060
{/* Delegated Messages */}
6161
<CollapsibleContent>
62-
<div className="bg-white/50 backdrop-blur-sm p-4">
62+
<div className="bg-white/50 backdrop-blur-sm">
6363
<ChatProvider agentId={agentId} threadId={threadId}>
64-
<div className="space-y-4">
64+
<div className="space-y-4 max-h-96 overflow-y-auto p-4">
6565
<ChatMessages />
6666
</div>
6767
</ChatProvider>

0 commit comments

Comments
 (0)