Give the title bar strip back its drag, and the rule its full height

The window's content runs the full height underneath the title bar, so
the shell covers the bar - and a webview covering the bar swallows the
gesture that moves the window. The strip standing in for it now says it
is a drag region.

That strip also moves inside each column instead of spanning both, so the
rule between the nav and the app runs the whole height of the window
rather than starting below the bar.
This commit is contained in:
2026-09-02 09:44:51 +02:00
parent aa19da48de
commit 0ae5746a30
3 changed files with 47 additions and 34 deletions
@@ -281,7 +281,14 @@ load — falls back to its initial in a tile of the same size, so the row never
## The window
The window keeps its ordinary macOS title bar, with the traffic lights where every other
window puts them. It follows the app's own Light or Dark choice through `set_theme`;
window puts them. Its content runs the full height *underneath* that bar, so each column
carries its own strip standing in for it — the nav's and the app's — rather than one
across the top. Two things follow from that, and both were bugs first:
- Those strips are **drag regions**. They cover the title bar, and a webview that covers
the title bar swallows the gesture that moves the window.
- They live **inside** each column rather than above both, so the rule between the nav and
the app runs the whole height of the window instead of starting below the bar. It follows the app's own Light or Dark choice through `set_theme`;
"System" hands it back to the OS, which is what System means.
An earlier version drew its own frame instead — a hidden title bar, a margin to grab, and
+34 -32
View File
@@ -195,13 +195,13 @@ export default function App() {
if (!config) return null;
return (
/* The inset is the nav's own colour, so the title bar above it and the
sidebar below read as one surface. */
<div
className="flex h-screen flex-col bg-white dark:bg-slate-900"
style={{ paddingTop: chrome }}
>
<div className="flex min-h-0 flex-1">
/* The window's content runs the full height, underneath the title bar. Each
column carries its own strip for that rather than one across the top, so
the rule between the nav and the app runs the whole height of the window
instead of starting below the bar. The strips are drag regions: they
cover the title bar, and without saying so they would swallow the gesture
that moves the window. */
<div className="flex h-screen bg-white dark:bg-slate-900">
<Nav
config={config}
activeId={activeId}
@@ -214,35 +214,37 @@ export default function App() {
onForward={() => activeId && api.historyGo(activeId, 1)}
onReload={() => activeId && api.historyGo(activeId, 0)}
unread={unread}
chrome={chrome}
/>
{/* The hole an app's native webview is positioned into. It stays empty
on purpose — anything drawn here would be painted over. */}
<div
ref={stageRef}
className="relative min-w-0 flex-1 overflow-hidden bg-slate-100 dark:bg-slate-950"
>
{backdrop && (
<img
src={backdrop}
alt=""
aria-hidden
/* Scaled up so the blur has no soft edge to give itself away. */
className="absolute inset-0 h-full w-full scale-110 object-cover blur-[16px]"
/>
)}
{config.apps.length === 0 && (
<div className="flex h-full flex-col items-center justify-center gap-3 px-6 text-center">
<p className="text-[13px] text-slate-500 dark:text-slate-400">
No apps yet. Add the tools you work in and they appear in the nav.
</p>
<button onClick={() => setSettingsOpen(true)} className={BTN_PRIMARY}>
Add your first app
</button>
</div>
)}
</div>
<div className="flex min-w-0 flex-1 flex-col">
<div data-tauri-drag-region className="shrink-0" style={{ height: chrome }} />
<div
ref={stageRef}
className="relative min-h-0 flex-1 overflow-hidden bg-slate-100 dark:bg-slate-950"
>
{backdrop && (
<img
src={backdrop}
alt=""
aria-hidden
/* Scaled up so the blur has no soft edge to give itself away. */
className="absolute inset-0 h-full w-full scale-110 object-cover blur-[16px]"
/>
)}
{config.apps.length === 0 && (
<div className="flex h-full flex-col items-center justify-center gap-3 px-6 text-center">
<p className="text-[13px] text-slate-500 dark:text-slate-400">
No apps yet. Add the tools you work in and they appear in the nav.
</p>
<button onClick={() => setSettingsOpen(true)} className={BTN_PRIMARY}>
Add your first app
</button>
</div>
)}
</div>
</div>
{offer && (
+5 -1
View File
@@ -15,6 +15,8 @@ interface Props {
onBack: () => void;
onForward: () => void;
onReload: () => void;
/** Height of the title bar the window's content runs underneath. */
chrome: number;
}
/**
@@ -29,7 +31,7 @@ export const navWidth = (collapsed: boolean) => (collapsed ? RAIL : PANEL);
export default function Nav({
config, activeId, collapsed, unread,
onSelect, onToggleCollapse, onOpenSettings, onToggleGroup,
onBack, onForward, onReload,
onBack, onForward, onReload, chrome,
}: Props) {
const groups = [...config.groups].sort((a, b) => a.order - b.order);
const inGroup = (id: string | null) =>
@@ -124,6 +126,7 @@ export default function Nav({
return (
<aside className={`${shell} items-center`} style={{ width: RAIL }}>
<div data-tauri-drag-region className="w-full shrink-0" style={{ height: chrome }} />
{/* Only the expander survives the rail's header. Back and forward are
a two-finger swipe and reload is ⌘R, so a toolbar here would be
clutter standing in for something nobody asked for. */}
@@ -178,6 +181,7 @@ export default function Nav({
return (
<aside className={shell} style={{ width: PANEL }}>
<div data-tauri-drag-region className="shrink-0" style={{ height: chrome }} />
<header className="flex items-center gap-0.5 border-b border-slate-200 px-1.5 py-2.5 dark:border-slate-800">
{controls}
<button