From aa77d35258b6fbc2eff95f0258d67e7dca4d59c3 Mon Sep 17 00:00:00 2001 From: Ethan Graf Date: Sun, 28 Jun 2026 11:42:22 -0400 Subject: [PATCH] Move split controls to the title bar + title-bar/sidebar polish MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Split Right / Split Down move from the bottom bar to the top bar as icon buttons (SplitRightIcon / SplitDownIcon) with hover tooltips, left of the workspace selector, shown only in tiling mode. Wired through the tiling handle (App.handleSplitRight/Down). - Title bar: compact fixed-height buttons (size-5) with header padding so hover highlights no longer reach the screen edge; tooltip + pointer cursor on the sidebar toggle, split buttons, and workspace menu. - Sidebar: fix asymmetric collapse animation — the open state now also transitions width, so re-opening animates instead of snapping. Co-Authored-By: Claude Opus 4.8 --- src/App.tsx | 11 +++++++++ src/components/AppSidebar.tsx | 8 ++++--- src/components/AppTitleBar.tsx | 34 ++++++++++++++++++++++++++-- src/components/WorkspaceMenu.tsx | 2 +- src/icons/index.tsx | 38 ++++++++++++++++++++++++++++++++ src/workspaces/tiling/tiling.tsx | 22 ++---------------- 6 files changed, 89 insertions(+), 26 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index c3933e8..24b8511 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -320,6 +320,15 @@ function AppShell() { [handleOpenEntry], ); + const handleSplitRight = useCallback( + () => tilingHandleRef.current?.splitRight(), + [], + ); + const handleSplitDown = useCallback( + () => tilingHandleRef.current?.splitDown(), + [], + ); + const handleNewFile = useCallback(async () => { const provider = resolveProvider(selectedProviderId); if (!provider) return; @@ -402,6 +411,8 @@ function AppShell() { onToggleSidebar={() => setSidebarOpen((o) => !o)} workspaceKind={workspaceKind} onWorkspaceKindChange={setWorkspaceKind} + onSplitRight={handleSplitRight} + onSplitDown={handleSplitDown} />
void; workspaceKind: WorkspaceKind; onWorkspaceKindChange: (kind: WorkspaceKind) => void; + onSplitRight: () => void; + onSplitDown: () => void; }; function SidebarToggleIcon({ className }: { className?: string }) { @@ -44,12 +47,14 @@ export function AppTitleBar({ onToggleSidebar, workspaceKind, onWorkspaceKindChange, + onSplitRight, + onSplitDown, }: AppTitleBarProps) { const noDrag = { WebkitAppRegion: 'no-drag' as const }; return (
@@ -57,15 +62,40 @@ export function AppTitleBar({ type="button" aria-controls="app-sidebar" aria-label={sidebarOpen ? 'Hide sidebar' : 'Show sidebar'} + title={sidebarOpen ? 'Hide sidebar' : 'Show sidebar'} aria-pressed={sidebarOpen} onClick={onToggleSidebar} - className="text-foreground hover:bg-border/40 rounded p-1.5" + className="text-foreground hover:bg-border/40 flex size-5 cursor-pointer items-center justify-center rounded" style={noDrag} >
+ {workspaceKind === 'tiling' ? ( + <> + + + + ) : null}
diff --git a/src/components/WorkspaceMenu.tsx b/src/components/WorkspaceMenu.tsx index 9016692..0f102bd 100644 --- a/src/components/WorkspaceMenu.tsx +++ b/src/components/WorkspaceMenu.tsx @@ -45,7 +45,7 @@ export function WorkspaceMenu({ value, onChange }: WorkspaceMenuProps) { aria-expanded={open} aria-label="Workspace" onClick={() => setOpen((o) => !o)} - className="text-foreground/90 hover:bg-border/40 focus-visible:ring-accent/50 inline-flex items-center gap-1 rounded px-2 py-1 text-xs outline-none focus-visible:ring-2" + className="text-foreground/90 hover:bg-border/40 focus-visible:ring-accent/50 inline-flex h-5 cursor-pointer items-center gap-1 rounded px-2 text-xs outline-none focus-visible:ring-2" > Workspace: {current?.label ?? value} diff --git a/src/icons/index.tsx b/src/icons/index.tsx index 3922c4e..99fa644 100644 --- a/src/icons/index.tsx +++ b/src/icons/index.tsx @@ -217,3 +217,41 @@ export function NewDirectoryIcon({ className }: IconProps) { ); } + +/** Split the pane to the right: a frame divided by a vertical line. */ +export function SplitRightIcon({ className }: IconProps) { + return ( + + + + + ); +} + +/** Split the pane downward: a frame divided by a horizontal line. */ +export function SplitDownIcon({ className }: IconProps) { + return ( + + + + + ); +} diff --git a/src/workspaces/tiling/tiling.tsx b/src/workspaces/tiling/tiling.tsx index a462254..6dcec75 100644 --- a/src/workspaces/tiling/tiling.tsx +++ b/src/workspaces/tiling/tiling.tsx @@ -544,26 +544,8 @@ export function TilingWorkspace({ {renderNode(tree)}
-
- - -
- Active: {activeTileId} -
+
+ Active: {activeTileId}
-- 2.51.2