Files
OSGKeyboard/OSGKeyboardShared/Typing/TypingAutocapitalization.swift
T
Rocky a8d58d8f0c feat(polish): allow mood emoji on custom styles and ship Flow/ASR fixes
Custom polish styles can opt in to emotion-matched emoji (default off), with
prompt-level opt-in detection so paste-only styles keep model-added emoji.
Also include Volcengine API-Key ASR auth, voice-processing capture, PiP flash
fix, and related keyboard Shift/haptics reliability work.
2026-08-06 15:11:12 +08:00

72 lines
2.2 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// TypingAutocapitalization.swift
// OSGKeyboard · Shared
//
// Decides when the English letter page should arm Shift once for
// automatic capitalization (mirrors UITextAutocapitalizationType).
import Foundation
public enum TypingAutocapitalizationMode: String, Sendable, Equatable {
case none
case words
case sentences
case allCharacters
}
public enum TypingAutocapitalization: Sendable {
/// Whether Shift should be armed for the next Latin letter.
public static func shouldCapitalize(
precedingText: String?,
mode: TypingAutocapitalizationMode
) -> Bool {
switch mode {
case .none:
return false
case .allCharacters:
return true
case .words:
return needsWordCapitalization(precedingText)
case .sentences:
return needsSentenceCapitalization(precedingText)
}
}
// MARK: - Private
private static func needsWordCapitalization(_ preceding: String?) -> Bool {
guard let preceding, !preceding.isEmpty else { return true }
guard let last = preceding.last else { return true }
return last.isWhitespace || last.isNewline
}
private static func needsSentenceCapitalization(_ preceding: String?) -> Bool {
guard let preceding, !preceding.isEmpty else { return true }
// Walk backward from the caret. Trailing spaces are ignored; a newline
// itself starts a new line (system .sentences behavior, e.g. Notes
// after Return). Otherwise capitalize only after a sentence terminator.
var index = preceding.endIndex
while index > preceding.startIndex {
index = preceding.index(before: index)
let character = preceding[index]
if character.isNewline {
return true
}
if character.isWhitespace {
continue
}
return isSentenceTerminator(character)
}
return true
}
private static func isSentenceTerminator(_ character: Character) -> Bool {
switch character {
case ".", "!", "?", "…", "。", "", "":
return true
default:
return false
}
}
}