Expand onboarding and adaptive keyboard intelligence

Add resilient usage analytics, OOBE gateway flows, clipboard semantic ranking, purchase recovery, style learning, and managed current-information search.
This commit is contained in:
Rocky
2026-08-22 16:33:18 +08:00
parent ac374631ae
commit e5a83843db
162 changed files with 23574 additions and 1013 deletions
+58 -7
View File
@@ -50,6 +50,7 @@ struct AIKeyboardView: View {
@ObservedObject var state: KeyboardState
@ObservedObject var typing: TypingSessionController
@ObservedObject private var clipboardHistory = ClipboardHistoryStore.shared
@ObservedObject private var semanticRanking = ClipboardSemanticRankingStore.shared
let onInsert: (String) -> Void
@AppStorage("keyboard.assistant.longPressCoachCount")
@@ -108,10 +109,13 @@ struct AIKeyboardView: View {
selectedSkillPage = 0
resetCarousel()
}
.onChange(of: state.enabledClipboardSkillIDs) { _, _ in
.onChange(of: state.enabledClipboardSkills) { _, _ in
selectedSkillPage = 0
resetCarousel()
}
.onChange(of: semanticRanking.snapshot) { _, _ in
selectedSkillPage = 0
}
.onChange(of: state.skillTipText) { _, tip in
guard let tip, !tip.isEmpty else { return }
#if DEBUG
@@ -292,8 +296,10 @@ struct AIKeyboardView: View {
@ViewBuilder
private var contextArea: some View {
ZStack {
if state.isOnboardingPracticeActive, activeStatus == nil {
Text(ExtL10n.string("keyboard.onboarding.practice.mic"))
if showsOOBEClipboardSkill, activeStatus == nil {
clipboardSkillPager
} else if let onboardingPracticeHint, activeStatus == nil {
Text(onboardingPracticeHint)
.font(.system(size: 13, weight: .semibold))
.foregroundStyle(palette.textPrimary)
.lineLimit(1)
@@ -532,7 +538,7 @@ struct AIKeyboardView: View {
if skill.id == AIClipboardSkillCatalog.translateID {
return AIClipboardSkillCatalog.translateButtonTitle(
translationTargetLocaleId: state.translationTargetLocaleId,
uiLanguage: AppGroupStore().uiLanguage
uiLanguage: state.uiLanguage
)
}
if let custom = skill.customName?.trimmingCharacters(in: .whitespacesAndNewlines),
@@ -550,6 +556,9 @@ struct AIKeyboardView: View {
return assistantIsResting
}
#endif
if showsOOBEClipboardSkill {
return assistantIsResting
}
guard assistantIsResting, state.clipboardHistoryEnabled else { return false }
guard !visibleClipboardSkills.isEmpty,
let newest = clipboardHistory.newestEntry,
@@ -568,12 +577,54 @@ struct AIKeyboardView: View {
return preview
}
#endif
return AIClipboardSkillCatalog.visible(
enabledIDs: state.enabledClipboardSkillIDs,
userCatalog: AppGroupStore().agentUserSkillCatalog
if let expectedSkillID = oobeExpectedSkillID {
return AIClipboardSkillCatalog.catalog.filter { $0.id == expectedSkillID }
}
guard let newest = clipboardHistory.newestEntry,
let snapshot = semanticRanking.snapshot,
snapshot.entryID == newest.id else {
return state.enabledClipboardSkills
}
return ClipboardSkillSemanticRanker.ranked(
skills: state.enabledClipboardSkills,
sourceText: newest.text,
analysis: snapshot.analysis,
uiLanguage: state.uiLanguage
)
}
private var showsOOBEClipboardSkill: Bool {
oobeExpectedSkillID != nil
}
private var oobeExpectedSkillID: String? {
switch state.oobePracticeSession?.expectedFeature {
case .clipboardTranslate:
return AIClipboardSkillCatalog.translateID
case .clipboardReply:
return AIClipboardSkillCatalog.replyID
case .voiceInput, .askAI, nil:
return nil
}
}
private var onboardingPracticeHint: String? {
switch state.oobePracticeSession?.expectedFeature {
case .voiceInput:
return ExtL10n.string("keyboard.onboarding.practice.mic")
case .clipboardTranslate:
return ExtL10n.string("keyboard.onboarding.practice.translate")
case .clipboardReply:
return ExtL10n.string("keyboard.onboarding.practice.reply")
case .askAI:
return ExtL10n.string("keyboard.onboarding.practice.askAI")
case nil:
return state.isOnboardingPracticeActive
? ExtL10n.string("keyboard.onboarding.practice.mic")
: nil
}
}
// MARK: - Primary actions
private var primaryActionRow: some View {