Skip to content

Workspace tab strip and a grouped, pinnable workspace sidebar with PR badges - #201

Merged
pondorasti merged 14 commits into
mainfrom
codevisor/papaya
Oct 14, 2026
Merged

pondorasti merged 14 commits into
mainfrom
codevisor/papaya

Conversation

@pondorasti

@pondorasti pondorasti commented Oct 6, 2026 •

Copy link
Copy Markdown
Member

Summary

Brings back the horizontal workspace tab strip on macOS (removed in a0241c0) and rebuilds the sidebar around one row per workspace, which can be grouped, pinned and archived, and shows each workspace's pull request.

The refreshed sidebar is behind a Sidebar refresh feature flag (Settings ▸ Developer ▸ Feature Flags), off by default; with it off, the classic sidebar from main is unchanged.

The main commits:

  1. Bring back workspace tabs and list one sidebar row per workspace: the tab strip, workspace rows, and keyboard navigation.
  2. Sign dev relay certificates with SHA-256: a standalone dev-environment fix (see below). It can be split into its own PR if preferred.
  3. Group, pin, and archive sidebar workspaces; show their PRs and activity: grouping, pinning, the archive, and PR badges.
  4. Put the sidebar refresh behind a Developer feature flag: the flag, with the classic sidebar kept as the default.
  5. Render the sidebar with native vibrancy and collapsible Mail-style headings: native text rendering and the heading behavior.

Smaller follow-ups after these: the Properties menu, row and group context menus, and project folders that span machines.

Tab strip

  • Restores the Chrome-style strip (PaneTab, PaneTabStrip, WorkspaceTabBar), shown once a workspace has more than one listed tab.
  • Tabs keep live chat and terminal status icons.
  • A drag commits one synced moveTab on release.
  • Tabs that hold only hidden agent terminals stay out of the strip, ⌘1–⌘9, and tab stepping.
  • Selecting, closing and renaming a tab are also accessibility actions.

Sidebar

  • Rows: two lines, the name over project · worktree · machine. Parts a group heading already shows, or that repeat the name, are left out. The leading icon is the chat's own: its status when it has one (error, waiting, working, unread), else its model.
  • Group By (None / Project / Machine): groups are collapsible rows; project rows offer New Chat. The list heading reads Workspaces / Projects / Machines. Dragging reorders within a group, spliced back into the shared order.
  • Project folders span machines: they fold by repository through ProjectGroup, the same model as Settings › Projects and the composer's picker. Each machine's record of the same git remote is one folder; projects without a remote stay on their own. Rows in a folder name their machine when there is more than one, and the folder's New Chat starts on the default machine when the project lives there.
  • Row menu: Pin / Unpin and Rename, then Archive. Group rows offer Collapse / Expand and, for projects, New Chat.
  • Pinned: right-click → Pin moves a workspace into a Pinned section at the top. Saved per device.
  • Archived: a collapsed, paged section at the bottom (NavigationWorkspaces.archived). Clicking a row asks before restoring; the hover button and the menu restore directly.
  • Section headings (Pinned, Workspaces / Projects / Machines, Archived): each collapses, Mail-style, with a chevron at the trailing edge that shows on hover. Collapsed state is saved per device, and keyboard stepping skips rows in a collapsed section.
  • Options: right-click the list for Grouping ▸ and Properties ▸. Properties toggles each row's Updated time and PR badge.
  • Icons: model, PR and archive icons share one 14pt size and weight. The bundled Octicons are the 24px set, which matches SF Symbols' 1pt regular stroke at that size.

Native sidebar rendering

  • NavigationSplitView draws its sidebar column without the sidebar glass's vibrancy: .primary text came out dark grey (0.19) instead of black, and headings lighter than Mail's.
  • The sidebar content now renders in a nested NSHostingView inside the column (SidebarVibrancyHost), which gets the native vibrancy. It replaces the earlier black-at-27% heading color.
  • Heading titles draw with an AppKit NSTextField: SwiftUI rasterizes 11pt semibold about 10% lighter than AppKit, even from the same NSFont.
  • Measured against Mail with its own labels rendered through our views: row text black (0.000), and headings and the chevron (11pt semibold) pixel-identical in size and weight.
  • The chevron's color is about 7% lighter than Mail's, which may be a hover state in the reference capture.
  • The nested hosting view starts its own SwiftUI graph, so the sidebar passes in the app environment, theme, and an action for opening Settings.

PR badges

  • GET /v1/fs/git/pull-request?path=… runs the machine's own gh pr view for the current branch, with prompts disabled and a 15s timeout.
  • It returns number, title, state, draft flag, URL, and checks rolled up to passing / failing / pending / none.
  • Answers are cached 60s per folder, and concurrent requests share one gh call.
  • It returns null (never an error) on a detached HEAD, a non-GitHub remote, or when gh is missing or signed out.
  • The macOS app refreshes visible workspaces every minute and re-asks each one at most every 3 minutes.
  • The badge uses GitHub's Octicons (MIT, Resources/Octicons-LICENSE.txt): green open, red failing checks, orange running, grey draft, purple merged, red closed. Clicking opens the PR.
  • The Swift client tolerates older servers that don't send checks.

Keyboard

  • ⇧⌘[ / ⇧⌘] step sidebar items (New Chat, then workspaces) while the sidebar is showing, and the workspace's tabs while it's hidden.
  • ⌥⌘← / ⌥⌘→ step the workspace's tabs; ⌥⌘↑ / ⌥⌘↓ step workspaces.
  • Collapsed groups are skipped.
  • The items sit in main's Window menu (after 7ab245e). The directional Focus Split commands keep their View-menu items but lose their key equivalents.

Dev Cloud fix (scripts/dev-net.mjs)

macOS's /usr/bin/openssl is LibreSSL, which signs with SHA-1 by default. The tunnel's rustls rejects SHA-1 certificates, so every relay handshake failed (DecryptError) and the relay-only Dev Cloud showed "Can't reach Dev Cloud". Certificates are now signed with -sha256, and stale SHA-1 ones are regenerated automatically. Linux CI was never affected.

Testing

  • Full bun run check passes on this branch rebased onto main (2b51f6b): JS format, lint, ratchet, boundaries, typecheck, coverage (100% gates), dev and release script tests, Swift format and lint, Swift tests, macOS transcript tests, and the iOS build.
  • New tests: git-pull-request.test.ts (10, fake gh, including the environment fallback and malformed answers), the git-review route (3, including the shared lookup and the 60s cache expiry), ListReorderTests (group moves), and dev-net.test.mjs (SHA-256 certificates).
  • Rebase onto main: main removed agent terminal tabs (1ae2c95), so the tab strip, ⌘1–⌘9, and tab stepping now list every tab, and the classic sidebar carries main's versions of the files this branch had moved.
  • Manual, in the dev app:
    • Flag off: the classic sidebar (workspace headings with tab rows) and no tab strip, as on main.
    • Flag on: the tab strip with ⌘1/⌘2 hints, activating rows, the rename dialog, drag to reorder within a group, collapsing and expanding sections, the Grouping / Properties context menu, project folders merged across This Mac and a Dev Cloud, and PR badges for draft, open, failing-CI, merged, and closed PRs.
    • Native rendering measured against Mail: black row text, and headings and the chevron identical in size and weight.
  • Not covered: ⇧⌘[ / ⇧⌘] from automation (background keystrokes don't reach menu shortcuts), the compact drawer, dark mode, and custom themes with the flag on.

The PR badge fixtures (#421–#424) and their test/pr-badge-* branches are closed and deleted.

🤖 Generated with Claude Code

@pondorasti
pondorasti marked this pull request as draft October 13, 2026 21:00
@pondorasti pondorasti changed the title Bring back workspace tabs and list one sidebar row per workspace Workspace tab strip and a grouped, pinnable workspace sidebar with PR badges Oct 13, 2026
pondorasti and others added 14 commits October 13, 2026 16:59
The macOS workspace tabs move back into a horizontal strip above the
content, and the sidebar lists each workspace as a single row instead of
nesting its tabs.

- Tab strip: restores the Chrome-style strip removed in a0241c0 (edge
  stacking, scrolling, close stability, ⌘1–⌘9 hints). It shows once a
  workspace has more than one listed tab, keeps the live chat and
  terminal status icons, and a drag commits one synced move on release.
- Sidebar: one row per workspace with a rolled-up status (error, waiting,
  working, unread, or an idle dot), its machine, hover archive, and a
  New Tab / Rename / Archive menu. Rows can be grouped by project or by
  machine; drags reorder within a group.
- Keyboard: ⌥⌘←/→ and ⇧⌘[/] step tabs, ⌥⌘↑/↓ step workspaces. The
  directional Focus Split commands keep their menu items but lose their
  key equivalents.
- Tab stepping and ⌘1–⌘9 skip tabs that hold only hidden agent
  terminals, matching what the strip shows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ects

macOS's /usr/bin/openssl is LibreSSL, which signs `x509 -req` with SHA-1
unless told otherwise. The tunnel's TLS (rustls/webpki) rejects SHA-1
certificates, so every client's relay handshake failed (DecryptError) and
the relay-only Dev Cloud machine showed "Can't reach Dev Cloud". The
runner's own health check still passed, which hid it; Linux CI signs with
SHA-256 by default.

Every request and signature now passes -sha256, and the digest is recorded
beside the certificate names so certificates an earlier run signed with
SHA-1 are regenerated once, automatically.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Builds on the restored tab strip and one-row-per-workspace sidebar.

Sidebar
- Workspace rows are two lines (name over project · worktree · machine)
  with the chat's own leading icon: its status when it has one, else its
  model.
- Group By None / Project / Machine, as collapsible project and machine
  rows; the list heading names the grouping. Drags reorder within a group.
- Right-click to Pin: pinned workspaces get their own section at the top.
- An Archived section at the bottom, collapsed by default and paged,
  restores a workspace with a click (after confirming) or from its menu.
- The options menu nests Grouping and Show; Show toggles each row's
  Updated time and PR badge.

PR badges
- GET /v1/fs/git/pull-request asks the machine's own `gh` for the current
  branch's pull request (60s cache, shared in-flight lookups), with checks
  rolled up to passing / failing / pending / none. Null, never an error,
  when gh can't tell.
- The macOS sidebar refreshes visible workspaces every few minutes and
  shows GitHub's Octicons (MIT, bundled) colored by state and checks;
  clicking opens the PR.
- Row icons share one size and weight: 14pt, 1pt strokes.

Keyboard
- ⇧⌘[ / ⇧⌘] step sidebar items while the sidebar shows, and the
  workspace's tabs while it is hidden; ⌥⌘←/→ step the workspace's tabs;
  ⌥⌘↑/↓ step workspaces. Collapsed groups are skipped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Settings ▸ Developer gains a Feature Flags section with a "Sidebar
refresh" toggle, off by default and stored per device. Off, clients keep
main's sidebar (restored as ClassicSidebarView), its shortcuts (⌥⌘ +
arrows focus splits, ⇧⌘[ / ⇧⌘] walk its tab rows) and no tab strip. On,
the refreshed sidebar, tab strip and shortcuts apply; switching is live.

The refreshed sidebar's section headers use 11pt semibold measured
against Mail's, and let the split view's native sidebar material show
under the system theme.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…adings

NavigationSplitView draws its sidebar column without the sidebar glass's
vibrancy, so row text came out dark grey and headings lighter than Mail's.
The sidebar content now renders in a nested hosting view inside the column,
which gets the native vibrancy (measured against Mail: black row text,
identical headings), replacing the black-at-27% heading color.

Heading titles draw with an AppKit label, since SwiftUI rasterizes 11pt
semibold with thinner strokes than AppKit; they are now pixel-identical to
Mail's. Pinned, the list heading, and Archived all collapse the same way,
with a hover-only 11pt semibold chevron at the trailing edge. The Archived
count and the list heading's options button are gone; grouping and row
details live in the list's context menu.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…cons

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Synced machines carry the same logical project id, so a project on two
machines is now one folder rather than one per machine. Its rows name their
machine when there is more than one, and its New Chat starts on this Mac's
default machine when the project lives there. The folder heading no longer
names a machine, so the heading subtitle is gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Group-by-project now uses the app's own linking model, as Settings ›
Projects and the composer's picker do: every machine's record of the same
repository (its server-derived repo key) is one folder, whatever the
per-machine project ids. Projects without a remote stay on their own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main removed agent terminal tabs (1ae2c95), so every tab is listed again:
the tab strip, ⌘1–⌘9, and tab stepping use the workspace's tabs directly,
and the tab icon no longer has an agent-owned variant. The classic sidebar
files this branch had deleted and restored now carry main's versions, and
its tab rows drop the hidden-terminal filtering as main's did.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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