feat(client/apple): the host tile wears its OS mark where the initial was
apple / swift (push) Successful in 4m32s
ci / web (push) Successful in 1m5s
docker / builders (--build-arg FEDORA_VERSION=44, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm, -f44) (push) Successful in 7s
docker / builders (ci/android-ci.Dockerfile, punktfunk-android-ci) (push) Successful in 6s
ci / docs-site (push) Successful in 1m18s
docker / builders (ci/arch-ci.Dockerfile, punktfunk-arch-ci) (push) Successful in 8s
docker / builders (ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 9s
docker / builders (ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 7s
docker / builders (ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 8s
docker / apps (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 18s
docker / apps (., web/Dockerfile, punktfunk-web) (push) Successful in 21s
docker / builders-arm64cross (push) Successful in 13s
docker / deploy-docs (push) Successful in 40s
ci / rust (push) Failing after 6m10s
ci / rust-arm64 (push) Successful in 14m47s
release / apple (push) Successful in 25m17s
apple / screenshots (push) Canceled after 13m52s
apple / swift (push) Successful in 4m32s
ci / web (push) Successful in 1m5s
docker / builders (--build-arg FEDORA_VERSION=44, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm, -f44) (push) Successful in 7s
docker / builders (ci/android-ci.Dockerfile, punktfunk-android-ci) (push) Successful in 6s
ci / docs-site (push) Successful in 1m18s
docker / builders (ci/arch-ci.Dockerfile, punktfunk-arch-ci) (push) Successful in 8s
docker / builders (ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 9s
docker / builders (ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 7s
docker / builders (ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 8s
docker / apps (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 18s
docker / apps (., web/Dockerfile, punktfunk-web) (push) Successful in 21s
docker / builders-arm64cross (push) Successful in 13s
docker / deploy-docs (push) Successful in 40s
ci / rust (push) Failing after 6m10s
ci / rust-arm64 (push) Successful in 14m47s
release / apple (push) Successful in 25m17s
apple / screenshots (push) Canceled after 13m52s
Apple parity with the Android card (a94b1d3c's sibling): the OS mark moves out of the status row and into the tile, replacing the monogram. The initial says nothing the name beside it doesn't already say — twice over on a row of home-worker-N boxes — while the mark identifies the machine at a glance. Both host surfaces, because on tvOS the console home is the only one there is: the touch cards (saved and discovered) and GamepadHomeView's badge, which needed the chain carried on HomeTile to reach it. Sized to the monogram's own point size, so it lands at ~48% of the tile everywhere, and tinted through the same foregroundStyle the letter used — the assets are template imagesets, so they follow it like an SF Symbol. A host that advertises no OS chain, or one we ship no art for, keeps its letter: `osIconImage` already returns nil for both, so a mixed row still reads as one set. The mark carries the accessibility label now, since the status row it used to ride no longer names the OS. Untouched: the widget draws no monogram, and AboutView's is the app's own logo, not a host's. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -54,6 +54,9 @@ private struct HomeTile: Identifiable {
|
||||
var hasLibrary = false
|
||||
/// Shows this SF symbol in the badge instead of the title monogram (the Add Host tile).
|
||||
var icon: String?
|
||||
/// The host's OS-identity chain. When we ship art for it, the badge wears the OS mark instead
|
||||
/// of the title's initial — the same substitution the touch cards make.
|
||||
var osChain: String?
|
||||
/// Offline saved host we hold a MAC for (and WoL is available) — activating it wakes first.
|
||||
var canWake = false
|
||||
let activate: () -> Void
|
||||
@@ -284,6 +287,7 @@ struct GamepadHomeView: View {
|
||||
// A pinned card is a shortcut, not a second host — Y (library) stays on the
|
||||
// host's own tile, where the host-level actions live.
|
||||
hasLibrary: profile == nil,
|
||||
osChain: host.osChain,
|
||||
canWake: autoWakeEnabled && PunktfunkConnection.wakeOnLANAvailable
|
||||
&& !online && !host.wakeMacs.isEmpty,
|
||||
activate: {
|
||||
@@ -297,6 +301,7 @@ struct GamepadHomeView: View {
|
||||
title: d.name,
|
||||
subtitle: "\(d.host):\(String(d.port))",
|
||||
isOnline: true,
|
||||
osChain: d.osChain,
|
||||
activate: { connectDiscovered(d) })
|
||||
}
|
||||
let add = HomeTile(
|
||||
@@ -420,6 +425,15 @@ private struct GamepadHostTile: View {
|
||||
Image(systemName: icon)
|
||||
.font(.system(size: Self.iconFont, weight: .semibold))
|
||||
.foregroundStyle(Color.brand)
|
||||
} else if let mark = osIconImage(for: tile.osChain) {
|
||||
// The OS mark stands in for the initial (template asset — tints like the text it
|
||||
// replaces), and carries the label, since nothing else on the tile names the OS.
|
||||
mark
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: Self.monogramFont, height: Self.monogramFont)
|
||||
.foregroundStyle(tile.filled ? .white : Color.brand)
|
||||
.accessibilityLabel(tile.osChain ?? "")
|
||||
} else {
|
||||
Text(monogram(tile.title))
|
||||
.font(.geistFixed(Self.monogramFont, .bold))
|
||||
|
||||
@@ -38,9 +38,16 @@ private func monogram(_ name: String) -> String {
|
||||
return String(first).uppercased()
|
||||
}
|
||||
|
||||
/// The squared monogram tile. `filled` = a solid brand-purple chip (saved hosts); otherwise a
|
||||
/// tinted outline (discovered hosts). Shows a spinner in place of the glyph while connecting.
|
||||
private func monogramTile(_ letter: String, m: CardMetrics, connecting: Bool, filled: Bool) -> some View {
|
||||
/// The squared host tile. `filled` = a solid brand-purple chip (saved hosts); otherwise a tinted
|
||||
/// outline (discovered hosts). Shows a spinner in place of the glyph while connecting.
|
||||
///
|
||||
/// `mark` is the host's OS mark, and it REPLACES the monogram when we have one: it identifies the
|
||||
/// machine better than its initial ever did, and on a row of similarly-named boxes the initial says
|
||||
/// nothing the name beneath it doesn't already say. A host that advertises no OS chain — or one we
|
||||
/// ship no art for — keeps its letter, so a mixed row still reads as one set.
|
||||
private func monogramTile(
|
||||
_ letter: String, osChain: String?, m: CardMetrics, connecting: Bool, filled: Bool
|
||||
) -> some View {
|
||||
let shape = RoundedRectangle(cornerRadius: m.radius - 3, style: .continuous)
|
||||
return ZStack {
|
||||
shape.fill(filled
|
||||
@@ -50,6 +57,16 @@ private func monogramTile(_ letter: String, m: CardMetrics, connecting: Bool, fi
|
||||
: AnyShapeStyle(Color.brand.opacity(0.14)))
|
||||
if connecting {
|
||||
ProgressView().tint(filled ? .white : Color.brand)
|
||||
} else if let mark = osIconImage(for: osChain) {
|
||||
// Template asset — tints from foregroundStyle exactly like the letter it stands in for.
|
||||
// Labelled, because this is where the OS is now announced: it used to ride the status
|
||||
// row below, which no longer carries it.
|
||||
mark
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: m.monogram, height: m.monogram)
|
||||
.foregroundStyle(filled ? Color.white : Color.brand)
|
||||
.accessibilityLabel(osChain ?? "")
|
||||
} else {
|
||||
// Fixed size (not Dynamic Type): the glyph is pinned inside a fixed tile, so it must
|
||||
// not scale up and spill out at large accessibility text sizes. minimumScaleFactor +
|
||||
@@ -133,7 +150,8 @@ struct HostCardView: View {
|
||||
let m = CardMetrics.current
|
||||
return Button(action: onConnect) {
|
||||
HStack(spacing: m.spacing) {
|
||||
monogramTile(monogram(host.displayName), m: m, connecting: isConnecting, filled: true)
|
||||
monogramTile(monogram(host.displayName), osChain: host.osChain,
|
||||
m: m, connecting: isConnecting, filled: true)
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
// The chip rides the TITLE line, anchored to the card's trailing edge — not
|
||||
// trailing the name, where it read as part of the title, and not on a line of
|
||||
@@ -295,16 +313,7 @@ struct HostCardView: View {
|
||||
/// certificate is pinned (the lock state, spelled out).
|
||||
@ViewBuilder private func statusRow(_ m: CardMetrics) -> some View {
|
||||
HStack(spacing: 6) {
|
||||
// The host's OS mark leads the row (template asset — tints like an SF Symbol);
|
||||
// absent entirely for a host that never advertised one, so those cards render
|
||||
// exactly as they always did.
|
||||
if let mark = osIconImage(for: host.osChain) {
|
||||
mark
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: m.status + 2, height: m.status + 2)
|
||||
.accessibilityLabel(host.osChain ?? "")
|
||||
}
|
||||
// The OS mark used to lead this row; it is the tile's glyph now (see monogramTile).
|
||||
RoundedRectangle(cornerRadius: 1.5)
|
||||
.fill(isOnline ? Color.green : Color.secondary.opacity(0.4))
|
||||
.frame(width: 6, height: 6)
|
||||
@@ -364,7 +373,8 @@ struct DiscoveredCardView: View {
|
||||
let m = CardMetrics.current
|
||||
return Button(action: onConnect) {
|
||||
HStack(spacing: m.spacing) {
|
||||
monogramTile(monogram(discovered.name), m: m, connecting: false, filled: false)
|
||||
monogramTile(monogram(discovered.name), osChain: discovered.osChain,
|
||||
m: m, connecting: false, filled: false)
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text(discovered.name)
|
||||
.font(.geist(m.name, .bold, relativeTo: .title3))
|
||||
@@ -375,14 +385,7 @@ struct DiscoveredCardView: View {
|
||||
.foregroundStyle(.secondary)
|
||||
.lineLimit(1)
|
||||
HStack(spacing: 6) {
|
||||
// Same leading OS mark as a saved card's status row — live from the advert.
|
||||
if let mark = osIconImage(for: discovered.osChain) {
|
||||
mark
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: m.status + 2, height: m.status + 2)
|
||||
.accessibilityLabel(discovered.osChain)
|
||||
}
|
||||
// The advert's OS mark is the tile's glyph now (see monogramTile).
|
||||
Image(systemName: discovered.requiresPairing
|
||||
? "lock.fill" : "antenna.radiowaves.left.and.right")
|
||||
.font(.system(size: m.status))
|
||||
|
||||
Reference in New Issue
Block a user