Repository navigation
Conversation
Signing in or out through the secure origin goes through several full-page redirects, and the pages along the way painted different backgrounds: the host page was the reset's white until Ember rendered, the browser's default canvas showed before either page's first paint, and the empty interact submode's operator-mode purple showed between redeeming the login token and the index route opening the workspace chooser. - The host's index.html paints the app's boot in the colour its Loading screen is about to be, and in the dark colour scheme, until the app renders (published realms keep the plain page). - The secure origin's shell declares the dark colour scheme, so its pre-stylesheet frame is dark too. - SecureSignIn opens the workspace chooser before booting the session when the page will land on it. - "Loading…", the host's "Signing you in…" and the secure origin's "Signing you in…" all draw one BusyMessage component. - The loopback confirmation reads in active voice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4b8fd22616
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let params = new URLSearchParams(search); | ||
| return ( | ||
| pathname === ENV.rootURL && | ||
| !params.has('operatorModeState') && | ||
| !params.has('cardPath') |
There was a problem hiding this comment.
Match the route’s empty-state chooser predicate
When the root URL already carries a serialized state such as { stacks: [], submode: 'interact', workspaceChooserOpened: false }, routes/index.gts deliberately replaces it with a state that opens the chooser, but this helper returns false solely because operatorModeState is present. That state can be produced by closing the chooser with no cards and can survive an expired session, so the first post-login render still shows the empty purple interact view before the route opens the chooser—the flash this change is intended to remove. Decode the state and mirror the route’s empty-state condition.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
[Claude Code 🤖] Fixed in 5e05cec861. landsOnWorkspaceChooser decodes operatorModeState and mirrors the index route: a root page whose state is an interact submode with no stacks lands on the chooser, whatever its chooser flag says. A new integration test covers { stacks: [], submode: 'interact', workspaceChooserOpened: false }.
| <div id='host-loading' class='session-loading' data-test-host-loading> | ||
| <BusyMessage @title='Loading…' /> |
There was a problem hiding this comment.
Keep signed-in loading text on the dark palette
When a persisted session exists, this branch renders BusyMessage outside AuthContainer or any dark-theme scope. Its color: var(--foreground, var(--boxel-light)) therefore resolves the root light-theme --foreground to black, so the white fallback is never used; this replaces the previous explicit white loading text and makes returning users see a dark title on the purple #686283 loading screen while the other shared busy screens remain white. Give this wrapper the intended foreground token or dark theme scope.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
[Claude Code 🤖] Fixed in 5e05cec861. The signed-in Loading wrapper sets --foreground: var(--boxel-light), so the title stays white outside AuthContainer.
habdelra
left a comment
There was a problem hiding this comment.
[Claude Code 🤖] This review checked two things: whether every page in the sign-in and sign-out chain stays on one dark background, and whether the boot script and the shared BusyMessage change anything else that renders packages/host/index.html (published realms in host mode, the prerender /render and /connect pages, and tests). It did not run the host acceptance or matrix suites, which need the shared test stack, so those results are unverified here.
One blocking issue: when a session is stored, the "Loading…" title renders black on the purple instead of white. Apart from that, the change does what it sets out to do.
- Set
--foregroundon.session-loadingsoBusyMessage's title stays light outsideAuthContainer. See the comment on.session-loadingintemplates/loading.gts. Blocking. - Add a comment on
HostModeService#isActivethat points to the boot script's copy of the same decision. See the comment onisAppinindex.html. Non-blocking.
| .session-loading { | ||
| background-color: #686283; |
There was a problem hiding this comment.
[Claude Code 🤖] When a session is stored, "Loading…" now renders black on the purple, where it used to be white. BusyMessage colours its title var(--foreground, var(--boxel-light)). Outside AuthContainer, nothing between :root and this wrapper sets --foreground, so the title inherits boxel-ui theme.css's :root value, var(--boxel-dark) (#000). The host never puts data-theme on <html>. I loaded variables.css, theme.css and global.css with this rule and .busy-title in headless Chrome, and the title computed to rgb(0, 0, 0). The old .loading-text was #fff.
Set the token on the wrapper:
| .session-loading { | |
| background-color: #686283; | |
| .session-loading { | |
| --foreground: var(--boxel-light); | |
| background-color: #686283; |
Regression, blocking: this is the first screen of every cold load for a signed-in user.
There was a problem hiding this comment.
[Claude Code 🤖] Fixed in 5e05cec861. The signed-in Loading wrapper sets --foreground: var(--boxel-light), so BusyMessage's title is white on the purple.
| var isApp = | ||
| !simulatingHostMode && | ||
| (config.hostsOwnAssets || | ||
| (realmServerHost !== undefined && | ||
| host.endsWith(realmServerHost) && | ||
| !host.endsWith('.localhost'))); |
There was a problem hiding this comment.
[Claude Code 🤖] isApp copies HostModeService#isActive (hostsOwnAssets, isRealmServerDomain, simulatingHostMode), but only this file points to the other. If someone changes how the service tells a published realm from the app, this copy will keep the old rule. A published realm's page would then get the dark boot paint and the color-scheme: dark meta. No test would catch it: the host test runner's tests/index.html doesn't include this script, and no assertion checks a host-mode page's boot. Can you add a comment on isActive / isRealmServerDomain that names this script, so the next change updates both copies?
This PR adds the duplicate, so the pointer belongs here. Non-blocking.
There was a problem hiding this comment.
[Claude Code 🤖] Done in 5e05cec861. HostModeService#isActive now names the boot script in index.html that makes the same published-realm decision, so a change to one is made in both.
Preview deploymentsHost Test Results 1 files ±0 1 suites ±0 2h 15m 51s ⏱️ + 7m 46s Results for commit e132ca1. ± Comparison against earlier commit 87868ac. Realm Server Test Results 1 files ± 0 366 suites ±0 2h 6m 48s ⏱️ + 4m 14s Results for commit e132ca1. ± Comparison against earlier commit 87868ac. For more details on these errors, see this check. |
… interact state - The signed-in Loading screen sits outside AuthContainer, so BusyMessage's `--foreground` fell back to boxel-ui's root black; the wrapper sets it to the light colour. - landsOnWorkspaceChooser mirrors the index route: a root page whose operator-mode state is an interact submode with no stacks lands on the chooser too, whatever its chooser flag says. - HostModeService#isActive names the boot script in index.html that makes the same published-realm decision. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd-out-through-the-secure-origin-flashes-white Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd-out-through-the-secure-origin-flashes-white build-record.json is regenerated from the merged source. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
[Claude Code 🤖] The red |
…nd-out-through-the-secure-origin-flashes-white build-record.json is regenerated from the merged source. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd-out-through-the-secure-origin-flashes-white build-record.json is regenerated from the merged source. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signing in or out through the secure origin is a chain of full-page redirects between the app and the secure origin, and every page in the chain now paints the same dark sign-in background from its first frame until the workspace chooser takes over. The flashes came from four places:
packages/host/index.htmlpaints the boot in the colour the Loading screen is about to use:#191624when no session is stored, the operator-mode purple when one is. It does this only on the app itself, never on a published realm's page, and only until the app has rendered something.<meta name="color-scheme" content="dark">darkens that canvas. The secure origin's shell declares it statically. The app's boot script adds it and takes it off again once the app renders, so the running app keeps its own colour scheme.SecureSignInnow opens the chooser in memory before booting the session when the page lands on it (the app's root with no card or operator-mode state), and the route writes the same state to the URL once it has booted.BusyMessagecomponent inpackages/secure-origin/src/components, so every busy screen in the chain shows the same title above the same spinner in the same place.The loopback hand-off confirmation also reads in active voice: "A program on this computer at … wants to sign in to Boxel as you. Continue only if you started this sign-in from that program."
Coming back to the app from the secure origin, Chrome can still show its default canvas for about 130 ms before it has parsed any of the page. That canvas is white in a light browser theme. The secure origin's
Cross-Origin-Opener-Policy: same-originputs the app in a new browsing context, so Chrome doesn't hold the previous frame, and nothing the page declares can reach that frame. In the Google flow, one more white flash isn't ours: Google's ownaccounts.youtube.com/accounts/SetSID"Redirecting" page paints white for about 130 ms.Recordings
Recorded from a Chrome window on staging, signed in as a staging test account, with the browser's address bar cropped off. "After" is a production build of this branch's host, served locally against staging. The secure origin there is still staging's own, so its loopback confirmation still shows the copy this PR replaces.
Sign in — before (white, then purple, then the chooser)
Sign in — after
Sign out — before (white while the app boots)
Sign out — after
Testing
matrix/secure-sign-in(11, two new: the chooser is open before the session boots on a root landing, and left alone when the page names its own state),matrix/login(10), acceptancehost mode tests(37, which include "Loading screen is never shown on host mode") andbasic(4) all pass locally.lint:buildpasses against the regeneratedbuild-record.json.🤖 Generated with Claude Code