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
+78 -17
View File
@@ -19,6 +19,67 @@
import { useCallback, useEffect, useRef, useState } from 'react';
interface SayTTSStatusCache {
available: boolean;
voices: Array<{ name: string; locale: string }>;
checkedAt: number;
}
interface UseSayTTSOptions {
enabled?: boolean;
}
const SAY_TTS_STATUS_TTL_MS = 30000;
let sayTTSStatusCache: SayTTSStatusCache | null = null;
let sayTTSStatusRequest: Promise<SayTTSStatusCache> | null = null;
async function getSayTTSStatus(): Promise<SayTTSStatusCache> {
const now = Date.now();
if (sayTTSStatusCache && now - sayTTSStatusCache.checkedAt < SAY_TTS_STATUS_TTL_MS) {
return sayTTSStatusCache;
}
if (sayTTSStatusRequest) {
return sayTTSStatusRequest;
}
sayTTSStatusRequest = (async () => {
try {
const response = await fetch('/api/tts/say/status');
if (!response.ok) {
const unavailableStatus: SayTTSStatusCache = {
available: false,
voices: [],
checkedAt: Date.now(),
};
sayTTSStatusCache = unavailableStatus;
return unavailableStatus;
}
const data = await response.json();
const nextStatus: SayTTSStatusCache = {
available: Boolean(data.available),
voices: Array.isArray(data.voices) ? data.voices : [],
checkedAt: Date.now(),
};
sayTTSStatusCache = nextStatus;
return nextStatus;
} catch {
const unavailableStatus: SayTTSStatusCache = {
available: false,
voices: [],
checkedAt: Date.now(),
};
sayTTSStatusCache = unavailableStatus;
return unavailableStatus;
} finally {
sayTTSStatusRequest = null;
}
})();
return sayTTSStatusRequest;
}
export interface UseSayTTSReturn {
/** Whether TTS is currently playing */
isPlaying: boolean;
@@ -61,7 +122,8 @@ function getAudioContext(): AudioContext {
return sharedAudioContext;
}
export function useSayTTS(): UseSayTTSReturn {
export function useSayTTS(options: UseSayTTSOptions = {}): UseSayTTSReturn {
const enabled = options.enabled ?? true;
const [isPlaying, setIsPlaying] = useState(false);
const [isAvailable, setIsAvailable] = useState(false);
const [voices, setVoices] = useState<Array<{ name: string; locale: string }>>([]);
@@ -97,28 +159,27 @@ export function useSayTTS(): UseSayTTSReturn {
// Check if macOS say is available
const checkAvailability = useCallback(async (): Promise<boolean> => {
try {
const response = await fetch('/api/tts/say/status');
if (response.ok) {
const data = await response.json();
setIsAvailable(data.available);
if (data.voices) {
setVoices(data.voices);
}
return data.available;
}
setIsAvailable(false);
return false;
} catch (err) {
console.error('[useSayTTS] Failed to check availability:', err);
if (!enabled) {
setIsAvailable(false);
setVoices([]);
return false;
}
}, []);
try {
const status = await getSayTTSStatus();
setIsAvailable(status.available);
setVoices(status.voices);
return status.available;
} catch {
setIsAvailable(false);
setVoices([]);
return false;
}
}, [enabled]);
// Check availability on mount
useEffect(() => {
checkAvailability();
void checkAvailability();
}, [checkAvailability]);
// Stop current playback