Repository navigation
feat(web): redesign the topbar location strip as captioned segments - #659
Conversation
31af90e to
cbfa9a2
Compare
cbfa9a2 to
334d466
Compare
Rinat S (rsolmano)
left a comment
There was a problem hiding this comment.
Non-blocking. I've already approved, so this is your call. Feel free to ship as is.
I like the direction: a clickable topbar is the right idea, and the code reuses the tree's actions, dialog and picker well. My concern is scope. The topbar now copies almost everything from the Projects tree and the Changes panel. All 4 real bugs the bot found came from that: rename lost while offline, rename landing on the wrong workspace, delete targeting the wrong workspace, and the React Compiler bailout.
Suggestion: ship a smaller first version.
- Keep: project switcher, the workspace Switch to list, New workspace ⌘N, the PR chip, and the captions.
- Drop for now: Rename / Remove / Open in / Reveal / Copy path in the topbar (they stay in the tree), and the branch card (Compare to stays in Changes).
That's roughly 300 fewer lines (code + e2e), and no rename/delete race logic in the title bar. We can add more later if users ask for it.
Small things, whichever way you go (inline below):
- There's a divider between the logo and PROJECT. Both specs say there shouldn't be one.
- REMOTE only appears when an open PR/MR exists. Worth saying in the PR text and
locationBar/SPEC.md. - The description says Copy path moved into
workspaceActions.ts, but it's still inline inProjectTreeandWorkspaceSegment. Fine to keep, just fix the text.
…andon it on navigation
7a8a106 to
9e0d034
Compare
Problem
The strip after the ThinkRail mark —
project › workspace ⑂ branch · from base · PR #N— was one line of plain, draggable text. Nothing in it could be clicked, project and git state had the same visual weight, the PR number carried no status, and the only ways to switch project or workspace or act on a workspace lived in the Projects tree. A survey of competitor agentic IDEs (Conductor, T3 Code, Vibe Kanban, JetBrains Air, Superset, plus Zed / GitHub Desktop as classic references) showed every one of them treating this bar as a control surface rather than a label.Approach
Replace the text line with a row of captioned segments —
PROJECT · WORKSPACE · BRANCH · from base · REMOTE · PULL REQUEST— whose values are controls. The direction was chosen from a mockup gallery (six directions + three hybrids, rendered in the real 40px bar with our palette, type and action cluster, across themes, widths and workspace states); the pick combines GitHub Desktop's labeled segments, JetBrains/Zed-style dropdown pills, and status chips, with the base branch folded into the BRANCH caption.Decisions worth a reviewer's eye:
topbar-actions." Every pill, chip, link and the inline rename input iswindow-no-drag; captions and hairlines stay in the drag region, so the window can still be grabbed between controls.shell/SPEC.mdrecords this; a unit test pins it.editor.listand the project workspace-list load move fromProjectTreeintopanels/workspaceActions.ts(Copy path stays a one-linecopyText(worktreePath)at each call site) (plus a sharedRemoveWorkspaceDialog); the tree and the topbar menu call the same functions. The branch card's Compare to is the Changes panel'sBranchPickerbound toworkspace.setDiffBase, so the two controls cannot disagree.tr-text-ui, workspacetr-title-section, branchtr-code-text(mono). A newui.captionstyle (10px / 500 / compact / wide / uppercase) exists because eyebrow's 18px line cannot sit above a 22px value row inside 40px. Two colour tokens (feedback-warning-muted,feedback-info-muted) complete the chip hover set that success/error already had.pr.open(no session-less PR or push RPC), so the strip reports remote state and the plan pane keeps the action. The PULL REQUEST segment appears only with an open review; REMOTE only while something is unpushed or behind — and since the host attaches those counts toOpenBranchReviewalone, REMOTE can only ever appear alongside an open PR/MR (a branch without one shows no remote state).py-4, a 10px caption row,gap-2, a 20px value row — so there is a real margin above the caption and below the pill (the first cut letjustify-centerleave ~1px above the caption).sm, branch and remote belowmd; inside the row the branch shrinks first (shrink-[4]), then the project, then the workspace. The workspace segment never hides, which the existingtopbar-chromephone-with-insets geometry test enforces.Changes
apps/web/src/shell/locationBar/— new sub-module (SPEC.md, barrel exportsLocationBar):Segment(caption/value grammar,pillClass,chipClass),ProjectSegment(project switcher: projects · Project home · Add project… via the shareduseOpenProject),WorkspaceSegment(actions + inline rename + Switch to siblings with the running indicator + New workspace ⌘N, which opens the shell-owned dialog; subscribes to session state only while open),BranchSegment(branch card: copy, based-on, compare-to, remote + review rows; REMOTE chips open the same card) andReviewSegment(PR/MR chip link),locationModelhelpers.shell/Shell.tsx— mountsLocationBarin place of the inline text;shell/SPEC.mdgains the module entry and the rewritten Composition paragraph.panels/workspaceActions.ts(+RemoveWorkspaceDialog.tsx) — shared action/loader implementations;ProjectTree.tsxmigrated to them (its inline request helpers and confirm copy removed);panels/SPEC.mdnotes the sharing.typography.jsonui.caption→tr-text-caption;colors.jsonfeedback-warning-muted/feedback-info-muted; regenerated CSS;TYPOGRAPHY.mdand the typography test counts (49 → 50 styles);colorUsage.test.tslearnsborder-l-0is a width, not a colour.locationBar.test.tsx(segment grammar per state, chip link vs static chip, no-drag coverage, no raw type/colour, model helpers); newe2e/topbar-location.spec.tsjourney (switch via menu → rename in place → branch card copy + compare-to retarget mirrored in Changes → New workspace dialog → Project home and back → phone-width drop order → remove falls back to Default).e2e/history-search.spec.tsnow clicks the header's left padding as its "outside" target because the strip's centre is a control.Screenshots
Same scenario and viewport (fixture
sample-project, worktreeworkspace-1, 1280×720, dark theme); captured with the e2e harness onorigin/mainand on this branch.Topbar strip — before (
origin/main)Topbar strip — after
Full window — before / after
New surfaces (after only) — workspace menu, branch card, project switcher
Related issues
None.
Checklist
bun run lint,bun run typecheck,bun run testbun run e2e, orbun run e2e:fullwhen touching agent behavior)SPEC.md/ top-level specs updated to reflect any boundary, contract, or behavior changeTesting
Run on the rebased head (
origin/main= #666; re-run after the later rebase and the hairline fix):bun run e2e→ all 8 shards passed, 479 tests (174 s)bun testinapps/web→ 1338 pass (incl. the 8 newlocationBartests);bun test ./scripts/→ 91 pass;packages/ui→ 9 passbunx turbo run typecheck --filter='!@thinkrail/desktop'→ 20/20 successful.@thinkrail/desktop#typecheckcould not run locally: itselectrobun preparestep waits on the project build lock held by a running desktop dev process on this machine. The desktop package is untouched by this PR; CI runs it unblocked.bun run lint→ 1359 files, no issuesbun run check:deps,check:boundaries,check:seams,check:spec-surface(new spec enrolled),typography:check,colors:check,spacing:check,provider-glyphs:check→ all OKspec_validate→ only the two pre-existing danglingtask-*links (submodule-server-branch-review,submodule-server-pr)