diff --git a/apps/x/apps/main/src/main.ts b/apps/x/apps/main/src/main.ts
index a77a8f7b..d2ad14e1 100644
--- a/apps/x/apps/main/src/main.ts
+++ b/apps/x/apps/main/src/main.ts
@@ -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,
diff --git a/apps/x/apps/renderer/src/App.css b/apps/x/apps/renderer/src/App.css
index 64d2b9a3..991236ea 100644
--- a/apps/x/apps/renderer/src/App.css
+++ b/apps/x/apps/renderer/src/App.css
@@ -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;
diff --git a/apps/x/apps/renderer/src/App.tsx b/apps/x/apps/renderer/src/App.tsx
index 56d953e3..5023da09 100644
--- a/apps/x/apps/renderer/src/App.tsx
+++ b/apps/x/apps/renderer/src/App.tsx
@@ -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 (
+
+ {/* Placeholder dots that show through when traffic lights are hidden (window unfocused) */}
+
+ {/* Sidebar toggle */}
+
+
+ )
+}
+
+/** Main content header that adjusts padding based on sidebar state */
+function ContentHeader({ children }: { children: React.ReactNode }) {
+ const { state } = useSidebar()
+ const isCollapsed = state === "collapsed"
+ return (
+
+ )
+}
+
function App() {
// File browser state (for Knowledge section)
const [selectedPath, setSelectedPath] = useState(null)
@@ -1816,10 +1856,8 @@ function App() {
selectedBackgroundTask={selectedBackgroundTask}
/>
- {/* Header with sidebar triggers */}
-
-
-
+ {/* Header - also serves as titlebar drag region, adjusts padding when sidebar collapsed */}
+
{headerTitle}
@@ -1848,7 +1886,7 @@ function App() {
setIsChatSidebarOpen(true)
}
}}
- className="text-foreground gap-1.5"
+ className="titlebar-no-drag text-foreground gap-1.5"
>
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
@@ -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"
>
Toggle Chat Sidebar
>
)}
-
+
{isGraphOpen ? (
@@ -2056,6 +2094,8 @@ function App() {
onAskHumanResponse={handleAskHumanResponse}
/>
)}
+ {/* Rendered last so its no-drag region paints over the sidebar drag region */}
+
{/* Floating chat input - shown when viewing files/graph and chat sidebar is closed */}
diff --git a/apps/x/apps/renderer/src/components/chat-sidebar.tsx b/apps/x/apps/renderer/src/components/chat-sidebar.tsx
index b5b32e09..876c7830 100644
--- a/apps/x/apps/renderer/src/components/chat-sidebar.tsx
+++ b/apps/x/apps/renderer/src/components/chat-sidebar.tsx
@@ -458,7 +458,7 @@ export function ChatSidebar({
{showContent && (
<>
{/* Header - minimal, expand and new chat buttons */}
-
+
{onOpenFullScreen && (
diff --git a/apps/x/apps/renderer/src/components/sidebar-content.tsx b/apps/x/apps/renderer/src/components/sidebar-content.tsx
index 4f7e65bc..bc6421b1 100644
--- a/apps/x/apps/renderer/src/components/sidebar-content.tsx
+++ b/apps/x/apps/renderer/src/components/sidebar-content.tsx
@@ -267,9 +267,12 @@ export function SidebarContentPanel({
return (
-
+
+ {/* Top spacer to clear the traffic lights + fixed toggle row */}
+
+ {/* Tab switcher - centered below the traffic lights row */}
-
+
{sectionTabs.map((tab) => (