The Record control draws the Stash mark #63

Merged
wk merged 1 commit from m3-60-stash-mark-control into main 2026-08-11 15:21:35 -04:00
Member

The Record control in Control Center, on the lock screen and on the Action Button drew record.circle.fill — the same glyph as every other app's record button. It now draws the Stash moustache. The label is still "Record" and nothing else about the control moves.

What changed

The mark is a custom SF Symbol, traced out of the app icon. A control renders its icon as a single-colour template, so the artwork itself cannot be used, and the repository had no glyph. The moustache is thresholded out of icon-1024.png and traced to one closed path — a trace rather than a redrawing, so the approved curve stays the approved curve. The waveform is dropped, per the decision on the issue: its bars would break up at the size Control Center draws.

A symbol rather than an image set, so the control sizes and tints the mark the way it sized the glyph it replaces. All nine <weight>-<scale> variants carry the same path; a filled mark has no stroke to thin or thicken, and Xcode refuses a symbol with no Regular-M glyph, which is all a three-source variable template would have shipped.

The catalog is compiled into both targets. The control renders inside StashWidgets, which cannot reach the app's bundle. The app's own catalog is not what is shared — it holds the 1024 px app icon, which an extension has no use for.

Tools/make-stash-mark.py is the trace, so the derivation is checkable and the mark can be regenerated at another size from one constant. Nothing in the build runs it; it needs potrace. The template geometry it writes against — the baselines, the cap height, the weight columns — is Apple's, read off a template shipped inside SF Symbols.app rather than guessed at.

One setting removed. ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME: AccentColor on StashWidgets named a colour that lives in the app's catalog and has never been compiled into the extension. It was inert while the extension had no catalog at all, and a build warning the moment StashSymbols gave it one.

Worth a decision

That setting being dead means the Live Activity draws in the system tint rather than amethyst, and always has. Making it amethyst means moving AccentColor into the shared catalog — a lock screen design change, and not this issue's to make. Happy to do it in its own change.

Tests

StashTests/StashMarkTests.swift loads StashMark out of the embedded StashWidgets.appex and checks it is a symbol image rather than a picture of one. That is this change's one silent failure mode: drop the shared catalog line from project.yml and the app still builds, every test still passes, and the control draws its label with no icon. Confirmed by removing that line and watching the tests fail.

867 tests pass. make lint clean. No build warnings.

Codex

  • review — one Low finding: the plan described three interpolation sources while the symbol ships nine. Accepted, PLAN.md corrected.
  • security-review — no findings. Nothing here reaches the network, reads a recording or touches the documents directory.
  • tests — three findings. Accepted the missing bundle test, above. Rejected a test that the control uses the mark: no API exposes a ControlWidget's rendered label, so a constant asserted against itself would read as coverage and be none. Rejected a test that re-runs the trace: it would need potrace on every machine and on CI to check an asset the build never regenerates.

Validated, and not

Validated in the simulator: the symbol compiles into both targets, loads from the extension bundle, and renders as the moustache at 12, 17, 24, 30 and 40 pt beside record.circle.fill, waveform and text — which is how its size was chosen. The build is installed on wes-iphone.

Not validated: Control Center, the lock screen and the Action Button on the device, and the tinted and accented states the system applies. The issue's legibility criterion lives there — the moustache's tapering tips are its thinnest feature and the smallest Control Center size is where they would go.

Closes #60

The Record control in Control Center, on the lock screen and on the Action Button drew `record.circle.fill` — the same glyph as every other app's record button. It now draws the Stash moustache. The label is still "Record" and nothing else about the control moves. ## What changed **The mark is a custom SF Symbol, traced out of the app icon.** A control renders its icon as a single-colour template, so the artwork itself cannot be used, and the repository had no glyph. The moustache is thresholded out of `icon-1024.png` and traced to one closed path — a trace rather than a redrawing, so the approved curve stays the approved curve. The waveform is dropped, per the decision on the issue: its bars would break up at the size Control Center draws. A symbol rather than an image set, so the control sizes and tints the mark the way it sized the glyph it replaces. All nine `<weight>-<scale>` variants carry the same path; a filled mark has no stroke to thin or thicken, and Xcode refuses a symbol with no `Regular-M` glyph, which is all a three-source variable template would have shipped. **The catalog is compiled into both targets.** The control renders inside `StashWidgets`, which cannot reach the app's bundle. The app's own catalog is not what is shared — it holds the 1024 px app icon, which an extension has no use for. **`Tools/make-stash-mark.py` is the trace**, so the derivation is checkable and the mark can be regenerated at another size from one constant. Nothing in the build runs it; it needs `potrace`. The template geometry it writes against — the baselines, the cap height, the weight columns — is Apple's, read off a template shipped inside SF Symbols.app rather than guessed at. **One setting removed.** `ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME: AccentColor` on `StashWidgets` named a colour that lives in the app's catalog and has never been compiled into the extension. It was inert while the extension had no catalog at all, and a build warning the moment `StashSymbols` gave it one. ## Worth a decision That setting being dead means the **Live Activity draws in the system tint rather than amethyst**, and always has. Making it amethyst means moving `AccentColor` into the shared catalog — a lock screen design change, and not this issue's to make. Happy to do it in its own change. ## Tests `StashTests/StashMarkTests.swift` loads `StashMark` out of the embedded `StashWidgets.appex` and checks it is a symbol image rather than a picture of one. That is this change's one silent failure mode: drop the shared catalog line from `project.yml` and the app still builds, every test still passes, and the control draws its label with no icon. Confirmed by removing that line and watching the tests fail. 867 tests pass. `make lint` clean. No build warnings. ## Codex - **review** — one Low finding: the plan described three interpolation sources while the symbol ships nine. **Accepted**, `PLAN.md` corrected. - **security-review** — no findings. Nothing here reaches the network, reads a recording or touches the documents directory. - **tests** — three findings. **Accepted** the missing bundle test, above. **Rejected** a test that the control uses the mark: no API exposes a `ControlWidget`'s rendered label, so a constant asserted against itself would read as coverage and be none. **Rejected** a test that re-runs the trace: it would need `potrace` on every machine and on CI to check an asset the build never regenerates. ## Validated, and not Validated in the simulator: the symbol compiles into both targets, loads from the extension bundle, and renders as the moustache at 12, 17, 24, 30 and 40 pt beside `record.circle.fill`, `waveform` and text — which is how its size was chosen. The build is installed on wes-iphone. **Not validated:** Control Center, the lock screen and the Action Button on the device, and the tinted and accented states the system applies. The issue's legibility criterion lives there — the moustache's tapering tips are its thinnest feature and the smallest Control Center size is where they would go. Closes #60
Control Center, the lock screen and the Action Button drew
`record.circle.fill`, which is what every other app's record button
draws. They now draw the Stash moustache.

A control renders its icon as a single-colour template, so the app icon
cannot be used and the repository had no glyph. The moustache is traced
out of `icon-1024.png` into a custom SF Symbol — a trace rather than a
redrawing, so the approved curve stays the approved curve, and a symbol
rather than an image set, so the control sizes and tints it the way it
sized the one it replaces. The waveform is dropped: its bars would break
up at the size Control Center draws.

The catalog holding it is compiled into both targets, because the control
renders inside the widget extension and cannot reach the app's bundle.
The tests load the symbol out of the built `.appex` for that reason —
dropping one line from `project.yml` otherwise leaves a control with no
icon and everything else green.

`ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME` goes from the extension.
It named a colour that has never been compiled into it, and it was inert
only because there was no catalog there to check it.

Refs #60

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
wk merged commit 6ab5d5c423 into main 2026-08-11 15:21:35 -04:00
wk deleted branch m3-60-stash-mark-control 2026-08-11 15:21:35 -04:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Stash/stash-ios!63
No description provided.