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:
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user