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
@@ -35,15 +35,18 @@ public enum DictationTextComposer {
}
}
let normalizedAnchor = normalizeForOverlap(anchor)
let normalizedLive = normalizeForOverlap(live)
let normalizedAnchor = TranscriptOverlapUtilities.normalized(anchor)
let normalizedLive = TranscriptOverlapUtilities.normalized(live)
let anchorNormChars = Array(normalizedAnchor)
let liveNormChars = Array(normalizedLive)
let normProbe = min(64, anchorNormChars.count, liveNormChars.count)
if normProbe > 0 {
for length in stride(from: normProbe, through: 3, by: -1) {
if anchorNormChars.suffix(length).elementsEqual(liveNormChars.prefix(length)) {
let drop = rawDropCount(in: live, normalizedPrefixLength: length)
let drop = TranscriptOverlapUtilities.rawDropCount(
in: live,
normalizedPrefixLength: length
)
return anchor + String(live.dropFirst(drop))
}
}
@@ -57,7 +60,7 @@ public enum DictationTextComposer {
let first = live.unicodeScalars.first else {
return false
}
return isCJK(last) && isCJK(first)
return HanScript.isIdeograph(last) && HanScript.isIdeograph(first)
}
/// Separator to place between existing document text and an inserted
@@ -71,7 +74,7 @@ public enum DictationTextComposer {
return ""
}
if CharacterSet.whitespacesAndNewlines.contains(last) { return "" }
if isCJK(last) || isCJK(first) { return "" }
if HanScript.isIdeograph(last) || HanScript.isIdeograph(first) { return "" }
// No space after opening brackets/quotes ("(", "[", "", """).
if CharacterSet(charactersIn: "([{\u{201C}\u{2018}\u{300C}\u{300E}\u{3010}\u{FF08}").contains(last) {
return ""
@@ -80,33 +83,4 @@ public enum DictationTextComposer {
if CharacterSet.punctuationCharacters.contains(first) { return "" }
return " "
}
static func normalizeForOverlap(_ text: String) -> String {
text.unicodeScalars.filter {
!CharacterSet.whitespacesAndNewlines.contains($0)
&& !CharacterSet.punctuationCharacters.contains($0)
}.map { Character($0) }.reduce(into: "") { $0.append($1) }
}
private static func rawDropCount(in text: String, normalizedPrefixLength: Int) -> Int {
var normalizedCount = 0
var rawIndex = text.startIndex
while rawIndex < text.endIndex, normalizedCount < normalizedPrefixLength {
let character = text[rawIndex]
if !character.isWhitespace, !character.isPunctuation {
normalizedCount += 1
}
rawIndex = text.index(after: rawIndex)
}
return text.distance(from: text.startIndex, to: rawIndex)
}
private static func isCJK(_ scalar: UnicodeScalar) -> Bool {
switch scalar.value {
case 0x3400...0x4DBF, 0x4E00...0x9FFF, 0xF900...0xFAFF:
return true
default:
return false
}
}
}