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
@@ -43,6 +43,8 @@ const MOBILE_WIDTH_THRESHOLD = 550;
const EXPANDED_LAYOUT_THRESHOLD = 1400;
// Sessions sidebar width in expanded layout
const SESSIONS_SIDEBAR_WIDTH = 280;
const SESSIONS_SIDEBAR_MIN_WIDTH = Math.round(SESSIONS_SIDEBAR_WIDTH * 0.7);
const SESSIONS_SIDEBAR_MAX_WIDTH = 520;
type VSCodeView = 'sessions' | 'chat' | 'settings';
@@ -80,7 +82,12 @@ export const VSCodeLayout: React.FC = () => {
const [currentView, setCurrentView] = React.useState<VSCodeView>(() => (bootDraftOpen ? 'chat' : 'sessions'));
const [containerWidth, setContainerWidth] = React.useState<number>(0);
const [expandedSidebarWidth, setExpandedSidebarWidth] = React.useState<number>(SESSIONS_SIDEBAR_WIDTH);
const [isResizingExpandedSidebar, setIsResizingExpandedSidebar] = React.useState(false);
const containerRef = React.useRef<HTMLDivElement>(null);
const expandedSidebarResizeStartXRef = React.useRef(0);
const expandedSidebarResizeStartWidthRef = React.useRef(SESSIONS_SIDEBAR_WIDTH);
const expandedSidebarResizePointerIdRef = React.useRef<number | null>(null);
const currentSessionId = useSessionStore((state) => state.currentSessionId);
const sessions = useSessionStore((state) => state.sessions);
@@ -357,6 +364,45 @@ export const VSCodeLayout: React.FC = () => {
const usesMobileLayout = containerWidth > 0 && containerWidth < MOBILE_WIDTH_THRESHOLD;
const usesExpandedLayout = containerWidth >= EXPANDED_LAYOUT_THRESHOLD;
const clampExpandedSidebarWidth = React.useCallback((value: number) => {
return Math.min(SESSIONS_SIDEBAR_MAX_WIDTH, Math.max(SESSIONS_SIDEBAR_MIN_WIDTH, value));
}, []);
const handleExpandedSidebarResizeStart = React.useCallback((event: React.PointerEvent<HTMLDivElement>) => {
try {
event.currentTarget.setPointerCapture(event.pointerId);
} catch {
// ignore
}
expandedSidebarResizePointerIdRef.current = event.pointerId;
expandedSidebarResizeStartXRef.current = event.clientX;
expandedSidebarResizeStartWidthRef.current = expandedSidebarWidth;
setIsResizingExpandedSidebar(true);
event.preventDefault();
}, [expandedSidebarWidth]);
const handleExpandedSidebarResizeMove = React.useCallback((event: React.PointerEvent<HTMLDivElement>) => {
if (expandedSidebarResizePointerIdRef.current !== event.pointerId) {
return;
}
const delta = event.clientX - expandedSidebarResizeStartXRef.current;
const nextWidth = clampExpandedSidebarWidth(expandedSidebarResizeStartWidthRef.current + delta);
setExpandedSidebarWidth((current) => (current === nextWidth ? current : nextWidth));
}, [clampExpandedSidebarWidth]);
const handleExpandedSidebarResizeEnd = React.useCallback((event: React.PointerEvent<HTMLDivElement>) => {
if (expandedSidebarResizePointerIdRef.current !== event.pointerId) {
return;
}
try {
event.currentTarget.releasePointerCapture(event.pointerId);
} catch {
// ignore
}
expandedSidebarResizePointerIdRef.current = null;
setIsResizingExpandedSidebar(false);
}, []);
// In expanded layout, always show chat (with sidebar alongside)
// Navigate to chat automatically when expanded layout is enabled and we're on sessions view
React.useEffect(() => {
@@ -392,8 +438,8 @@ export const VSCodeLayout: React.FC = () => {
<div className="flex h-full">
{/* Sessions sidebar */}
<div
className="h-full border-r border-border overflow-hidden flex-shrink-0"
style={{ width: SESSIONS_SIDEBAR_WIDTH }}
className={cn('relative h-full border-r border-border overflow-hidden flex-shrink-0', isResizingExpandedSidebar && 'select-none')}
style={{ width: expandedSidebarWidth, minWidth: expandedSidebarWidth, maxWidth: expandedSidebarWidth }}
>
<SessionSidebar
mobileVariant
@@ -401,6 +447,19 @@ export const VSCodeLayout: React.FC = () => {
hideDirectoryControls
showOnlyMainWorkspace
/>
<div
className={cn(
'absolute right-0 top-0 z-20 h-full w-[3px] cursor-col-resize transition-colors hover:bg-[var(--interactive-border)]/80',
isResizingExpandedSidebar && 'bg-[var(--interactive-border)]'
)}
onPointerDown={handleExpandedSidebarResizeStart}
onPointerMove={handleExpandedSidebarResizeMove}
onPointerUp={handleExpandedSidebarResizeEnd}
onPointerCancel={handleExpandedSidebarResizeEnd}
role="separator"
aria-orientation="vertical"
aria-label="Resize sessions sidebar"
/>
</div>
{/* Chat content */}
<div className="flex-1 flex flex-col min-w-0">
@@ -533,7 +592,7 @@ const VSCodeHeader: React.FC<VSCodeHeaderProps> = ({ title, showBack, onBack, on
}, [setQuotaDisplayMode]);
return (
<div className="flex items-center gap-1.5 pl-1 pr-2 py-1 border-b border-border bg-background shrink-0">
<div className="flex items-center gap-1.5 pl-3 pr-2 py-1 border-b border-border bg-background shrink-0">
{showBack && onBack && (
<button
onClick={onBack}