feat: enhance sidebar components for VS Code runtime support and improve styling

This commit is contained in:
Bohdan Triapitsyn
2025-12-28 03:28:13 +02:00
parent 21f164ea73
commit df07094d1e
12 changed files with 164 additions and 50 deletions
@@ -16,6 +16,7 @@ import { OpenChamberPage } from '@/components/sections/openchamber/OpenChamberPa
import { OpenChamberSidebar, type OpenChamberSection } from '@/components/sections/openchamber/OpenChamberSidebar';
import { ErrorBoundary } from '@/components/ui/ErrorBoundary';
import { useDeviceInfo } from '@/lib/device';
import { isVSCodeRuntime } from '@/lib/desktop';
const SETTINGS_SECTIONS = (() => {
const filtered = SIDEBAR_SECTIONS.filter(section => section.id !== 'sessions');
@@ -29,12 +30,18 @@ const SETTINGS_SIDEBAR_MIN_WIDTH = 200;
const SETTINGS_SIDEBAR_MAX_WIDTH = 500;
const SETTINGS_SIDEBAR_DEFAULT_WIDTH = 264;
// Width threshold for hiding tab labels (show icons only)
const TAB_LABELS_MIN_WIDTH = 700;
interface SettingsViewProps {
onClose?: () => void;
/** Force mobile layout regardless of device detection */
forceMobile?: boolean;
}
export const SettingsView: React.FC<SettingsViewProps> = ({ onClose }) => {
const { isMobile } = useDeviceInfo();
export const SettingsView: React.FC<SettingsViewProps> = ({ onClose, forceMobile }) => {
const deviceInfo = useDeviceInfo();
const isMobile = forceMobile ?? deviceInfo.isMobile;
const [activeTab, setActiveTab] = React.useState<SidebarSection>('settings');
const [selectedOpenChamberSection, setSelectedOpenChamberSection] = React.useState<OpenChamberSection>('visual');
// Mobile drill-down state: show page content instead of sidebar
@@ -51,6 +58,8 @@ export const SettingsView: React.FC<SettingsViewProps> = ({ onClose }) => {
});
const [hasManuallyResized, setHasManuallyResized] = React.useState(false);
const [isResizing, setIsResizing] = React.useState(false);
const [containerWidth, setContainerWidth] = React.useState(0);
const containerRef = React.useRef<HTMLDivElement>(null);
const startXRef = React.useRef(0);
const startWidthRef = React.useRef(sidebarWidth);
@@ -64,11 +73,32 @@ export const SettingsView: React.FC<SettingsViewProps> = ({ onClose }) => {
return /Macintosh|Mac OS X/.test(navigator.userAgent || '');
}, []);
const isVSCode = React.useMemo(() => isVSCodeRuntime(), []);
React.useEffect(() => {
if (typeof window === 'undefined') return;
setIsDesktopApp(typeof (window as typeof window & { opencodeDesktop?: unknown }).opencodeDesktop !== 'undefined');
}, []);
// Track container width for responsive tab labels
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);
setContainerWidth(container.clientWidth);
return () => observer.disconnect();
}, []);
const showTabLabels = containerWidth === 0 || containerWidth >= TAB_LABELS_MIN_WIDTH;
// Update proportional width on window resize (if not manually resized)
React.useEffect(() => {
if (typeof window === 'undefined') return;
@@ -221,7 +251,7 @@ export const SettingsView: React.FC<SettingsViewProps> = ({ onClose }) => {
const showLeadingDivider = isDesktopApp && isMacPlatform;
return (
<div className={cn('flex h-full flex-col overflow-hidden', isDesktopApp ? 'bg-transparent' : 'bg-background')}>
<div ref={containerRef} className={cn('flex h-full flex-col overflow-hidden', isDesktopApp ? 'bg-transparent' : 'bg-background')}>
{/* Header with tabs and close button */}
<div
onMouseDown={!isMobile ? handleDragStart : undefined}
@@ -296,7 +326,7 @@ export const SettingsView: React.FC<SettingsViewProps> = ({ onClose }) => {
aria-label={label}
>
<PhosphorIcon className="h-4 w-4" weight="regular" />
<span className="header-tab-label">{label}</span>
{showTabLabels && <span>{label}</span>}
</button>
{/* Vertical divider after each tab */}
<div className="h-full w-px bg-border" aria-hidden="true" />
@@ -338,7 +368,7 @@ export const SettingsView: React.FC<SettingsViewProps> = ({ onClose }) => {
<ErrorBoundary>{renderPageContent()}</ErrorBoundary>
</div>
) : (
<div className="flex-1 overflow-hidden bg-sidebar">
<div className={cn('flex-1 overflow-hidden', isVSCode ? 'bg-background' : 'bg-sidebar')}>
<ErrorBoundary>{renderSidebarContent()}</ErrorBoundary>
</div>
)
@@ -351,7 +381,9 @@ export const SettingsView: React.FC<SettingsViewProps> = ({ 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={{