feat(ios,android): P2 wave, git panel, token UX, per-host WS token, docs
App layer, four sequential slices (a shared .xcodeproj means adding files regenerates it, so these could not run in parallel): - token UX end to end: pairing prompts for a token when a host 401s, POST /auth validates it, and 204-without-Set-Cookie is correctly read as "this server has auth disabled" rather than "authenticated". A host paired before the token was turned on recovers by re-pairing in place. Remove-host now exists and finally gives PushRegistrar.handleHostRemoved a caller. - project git panel + worktree lifecycle (T-iOS-32) + claude --resume history — the parity gap with Android and the web front end. - terminal search (T-iOS-33) and voice PTT (T-iOS-31) with an epoch guard so a session switch between dictation and confirm cannot inject into the wrong session. - theme + Dynamic Type (T-iOS-34) and web ?join= interop (T-iOS-35). RootView no longer hard-locks .preferredColorScheme(.dark). Also unpins SwiftTerm to 1.15.0 by dropping the local hasActiveSelection that collided with the upstream one, verified green from a fresh derivedDataPath. Includes the two HIGH fixes the security review found: - iOS resolved the WS token host-independently, so a token-gated host sitting next to an open one could never open a terminal and no on-screen remedy could fix it. Now one transport per host; cross-host leakage is structurally impossible since both read paths return only that host's own value. - Android reported the host's own git-credential 401 (git-ops.ts:108, "Push authentication required on the host.") as "your access token is wrong", because a blanket 401 mapping ran ahead of the per-route one. Git-write routes are now ROUTE_DEFINED and keep the server's message. And the doc sync: README/ios README no longer claim the client is unmerged on feat/ios-client, the Clients section finally lists Android, and the plan checkboxes reflect what is actually built. iOS 534 app tests + 452 package tests; Android 687 tests.
This commit is contained in:
@@ -72,6 +72,9 @@ struct TelemetryChip: View {
|
||||
.background(.quaternary, in: Capsule())
|
||||
.opacity(isStale ? DS.Opacity.stale : 1)
|
||||
.grayscale(isStale ? 1 : 0)
|
||||
// T-iOS-34 · a `lineLimit(1)` tabular pill cannot wrap, so past a point
|
||||
// extra size only truncates it. Same ceiling as `dsMetaText`.
|
||||
.dynamicTypeSize(...DS.Typography.numericClamp)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,6 +125,12 @@ struct DSButtonStyle: ButtonStyle {
|
||||
enum Kind { case primary, secondary, destructive }
|
||||
var kind: Kind = .primary
|
||||
|
||||
/// T-iOS-34 · how far a label may shrink before it would rather overflow.
|
||||
/// A gate's two half-width buttons at AX5 hold a word that cannot hyphenate
|
||||
/// ("Approve"); a small scale-down plus wrapping keeps it inside the pill
|
||||
/// instead of bleeding past the rounded edge.
|
||||
fileprivate static let labelMinScale: CGFloat = 0.75
|
||||
|
||||
func makeBody(configuration: Configuration) -> some View {
|
||||
DSButtonBody(kind: kind, configuration: configuration)
|
||||
}
|
||||
@@ -137,6 +146,12 @@ struct DSButtonStyle: ButtonStyle {
|
||||
var body: some View {
|
||||
configuration.label
|
||||
.font(DS.Typography.body.weight(.semibold))
|
||||
// Dynamic Type: wrap + center + a bounded shrink, then let the
|
||||
// pill grow taller. `minHeight` (not `height`) is what keeps a
|
||||
// wrapped AX5 label from being clipped to 44pt.
|
||||
.multilineTextAlignment(.center)
|
||||
.minimumScaleFactor(DSButtonStyle.labelMinScale)
|
||||
.padding(.horizontal, DS.Space.sm8)
|
||||
.frame(maxWidth: .infinity, minHeight: DS.Layout.minHitTarget)
|
||||
.foregroundStyle(foreground)
|
||||
.background(background, in: RoundedRectangle(cornerRadius: DS.Radius.md12))
|
||||
|
||||
Reference in New Issue
Block a user