feat(keyboard): harden polish/clipboard guards and ship 1.6.6 (build 59)

Keep marketing version at 1.6.6 and bump build to 59. Strengthen never-answer
polish safeguards, clipboard reply-intent continuity, voice undo UX, device
UITests harness, eval fixtures, and What's New assets.
This commit is contained in:
Rocky
2026-08-09 12:45:33 +08:00
parent bfe2cd2001
commit 5d7fcdf24e
41 changed files with 2876 additions and 464 deletions
@@ -2,7 +2,8 @@
// OSGKeyboard · Keyboard Extension
//
// Inserts Flow transcripts from the host app and surfaces polish warnings
// without re-running LLM polish in the extension.
// without re-running LLM polish in the extension. Also tracks the last
// voice insertion so the undo button can roll it back safely.
import OSGKeyboardShared
@@ -14,6 +15,14 @@ final class KeyboardTextInserter {
private let contextBeforeInput: () -> String?
private let scheduleAutoClearError: () -> Void
/// Exact string last inserted by voice (including any word-boundary
/// separator). Cleared after a successful undo or when the caret no
/// longer sits after that text.
private var lastInsertedText: String?
/// Suppresses availability refresh while we walk `deleteBackward`
/// for undo, so intermediate contexts don't flicker the button.
private var isUndoing = false
init(
state: KeyboardState,
insertText: @escaping (String) -> Void,
@@ -55,7 +64,9 @@ final class KeyboardTextInserter {
previousContext: contextBeforeInput(),
insertion: trimmed
)
insertText(separator + trimmed)
let inserted = separator + trimmed
insertText(inserted)
recordLastInsertion(inserted)
state.lastTranscript = ""
state.level = 0
if let warning = delivery.polishWarning {
@@ -66,4 +77,50 @@ final class KeyboardTextInserter {
}
OSGLog.keyboardExt.info("flow insert length=\(trimmed.count, privacy: .public)")
}
/// Roll back the last voice insertion when it is still at the caret.
func undoLastInsertion() {
guard let text = lastInsertedText, !text.isEmpty else { return }
guard let preceding = contextBeforeInput(), preceding.hasSuffix(text) else {
clearLastInsertion()
return
}
isUndoing = true
defer { isUndoing = false }
for _ in 0..<text.count {
deleteBackward()
}
clearLastInsertion()
OSGLog.keyboardExt.info("voice undo length=\(text.count, privacy: .public)")
}
/// Re-evaluate whether the recorded insertion is still undoable.
/// Call from `textDidChange` / `selectionDidChange`.
func refreshUndoAvailability() {
guard !isUndoing else { return }
guard let text = lastInsertedText, !text.isEmpty else {
if state.undoAvailable { state.undoAvailable = false }
return
}
let available = contextBeforeInput()?.hasSuffix(text) == true
if !available {
// Caret moved or the user edited the insertion drop the record.
lastInsertedText = nil
}
if state.undoAvailable != available {
state.undoAvailable = available
}
}
private func recordLastInsertion(_ text: String) {
lastInsertedText = text
state.undoAvailable = true
}
private func clearLastInsertion() {
lastInsertedText = nil
state.undoAvailable = false
}
}