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:
@@ -44,11 +44,21 @@ struct DesignSystemTests {
|
||||
#expect(!approxEqual(waiting, stuck))
|
||||
}
|
||||
|
||||
@Test("semantic status colors match the desktop/web status palette")
|
||||
/// T-iOS-34 **有意改写**:这三个 token 从"单值"变成了 scheme-adaptive
|
||||
/// (深色 = web 原值不变,浅色 = 新增的可读变体,见 `Tokens.swift`)。原用例
|
||||
/// 用 `UIColor(color).getRed(...)` 隐式按**测试进程当前 trait**(浅色)解析,
|
||||
/// 于是量到了新的浅色值。断言的意图没变 —— "深色档必须逐字节等于桌面 web 的
|
||||
/// 状态色" —— 只是现在必须**显式指定深色 trait** 才能表达这个意图。
|
||||
/// 浅色档的取值与对比度门槛由 `AppThemeTests` 覆盖。
|
||||
@Test("semantic status colors match the desktop/web status palette (dark scheme)")
|
||||
func statusColorsMatchDirection() {
|
||||
assertColor(StatusStyle.style(for: DisplayStatus.working).color, r: 70, g: 208, b: 127) // #46D07F web --green
|
||||
assertColor(StatusStyle.style(for: DisplayStatus.waiting).color, r: 245, g: 177, b: 76) // #F5B14C web --amber
|
||||
assertColor(StatusStyle.style(for: DisplayStatus.stuck).color, r: 255, g: 107, b: 107) // #FF6B6B web --red
|
||||
let dark = UITraitCollection(userInterfaceStyle: .dark)
|
||||
assertColor(StatusStyle.style(for: DisplayStatus.working).color, in: dark,
|
||||
r: 70, g: 208, b: 127) // #46D07F web --green
|
||||
assertColor(StatusStyle.style(for: DisplayStatus.waiting).color, in: dark,
|
||||
r: 245, g: 177, b: 76) // #F5B14C web --amber
|
||||
assertColor(StatusStyle.style(for: DisplayStatus.stuck).color, in: dark,
|
||||
r: 255, g: 107, b: 107) // #FF6B6B web --red
|
||||
}
|
||||
|
||||
@Test("the wire ClaudeStatus bridge covers all five cases")
|
||||
@@ -150,6 +160,13 @@ struct DesignSystemTests {
|
||||
assertRGBA(rgba(color), r: r, g: g, b: b)
|
||||
}
|
||||
|
||||
/// Same, but resolving an adaptive token in an explicit scheme.
|
||||
private func assertColor(
|
||||
_ color: Color, in traits: UITraitCollection, r: Int, g: Int, b: Int
|
||||
) {
|
||||
assertRGBA(rgba(UIColor(color).resolvedColor(with: traits)), r: r, g: g, b: b)
|
||||
}
|
||||
|
||||
private func assertRGBA(_ value: RGBA, r: Int, g: Int, b: Int, tolerance: CGFloat = 0.02) {
|
||||
#expect(abs(value.r - CGFloat(r) / 255) < tolerance)
|
||||
#expect(abs(value.g - CGFloat(g) / 255) < tolerance)
|
||||
|
||||
Reference in New Issue
Block a user