Fade out loading screen and preload wallpaper#173
Conversation
Add smooth fade-out handling for the loading screen and coordinate hiding between theme and wallpaper readiness. Introduce hideLoadingScreen() with transitionend and fallback timeout, and use global flags (_themeReady/_wallpaperReady) so loading screen is only removed once both are ready (with a 3s safety fallback). In wallpaper.js add signalWallpaperReady(), preloadAndApplyBackground() to decode images before applying, and call these from checkAndUpdateImage/applyRandomImage flows; ensure Blob URL cleanup. Add CSS rules for #LoadingScreen fade-out transition and pointer-events handling.
Add early dark-mode detection in preload.js and a corresponding CSS rule to avoid a light→dark flash on first paint. The preload script now sets the loading screen color default to #000000ff and checks localStorage (preferredTheme, selectedTheme, bgType) plus system preference to decide whether to add an 'early-dark-filter' class to <html>. style.css adds the .early-dark-filter rule (invert + hue-rotate) which is intended to be removed later by wallpaper.js once full DOM selectors and styles are available.
Save the selected background type to localStorage and remove the pre-applied early-dark-filter when toggling wallpapers (scripts/wallpaper.js). Update CSS filter declarations to consistently apply invert+hue-rotate (and -webkit-filter) to page elements including #darkTheme and .favicon to avoid light→dark flash and ensure those elements are inverted where appropriate (style.css).
📝 WalkthroughWalkthroughThe changes implement an early dark-mode filter system applied during preload to prevent light→dark flashing, introduce a fade-out animation for the loading screen, establish inter-script readiness signaling ( Changes
Sequence Diagram(s)sequenceDiagram
participant Browser as Browser
participant Preload as Preload Script
participant Theme as Theme Script
participant Wallpaper as Wallpaper Script
participant DOM as DOM/CSS
Browser->>Preload: Execute preload.js
Preload->>Preload: Read theme from localStorage<br/>& prefers-color-scheme
Preload->>DOM: Apply early-dark-filter class
Note over DOM: Filter applied before paint
Browser->>Theme: Execute theme.js
Theme->>Theme: Signal window._themeReady = true
Theme->>DOM: Check window._wallpaperReady
Browser->>Wallpaper: Execute wallpaper.js
Wallpaper->>Wallpaper: Preload background image
Wallpaper->>Wallpaper: Signal window._wallpaperReady = true
Wallpaper->>Theme: Call hideLoadingScreen()
Theme->>DOM: Add fade-out class to `#LoadingScreen`
DOM->>DOM: Opacity transition 0.3s
Theme->>DOM: Hide loading screen on transitionend
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Suggested labels
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 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 |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@scripts/preload.js`:
- Line 10: The current line always overwrites the CSS default with black when
localStorage lacks LoadingScreenColor; change the behavior so you only call
document.documentElement.style.setProperty for the '--Loading-Screen-Color' when
localStorage.getItem('LoadingScreenColor') returns a non-null/non-empty value
(i.e., keep the built-in theme CSS default when no stored value exists) —
inspect and update the code using the existing
localStorage.getItem('LoadingScreenColor') check and the
document.documentElement.style.setProperty call to be conditional rather than
using the "|| '#000000ff'" fallback.
- Around line 17-23: Preload currently computes isDarkMode only from
preferredTheme (and system preference), which misses legacy users whose legacy
key enableDarkModeCheckboxState === "checked" was migrated later; update
preload.js to also read localStorage.getItem('enableDarkModeCheckboxState') and
treat that value === "checked" as equivalent to preferredTheme === "dark" when
computing isDarkMode (i.e., set isDarkMode = true if legacy flag is checked OR
existing preferredTheme logic is true), and keep existing
selectedTheme/bgType/isColorBg handling unchanged so legacy users get the dark
first paint.
In `@scripts/wallpaper.js`:
- Around line 174-179: The blob URL created by URL.createObjectURL (imageUrl) is
only revoked on the saved-random branch, causing leaks on the upload/restore and
stale-random paths; update the logic so every created imageUrl is revoked: call
URL.revokeObjectURL(imageUrl) after preloadAndApplyBackground(imageUrl) resolves
in the upload/restore branch (e.g., inside the .then of
preloadAndApplyBackground before or after signalWallpaperReady()), and ensure
the stale-random/saved-random flow also revokes any unused or temporary imageUrl
immediately after use (or immediately if it isn’t passed to
preloadAndApplyBackground). Target the usage sites around imageUrl,
URL.createObjectURL, preloadAndApplyBackground, and signalWallpaperReady to add
the revoke calls so no branch leaves the blob URL alive.
- Around line 141-156: The daily/midnight refresh path currently calls
signalWallpaperReady() immediately after fetch/save instead of waiting for the
image to decode; update the refresh logic to call
preloadAndApplyBackground(imageUrl) (the helper that decodes and applies the
image) and only call signalWallpaperReady() after that Promise resolves. Replace
the direct apply/notify calls in the midnight refresh branch and the similar
code around the other occurrence (previously at the block near the code that
runs at lines corresponding to the second occurrence) so both use
preloadAndApplyBackground(imageUrl). Ensure toggleBackgroundType(true) and
setting of --bg-image remain inside preloadAndApplyBackground and that the
refresh code awaits it before signaling readiness.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 55117c5e-75be-44b9-b459-8ce23ff2f02c
📒 Files selected for processing (4)
scripts/preload.jsscripts/theme.jsscripts/wallpaper.jsstyle.css
|
|
||
| // Set Loading Screen Color before Everything Loads | ||
| document.documentElement.style.setProperty('--Loading-Screen-Color', localStorage.getItem('LoadingScreenColor') || "#bbd6fd"); | ||
| document.documentElement.style.setProperty('--Loading-Screen-Color', localStorage.getItem('LoadingScreenColor') || "#000000ff"); |
There was a problem hiding this comment.
The fallback color is now wrong for the default-theme path.
LoadingScreenColor is only written from the explicit theme-apply paths in scripts/theme.js. Users who stay on the built-in blue theme never hit those writes, so this falls back to black on every load instead of the real default background.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/preload.js` at line 10, The current line always overwrites the CSS
default with black when localStorage lacks LoadingScreenColor; change the
behavior so you only call document.documentElement.style.setProperty for the
'--Loading-Screen-Color' when localStorage.getItem('LoadingScreenColor') returns
a non-null/non-empty value (i.e., keep the built-in theme CSS default when no
stored value exists) — inspect and update the code using the existing
localStorage.getItem('LoadingScreenColor') check and the
document.documentElement.style.setProperty call to be conditional rather than
using the "|| '#000000ff'" fallback.
| const preferredTheme = localStorage.getItem('preferredTheme'); | ||
| const selectedTheme = localStorage.getItem('selectedTheme'); | ||
| const savedBgType = localStorage.getItem('bgType'); | ||
| const systemDark = window.matchMedia('(prefers-color-scheme: dark)').matches; | ||
|
|
||
| const isDarkMode = preferredTheme === 'dark' || (preferredTheme === 'system' && systemDark); | ||
| const isColorBg = savedBgType !== 'wallpaper'; // default to color if not saved |
There was a problem hiding this comment.
Legacy dark-mode users still miss the preload filter.
theme.js still migrates enableDarkModeCheckboxState === "checked" to preferredTheme = "dark" at startup. Because this preload path only reads preferredTheme, those users still get the light first paint on their first post-upgrade load.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/preload.js` around lines 17 - 23, Preload currently computes
isDarkMode only from preferredTheme (and system preference), which misses legacy
users whose legacy key enableDarkModeCheckboxState === "checked" was migrated
later; update preload.js to also read
localStorage.getItem('enableDarkModeCheckboxState') and treat that value ===
"checked" as equivalent to preferredTheme === "dark" when computing isDarkMode
(i.e., set isDarkMode = true if legacy flag is checked OR existing
preferredTheme logic is true), and keep existing selectedTheme/bgType/isColorBg
handling unchanged so legacy users get the dark first paint.
| // Helper to preload and decode an image before applying it as background | ||
| function preloadAndApplyBackground(imageUrl) { | ||
| return new Promise((resolve) => { | ||
| const img = new Image(); | ||
| img.src = imageUrl; | ||
| img.decode().then(() => { | ||
| document.body.style.setProperty("--bg-image", `url(${imageUrl})`); | ||
| toggleBackgroundType(true); | ||
| resolve(); | ||
| }).catch(() => { | ||
| // Fallback: apply even if decode fails | ||
| document.body.style.setProperty("--bg-image", `url(${imageUrl})`); | ||
| toggleBackgroundType(true); | ||
| resolve(); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
The daily refresh path still hides before the new wallpaper is decode-ready.
This branch bypasses preloadAndApplyBackground(), so signalWallpaperReady() runs once fetch/save finish, not once the new image has decoded. That leaves the midnight refresh path still vulnerable to the wallpaper pop-in this PR is trying to eliminate.
Also applies to: 183-187
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/wallpaper.js` around lines 141 - 156, The daily/midnight refresh path
currently calls signalWallpaperReady() immediately after fetch/save instead of
waiting for the image to decode; update the refresh logic to call
preloadAndApplyBackground(imageUrl) (the helper that decodes and applies the
image) and only call signalWallpaperReady() after that Promise resolves. Replace
the direct apply/notify calls in the midnight refresh branch and the similar
code around the other occurrence (previously at the block near the code that
runs at lines corresponding to the second occurrence) so both use
preloadAndApplyBackground(imageUrl). Ensure toggleBackgroundType(true) and
setting of --bg-image remain inside preloadAndApplyBackground and that the
refresh code awaits it before signaling readiness.
| const imageUrl = URL.createObjectURL(blob); | ||
|
|
||
| if (imageType === "upload") { | ||
| document.body.style.setProperty("--bg-image", `url(${imageUrl})`); | ||
| toggleBackgroundType(true); | ||
| preloadAndApplyBackground(imageUrl).then(() => { | ||
| signalWallpaperReady(); | ||
| }); |
There was a problem hiding this comment.
imageUrl now leaks on two branches.
The Blob URL is created before the branch, but only the saved-random path revokes it. The upload restore path keeps it alive for the life of the tab, and the stale-random refresh path leaks an unused URL immediately.
Also applies to: 183-194
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/wallpaper.js` around lines 174 - 179, The blob URL created by
URL.createObjectURL (imageUrl) is only revoked on the saved-random branch,
causing leaks on the upload/restore and stale-random paths; update the logic so
every created imageUrl is revoked: call URL.revokeObjectURL(imageUrl) after
preloadAndApplyBackground(imageUrl) resolves in the upload/restore branch (e.g.,
inside the .then of preloadAndApplyBackground before or after
signalWallpaperReady()), and ensure the stale-random/saved-random flow also
revokes any unused or temporary imageUrl immediately after use (or immediately
if it isn’t passed to preloadAndApplyBackground). Target the usage sites around
imageUrl, URL.createObjectURL, preloadAndApplyBackground, and
signalWallpaperReady to add the revoke calls so no branch leaves the blob URL
alive.
Add smooth fade-out handling for the loading screen and coordinate hiding between theme and wallpaper readiness. Introduce hideLoadingScreen() with transitionend and fallback timeout, and use global flags (_themeReady/_wallpaperReady) so loading screen is only removed once both are ready (with a 3s safety fallback). In wallpaper.js add signalWallpaperReady(), preloadAndApplyBackground() to decode images before applying, and call these from checkAndUpdateImage/applyRandomImage flows; ensure Blob URL cleanup. Add CSS rules for #LoadingScreen fade-out transition and pointer-events handling.## 📌 Description
🎨 Visual Changes (Screenshots / Videos)
🔗 Related Issues
✅ Checklist
Add smooth fade-out handling for the loading screen and coordinate hiding between theme and wallpaper readiness. Introduce hideLoadingScreen() with transitionend and fallback timeout, and use global flags (_themeReady/_wallpaperReady) so loading screen is only removed once both are ready (with a 3s safety fallback). In wallpaper.js add signalWallpaperReady(), preloadAndApplyBackground() to decode images before applying, and call these from checkAndUpdateImage/applyRandomImage flows; ensure Blob URL cleanup. Add CSS rules for #LoadingScreen fade-out transition and pointer-events handling.
Overview
This PR prevents theme and wallpaper flashes on page load by implementing coordinated fade-out of the loading screen and preloading wallpaper images before they are applied. The changes introduce readiness synchronization between theme and wallpaper modules, early dark-mode detection, and image preload/decode workflows.
Key Changes
Loading Screen Synchronization
hideLoadingScreen()helper inscripts/theme.jsthat fades out#LoadingScreenby adding afade-outclass with a 0.3s CSS transition, hides the element ontransitionend, and includes a 500ms fallback timeoutwindow._themeReadyandwindow._wallpaperReady) so the loading screen is only removed once both theme and wallpaper modules signal readinessWallpaper Preloading
preloadAndApplyBackground(imageUrl)inscripts/wallpaper.jsthat decodes images viaimg.decode()before applying them, with fallback handling for decode failuressignalWallpaperReady()that coordinates with the theme module to conditionally hide the loading screen once both are readycheckAndUpdateImage()control flow to use preload for stored uploads and re-applied random images, with proper Blob URL cleanupEarly Dark-Mode Detection
scripts/preload.jsthat reads stored theme preference and checks systemprefers-color-schemebefore initial page paintearly-dark-filterclass to document root to prevent light-to-dark flashes during first render#LoadingScreencolor variable from#bbd6fdto#000000ffCSS Enhancements
html.early-dark-filterrule withfilter: invert(1) hue-rotate(180deg)to prevent early flash#LoadingScreenwithopacity: 1andtransition: opacity 0.3s ease#LoadingScreen.fade-outwithopacity: 0andpointer-events: nonefor fade-out animationinvert(1) hue-rotate(180deg)with webkit prefixesStorage Persistence
toggleBackgroundTypeinscripts/wallpaper.jsto persist selected background type tolocalStorageasbgTypeearly-dark-filterclass when background type is toggledTechnical Details
_themeReadyand_wallpaperReadysignals before executing fade-outdecode()method for safe decoding with fallback behavior on failure