Skip to content

Fix Panel C target reachability and compact power-up tray re-anchoring on fullscreen lifecycle changes - #76

Merged
TeacherEvan merged 3 commits into
mainfrom
copilot/fix-panel-c-falling-targets
Apr 19, 2026
Merged

TeacherEvan merged 3 commits into
mainfrom
copilot/fix-panel-c-falling-targets

Conversation

Copilot AI commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

This PR addresses two high-impact gameplay regressions: (1) Panel C falling targets could be culled before entering the player’s visible catch zone, and (2) the power-up tray could render in the wrong position until a fullscreen/back lifecycle event forced reflow.

  • Panel C target visibility (Slice 1)

    • Restricted the “stuck at bottom” symbol cleanup heuristic to non-mobile mode so compact/mobile gameplay does not prematurely remove active targets before they enter the catchable viewport band.
    • Preserves desktop anti-clutter behavior while restoring mobile/compact target reachability.
  • Power-up tray positioning lifecycle bug (Slice 2)

    • Wired tray layout sync to fullscreen lifecycle and shared resize notifications.
    • worm-powerups.ui.js now listens to:
      • fullscreenchange
      • shared resize observer hub subscription (immediate: true)
    • This keeps tray anchoring consistent during fullscreen transitions and initial layout stabilization.
  • Regression coverage

    • Extended mobile symbol-rain WebView-like contract to assert at least one live symbol reaches the lower visible spawn-to-catch band in Panel C.
    • Added compact layout regression asserting power-up tray re-anchors into top-safe zone after fullscreen lifecycle changes.
const isStuckAtBottom =
  !state.isMobileMode &&
  symbolObj.y > containerHeight - 100 &&
  state.activeFallingSymbols.length > 30;
https://github.com/user-attachments/assets/46107034-b117-4e5a-a80e-2f77697296d4

Summary by Sourcery

Ensure mobile symbol rain targets remain reachable in Panel C and keep the compact power-up tray correctly anchored during fullscreen and layout changes.

Bug Fixes:

  • Prevent active falling symbols in Panel C from being prematurely culled in mobile/compact mode before entering the catch zone.
  • Ensure the compact power-up tray re-anchors into a safe, in-bounds position after fullscreen lifecycle changes and layout reflows.

Enhancements:

  • Hook the power-up tray layout sync into fullscreenchange events and a shared resize observer for more robust layout updates.

Tests:

  • Add a mobile symbol-rain regression test that asserts at least one live symbol reaches the lower catch zone in Panel C.
  • Add a compact-layout regression test verifying the power-up tray re-anchors correctly after fullscreen lifecycle changes.

Copilot AI linked an issue Apr 18, 2026 that may be closed by this pull request
@vercel

vercel Bot commented Apr 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
math-master-html Ready Ready Preview, Comment Apr 18, 2026 1:47pm

…screen lifecycle

Agent-Logs-Url: https://github.com/TeacherEvan/MathMasterHTML/sessions/8970ed66-eade-42c9-a1ec-f2e3e6fe5730

Co-authored-by: TeacherEvan <189960447+TeacherEvan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix bug with falling characters in Panel C viewport Fix Panel C target reachability and compact power-up tray re-anchoring on fullscreen lifecycle changes Apr 18, 2026
Copilot AI requested a review from TeacherEvan April 18, 2026 13:48

@TeacherEvan TeacherEvan left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Is this the best you can do? @copilot

Copilot AI commented Apr 18, 2026

Copy link
Copy Markdown
Contributor Author

Is this the best you can do? @copilot

For the requested minimal/surgical scope, yes—b922bf3 is the smallest runtime fix set that resolves both regressions, and aafa3b6 tightens the regression assertions. UI screenshot: https://github.com/user-attachments/assets/46107034-b117-4e5a-a80e-2f77697296d4 . If you want a broader hardening pass, I can do a follow-up with expanded fullscreen/device-lifecycle coverage.

@TeacherEvan
TeacherEvan marked this pull request as ready for review April 19, 2026 06:34
Copilot AI review requested due to automatic review settings April 19, 2026 06:34
@sourcery-ai

sourcery-ai Bot commented Apr 19, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Restricts the Panel C 'stuck at bottom' cleanup heuristic to desktop-only and wires the compact power-up tray layout to fullscreen and shared resize lifecycle events, with new regression tests ensuring mobile targets reach the catch zone and the compact tray re-anchors after fullscreen changes.

Sequence diagram for power-up tray layout sync on fullscreen and resize events

sequenceDiagram
  participant Window
  participant Document
  participant SharedResizeObserver
  participant WormPowerupsUI

  rect rgb(230,230,250)
    Window->>WormPowerupsUI: init()
    WormPowerupsUI->>WormPowerupsUI: _displayLayoutHandler = syncDisplayLayout
    Window->>WormPowerupsUI: addEventListener resize
    Window->>WormPowerupsUI: addEventListener orientationchange
    Document->>WormPowerupsUI: addEventListener fullscreenchange
    Window->>Window: __ensureSharedResizeObserver()
    Window-->>WormPowerupsUI: sharedResizeObserver
    WormPowerupsUI->>SharedResizeObserver: subscribe(_displayLayoutHandler, immediate true, source worm-powerups-ui)
    SharedResizeObserver-->>WormPowerupsUI: initial callback (immediate)
    WormPowerupsUI->>WormPowerupsUI: syncDisplayLayout()
  end

  rect rgb(220,255,220)
    Window->>WormPowerupsUI: resize event
    WormPowerupsUI->>WormPowerupsUI: _displayLayoutHandler()
    WormPowerupsUI->>WormPowerupsUI: syncDisplayLayout()
  end

  rect rgb(220,245,255)
    Document->>WormPowerupsUI: fullscreenchange event
    WormPowerupsUI->>WormPowerupsUI: _displayLayoutHandler()
    WormPowerupsUI->>WormPowerupsUI: syncDisplayLayout()
  end

  rect rgb(255,245,220)
    SharedResizeObserver->>WormPowerupsUI: resize notification
    WormPowerupsUI->>WormPowerupsUI: _displayLayoutHandler()
    WormPowerupsUI->>WormPowerupsUI: syncDisplayLayout()
  end
Loading

Flow diagram for Panel C target cleanup and stuck at bottom heuristic

flowchart TD
  A[Start per symbol] --> B[Read symbolObj from activeFallingSymbols]
  B --> C{symbolObj.y > containerHeight + 50?}
  C -- Yes --> D[Mark as off screen and remove]
  C -- No --> E{!state.isMobileMode?}
  E -- No --> H[Skip stuck at bottom cleanup for mobile]
  E -- Yes --> F{symbolObj.y > containerHeight - 100?}
  F -- No --> H
  F -- Yes --> G{activeFallingSymbols.length > 30?}
  G -- Yes --> I[Mark as stuck at bottom and remove]
  G -- No --> H
  H[Keep symbol active] --> J[Check next symbol]
  D --> J
  I --> J
  J --> K[End per symbol]
Loading

File-Level Changes

Change Details Files
Guard Panel C bottom-cleanup heuristic so mobile/compact falling symbols are not culled before reaching the catch zone.
  • Update stuck-at-bottom calculation to be disabled when mobile mode is active so only off-screen symbols are culled on compact/mobile layouts
src/scripts/symbol-rain.animation.js
Make the compact power-up tray resync its layout on fullscreen and shared resize lifecycle events.
  • Extend power-up UI initialization to listen for document fullscreenchange events and trigger display layout sync
  • Subscribe to a shared resize observer hub with immediate invocation to keep tray anchoring consistent across resizes and fullscreen transitions
src/scripts/worm-powerups.ui.js
Add regression tests for compact tray re-anchoring after fullscreen lifecycle changes.
  • Introduce a compact-layout test that forces a fullscreenchange event after moving the tray deep into the panel and asserts it re-anchors into the top-safe region of Panel B controls
tests/powerups.spec.js
Add mobile symbol rain regression test ensuring at least one live symbol reaches the lower catchable band in Panel C.
  • Extend the symbol rain mobile test suite with a poll that inspects falling symbols in Panel C and asserts at least one unclicked symbol intersects the panel and reaches a configured catch-zone threshold
tests/symbol-rain.mobile.spec.js

Assessment against linked issues

Issue Objective Addressed Explanation
#75 Reproduce and fix the Panel C bug where falling characters/targets fail to enter the player's viewport during gameplay, and add a Playwright test asserting that at least one target reaches the visible spawn-to-catch zone.
#75 Reproduce and fix the power-ups UI being rendered in the wrong location until a fullscreen/back action, by correcting its positioning behavior (likely around fullscreen/layout events), and add a regression test for this lifecycle.

Possibly linked issues

  • #N/A: They match on Slice 1 and 2: fixing Panel C falling targets and power-up tray fullscreen positioning with tests.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@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.

Hey - I've found 2 issues, and left some high level feedback:

  • The new fullscreenchange listener and shared resize observer subscription in worm-powerups.ui.js are only registered and never removed; consider mirroring whatever teardown/unsubscribe logic exists for resize/orientationchange to avoid leaks when the UI is destroyed or reinitialized.
  • The compact layout test’s COMPACT_TRAY_MAX_TOP = 80 is a fairly strict magic value that may become brittle with small layout tweaks; it may be more robust to derive this bound from the actual control panel or safe-area geometry instead of a fixed pixel threshold.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new `fullscreenchange` listener and shared resize observer subscription in `worm-powerups.ui.js` are only registered and never removed; consider mirroring whatever teardown/unsubscribe logic exists for `resize`/`orientationchange` to avoid leaks when the UI is destroyed or reinitialized.
- The compact layout test’s `COMPACT_TRAY_MAX_TOP = 80` is a fairly strict magic value that may become brittle with small layout tweaks; it may be more robust to derive this bound from the actual control panel or safe-area geometry instead of a fixed pixel threshold.

## Individual Comments

### Comment 1
<location path="src/scripts/worm-powerups.ui.js" line_range="103-106" />
<code_context>
       this._displayLayoutHandler = () => this.syncDisplayLayout();
       window.addEventListener("resize", this._displayLayoutHandler);
       window.addEventListener("orientationchange", this._displayLayoutHandler);
+      document.addEventListener("fullscreenchange", this._displayLayoutHandler);
+      const sharedResizeObserver =
+        window.__ensureSharedResizeObserver?.() || window.SharedResizeObserver;
+      sharedResizeObserver?.subscribe?.(this._displayLayoutHandler, {
+        immediate: true,
+        source: "worm-powerups-ui",
</code_context>
<issue_to_address>
**issue (bug_risk):** Consider how the new fullscreen listener and resize observer subscription are cleaned up to avoid leaks.

These are long-lived subscriptions. Please ensure the UI’s teardown/dispose path removes the `document` `fullscreenchange` listener and unsubscribes from the shared resize observer (if it returns an unsubscribe handle); otherwise repeated teardown/recreate cycles may accumulate handlers.
</issue_to_address>

### Comment 2
<location path="tests/powerups.spec.js" line_range="9" />
<code_context>
   waitForGameplayInputReady,
 } from "./utils/onboarding-runtime.js";

+const COMPACT_TRAY_MAX_TOP = 80;
+
 async function pressPowerUp(page, type) {
</code_context>
<issue_to_address>
**suggestion:** Avoid hard-coding a magic top threshold for the compact tray to reduce layout brittleness.

Using a fixed `COMPACT_TRAY_MAX_TOP = 80` couples this test to the current pixel layout and makes it brittle to spacing/typography changes. Please derive this bound from the actual compact controls layout (e.g. from the controls’ bounding rect plus a margin), or at minimum add a clear comment explaining why `80` is valid and when it should be updated, so layout tweaks don’t cause spurious test failures.

```suggestion
/**
 * Upper bound (in CSS px) for the compact power-up tray’s top position.
 *
 * This value is derived from the current compact layout:
 * - ~56px: tray height + internal padding
 * - ~16px: top HUD spacing
 * - ~8px: safety margin so minor typography/spacing tweaks don’t break the test
 *
 * If the compact controls are visibly moved lower in the viewport (e.g. layout
 * redesign, font/spacing changes), update this threshold to be slightly larger
 * than the tray’s new expected top position. The intent is to assert that the
 * tray remains “near the top” of the screen, not to lock the exact pixel value.
 */
const COMPACT_TRAY_MAX_TOP = 80;
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +103 to +106
document.addEventListener("fullscreenchange", this._displayLayoutHandler);
const sharedResizeObserver =
window.__ensureSharedResizeObserver?.() || window.SharedResizeObserver;
sharedResizeObserver?.subscribe?.(this._displayLayoutHandler, {

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.

issue (bug_risk): Consider how the new fullscreen listener and resize observer subscription are cleaned up to avoid leaks.

These are long-lived subscriptions. Please ensure the UI’s teardown/dispose path removes the document fullscreenchange listener and unsubscribes from the shared resize observer (if it returns an unsubscribe handle); otherwise repeated teardown/recreate cycles may accumulate handlers.

Comment thread tests/powerups.spec.js
waitForGameplayInputReady,
} from "./utils/onboarding-runtime.js";

const COMPACT_TRAY_MAX_TOP = 80;

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.

suggestion: Avoid hard-coding a magic top threshold for the compact tray to reduce layout brittleness.

Using a fixed COMPACT_TRAY_MAX_TOP = 80 couples this test to the current pixel layout and makes it brittle to spacing/typography changes. Please derive this bound from the actual compact controls layout (e.g. from the controls’ bounding rect plus a margin), or at minimum add a clear comment explaining why 80 is valid and when it should be updated, so layout tweaks don’t cause spurious test failures.

Suggested change
const COMPACT_TRAY_MAX_TOP = 80;
/**
* Upper bound (in CSS px) for the compact power-up tray’s top position.
*
* This value is derived from the current compact layout:
* - ~56px: tray height + internal padding
* - ~16px: top HUD spacing
* - ~8px: safety margin so minor typography/spacing tweaks don’t break the test
*
* If the compact controls are visibly moved lower in the viewport (e.g. layout
* redesign, font/spacing changes), update this threshold to be slightly larger
* than the tray’s new expected top position. The intent is to assert that the
* tray remains “near the top” of the screen, not to lock the exact pixel value.
*/
const COMPACT_TRAY_MAX_TOP = 80;

@TeacherEvan
TeacherEvan merged commit 22e285c into main Apr 19, 2026
7 checks passed
@TeacherEvan
TeacherEvan deleted the copilot/fix-panel-c-falling-targets branch April 19, 2026 06:36

Copilot AI 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.

Pull request overview

Fixes two gameplay regressions impacting mobile/compact play by (1) preventing premature cleanup of Panel C falling symbols in mobile mode and (2) ensuring the power-up tray re-anchors correctly across fullscreen/resize lifecycle events.

Changes:

  • Gate the “stuck at bottom” cleanup heuristic in symbol rain so it does not run in mobile mode.
  • Hook power-up tray layout syncing into fullscreen lifecycle + shared resize observer notifications.
  • Add Playwright regression assertions for Panel C catch-zone reachability and compact tray re-anchoring after fullscreen lifecycle changes.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
tests/symbol-rain.mobile.spec.js Adds a contract asserting at least one live Panel C symbol reaches a lower “catch zone” band in a WebView-like mobile runtime.
tests/powerups.spec.js Adds a compact-layout regression test asserting the power-up tray re-anchors after a fullscreen lifecycle signal.
src/scripts/worm-powerups.ui.js Adds fullscreen + shared resize observer hooks to keep tray layout synced during lifecycle changes.
src/scripts/symbol-rain.animation.js Prevents the “stuck at bottom” cleanup from removing active symbols in mobile mode.

Comment on lines 101 to +109
window.addEventListener("resize", this._displayLayoutHandler);
window.addEventListener("orientationchange", this._displayLayoutHandler);
document.addEventListener("fullscreenchange", this._displayLayoutHandler);
const sharedResizeObserver =
window.__ensureSharedResizeObserver?.() || window.SharedResizeObserver;
sharedResizeObserver?.subscribe?.(this._displayLayoutHandler, {
immediate: true,
source: "worm-powerups-ui",
});

Copilot AI Apr 19, 2026

Copy link

Choose a reason for hiding this comment

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

_bindUIEventHandlers now subscribes to the shared resize observer hub but still binds direct resize/orientationchange/fullscreenchange listeners. Because the hub already forwards those events, syncDisplayLayout() will be invoked multiple times per lifecycle event (and the hub subscription can’t be cleaned up because the unsubscribe function isn’t stored). Consider using either the hub (preferred) or the direct listeners as a fallback, and store the unsubscribe callback so it can be invoked if the system is ever torn down/reinitialized.

Suggested change
window.addEventListener("resize", this._displayLayoutHandler);
window.addEventListener("orientationchange", this._displayLayoutHandler);
document.addEventListener("fullscreenchange", this._displayLayoutHandler);
const sharedResizeObserver =
window.__ensureSharedResizeObserver?.() || window.SharedResizeObserver;
sharedResizeObserver?.subscribe?.(this._displayLayoutHandler, {
immediate: true,
source: "worm-powerups-ui",
});
}
if (!this._displayLayoutUnsubscribe) {
const sharedResizeObserver =
window.__ensureSharedResizeObserver?.() || window.SharedResizeObserver;
if (sharedResizeObserver?.subscribe) {
const unsubscribe = sharedResizeObserver.subscribe(
this._displayLayoutHandler,
{
immediate: true,
source: "worm-powerups-ui",
},
);
this._displayLayoutUnsubscribe =
typeof unsubscribe === "function" ? unsubscribe : null;
} else {
window.addEventListener("resize", this._displayLayoutHandler);
window.addEventListener("orientationchange", this._displayLayoutHandler);
document.addEventListener("fullscreenchange", this._displayLayoutHandler);
}

Copilot uses AI. Check for mistakes.

This branch was successfully deployed

1 active deployment
Preview aafa3b6d Deployed Apr 18, 2026 by vercel[bot]
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.

Issues for model Claude Opus 4.7 to handle

3 participants