feat: vscode extension (#59)
* feat: add initial VS Code extension plan and implementation tasks * feat(vscode): added initial version of an Openchamber VSCode extension * feat(vscode): enhance VS Code extension with theme integration and session management * feat(vscode): implement connection status handling and overlay in VSCode layout * feat: move extension to secondary sidebar * chore: upgrade @opencode-ai/sdk to 1.0.150 * vscode: editor bridge, file picker, click-to-open in tool parts * vscode: layout session lifecycle, theme sync, typography overrides * ui: compact mode for vscode, model search, autocomplete width fixes * perf: scroll force flag, raf placeholder, git polling backoff * ui: tool output styling, markdown code block fix, gitignore * refactor: update typography handling for VSCode runtime, remove unused styles * docs: update README with VS Code extension details and add extension image * docs: update changelog with new features and performance improvements
This commit is contained in:
committed by
GitHub
parent
610ccf4c62
commit
bb72c0fb0c
@@ -6,7 +6,7 @@ import React, {
|
||||
} from 'react';
|
||||
import type { Theme, ThemeMode } from '@/types/theme';
|
||||
import type { DesktopSettings } from '@/lib/desktop';
|
||||
import { isDesktopRuntime } from '@/lib/desktop';
|
||||
import { isDesktopRuntime, isVSCodeRuntime } from '@/lib/desktop';
|
||||
import { CSSVariableGenerator } from '@/lib/theme/cssGenerator';
|
||||
import { updateDesktopSettings } from '@/lib/persistence';
|
||||
import {
|
||||
@@ -16,6 +16,8 @@ import {
|
||||
flexokiDarkTheme,
|
||||
} from '@/lib/theme/themes';
|
||||
import { ThemeSystemContext, type ThemeContextValue } from './theme-system-context';
|
||||
import type { VSCodeThemePayload } from '@/lib/theme/vscode/adapter';
|
||||
import { useUIStore } from '@/stores/useUIStore';
|
||||
|
||||
type ThemePreferences = {
|
||||
themeMode: ThemeMode;
|
||||
@@ -50,6 +52,8 @@ const ensureThemeById = (themeId: string, variant: 'light' | 'dark'): Theme => {
|
||||
return fallback ?? fallbackThemeForVariant(variant);
|
||||
};
|
||||
|
||||
const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;
|
||||
|
||||
const validateThemeId = (themeId: string | null, variant: 'light' | 'dark'): string => {
|
||||
if (!themeId) {
|
||||
return variant === 'light' ? DEFAULT_LIGHT_ID : DEFAULT_DARK_ID;
|
||||
@@ -126,8 +130,19 @@ export function ThemeSystemProvider({ children, defaultThemeId }: ThemeSystemPro
|
||||
const cssGenerator = useMemo(() => new CSSVariableGenerator(), []);
|
||||
const [preferences, setPreferences] = useState<ThemePreferences>(() => buildInitialPreferences(defaultThemeId));
|
||||
const [systemPrefersDark, setSystemPrefersDark] = useState<boolean>(() => getSystemPreference());
|
||||
const [vscodeTheme, setVSCodeTheme] = useState<Theme | null>(() => {
|
||||
if (typeof window === 'undefined' || !isVSCodeRuntime()) {
|
||||
return null;
|
||||
}
|
||||
const existing = (window as unknown as { __OPENCHAMBER_VSCODE_THEME__?: Theme }).__OPENCHAMBER_VSCODE_THEME__;
|
||||
return existing || null;
|
||||
});
|
||||
const isVSCode = useMemo(() => isVSCodeRuntime(), []);
|
||||
|
||||
const currentTheme = useMemo(() => {
|
||||
if (isVSCode && vscodeTheme) {
|
||||
return vscodeTheme;
|
||||
}
|
||||
if (preferences.themeMode === 'light') {
|
||||
return ensureThemeById(preferences.lightThemeId, 'light');
|
||||
}
|
||||
@@ -137,9 +152,42 @@ export function ThemeSystemProvider({ children, defaultThemeId }: ThemeSystemPro
|
||||
return systemPrefersDark
|
||||
? ensureThemeById(preferences.darkThemeId, 'dark')
|
||||
: ensureThemeById(preferences.lightThemeId, 'light');
|
||||
}, [preferences, systemPrefersDark]);
|
||||
}, [isVSCode, preferences, systemPrefersDark, vscodeTheme]);
|
||||
|
||||
const availableThemes = themes;
|
||||
const availableThemes = useMemo(
|
||||
() => (isVSCode && vscodeTheme ? [vscodeTheme, ...themes] : themes),
|
||||
[isVSCode, vscodeTheme],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isVSCode) {
|
||||
return;
|
||||
}
|
||||
|
||||
const applyVSCodeTheme = (theme: Theme) => {
|
||||
setVSCodeTheme(theme);
|
||||
const variant: ThemeMode = theme.metadata.variant === 'dark' ? 'dark' : 'light';
|
||||
const uiStore = useUIStore.getState();
|
||||
if (uiStore.theme !== variant) {
|
||||
uiStore.setTheme(variant);
|
||||
}
|
||||
};
|
||||
|
||||
const handleThemeEvent = (event: Event) => {
|
||||
const detail = (event as CustomEvent<VSCodeThemePayload>).detail;
|
||||
if (detail?.theme) {
|
||||
applyVSCodeTheme(detail.theme);
|
||||
}
|
||||
};
|
||||
|
||||
const existing = (window as unknown as { __OPENCHAMBER_VSCODE_THEME__?: Theme }).__OPENCHAMBER_VSCODE_THEME__;
|
||||
if (existing) {
|
||||
applyVSCodeTheme(existing);
|
||||
}
|
||||
|
||||
window.addEventListener('openchamber:vscode-theme', handleThemeEvent as EventListener);
|
||||
return () => window.removeEventListener('openchamber:vscode-theme', handleThemeEvent as EventListener);
|
||||
}, [isVSCode]);
|
||||
|
||||
const updateBrowserChrome = useCallback((theme: Theme) => {
|
||||
if (typeof document === 'undefined') {
|
||||
@@ -177,17 +225,25 @@ export function ThemeSystemProvider({ children, defaultThemeId }: ThemeSystemPro
|
||||
metaThemeColorMedia.setAttribute('content', chromeColor);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const applyVSCodeRuntimeClass = useCallback((enabled: boolean) => {
|
||||
if (typeof document === 'undefined') {
|
||||
return;
|
||||
}
|
||||
document.documentElement.classList.toggle('vscode-runtime', enabled);
|
||||
}, []);
|
||||
|
||||
useIsomorphicLayoutEffect(() => {
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
cssGenerator.apply(currentTheme);
|
||||
applyVSCodeRuntimeClass(isVSCode);
|
||||
updateBrowserChrome(currentTheme);
|
||||
|
||||
const root = document.documentElement;
|
||||
root.classList.remove('light', 'dark');
|
||||
root.classList.add(currentTheme.metadata.variant);
|
||||
}, [cssGenerator, currentTheme, updateBrowserChrome]);
|
||||
}, [applyVSCodeRuntimeClass, cssGenerator, currentTheme, isVSCode, updateBrowserChrome]);
|
||||
|
||||
useEffect(() => {
|
||||
if (preferences.themeMode !== 'system' || typeof window === 'undefined') {
|
||||
|
||||
Reference in New Issue
Block a user