titlebar refactor

This commit is contained in:
tusharmagar 2026-02-06 19:02:22 +05:30
parent cbe34aec8e
commit 840ae4b9cb
5 changed files with 67 additions and 13 deletions

View file

@ -71,6 +71,8 @@ function createWindow() {
height: 800,
show: false, // Don't show until ready
backgroundColor: "#252525", // Prevent white flash (matches dark mode)
titleBarStyle: "hiddenInset",
trafficLightPosition: { x: 12, y: 12 },
webPreferences: {
// IMPORTANT: keep Node out of renderer
nodeIntegration: false,

View file

@ -255,6 +255,15 @@
}
}
/* Titlebar drag regions for frameless window */
.titlebar-drag-region {
-webkit-app-region: drag;
}
.titlebar-no-drag {
-webkit-app-region: no-drag;
}
.graph-view {
background-color: var(--background);
user-select: none;

View file

@ -6,7 +6,7 @@ import type { LanguageModelUsage, ToolUIPart } from 'ai';
import './App.css'
import z from 'zod';
import { Button } from './components/ui/button';
import { CheckIcon, LoaderIcon, ArrowUp, PanelRightIcon, SquarePen, Square } from 'lucide-react';
import { CheckIcon, LoaderIcon, ArrowUp, PanelLeftIcon, PanelRightIcon, SquarePen, Square } from 'lucide-react';
import { cn } from '@/lib/utils';
import { MarkdownEditor } from './components/markdown-editor';
import { ChatInputBar } from './components/chat-button';
@ -43,7 +43,7 @@ import { ToolPermissionRequestEvent, AskHumanRequestEvent } from '@x/shared/src/
import {
SidebarInset,
SidebarProvider,
SidebarTrigger,
useSidebar,
} from "@/components/ui/sidebar"
import { TooltipProvider } from "@/components/ui/tooltip"
import { Separator } from "@/components/ui/separator"
@ -443,6 +443,46 @@ function ChatInputWithMentions({
)
}
/** Traffic light placeholders + toggle button, fixed next to macOS traffic lights */
function FixedSidebarToggle() {
const { toggleSidebar } = useSidebar()
return (
<div className="fixed left-0 top-0 z-50 flex h-10 items-center" style={{ WebkitAppRegion: 'no-drag' } as React.CSSProperties}>
{/* Placeholder dots that show through when traffic lights are hidden (window unfocused) */}
<div className="flex items-center gap-2 pl-[13px]">
<div className="h-3 w-3 rounded-full bg-border" />
<div className="h-3 w-3 rounded-full bg-border" />
<div className="h-3 w-3 rounded-full bg-border" />
</div>
{/* Sidebar toggle */}
<button
type="button"
onClick={toggleSidebar}
className="ml-2.5 flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground transition-colors"
aria-label="Toggle Sidebar"
>
<PanelLeftIcon className="size-4" />
</button>
</div>
)
}
/** Main content header that adjusts padding based on sidebar state */
function ContentHeader({ children }: { children: React.ReactNode }) {
const { state } = useSidebar()
const isCollapsed = state === "collapsed"
return (
<header
className={cn(
"titlebar-drag-region flex h-12 shrink-0 items-center gap-2 border-b border-border px-3 bg-background transition-[padding] duration-200 ease-linear",
isCollapsed && "pl-[108px]"
)}
>
{children}
</header>
)
}
function App() {
// File browser state (for Knowledge section)
const [selectedPath, setSelectedPath] = useState<string | null>(null)
@ -1816,10 +1856,8 @@ function App() {
selectedBackgroundTask={selectedBackgroundTask}
/>
<SidebarInset className="overflow-hidden! min-h-0">
{/* Header with sidebar triggers */}
<header className="flex h-12 shrink-0 items-center gap-2 border-b border-border px-3 bg-background">
<SidebarTrigger className="-ml-1" />
<Separator orientation="vertical" className="h-4" />
{/* Header - also serves as titlebar drag region, adjusts padding when sidebar collapsed */}
<ContentHeader>
<span className="text-sm font-medium text-muted-foreground flex-1">
{headerTitle}
</span>
@ -1848,7 +1886,7 @@ function App() {
setIsChatSidebarOpen(true)
}
}}
className="text-foreground gap-1.5"
className="titlebar-no-drag text-foreground gap-1.5"
>
<SquarePen className="size-4" />
New Chat
@ -1859,7 +1897,7 @@ function App() {
variant="ghost"
size="sm"
onClick={() => setIsGraphOpen(false)}
className="text-foreground"
className="titlebar-no-drag text-foreground"
>
Close Graph
</Button>
@ -1871,14 +1909,14 @@ function App() {
variant="ghost"
size="icon"
onClick={() => setIsChatSidebarOpen(!isChatSidebarOpen)}
className="size-7 -mr-1"
className="titlebar-no-drag size-7 -mr-1"
>
<PanelRightIcon />
<span className="sr-only">Toggle Chat Sidebar</span>
</Button>
</>
)}
</header>
</ContentHeader>
{isGraphOpen ? (
<div className="flex-1 min-h-0">
@ -2056,6 +2094,8 @@ function App() {
onAskHumanResponse={handleAskHumanResponse}
/>
)}
{/* Rendered last so its no-drag region paints over the sidebar drag region */}
<FixedSidebarToggle />
</SidebarProvider>
{/* Floating chat input - shown when viewing files/graph and chat sidebar is closed */}

View file

@ -458,7 +458,7 @@ export function ChatSidebar({
{showContent && (
<>
{/* Header - minimal, expand and new chat buttons */}
<header className="flex h-12 shrink-0 items-center justify-end gap-1 px-2">
<header className="titlebar-drag-region flex h-12 shrink-0 items-center justify-end gap-1 px-2">
{onOpenFullScreen && (
<Tooltip>
<TooltipTrigger asChild>

View file

@ -267,9 +267,12 @@ export function SidebarContentPanel({
return (
<Sidebar className="border-r-0" {...props}>
<SidebarHeader>
<SidebarHeader className="titlebar-drag-region">
{/* Top spacer to clear the traffic lights + fixed toggle row */}
<div className="h-8" />
{/* Tab switcher - centered below the traffic lights row */}
<div className="flex items-center px-2 py-1.5">
<div className="flex w-full rounded-lg bg-sidebar-accent/50 p-0.5">
<div className="titlebar-no-drag flex w-full rounded-lg bg-sidebar-accent/50 p-0.5">
{sectionTabs.map((tab) => (
<button
key={tab.id}