Skip to content

[Refactor] Reduce sidebar to two states: docked or hidden #343

@Astro-Han

Description

@Astro-Han

Background

The sidebar currently has three render modes:

Mode Trigger Behavior
A. Docked xl+ viewport, sidebar.opened=true Permanent column, main content shrinks
B. Rail + hover xl+ viewport, sidebar.opened=false Rail stays at 48px; hovering a project rail icon spawns a floating panel
C. Mobile drawer <xl viewport, mobileSidebar.opened=true Full-screen drawer overlay

Internally this is implemented as two persisted booleans (sidebar.opened, mobileSidebar.opened) plus a runtime state.hoverProject plus a createAim mouse-aim debounce plus an arm/disarm/navLeave 300ms grace timer plus a peek sub-state machine, all gated by xl: Tailwind viewport prefixes.

Why this is over-engineered

Three-question audit, user perspective:

1. Can it be cut?

  • Mode B serves no real user. It is a borrowed VSCode/Slack activity-bar pattern. PawWork's audience is "open-source AI agent for everyone" — non-technical knowledge workers. They either use the sidebar permanently (Mode A, the default) or they want the sidebar gone (and would press the toggle, not learn to hover a rail). The "rail-only with floating reveal" model is an IDE habit that does not map to agent management. ChatGPT, Claude.ai, and Cursor desktop apps all skip this mode.
  • Mode C serves no real user either. PawWork ships only as an Electron desktop app (packages/web does not exist; packages/console is upstream-only and not shipped). The only way to trigger Mode C is to drag the desktop window narrower than 1280px. On a 13" MBA half-snap that lands near 640px, on 14"+ MBP half-snap near 800px. Nobody actually manages agents in an 800px window — they either widen the window or quit. ChatGPT / Claude.ai / Cursor desktop all decline to ship a separate mobile drawer mode and rely on a window minWidth instead.

Both modes can be cut.

2. Is what remains good enough?

After removing B and C the sidebar collapses to two states:

  • Docked (default): occupies layout space, main shrinks
  • Hidden: removed from layout, main fills 100%, summon with mod+\

Same behavior across every viewport. This matches the ChatGPT / Claude.ai model and is straightforward to reason about.

3. Is it surprising in any bad way?

No. A single mod+\ toggle that always does the same thing is less surprising than the current "hover triggers floating panel that disappears if your mouse drifts" mechanic that today's aim + arm/disarm debounce is busy papering over.

Proposed change

Code

Delete:

  • state.hoverProject plus setHoverProject plus clearHoverProjectSoon plus createAim instance plus the arm / disarm / navLeave 300ms grace timer in packages/app/src/pages/layout.tsx
  • state.peek / peeked / peekt / peekProject (peek state machine, attached to Mode B)
  • sidebarHovering and sidebarExpanded derived memos and all 27+ downstream subscribers (collapse to plain sidebar.opened)
  • mobileSidebar store and its show / hide / toggle / opened API in packages/app/src/context/layout.tsx
  • The xl:hidden mobile drawer DOM and overlay in packages/app/src/pages/layout.tsx (around lines 2571-2596 in the current snapshot), and the xl:block desktop nav split (merged into a single nav)
  • The mobile menu icon trigger in packages/app/src/components/titlebar.tsx
  • The mobile?: boolean prop chain across pages/layout/pawwork-sidebar.tsx, pages/layout/sidebar-items.tsx, and any callers
  • Mobile-specific overlay and drawer rules in packages/app/src/index.css

Adjust:

  • Sidebar visibility comes from sidebar.opened only. When false, the sidebar is removed from the layout and main fills 100%. Summon with the existing mod+\ keybind; no floating overlay, no rail.
  • Raise minWidth from 480 to 768px in packages/desktop-electron/src/main/windows.ts. This guarantees a usable two-column layout for everyone and makes the "no mobile mode" decision honest. 768 keeps half-screen viable on 14"+ MBPs while 800+ would cut all MBP half-snap workflows.

Estimated impact

Roughly −400 to −500 net lines and the removal of one nontrivial state machine. No new dependencies. Stored layout retains a mobileSidebar key from this store; migrateStoredLayout in packages/app/src/context/layout.tsx will strip it on read.

Out of scope

  • PR refactor(app): grayscale execution polish — composer overlay, sidebar row, error page, right-panel divider #342 (current grayscale-execution PR) does not touch this. Land that first, then open a separate PR for this refactor.
  • Existing Mode A users keep the same default behavior. Existing Mode B users (if any) lose their preferred mode — acceptable given the audit above, but worth one line in the changelog.
  • A future "small-window adaptive layout" can always come back as a real responsive design if the data ever justifies it. Right now there is no data justifying it.

Verification plan when the PR opens

  1. Sidebar docked (default) → main shrinks, identical to today's Mode A.
  2. mod+\ once → sidebar hidden, main fills 100%, no rail visible, no floating overlay anywhere.
  3. mod+\ again → sidebar restored to its previous docked width.
  4. Resize Electron window to minWidth (768px) → sidebar still respects user's docked / hidden choice; no mobile drawer DOM rendered at any width.
  5. Search for sidebarHovering, sidebarExpanded, mobileSidebar, hoverProject, peek, createAim across packages/app/src — zero references should remain.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Medium priorityappApplication behavior and product flowsenhancementNew feature or requestplatformElectron shell, OS integration, packaging, updater, signing, paths, and permissionsuiDesign system and user interface

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions