feat: move projects to sidebar rail and speed up session switching (#506)
* feat: move projects from header tabs to left sidebar rail * refactor: remove variant from git generation session context * feat: implemented drandrop action for navrails * refactor: improve sidebar drag-and-drop smoothness and remove floating overlay * fix: prevent mobile nav rail touch from closing drawer and opening menu * fix: hide header tabs on desktop * fix: prevent session flicker when switching projects * style: soften chat panel divider borders * style: improve icon contrast across core navigation * fix: stabilize session selection during project switching * style: unify sidebar surfaces and transparent section layers * style: remove UI shadows and keep only scroll shadow * perf: speed up project switching with cached session loading * perf: speed up Git changes view and background refresh * perf: make session switching lighter and less aggressive * perf: optimized sessions list loading while project switching * perf: smooth chat rendering and reduce interaction spikes * fix: restore reliable load older messages visibility
This commit is contained in:
committed by
GitHub
parent
4c69bccf56
commit
10851bd7ac
@@ -1,17 +1,11 @@
|
||||
import React from 'react';
|
||||
import { RiDownloadLine, RiInformationLine, RiQuestionLine, RiSettings3Line } from '@remixicon/react';
|
||||
import { toast } from '@/components/ui';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { ErrorBoundary } from '../ui/ErrorBoundary';
|
||||
import { useUIStore } from '@/stores/useUIStore';
|
||||
import { useUpdateStore } from '@/stores/useUpdateStore';
|
||||
import { UpdateDialog } from '../ui/UpdateDialog';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/tooltip';
|
||||
|
||||
export const SIDEBAR_CONTENT_WIDTH = 264;
|
||||
const SIDEBAR_MIN_WIDTH = 300;
|
||||
export const SIDEBAR_CONTENT_WIDTH = 250;
|
||||
const SIDEBAR_MIN_WIDTH = 250;
|
||||
const SIDEBAR_MAX_WIDTH = 500;
|
||||
const CHECK_FOR_UPDATES_EVENT = 'openchamber:check-for-updates';
|
||||
|
||||
interface SidebarProps {
|
||||
isOpen: boolean;
|
||||
@@ -20,70 +14,10 @@ interface SidebarProps {
|
||||
}
|
||||
|
||||
export const Sidebar: React.FC<SidebarProps> = ({ isOpen, isMobile, children }) => {
|
||||
const { sidebarWidth, setSidebarWidth, setSettingsDialogOpen, setAboutDialogOpen, toggleHelpDialog } = useUIStore();
|
||||
const { sidebarWidth, setSidebarWidth } = useUIStore();
|
||||
const [isResizing, setIsResizing] = React.useState(false);
|
||||
const startXRef = React.useRef(0);
|
||||
const startWidthRef = React.useRef(sidebarWidth || SIDEBAR_CONTENT_WIDTH);
|
||||
const [updateDialogOpen, setUpdateDialogOpen] = React.useState(false);
|
||||
|
||||
const updateStore = useUpdateStore();
|
||||
const pendingMenuUpdateCheckRef = React.useRef(false);
|
||||
|
||||
const checkForUpdates = updateStore.checkForUpdates;
|
||||
const { available, downloaded, checking } = updateStore;
|
||||
|
||||
const [isDesktopApp, setIsDesktopApp] = React.useState<boolean>(() => {
|
||||
if (typeof window === 'undefined') {
|
||||
return false;
|
||||
}
|
||||
return Boolean((window as unknown as { __TAURI__?: unknown }).__TAURI__);
|
||||
});
|
||||
|
||||
|
||||
|
||||
React.useEffect(() => {
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
setIsDesktopApp(Boolean((window as unknown as { __TAURI__?: unknown }).__TAURI__));
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
const handleMenuUpdateCheck = () => {
|
||||
if (!(window as unknown as { __TAURI__?: unknown }).__TAURI__) {
|
||||
return;
|
||||
}
|
||||
pendingMenuUpdateCheckRef.current = true;
|
||||
void checkForUpdates();
|
||||
};
|
||||
|
||||
window.addEventListener(CHECK_FOR_UPDATES_EVENT, handleMenuUpdateCheck as EventListener);
|
||||
return () => {
|
||||
window.removeEventListener(CHECK_FOR_UPDATES_EVENT, handleMenuUpdateCheck as EventListener);
|
||||
};
|
||||
}, [checkForUpdates]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!pendingMenuUpdateCheckRef.current) {
|
||||
return;
|
||||
}
|
||||
if (checking) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (available || downloaded) {
|
||||
setUpdateDialogOpen(true);
|
||||
} else {
|
||||
toast.success('No updates available', {
|
||||
description: 'You are running the latest version.',
|
||||
});
|
||||
}
|
||||
pendingMenuUpdateCheckRef.current = false;
|
||||
}, [available, downloaded, checking]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isMobile || !isResizing) {
|
||||
@@ -119,7 +53,6 @@ export const Sidebar: React.FC<SidebarProps> = ({ isOpen, isMobile, children })
|
||||
}, [isMobile, isResizing]);
|
||||
|
||||
if (isMobile) {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -141,10 +74,8 @@ export const Sidebar: React.FC<SidebarProps> = ({ isOpen, isMobile, children })
|
||||
return (
|
||||
<aside
|
||||
className={cn(
|
||||
'relative flex h-full overflow-hidden border-r border-border',
|
||||
isDesktopApp
|
||||
? 'bg-[color:var(--sidebar-overlay-strong)] backdrop-blur supports-[backdrop-filter]:bg-[color:var(--sidebar-overlay-soft)]'
|
||||
: 'bg-sidebar',
|
||||
'relative flex h-full overflow-hidden border-r border-border/40',
|
||||
'bg-sidebar/50',
|
||||
isResizing ? 'transition-none' : 'transition-[width] duration-300 ease-in-out',
|
||||
!isOpen && 'border-r-0'
|
||||
)}
|
||||
@@ -179,86 +110,6 @@ export const Sidebar: React.FC<SidebarProps> = ({ isOpen, isMobile, children })
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<ErrorBoundary>{children}</ErrorBoundary>
|
||||
</div>
|
||||
<div className="flex-shrink-0 border-t border-border h-12 px-2 bg-sidebar">
|
||||
<div className="flex h-full items-center justify-between gap-2">
|
||||
<button
|
||||
onClick={() => setSettingsDialogOpen(true)}
|
||||
className={cn(
|
||||
'flex h-8 items-center gap-2 rounded-md px-2',
|
||||
'text-sm font-semibold text-sidebar-foreground/90',
|
||||
'hover:text-sidebar-foreground hover:bg-interactive-hover',
|
||||
'transition-all duration-200'
|
||||
)}
|
||||
>
|
||||
<RiSettings3Line className="h-4 w-4" />
|
||||
<span>Settings</span>
|
||||
</button>
|
||||
<div className="flex items-center gap-1">
|
||||
{(available || downloaded) ? (
|
||||
<button
|
||||
onClick={() => setUpdateDialogOpen(true)}
|
||||
className={cn(
|
||||
'flex items-center gap-1.5 rounded-md px-2 py-1',
|
||||
'text-xs font-semibold',
|
||||
'bg-primary/10 text-primary',
|
||||
'hover:bg-primary/20',
|
||||
'transition-colors'
|
||||
)}
|
||||
>
|
||||
<RiDownloadLine className="h-3.5 w-3.5" />
|
||||
<span>Update</span>
|
||||
</button>
|
||||
|
||||
) : !isDesktopApp && (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<button
|
||||
onClick={() => setAboutDialogOpen(true)}
|
||||
className={cn(
|
||||
'flex h-8 w-8 items-center justify-center rounded-md',
|
||||
'text-sidebar-foreground/70',
|
||||
'hover:text-sidebar-foreground hover:bg-interactive-hover',
|
||||
'transition-all duration-200'
|
||||
)}
|
||||
>
|
||||
<RiInformationLine className="h-4 w-4" />
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="top">About OpenChamber</TooltipContent>
|
||||
</Tooltip>
|
||||
)}
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<button
|
||||
onClick={toggleHelpDialog}
|
||||
className={cn(
|
||||
'flex h-8 w-8 items-center justify-center rounded-md',
|
||||
'text-sidebar-foreground/70',
|
||||
'hover:text-sidebar-foreground hover:bg-interactive-hover',
|
||||
'transition-all duration-200'
|
||||
)}
|
||||
aria-label="Keyboard shortcuts"
|
||||
>
|
||||
<RiQuestionLine className="h-4 w-4" />
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="top">Keyboard shortcuts</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<UpdateDialog
|
||||
open={updateDialogOpen}
|
||||
onOpenChange={setUpdateDialogOpen}
|
||||
info={updateStore.info}
|
||||
downloading={updateStore.downloading}
|
||||
downloaded={updateStore.downloaded}
|
||||
progress={updateStore.progress}
|
||||
error={updateStore.error}
|
||||
onDownload={updateStore.downloadUpdate}
|
||||
onRestart={updateStore.restartToUpdate}
|
||||
runtimeType={updateStore.runtimeType}
|
||||
/>
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user