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
@@ -0,0 +1,71 @@
// EditHintScheduler.swift
// OSGKeyboard · Keyboard Extension
//
// Owns the edit-hint lifetime so every producer shares one expiration order.
import Foundation
import OSGKeyboardShared
@MainActor
final class EditHintScheduler {
typealias Sleeper = @MainActor (Duration) async -> Void
private let state: KeyboardState
private let sleeper: Sleeper
private var task: Task<Void, Never>?
private var generation: UInt64 = 0
init(
state: KeyboardState,
sleeper: @escaping Sleeper = { duration in
try? await Task.sleep(for: duration)
}
) {
self.state = state
self.sleeper = sleeper
}
func show(message: String, isPositive: Bool, duration: Duration) {
let scheduledGeneration = advanceGeneration()
task?.cancel()
state.editHint = message
state.editHintIsPositive = isPositive
let sleeper = sleeper
task = Task { @MainActor [weak self, sleeper] in
await sleeper(duration)
guard let self, self.generation == scheduledGeneration else {
return
}
self.task = nil
self.clearHint()
}
}
func clearPositive() {
guard state.editHintIsPositive else { return }
advanceGeneration()
task?.cancel()
task = nil
clearHint()
}
func invalidate() {
advanceGeneration()
task?.cancel()
task = nil
clearHint()
}
@discardableResult
private func advanceGeneration() -> UInt64 {
generation &+= 1
return generation
}
private func clearHint() {
state.editHint = nil
state.editHintIsPositive = false
}
}