Every profile row in the layer picker now shows its colour chip, so the dropdown is
where you SEE the catalog rather than read it. It stays a Picker for that: the
platform draws the selection checkmark in its own column, which leaves each row's
icon free to be the chip. Hand-rolling the selection would have cost one or the
other.
Delete comes back to the menu it was taken from, next to New, Edit and Duplicate —
all four with icons now. Its warning goes with it: the count of hosts that fall back
and pinned cards that disappear is in the question, not discovered afterwards. The
editor sheet drops its own delete section rather than offering the same destructive
action in two places.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The create/update flow was four menu items, three bare text alerts and a submenu of
colour NAMES with no colour anywhere on it. Making a "Work" profile meant: name it in
an alert, find it again in the scope menu, open a submenu, and pick "Amber" on faith.
A profile is a name and a colour, so they are decided together now — in one editor
sheet that serves create, duplicate and edit, with a live chip at the top showing
exactly what the host cards will render. The palette is swatches you can see, in a
grid, with a checkmark AND a ring on the chosen one (the tick alone washes out on the
pale hues) and a 44pt target under each 30pt dot. Default leads the row, so "no
colour" is a choice on the same shelf as the rest rather than the absence of one.
Duplicate opens the sheet rather than committing on the spot: it arrives carrying the
source's colour and overrides with a free name filled in, so it can be renamed before
it exists rather than after. Creating lands you in the new profile — being left on
the defaults is how you end up editing the wrong layer.
Delete moved into the sheet with the rest, and its warning is where the button is:
the count of hosts and pinned cards that will change sits under it before you press
it, not only in the confirmation after.
The name's uniqueness check now reports inline and in red instead of through a
disabled button and an alert message.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
About was a bare "Punktfunk / Version x" header over 885 KB of license text — it
answered the one question nobody opens About to ask. It is now the shape every Mac
user already knows: the app's icon, its name, the version (selectable, because a bug
report is worth more with it), the tagline, and then the ways out — documentation,
community, source, licenses. The license wall is still `AcknowledgementsView`, one
push in on iOS/tvOS and a sheet on macOS, where a preferences tab has no navigation
stack to push onto.
Every platform hides the app icon somewhere different and tvOS can't hand it over at
all (layered assets, no single image), so there's a drawn fallback in the same brand
gradient and Geist monogram as the host cards — where the real icon is unavailable it
reads as a mark, not as a broken image. tvOS also has no browser: the addresses are
text to read off the screen rather than links to nowhere.
And profile colours were half-built by me: the catalog stored `accent`, the chips
tinted from it, and nothing in the app ever SET one. The scope menu grows "Color ▸"
over a fixed palette — the chip is small tinted text on a tinted capsule, and a
colour picked freehand lands somewhere unreadable often enough to matter. The palette
is what this client OFFERS, not what it accepts: any `#RRGGBB` another platform
writes still renders, which is what Android needs from this field.
The colour now shows wherever the profile is named — the scope switcher's dot, the
card chips, and the HUD's session line, which tints to the same colour the card that
launched it wore. A colour is an identifier, and an identifier that changes between
surfaces isn't one.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The resolution marker hung off the Match-window toggle, which read as if that toggle
alone were overridden. Match-window, width and height are ONE override — they reset
together — so the marker belongs to the group, under the size control that ends it,
not to the first of the two rows that write it.
The new section footers inherited the app's 17pt body font, so the profile picker's
description sat visibly larger than every other description in Settings. Same style
as the footers that were already there.
And the marker carries a bordered button, which needs more air under the control
above it than a line of text does — most visible under the segmented refresh picker.
Added inside the marker, so every row that shows it is spaced alike.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The `described` idiom capped its caption at 360pt, which was only ever about
READING — past roughly 46 characters a line stops scanning well on a wide Mac window
or an iPad detail pane. On an iPhone the cell is narrower than the cap in the first
place, so the cap did nothing and every caption laid out to the full cell width,
running its last line straight under the row's switch.
`CaptionWidth` now carries both limits: the reading cap, plus an iOS trailing inset
that reserves the control column (a `UISwitch` is 51pt, so 60 with room). Order
matters — the inset shrinks what the text is offered, then the cap applies, so a
narrow phone cell clears the switch and a wide pane still caps.
One rule in one place: the override marker and the iOS resolution wheel's caption
each carried their own copy of the 360, and neither would have grown the inset.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
One chrome served both platforms and only worked on one. In the iOS settings list
the macOS shape — a bordered `.menuStyle(.button)` with the caption stacked under it
— rendered as neither a row nor a control: icon only, no label, and four lines of
wrapped caption making the first thing on the screen a block of text.
The menu CONTENTS stay one definition; the chrome is now per platform. macOS keeps
the button menu heading the preferences window. iOS gets a standard value row —
"Editing" on the left, the current layer on the right, the system's up/down chevron
— with the caption where a caption belongs in a grouped list: the section footer.
The two prompts the menu can raise moved into a shared `profilePrompts` so both
chromes carry them without a second copy.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Reset read as prose. Next to "Overrides Default settings" in the same tint and the
same size, the one action that can undo an override looked like the third word of
the notice. It is a bordered control now, pushed to the far edge of the caption
line with an undo glyph.
The scope switcher showed above the About tab. It sat over the whole macOS TabView,
so on the acknowledgements page — which edits nothing — it read as belonging to
them. The tabs are tagged and the switcher sits that one out.
Pinned cards were taller than their host's. The profile chip had a line of its own,
so a card with a profile and one without were different heights and a pin stuck out
of its grid row. The chip rides the title line instead, which is also where it reads
as "this card connects with that". Prominence is fill and weight only — a chip with
a bigger type size would have brought the height difference straight back.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`ContentView.body` grew two modifiers and stopped type-checking on the iOS slice —
"unable to type-check this expression in reasonable time", a failure macOS builds
never show and the one Apple CI never builds. Split into the screen plus its
lifecycle drivers, then the prompt chain, with each alert's presentation Binding
lifted out the way the deep-link one already was.
tvOS couldn't be checked from the command line at all: HomeView imports the slide
transition, which ships with the Xcode project only because its manifest breaks
SwiftPM's whole-graph validation. `canImport` instead of a bare `os(tvOS)` gate
makes the tvOS sources compile without it, and the app still gets the transition
because there the module is present. Both slices now typecheck by hand.
The scope switcher is tvOS-gated with them: a name prompt and a nested management
menu are not what a remote does well, and §5.4 keeps profile EDITING off
controller-first surfaces in v1 — they honor bindings and render pinned cards.
Also: release the session's settings latch when a connect fails, is refused, or is
abandoned mid-handshake. Only `disconnect` cleared it, so a failed dial left the
would-be session's resolution latched over the globals.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
One settings surface, two layers. A scope switcher at the top of the Mac window and
the iOS sidebar swaps the whole screen between Default settings and one profile's
overrides; the section builders stay the single definition of every row and just
change which layer their binding reads and writes (design §5.1). A parallel profile
editor would have drifted from this one field by field, and the revamp's captions
and curation would have had to be written twice.
`SettingsFields` is where a row's three faces meet — the UserDefaults key its global
lives under, the overlay slot an override lives in, and the name a reset carries.
Keeping them in one place is what stops a row from writing an override the reset
button can't find.
Every row shows the EFFECTIVE value, so an untouched row reads as the live global.
Touching a control records the override — always, even when the new value equals
today's global, because that is a pin and the profile must keep it when the global
later moves. Nothing is inferred by diffing at save time.
Overridden rows say so (accent dot + "Overrides Default settings") and carry the
only way back: an explicit Reset. That pair is not garnish. The model deliberately
never infers "not overridden" from a value comparison, so without a reset affordance
a profile is a one-way door.
Tier-G and tier-H rows don't render in profile scope at all — this device's speaker,
microphone and channel, its pointer capture and haptics, the HUD corner, the library
switch, the gamepad-UI switch, which physical pad is forwarded, and auto-wake, which
is about a host and a network rather than about Game vs Work. Sections that would be
left empty (Session off macOS, Library) collapse instead of rendering a bare group.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>