Two issues over one bar, because they are one relayout — #42's own
findings say giving wa-slider a label grows it 6px → 14px and moves
the transport, which is #23's subject.
Commits
commit
what
issues
7c3937e
the three-column bar, the inline volume + its setting, the specs, CLAUDE.md and two NOTES.md entries
The bar was 320px 1fr auto, so the transport sat in the middle of what
the metadata and the queue button did not use: its centre was ~140px
right of the window's, at every width. The outer two tracks are the
same expression now, so the middle is centred by construction rather
than by arithmetic that has to be redone whenever a control joins the
bar. Measured offset is 0 at 800, 900, 1100 and 1440, and at 200% text.
The cap, which was a regression before it was a decision
Making both sides --now-playing-width is perfectly centred and costs
the control you drag:
layout
seek track @800px
@200% text
320px 1fr auto (before)
257
130
both sides --now-playing-width
61
0
both sides min(…, 25%) (shipped)
246
124
Centring is free at 1440 and expensive at 800, so a change checked only
at a comfortable width looks perfect. The right-hand group needs ~141px
and was being handed 320 to keep the arithmetic symmetric.
It is a min() rather than a breakpoint because --now-playing-width
is user state — the metadata panel has a drag handle — which is the
same reasoning the queue panel's overlay mode uses. Tying both sides to
that variable is also what keeps dragging meaningful: a plain 1fr … 1fr centres just as well and silently makes the handle a no-op.
Click open, drag, click closed is three gestures for a control the bar
has room to show. The volume moved out of audio-player into the bar,
because the transport column has to hold the transport and nothing else,
and it joins the queue button in one cell rather than a second
column, since the centring compares columns.
The popup survives as a setting. The stored flag names the popup,
which is backend/config's polarity rule — the zero value has to be the
intended answer, so an existing config.toml gets the new default with
no migration. Verified live in both directions, without a restart.
Inline, the icon becomes the mute toggle and is named after that
action rather than after the state, because with the slider beside it
there is nothing left to disclose. The component tier now covers both
presentations rather than whichever one is default — and drives the
switch through GeneralConfigChanged, the store's own refresh trigger,
rather than a test-only reset.
It stands down below 600px whatever the setting says: that is about the
platform rather than preference, and is why mediacontrols' Android
handler implements no volume callback.
A phone-only bug fixed in passing
Three nested rules in this block began with a bare element selector,
which Chrome 120 relaxed and the phone's Chrome 113 silently drops —
including the text-overflow: ellipsis on the bar's own title and
artist, which has therefore never truncated on the device. Same class as
the now-playing marquee whose ellipsis sat on the wrong box. They are &-prefixed now; #154 carries the class and a proposal to extend make css-check, since no tier can see this.
Verification
e2e/specs/bottom-bar.spec.ts pins the two halves separately on
purpose: an uncapped build is perfectly centred and fails only the
seek-bar assertion, so a spec asserting centring alone would have passed
the regression above. Both mutation-tested:
mutation
caught by
the old 320px 1fr auto
all four centring tests (and the seek-bar one)
side tracks uncapped
the seek-bar test only
make lint — 0 issues, three configurations
make test — green, three passes
make ui-test — 949 tests
make e2e — 186 passed (was 178), twice against one app
make css-check, make bindings-check, make skill-check
npx tsc --noEmit in both packages
Screenshots read at 1440, 900 and 800×200% text; the setting
toggled live in both directions
Two existing specs changed, both because the change earned it: control-names no longer opens a popup to reach the slider (the name
assertion is unchanged; the route got shorter), and phone-shell's audio-player volume-control locator would have kept passing after
the move — toBeHidden() is satisfied by an element that does not
exist — so it now also asserts the element is there.
Two issues over one bar, because they are one relayout — #42's own
findings say giving `wa-slider` a label grows it 6px → 14px and **moves
the transport**, which is #23's subject.
## Commits
| commit | what | issues |
|---|---|---|
| `7c3937e` | the three-column bar, the inline volume + its setting, the specs, `CLAUDE.md` and two `NOTES.md` entries | #23, #42 |
## #23 — centred means centred in the window
The bar was `320px 1fr auto`, so the transport sat in the middle of what
the metadata and the queue button did not use: **its centre was ~140px
right of the window's**, at every width. The outer two tracks are the
same expression now, so the middle is centred by construction rather
than by arithmetic that has to be redone whenever a control joins the
bar. Measured offset is 0 at 800, 900, 1100 and 1440, and at 200% text.
## The cap, which was a regression before it was a decision
Making both sides `--now-playing-width` is *perfectly centred* and costs
the control you drag:
| layout | seek track @800px | @200% text |
|---|---|---|
| `320px 1fr auto` (before) | 257 | 130 |
| both sides `--now-playing-width` | **61** | **0** |
| both sides `min(…, 25%)` (shipped) | 246 | 124 |
Centring is free at 1440 and expensive at 800, so a change checked only
at a comfortable width looks perfect. The right-hand group needs ~141px
and was being handed 320 to keep the arithmetic symmetric.
It is a `min()` rather than a breakpoint because `--now-playing-width`
is **user state** — the metadata panel has a drag handle — which is the
same reasoning the queue panel's overlay mode uses. Tying both sides to
that variable is also what keeps dragging meaningful: a plain
`1fr … 1fr` centres just as well and silently makes the handle a no-op.
## #42 — the slider is simply there
Click open, drag, click closed is three gestures for a control the bar
has room to show. The volume moved out of `audio-player` into the bar,
because the transport column has to hold the transport and nothing else,
and it joins the queue button in **one cell** rather than a second
column, since the centring compares columns.
The popup survives as a setting. The stored flag names the **popup**,
which is `backend/config`'s polarity rule — the zero value has to be the
intended answer, so an existing `config.toml` gets the new default with
no migration. Verified live in both directions, without a restart.
Inline, the icon becomes the **mute toggle** and is named after that
action rather than after the state, because with the slider beside it
there is nothing left to disclose. The component tier now covers both
presentations rather than whichever one is default — and drives the
switch through `GeneralConfigChanged`, the store's own refresh trigger,
rather than a test-only reset.
It stands down below 600px whatever the setting says: that is about the
platform rather than preference, and is why `mediacontrols`' Android
handler implements no volume callback.
## A phone-only bug fixed in passing
Three nested rules in this block began with a bare element selector,
which Chrome 120 relaxed and **the phone's Chrome 113 silently drops** —
including the `text-overflow: ellipsis` on the bar's own title and
artist, which has therefore never truncated on the device. Same class as
the `now-playing` marquee whose ellipsis sat on the wrong box. They are
`&`-prefixed now; **#154** carries the class and a proposal to extend
`make css-check`, since no tier can see this.
## Verification
`e2e/specs/bottom-bar.spec.ts` pins the two halves **separately on
purpose**: an uncapped build is perfectly centred and fails only the
seek-bar assertion, so a spec asserting centring alone would have passed
the regression above. Both mutation-tested:
| mutation | caught by |
|---|---|
| the old `320px 1fr auto` | all four centring tests (and the seek-bar one) |
| side tracks uncapped | the seek-bar test **only** |
- `make lint` — 0 issues, three configurations
- `make test` — green, three passes
- `make ui-test` — 949 tests
- `make e2e` — 186 passed (was 178), **twice** against one app
- `make css-check`, `make bindings-check`, `make skill-check`
- `npx tsc --noEmit` in both packages
- **Screenshots read** at 1440, 900 and 800×200% text; the setting
toggled live in both directions
Two existing specs changed, both because the change earned it:
`control-names` no longer opens a popup to reach the slider (the name
assertion is unchanged; the route got shorter), and `phone-shell`'s
`audio-player volume-control` locator would have **kept passing** after
the move — `toBeHidden()` is satisfied by an element that does not
exist — so it now also asserts the element is there.
Closes #23
Closes #42
Two issues over one bar, because they are one relayout. #42's own
findings say so: giving wa-slider a label grows it 6px to 14px and
moves the transport, which is #23's subject, so doing them in sequence
means measuring the bar twice and throwing the first set away.
The bar was `320px 1fr auto`, so the transport sat in the middle of
what the metadata and the queue button did not use — its centre was
~140px right of the window's at every width. The outer two tracks are
the same expression now, so the middle is centred by construction.
The side width is the metadata's, capped at a quarter of the bar, and
the cap was measured as a regression before it was a decision:
reserving the full `--now-playing-width` on both sides is perfectly
centred and takes the seek bar's track from 257px to 61px at 800px, and
to 0 at 200% text. The control you drag was paying for the symmetry.
With the cap it is 246, which is parity. It is a `min()` rather than a
breakpoint because that variable is user state — the metadata has a
drag handle — and tying both sides to it is also what keeps dragging
meaningful; a plain `1fr … 1fr` centres just as well and silently makes
the handle a no-op.
The volume moved out of `audio-player` into the bar because the
transport column has to hold the transport and nothing else, and it
joins the queue button in one cell rather than a second column, since
the centring compares columns.
It is a slider by default and a popup by setting. The stored flag names
the *popup*, which is this config's polarity rule — the zero value has
to be the intended answer, so an existing config.toml gets the new
default with no migration. Inline, the icon is the mute toggle and is
named after that action rather than the state, because with the slider
beside it there is nothing to disclose; the component tier now covers
both presentations rather than whichever is default.
Three nested rules in this block began with a bare element selector,
which Chrome 120 relaxed and the phone's Chrome 113 **silently drops** —
including the ellipsis on the bar's own title and artist, which has
therefore never truncated on the device. They are `&`-prefixed now.
Filed as #154 for the class and for a check.
`bottom-bar.spec.ts` pins both halves separately on purpose: an
uncapped build is perfectly centred and fails only the seek-bar width,
so a spec asserting centring alone would have passed the regression
above. Both were verified by mutation.
Closes#23Closes#42
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Two issues over one bar, because they are one relayout — #42's own
findings say giving
wa-slidera label grows it 6px → 14px and movesthe transport, which is #23's subject.
Commits
7c3937eCLAUDE.mdand twoNOTES.mdentries#23 — centred means centred in the window
The bar was
320px 1fr auto, so the transport sat in the middle of whatthe metadata and the queue button did not use: its centre was ~140px
right of the window's, at every width. The outer two tracks are the
same expression now, so the middle is centred by construction rather
than by arithmetic that has to be redone whenever a control joins the
bar. Measured offset is 0 at 800, 900, 1100 and 1440, and at 200% text.
The cap, which was a regression before it was a decision
Making both sides
--now-playing-widthis perfectly centred and coststhe control you drag:
320px 1fr auto(before)--now-playing-widthmin(…, 25%)(shipped)Centring is free at 1440 and expensive at 800, so a change checked only
at a comfortable width looks perfect. The right-hand group needs ~141px
and was being handed 320 to keep the arithmetic symmetric.
It is a
min()rather than a breakpoint because--now-playing-widthis user state — the metadata panel has a drag handle — which is the
same reasoning the queue panel's overlay mode uses. Tying both sides to
that variable is also what keeps dragging meaningful: a plain
1fr … 1frcentres just as well and silently makes the handle a no-op.#42 — the slider is simply there
Click open, drag, click closed is three gestures for a control the bar
has room to show. The volume moved out of
audio-playerinto the bar,because the transport column has to hold the transport and nothing else,
and it joins the queue button in one cell rather than a second
column, since the centring compares columns.
The popup survives as a setting. The stored flag names the popup,
which is
backend/config's polarity rule — the zero value has to be theintended answer, so an existing
config.tomlgets the new default withno migration. Verified live in both directions, without a restart.
Inline, the icon becomes the mute toggle and is named after that
action rather than after the state, because with the slider beside it
there is nothing left to disclose. The component tier now covers both
presentations rather than whichever one is default — and drives the
switch through
GeneralConfigChanged, the store's own refresh trigger,rather than a test-only reset.
It stands down below 600px whatever the setting says: that is about the
platform rather than preference, and is why
mediacontrols' Androidhandler implements no volume callback.
A phone-only bug fixed in passing
Three nested rules in this block began with a bare element selector,
which Chrome 120 relaxed and the phone's Chrome 113 silently drops —
including the
text-overflow: ellipsison the bar's own title andartist, which has therefore never truncated on the device. Same class as
the
now-playingmarquee whose ellipsis sat on the wrong box. They are&-prefixed now; #154 carries the class and a proposal to extendmake css-check, since no tier can see this.Verification
e2e/specs/bottom-bar.spec.tspins the two halves separately onpurpose: an uncapped build is perfectly centred and fails only the
seek-bar assertion, so a spec asserting centring alone would have passed
the regression above. Both mutation-tested:
320px 1fr automake lint— 0 issues, three configurationsmake test— green, three passesmake ui-test— 949 testsmake e2e— 186 passed (was 178), twice against one appmake css-check,make bindings-check,make skill-checknpx tsc --noEmitin both packagestoggled live in both directions
Two existing specs changed, both because the change earned it:
control-namesno longer opens a popup to reach the slider (the nameassertion is unchanged; the route got shorter), and
phone-shell'saudio-player volume-controllocator would have kept passing afterthe move —
toBeHidden()is satisfied by an element that does notexist — so it now also asserts the element is there.
Closes #23
Closes #42
7c3937ec70todc8db159f9