Skip to content

fix(docker): blank page on re-navigation, check-updates survival, cache busting - #18

Merged
Inch-high merged 3 commits into
mainfrom
fix/docker-blank-page-and-check-updates-nav
Jun 7, 2026
Merged

Inch-high merged 3 commits into
mainfrom
fix/docker-blank-page-and-check-updates-nav

Conversation

@Inch-high

@Inch-high Inch-high commented Jun 7, 2026 •

Copy link
Copy Markdown
Owner

Summary

Fixes the /Docker page failing to load when navigating away and back — especially while an update or "check for updates" is running — plus related UX gaps surfaced while debugging.

Originated from a report: "It fails to load when updates are running or it's checking for updates and you navigate away and back again." The visible symptom was a completely blank page (no toolbar, no skeleton), which turned out to be a mount-point race rather than the data-fetch path.

Changes

  • Blank page on re-navigation (boot.ts) — On the newer Unraid web-component shell, the legacy .page body (our #modernui-docker-root) can be injected a tick after our head-loaded bundle executes. boot() did a one-shot querySelector and bailed permanently if the root was absent, leaving the page blank with no retry. Now waits for the mount point via a MutationObserver (10s cap) before giving up.

  • Check-for-updates survives navigation (md-docker-page.ts, boot.ts) — The run is a detached server-side worker (PID lock), but the "Checking…" UI was a per-instance flag that reset on reload. boot() now re-attaches to an in-flight run (resumeCheckForUpdatesIfRunning) and resumes polling, so badges refresh on completion as if uninterrupted.

  • Completion banner (md-docker-page.ts) — Completion was silent, especially the "0 updates" case. Added a transient, dismissible result banner: N updates available / All containers up to date / failure note. Fires on both the normal and resumed paths. Unit-tested via the real poll loop (poll-check-updates.test.ts).

  • Cache-busting (install.php) — loader.js injected the page bundles with no cache-busting (even though loader.js + modernui.css already used filemtime busting), so a plugin update could serve a stale bundle until a hard refresh. Now appends ?v=<filemtime> to each injected bundle URL.

  • Gated debug logging (debug.ts, actions.ts, boot.ts, md-docker-page.ts, docker-state.php) — Off by default; enable with ?dockerdebug=1 (or the modernui-docker-debug sessionStorage key — per-session, cleared when the tab closes). Timestamped trace across boot / resync / poll, a SnapshotError carrying the HTTP status (distinguishes the transient 503), and a server-side transient-503 log line. This is what made the root cause diagnosable from the console; kept in because it's inert unless enabled.

Testing

  • npx vitest run tests/unit-ts/docker — 117 passed (adds 1 test covering the completion banner)
  • biome ci src — clean
  • php -l + php-cs-fixer — clean
  • Deployed to a live Unraid host and verified end-to-end via the debug trace: re-navigation now mounts, an in-flight check resumes and polls to completion (~26s), the banner reports the count, and the generated loader.js carries ?v=<mtime> on every bundle.

Note: tests/unit-php/save-docker-autostart.test.php fails on this branch, but it fails identically on main (pre-existing, unrelated to these changes).

Notes

No version bump / release tag in this PR — it's the fix set only. Debug logging is left in (gated). Happy to strip it or cut a release as a follow-up.

🤖 Generated with Claude Code

Inch-high and others added 3 commits June 7, 2026 17:39
…he busting

The /Docker page could fail to load (entirely blank — no toolbar, no
skeleton) when navigating away and back, and a "check for updates" run
started before navigating gave no feedback after returning. Root causes
and fixes:

- Mount-point race: on the newer Unraid web-component shell the legacy
  .page body (our #modernui-docker-root) can be injected a tick after our
  head-loaded bundle runs. boot() did a one-shot querySelector and bailed
  permanently if absent, leaving the page blank with no retry. Now waits
  for the mount point via a MutationObserver (10s cap) before bailing.

- Check-for-updates didn't survive navigation: the run is a detached
  server-side worker (PID lock), but the "Checking…" UI was a per-instance
  flag that reset on reload. boot() now re-attaches to an in-flight run and
  resumes polling, so the badges refresh on completion as if uninterrupted.

- Completion was silent (especially the "0 updates" case). Added a
  transient, dismissible result banner: "N updates available" / "All
  containers up to date" / failure note. Fires on both the normal and
  resumed paths.

- Stale-bundle blank after updates: loader.js injected the page bundles
  with no cache-busting (though loader.js + modernui.css already used
  filemtime busting). A plugin update could serve a stale bundle until a
  hard refresh. install.php now appends ?v=<filemtime> to each injected
  bundle URL.

Also adds gated debug logging (enable with ?dockerdebug=1) across the boot
/ resync / poll path plus a server-side transient-503 log line, to make
this class of "fails to load" report diagnosable from the console.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The ?dockerdebug=1 flag was persisted to localStorage, so once enabled it
stayed on across every page load forever — surprising and noisy. Move it to
sessionStorage: it still survives a same-tab navigate-away-and-back (the
repro this logging is for), but clears when the tab closes. Also drop any
leftover localStorage flag on load so previously-enabled sessions turn off.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The poll-check-updates tests gave the page a mock store without getState,
so _announceCheckResult() (added with the result banner) threw a swallowed
TypeError on every completion tick — the banner path ran in production but
had zero test coverage and spewed misleading "status poll failed" noise.

Add getState to the mock (parameterised by container list) and assert the
banner: the "all up to date" case on the existing completion test, plus a
new test for the pluralised "N updates available" count.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Inch-high
Inch-high merged commit 5b0a715 into main Jun 7, 2026
2 checks passed
Inch-high added a commit that referenced this pull request Jun 7, 2026
…tes survival

Patch release of the PR #18 fix set (already merged to main):
- /Docker no longer blanks on navigate-away-and-back (MutationObserver waits
  for the mount point instead of a one-shot querySelector that bailed).
- A "check for updates" run survives navigation — boot() re-attaches to the
  in-flight detached worker and resumes polling.
- New transient result banner reports the check outcome, including "0 updates".
- install.php cache-busts the injected page bundles (?v=<filemtime>).
- Gated debug logging (?dockerdebug=1) across boot/resync/poll.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant