The first-run wizard does not dismiss when a library appears by another route #175

Open
opened 2026-08-21 05:34:29 +00:00 by logan · 2 comments
Collaborator

Report

The first-run wizard stays up, full-screen and intercepting every
pointer event, when a library appears by a route other than its own
"Choose Folder" flow. Tripped over on a device while verifying #64.

Reproduction (a fresh YJ_HOME, so the wizard is showing):

__yj.call("library.Library.AddLibrary", ["/sdcard/Music/yj"])

The call succeeds and returns the new library row, the scan runs and
finds the tracks, and GetTracks answers with nine of them — and
document.querySelector("first-run-wizard") is still rendering at the
full viewport. Relaunching the app clears it (the element is then
present at 0 height, which is its "there is a library" state).

Findings

  • The wizard dismisses itself as a step in its own flow rather than
    in response to the state it exists to wait for, so any other route to
    a first library leaves it up.
  • Whether a user can reach this is not obvious and may be "no": while
    the wizard is up it intercepts pointer events, which is exactly what
    stops Settings being reachable to add a library from there.
    Platform/Android's storage-access flow is the place to check,
    since that one does not necessarily end inside the wizard's own
    handler.
  • Cheap to make robust regardless: the wizard already knows what it is
    waiting for, and a library-changed subscription is the same shape
    every other view uses.

Direction

Decide first whether a user-reachable path exists; if it does this is a
lockout, and if it does not it is still one subscription away from
being correct by construction rather than by sequence. Either way the
dismissal should follow the library existing, not the button being
pressed.

**Report** The first-run wizard stays up, full-screen and intercepting every pointer event, when a library appears by a route other than its own "Choose Folder" flow. Tripped over on a device while verifying #64. **Reproduction** (a fresh `YJ_HOME`, so the wizard is showing): ``` __yj.call("library.Library.AddLibrary", ["/sdcard/Music/yj"]) ``` The call succeeds and returns the new library row, the scan runs and finds the tracks, and `GetTracks` answers with nine of them — and `document.querySelector("first-run-wizard")` is still rendering at the full viewport. Relaunching the app clears it (the element is then present at 0 height, which is its "there is a library" state). **Findings** - The wizard dismisses itself as a step in its *own* flow rather than in response to the state it exists to wait for, so any other route to a first library leaves it up. - Whether a user can reach this is not obvious and may be "no": while the wizard is up it intercepts pointer events, which is exactly what stops Settings being reachable to add a library from there. `Platform/Android`'s storage-access flow is the place to check, since that one does not necessarily end inside the wizard's own handler. - Cheap to make robust regardless: the wizard already knows what it is waiting for, and a library-changed subscription is the same shape every other view uses. **Direction** Decide first whether a user-reachable path exists; if it does this is a lockout, and if it does not it is still one subscription away from being correct by construction rather than by sequence. Either way the dismissal should follow the library existing, not the button being pressed.
logan added the Kind/Bug
Reviewed
Confirmed
1
Area/Shell-Nav
Priority
Low
4
labels 2026-08-21 05:34:29 +00:00
logan self-assigned this 2026-08-30 10:33:29 +00:00
logan added the
Status
In Progress
label 2026-08-30 10:33:29 +00:00
Author
Collaborator

Picking this up on fix/175-wizard-follows-the-library.

Taking it on the Direction's second half, which is settled regardless
of how its first question is answered: the dismissal should follow the
library existing, not the button being pressed.
The wizard already
subscribes to nothing; LibraryAdded is emitted by AddLibrary
whoever calls it, so one subscription makes the dismissal correct by
construction rather than by sequence.

On the first question — whether a user can reach this — what the code
says will go in the PR rather than being decided here: there are
exactly two frontend callers of AddLibrary (this wizard and
Settings' Add Library), and while the wizard is up its wa-dialog is
modal, which is what makes Settings unreachable. Android's
storage-access flow is the case that needs a device, and it is not
what the change depends on.

Picking this up on `fix/175-wizard-follows-the-library`. Taking it on the Direction's second half, which is settled regardless of how its first question is answered: *the dismissal should follow the library existing, not the button being pressed.* The wizard already subscribes to nothing; `LibraryAdded` is emitted by `AddLibrary` whoever calls it, so one subscription makes the dismissal correct by construction rather than by sequence. On the first question — whether a user can reach this — what the code says will go in the PR rather than being decided here: there are exactly two frontend callers of `AddLibrary` (this wizard and Settings' Add Library), and while the wizard is up its `wa-dialog` is modal, which is what makes Settings unreachable. Android's storage-access flow is the case that needs a device, and it is not what the change depends on.
Author
Collaborator

PR: #234 (fix/175-wizard-follows-the-library)

One subscription: the wizard dismisses on LibraryAdded rather than at
the end of its own flow, registered before its initial read so a library
arriving mid-call is not answered with a stale empty list.

Taken without settling this issue's first question, deliberately.
The Direction says the dismissal should follow the library existing
either way, so the answer moves the priority and not the diff. What
the code says about the question, for whoever answers it: there are
exactly two frontend callers of AddLibrary (this wizard and Settings'
Add Library), and while the wizard is up its wa-dialog is a native
modal — measured on the real app, document.elementFromPoint(550, 400)
is FIRST-RUN-WIZARD — which is what makes Settings unreachable. So on
desktop it reads as robustness rather than a lockout; Android's
storage-access flow is still the open half and still needs a device.

Verified with five new component-tier cases (planted against the
reverted component: the two new-behaviour cases fail, the three pinning
existing behaviour pass), and with this issue's own reproduction against
a real fresh YJ_HOME — before the fix the dialog is still open and
still intercepting after AddLibrary, after it the dialog is gone and
that point is HOME-VIEW. CI green on both jobs, chromium and webkit.

Left Status/In Progress on and not merged.

PR: https://git.ljones.me/yonlu/yellowjacket/pulls/234 (`fix/175-wizard-follows-the-library`) One subscription: the wizard dismisses on `LibraryAdded` rather than at the end of its own flow, registered before its initial read so a library arriving mid-call is not answered with a stale empty list. **Taken without settling this issue's first question, deliberately.** The Direction says the dismissal should follow the library existing *either way*, so the answer moves the priority and not the diff. What the code says about the question, for whoever answers it: there are exactly two frontend callers of `AddLibrary` (this wizard and Settings' Add Library), and while the wizard is up its `wa-dialog` is a native modal — measured on the real app, `document.elementFromPoint(550, 400)` is `FIRST-RUN-WIZARD` — which is what makes Settings unreachable. So on desktop it reads as robustness rather than a lockout; **Android's storage-access flow is still the open half and still needs a device.** Verified with five new component-tier cases (planted against the reverted component: the two new-behaviour cases fail, the three pinning existing behaviour pass), and with this issue's own reproduction against a real fresh `YJ_HOME` — before the fix the dialog is still open and still intercepting after `AddLibrary`, after it the dialog is gone and that point is `HOME-VIEW`. CI green on both jobs, chromium and webkit. Left `Status/In Progress` on and not merged.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: yonlu/yellowjacket#175