Skip to content

sync: dev to extern-contrib#993

Merged
PythonSmall-Q merged 16 commits into
extern-contribfrom
dev
Jun 20, 2026
Merged

sync: dev to extern-contrib#993
PythonSmall-Q merged 16 commits into
extern-contribfrom
dev

Conversation

@github-actions

@github-actions github-actions Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

sync-branches: New code has just landed in dev, so let's bring extern-contrib up to speed!


Summary by cubic

Sync extern-contrib with dev to ship 3.5.3. Fixes the dark-mode flash in MonochromeUI/NewBootstrap by applying the correct theme and Bootstrap CSS before first paint and cleaning out old styles early.

  • Bug Fixes
    • Run at document-start to set data-bs-theme and inject Bootstrap 5 + skin CSS before first paint.
    • Cache Bootstrap CSS via @resource; handle first-install cache miss with a safe CDN fallback.
    • Remove legacy Bootstrap/theme link tags via MutationObserver and early DOMContentLoaded cleanup.
    • Prevent CLS by temporarily hiding the page; reveal immediately after cleanup in the main IIFE.

Written for commit 2ff4536. Summary will update on new commits.

Review in cubic

claude and others added 16 commits June 14, 2026 07:38
Bootstrap 5.3.3 CSS was previously fetched from CDN at document-idle,
causing a flash of the old light-themed page before dark mode took effect.

Changes:
- Add @run-at document-start so an early shim can run before first paint
- Add @resource BootstrapCSS + @require Bootstrap JS bundle so both are
  cached by the userscript manager (no CDN round-trip on page load)
- Add @grant GM_getResourceText
- Early synchronous IIFE: reads saved theme/settings from localStorage,
  sets data-bs-theme on <html> immediately, injects Bootstrap CSS and the
  MonochromeUI or default skin CSS before the browser paints anything, and
  uses a MutationObserver to drop the page's own old Bootstrap/theme link
  tags before they are ever fetched
- Extract MonochromeSkinCSS and NewBootstrapSkinCSS to top-level consts so
  they are available both to the early block and to the late Style element
  (which still applies them at document-end for any dynamic page additions)
- Remove Bootstrap CSS + JS from the runtime CDN resources array
- Remove the now-redundant post-load link-removal loop, data-bs-theme
  assignment, and earlyStyle CSS-variable injection from the NewBootstrap block
- Add DOMContentLoaded wait at the start of the main async IIFE so all
  existing DOM-dependent code continues to work correctly under document-start

https://claude.ai/code/session_01B1RgyUvtsWWhS2hdNiUhZb
The early block now owns the MonochromeSkinCSS / NewBootstrapSkinCSS
injection. The Style element created at document-end is kept (other
code appends to it further down the IIFE), but it no longer re-sets
the skin CSS that was already applied synchronously at document-start.

https://claude.ai/code/session_01B1RgyUvtsWWhS2hdNiUhZb
If GM_getResourceText("BootstrapCSS") returns null (the @resource has
not been downloaded yet on first install or update), the early block was
still setting up the MutationObserver that blocked the page's original
Bootstrap CSS — leaving the page with no Bootstrap CSS at all.

Fix: bail out of the early block before the observer is set up when the
resource is unavailable, so the page's original stylesheets load as a
fallback (flash still happens, but the page renders correctly).

Also add a CDN fallback entry to the runtime resources array: on the
rare load where the @resource wasn't ready, Bootstrap 5.3.3 CSS is
fetched from CDN instead, maintaining the same behaviour as before this
PR. Subsequent page loads use the cached @resource and no CDN request.

https://claude.ai/code/session_01B1RgyUvtsWWhS2hdNiUhZb
The browser preload scanner starts fetching old Bootstrap/theme
stylesheets before the document-start MutationObserver can remove
the link elements. Once fetched, those stylesheets are applied and
override our early-injected Bootstrap 5 + skin CSS — causing the new
UI to render for a moment and then immediately revert.

Restoring the IIFE-time link removal loop fixes this: removing a
<link> from the DOM un-applies its stylesheet from the CSSOM
immediately, regardless of whether the preload scanner had already
fetched it. The two layers now work together:

  1. MutationObserver (document-start): best-effort early block that
     prevents old CSS from ever loading when the timing works out.
  2. Link removal loop (document-idle): guaranteed cleanup that
     removes any preload-scanner-fetched stylesheets after the DOM
     is ready, eliminating the revert.

https://claude.ai/code/session_01B1RgyUvtsWWhS2hdNiUhZb
The visible flash was caused by old Bootstrap 3 CSS sitting in the CSSOM
from the moment DOMContentLoaded fired until the IIFE's link removal loop
ran — a window that includes a full loginpage.php network round-trip.

Move the definitive link removal into the early block's DOMContentLoaded
handler so it fires at the earliest possible moment after the DOM is
ready, before any async work in the main IIFE. The observer is also
disconnected at the same time.

The IIFE-level link removal loop is kept as a belt-and-suspenders
fallback for edge cases (e.g. early block disabled due to cache miss).

https://claude.ai/code/session_01B1RgyUvtsWWhS2hdNiUhZb
No JavaScript approach can reliably prevent the browser preload scanner
from fetching and visibly applying old Bootstrap CSS before DOMContentLoaded.
Attempting to evict stylesheets at DOMContentLoaded still leaves a window
where old CSS is visible, causing CLS.

Instead, hide the page immediately at document-start with
  html { opacity: 0 !important; }
so the user never sees the intermediate broken state. The page is revealed
in the DOMContentLoaded handler, after old stylesheet links have been
removed and the correct Bootstrap 5 + skin CSS is already in place.
A 3-second safety-net timeout ensures the page is always revealed even
if something unexpected prevents DOMContentLoaded from firing.

This is the standard FOUC prevention pattern. The hidden window is just
HTML parse time (~50-150 ms for a server-rendered page), after which the
user sees the correct final state with zero CLS.

The hide style is only injected when the @resource is available
(_earlyBootstrapInjected = true), so the fallback path (cache miss on
first install) continues to show the page immediately.

https://claude.ai/code/session_01B1RgyUvtsWWhS2hdNiUhZb
The DOMContentLoaded listener registered inside the early IIFE's
sandbox context was not firing reliably, causing the opacity:0 FOUC
prevention style to only be removed by the 3-second safety-net
timeout — resulting in a mandatory 3 s blank page on every load.

Fix: expose the FOUC style element and MutationObserver via top-level
variables (_foucStyle, _earlyObs) and perform the reveal + link
cleanup at the top of the main async IIFE, immediately after its
DOMContentLoaded wait. The IIFE's own DOMContentLoaded mechanism is
proven to work, so the reveal is now reliable.

The early block still hides the page at document-start and arms the
MutationObserver for best-effort link interception; the IIFE takes
care of teardown and the final CSSOM cleanup at DOMContentLoaded time,
before the loginpage.php fetch begins.

https://claude.ai/code/session_01B1RgyUvtsWWhS2hdNiUhZb
When GM_getResourceText("BootstrapCSS") returns null (first install or
@resource not yet cached), the early block exits before injecting
MonochromeSkinCSS/NewBootstrapSkinCSS. The Style element in the IIFE
now injects the skin CSS (plus AddAnimation/AddColorText overrides)
whenever _earlyBootstrapInjected is false, so the skin is always
applied regardless of cache state.

https://claude.ai/code/session_01B1RgyUvtsWWhS2hdNiUhZb
fix: eliminate dark-mode flash in MonochromeUI/NewBootstrap
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying xmoj-script-dev-channel with  Cloudflare Pages  Cloudflare Pages

Latest commit: 2ff4536
Status: ✅  Deploy successful!
Preview URL: https://ab308179.xmoj-script-dev-channel.pages.dev

View logs

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @github-actions[bot], your pull request is larger than the review limit of 150000 diff characters

@PythonSmall-Q PythonSmall-Q merged commit b305b74 into extern-contrib Jun 20, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants