🤖 feat: show a Restarting screen immediately on Install & restart - #4244
Conversation
Both updaters jumped from "downloaded" straight to the process restart, so "Install & restart" left the old UI on screen for seconds (Squirrel handshake on macOS, graceful server teardown on npm) and then the app vanished. - Add a "restarting" UpdateStatus emitted synchronously once the install is going ahead: the desktop updater before quitAndInstall(), the server updater after the blocker gate and before activation (also on the forced path). - New UpdateRestartOverlay covers the whole app the moment that status arrives and stays up through the reconnect gap until the relaunched server reports another status, or an install error clears it. - About dialog shows the restarting state and disables update actions; the title bar badge spins.
…clicks - desktop UpdaterService.setChannel now refuses while an install is restarting, so the status cannot be reset to idle mid-quitAndInstall (found in UAT) - UpdateRestartOverlay opts back into pointer events under Radix's modal body lock
LoadingScreen (and the restart overlay) rely on .boot-loader rules that only exist inline in index.html, so their stories rendered unstyled (top-left, no sizing). Lift those rules into the preview head from index.html itself.
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: 494e51c9ab
ℹ️ 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".
…rrors - updater errors reported through electron-updater's error event while restarting now map to the install phase (retry stays available) - UpdateRestartOverlay portals to <body> so an open Radix modal's aria-hidden on the app root cannot hide the status from assistive technology - the About dialog closes itself when restarting lands, releasing its focus trap and aria-hidden so the restart screen is what keyboard/AT land on
|
@codex review |
🛡️ Codex Security Review · Automatically triggeredSecurity review completed. No security issues were found in this pull request. Reviewed commit: Only the user who started this review can view the report in Codex. ℹ️ About Codex security reviews in GitHubThis is an experimental Codex feature. Security reviews are triggered when:
Once complete, Codex will leave suggestions, or a comment if no findings are found. |
|
Codex Review: Didn't find any major issues. Breezy! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
Summary
Pressing Install & restart now swaps the whole UI to a full-screen "Restarting Xum…" screen the moment the install is confirmed, instead of leaving the old UI on screen until the process abruptly restarts.
Background
Both updaters went straight from
downloadedto the restart with no status change. On desktop,autoUpdater.quitAndInstall()can block for seconds (Squirrel on macOS fetching the update from electron-updater's local proxy) while the window stays visible; on the npm server the graceful restart (abort streams, close terminals, dispose services) can take up to the teardown budget. The only feedback was a transient "Installing…" button label, cleared by the next status event.Implementation
restartingvariant inUpdateStatusSchema({ type: "restarting", info: { version } }).src/desktop/updater.ts: emitsrestartingsynchronously beforemarkUpdateInstallInProgress()/quitAndInstall()(real path only; theDEBUG_UPDATERfake path is unchanged so developers are not trapped behind the cover). Retries after an install error reuse the remembered downloadedUpdateInfo.setChannelnow refuses while restarting, since resetting toidlethere would tear the screen down mid-quit, and updater errors reported through theerrorevent while restarting map to the install phase so the retry stays available.serverUpdater.ts: emitsrestartingright after the blocker gate passes (also on the forced path), beforeactivateUpdate+ restart. Still no await between the idle snapshot, the broadcast, the atomic swap, and the shutdown latch.UpdateRestartOverlay(mounted inAppLoaderInnernext to<App />, portaled to<body>so an open modal's aria-hidden on the app root cannot hide the status from assistive technology): subscribes toupdate.onStatusitself, covers the viewport above dialogs/toasts/menus, and deliberately keeps the cover up whileapiis null during the server reconnect gap; the relaunched server's first status (or an install error) clears it.pointer-events-autoso Radix's modal body lock (About dialog open) cannot let clicks fall through.restartinglands (releasing Radix's focus trap and the aria-hidden it put on the app root) and, if reopened, shows "Restarting to install …" with update actions disabled; title bar badge spins..storybook/main.tslifts the inline.boot-loaderCSS fromindex.htmlinto the preview head soLoadingScreen-based stories render as they do in the app (they were unstyled before).Validation
restartingis emitted beforequitAndInstall/ beforeactivate+restart, never while blocked, followed byerror/installwhen activation throws; the overlay appears onrestarting, survivesapi === null, and clears on the next status;setChannelrefuses while restarting (red/green checked).Restartingstory.setChannelguard gap) is fixed here.Risks
update.onStatusstream, and every other consumer ignores unknown-to-them states by falling through to no-ops. If a relaunched server never comes back, the browser client keeps showing the restart screen (no timeout/escape hatch yet); today it would show a reconnect banner over a dead UI instead.Generated with
xum• Model:anthropic:claude-fable-5-1• Thinking:xhigh• Cost:$66.69