Skip to content

Dock the companions as one full-height right plane - #1177

Open
PavelMakarchuk wants to merge 8 commits into
mainfrom
feat/side-panel-plane
Open

Dock the companions as one full-height right plane#1177
PavelMakarchuk wants to merge 8 commits into
mainfrom
feat/side-panel-plane

Conversation

@PavelMakarchuk

@PavelMakarchuk PavelMakarchuk commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

The draft reform panel and a report's adjust rail are the same shape — a column of provisions beside the content — but were built twice with diverging chrome (teal vs grey, chevron vs X-plus-floating-pill, 380 vs 340px, different sticky offsets). The adjust panel in particular read as a pop-up rather than part of the page.

What this is now

The right plane is a structural column of the shell, not a floating box. StandardLayout's flagship shell renders three columns — left sidebar, scrolling <main>, right plane — and the shared SidePanel portals its content into the third. Full height, immunity to the content's scrolling and wrapping, and correct behavior at any viewport width all follow from the layout rather than from tuned constants. Where the slot doesn't exist (tests, legacy shell) the panel renders in place.

Both companions consume SidePanel: teal chrome means unsaved work (the draft), grey means clean (the adjust rail). The draft opens by default and remembers its fold across navigations (sessionStorage); the adjust rail starts as a folded spine, because the report is what you came for. WorkspaceLayout and the report pages no longer hand-roll any rail geometry.

Review findings addressed

A high-effort review of the first iteration found 10 issues; all are fixed in the final commit, most made unrepresentable by the structural change: the inert sticky (panel scrolled off-screen), the mid-width wrap that dropped the opened panel below the report, the viewport-height overshoot, the fold resetting on every navigation, the open header's chevron wearing the folded orientation, the stale reveal-scroll that could yank the viewport minutes later, same-path folder reveals being state no-ops, bracket-sibling headers hovering in lockstep, the dropdown re-anchoring under the cursor, and the invisible hover hint truncating folder labels.

Also in this PR

  • Folder headers in search results look clickable (hover tint, underline, sliding arrow, "Open in tree" hint) and open the policy tree at that folder, expanded and scrolled into view, with results staying visible in flow
  • Bracket-index paths resolve to their real tree parent (...max[0]...max), which previously made the reveal a silent no-op for every rate schedule
  • The draft panel header reads "Draft reform" / "Editing reform" instead of narrating

Testing

105 tests across the flagship component suites (portal targeting, fold persistence across remount, reveal repeatability, hover affordances); 344 across flagship + libs; typecheck and eslint clean. Verified in the running app: the plane runs the full viewport height beside the content, stays put while the report scrolls (panelTop: 0, panelBottom: 700 with <main> scrolled), opens beside the report at 1000px where it previously teleported below, and holds its fold across Build → Reforms navigation.

🤖 Generated with Claude Code

PavelMakarchuk and others added 2 commits August 26, 2026 20:59
The build page announced "Loading the parameter index…" in static grey
text for the seconds it takes to fetch and index the US tree, which
reads more like a dead page than work in progress.

Both waits on the page — the search index and the policy tree — now
carry a spinner beside the same wording.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The draft reform and a report's adjust rail are the same shape — a
column of provisions beside the content — but they were built twice and
sat differently: teal card versus grey card, chevron versus X, a
floating pill versus a header toggle, 380px versus 340px, sticky at 0
versus at 24px. The adjust panel in particular read as a pop-up rather
than part of the page.

SidePanel now owns that chrome once: a column running the full height of
the shell beside the content, scrolling its own body, folding to a spine
that keeps its place in the layout rather than vanishing. Teal marks a
panel holding unsaved work, so the colour means something instead of
recording which component was written first.

The draft opens by default and the report's adjust rail starts folded —
the report is what you came for.

340px, not 380: at 380 the report row's 640px content column and the
panel no longer shared a flex line at 1280px, and the panel wrapped
underneath the report.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 26, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
policyengine-calculator-next Ready Ready Preview Aug 30, 2026 9:17am
policyengine-website Ready Ready Preview Aug 30, 2026 9:17am

Request Review

"Here's your draft reform" narrates at the reader every time they open a
page, and "Hand-built" beside it labelled the panel with trivia about
how the draft started. The header now says what the panel is — Draft
reform, or Editing reform — and nothing else.

The source note stays in the model, where the report carries it as
provenance.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three faults in the reveal, all visible the first time you use it.

The folder header gave no sign it was a control: same grey as the rows
above it, no hover state. It now tints, underlines and slides its arrow
on hover and focus, with an "open in tree" hint appearing beside it.

The results list floated over the page, so opening a folder scrolled the
tree underneath the very list that opened it. With the tree open the
results now sit in the page flow and the tree renders below them.

And the reveal quietly did nothing for bracketed parameters: a folder
path of `...eitc.max[0]` names a node the tree has no row for, since
brackets render inside their parent. The trailing index is dropped, and
the scroll now waits for a render where the row exists rather than an
animation frame that fires before it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The panel claimed to be a docked plane but was a floating viewport-tall
box inside the scrolling content, and the review caught the ways that
lie surfaced: its sticky had no travel (the wrapper shrink-wrapped to
the panel's own height), so it scrolled away with the page; between
~710px and ~1010px of content width the folded spine fit inline but the
opened panel wrapped below the entire report; and the hardcoded
calc(100vh - 48px) overflowed the scrollport by WorkspaceLayout's 16px
margin.

The plane is now structural. StandardLayout's flagship shell renders a
third column — left sidebar, scrolling main, right plane — and
SidePanel portals into it, so full height and immunity to the content's
scroll and wrapping follow from the layout rather than from tuned
constants. WorkspaceLayout and the report pages stop hand-rolling rail
geometry entirely. Where the slot does not exist (tests, the legacy
shell) the panel renders in place.

The rest of the review lands with it:

- The draft's fold persists across navigations (sessionStorage, opt-in
  per storageKey) instead of springing back open on every page change.
- The open header's chevron points down again; the extraction had
  hardcoded the folded rotation.
- A reveal that cannot find its row clears immediately instead of
  arming a stale scroll that could yank the viewport minutes later and
  taxing every tree toggle with a full DOM scan.
- Re-clicking a folder header reveals again (a sequence number rides
  along with the path, so same-path clicks are no longer state no-ops).
- Folder hover is keyed by the rendered group, not the bracket-stripped
  path, so Bracket 1 and Bracket 2 headers no longer light up together.
- Build keeps results in flow always rather than flipping anchoring
  mid-interaction under the pointer.
- The "Open in tree" hint renders only on hover — invisible, it still
  reserved width and truncated folder names — and is sentence case.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The panel header said "Draft reform" — a category label — while the
reform's actual name sat in an input a few rows down. The name is the
identity, the way a document title is: the header (and the folded
spine) now carry the reform's own name, live as it is typed, with "New
reform" before one exists. A small uppercase kicker — Draft, or Editing
— holds the state the old title carried.

Visually the plane now mirrors the left sidebar instead of shouting
next to it: the same flat gray surface, the same quiet border, the
kicker set like the sidebar's section labels, and teal reserved for
text the way the sidebar reserves it for the active item — no colored
bands.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The fold was an instant swap — panel gone, spine there, nothing moved.
Both faces now stay mounted inside one container whose width eases
between panel and spine while the faces crossfade; visibility (not just
opacity) keeps the hidden face out of the accessibility tree and tab
order, and prefers-reduced-motion turns the whole thing off.

In the draft panel, the name input moves to the top: it is the reform's
identity, and the header mirrors it as you type — burying it under the
provisions had it arriving last. The current-law note stays with the
provisions it describes.

The actions restack by weight: Run report takes the full row, Save and
Discard share the one below it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Opening a search result's folder revealed it in the policy tree — below
the dropdown, off-screen from where the reader was looking, so the
click appeared to do nothing. And the affordance was a hover-appearing
hint that shifted the row.

A folder header now flips the dropdown itself to the folder's contents:
a back row, the folder's name with its parameter count, and every
parameter it holds — the siblings the query missed are the point —
each addable in place. Editing the query or pressing Escape steps back
to the matches. The affordance is a constant chevron: folders open,
rows add.

The folder's display name is the longest breadcrumb prefix its
contents share, since the clicked header can sit deeper than the
folder itself (bracket indices fold into their parent).

The tree-reveal machinery this replaces — expandTo, the reveal
sequence, the pending scroll, the data-path hooks — is removed;
ParameterTreeBrowser is a plain expand/collapse browser again.

Co-Authored-By: Claude Fable 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