From 6c4f37ef91bf68776f32eddaf41c75201c43c5d5 Mon Sep 17 00:00:00 2001 From: Claude Fable 5 Date: Sun, 12 Jul 2026 04:16:56 +0200 Subject: [PATCH] Make the sidebar width drag-resizable and persistent (#99) - SidebarResizer: pointer-drag handle on the sidebar's right edge, keyboard-adjustable (arrows, Home/End), double-click resets to the 16rem default; width clamped to 12-32rem - AppLayout persists the width via usePersistentState (ui.sidebar.width) and sets --sidebar-width inline on .app-body, so collapse/force-hide keep animating from/to the chosen width - localized aria-label (de+en), handle hidden while collapsed Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01EwZ4jR4KFAPvpjWevfUGX1 --- apps/web/src/layout/AppLayout.tsx | 13 +++- apps/web/src/layout/SidebarResizer.tsx | 86 ++++++++++++++++++++++++++ apps/web/src/styles/base.css | 16 +++++ packages/shared/i18n/de/common.json | 1 + packages/shared/i18n/en/common.json | 1 + 5 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 apps/web/src/layout/SidebarResizer.tsx diff --git a/apps/web/src/layout/AppLayout.tsx b/apps/web/src/layout/AppLayout.tsx index c274dd9..0a296ea 100644 --- a/apps/web/src/layout/AppLayout.tsx +++ b/apps/web/src/layout/AppLayout.tsx @@ -5,12 +5,19 @@ import { usePersistentState } from '../lib/use-persistent-state'; import { Footer } from './Footer'; import { SidebarChromeContext } from './sidebar-chrome'; import { Sidebar } from './Sidebar'; +import { clampSidebarWidth, SIDEBAR_DEFAULT_WIDTH_REM, SidebarResizer } from './SidebarResizer'; import { TopBar } from './TopBar'; export function AppLayout(): React.JSX.Element { const [sidebarCollapsed, setSidebarCollapsed] = usePersistentState('ui.sidebar.collapsed', false); + const [sidebarWidth, setSidebarWidth] = usePersistentState( + 'ui.sidebar.width', + SIDEBAR_DEFAULT_WIDTH_REM, + ); const [forcedHidden, setForcedHidden] = useState(false); const collapsed = sidebarCollapsed || forcedHidden; + // Clamp on read too — the persisted value may predate a bounds change. + const widthRem = clampSidebarWidth(sidebarWidth); // Ctrl/Cmd+\ toggles the sidebar (same shortcut as Notion), regardless of // which element has focus. @@ -32,8 +39,12 @@ export function AppLayout(): React.JSX.Element { sidebarCollapsed={collapsed} onToggleSidebar={() => setSidebarCollapsed(!sidebarCollapsed)} /> -
+
+ {!collapsed && }