feat: improve VS Code dev flow and stabilize sidebar/chat behavior (#754)

* fix: improve session sidebar tooltip and truncation behavior

- Keep new-draft tooltip anchored to its trigger button
- Fix minimal-mode worktree/group header text truncation
- Tune minimal-mode right padding to reduce early label clipping

* fix: render reasoning through markdown pipeline

- Use Streamdown rendering for reasoning in live chat mode
- Remove italic styling from reasoning text
- Render expanded reasoning content with MarkdownRenderer

* chore: remove legacy electron dependencies

- Removed unused Electron packages from root and UI manifests
- Deleted obsolete Electron context menu type declaration
- Regenerated lockfile after dependency cleanup

* fix: handle non-repository folders in git status API

- Prevent 500 errors when status is requested outside a valid Git repo
- Improve repository detection using `git rev-parse --git-dir`
- Reduce noisy server logs for expected non-repo status checks

* fix unloaded session chat layout flicker

* fix: reduce noisy TTS status polling

Cache and dedupe TTS status requests, and only check provider availability when the related voice features are enabled so disabled voice setups stay quiet.

* perf: throttle background PR git status refreshes

* fix: improve VS Code Explorer file drop mentions in chat

- Add Explorer context action to insert selected files as @mentions.
- Handle Explorer drag-and-drop to prefill @file mentions instead of attachments.
- Prevent duplicate plain-path text when dropping multiple files.

* fix: deduplicate recent sessions in VS Code sidebar

- Hide sessions from main list when already shown in recent
- Apply dedup only in VS Code runtime
- Keep session search behavior unchanged

* feat: add true HMR dev flow for VS Code extension

- Load VS Code webview from Vite dev server with React refresh preamble
- Add `vscode:dev` runner that starts watchers and opens Extension Development Host
- Update VS Code dev docs and scripts to use the new HMR startup flow

* feat: polish VS Code session sidebar and attachment UX

- Add resizable sessions sidebar in VS Code layout
- Tighten session list spacing and hover behavior in VS Code
- Remove bulk file/image attach success toasts while keeping error toasts
This commit is contained in:
Bohdan Triapitsyn
2026-03-23 23:51:55 +02:00
committed by GitHub
parent ea6d4c4d43
commit 1231fd773e
39 changed files with 1441 additions and 791 deletions
@@ -1186,16 +1186,16 @@ const AssistantMessageBody: React.FC<Omit<MessageBodyProps, 'isUser'>> = ({
/>
);
} else {
const partText = (part as { text?: string }).text;
if (partText && partText.trim().length > 0) {
rendered.push(
<FadeInOnReveal key={`reasoning-${messageId}-${i}`}>
<div className="my-0.5 text-sm text-muted-foreground/60 italic leading-relaxed whitespace-pre-wrap">
{partText}
</div>
</FadeInOnReveal>
);
}
rendered.push(
<AssistantTextPart
key={`reasoning-${messageId}-${i}`}
part={part}
messageId={messageId}
streamPhase={streamPhase}
chatRenderMode={chatRenderMode}
onContentChange={onContentChange}
/>
);
}
}
i++;
@@ -7,6 +7,7 @@ import type { ContentChangeReason } from '@/hooks/useChatScrollManager';
import { ScrollableOverlay } from '@/components/ui/ScrollableOverlay';
import { useUIStore } from '@/stores/useUIStore';
import { useDurationTickerNow } from './useDurationTicker';
import { MarkdownRenderer } from '../../MarkdownRenderer';
type PartWithText = Part & { text?: string; content?: string; time?: { start?: number; end?: number } };
@@ -78,6 +79,7 @@ type ReasoningTimelineBlockProps = {
blockId: string;
time?: { start?: number; end?: number };
showDuration?: boolean;
isStreaming?: boolean;
};
export const ReasoningTimelineBlock: React.FC<ReasoningTimelineBlockProps> = ({
@@ -87,6 +89,7 @@ export const ReasoningTimelineBlock: React.FC<ReasoningTimelineBlockProps> = ({
blockId,
time,
showDuration = true,
isStreaming = false,
}) => {
const [isExpanded, setIsExpanded] = React.useState(false);
@@ -140,7 +143,7 @@ export const ReasoningTimelineBlock: React.FC<ReasoningTimelineBlockProps> = ({
{(summary || (showDuration && typeof timeStart === 'number')) ? (
<div className="flex items-center gap-1 flex-1 min-w-0 typography-meta text-muted-foreground/70">
{summary ? <span className="flex-1 min-w-0 truncate italic">{summary}</span> : null}
{summary ? <span className="flex-1 min-w-0 truncate">{summary}</span> : null}
{showDuration && typeof timeStart === 'number' ? (
<span className="relative flex-shrink-0 tabular-nums text-right">
<span className="text-muted-foreground/80 transition-opacity duration-150">
@@ -163,11 +166,17 @@ export const ReasoningTimelineBlock: React.FC<ReasoningTimelineBlockProps> = ({
)}
>
<ScrollableOverlay
as="blockquote"
as="div"
outerClassName="max-h-80"
className="whitespace-pre-wrap break-words typography-meta italic text-muted-foreground/70 p-0"
className="p-0"
>
{text}
<MarkdownRenderer
content={text}
messageId={blockId}
isAnimated={false}
isStreaming={isStreaming}
variant="reasoning"
/>
</ScrollableOverlay>
</div>
)}
@@ -191,6 +200,7 @@ const ReasoningPart: React.FC<ReasoningPartProps> = ({
const rawText = partWithText.text || partWithText.content || '';
const textContent = React.useMemo(() => cleanReasoningText(rawText), [rawText]);
const time = partWithText.time;
const isStreaming = chatRenderMode === 'live' && typeof time?.end !== 'number';
// Show reasoning even if time.end isn't set yet (during streaming)
// Only hide if there's no text content
@@ -206,6 +216,7 @@ const ReasoningPart: React.FC<ReasoningPartProps> = ({
blockId={part.id || `${messageId}-reasoning`}
time={time}
showDuration={chatRenderMode !== 'sorted'}
isStreaming={isStreaming}
/>
);
};