From df07094d1e58be02be799e56538ca17916a3ebe1 Mon Sep 17 00:00:00 2001 From: Bohdan Triapitsyn Date: Sun, 28 Dec 2025 03:28:13 +0200 Subject: [PATCH] feat: enhance sidebar components for VS Code runtime support and improve styling --- CHANGELOG.md | 1 + .../layout/SidebarContextSummary.tsx | 2 +- .../ui/src/components/layout/VSCodeLayout.tsx | 61 +++++++++++-------- .../sections/agents/AgentsSidebar.tsx | 13 +++- .../sections/commands/CommandsSidebar.tsx | 13 +++- .../git-identities/GitIdentitiesSidebar.tsx | 13 +++- .../openchamber/OpenChamberSidebar.tsx | 17 +++++- .../sections/providers/ProvidersSidebar.tsx | 13 +++- .../sections/shared/SettingsSidebarHeader.tsx | 2 +- .../sections/shared/SettingsSidebarLayout.tsx | 14 ++++- .../ui/src/components/views/SettingsView.tsx | 44 +++++++++++-- packages/ui/src/lib/theme/vscode/adapter.ts | 21 +++++-- 12 files changed, 164 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5305ad17..e0dd2f36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file. - ESC key now closes settings; double-ESC abort only works on chat tab without overlays. - Added responsive tab labels in settings header (icons only at narrow widths). - Improved session activity status handling and message step completion logic. +- Introduced enchanced VSCode extension settings with dynamic layout based on width. ## [1.3.6] - 2025-12-27 diff --git a/packages/ui/src/components/layout/SidebarContextSummary.tsx b/packages/ui/src/components/layout/SidebarContextSummary.tsx index 1829573c..723d2d8d 100644 --- a/packages/ui/src/components/layout/SidebarContextSummary.tsx +++ b/packages/ui/src/components/layout/SidebarContextSummary.tsx @@ -48,7 +48,7 @@ export const SidebarContextSummary: React.FC = ({ cl }, [directoryFull]); return ( -
+
Session {activeSessionTitle} diff --git a/packages/ui/src/components/layout/VSCodeLayout.tsx b/packages/ui/src/components/layout/VSCodeLayout.tsx index 215ab2ba..41752cdc 100644 --- a/packages/ui/src/components/layout/VSCodeLayout.tsx +++ b/packages/ui/src/components/layout/VSCodeLayout.tsx @@ -1,17 +1,21 @@ import React from 'react'; import { ErrorBoundary } from '../ui/ErrorBoundary'; import { SessionSidebar } from '@/components/session/SessionSidebar'; -import { ChatView } from '@/components/views'; +import { ChatView, SettingsView } from '@/components/views'; import { useSessionStore } from '@/stores/useSessionStore'; import { useConfigStore } from '@/stores/useConfigStore'; import { ContextUsageDisplay } from '@/components/ui/ContextUsageDisplay'; import { RiAddLine, RiArrowLeftLine, RiSettings3Line } from '@remixicon/react'; -import { OpenChamberPage } from '@/components/sections/openchamber/OpenChamberPage'; + +// Width threshold for mobile vs desktop layout in settings +const MOBILE_WIDTH_THRESHOLD = 550; type VSCodeView = 'sessions' | 'chat' | 'settings'; export const VSCodeLayout: React.FC = () => { const [currentView, setCurrentView] = React.useState('chat'); + const [containerWidth, setContainerWidth] = React.useState(0); + const containerRef = React.useRef(null); const currentSessionId = useSessionStore((state) => state.currentSessionId); const sessions = useSessionStore((state) => state.sessions); const newSessionDraftOpen = useSessionStore((state) => Boolean(state.newSessionDraft?.open)); @@ -110,8 +114,28 @@ export const VSCodeLayout: React.FC = () => { void hydrateMessages(); }, [connectionStatus, currentSessionId, currentView, hasInitializedOnce, loadMessages, messages, newSessionDraftOpen]); + // Track container width for responsive settings layout + React.useEffect(() => { + const container = containerRef.current; + if (!container) return; + + const observer = new ResizeObserver((entries) => { + for (const entry of entries) { + setContainerWidth(entry.contentRect.width); + } + }); + + observer.observe(container); + // Set initial width + setContainerWidth(container.clientWidth); + + return () => observer.disconnect(); + }, []); + + const usesMobileLayout = containerWidth > 0 && containerWidth < MOBILE_WIDTH_THRESHOLD; + return ( -
+
{currentView === 'sessions' ? (
{
) : currentView === 'settings' ? ( -
- -
- -
-
+ setCurrentView('sessions')} + forceMobile={usesMobileLayout} + /> ) : (
= ({ title, showBack, onBack, on {showBack && onBack && ( {/* Vertical divider after each tab */} ) : ( -
+
{renderSidebarContent()}
) @@ -351,7 +381,9 @@ export const SettingsView: React.FC = ({ onClose }) => { 'relative overflow-hidden border-r', isDesktopApp ? 'bg-[color:var(--sidebar-overlay-strong)] backdrop-blur supports-[backdrop-filter]:bg-[color:var(--sidebar-overlay-soft)]' - : 'bg-sidebar', + : isVSCode + ? 'bg-background' + : 'bg-sidebar', isResizing ? 'transition-none' : '' )} style={{ diff --git a/packages/ui/src/lib/theme/vscode/adapter.ts b/packages/ui/src/lib/theme/vscode/adapter.ts index c06db6ff..d3f0c006 100644 --- a/packages/ui/src/lib/theme/vscode/adapter.ts +++ b/packages/ui/src/lib/theme/vscode/adapter.ts @@ -12,12 +12,14 @@ export type VSCodeThemeColorToken = | 'editor.lineHighlightBackground' | 'editorCursor.foreground' | 'focusBorder' + | 'contrastBorder' | 'diffEditor.insertedTextBackground' | 'diffEditor.insertedTextBorder' | 'diffEditor.insertedLineBackground' | 'gitDecoration.addedResourceForeground' | 'sideBar.background' | 'sideBar.foreground' + | 'sideBar.border' | 'panel.background' | 'panel.foreground' | 'panel.border' @@ -69,12 +71,14 @@ const VARIABLE_MAP: Record = { 'editor.lineHighlightBackground': '--vscode-editor-lineHighlightBackground', 'editorCursor.foreground': '--vscode-editorCursor-foreground', focusBorder: '--vscode-focusBorder', + contrastBorder: '--vscode-contrastBorder', 'diffEditor.insertedTextBackground': '--vscode-diffEditor-insertedTextBackground', 'diffEditor.insertedTextBorder': '--vscode-diffEditor-insertedTextBorder', 'diffEditor.insertedLineBackground': '--vscode-diffEditor-insertedLineBackground', 'gitDecoration.addedResourceForeground': '--vscode-gitDecoration-addedResourceForeground', 'sideBar.background': '--vscode-sideBar-background', 'sideBar.foreground': '--vscode-sideBar-foreground', + 'sideBar.border': '--vscode-sideBar-border', 'panel.background': '--vscode-panel-background', 'panel.foreground': '--vscode-panel-foreground', 'panel.border': '--vscode-panel-border', @@ -200,11 +204,14 @@ export const buildVSCodeThemeFromPalette = (palette: VSCodeThemePalette): Theme palette.colors[token] ?? fallback; const sidebarBg = read('sideBar.background', base.colors.surface.background); - const sidebarFg = read('sideBar.foreground', read('descriptionForeground', base.colors.surface.mutedForeground)); const panelBg = read('panel.background', read('editor.background', base.colors.surface.elevated)); const panelFg = read('panel.foreground', read('editor.foreground', base.colors.surface.foreground)); const background = sidebarBg; const foreground = read('editor.foreground', base.colors.surface.foreground); + // Use descriptionForeground for muted text with reduced opacity for less prominence + // This makes inactive tabs and secondary text clearly distinguishable from active/primary text + const rawMutedFg = read('descriptionForeground', base.colors.surface.mutedForeground); + const mutedFg = applyAlpha(rawMutedFg, palette.kind === 'light' ? 0.55 : 0.5); // Prefer VS Code's "added diff" color as our primary accent when available (users expect this to match their theme). const diffInserted = palette.colors['diffEditor.insertedTextBorder'] ?? palette.colors['diffEditor.insertedLineBackground'] @@ -219,8 +226,14 @@ export const buildVSCodeThemeFromPalette = (palette: VSCodeThemePalette): Theme const selection = read('editor.selectionBackground', activeBg); const selectionFg = read('editor.selectionForeground', foreground); const focus = read('focusBorder', accent); - // Prefer panel border for a less prominent, more consistent border color in webviews. - const border = read('panel.border', read('input.border', base.colors.interactive.border)); + // Build a visible border color: prefer contrastBorder (high-contrast themes), then sideBar.border, panel.border, input.border + // If the chosen border is too transparent or matches background, derive one from foreground + const rawBorder = read('contrastBorder', '') || + read('sideBar.border', '') || + read('panel.border', '') || + read('input.border', base.colors.interactive.border); + // Ensure border has enough visibility by applying minimum opacity + const border = rawBorder ? applyAlpha(forceOpaque(rawBorder), palette.kind === 'light' ? 0.15 : 0.2) : applyAlpha(foreground, palette.kind === 'light' ? 0.1 : 0.15); const focusRing = applyAlpha(focus, palette.kind === 'light' ? 0.35 : 0.45); const cursor = read('editorCursor.foreground', base.colors.interactive.cursor); const badgeBg = read('badge.background', accent); @@ -263,7 +276,7 @@ export const buildVSCodeThemeFromPalette = (palette: VSCodeThemePalette): Theme background, foreground, muted: activeBg, - mutedForeground: sidebarFg, + mutedForeground: mutedFg, elevated: panelBg, elevatedForeground: panelFg, overlay: read('statusBar.background', base.colors.surface.overlay),