Repository navigation
feat(web): Minimal interface with edge tool rails, and remount-free panel toggles - #680
Merged
Merged
Conversation
danyaberezun
requested review from
Olga Lavrichenko (OLavrik),
Rustam Sadykov (SBOne-Kenobi) and
Rinat S (rsolmano)
as code owners
October 8, 2026 08:28
danyaberezun
force-pushed
the
workspace-archive-research
branch
from
October 8, 2026 16:15
5f57262 to
935502d
Compare
danyaberezun
force-pushed
the
modern-interface-design
branch
from
October 9, 2026 14:17
11cedb5 to
62fe5ce
Compare
danyaberezun
added a commit
that referenced
this pull request
Oct 9, 2026
danyaberezun
force-pushed
the
modern-interface-design
branch
from
October 9, 2026 21:03
3b04ff2 to
9b921d9
Compare
danyaberezun
force-pushed
the
modern-interface-design
branch
from
October 9, 2026 21:06
9b921d9 to
9d291cf
Compare
danyaberezun
force-pushed
the
modern-interface-design
branch
from
October 9, 2026 21:06
9d291cf to
ae9def2
Compare
…, and remount-free panel toggles Projects, Specs, Files, Changes and Review move from per-pane tab strips to persistent left/right/bottom edge rails. The rail renders the layout's group model instead of hiding it: entries come in clusters, one per pane in document order, separated by a hairline; one pressed icon per open pane; removed tools sit dimmed in a trailing cluster. Drops follow the clusters (entry halves insert into that pane, a "New pane here" zone at every boundary creates one) and the pane header title drags its tool. Terminal groups keep named session tabs, and Project Home composes the same rail and pane header. Tools and terminals never share a pane (canJoinAuxiliaryGroup): enforced in every mutation, in normalization on load and across workspaces, in passive terminal placement and preset remaps. The mixed-order anchors (beforeToolByTabId) and terminal recall (lastTerminalByGroup) go with it. Menus name a pane by its contents, hide commands that can never apply to a tab, and the layout owns New terminal / New terminal pane below. Panels no longer remount on shape changes: structural-only panel-group keys, committed sizes re-applied imperatively, and topology-dependent minimums relaxed for the one membership-change commit (useTopologySettled). Themes: the standard Light/Dark and High Contrast palettes are unchanged; the quieter Minimal palettes ship as Minimal Light / Minimal Dark. A theme is a palette and role usage never varies by theme. The rails live in the split workbench renderer (workbenchRails.tsx and friends); the interface design lab that produced the direction stays out of the repository (design/ is gitignored). The spec-graph index also skips the scratch directories other assistants keep in a project (.claude, .superpowers, .gsd, .mockups, .playwright-mcp), whose plans and generated skills carry spec-shaped frontmatter. README screenshots are re-captured from this head (the real project in an isolated host: workbench light/dark and the Specs tool), with alt text that names the edge rails.
danyaberezun
force-pushed
the
modern-interface-design
branch
from
October 9, 2026 21:39
ae9def2 to
6f46f6b
Compare
2 of 5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The workbench's visual language had drifted behind contemporary agentic IDEs: tool panels were tab strips with heavy framing and the start screen (Project Home) used a different chrome than the workspace view. Separately, every click on an edge rail or panel toggle re-rendered the whole application: each fold/hide/show of any region remounted every
ResizablePanelGroupand everything inside it — chat transcript (all turns re-parsed), editors, terminals, tool panes — ~1,900 component mounts and 45–65 React commits per click, and a mere focus event into the composer re-rendered the entire workbench chrome.Approach
Minimal direction, style not UX. After a source-linked study of T3 Code, Conductor, Vibe Kanban, Claude Code desktop, Codex, Cursor, VS Code, IntelliJ, Zed and Linear (an isolated research lab with mockups of six directions and three style skins; kept out of the repository —
design/is gitignored — because it is a research artefact with its own tokens and fonts, not product code), the user chose the Minimal treatment with IntelliJ-style edge tool rails. The workbench arrangement, resource tabs and workflows are unchanged: Projects / Specs / Files / Changes / Review move to persistent edge rails (Fill icon + accent edge when shown, activate again to hide), terminal groups keep named session tabs, and Project Home gets the same rail + plain pane header through primitives exported fromshell/layout, so the start screen and the workspace share one chrome vocabulary.A theme is a palette. The standard Light/Dark and both High Contrast themes render exactly as on
main. The quieter Minimal palettes (neutral surface hierarchy, restrained green, neutralbubbleAccent) ship as two additional themes — Minimal Light and Minimal Dark — selectable in Appearance. Role usage never varies by theme: no component branches on the active theme or its contrast metadata to drop a fill or a border, and what a manifest key cannot express (frameless bubbles, fill-less selection) is a product decision for all themes rather than a theme — so those Minimal-study ideas were deliberately not shipped.themes/SPEC.mdrecords the rule.No remount on shape changes. react-resizable-panels 2.1.9 reconciles added/removed panels in place when panels carry
id+order, so theprojectionEpoch/topology keys on every panel group were unnecessary. Panels now change membership in place, the bottom column is one always-mounted vertical group with a conditional bottom panel, committed sizes are re-applied imperatively, and topology-dependentminSizes are relaxed for the single membership-change commit (useTopologySettled) because the library re-evaluates a panel's constraints against its not-yet-recalculated layout and otherwise throws (Panel data not found) on alignment changes. Focus events no longer write identical attention objects (sameLayoutAttention),useWorkspaceRead.reloadis memoized, and tool-pane elements are built once per workspace so tool bodies bail out.The rail mirrors the panes; tools and terminals never share one. A first review of the rails against the layout grammar found that the group model every gesture runs on had become invisible: the rail was one flat icon list per side, the pane header showed only the selected tool's name, the only drag handle was the 32 px edge icon, and a tool dropped into a pane silently replaced the one on screen. Rather than change the frame model (IntelliJ's two-slot sides) or bring tab strips back, the rail now renders the model: icons come in clusters, one per pane in document order, separated by a hairline; one pressed icon per open pane; removed tools sit dimmed in a trailing cluster. Drops follow the clusters — an entry's halves insert into that pane, a New pane here zone at every cluster boundary creates one — and the pane header title is a drag handle for its tool, so a user who looks at the pane finds something to grab. Mixed tool + terminal panes are gone (
canJoinAuxiliaryGroup, enforced in every mutation, in normalization on load and across workspaces, in passive placement and preset remaps); with them went the mixed-order anchors (beforeToolByTabId) and terminal recall (lastTerminalByGroup) — a terminal pane's selection is its recall. Menus name a pane by its contents (Move to right pane (Changes, Review)), hide commands that can never apply to a tab, and the layout owns New terminal / New terminal pane below.layout/SPEC.mdrecords the rules; the analysis that led here is summarised in the PR discussion.Rebased on
main(#659, #663 and #682 have landed). The rails live in #682's split renderer:workbenchRails.tsx(region rail + droppable entry),AuxiliaryGroupHeaderinworkbenchSide.tsx, the settle/enforce/focus hooks and rail DOM ids inworkbenchShared.tsx. Three deliberate adaptations to #682's render-isolation model are documented inlayout/SPEC.md: the rail receivesattentionexplicitly like a stack (its entries render from selection), the pane header's terminal strip selects throughonSelectTab, andonUserNavigationis back onSharedGroupPropsas a stable callback.e2e/perf/layoutIsolation.perf.tsstill passes. The README and its screenshots are left asmainhas them; they should be re-captured at retina size after this lands.Changes
apps/web/src/shell/layout: persistentAuxiliaryRails rendered as per-pane clusters (clusterAuxiliaryRail, hairline separators, dimmed unplaced cluster) with "New pane here" boundary drop zones; plainAuxiliaryPaneHeaderwhose title drags its tool, + Hide control;workbenchRails.tsx;canJoinAuxiliaryGroup/SEPARATE_PANES_REASON/describeLayoutGroup;normalizeWorkspaceView; structural-only panel-group keys,useEnforcedLayout,useTopologySettled; SPEC gains Shape changes without remounts, the pane-cluster rail grammar, the tool/terminal pane rule and the module structure.apps/web/src/shell/ProjectHome.tsx(new) replaces the old welcome branch andCollapsedPanelRail;useCollapsibleRegionis one toggle shared by the rail entry, Hide andMod+B.apps/web/src/shell/layoutState:applyLayoutAttentiondrops no-op writes;libgainssameLayoutAttention.apps/web/src/shell/WorkspaceWorkbench.tsx: tool bodies memoized per workspace;panels/useWorkspaceRead.ts: stablereload;renderSideMenuActionsremoved — the layout renders New terminal / New terminal pane below itself throughonNewTerminal(groupId, area, { newPaneBelow })→addTerminal→place-terminalintent.themes/bundled/minimal-dark.theme.jsonandminimal-light.theme.jsonadded;dark/light/both High Contrast manifests byte-identical tomain.colors.jsongains thefeedback-info-muted/feedback-warning-mutedsteps the settled-reason badges and topbar chips use. Selection on tabs and rail entries iscontrol-bg-selected+ accent marker in every theme (no per-theme selectors). Deadanimate-fade-inremoved.tool-rails(cluster order, boundary vs insert drops, header drag, tool-over-terminal refusal, dimmed unplaced restore, fold/reload of a terminal pane's selection),rail-layout-regressions,project-home-chromespecs (incl. a DOM-continuity guard that fails if any panel group remounts); mixed-pane journeys inlayout,bottom-panel,close-quit-shortcuts,workspace-tabsrewritten to the pane grammar;theme.spec.tspins the selected-tab/rail-entry surface and marker under every manifest;perf:rendergains aregion-togglesscenario;review.spec.tsgains the composer-closed wait its sibling already had (pre-existing flake).perf:rendersix-toggle scenario 83.7 ms → 29.8 ms median.Screenshots
Same isolated fixture, 1600×1000, bottom terminal hidden in both. Before =
main(830de941), after = this branch. The standard palettes are identical on both sides by design — only the chrome changes.Rail grammar (after only — the rail is new): dragging Files paints "New pane here" zones at every pane boundary, and dropping on one opens a third pane; the right rail shows three clusters.
New themes (no "before" exists):
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
On the final tree:
bun run lint→ clean (1443 files);bun run check:deps/check:boundaries/check:seams/check:spec-surface→ OK;cd apps/web && bun run typography:check && colors:check && spacing:check && provider-glyphs:check→ OKbun run typecheck --filter='!@thinkrail/desktop'→ 19 tasks green. The desktop task (electrobun prepare && tsc) cannot run on this machine while the ThinkRail dev app holds the electrobun build lock; no desktop file changes in this PR — CI covers it.bun run test→ 20 packages green (web 1615; server 1401 when run alone — twoagentSessionManagercases are load-flaky under the parallel turbo run and are byte-identical tomain)bun run e2e(complete no-agent suite) → all 8 shards passedbun run perf:render --runs 1 e2e/perf/layoutIsolation.perf.ts(refactor(web): split Workbench and isolate layout rerenders #682's isolation proof) → 2 passed