feat(keyboard): ship AI hint carousel, home library cards, and clipboard polish

Rotate AI idle suggestions with optional remote packs, move history/dictionary onto self-sizing Home preview cards, harden clipboard capture/prompting, and simplify keyboard chrome by dropping most liquid-glass shadows.
This commit is contained in:
Rocky
2026-08-13 01:00:51 +08:00
parent fd6e0d3e7e
commit 9f308fadd2
202 changed files with 10897 additions and 5962 deletions
@@ -14,11 +14,97 @@ final class ClipboardHistoryPolicyTests: XCTestCase {
XCTAssertNil(ClipboardHistoryPolicy.acceptedText(from: "123456"))
XCTAssertNil(ClipboardHistoryPolicy.acceptedText(from: "12-34-56"))
XCTAssertNotNil(ClipboardHistoryPolicy.acceptedText(from: "订单号 1234567890"))
XCTAssertNotNil(ClipboardHistoryPolicy.acceptedText(from: "订单号 123456"))
XCTAssertNotNil(ClipboardHistoryPolicy.acceptedText(from: "账号 123456"))
XCTAssertEqual(ClipboardHistoryPolicy.acceptedText(from: "2026"), "2026")
XCTAssertEqual(ClipboardHistoryPolicy.acceptedText(from: "08-11"), "08-11")
XCTAssertEqual(ClipboardHistoryPolicy.acceptedText(from: "2026-08-11"), "2026-08-11")
XCTAssertEqual(ClipboardHistoryPolicy.acceptedText(from: "20260811"), "20260811")
XCTAssertNil(ClipboardHistoryPolicy.acceptedText(from: "02-31"))
XCTAssertNil(ClipboardHistoryPolicy.acceptedText(from: "20260231"))
}
func testAcceptsPlainTextAndEmoji() {
XCTAssertEqual(ClipboardHistoryPolicy.acceptedText(from: " hello "), "hello")
XCTAssertEqual(ClipboardHistoryPolicy.acceptedText(from: "你好😀"), "你好😀")
XCTAssertEqual(ClipboardHistoryPolicy.acceptedText(from: "acct-123"), "acct-123")
}
func testSecureFieldPasteboardGenerationRemainsSuppressedAfterFocusChanges() {
XCTAssertTrue(
ClipboardHistoryPolicy.shouldSuppressCapture(
changeCount: 42,
secureFieldSuppressedChangeCount: 42
)
)
XCTAssertFalse(
ClipboardHistoryPolicy.shouldSuppressCapture(
changeCount: 43,
secureFieldSuppressedChangeCount: 42
)
)
}
func testRejectsConservativeSensitiveContentMatrix() {
let jwt = """
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.\
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkphbmUgRG9lIn0.\
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
"""
let rejected: [(String, ClipboardHistoryPolicy.RejectionReason)] = [
("-----BEGIN PRIVATE KEY-----", .privateKey),
("-----BEGIN RSA PRIVATE KEY-----", .privateKey),
(jwt, .jwt),
("Authorization: Bearer abcdefghijklmnopqrstuvwxyz012345", .bearerToken),
("Bearer abcd+efgh/ijklmnop==", .bearerToken),
("sk-proj-abcdefghijklmnopqrstuvwxyz0123456789", .providerKey),
("sk-ant-abcdefghijklmnopqrstuvwxyz0123456789", .providerKey),
("github_pat_abcdefghijklmnopqrstuvwxyz012345", .providerKey),
("4111 1111 1111 1111", .paymentCard),
]
for (text, reason) in rejected {
XCTAssertEqual(
ClipboardHistoryPolicy.rejectionReason(for: text),
reason,
"Expected \(reason) for \(text)"
)
XCTAssertNil(ClipboardHistoryPolicy.acceptedText(from: text))
}
}
func testSensitiveFilterAvoidsCommonFalsePositives() {
let accepted = [
"Bearer is an authentication scheme",
"sk-short-example",
"订单号 1234567890",
"年份 2026",
"账号 123456",
"4111 1111 1111 1112",
"490154203237518",
]
for text in accepted {
XCTAssertEqual(ClipboardHistoryPolicy.acceptedText(from: text), text)
}
}
func testEntryAndPayloadByteBoundaries() {
let exactEntry = String(repeating: "x", count: ClipboardHistoryPolicy.maxEntryUTF8Bytes)
let oversizedEntry = exactEntry + "x"
XCTAssertEqual(ClipboardHistoryPolicy.acceptedText(from: exactEntry), exactEntry)
XCTAssertEqual(
ClipboardHistoryPolicy.rejectionReason(for: oversizedEntry),
.exceedsEntrySize
)
let fifteen = (0..<15).map { index in
ClipboardHistoryEntry(text: String(repeating: Character("\(index % 10)"), count: 16_000))
}
let seventeen = (0..<17).map { index in
ClipboardHistoryEntry(text: "\(index)-" + String(repeating: "x", count: 16_000))
}
XCTAssertTrue(ClipboardHistoryPolicy.encodedPayloadFitsLimit(fifteen))
XCTAssertFalse(ClipboardHistoryPolicy.encodedPayloadFitsLimit(seventeen))
}
func testMergeDedupesAndPinsNewest() {
@@ -41,19 +127,3 @@ final class ClipboardHistoryPolicyTests: XCTestCase {
XCTAssertEqual(tokens, ["great", "experience", "he", "writes"])
}
}
@MainActor
final class ClipboardHistoryStoreTests: XCTestCase {
func testIngestPersistsAndCapsAtFifteen() {
let suiteName = "ClipboardHistoryStoreTests.\(UUID().uuidString)"
let suite = UserDefaults(suiteName: suiteName)!
defer { suite.removePersistentDomain(forName: suiteName) }
let store = ClipboardHistoryStore(defaults: suite)
for index in 0..<20 {
store.ingest(rawText: "item-\(index)", changeCount: index)
}
XCTAssertEqual(store.entries.count, 15)
XCTAssertEqual(store.entries.first?.text, "item-19")
XCTAssertEqual(store.entries.last?.text, "item-5")
}
}