Merge branch 'feature/intelligent-polish-and-personal-dict' into main

Resolved real conflicts introduced by translation-polish-2 (#3) and
docs refresh (#4) being merged to main during development:

- OSGKeyboardShared/Models/ProviderConfig.swift
  Add polishIntensity field alongside translationTargetLocaleId /
  polishScenarioId / handednessPreference. Both new fields coexist.

- OSGKeyboardShared/Services/AppGroupStore.swift
  Keep main's translation setters; add polishIntensity setter,
  detectedAppContext setter, and personalDictionary getter/setter.

- OSGKeyboardShared/Services/PolishingService.swift
  Merge v0.2.1 translate-and-polish path with v0.3.0 context-aware
  intelligent prompt. New polish() entry point accepts both modes;
  translation mode still uses TranslationPrompt, polish mode now
  uses buildPrompt(for:context:).

- OSGKeyboard/Views/SettingsView.swift
  Compose localEngineSettingsSection (main) with polishIntensitySection,
  languageAndModelsSection, systemPromptLinkSection, personalDictionaryLinkSection (feature).

- OSGKeyboardShared/{en,zh-Hans}.lproj/Shared.strings
  Concat scenario keys + polish intensity / app context / dictionary keys.

Auto-merged without conflict:
- AppGroupStore new methods (localASRBackend, etc.)
- FlowSessionManager, KeyboardViewController (auto-merged)
- MaterialIcon, HistoryView (auto-merged, my icons added on top)

Co-authored-by: Mavis <Mavis@hkgood.dev>
This commit is contained in:
Mavis
2026-07-03 07:34:17 +00:00
19 changed files with 1777 additions and 49 deletions
@@ -381,6 +381,13 @@ public final class KeyboardViewController: UIInputViewController {
return
}
// v0.3.0: detect the app context (code / email / chat / doc)
// *before* either path. The Flow session's polisher and the
// legacy handoff's polisher both read this from the App
// Group. Cheap (heuristic over 2 KB), safe to run every
// press of the mic.
detectAndStoreAppContext()
if FlowSessionBridge.isSessionActive() {
startFlowRecording()
} else {
@@ -420,6 +427,22 @@ public final class KeyboardViewController: UIInputViewController {
debug("startFlowRecording")
}
/// v0.3.0: run the 3-fallback context detector on the text
/// already at the cursor and persist the result to the App
/// Group. Cheap (heuristic over 2 KB of preceding text) so
/// safe to run on every press of the mic; we deliberately
/// avoid hitting the App Group on every keystroke.
private func detectAndStoreAppContext() {
let preceding = textDocumentProxy.documentContextBeforeInput
let store = AppGroupStore()
let detector = AppContextDetector()
let context = detector.detect(
precedingText: preceding,
storedCache: store.detectedAppContext
)
store.setDetectedAppContext(context)
}
private func startUtteranceCountdown() {
utteranceStartedAt = Date().timeIntervalSince1970
state.utteranceRemainingSeconds = Int(FlowSessionKeys.maxUtteranceDuration)