Skip to content

Inbox: the Workspaces sidebar (close, open, peek, ⌘B) - #1810

Merged
backnotprop merged 2 commits into
mainfrom
feat/inbox-sidebar
Oct 9, 2026
Merged

backnotprop merged 2 commits into
mainfrom
feat/inbox-sidebar

Conversation

@backnotprop

@backnotprop backnotprop commented Oct 9, 2026 •

Copy link
Copy Markdown
Owner

The Inbox window's sidebar is now Workspaces' own sidebar primitive, copied with the owner's word (2026-10-09, recorded in the Workspaces brief research/plans/plannotator-inbox-2026-10-06/briefs/inbox-sidebar.md). It closes and opens on the same spring. ⌘B / Ctrl+B toggles it and the choice is remembered. A rest on the left edge peeks it while it is closed. The edge collapses on a click and resizes on a drag. At a phone width it becomes a sheet. Only the mechanics and the motion came across. The skin stays the Inbox's own (inbox.css, the Tater mark, the sections).

Files copied (Workspaces repo, read-only source at ae582a66e; commit = last change to each file)

Plannotator file Workspaces source Commit Port
packages/inbox/shell/shell-motion.ts apps/web/src/lib/shell-motion.ts cd4a9bfd1 verbatim; imports motion/react; useRailMotion left out (no rail)
packages/inbox/shell/shell-animate.ts apps/web/src/lib/shell-animate.ts cd4a9bfd1 verbatim; imports motion/react
packages/inbox/shell/motion-style-div.tsx apps/web/src/components/ui/motion-style-div.tsx cd4a9bfd1 verbatim; imports motion/react
packages/inbox/shell/useResizablePanel.ts apps/web/src/hooks/useResizablePanel.ts a15a2839b verbatim except the width persists through Plannotator's cookie storage instead of localStorage
packages/inbox/shell/sidebar.tsx apps/web/src/components/ui/sidebar.tsx cd4a9bfd1 provider, ⌘B, accepted-width MotionValue, docked offcanvas motion, inert at settle, mobile sheet on the same spring: as is. Tailwind classes became ib- classes. The provider no longer reads or writes localStorage: the Inbox drives open. Icon mode, none, floating/inset, right side, SidebarRail, the menu/group/skeleton parts and TooltipProvider were left out. The sheet portals into the provider's wrapper so it keeps the Inbox tokens, and uses Base UI's Dialog directly.
(inside sidebar.tsx) PanelLeftIcon apps/web/src/components/icons/PanelLeftIcon.tsx 0219b4694 verbatim
packages/inbox/shell/SidebarPeek.tsx apps/web/src/components/sidebar/SidebarPeek.tsx + apps/web/src/hooks/useIsDesktopLg.ts 499aaa1d9, 4459a16b8 takes the navigation as children. The hidden panel also gets visibility: hidden once its slide-out ends, so the window holds one visible navigation.
packages/inbox/shell/CollapsibleEdge.tsx apps/web/src/components/CollapsibleEdge.tsx + apps/web/src/components/ResizeHandle.tsx 68afaf421, 227b573f2 Base UI Tooltip directly; left side only; always a separator
packages/inbox/shell/SidebarShell.tsx the sidebar half of apps/web/src/app/AppShell.tsx e2c77711d snap-close and edge click call the Inbox's onOpenChange directly instead of Workspaces' close/open refs. The page frame, search and assistant stay in Workspaces.
inbox.css (sidebar block) geometry from the files above; --ease-enter, --shadow-sheet, --shadow-tooltip, the scrim colour from apps/web/src/styles.css a9184e4f0 as --ib- tokens on :root (the edge tooltip portals to the body)

What else changed

  • packages/inbox/App.tsx: the shell wraps the Inbox's Sidebar (navigation) and main. open is the stored choice && !fileOpen. A file beside a thread (record 2.2) now closes the sidebar on the same spring instead of display: none, and the stored choice is left as it was. The old .ib-fileopen grid rules are gone.
  • packages/inbox/components/Sidebar.tsx: a pick in the phone sheet closes the sheet.
  • The toggle (Workspaces' trigger, "first control of the page header, open or closed") sits before the title on the list, the first run, Decisions and Settings.
  • The open state is the Inbox's own cookie plannotator-inbox-sidebar-open, read and written with ui's storage in App.tsx (open when absent), as the announcement cookies are. Nothing in packages/ui changes (door review: the shared registry fed the guides.show viewer's pinned manifest).
  • ⌘B / Ctrl+B is always prevented (Firefox's bookmarks sidebar never opens) and does not toggle while the key lands in an input, a textarea or contentEditable (event.composedPath()[0]), so typing in the reply box keeps the sidebar.
  • packages/inbox/package.json: motion ^12.38.0 and @base-ui/react ^1.7.0. These are the specs packages/ui already pins. The lockfile resolves the same motion@12.43.0, framer-motion@12.43.0 (the version Workspaces runs) and @base-ui/react@1.7.0, with no second copy (bun.lock +2 lines).
  • AGENTS.md: the Inbox section names the sidebar and the new spec.

Geometry values (not person-facing limits)

The default width is 232 px (the record). The drag range is 200 to 520 px, with Workspaces' 520 max. The min is below Workspaces' 240 so the record's width fits inside the range. The edge closes past 100 px (Workspaces' 0.5 ratio). It keeps 320 px of the page (Workspaces' reserveViewport). The width cookie is plannotator-inbox-sidebar-width. The peek shows from 1024 px, the sheet below 768 px, both as in Workspaces.

Bundle and binary size (same build commands, before = origin/main 545c7af)

Artifact Before After Delta
apps/hook/dist/inbox.html 20,950,986 B (gz -9 5,519,680) 21,002,709 B (gz 5,541,926) +51,723 B (gz +22,246)
apps/hook/dist/surface.html (build:surface) 20,218,145 B 20,218,145 B 0: byte-identical
compiled plannotator binary 147,228,002 B 147,277,538 B +49,536 B

The guides.show viewer build matches its pinned manifest (build:viewer then check:manifest: in sync).

Build: bun run --cwd apps/review build && bun run build:hook && bun build apps/hook/server/index.ts --compile --no-compile-autoload-bunfig --define '__CLI_VERSION__="0.0.0-dev"' --outfile <out>.

Proofs

  • tests/e2e/inbox-sidebar.spec.ts (new, in test:e2e:inbox, compiled binary, temp PLANNOTATOR_DATA_DIR and HOME, PLANNOTATOR_BROWSER=none), 7/7:
    • the toggle closes and opens, with the spacer width sampled every frame: 22 frames between 232 and 0, monotonic, never wider than 232; the list takes the width; the panel is inert at settle; the cookie flips
    • ⌘B and Ctrl+B; a reload restores closed with no travel
    • ⌘B in the reply box: the sidebar stays open, the box keeps focus and its text
    • the peek: nothing in the first 250 ms, the panel after the 600 ms rest, a click in it navigates, it hides on leave, and the stored choice stays closed
    • the edge: drag to 300, snap-close below 100 mid-drag, reopen when the held pointer comes back, arrows and Home, click to collapse
    • reduced motion: no intermediate frames
    • 600 px: the sheet, a pick closes it, ⌘B opens it, Escape closes it; no page errors
  • The whole test:e2e:inbox (every Inbox spec plus the new one) passes 80/80 against the compiled binary at the head.
  • Recording, PNGs (light and dark) and curves: research/operational/inbox-proofs/sidebar-2026-10-09/ in the Workspaces meta layer (inbox-sidebar-e2e.mp4, open-*.png, closed-*.png, peek-*.png, narrow-sheet-*.png, curves.json).
  • Side by side with Workspaces: side-by-side-strip.png in the same folder. It compares Workspaces' own unmodified primitive, mounted in a harness in a temp clone at ae582a6 and served by Vite on free port 49224, with the Inbox. Both are captured at the same instants after the click under Playwright's fake clock. On the real clock, the per-frame widths match frame for frame within one frame's alignment. Both settle in 21 frames: close 226.3/215.8, 112.7/110.0, ... 0.3/0.3, 0. The harness and script are in frames/.

Gates

  • bun run typecheck green.
  • bun test packages apps/hook tests scripts under a temp data dir: 5876 pass. One unrelated failure, packages/server/agent-terminal.test.ts "node sidecar rebuilds spawn options", read a JSON file before it was written; it passes alone (5/5).
  • bun run check:release-version green.
  • Gitleaks over origin/main..HEAD: no leaks.
  • Semgrep with .semgrep.yml (the CI image) over the changed source: 0 findings.

Left out

  • The Workspaces sidebar's content (search, the project tree, the your-turn dot).
  • The page frame's rounded corner.
  • The icon rail mode.
  • Any new DOM test.

Deviations from the window record

  • The record draws a fixed 232 px sidebar that is always visible. This PR adds the owner's 2026-10-09 ask: close and open, ⌘B, peek, the toggle in each page header (Workspaces' placement), resize, and the phone sheet.
  • A file beside a thread closes the sidebar on the spring rather than removing it.

@backnotprop
backnotprop merged commit d36d737 into main Oct 9, 2026
32 checks passed
@backnotprop
backnotprop deleted the feat/inbox-sidebar branch October 9, 2026 17:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant