fix(mobile): the session rail is the workspace's screen, not a drawer - #247
Open
pythonlearner1025 wants to merge 1 commit into
Open
fix(mobile): the session rail is the workspace's screen, not a drawer#247pythonlearner1025 wants to merge 1 commit into
pythonlearner1025 wants to merge 1 commit into
Conversation
Below 900px the rail slid in at min(88vw, 348px) over the chat, behind a scrim. It now fills the width, and a workspace opens on it. Picking a session hides it, so the pick reads as a page change. Four more phone defects go with it: - Create workspace opened at z-index 100 under the rail's z-index 400. Its handler now closes the rail first, like its five siblings. - The create-workspace footer was pinned and ate 70px of every screen. Its buttons also hugged the right edge, because the base rule's justify-content survived the mobile override. The dialog now scrolls as one page, the header sticks, and the buttons end the form. - The member row is an eight-column grid that dropped to three columns at 620px, so five children auto-placed into 28px columns and overlapped. The four machine controls now share one wrapper: display: contents on desktop, a wrapped band on a phone. - The scrim can no longer cover anything, so it is deleted rather than hidden. Desktop does not move. Every edit sits inside an existing mobile media query or behind mobileWebApp, and test/mobile-only-styles.test.ts proves it by parsing the stylesheets. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019ZvBaFqU4mgX75HAbiYJYV
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.
What part this touches
The webApp draws a workspace as three columns. Column one is a 48px strip of
workspace tiles. Column two is the session rail. Column three is the work area.
Below 900px the shell changes shape.
ShellNav's wrapper stops beingdisplay: contentsand becomes an off-canvas drawer that carries the strip andthe rail together. A hamburger in the pane chrome opens it. A scrim closes it.
Three dialogs sit above that shell: create workspace, workspace details, and the
shared modal screen. Each one is a fixed overlay with its own z-index.
This PR only changes what a phone sees. Desktop is untouched, and a test proves
it.
The bugs
One: the rail is a drawer, not a screen. It opens at
min(88vw, 348px)(strip-rail.css:539). On a 390px phone that is 343px, andthe rail itself gets 295px. A session title clips. The chat shows through on the
right, under a scrim.
Two: a workspace opens on the chat, not on the list. Every route change runs
setDrawerOpen(false)(CloudApp.tsx:286). So a phone lands on Lody's own homescreen and the session list is one tap away.
Three: create workspace opens underneath the rail.
.create-workspace-screenisz-index: 100..shell-navisz-index: 400. Sothe dialog opens and the rail covers it. The member sees slivers of a dialog they
cannot reach. Five sibling handlers already close the drawer on a phone before
they open a dialog.
onCreateWorkspacewas missed.Four: the create-workspace buttons are pinned and mis-aligned. The footer is
flex: nonein a column flex dialog, so it eats 70px of every phone screen. Thebase rule sets
justify-content: flex-end; the mobile rule setsdisplay: gridand the buttons
width: 100%. Thejustify-contentsurvives, so the grid columnis max-content and the buttons hug the right edge at 186px instead of filling.
Five: the member row overlaps itself.
.workspace-member-rowis aneight-column grid. At
max-width: 620pxit becomes three columns, and the fiveremaining children auto-place into those three columns on implicit rows. The
state chip lands in a 28px column and overlaps the machine-type select. The
volume meter lands in the last 28px column and runs off the card.
The fix
nothing. The X in the strip and the Escape key already close the rail.
routeShowsMobileRail(route)names one rule: the rail is the workspace's ownscreen, so it shows at the workspace root and nowhere else.
else. On desktop it still only closes.
onCreateWorkspacecloses the rail on a phone first, like its five siblings.The buttons end the form and fill the width.
display: contentson desktop, so nothing there moves. On a phone it becomesa wrapped band on its own row.
The risk trade
The rail now covers Lody's home screen, which carries a composer that starts a
session from the first message. That composer costs one extra tap:
New session,or the X. The trade buys a phone layout where the list is the workspace and a
session is a page, which is what a phone user expects.
display: contentsis the one tool that adds a wrapper without adding a box. Thealternative was to reorder the member row in JSX and rebuild the desktop grid,
which risks the change this PR promises not to make.
The rejected alternative for bug three: raise the dialog above z-index 400.
.webapp-modal-screen(z-index 300) has the same relationship, so raising onemodal leaves every other one wrong. Closing the rail keeps one rule.
Tests
Two new test files, three extended.
test/mobile-rail-route.test.ts—routeShowsMobileRailover everyChatAddressarm and everyAppRoutearm. Landing is true. Session, sharedsession, landing terminal, session terminal, null, archive, home and settings
are all false.
test/mobile-only-styles.test.ts— the zero-desktop-change gate. It parses thethree stylesheets and asserts every changed declaration appears only inside a
@media (max-width: N)block, that.workspace-member-machinedeclaresdisplay: contentsoutside every block, that the blueprint action row staysflex on a phone, and that the scrim is deleted rather than hidden.
test/shell-smoke.test.tsx— two cases on the realCloudApp: a mobileworkspace landing opens the rail, and Create workspace closes it first.
test/shell-mobile-drawer.test.tsx— the drawer closes through the strip's ownbutton, and renders no scrim.
test/WorkspaceDetailsDialog.test.tsx— the chip, the type select, the volumemeter and the actions menu share one
.workspace-member-machineband.Every one fails when its own source change is reverted. Measured:
mobile-rail-routeexpected false to be truemobile-only-styles.shell-nav must declare width: 100% inside a max-width query: expected false to be truemobile-only-stylesstrip-rail.css: the scrim must be deleted, not hidden: expected true to be falsemobile-only-styles.create-workspace-dialog must declare overflow-y: auto inside a max-width query: expected false to be truemobile-only-stylesthe desktop member machine wrapper must use display: contents: expected false to be trueshell-mobile-drawerexpected <button class="shell-nav-scrim shell-nav-scrim--open"> to be nullshell-smokeexpected 'false' to be 'true'shell-smokeexpected 'true' to be 'false'WorkspaceDetailsDialogmember machine controls must share one mobile band: expected null not to be nullGates, all run on the final tree:
The 12 skipped files are the existing missing-Lody-bundle skip path.
What is NOT proven: pixel geometry. jsdom has no layout engine, so no test
measures a rendered width. The stylesheet gate proves placement and the DOM
tests prove structure. I checked the four phone layouts by hand against a 390 x
844 mock built from
tokens.css.Deploy
The webApp ships inside the control-plane Worker. A merge to
maindeploys it tocanary with no other step. No box image and no payload change: this is browser
code.
# canary, automatic on merge to main npm run deploy -w packages/control-planeRollback: revert the commit and merge. Canary redeploys the previous bundle.
Members get it on the next page load.
🤖 Generated with Claude Code
https://claude.ai/code/session_019ZvBaFqU4mgX75HAbiYJYV