Fix duplicate DOM elements from custom game JS on restart#1833
Merged
Conversation
"Start from the beginning" / Load reused the live DOM instead of a fresh page load, but only ever cleared the output pane. Games commonly use their external <javascript> resources to inject elements elsewhere in the chrome (e.g. a custom sidebar pane), and those re-run their setup on every restart (RegisterExternalScripts fires on every Initialise call, and a game's StartGame function can also call JS.* directly) — so anything the previous run injected was still sitting there when the new run added another one. WasmPlayer: restartGame() now rebuilds the whole player chrome via a new swapInPlayerUi() helper, same as the initial boot, instead of only resetting #divOutput. Also dedupe addExternalScript() by URL in the shared player.js so a script's top-level setup code doesn't re-execute at all across restarts. WebPlayer: mirrors the same idea — Game.razor now wraps its rendered chrome in #qv-player-chrome (Blazor never re-diffs it once rendered, since the underlying string is constant) and resets it via WebPlayer.resetPlayerUi() before a restart. Verified with Playwright against both players: a reproducer fixture (tests/e2e/fixtures/restart-test.aslx) confirms exactly one injected pane survives repeated restarts, and confirms the old code duplicates it (1 → 2 → 3) to validate the regression tests are meaningful. Co-Authored-By: Claude Sonnet 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.
Summary
<javascript>resources to inject elements elsewhere in the chrome (e.g. a custom sidebar pane), and that setup code re-runs on every restart (RegisterExternalScriptsfires on everyInitialisecall, and a game'sStartGamefunction can callJS.*directly too) — so anything the previous run injected was still there when the new run added another one.restartGame()now rebuilds the whole player chrome via a newswapInPlayerUi()helper, same as the initial boot, instead of only resetting#divOutput. Also dedupesaddExternalScript()by URL in the sharedplayer.jsso a script's top-level setup code doesn't re-execute across restarts.Game.razorwraps its rendered chrome in#qv-player-chrome(Blazor never re-diffs it once rendered, since the underlying string is constant) and resets it viaWebPlayer.resetPlayerUi()before a restart.Test plan
dotnet build --configuration Release(full solution)dotnet test --configuration Release(all projects pass)tests/e2e/verify-wasmplayer-restart-sidebar-dup.mjs+ fixture gametests/e2e/fixtures/restart-test.aslx): confirms exactly 1 injected pane survives repeated restarts; confirmed the old code duplicates it (1 → 2 → 3) to validate the test is meaningfultests/e2e/verify-webplayer-restart-chrome-reset.mjs): confirms a manually-injected chrome marker is wiped on restart and gameplay still works afterwards; confirmed the old code leaves the marker in place🤖 Generated with Claude Code