Android: make the queue a real screen instead of a pop-open sidebar #55

Closed
opened 2026-08-18 05:58:15 +00:00 by logan · 2 comments
Collaborator

Report

The queue does not work as a slide-out sidebar on mobile. Make it a proper screen so it flows better.

Findings

  • queue-panel is a panel in the shell's grid; on phone widths it overlays and shares the "drawer coming in from the side" problems reported for the "More" menu.
  • Views are chunks (VIEW_LOADERS / DETAIL_LOADERS in frontend/index.ts); a queue screen is a detail view, which also gives it the back stack for free (history.back() / navigate-back).
  • The related request is that the queue becomes reachable only from the Now Playing screen on mobile (see the slimmed-controls issue), which fits a detail view exactly.
  • The panel's own behaviours must come along: roving tab stop, Alt+Arrow reorder, drag reorder, selection, and virtualizer.requestUpdate() on selection/current-track change.

Direction

One component, two mount points: inline panel on desktop, DETAIL_LOADERS entry on phone widths. Do not fork the implementation.

**Report** The queue does not work as a slide-out sidebar on mobile. Make it a proper screen so it flows better. **Findings** - `queue-panel` is a panel in the shell's grid; on phone widths it overlays and shares the "drawer coming in from the side" problems reported for the "More" menu. - Views are chunks (`VIEW_LOADERS` / `DETAIL_LOADERS` in `frontend/index.ts`); a queue *screen* is a detail view, which also gives it the back stack for free (`history.back()` / `navigate-back`). - The related request is that the queue becomes reachable **only from the Now Playing screen** on mobile (see the slimmed-controls issue), which fits a detail view exactly. - The panel's own behaviours must come along: roving tab stop, Alt+Arrow reorder, drag reorder, selection, and `virtualizer.requestUpdate()` on selection/current-track change. **Direction** One component, two mount points: inline panel on desktop, `DETAIL_LOADERS` entry on phone widths. Do not fork the implementation.
logan added the Area/QueueKind/FeaturePlatform/Android
Priority
High
2
Status
Blocked
1
labels 2026-08-18 14:36:23 +00:00
Author
Collaborator

#24 is decided, which settles what this issue was waiting for: the queue panel
gets an overlay mode with a scrim, chosen by a computed rule
(viewport - sidebar - queueWidth >= 480 stays inline) rather than a media
query, because the panel's width is drag-resizable and persisted.

That is the shape this issue needs, and the measurement that justifies it is
stronger than either issue assumed. The panel's host is flex-shrink: 0 and
sits in the flow of .content-area, so it takes its width from the content
instead of covering it:

viewport main panel left
390x780 69px
320x600 0px

So on a phone the queue does not overlay the page badly — it removes it. The
Direction here ("one component, two mount points, do not fork it") is carried
into plan 018 verbatim: the overlay is a presentation of the same
queue-panel, so the roving tab stop, Alt+Arrow reorder, drag reorder,
selection semantics and the virtualizer.requestUpdate() on selection and
current-track change all come along untouched.

Plan 018 deliberately stops short of the DETAIL_LOADERS mount this issue
wants — that is this issue's own work — but nothing blocks it once the overlay
lands. Dropping Status/Blocked; the Gitea dependency on #24 stays until
#24 closes, which is the accurate state.

Plan: .planning/plans/active/018-supported-sizes-and-the-queue-model.md

#24 is decided, which settles what this issue was waiting for: the queue panel gets an **overlay mode with a scrim**, chosen by a computed rule (`viewport - sidebar - queueWidth >= 480` stays inline) rather than a media query, because the panel's width is drag-resizable and persisted. That is the shape this issue needs, and the measurement that justifies it is stronger than either issue assumed. The panel's host is `flex-shrink: 0` and sits *in the flow* of `.content-area`, so it takes its width from the content instead of covering it: | viewport | main panel left | |---|---| | 390x780 | **69px** | | 320x600 | **0px** | So on a phone the queue does not overlay the page badly — it removes it. The Direction here ("one component, two mount points, do not fork it") is carried into plan 018 verbatim: the overlay is a presentation of the same `queue-panel`, so the roving tab stop, Alt+Arrow reorder, drag reorder, selection semantics and the `virtualizer.requestUpdate()` on selection and current-track change all come along untouched. Plan 018 deliberately stops short of the `DETAIL_LOADERS` mount this issue wants — that is this issue's own work — but nothing blocks it once the overlay lands. **Dropping `Status/Blocked`**; the Gitea dependency on #24 stays until #24 closes, which is the accurate state. Plan: `.planning/plans/active/018-supported-sizes-and-the-queue-model.md`
logan removed the
Status
Blocked
1
label 2026-08-19 14:21:45 +00:00
logan self-assigned this 2026-08-21 01:59:05 +00:00
logan added the
Status
In Progress
label 2026-08-21 01:59:05 +00:00
Author
Collaborator

Taking this. Branch: 55-queue-as-a-screen.

Approach, and it is not the one in the Direction — the measurement is
why.
Read the whole comment before the diff; the shape changed.

Measured on the running app at 880adff, at the reference device's real
viewport (424x439), with the queue open:

box rect
.main-panel 424 x 318
queue-panel host (overlay) 424 x 318
.panel-content 424 x 318
.scrim 424 x 318, entirely underneath the panel

So #24's overlay is already pixel-for-pixel the main panel. A
DETAIL_LOADERS mount would render the same rectangle in the same
place
. What is missing is not the drawing, it is the place in the
navigation model:

  • Back navigates the page underneath and leaves the queue up. Opened
    the queue on Artists and pressed back: data-active-view went
    artists -> albums, open stayed true. The user's back press
    moves something they cannot see and costs them their place.
  • The scrim has zero reachable pixels on a phone, because
    panel-content is width: 100% there. #24's tap-outside-to-close does
    not exist on the device this issue is about.
  • The only way out is a 25x21px "Close queue" button.

So the queue is already a screen in every respect except the two that
make a screen a screen. It gets those, from the mechanism the app
already has, and it keeps its one mount point.

The rule is the one #24 already computes. The queue is a place
exactly while it is an overlay, and a column while it is inline —
which is available - panelWidth < 480, measured and not breakpointed,
so a dragged-wide panel is right for free. Opening an overlay queue
dispatches navigate {view: 'queue'}; opening a column queue toggles the
attribute as it does today. Back should not close a column somebody
docked.

Why not the DETAIL_LOADERS mount, since the Direction asks for it —
three costs, no measured benefit:

  1. .main-panel > * is contain: layout style paint under a
    .main-panel that is contain: content. queue-panel today computes
    contain: layout style inside a .content-area that is the same, so
    its wa-popup context menu escapes. Moving it in would put a working
    menu inside two paint-containing ancestors — #60's defect, on the
    reference device, and no tier here can see it (CI's Chromium and
    WebKit both have the Popover API, so the popup is top-layered and a
    spec asserting "not clipped" is green on the broken build).
  2. A detail view is ephemeral, so selection, focusedIndex and scroll
    position would be discarded on every visit. The overlay keeps them.
  3. A second <queue-panel> in the DOM is the bottom-nav drawer trap by
    construction — four e2e specs locate queue-panel by tag.

This also settles the #60-before-#55 question raised on the previous
session's handover: it only arose because #55-as-specified moves the menu
into the contained region. This does not move the menu at all, so the
ordering constraint dissolves rather than being ignored. Argued on #73.

Verification will be the whole e2e suite, not the spec I write — the last
two changes each broke a spec about something else.

Taking this. Branch: `55-queue-as-a-screen`. **Approach, and it is not the one in the Direction — the measurement is why.** Read the whole comment before the diff; the shape changed. Measured on the running app at `880adff`, at the reference device's real viewport (424x439), with the queue open: | box | rect | |---|---| | `.main-panel` | 424 x 318 | | `queue-panel` host (overlay) | 424 x 318 | | `.panel-content` | 424 x 318 | | `.scrim` | 424 x 318, **entirely underneath the panel** | So #24's overlay is already pixel-for-pixel the main panel. A `DETAIL_LOADERS` mount would render *the same rectangle in the same place*. What is missing is not the drawing, it is the place in the navigation model: - **Back navigates the page underneath and leaves the queue up.** Opened the queue on Artists and pressed back: `data-active-view` went `artists` -> `albums`, `open` stayed `true`. The user's back press moves something they cannot see and costs them their place. - **The scrim has zero reachable pixels on a phone**, because `panel-content` is `width: 100%` there. #24's tap-outside-to-close does not exist on the device this issue is about. - The only way out is a 25x21px "Close queue" button. So the queue is already a screen in every respect except the two that make a screen a screen. It gets those, from the mechanism the app already has, and it keeps its one mount point. **The rule is the one #24 already computes.** The queue is a *place* exactly while it is an `overlay`, and a *column* while it is inline — which is `available - panelWidth < 480`, measured and not breakpointed, so a dragged-wide panel is right for free. Opening an overlay queue dispatches `navigate {view: 'queue'}`; opening a column queue toggles the attribute as it does today. Back should not close a column somebody docked. **Why not the `DETAIL_LOADERS` mount**, since the Direction asks for it — three costs, no measured benefit: 1. `.main-panel > *` is `contain: layout style paint` under a `.main-panel` that is `contain: content`. `queue-panel` today computes `contain: layout style` inside a `.content-area` that is the same, so its `wa-popup` context menu escapes. Moving it in would put a working menu inside two paint-containing ancestors — #60's defect, on the reference device, and **no tier here can see it** (CI's Chromium and WebKit both have the Popover API, so the popup is top-layered and a spec asserting "not clipped" is green on the broken build). 2. A detail view is ephemeral, so selection, `focusedIndex` and scroll position would be discarded on every visit. The overlay keeps them. 3. A second `<queue-panel>` in the DOM is the `bottom-nav` drawer trap by construction — four e2e specs locate `queue-panel` by tag. **This also settles the #60-before-#55 question** raised on the previous session's handover: it only arose because #55-as-specified moves the menu into the contained region. This does not move the menu at all, so the ordering constraint dissolves rather than being ignored. Argued on #73. Verification will be the whole e2e suite, not the spec I write — the last two changes each broke a spec about something else.
logan closed this issue 2026-08-21 03:00:45 +00:00
gitea-actions bot removed the
Status
In Progress
label 2026-08-21 03:00:58 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Reference: yonlu/yellowjacket#55