Files
OSGKeyboard/OSGKeyboardMac/MacAppContextService.swift
T
Rocky 498f407585 feat(account): add managed credits and cloud gateway
Introduce optional Apple account-backed credits with scoped gateway access while preserving local and BYOK paths. Refresh assistant behavior, tests, privacy disclosures, docs, and the website for the 2.0 experience.
2026-08-20 11:43:21 +08:00

102 lines
3.7 KiB
Swift

// MacAppContextService.swift
// OSGKeyboard · Mac
//
// Unlike the iOS keyboard extension, macOS can read the frontmost app's
// bundle ID via NSWorkspace and map it to `AppContext` for polish prompts.
import AppKit
import Foundation
enum MacAppContextService {
/// Bundle IDs → coarse polish context (macOS + cross-platform).
private static let contextByBundleId: [String: AppContext] = [
// Code / dev
"com.apple.dt.Xcode": .code,
"com.microsoft.VSCode": .code,
"com.google.android.studio": .code,
"com.jetbrains.intellij": .code,
"com.jetbrains.AppCode": .code,
"com.sublimetext.4": .code,
"com.github.GitHubClient": .code,
"com.apple.Terminal": .code,
"com.googlecode.iterm2": .code,
"dev.warp.Warp-Stable": .code,
// Email
"com.apple.mail": .email,
"com.microsoft.Outlook": .email,
"com.google.Gmail": .email,
"com.readdle.smartemail": .email,
// Chat / IM
"com.tencent.xinWeChat": .chat,
"com.tencent.qq": .chat,
"com.tencent.wework": .chat,
"com.tinyspeck.slackmacgap": .chat,
"com.hnc.Discord": .chat,
"com.microsoft.teams": .chat,
"com.microsoft.teams2": .chat,
"ru.keepcoder.Telegram": .chat,
"net.whatsapp.WhatsApp": .chat,
"com.apple.MobileSMS": .chat,
"com.facebook.archon": .chat,
"com.laiwang.DingTalk": .chat,
"com.bytedance.feishu": .chat,
// Documents / notes
"com.apple.Notes": .document,
"com.apple.iWork.Pages": .document,
"notion.id": .document,
"md.obsidian": .document,
"net.shinyfrog.bear": .document,
"com.agiletortoise.Drafts-OSX": .document,
"com.microsoft.Word": .document,
"com.google.GoogleDocs": .document,
"com.evernote.Evernote": .document,
"com.microsoft.onenote.mac": .document
]
/// Chat-style apps from the shared host registry (iOS bundle IDs often
/// match Mac counterparts for cross-platform IM).
private static let chatBundleIdsFromRegistry: Set<String> = {
Set(HostAppURLRegistry.entries.map(\.bundleId))
}()
static func frontmostApplicationName() -> String? {
NSWorkspace.shared.frontmostApplication?.localizedName
}
static func frontmostBundleIdentifier() -> String? {
NSWorkspace.shared.frontmostApplication?.bundleIdentifier
}
static func detectContext() -> AppContext {
detectContext(bundleIdentifier: frontmostBundleIdentifier())
}
/// Resolve polish context from the application captured for this dictation
/// session. This avoids reading OSGKeyboard itself after a popover steals
/// focus.
static func detectContext(bundleIdentifier bundleId: String?) -> AppContext {
guard let bundleId else { return .unknown }
if let mapped = contextByBundleId[bundleId] { return mapped }
if chatBundleIdsFromRegistry.contains(bundleId) { return .chat }
if bundleId.hasPrefix("com.apple.Safari") || bundleId.contains("chrome") {
return .document
}
return .unknown
}
/// Persist detected context into the shared configuration store so
/// `PolishingService` reads the same signal as on iOS.
static func captureAndPersist(to store: AppGroupStore) {
let context = detectContext()
store.setDetectedAppContext(context)
}
static func captureAndPersist(
application: NSRunningApplication?,
to store: AppGroupStore
) {
let context = detectContext(bundleIdentifier: application?.bundleIdentifier)
store.setDetectedAppContext(context)
}
}