feat(keyboard): rank pinyin by habit and persist user dictionaries

Erase dialect syllables before abbrev so wom prefers 我们, flush librime
on leave, and add a settings action to forget learned Chinese and English order.
This commit is contained in:
Rocky
2026-08-14 21:49:03 +08:00
parent 2c3a3f80f3
commit b13a1e904b
15 changed files with 588 additions and 5 deletions
@@ -26,7 +26,13 @@ public final class TypingSessionController: ObservableObject {
@Published public private(set) var lastErrorNeedsHostDeployment: Bool = false
/// When true, English suggestions / autocorrect stay off (secure fields).
@Published public var suggestionsEnabled: Bool = true
/// Chinese composition is also skipped so passwords never enter Rime userdb.
@Published public var suggestionsEnabled: Bool = true {
didSet {
guard oldValue, !suggestionsEnabled else { return }
abandonChineseComposition()
}
}
/// Chevron appears only for Chinese composition with at least two candidates.
public var canExpandCandidatePanel: Bool {
@@ -306,6 +312,12 @@ public final class TypingSessionController: ObservableObject {
return handleEnglishCharacter(ch)
}
if !suggestionsEnabled {
clearOneShotShiftIfNeeded()
abandonChineseComposition()
return .insert(String(ch))
}
// Chinese + Shift: insert Latin directly (iOS-style mix-in), leave Rime
// composition untouched. Rime's alphabet is lowercase-only, so uppercase
// keycodes would otherwise be rejected with no output.
@@ -327,6 +339,10 @@ public final class TypingSessionController: ObservableObject {
return handleEnglishSpace()
}
clearPeriodShortcut()
if !suggestionsEnabled {
abandonChineseComposition()
return .insert(" ")
}
let text = engine.processSpace() ?? " "
composition = engine.composition
syncCandidatePanelVisibility()
@@ -338,6 +354,10 @@ public final class TypingSessionController: ObservableObject {
if language == .english {
return commitEnglishWord(suffix: "\n")
}
if !suggestionsEnabled {
abandonChineseComposition()
return .insert("\n")
}
let text = engine.processReturn() ?? "\n"
composition = engine.composition
syncCandidatePanelVisibility()
@@ -349,6 +369,9 @@ public final class TypingSessionController: ObservableObject {
if language == .english {
return selectEnglishCandidate(at: index)
}
if !suggestionsEnabled {
return .none
}
guard composition.candidates.indices.contains(index) else { return .none }
// Display order may put phrases before first-syllable chars; select by engine index.
let engineIndex = composition.candidates[index].engineIndex
@@ -360,6 +383,13 @@ public final class TypingSessionController: ObservableObject {
return text.isEmpty ? .none : .insert(text)
}
/// Drop in-flight pinyin so secure fields cannot commit into userdb.
private func abandonChineseComposition() {
engineStorage?.clearComposition()
composition = .empty
isCandidatePanelExpanded = false
}
// MARK: - English
private func handleEnglishSpace() -> TypingOutput {