Skip to content

fix(harness): keep the rail's footer panels and plan card readable at a narrow rail - #703

Open
devin-ai-integration[bot] wants to merge 7 commits into
mainfrom
devin/1787649876-profile-menu-narrow-rail
Open

fix(harness): keep the rail's footer panels and plan card readable at a narrow rail#703
devin-ai-integration[bot] wants to merge 7 commits into
mainfrom
devin/1787649876-profile-menu-narrow-rail

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Primary change type

  • Bug fix

Problem and motivation

Reported in #studio-feedback: the rail's profile menu paints its rows on top of each other. The panel opens with AnchoredPopover's matchWidth, so it inherited the rail's width — draggable down to RAIL_MIN (180px), narrower than "Open Sapiom dashboard" — and every long label wrapped inside a fixed 30px row and spilled over its neighbour.

Summary and scope

Takes the answer the studio rail reference already gives (design-eng/sapiom-studio/src/styles/rail.css): a rail footer panel is its own surface and never matches its trigger.

- <AnchoredPopover placement="up-start" matchWidth className="profile-menu">
+ <AnchoredPopover placement="right-end" besideRef={railRef} className="profile-menu">

  :root { --menu-w: min(var(--col-w), calc(100vw - var(--sp4))); --menu-pad-x: var(--sp3); }

  .profile-menu     { width: var(--menu-w); min-width: 240px;
                      padding: var(--sp2) calc(var(--menu-pad-x) - var(--row-bleed)); }
- .profile-menu-item { height: var(--tree-row-h); }
+ .profile-menu-item { height: auto; min-height: var(--tree-row-h);
+                      white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

- <AnchoredPopover placement="up-start" matchWidth className="settings-popover">
+ <AnchoredPopover placement="right-end" besideRef={railRef} className="settings-popover">

  .settings-popover { padding: var(--sp4);
+                     width: min(320px, calc(100vw - var(--sp4))); min-width: 280px; }

  .plan-card-readout { white-space: nowrap;
+                      overflow: hidden; text-overflow: ellipsis; }

Three things, all the reference's: its own width, its own ground (right-end + besideRef is this repo's spelling of the reference's right-bottom — a panel on the rail's own footprint reads as nothing having happened), and the roomier menu well. Row height becomes a floor rather than a cap, so a row that grows pushes the next one down instead of over it; AnchoredPopover's existing measured pass keeps the panel inside the viewport. railRef is now passed into ProfileRow — the rail's right edge is the one the panel must clear, exactly as the history flyer already does. The settings popover shares the footer trigger and the same failure mode, so it takes the same treatment at the reference's own 320px/280px.

The same drag exposed the footer's plan card (reported with a screenshot): its copy column shrinks correctly, but only the plan name clipped — the money readout kept its full width and painted under the Upgrade pill. It now truncates like the name above it, so the row degrades to $12.4… rather than to two overlapping strings; the CTA never moves.

Related work

Related issue or discussion: Slack #studio-feedback ("menu elements are overlapping")

Validation

pnpm --filter @sapiom/harness build                     — pass
pnpm --filter @sapiom/harness lint                      — 0 errors (1 pre-existing warning in src/server/rest.test.ts)
pnpm --filter @sapiom/harness typecheck                 — pass
pnpm --filter @sapiom/harness-desktop typecheck         — pass
pnpm exec playwright test … popover-crop                — 6 passed
pnpm exec playwright test … (full harness suite)        — 322 passed

Tests and documentation

web/e2e/popover-crop.spec.ts gains a case at the rail's minimum width that asserts each row is one line tall, that no label overflows its row, that rows stay vertically ordered, and that the panel holds its own readable width and clears the rail's right edge; the settings-popover case now asserts the same width floor and rail clearance. web/e2e/rail-footer-cards.spec.ts gains the plan-card equivalent: at the same width the readout must end before the Upgrade pill starts and must be ellipsised rather than overflowing. Reverting either CSS fix fails its test (measured, not assumed). No docs change: no user-facing behavior beyond the fix.

Compatibility and release impact

  • Breaking or externally visible changes: None beyond the visual fix.
  • Changeset: Added (.changeset/profile-menu-narrow-rail.md, @sapiom/harness patch).

Security

  • I have not included secrets, credentials, private data, or unsanitized logs.
  • This pull request does not publicly disclose a suspected vulnerability.

AI assistance

  • I used AI assistance and have described it below.

Written by Devin; the cause was traced in WorkflowsRail.tsx/AnchoredPopover.tsx, the fix mirrors the design-eng studio rail, and it is verified by the commands above plus a regression test confirmed to fail without the change.

Checklist

  • I read CONTRIBUTING.md, and this contribution follows the direct-PR or issue-first policy.
  • This pull request addresses one focused problem and contains no unrelated cleanup.
  • I added or updated tests.
  • I ran the relevant build, typecheck, lint, and test commands.
  • I updated documentation for user-facing changes, or marked it N/A above.
  • I added a Changeset for a published-package change.
  • I can explain and maintain every submitted change, including any AI-assisted work.

Link to Devin session: https://app.devin.ai/sessions/82c2067ac57342c4b21d948b507f3f23
Requested by: @ratataque

…rapping its rows

matchWidth pins the panel to the rail's width, so at RAIL_MIN labels like "Open Sapiom dashboard" wrapped inside a fixed-height row and read as overlapping items.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@github-actions github-actions Bot added bug Something isn't working contributor: external Pull request author does not have write, maintain, or admin access to sapiom-js needs-triage Awaiting maintainer review and classification size: small Review size is at most 100 changed lines area: studio Changes to Agent Studio or harness applications labels Aug 25, 2026
Match the studio rail reference: the footer menu never matches its trigger. It takes --menu-w with a 240px floor and its rows stay one line, so dragging the rail to its minimum no longer wraps labels over each other.
@devin-ai-integration devin-ai-integration Bot changed the title fix(harness): let the profile menu outgrow a narrow rail instead of wrapping its rows fix(harness): give the profile menu the studio rail's own readable width Aug 25, 2026
@github-actions github-actions Bot added contribution: incomplete Required pull request information is incomplete or ambiguous review: manual External pull request requires maintainer review before automation labels Aug 25, 2026
ratataque and others added 2 commits August 25, 2026 14:09
The rest of the studio rail reference's footer-menu anatomy: right-bottom beside the rail (harness's right-end + besideRef) and the roomier menu well, so the panel gets its own ground instead of floating on the rail's footprint.
…wn width

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@github-actions github-actions Bot added size: medium Review size is 101–500 changed lines and removed size: small Review size is at most 100 changed lines labels Aug 25, 2026
@devin-ai-integration devin-ai-integration Bot changed the title fix(harness): give the profile menu the studio rail's own readable width fix(harness): let the rail's footer panels outgrow a narrow rail instead of wrapping their rows Aug 25, 2026
…ar of it

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

Browser-verified: rail footer panels at RAIL_MIN (180px)

Ran the harness SPA locally in mock mode (VITE_MOCK=1, /?seed=0), dragged the workflows rail to its minimum with a real pointer drag (measured railW: 180), and exercised both footer panels.

🔴 Before (origin/main) — menu is 148px wide, sits on top of the 180px rail (x: 16), labels wrap and paint over each other, menu becomes internally scrollable:

before - profile menu wrapping and overlapping at 180px rail

🟢 After (this PR) — menu is 256px at x: 184, clears the rail's right edge, every row on one line:

after - profile menu single-line beside the rail

Measured on this branch at railW: 180:

panel x width clears rail inside viewport
profile-menu 184 256
settings-popover 184 320

All 5 profile rows report height: 30 with no text clipping and no overlap.

Recording of the full run:

recording

Light mode at the narrow rail

light mode profile menu at 180px rail
light mode settings popover at 180px rail

Regression checks
  • Wide rail (320px): profile menu x:324 w:256, settings popover x:324 w:320, bottom edges aligned to the footer row.
  • Dark and light mode at both rail widths.
  • Escape and outside-click dismissal both still work.
  • Settings controls usable (telemetry toggle flips, editor select opens, popover stays open).
  • Narrow browser window (888px app viewport): uncropped, rows single-line.
  • Full harness suite: 321 passed (popover-crop 6/6, dismiss 10/10).
⚠️ Two things a reviewer should know
  1. Mock mode never renders "Check for updates" or "Disconnect" in the profile menu — the first is desktop-host-gated, the second lives in the settings popover. Those two (the longest labels in the original report) were not visually verified here; worth one look in the Electron app.
  2. getBoundingClientRect() cannot detect this bug. On origin/main the rows still report clean, non-overlapping 30px boxes while the text visibly spills over the neighbouring row — the row height is fixed and the wrapped text paints outside its own box. Layout assertions for this need to be pixel-based or use scrollWidth > clientWidth.

ratataque and others added 2 commits August 25, 2026 15:50
…er the Upgrade pill

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…ormatting

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration devin-ai-integration Bot changed the title fix(harness): let the rail's footer panels outgrow a narrow rail instead of wrapping their rows fix(harness): keep the rail's footer panels and plan card readable at a narrow rail Aug 25, 2026
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

Browser-verified: plan-card readout truncation at RAIL_MIN (180px)

Ran the harness SPA in mock mode (VITE_MOCK=1, /?seed=0) at rail width 180 and A/B'd the change by
temporarily reverting only the two new declarations in .plan-card-readout.

Before → after at rail width 180px (dark)

🔴 Without the CSS — the readout is unclipped (scrollWidth === clientWidth === 86) and its box
overruns the Upgrade pill's left edge by 54.6px; Free plan / $12.40 / $50 paint straight
through the CTA:

before - readout painting through the Upgrade pill at 180px rail

🟢 With the CSS (this PR) — readout is clipped (scrollWidth 86 > clientWidth 24) and ends 8px
before
the pill; name, readout, pill and are four distinct elements:

after - readout ellipsised clear of the Upgrade pill

state clipped readout right Upgrade left clears pill
pre-fix CSS 114.4 59.8 ❌ (−54.6px)
this PR 51.8 59.8 ✅ (+8px)
Light mode at 180px, and the full readout at a normal rail

Light mode at 180px — identical geometry (51.8 vs 59.8, clipped):

light mode plan card at 180px rail

Rail reset to its 320px default — full $12.40 / $50 back, scrollWidth === clientWidth === 164:

wide rail showing the full readout

Also checked
  • Upgrade still hit-testable at 180px → lands on /settings?tab=billing.
  • overflow menu still opens at 180px with both billing links, dismisses on Escape.
  • Restoring HEAD's styles.css after the A/B removes the overlap again; tree left clean.
  • rail-footer-cards.spec.ts: 7 passed, incl. keeps the readout clear of the Upgrade pill at the rail's minimum width.
  • The narrow width was seeded (sapiom-harness-pane-widths{"rail":180} + reload, the same mechanism the spec uses) rather than dragged in this run; the drag reached 180 in the earlier run on this branch.
⚠️ Two notes for a reviewer
  1. The truncation is more aggressive than $12.4… — at 180px the copy column is only ~24px, so it
    renders Fr… / $1…. The overlap is fixed, but the readout conveys no information at the minimum
    width and there's no title/tooltip fallback. Worth deciding whether the card should instead wrap the
    CTA, hide the readout below a threshold, or expose the value on hover.
  2. The Upgrade anchor is target="_blank" but the click navigated the same tab in the plain SPA.
    The CTA works; the spec only asserts the attribute, never a real click.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: studio Changes to Agent Studio or harness applications bug Something isn't working contribution: incomplete Required pull request information is incomplete or ambiguous contributor: external Pull request author does not have write, maintain, or admin access to sapiom-js needs-triage Awaiting maintainer review and classification review: manual External pull request requires maintainer review before automation size: medium Review size is 101–500 changed lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant