Skip to content

fix(editor): don't leave the entry landing over a chart loaded via editSong - #164

Merged
byrongamatos merged 1 commit into
mainfrom
fix/entry-landing-load-race
Jul 9, 2026
Merged

fix(editor): don't leave the entry landing over a chart loaded via editSong#164
byrongamatos merged 1 commit into
mainfrom
fix/entry-landing-load-race

Conversation

@byrongamatos

@byrongamatos byrongamatos commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Opening a song through the library card’s “Open in editor” action, or returning from the 3D highway’s “Edit region” button, could finish the load underneath a full-screen overlay that silently ate every canvas click.

The race

window.editSong navigates and loads in the same tick:

window.editSong = (filename) => {
    showScreen(plugin-editor);   // arms the landing timer
    loadCDLC(filename);            // still fetching when it fires
};

The screen activation arms _editorMaybeShowStartLanding on a setTimeout(…, 80) (and …, 120 when the screen is already active at init()). That function asks “is anything loaded?” only when it fires — so a load still fetching at +80 ms loses the race. And nothing ever took the landing down again: its own buttons were the only remove() call sites.

Why it wasn’t merely cosmetic

canvas.addEventListener(mousedown, onMouseDown);          // canvas
_globalListeners.add(document, mousemove, onMouseMove);   // document

mousemove fires straight through any overlay, so canvas.style.cursor still turns ew-resize over a note’s right edge. mousedown is bound to the canvas, so it is swallowed and the drag never starts. Cursor arms, nothing grabs — with a bg-black/70 scrim sitting there to explain it, if you happened to look.

Fix

Count loads in flight (_editorLoadsInFlight). A load in flight suppresses the landing; starting a load dismisses a landing already up.

A counter rather than a boolean because two loads can overlap — editSong twice, or a fast failure alongside a slow fetch — and the first to settle would otherwise clear a guard the second still needs held. Codex caught that on the first pass.

Verification

A headless harness drives the real flow (window.editSong on a 33 MB feedpak) and asserts both halves. It fails on main and passes here:

check before after
empty editor still offers the landing PASS PASS
no landing while a load is in flight FAIL PASS
no landing after the load completes FAIL PASS
elementFromPoint over a note DIV#editor-start-landing CANVAS#editor-canvas
canvas mousedown reaches onMouseDown FAIL PASS

The first row matters: it fails a “fix” that just deletes the landing.

node --test 88/88, npm run lint 0 errors (10 pre-existing warnings), Codex clean.

Not fixed here

The mousedown-on-canvas / mousemove-on-document asymmetry is the amplifier — any element above the canvas produces the same “cursor works, drag doesn’t” signature. Moving mousedown to document would change click-outside semantics, so it is left alone; the real defence is not leaving overlays around, which is what this PR does.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Fixed an issue where opening a song could leave an invisible overlay on the canvas and block clicks.
    • Improved screen loading behavior so the landing overlay no longer appears while content is still loading, and it is dismissed when loading begins.

…itSong

`window.editSong` calls `showScreen('plugin-editor')` and `loadCDLC(filename)`
in the same tick. The screen activation arms the entry landing on a
`setTimeout(…, 80)` (and `…, 120` when the screen is already active at init),
and `_editorMaybeShowStartLanding` asks "is anything loaded?" only when that
timer fires. A load still fetching at +80ms therefore loses the race, and
nothing ever took the landing down again — its own buttons were the only
`remove()` call sites. A large feedpak finished loading underneath a
`fixed inset-0 z-50` overlay.

That would be merely cosmetic if the overlay were inert. It is not:

    canvas.addEventListener('mousedown', onMouseDown);          // canvas
    _globalListeners.add(document, 'mousemove', onMouseMove);   // document

`mousemove` fires straight through any overlay, so `canvas.style.cursor` still
becomes `ew-resize` over a note's right edge, while `mousedown` is swallowed and
the drag never starts. Cursor arms, nothing grabs.

Both entrances to `editSong` are real user flows: the library song-card's "Open
in editor" action, and the 3D highway's "Edit region" return path
(`returnToEditorFromHighway`).

Fix: count loads in flight. A load in flight suppresses the landing; starting a
load dismisses a landing already up. A counter rather than a flag because two
loads can overlap — a fast failure would otherwise clear a guard a slower load
still needs held (Codex).

Verified headlessly through `editSong` on a 33MB feedpak: before, the landing
survives the load, `elementFromPoint` over a note returns
`DIV#editor-start-landing`, and a click selects nothing; after, the canvas is
topmost and the click selects. An empty editor still offers the landing, so the
harness also fails a "fix" that just deletes the feature.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 9, 2026 18:24
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: bc6e9b9a-3f1a-487c-8d82-1cf252358280

📥 Commits

Reviewing files that changed from the base of the PR and between 281448a and e362206.

📒 Files selected for processing (2)
  • CHANGELOG.md
  • src/main.js

📝 Walkthrough

Walkthrough

Adds an _editorLoadsInFlight counter in src/main.js, incremented at the start of loadCDLC and decremented in a finally block, used to suppress _editorMaybeShowStartLanding() while a load is active. Updates CHANGELOG.md documenting the fixed overlay/click-swallowing race.

Changes

Landing Overlay Race Fix

Layer / File(s) Summary
In-flight load tracking and landing guard
src/main.js
loadCDLC increments _editorLoadsInFlight on entry and decrements it in a finally block; _editorMaybeShowStartLanding() returns early when loads are in flight, preventing the overlay from appearing during concurrent loads.
Changelog entry
CHANGELOG.md
Documents the invisible overlay/click-swallowing bug and describes the fix suppressing/dismissing the landing during in-flight loads.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant editSong
  participant loadCDLC
  participant editorMaybeShowStartLanding

  User->>editSong: open song from library/highway
  editSong->>loadCDLC: loadCDLC(filename)
  loadCDLC->>loadCDLC: _editorLoadsInFlight++
  editSong->>editorMaybeShowStartLanding: showScreen triggers check
  editorMaybeShowStartLanding->>editorMaybeShowStartLanding: return early (loads in flight > 0)
  loadCDLC->>loadCDLC: finally: _editorLoadsInFlight--
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is clear and accurately summarizes the main fix: preventing the editor landing overlay from remaining over charts opened via editSong.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/entry-landing-load-race

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ast-grep (0.44.1)
src/main.js

ast-grep timed out on this file


Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a race where the editor’s “start landing” overlay could remain on top of the canvas when a song is opened via window.editSong (or similar flows), preventing mousedown interactions from reaching the canvas.

Changes:

  • Track in-flight editor loads via _editorLoadsInFlight and suppress showing the start landing while a load is running.
  • Dismiss any existing start landing overlay when a load begins, ensuring it can’t persist over an active editor session.
  • Document the issue and fix in CHANGELOG.md.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
src/main.js Adds an in-flight load counter, removes the landing overlay on load start, and prevents landing display while loads are running.
CHANGELOG.md Records the bug scenario and the behavioral change (landing suppressed during loads; dismissed on load start).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@byrongamatos
byrongamatos merged commit fd8d3c2 into main Jul 9, 2026
5 checks passed
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.

2 participants