Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 25 additions & 7 deletions docs/SLICE-014-analog-controls.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# CUETools Linux Slice Brief: SLICE-014 analog controls

Version: 0.2. Date: 2026-08-21. Status: Approved for build.
Version: 1.0. Date: 2026-08-23. Status: Done.
Companion documents: ARCHITECTURE.md, ENGINEERING.md, DECISION-LOG.md
(D-075/D-076 icon language, D-077 house voice).

Expand Down Expand Up @@ -104,7 +104,18 @@ No persistent data.
name (renamed out of reach), and the lamp lens ran thin (widened).
- [x] Suite green (143) including the accent-key contrast pins (label
vs both gradient stops, both themes; disabled exempt per WCAG 1.4.3).
- [ ] Owner's live eyeball on the real app.
- [x] Owner's live eyeball on the real app (2026-08-23). It found the
one thing this slice's own guardrail had told it to protect, and the
slice had broken anyway: section 10 says never make a control's state
less legible than before, and "the unpowered read must never cost the
user the ability to see a control exists". The dead key's legend had
been left on the structural `Line` brush, which measured **1.2:1** on
the dark face and 1.5:1 on the light one. That is not dim, it is
invisible, and nothing caught it because WCAG exempts disabled
controls so nothing had ever measured them. Fixed and bounded both
ways by test as D-087.
The same pass replaced the hover outline with the housing lamp
(D-088), which supersedes this slice's non-geometric hover cue.

## 10. Agent guardrails for this build

Expand All @@ -119,17 +130,24 @@ No persistent data.

## 11. Slice definition of done

- [ ] All acceptance criteria pass with linked evidence.
- [ ] No unlabeled shortcuts inside the boundary.
- [ ] Documents updated: statuses, ADD section 15, manual touches if
any text describes the old controls.
- [ ] Owner eyeball complete.
- [x] All acceptance criteria pass with linked evidence.
- [x] No unlabeled shortcuts inside the boundary.
- [x] Documents updated: statuses, DECISION-LOG (D-087, D-088). No
manual text described the old controls, so none needed changing.
- [x] Owner eyeball complete (2026-08-23). See section 9: it caught a
real breach of this slice's own guardrail, now fixed as D-087.

## 12. What this unlocks

- The WPF head's analog pass, from a proven Linux look.
- The full-sweep restyle decision (combos, spinners) with real
evidence about whether the bench language helps or hurts data entry.
**Taken, as SLICE-016.** The answer was that the question had a
false premise: eleven of the fifteen selectors were never lists, so
they became banks of interlocked keys rather than restyled combos,
and only the four real lists kept a dropdown behind a machined
window. Each bank key is this slice's machined cap, so the language
carried over without a second control family.

---

Expand Down
68 changes: 52 additions & 16 deletions docs/SLICE-015-soft-body-keys.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# CUETools Linux Slice Brief: SLICE-015 soft-body rubber keys

Version: 0.2. Date: 2026-08-21. Status: Approved for build.
Version: 1.0. Date: 2026-08-23. Status: Done, with two measurements
carried (section 9): press-fidelity renders at each scale factor, and
the render half of the frame budget. Neither blocks use; both are
named rather than ticked.
Companion documents: ARCHITECTURE.md, ENGINEERING.md, DECISION-LOG.md
(D-080 scope, D-081 the glyph recipe, D-082 the open skill question,
D-083 why that recipe is right and the upgrade path if it is not
Expand Down Expand Up @@ -127,14 +130,36 @@ No persistent data.

## 9. Verification evidence required

- [ ] Model tests green in the committed harness (no renderer needed).
- [ ] Pointer/keyboard input tests green.
- [ ] Rest-identity and press-fidelity renders archived, both themes,
at 1.0x / 1.25x / 1.5x / 2.0x.
- [ ] The LCD-subpixel check on the REAL Avalonia window surface
(D-081's named build gate), not on an SKBitmap-backed canvas.
- [ ] Software-rendering and mid-rip frame-budget measurements.
- [ ] Owner walkthrough.
- [x] Model tests green in the committed harness (no renderer needed).
31 tests, including the six added on 2026-08-23 for the off-key drag
(D-086).
- [x] Pointer/keyboard input tests green.
- [x] Rest-identity and press-fidelity renders archived, both themes:
`docs/evidence/2026-08-23-seam-light/`. Rest, hover and a held corner
press, plus the dead-key legend and a bank position.
- [~] Renders at 1.0x / 1.25x / 1.5x / 2.0x. Covered for LAYOUT by
ScaleMatrixTests, which asserts no clipping and landmark reachability
at all five factors in both themes on every push. NOT covered as
archived press-fidelity images at each factor.
- [-] The LCD-subpixel check on the REAL Avalonia window surface.
**Retired by D-085.** This gate belonged to D-081's texture recipe,
which existed to feed the mesh renderer. The owner's gate verdict
kept the projective path, which rasterises no texture and samples
nothing, so there is no resampled glyph to check. Nothing was
skipped here; the thing being measured stopped existing.
- [~] Software-rendering and mid-rip frame-budget measurements.
The per-frame ARITHMETIC is measured: a homography solve is
**12.897 us** and one field sample **0.802 us**, so a single
deforming key costs **0.155 percent** of a 120 Hz frame. For scale,
the mesh path this replaced measured 2999 us per key per frame, or
36 percent of the same budget - about 230x more. What is NOT measured
is Avalonia's own render and composite cost for the extra layers, or
either number under software rendering or during a live rip.
- [x] Owner walkthrough. 2026-08-23, on the flag-gated build. Verdict
recorded as D-085: the shaded dip reads as below the surface, so the
rim stays pinned and the mesh renderer is unfunded. The same
walkthrough produced D-086 (the off-key drag defect), D-087 (the
unreadable dead legend) and D-088 (light instead of an outline).

## 10. Agent guardrails for this build

Expand All @@ -161,13 +186,24 @@ No persistent data.

## 11. Slice definition of done

- [ ] All acceptance criteria pass with linked evidence.
- [ ] No unlabeled shortcuts inside the boundary.
- [ ] Documents updated: statuses, ADD section 15, the manual if any
user-visible text describes the old press.
- [ ] The soft-body skill written, and D-082 resolved so it is
actually discoverable from both heads.
- [ ] Owner walkthrough complete.
- [x] All acceptance criteria pass with linked evidence, except the two
measurements named in section 9 and marked there rather than here.
- [x] No unlabeled shortcuts inside the boundary. Two are labeled: the
rail keys do not deform (light only, see D-088 and section 4), and
the frame-budget measurement is arithmetic-only.
- [x] Documents updated: statuses, DECISION-LOG (D-085 through D-088).
No manual text described the old press, so none needed changing.
- [x] The soft-body skill written, and D-082 resolved so it is actually
discoverable from both heads. Verified live: the repo-root symlink
surfaces it here with no per-skill wiring.
- [x] Owner walkthrough complete (2026-08-23, D-085).

**Kill criterion fired, in the good direction.** Section 12's first
entry says: if the owner sees the shaded dip and cannot tell the
outline is not bending, ship the cheap transform path and drop the
mesh. That is what happened. The dip read as below the surface, so the
cheap path is not a fallback here - it is the answer, and it measures
about 230x cheaper than the alternative it displaced.

## 12. Kill criteria

Expand Down
35 changes: 24 additions & 11 deletions docs/SLICE-016-selectors.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,27 @@ in `RipView.axaml`, and the Advanced page tooltips.
| Encoder, mode, enum | Encoder settings | up to 9 codecs, per-codec modes | window |
| Action | Queue | short fixed set | window |

CTDB sharing is the interesting one. It has only three options, which
says bank, but its labels run about 330 pixels laid out as one, which
says window. Long labels win. Shortening them to Ask, Always, Never
would make it a clean bank, but that is a wording change to a consent
control and it is the owner's call.

Queue action is the other judgement call. It sits inside a table row,
where a bank fights the column sizing.
CTDB sharing is the interesting one, and it stays a window. Measured
2026-08-23: the Settings row is 455 DIP wide and its control gets 296.
A three-key bank with these labels needs about 352 for the keys, and
the row label "Share verified rips with CTDB" takes another 190, so it
does not fit beside its label at any padding worth having. It WOULD
fit with the label stacked above, but that is the only row on the page
that would break the label-left rhythm.
The alternative is shortening the options to Ask, Always, Never, which
reads fine under that row label and would make a clean bank. Not
taken. It is a wording change to a consent control, `settings.md` line
34 quotes the current strings, and the long labels are the ones that
still say what they mean when a screen reader reads the option without
the row label. If the owner wants the bank, the change is those three
strings, the manual line, and an accessible name on the bank; it is
about five minutes and it is reversible.

Queue action BECAME a bank. The earlier reasoning was wrong on a fact:
it is not in a table row. It sits in a `LastChildFill="False"` toolbar
where a wider control just pushes the next one along, and its options
are `Verify` and `Convert` - two short labels, which is the rocker
case. Both now show without a click.

## 4. Width, and where v0.1 got it wrong

Expand Down Expand Up @@ -145,9 +158,9 @@ this landed as one pass.
role are untouched. `Classes="window counted"` adds the position
readout, which stays opt-in per the v0.1 ruling.

Sixteen call sites converted: six to banks (quality, layout on Rip and
on Settings, metadata search, album art search, network) and ten to
windows.
Sixteen call sites converted: SEVEN to banks (quality, layout on Rip
and on Settings, metadata search, album art search, network, and the
Queue action added 2026-08-23) and nine to windows.

## 8. Verification

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions src/CUETools.Linux.App/Views/QueueView.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
<TextBlock DockPanel.Dock="Left" Text="Action"
Foreground="{DynamicResource Muted}" FontSize="11"
VerticalAlignment="Center" Margin="0,0,8,0" />
<ComboBox Classes="window" DockPanel.Dock="Left" Width="100" VerticalAlignment="Center"
ItemsSource="{Binding Actions}"
SelectedItem="{Binding SelectedAction, Mode=TwoWay}" />
<ListBox Classes="bank" DockPanel.Dock="Left" VerticalAlignment="Center"
ItemsSource="{Binding Actions}"
SelectedItem="{Binding SelectedAction, Mode=TwoWay}" />
<Button DockPanel.Dock="Left" Width="220" MinWidth="140"
VerticalAlignment="Center" Margin="8,0,0,0"
Click="OnCodecPickerClick"
Expand Down
18 changes: 15 additions & 3 deletions tests/CUETools.Linux.Tests/SelectorLayoutTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,28 @@ public void NoSelectorBankEverHasAPositionCutOff()
var window = new MainWindow(new ThemeState(), graph.Verify, graph.Convert, graph);
window.Show();

// every page that carries a bank, not just Rip. The clipping this test
// exists to catch is invisible at page level, so a page left unvisited
// is a page left unchecked.
var pages = new (string Name, Action Show)[]
{
("rip", window.ShowRipPage),
("settings", window.ShowSettingsPage),
("queue", window.ShowQueuePage),
("convert", window.ShowConvertPage),
};

var checkedAny = false;
foreach (ThemeVariant variant in new[] { ThemeVariant.Dark, ThemeVariant.Light })
{
Avalonia.Application.Current!.RequestedThemeVariant = variant;
foreach (var (w, h, scale, name) in Widths)
foreach (var (pageName, show) in pages)
{
window.SetRenderScaling(scale);
window.Width = w;
window.Height = h;
window.ShowRipPage();
show();
Avalonia.Threading.Dispatcher.UIThread.RunJobs();

foreach (ListBox bank in window.GetVisualDescendants()
Expand All @@ -80,10 +92,10 @@ public void NoSelectorBankEverHasAPositionCutOff()
double right = topLeft!.Value.X + item.Bounds.Width;

Assert.True(right <= bank.Bounds.Width + 0.5,
$"{name} {variant}: bank position \"{item.Content}\" ends at {right:0.0} " +
$"{pageName} {name} {variant}: bank position \"{item.Content}\" ends at {right:0.0} " +
$"inside a bank only {bank.Bounds.Width:0.0} wide, so it is cut off");
Assert.True(item.Bounds.Width > 1,
$"{name} {variant}: bank position \"{item.Content}\" collapsed to " +
$"{pageName} {name} {variant}: bank position \"{item.Content}\" collapsed to " +
$"{item.Bounds.Width:0.0} wide");
}
}
Expand Down
Loading