Skip to content

Apply the workspace redesign to the sidebar and pane canvas - #59

Closed
BunsDev wants to merge 9 commits into
mainfrom
feat/pane-2d-tiling
Closed

Apply the workspace redesign to the sidebar and pane canvas#59
BunsDev wants to merge 9 commits into
mainfrom
feat/pane-2d-tiling

Conversation

@BunsDev

@BunsDev BunsDev commented Aug 10, 2026

Copy link
Copy Markdown
Member

Implements the Psyche Workspace Redesign in the macOS Tauri app, over the 2D pane tiling already on this branch.

The design ships as an interactive prototype plus ten spec boards covering the audit, tokens, component anatomy, the status matrix, the keyboard model and the responsive rules. This lands the sidebar and pane-canvas half of it.

The redesign

Tokens. The default theme now states the spec's neutral ramp (#0B0B0D → #1E1E24) and reserves violet for one job: marking where keyboard input goes. Selection became a neutral raised fill and status a worded chip, so the three signals that used to share one accent are finally distinct. The theme picker, vibrancy toggle and opacity slider keep working — the other five themes just restate their own ramp.

Session row. 44px, border-box. The leading glyph reports the lane's git state rather than duplicating the pane kind, which moves to the meta line beside the branch. Rows carry focus-set membership swatches — squares, so they can't be read as status dots — and the focused pane's row gets a violet bar layered over the neutral selection fill.

Pane frame. A 32px six-track header whose buttons never clip, with a span cycle (tiled → full column → full row) and a maximise/restore control. Panes degrade on their own width through container queries rather than the window's, dropping the meta line below 460px and the status word below 300px.

Focus sets. Multi-select from the + menu, a pick bar that exists only while picking, and a colour-coded subset that scopes the canvas. Clicking a member scopes to its set; the project header returns to all panes. Inside a set, × removes from the set rather than hiding the pane.

Focus mode + minimap. Maximise draws one pane and a 78px rail keeps the rest reachable. Esc restores the tiling.

Span, focus mode and set scoping are all derived layouts — spanLayout() and retainThreads() build them from the tiled tree without touching it. Leaving a mode restores exactly the tiling you had, and a pane opened or closed mid-mode lands in the tiling underneath.

Bugs found along the way

The first click on any pane-header button was swallowed. Focusing a pane on pointerdown re-rendered the canvas, which detaches and re-appends the panes — so the click target left the document before pointerup and no click fired. The close button had been special-cased for this; the guard now covers every header button. Caught in the browser, not in tests.

native/ was gitignored while 51 files under it were tracked. git add refused those files unless forced, and git add -A silently skipped every change to the desktop and iOS apps. Replaced with rules for what is actually generated (cargo's target, SwiftPM's .build, the Xcode derived-data path, Tauri's generated schemas). That ignore is also how editor.bundle.js came to be deleted from a working tree while index.html still had a script tag pointing at it — the app couldn't load its editor and git status had nothing to say about it. It's now tracked alongside its two siblings, with a test that fails if any bundle drifts from its sources.

Verification

  • Full suite 1496 passed, 3 failed — all three are the pre-existing release-signing failures (appStoreConnect, releaseWorkflow), which fail identically on a clean tree. Typecheck clean.
  • 57 tests added: spanLayout/retainThreads unit tests, span/maximise/set-scoping through the derived-root path, the container-query tiers, the row anatomy, the timed close confirm, and the bundle freshness check.
  • Driven live in headless Chromium with a stubbed Tauri bridge, no console errors: the 44px row, the six-track header (19px 698px 7px 22px 22px 22px), meta hiding at 363px, all buttons still visible at the 200px floor, span producing a full-height column, pick mode → create → scoped canvas → project header restoring all panes, and focus mode with a working minimap.
  • The freshness check was verified by breaking it three ways — a bundle edited in place, a source edited without rebuilding, and a referenced bundle deleted — and confirming each fails with a message naming the file and the fix.

Notes for review

  • The pane header's retry button had already been dropped in the shared checkout by concurrent work. That removal overlaps the header rewrite and could not be separated, so it and its matching test update are part of 9748976.
  • Focus sets are session-lived and scoped per project + worktree, since a set spanning two worktrees would have no single canvas to scope. Persisting them means extending the workspace snapshot schema and is left as a follow-up.
  • Still outstanding from the design, all in the tools dock: the split/stacked diff viewer with word-level highlights, dock tab pop-out and drag-to-canvas, and the voice call bar.

🤖 Generated with Claude Code

BunsDev and others added 9 commits August 10, 2026 07:43
The canvas could only stack panes vertically: layoutRects split purely on
height, insertBelow always appended below, and no operation could move a
pane once placed. Panes now tile in both directions and can be dragged
onto another pane's edge to re-tile.

Pane tree:

- Splits carry an `orientation`. "column" is the default by omission, so
  layouts persisted before this change still load as the vertical stacks
  they were.
- layoutRects is axis-generic, and minimumSize sums along a split's own
  axis while taking the max across it. The old minimumHeight only ever
  summed heights, so it could not describe side-by-side minimums.
- insertRelative places a pane above/below/left/right; moveLeaf prunes
  the pane from its old slot before re-inserting, so the branch it leaves
  collapses rather than keeping an empty slot. Impossible moves return
  the original root by identity so callers can detect a no-op cheaply.

Interaction:

- Pointer events, not HTML5 drag-and-drop: panes host live xterm
  canvases, where a native drag image reads as a rendering glitch, and
  owning the gesture lets the drop target be a region of a pane rather
  than the whole element.
- Nearest edge wins, giving four triangular drop zones meeting at the
  centre. Escape cancels; a lone pane never starts a drag.
- Dividers are orientation-aware for pointer drags (clientX vs clientY)
  and keys (left/right on a row, up/down on a column). ARIA reports the
  separator's own orientation, the opposite of its drag axis.

The drop highlight is position:fixed, since client rects are already
viewport-space - no positioned ancestor required and no pane's overflow
can clip it. It animates between targets so the gesture reads as
continuous, and holds still under prefers-reduced-motion.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The pane tree is imported dynamically, so its exports are untyped and
`.map((leaf) => ...)` callbacks tripped TS7006 under tsconfig.test.json.
Local `vitest run` never surfaced this - only `pnpm typecheck` does, which
is why CI caught it and the local run did not.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Two passes of the Psyche workspace redesign, covering the token layer, the
session row, the pane frame, focus sets and the focus-mode minimap.

Tokens. The default theme now states the spec's neutral ramp (#0B0B0D ->
#1E1E24) and reserves violet for one job: marking where keyboard input
goes. Selection became a neutral raised fill and status a worded chip, so
the three signals that used to share one accent are finally distinct. The
theme picker, vibrancy toggle and opacity slider keep working.

Session row. 44px, border-box. The leading glyph reports the lane's git
state rather than duplicating the pane kind, which moves to the meta line
beside the branch. Rows carry focus-set membership swatches -- squares, so
they cannot be read as status dots -- and the focused pane's row gets a
violet bar layered over the neutral selection fill.

Pane frame. A 32px six-track header whose buttons never clip, with a span
cycle (tiled -> full column -> full row) and a maximise/restore control.
Panes degrade on their own width through container queries instead of the
window's. Span, focus mode and set scoping are all derived layouts: none
of them edits the tiled tree, so leaving a mode restores exactly the
tiling you had, and a pane opened or closed mid-mode lands in the tiling
underneath.

Focus sets. Multi-select from the + menu, a pick bar that exists only
while picking, and a colour-coded subset that scopes the canvas. Clicking
a member scopes to its set; the project header returns to all panes.

Also fixes a latent bug: focusing a pane on pointerdown re-rendered the
canvas and detached the click target before pointerup, so the first click
on any pane-header button was swallowed. The close button had been
special-cased for this; the guard now covers every header button.

The pane header's retry button had already been dropped in this
checkout by concurrent work. That removal and its matching test update
overlap the header rewrite, so they are unavoidably part of this commit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
native/ holds the macOS Tauri app and the iOS app, and 51 files under it
are tracked -- but the blanket `native/` rule meant git pruned the whole
directory during a worktree scan. Two consequences: `git add native/...`
refused tracked files unless forced, and `git add -A` silently skipped
every change in the desktop and iOS apps. Anyone committing here had to
know to reach for -f, and anyone who did not simply lost the change.

Replaced with rules for what is actually generated: cargo's target dir,
SwiftPM's .build, the Xcode derived-data path, and Tauri's generated
capability schemas, plus the usual Xcode user-state directories. The
tauri package's node_modules was already covered by the global rule.

Verified no tracked file became ignored, the 4.6G target tree and the
82M .build tree are still ignored, and `git add` now works unforced.

This surfaces two files the blanket rule had been hiding, both left
untracked pending a call on them: the tauri package's pnpm-lock.yaml,
and web/editor.bundle.js -- whose two siblings, sessions.bundle.js and
panes.bundle.js, are both tracked.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
native/macos/psyche-build-tauri is a pnpm workspace member and there is no
shared-workspace-lockfile override, so the root pnpm-lock.yaml is the one
pnpm reads -- it already carries an importer entry pinning every dependency
of the desktop app.

The lockfile sitting inside the member is a leftover from running
`pnpm install` in that directory. Nothing consumes it, nothing keeps it in
step with the root, and tracking it would offer a second and silently
drifting answer to which versions the desktop app builds against.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
index.html loads three bundles -- editor, sessions, panes -- but only two
were tracked. The odd one out was invisible to git, which is how it came to
be deleted from a working tree while index.html still had a script tag
pointing at it, leaving the app unable to load its editor with nothing in
`git status` to say why.

The blanket `native/` ignore is what hid the gap: the file could never have
shown up as untracked while that rule stood, so the inconsistency had no way
to surface. With that rule gone, tracking it restores the invariant that
everything index.html references is in the repo.

esbuild's output is deterministic here -- two consecutive `pnpm build:web`
runs produce byte-identical bundles -- so this does not add diff churn.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The three web bundles are build output that lives in the repo, so nothing
stopped a commit landing with an edited source and a stale bundle beside it
-- the app would load last build's code and the diff would look right.

Rebuilds each bundle and compares bytes. The esbuild flags come from parsing
package.json's build:web rather than being restated here, so changing the
build cannot leave the check validating a command that no longer runs; the
rebuild simply follows it. Output goes to a temp dir, never over the tracked
bundle.

Also asserts every script index.html references exists on disk. That is the
failure the blanket `native/` ignore allowed: a referenced bundle was
deleted from a working tree with nothing in `git status` to show for it,
leaving the app unable to load its editor.

Verified by breaking it three ways -- a bundle edited in place, a source
edited without rebuilding, and a referenced bundle deleted -- and confirming
each fails with a message naming the file and the fix. Equal-length drift is
the usual shape, so the message reports the first differing byte rather than
just the size.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 10, 2026 14:02
@vercel

vercel Bot commented Aug 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
psyche-build-docs Ready Ready Preview Aug 10, 2026 2:02pm

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Applies the Workspace Redesign to the macOS Tauri web UI by updating theme tokens and restyling the sidebar session rows and pane canvas, while also extending the pane-tree model to support 2D tiling (row/column splits), derived layouts (span / focus-set scoping), and new interactions (drag-to-reposition, focus-mode minimap, focus sets).

Changes:

  • Updates theme tokens and CSS to implement redesigned session row anatomy, pane header layout, container-query tiers, focus-set pick UI, drag/drop affordances, and minimap styling.
  • Extends the pane tree to support split orientation, relative insertion/moves, derived layouts (spanLayout, retainThreads), and 2D layout math.
  • Adds/updates Vitest coverage around pane tree 2D tiling, span/focus modes, focus sets, and introduces a committed-bundle freshness check; fixes .gitignore to track native/ sources while ignoring generated outputs.

Reviewed changes

Copilot reviewed 12 out of 15 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
native/macos/psyche-build-tauri/web/styles.css Redesign tokens + sidebar/pane-canvas styling, container queries, drag indicator, focus sets UI, minimap styles.
native/macos/psyche-build-tauri/web/panes/pane-tree.mjs Adds split orientation + 2D layout, move/retain/span derived layout helpers, updated canFit/layout math.
native/macos/psyche-build-tauri/web/panes/pane-entry.js Re-exports new pane-tree APIs for the browser bundle.
native/macos/psyche-build-tauri/web/panes.bundle.js Rebuilt minified bundle for updated pane-tree exports/logic.
native/macos/psyche-build-tauri/web/main.js Wires new pane header controls, 2D tiling drag-to-reposition, focus sets, focus-mode minimap, status rendering changes.
native/macos/psyche-build-tauri/web/index.html Adds “Focus set — pick panes” menu item.
docs/superpowers/specs/2026-08-10-native-session-persistence-design.md New design/spec doc for native session persistence (not implemented in this PR).
docs/superpowers/plans/2026-08-10-native-session-persistence.md New implementation plan doc for native session persistence.
.gitignore Stops blanket-ignoring native/; ignores only generated build outputs + nested lockfile.
tests/tauriWebBundles.test.ts Adds a byte-level bundle freshness test that rebuilds bundles using build:web flags.
tests/tauriPhysicalPanes.test.ts Updates tests for new pane minimums and new span/maximize/focus-set derived-root behavior.
tests/tauriPaneTree.test.ts Adds coverage for orientation, relative insertion, move, retainThreads, and spanLayout.
tests/tauriCovenSessionSiderail.test.ts Updates siderail rendering harness/tests for git-state glyphs, focus sets, and timed close confirm.
tests/tauriCovenLaunch.test.ts Updates retry control expectations (moved from header button to context menu).

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +1278 to +1280
.terminal-pane-header { cursor: grab; }
body.is-pane-dragging { cursor: grabbing; user-select: none; }
body.is-pane-dragging .terminal-pane-header { cursor: grabbing; }
Comment on lines +1407 to +1417
.pane-minimap {
width: 78px;
min-height: 0;
display: flex;
flex-direction: column;
gap: 6px;
overflow-y: auto;
padding: 8px 6px 4px 0;
scrollbar-width: none;
}
.pane-minimap::-webkit-scrollbar { display: none; }
@BunsDev

BunsDev commented Aug 10, 2026

Copy link
Copy Markdown
Member Author

Superseded by #61, which is the same work rebased onto current main.

This branch was cut before main picked up #55 (2D tiling) and #56 (dock/diffs rework), so it carried commits duplicating both and conflicted in six files. #61 contains only the five redesign commits, applied on top of main.

@BunsDev BunsDev closed this Aug 10, 2026
@BunsDev
BunsDev deleted the feat/pane-2d-tiling branch August 10, 2026 14:30
@BunsDev
BunsDev restored the feat/pane-2d-tiling branch August 10, 2026 14:55
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.

2 participants