Skip to content

Commit 61d58d8

Browse files
authored
Mcp redesign 2 (#1361)
* initial setting window created * wip * mcp tool calling kind of works * not working ideally * tool call kind of fixed ui * now all stores tool call messages correctly * polished design * so broken * before experiment * minimum working version * middle of running tests... * ran tests * fixed ai comments * minor fixes * minor fixes again
1 parent abd9e62 commit 61d58d8

File tree

27 files changed

+1150
-282
lines changed

27 files changed

+1150
-282
lines changed

apps/desktop/src/components/editor-area/index.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,9 @@ export function useEnhanceMutation({
477477
? provider.languageModel("onboardingModel")
478478
: provider.languageModel("defaultModel");
479479

480+
console.log("model: ", model);
481+
console.log("provider: ", provider);
482+
480483
if (sessionId !== onboardingSessionId) {
481484
analyticsCommands.event({
482485
event: "normal_enhance_start",

apps/desktop/src/components/right-panel/components/chat/chat-messages-view.tsx

Lines changed: 86 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEffect, useRef } from "react";
1+
import { useEffect, useRef, useState } from "react";
22
import { ChatMessage } from "./chat-message";
33
import { Message } from "./types";
44

@@ -7,14 +7,93 @@ interface ChatMessagesViewProps {
77
sessionTitle?: string;
88
hasEnhancedNote?: boolean;
99
onApplyMarkdown?: (markdownContent: string) => void;
10+
isGenerating?: boolean;
11+
isStreamingText?: boolean;
1012
}
1113

12-
export function ChatMessagesView({ messages, sessionTitle, hasEnhancedNote, onApplyMarkdown }: ChatMessagesViewProps) {
14+
function ThinkingIndicator() {
15+
return (
16+
<>
17+
<style>
18+
{`
19+
@keyframes thinking-dots {
20+
0%, 20% { opacity: 0; }
21+
50% { opacity: 1; }
22+
100% { opacity: 0; }
23+
}
24+
.thinking-dot:nth-child(1) { animation-delay: 0s; }
25+
.thinking-dot:nth-child(2) { animation-delay: 0.2s; }
26+
.thinking-dot:nth-child(3) { animation-delay: 0.4s; }
27+
.thinking-dot {
28+
animation: thinking-dots 1.2s infinite;
29+
display: inline-block;
30+
}
31+
`}
32+
</style>
33+
<div style={{ color: "rgb(115 115 115)", fontSize: "0.875rem", padding: "4px 0" }}>
34+
<span>Thinking</span>
35+
<span className="thinking-dot">.</span>
36+
<span className="thinking-dot">.</span>
37+
<span className="thinking-dot">.</span>
38+
</div>
39+
</>
40+
);
41+
}
42+
43+
export function ChatMessagesView(
44+
{ messages, sessionTitle, hasEnhancedNote, onApplyMarkdown, isGenerating, isStreamingText }: ChatMessagesViewProps,
45+
) {
1346
const messagesEndRef = useRef<HTMLDivElement>(null);
47+
const [showThinking, setShowThinking] = useState(false);
48+
const thinkingTimeoutRef = useRef<NodeJS.Timeout | null>(null);
49+
50+
const shouldShowThinking = () => {
51+
if (!isGenerating) {
52+
return false;
53+
}
54+
55+
if (messages.length === 0) {
56+
return true;
57+
}
58+
59+
const lastMessage = messages[messages.length - 1];
60+
if (lastMessage.isUser) {
61+
return true;
62+
}
63+
64+
if (!lastMessage.isUser && !isStreamingText) {
65+
return true;
66+
}
67+
68+
return false;
69+
};
70+
71+
useEffect(() => {
72+
const shouldShow = shouldShowThinking();
73+
74+
if (thinkingTimeoutRef.current) {
75+
clearTimeout(thinkingTimeoutRef.current);
76+
thinkingTimeoutRef.current = null;
77+
}
78+
79+
if (shouldShow) {
80+
thinkingTimeoutRef.current = setTimeout(() => {
81+
setShowThinking(true);
82+
}, 200);
83+
} else {
84+
setShowThinking(false);
85+
}
86+
87+
return () => {
88+
if (thinkingTimeoutRef.current) {
89+
clearTimeout(thinkingTimeoutRef.current);
90+
}
91+
};
92+
}, [isGenerating, isStreamingText, messages]);
1493

1594
useEffect(() => {
1695
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
17-
}, [messages]);
96+
}, [messages, showThinking]);
1897

1998
return (
2099
<div className="flex-1 overflow-y-auto p-4 space-y-4 select-text">
@@ -27,6 +106,10 @@ export function ChatMessagesView({ messages, sessionTitle, hasEnhancedNote, onAp
27106
onApplyMarkdown={onApplyMarkdown}
28107
/>
29108
))}
109+
110+
{/* Thinking indicator with debounce - no flicker! */}
111+
{showThinking && <ThinkingIndicator />}
112+
30113
<div ref={messagesEndRef} />
31114
</div>
32115
);

apps/desktop/src/components/right-panel/components/chat/message-content.tsx

Lines changed: 80 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { commands as miscCommands } from "@hypr/plugin-misc";
22
import Renderer from "@hypr/tiptap/renderer";
3+
import { PencilRuler } from "lucide-react";
34
import { useEffect, useState } from "react";
45
import { MarkdownCard } from "./markdown-card";
56
import { Message } from "./types";
@@ -122,32 +123,87 @@ function MarkdownText({ content }: { content: string }) {
122123
}
123124

124125
export function MessageContent({ message, sessionTitle, hasEnhancedNote, onApplyMarkdown }: MessageContentProps) {
125-
if (message.content === "Generating...") {
126+
if (message.type === "tool-start") {
126127
return (
127-
<>
128-
<style>
129-
{`
130-
@keyframes thinking-dots {
131-
0%, 20% { opacity: 0; }
132-
50% { opacity: 1; }
133-
100% { opacity: 0; }
134-
}
135-
.thinking-dot:nth-child(1) { animation-delay: 0s; }
136-
.thinking-dot:nth-child(2) { animation-delay: 0.2s; }
137-
.thinking-dot:nth-child(3) { animation-delay: 0.4s; }
138-
.thinking-dot {
139-
animation: thinking-dots 1.2s infinite;
140-
display: inline-block;
141-
}
142-
`}
143-
</style>
144-
<div style={{ color: "rgb(115 115 115)", fontSize: "0.875rem", padding: "4px 0" }}>
145-
Thinking
146-
<span className="thinking-dot">.</span>
147-
<span className="thinking-dot">.</span>
148-
<span className="thinking-dot">.</span>
128+
<div
129+
style={{
130+
backgroundColor: "rgb(250 250 250)",
131+
border: "1px solid rgb(229 229 229)",
132+
borderRadius: "6px",
133+
padding: "12px 16px",
134+
}}
135+
>
136+
<div
137+
style={{
138+
color: "rgb(115 115 115)",
139+
fontSize: "0.875rem",
140+
display: "flex",
141+
alignItems: "center",
142+
gap: "8px",
143+
}}
144+
>
145+
<PencilRuler size={16} color="rgb(115 115 115)" />
146+
<span style={{ fontWeight: "400" }}>
147+
Called tool: {message.content}
148+
</span>
149149
</div>
150-
</>
150+
</div>
151+
);
152+
}
153+
154+
if (message.type === "tool-result") {
155+
return (
156+
<div
157+
style={{
158+
backgroundColor: "rgb(248 248 248)",
159+
border: "1px solid rgb(224 224 224)",
160+
borderRadius: "6px",
161+
padding: "12px 16px",
162+
}}
163+
>
164+
<div
165+
style={{
166+
color: "rgb(115 115 115)",
167+
fontSize: "0.875rem",
168+
display: "flex",
169+
alignItems: "center",
170+
gap: "8px",
171+
}}
172+
>
173+
<PencilRuler size={16} color="rgb(115 115 115)" />
174+
<span style={{ fontWeight: "400" }}>
175+
{message.content}
176+
</span>
177+
</div>
178+
</div>
179+
);
180+
}
181+
182+
if (message.type === "tool-error") {
183+
return (
184+
<div
185+
style={{
186+
backgroundColor: "rgb(252 252 252)",
187+
border: "1px solid rgb(229 229 229)",
188+
borderRadius: "6px",
189+
padding: "12px 16px",
190+
}}
191+
>
192+
<div
193+
style={{
194+
color: "rgb(115 115 115)",
195+
fontSize: "0.875rem",
196+
display: "flex",
197+
alignItems: "center",
198+
gap: "8px",
199+
}}
200+
>
201+
<PencilRuler size={16} color="rgb(115 115 115)" />
202+
<span style={{ fontWeight: "400" }}>
203+
Tool Error: {message.content}
204+
</span>
205+
</div>
206+
</div>
151207
);
152208
}
153209

apps/desktop/src/components/right-panel/components/chat/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export interface Message {
1010
parts?: MessagePart[];
1111
isUser: boolean;
1212
timestamp: Date;
13+
type: "text-delta" | "tool-start" | "tool-result" | "tool-error" | "generating";
1314
}
1415

1516
export type ChatSession = {

0 commit comments

Comments
 (0)