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
@@ -22,6 +22,7 @@ import { RiAddLine, RiAiAgentFill, RiAiAgentLine, RiDeleteBinLine, RiFileCopyLin
import { useAgentsStore, isAgentBuiltIn, isAgentHidden } from '@/stores/useAgentsStore';
import { useUIStore } from '@/stores/useUIStore';
import { useDeviceInfo } from '@/lib/device';
import { isVSCodeRuntime } from '@/lib/desktop';
import { cn } from '@/lib/utils';
import type { Agent } from '@opencode-ai/sdk';
import { ScrollableOverlay } from '@/components/ui/ScrollableOverlay';
@@ -50,6 +51,8 @@ export const AgentsSidebar: React.FC<AgentsSidebarProps> = ({ onItemSelect }) =>
return typeof window.opencodeDesktop !== 'undefined';
});
const isVSCode = React.useMemo(() => isVSCodeRuntime(), []);
React.useEffect(() => {
if (typeof window === 'undefined') return;
setIsDesktopRuntime(typeof window.opencodeDesktop !== 'undefined');
@@ -59,6 +62,12 @@ export const AgentsSidebar: React.FC<AgentsSidebarProps> = ({ onItemSelect }) =>
loadAgents();
}, [loadAgents]);
const bgClass = isDesktopRuntime
? 'bg-transparent'
: isVSCode
? 'bg-background'
: 'bg-sidebar';
const handleCreateAgent = () => {
if (!newAgentName.trim()) {
toast.error('Agent name is required');
@@ -132,9 +141,9 @@ export const AgentsSidebar: React.FC<AgentsSidebarProps> = ({ onItemSelect }) =>
const customAgents = visibleAgents.filter((agent) => !isAgentBuiltIn(agent));
return (
<div className={cn('flex h-full flex-col', isDesktopRuntime ? 'bg-transparent' : 'bg-sidebar')}>
<div className={cn('flex h-full flex-col', bgClass)}>
<Dialog open={isCreateDialogOpen} onOpenChange={setIsCreateDialogOpen}>
<div className={cn('border-b border-border/40 px-3 dark:border-white/10', isMobile ? 'mt-2 py-3' : 'py-3')}>
<div className={cn('border-b px-3', isMobile ? 'mt-2 py-3' : 'py-3')}>
<div className="flex items-center justify-between gap-2">
<span className="typography-meta text-muted-foreground">Total {visibleAgents.length}</span>
<DialogTrigger asChild>
@@ -22,6 +22,7 @@ import { RiAddLine, RiTerminalBoxLine, RiMore2Line, RiDeleteBinLine, RiFileCopyL
import { useCommandsStore, type Command } from '@/stores/useCommandsStore';
import { useUIStore } from '@/stores/useUIStore';
import { useDeviceInfo } from '@/lib/device';
import { isVSCodeRuntime } from '@/lib/desktop';
import { ScrollableOverlay } from '@/components/ui/ScrollableOverlay';
import { cn } from '@/lib/utils';
@@ -49,6 +50,8 @@ export const CommandsSidebar: React.FC<CommandsSidebarProps> = ({ onItemSelect }
return typeof window.opencodeDesktop !== 'undefined';
});
const isVSCode = React.useMemo(() => isVSCodeRuntime(), []);
React.useEffect(() => {
if (typeof window === 'undefined') return;
setIsDesktopRuntime(typeof window.opencodeDesktop !== 'undefined');
@@ -58,6 +61,12 @@ export const CommandsSidebar: React.FC<CommandsSidebarProps> = ({ onItemSelect }
loadCommands();
}, [loadCommands]);
const bgClass = isDesktopRuntime
? 'bg-transparent'
: isVSCode
? 'bg-background'
: 'bg-sidebar';
const handleCreateCommand = () => {
if (!newCommandName.trim()) {
toast.error('Command name is required');
@@ -110,9 +119,9 @@ export const CommandsSidebar: React.FC<CommandsSidebarProps> = ({ onItemSelect }
};
return (
<div className={cn('flex h-full flex-col', isDesktopRuntime ? 'bg-transparent' : 'bg-sidebar')}>
<div className={cn('flex h-full flex-col', bgClass)}>
<Dialog open={isCreateDialogOpen} onOpenChange={setIsCreateDialogOpen}>
<div className={cn('border-b border-border/40 px-3 dark:border-white/10', isMobile ? 'mt-2 py-3' : 'py-3')}>
<div className={cn('border-b px-3', isMobile ? 'mt-2 py-3' : 'py-3')}>
<div className="flex items-center justify-between gap-2">
<span className="typography-meta text-muted-foreground">Total {commands.length}</span>
<DialogTrigger asChild>
@@ -21,6 +21,7 @@ import {
import { useGitIdentitiesStore } from '@/stores/useGitIdentitiesStore';
import { useUIStore } from '@/stores/useUIStore';
import { useDeviceInfo } from '@/lib/device';
import { isVSCodeRuntime } from '@/lib/desktop';
import { ScrollableOverlay } from '@/components/ui/ScrollableOverlay';
import { cn } from '@/lib/utils';
import type { GitIdentityProfile } from '@/stores/useGitIdentitiesStore';
@@ -65,6 +66,8 @@ export const GitIdentitiesSidebar: React.FC<GitIdentitiesSidebarProps> = ({ onIt
return typeof window.opencodeDesktop !== 'undefined';
});
const isVSCode = React.useMemo(() => isVSCodeRuntime(), []);
React.useEffect(() => {
if (typeof window === 'undefined') return;
setIsDesktopRuntime(typeof window.opencodeDesktop !== 'undefined');
@@ -75,6 +78,12 @@ export const GitIdentitiesSidebar: React.FC<GitIdentitiesSidebarProps> = ({ onIt
loadGlobalIdentity();
}, [loadProfiles, loadGlobalIdentity]);
const bgClass = isDesktopRuntime
? 'bg-transparent'
: isVSCode
? 'bg-background'
: 'bg-sidebar';
const handleCreateProfile = () => {
setSelectedProfile('new');
onItemSelect?.();
@@ -95,8 +104,8 @@ export const GitIdentitiesSidebar: React.FC<GitIdentitiesSidebarProps> = ({ onIt
};
return (
<div className={cn('flex h-full flex-col', isDesktopRuntime ? 'bg-transparent' : 'bg-sidebar')}>
<div className={cn('border-b border-border/40 px-3 dark:border-white/10', isMobile ? 'mt-2 py-3' : 'py-3')}>
<div className={cn('flex h-full flex-col', bgClass)}>
<div className={cn('border-b px-3', isMobile ? 'mt-2 py-3' : 'py-3')}>
<div className="flex items-center justify-between gap-2">
<span className="typography-meta text-muted-foreground">Total {profiles.length}</span>
<Button
@@ -1,7 +1,7 @@
import React from 'react';
import { ScrollableOverlay } from '@/components/ui/ScrollableOverlay';
import { useDeviceInfo } from '@/lib/device';
import { isWebRuntime } from '@/lib/desktop';
import { isVSCodeRuntime, isWebRuntime } from '@/lib/desktop';
import { AboutSettings } from './AboutSettings';
import { cn } from '@/lib/utils';
@@ -48,13 +48,24 @@ export const OpenChamberSidebar: React.FC<OpenChamberSidebarProps> = ({
return typeof window.opencodeDesktop !== 'undefined';
});
const isVSCode = React.useMemo(() => isVSCodeRuntime(), []);
React.useEffect(() => {
if (typeof window === 'undefined') return;
setIsDesktopRuntime(typeof window.opencodeDesktop !== 'undefined');
}, []);
// Desktop app: transparent for blur effect
// VS Code: bg-background (same as page content)
// Web/mobile: bg-sidebar
const bgClass = isDesktopRuntime
? 'bg-transparent'
: isVSCode
? 'bg-background'
: 'bg-sidebar';
return (
<div className={cn('flex h-full flex-col', isDesktopRuntime ? 'bg-transparent' : 'bg-sidebar')}>
<div className={cn('flex h-full flex-col', bgClass)}>
<ScrollableOverlay outerClassName="flex-1 min-h-0" className="space-y-1 px-3 py-2 overflow-x-hidden">
{OPENCHAMBER_SECTION_GROUPS.map((group) => {
const isSelected = selectedSection === group.id;
@@ -84,7 +95,7 @@ export const OpenChamberSidebar: React.FC<OpenChamberSidebarProps> = ({
{/* Mobile footer: About section */}
{showAbout && (
<div className="border-t border-border/40 px-3 py-4">
<div className="border-t px-3 py-4">
<AboutSettings />
</div>
)}
@@ -4,6 +4,7 @@ import { ProviderLogo } from '@/components/ui/ProviderLogo';
import { Button } from '@/components/ui/button';
import { useConfigStore } from '@/stores/useConfigStore';
import { useDeviceInfo } from '@/lib/device';
import { isVSCodeRuntime } from '@/lib/desktop';
import { RiAddLine, RiStackLine } from '@remixicon/react';
import { cn } from '@/lib/utils';
@@ -24,14 +25,22 @@ export const ProvidersSidebar: React.FC<ProvidersSidebarProps> = ({ onItemSelect
return typeof window.opencodeDesktop !== 'undefined';
});
const isVSCode = React.useMemo(() => isVSCodeRuntime(), []);
React.useEffect(() => {
if (typeof window === 'undefined') return;
setIsDesktopRuntime(typeof window.opencodeDesktop !== 'undefined');
}, []);
const bgClass = isDesktopRuntime
? 'bg-transparent'
: isVSCode
? 'bg-background'
: 'bg-sidebar';
return (
<div className={cn('flex h-full flex-col', isDesktopRuntime ? 'bg-transparent' : 'bg-sidebar')}>
<div className={cn('border-b border-border/40 px-3 dark:border-white/10', isMobile ? 'mt-2 py-3' : 'py-3')}>
<div className={cn('flex h-full flex-col', bgClass)}>
<div className={cn('border-b px-3', isMobile ? 'mt-2 py-3' : 'py-3')}>
<div className="flex items-center justify-between gap-2">
<span className="typography-meta text-muted-foreground">Total {providers.length}</span>
<Button
@@ -37,7 +37,7 @@ export const SettingsSidebarHeader: React.FC<SettingsSidebarHeaderProps> = ({
return (
<div
className={cn(
'border-b border-border/40 px-3 dark:border-white/10',
'border-b px-3',
isMobile ? 'mt-2 py-3' : 'py-3'
)}
>
@@ -1,5 +1,6 @@
import React from 'react';
import { ScrollableOverlay } from '@/components/ui/ScrollableOverlay';
import { isVSCodeRuntime } from '@/lib/desktop';
import { cn } from '@/lib/utils';
interface SettingsSidebarLayoutProps {
@@ -37,16 +38,27 @@ export const SettingsSidebarLayout: React.FC<SettingsSidebarLayoutProps> = ({
return typeof window.opencodeDesktop !== 'undefined';
});
const isVSCode = React.useMemo(() => isVSCodeRuntime(), []);
React.useEffect(() => {
if (typeof window === 'undefined') return;
setIsDesktopRuntime(typeof window.opencodeDesktop !== 'undefined');
}, []);
// Desktop app: transparent for blur effect
// VS Code: bg-background (same as page content)
// Web/mobile: bg-sidebar
const bgClass = isDesktopRuntime
? 'bg-transparent'
: isVSCode
? 'bg-background'
: 'bg-sidebar';
return (
<div
className={cn(
'flex h-full flex-col',
isDesktopRuntime ? 'bg-transparent' : 'bg-sidebar',
bgClass,
className
)}
>