Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions apps/desktop/src/components/editor-area/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,9 @@ export function useEnhanceMutation({
? provider.languageModel("onboardingModel")
: provider.languageModel("defaultModel");

console.log("model: ", model);
console.log("provider: ", provider);

if (sessionId !== onboardingSessionId) {
analyticsCommands.event({
event: "normal_enhance_start",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useRef } from "react";
import { useEffect, useRef, useState } from "react";
import { ChatMessage } from "./chat-message";
import { Message } from "./types";

Expand All @@ -7,14 +7,93 @@ interface ChatMessagesViewProps {
sessionTitle?: string;
hasEnhancedNote?: boolean;
onApplyMarkdown?: (markdownContent: string) => void;
isGenerating?: boolean;
isStreamingText?: boolean;
}

export function ChatMessagesView({ messages, sessionTitle, hasEnhancedNote, onApplyMarkdown }: ChatMessagesViewProps) {
function ThinkingIndicator() {
return (
<>
<style>
{`
@keyframes thinking-dots {
0%, 20% { opacity: 0; }
50% { opacity: 1; }
100% { opacity: 0; }
}
.thinking-dot:nth-child(1) { animation-delay: 0s; }
.thinking-dot:nth-child(2) { animation-delay: 0.2s; }
.thinking-dot:nth-child(3) { animation-delay: 0.4s; }
.thinking-dot {
animation: thinking-dots 1.2s infinite;
display: inline-block;
}
`}
</style>
<div style={{ color: "rgb(115 115 115)", fontSize: "0.875rem", padding: "4px 0" }}>
<span>Thinking</span>
<span className="thinking-dot">.</span>
<span className="thinking-dot">.</span>
<span className="thinking-dot">.</span>
</div>
</>
);
}

export function ChatMessagesView(
{ messages, sessionTitle, hasEnhancedNote, onApplyMarkdown, isGenerating, isStreamingText }: ChatMessagesViewProps,
) {
const messagesEndRef = useRef<HTMLDivElement>(null);
const [showThinking, setShowThinking] = useState(false);
const thinkingTimeoutRef = useRef<NodeJS.Timeout | null>(null);

Comment on lines +48 to +49
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix type for thinkingTimeoutRef to avoid TS/browser typing mismatch

NodeJS.Timeout is incorrect in browser React apps and may cause typing errors. Use ReturnType.

Apply this diff:

-  const thinkingTimeoutRef = useRef<NodeJS.Timeout | null>(null);
+  const thinkingTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const thinkingTimeoutRef = useRef<NodeJS.Timeout | null>(null);
const thinkingTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
🤖 Prompt for AI Agents
In
apps/desktop/src/components/right-panel/components/chat/chat-messages-view.tsx
around lines 48-49, the thinkingTimeoutRef is typed as NodeJS.Timeout | null
which mismatches browser setTimeout types; change its type to ReturnType<typeof
setTimeout> | null (i.e. use ReturnType<typeof setTimeout> instead of
NodeJS.Timeout) so the ref matches the DOM/browser timer return type and avoid
TS errors.

const shouldShowThinking = () => {
if (!isGenerating) {
return false;
}

if (messages.length === 0) {
return true;
}

const lastMessage = messages[messages.length - 1];
if (lastMessage.isUser) {
return true;
}

if (!lastMessage.isUser && !isStreamingText) {
return true;
}

return false;
};

useEffect(() => {
const shouldShow = shouldShowThinking();

if (thinkingTimeoutRef.current) {
clearTimeout(thinkingTimeoutRef.current);
thinkingTimeoutRef.current = null;
}

if (shouldShow) {
thinkingTimeoutRef.current = setTimeout(() => {
setShowThinking(true);
}, 200);
} else {
setShowThinking(false);
}

return () => {
if (thinkingTimeoutRef.current) {
clearTimeout(thinkingTimeoutRef.current);
}
};
}, [isGenerating, isStreamingText, messages]);

useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
}, [messages]);
}, [messages, showThinking]);

return (
<div className="flex-1 overflow-y-auto p-4 space-y-4 select-text">
Expand All @@ -27,6 +106,10 @@ export function ChatMessagesView({ messages, sessionTitle, hasEnhancedNote, onAp
onApplyMarkdown={onApplyMarkdown}
/>
))}

{/* Thinking indicator with debounce - no flicker! */}
{showThinking && <ThinkingIndicator />}

<div ref={messagesEndRef} />
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { commands as miscCommands } from "@hypr/plugin-misc";
import Renderer from "@hypr/tiptap/renderer";
import { PencilRuler } from "lucide-react";
import { useEffect, useState } from "react";
import { MarkdownCard } from "./markdown-card";
import { Message } from "./types";
Expand Down Expand Up @@ -122,32 +123,87 @@ function MarkdownText({ content }: { content: string }) {
}

export function MessageContent({ message, sessionTitle, hasEnhancedNote, onApplyMarkdown }: MessageContentProps) {
if (message.content === "Generating...") {
if (message.type === "tool-start") {
return (
<>
<style>
{`
@keyframes thinking-dots {
0%, 20% { opacity: 0; }
50% { opacity: 1; }
100% { opacity: 0; }
}
.thinking-dot:nth-child(1) { animation-delay: 0s; }
.thinking-dot:nth-child(2) { animation-delay: 0.2s; }
.thinking-dot:nth-child(3) { animation-delay: 0.4s; }
.thinking-dot {
animation: thinking-dots 1.2s infinite;
display: inline-block;
}
`}
</style>
<div style={{ color: "rgb(115 115 115)", fontSize: "0.875rem", padding: "4px 0" }}>
Thinking
<span className="thinking-dot">.</span>
<span className="thinking-dot">.</span>
<span className="thinking-dot">.</span>
<div
style={{
backgroundColor: "rgb(250 250 250)",
border: "1px solid rgb(229 229 229)",
borderRadius: "6px",
padding: "12px 16px",
}}
>
<div
style={{
color: "rgb(115 115 115)",
fontSize: "0.875rem",
display: "flex",
alignItems: "center",
gap: "8px",
}}
>
<PencilRuler size={16} color="rgb(115 115 115)" />
<span style={{ fontWeight: "400" }}>
Called tool: {message.content}
</span>
</div>
</>
</div>
);
}

if (message.type === "tool-result") {
return (
<div
style={{
backgroundColor: "rgb(248 248 248)",
border: "1px solid rgb(224 224 224)",
borderRadius: "6px",
padding: "12px 16px",
}}
>
<div
style={{
color: "rgb(115 115 115)",
fontSize: "0.875rem",
display: "flex",
alignItems: "center",
gap: "8px",
}}
>
<PencilRuler size={16} color="rgb(115 115 115)" />
<span style={{ fontWeight: "400" }}>
{message.content}
</span>
</div>
</div>
);
}

if (message.type === "tool-error") {
return (
<div
style={{
backgroundColor: "rgb(252 252 252)",
border: "1px solid rgb(229 229 229)",
borderRadius: "6px",
padding: "12px 16px",
}}
>
<div
style={{
color: "rgb(115 115 115)",
fontSize: "0.875rem",
display: "flex",
alignItems: "center",
gap: "8px",
}}
>
<PencilRuler size={16} color="rgb(115 115 115)" />
<span style={{ fontWeight: "400" }}>
Tool Error: {message.content}
</span>
</div>
</div>
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export interface Message {
parts?: MessagePart[];
isUser: boolean;
timestamp: Date;
type: "text-delta" | "tool-start" | "tool-result" | "tool-error" | "generating";
}

export type ChatSession = {
Expand Down
Loading
Loading