fix(ui): delay-gate the startup splash so it never flashes on fast cached boots - #11885
Merged
lalalune merged 1 commit intoJul 3, 2026
Merged
Conversation
…ched boots On a warm/cached load the app boots almost instantly, so the view transitions loading -> none within a few milliseconds. StartupShell returned the full-screen orange StartupLoading splash immediately for the "loading" kind, so those few ms painted the splash and then ripped it away — a jarring flash. Gate the loading splash behind STARTUP_SPLASH_DELAY_MS (220ms): render it only once the loading state has persisted past the threshold. A boot that becomes ready first never paints it. Error / pairing / bootstrap views stay immediate. The delay uses an effect-based timer (useState + useEffect + setTimeout, cleared on unmount / view-kind change) so no render-time clock/timer is introduced and the audit:ui-determinism gate stays green. The startup-shell:first-paint mark now fires only when visible startup UI actually paints (immediate views, or the splash once its gate opens) — never on a null-rendering mount — preserving the dedupe-by-name behavior. StartupLoading markup/roles/test ids are unchanged. Closes elizaOS#11883 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Your trial has ended. Reactivate Greptile to resume code reviews.
Contributor
|
Caution Review failedAn error occurred during the review process. Please try again later. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
lalalune
pushed a commit
that referenced
this pull request
Jul 3, 2026
lalalune
pushed a commit
that referenced
this pull request
Jul 3, 2026
…tartup-shell:mounted mark keeps boot-trace harness reachable (#11885 review fixes) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
Problem
Closes #11883.
On a warm/cached load — where the app boots almost instantly — the full-screen
elizaOSbooting/splash screen flashes for a fraction of a second and is immediately ripped away. It's a jarring flash on every fast (2nd) load. The splash should only appear when the app does not load right away.Root cause
packages/ui/src/components/shell/StartupShell.tsxreturned the full-screen orangeStartupLoadingsplash immediately wheneverview.kind === "loading"(the fall-through after theerror/pairing/bootstrap/nonebranches). A fast cached boot transitionsloading → nonewithin a few milliseconds, so the splash painted for those few ms and then unmounted → flash.Fix
Gate only the loading splash behind a short delay,
STARTUP_SPLASH_DELAY_MS = 220:useDelayElapsed(active, delayMs)returnstrueonly after the loading state has persisted continuously for the threshold, and resets tofalsethe instant loading ends. The timer lives in auseEffect(setTimeout, cleared on unmount / when the view kind changes) — no render-time clock/timer, soaudit:ui-determinismstays green.loading) before the threshold, the splash never renders — the flash is eliminated.error,pairing, andbootstrapviews still render immediately (they are terminal/interactive, not fast-flash cases).startup-shell:first-painttelemetry mark now fires when visible startup UI actually paints — the immediate views, or the splash once its gate opens — and never on a null-rendering mount. Existing dedupe-by-name behavior is preserved.StartupLoading's markup,data-testid="startup-shell-loading",data-startup-phase, and roles are unchanged — only when it appears changed.Evidence
Primary proof is a new co-located unit test (
StartupShell.test.tsx, vitest + jsdom, fake timers), 8 cases:nonebefore the threshold (fast cached boot — no mark);loading → none → loadingrestarts the timer and only paints after a fresh full threshold;error/pairing/bootstraprender immediately and mark first-paint;nonerenders nothing and marks nothing.Verification run in this branch:
bun run --cwd packages/ui typecheck— passbunx @biomejs/biome checkon both changed files — pass (clean)bunx vitest run src/components/shell/StartupShell.test.tsx— 8/8 passbunx vitest run src/components/shell/— 36 files, 624 pass / 7 skipped (no regressions in the shell suite, incl. the existingStartupFailureViewtest)bun run audit:ui-determinism— StartupShell is not flagged (0 occurrences); this change adds no render-time nondeterminism. (The audit's pre-existing failures are all in unrelatedcloud/files with a stale baseline ondevelop.)Timing behavior, before → after: on a fast cached boot the splash used to paint for a few ms then vanish (flash); now it never mounts. On a genuinely slow boot the splash still appears (220ms later) and stays until the app is ready, exactly as before.
N/A rows: real-LLM trajectories, backend logs, audio — N/A (pure client-side render-timing change; no agent/model/prompt/voice behavior touched). Screenshots/video — the flash is a sub-frame timing artifact best proven by the fake-timer unit test above; there is no steady-state visual change to the splash itself.
🤖 Generated with Claude Code