Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@
#### 功能

- 新增输入法健康诊断:输入法列表为空、读取失败或自动切换失败时,菜单会显示具体状态,并提供重新加载输入法、重试当前 App 和打开键盘设置等恢复操作。
- 恢复启动时静默检查并开启定时自动检查;发现新版本后,一级菜单会显示具体版本,只有用户点击后才会打开 Sparkle 更新窗口。

#### 优化

- 将版本、项目主页、开发者、帮助、诊断和问题反馈收进“帮助与关于”子菜单,减少低频支持入口对一级菜单空间的占用。

#### 修复

- 取消启动时的静默更新检查,确保只有用户主动点击“检查更新…”时才会访问 GitHub。
- 修复恢复已忽略 App 时丢失原输入法策略的问题,并为菜单栏图标补充当前 App 状态的 VoiceOver 描述。
- 修复输入法目录加载期间激活 App 时可能漏掉自动切换的问题;目录加载完成后仅补偿处理此前被跳过的当前 App。
- 修复手动切换到正确输入法后仍保留失败警告的问题,并让 VoiceOver 在输入法异常时优先播报警告状态。
Expand All @@ -21,10 +25,14 @@
#### Features

- Added input method health diagnostics. When the input method catalog is empty, fails to load, or an automatic switch fails, the menu now explains the state and offers recovery actions to reload input methods, retry the current app, or open Keyboard Settings.
- Restored a silent startup check and enabled scheduled automatic checks. When an update is available, the root menu shows its version, and Sparkle presents its update window only after the user clicks it.

#### Improvements

- Moved version, project, developer, help, diagnostics, and issue-reporting information into a `Help & About` submenu so low-frequency support actions no longer crowd the root menu.

#### Fixes

- Removed the silent update check at startup so TypeSwitch contacts GitHub for updates only after the user clicks `Check for Updates…`.
- Fixed restored ignored apps losing their previous input method strategy, and added VoiceOver descriptions for the current app state in the menu bar icon.
- Fixed automatic switching being skipped when an app activates while the input method catalog is loading; TypeSwitch now retries only the current app whose activation was deferred.
- Fixed stale failure warnings after manually selecting the correct input method, and made VoiceOver announce input method warnings before the current app state.
Expand Down
Binary file modified Documentation/Screenshots/en-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Documentation/Screenshots/en-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Documentation/Screenshots/raw/en-dark-main.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Documentation/Screenshots/raw/en-dark-strategy.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Documentation/Screenshots/raw/en-light-main.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Documentation/Screenshots/raw/en-light-strategy.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Documentation/Screenshots/raw/zh-Hans-dark-main.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Documentation/Screenshots/raw/zh-Hans-dark-strategy.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Documentation/Screenshots/raw/zh-Hans-light-main.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Documentation/Screenshots/raw/zh-Hans-light-strategy.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Documentation/Screenshots/zh-Hans-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Documentation/Screenshots/zh-Hans-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion Project.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ let project = Project(
"CFBundleVersion": .string(buildVersion), // 构建版本号
"SUFeedURL": "https://github.com/ygsgdbd/TypeSwitch/releases/latest/download/appcast.xml",
"SUPublicEDKey": "tzapCS0NiXBf0Alm3T2veTCaauxr2dmMCnIFYaK+lOs=",
"SUEnableAutomaticChecks": false,
"SUAllowsAutomaticUpdates": false,
"SUEnableAutomaticChecks": true,
"SUAutomaticallyUpdate": false,
]),
sources: ["TypeSwitch/Sources/**"],
resources: ["TypeSwitch/Resources/**"],
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
- **Choose the behavior that fits.** Let an app inherit the default rule, remember its `Last Switch`, select a `Specific Input Method`, or ignore apps you do not want TypeSwitch to manage.
- **Configure apps where you find them.** Set a rule for the current app, review running apps by configuration status, or manage every saved rule without leaving the menu bar.
- **Keep rules and results tidy.** Find rules for missing apps, remove stale settings, review successful switch counts, and clear statistics when needed.
- **Fit TypeSwitch into your workflow.** Launch it at login, check for updates through Sparkle, open the GitHub repository, or press `Command + Q` to quit.
- **Fit TypeSwitch into your workflow.** Launch it at login, let Sparkle check silently for updates, open project and support links from `Help & About`, or press `Command + Q` to quit.

## 🪶 Native and Lightweight

Expand Down Expand Up @@ -91,7 +91,7 @@ brew upgrade typeswitch
3. In Applications, Control-click or right-click `TypeSwitch.app`, choose `Open`, then confirm that you want to open it.
4. If macOS still blocks the app, open System Settings → Privacy & Security, find the TypeSwitch security message, and choose `Open Anyway`.
5. Grant any system permissions macOS requests.
6. Use `Check for Updates…` from the menu bar app to check GitHub Releases for future updates.
6. TypeSwitch checks GitHub Releases silently. When a new version is available, the menu shows its version; click it to open the update window.

## 🧭 Usage

Expand All @@ -108,7 +108,7 @@ brew upgrade typeswitch

- App rules, the Default Rule for Unconfigured Apps, and switch statistics are stored locally.
- TypeSwitch has no server-side component in this repository.
- TypeSwitch does not check for updates or contact GitHub at startup. `GitHub Repository` and `Check for Updates…` contact GitHub only after you click them.
- TypeSwitch contacts its GitHub appcast at startup and periodically while running to check for updates. Background checks only update the menu status and never present UI, download, or install automatically; the Sparkle update window opens only after you click `Check for Updates…` or `New Version … Available…`.
- Input method switching uses macOS system input sources.
- Launch at Login uses macOS Login Items, with a LaunchAgent fallback when needed.

Expand All @@ -118,7 +118,7 @@ This project uses:

- [The Composable Architecture](https://github.com/pointfreeco/swift-composable-architecture) `1.26.0` for app architecture and state management
- [Sharing](https://github.com/pointfreeco/swift-sharing) `2.9.1` for file-backed shared state
- [Sparkle](https://github.com/sparkle-project/Sparkle) `2.9.4` for manual update checks and appcast support
- [Sparkle](https://github.com/sparkle-project/Sparkle) `2.9.4` for silent automatic checks, manual updates, and appcast support
- [SwifterSwift](https://github.com/SwifterSwift/SwifterSwift) `8.0.0` for Swift extensions
- Point-Free support libraries: CasePaths, Dependencies, and PerceptionCore
- [Tuist](https://github.com/tuist/tuist) for project generation and build configuration
Expand Down
8 changes: 4 additions & 4 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
- **选择适合自己的切换方式。** 可让 App 继承默认规则、记住上次切换、指定输入法,或忽略不希望 TypeSwitch 管理的 App。
- **随时配置遇到的 App。** 可直接设置当前 App,从按配置状态分组的运行中 App 里选择,或在菜单栏中管理全部已保存规则。
- **保持规则与统计整洁。** 查找不存在 App 的规则、移除失效设置、查看成功切换次数,并按需清零统计。
- **融入日常工作流程。** 支持登录时打开、通过 Sparkle 检查更新、访问 GitHub 仓库,以及使用 `Command + Q` 退出。
- **融入日常工作流程。** 支持登录时打开、通过 Sparkle 静默检查更新、从“帮助与关于”访问项目与支持入口,以及使用 `Command + Q` 退出。

## 🪶 原生与轻量

Expand Down Expand Up @@ -91,7 +91,7 @@ brew upgrade typeswitch
3. 在“应用程序”中按住 Control 点击或右键点击 `TypeSwitch.app`,选择“打开”,然后确认打开。
4. 如果 macOS 仍然阻止运行,请打开“系统设置”→“隐私与安全性”,找到 TypeSwitch 的安全提示并选择“仍要打开”。
5. 授予 macOS 请求的系统权限。
6. 后续可在菜单栏应用中使用“检查更新…”从 GitHub Releases 检查更新。
6. TypeSwitch 会静默检查 GitHub Releases;发现新版本后,菜单会显示版本号,点击后才会打开更新窗口。

## 🧭 使用说明

Expand All @@ -108,7 +108,7 @@ brew upgrade typeswitch

- App 规则、未配置 App 的默认规则和切换统计都存储在本地。
- 本仓库中没有服务端组件。
- TypeSwitch 启动时不会检查更新或访问 GitHub。只有在你主动点击“GitHub 仓库”或“检查更新…”后才会访问 GitHub。
- TypeSwitch 会在启动时及运行期间定时访问 GitHub appcast 检查更新。后台检查只更新菜单状态,不会自动弹窗、下载或安装;点击“检查更新…”或“新版本 … 可用…”后才会打开 Sparkle 更新窗口。
- 输入法切换使用 macOS 系统输入源。
- “登录时打开”使用 macOS 登录项;必要时会回退到 LaunchAgent。

Expand All @@ -118,7 +118,7 @@ brew upgrade typeswitch

- [The Composable Architecture](https://github.com/pointfreeco/swift-composable-architecture) `1.26.0`:应用架构和状态管理
- [Sharing](https://github.com/pointfreeco/swift-sharing) `2.9.1`:基于文件的共享状态持久化
- [Sparkle](https://github.com/sparkle-project/Sparkle) `2.9.4`:手动检查更新和 appcast 支持
- [Sparkle](https://github.com/sparkle-project/Sparkle) `2.9.4`:静默自动检查、手动更新和 appcast 支持
- [SwifterSwift](https://github.com/SwifterSwift/SwifterSwift) `8.0.0`:Swift 扩展
- Point-Free 支持库:CasePaths、Dependencies、PerceptionCore
- [Tuist](https://github.com/tuist/tuist):项目生成和构建配置
Expand Down
7 changes: 5 additions & 2 deletions TypeSwitch/Resources/Base.lproj/Localizable.strings
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// 菜单
"menu.quit" = "退出 TypeSwitch";
"menu.github_repository" = "GitHub 仓库";
"menu.accessibility_configured" = "TypeSwitch,当前 App 已配置";
"menu.accessibility_unconfigured" = "TypeSwitch,当前 App 未配置";
"menu.accessibility_ignored" = "TypeSwitch,当前 App 已忽略";
Expand All @@ -17,13 +16,17 @@
"settings.general.auto_launch_requires_approval" = "请前往“系统设置”>“通用”>“登录项”允许 TypeSwitch。";
"settings.general.open_login_items" = "打开登录项设置";
"settings.general.check_for_updates" = "检查更新…";
"settings.general.checking_for_updates" = "正在检查更新…";
"settings.general.update_available" = "新版本 %@ 可用…";
"settings.fallback.default_input_method" = "未配置 App 的默认规则";

// 支持
"support.version" = "TypeSwitch v%@";
"support.help_and_about" = "帮助与关于";
"support.developer" = "开发者:%@";
"support.help" = "使用帮助";
"support.copy_diagnostics" = "复制诊断信息";
"support.report_issue" = "报告问题";
"support.report_issue" = "报告问题…";

// 应用列表
"apps.section.current_app" = "当前应用";
Expand Down
7 changes: 5 additions & 2 deletions TypeSwitch/Resources/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Menu
"menu.quit" = "Quit TypeSwitch";
"menu.github_repository" = "GitHub Repository";
"menu.accessibility_configured" = "TypeSwitch, current app configured";
"menu.accessibility_unconfigured" = "TypeSwitch, current app unconfigured";
"menu.accessibility_ignored" = "TypeSwitch, current app ignored";
Expand All @@ -17,13 +16,17 @@
"settings.general.auto_launch_requires_approval" = "Approve TypeSwitch in System Settings > General > Login Items.";
"settings.general.open_login_items" = "Open Login Items Settings";
"settings.general.check_for_updates" = "Check for Updates…";
"settings.general.checking_for_updates" = "Checking for Updates…";
"settings.general.update_available" = "New Version %@ Available…";
"settings.fallback.default_input_method" = "Default Rule for Unconfigured Apps";

// Support
"support.version" = "TypeSwitch v%@";
"support.help_and_about" = "Help & About";
"support.developer" = "Developer: %@";
"support.help" = "Help";
"support.copy_diagnostics" = "Copy Diagnostics";
"support.report_issue" = "Report an Issue";
"support.report_issue" = "Report an Issue…";

// App List
"apps.section.current_app" = "Current App";
Expand Down
7 changes: 5 additions & 2 deletions TypeSwitch/Resources/zh-Hans.lproj/Localizable.strings
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// 菜单
"menu.quit" = "退出 TypeSwitch";
"menu.github_repository" = "GitHub 仓库";
"menu.accessibility_configured" = "TypeSwitch,当前 App 已配置";
"menu.accessibility_unconfigured" = "TypeSwitch,当前 App 未配置";
"menu.accessibility_ignored" = "TypeSwitch,当前 App 已忽略";
Expand All @@ -17,13 +16,17 @@
"settings.general.auto_launch_requires_approval" = "请前往“系统设置”>“通用”>“登录项”允许 TypeSwitch。";
"settings.general.open_login_items" = "打开登录项设置";
"settings.general.check_for_updates" = "检查更新…";
"settings.general.checking_for_updates" = "正在检查更新…";
"settings.general.update_available" = "新版本 %@ 可用…";
"settings.fallback.default_input_method" = "未配置 App 的默认规则";

// 支持
"support.version" = "TypeSwitch v%@";
"support.help_and_about" = "帮助与关于";
"support.developer" = "开发者:%@";
"support.help" = "使用帮助";
"support.copy_diagnostics" = "复制诊断信息";
"support.report_issue" = "报告问题";
"support.report_issue" = "报告问题…";

// 应用列表
"apps.section.current_app" = "当前应用";
Expand Down
7 changes: 5 additions & 2 deletions TypeSwitch/Resources/zh-Hant.lproj/Localizable.strings
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// 選單
"menu.quit" = "結束 TypeSwitch";
"menu.github_repository" = "GitHub 儲存庫";
"menu.accessibility_configured" = "TypeSwitch,目前 App 已設定";
"menu.accessibility_unconfigured" = "TypeSwitch,目前 App 未設定";
"menu.accessibility_ignored" = "TypeSwitch,目前 App 已忽略";
Expand All @@ -17,13 +16,17 @@
"settings.general.auto_launch_requires_approval" = "請前往「系統設定」>「一般」>「登入項目」允許 TypeSwitch。";
"settings.general.open_login_items" = "開啟登入項目設定";
"settings.general.check_for_updates" = "檢查更新…";
"settings.general.checking_for_updates" = "正在檢查更新…";
"settings.general.update_available" = "新版本 %@ 可用…";
"settings.fallback.default_input_method" = "未設定 App 的預設規則";

// 支援
"support.version" = "TypeSwitch v%@";
"support.help_and_about" = "說明與關於";
"support.developer" = "開發者:%@";
"support.help" = "使用說明";
"support.copy_diagnostics" = "複製診斷資訊";
"support.report_issue" = "回報問題";
"support.report_issue" = "回報問題…";

// 應用程式列表
"apps.section.current_app" = "目前應用";
Expand Down
11 changes: 9 additions & 2 deletions TypeSwitch/Sources/App/TypeSwitchApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ struct TypeSwitchApp: App {
let readmeBackdropWindow: NSWindow?
let readmeColorScheme: ColorScheme?
let store: StoreOf<AppFeature>
let updateMonitor: SparkleUpdateMonitor
let updaterController: SPUStandardUpdaterController

init() {
Expand All @@ -33,12 +34,14 @@ struct TypeSwitchApp: App {
AppFeature()
}
let startsUpdater = startsLiveServices && !isTesting
let updateMonitor = SparkleUpdateMonitor()
let updaterController = SPUStandardUpdaterController(
startingUpdater: startsUpdater,
updaterDelegate: nil,
userDriverDelegate: nil
updaterDelegate: updateMonitor,
userDriverDelegate: updateMonitor
)
self.store = store
self.updateMonitor = updateMonitor
self.updaterController = updaterController
self.menuTrackingObservers = [
NotificationCenter.default.addObserver(
Expand Down Expand Up @@ -73,12 +76,16 @@ struct TypeSwitchApp: App {
if startsLiveServices {
store.send(.task)
}
if startsUpdater {
updateMonitor.startSilentCheck(using: updaterController.updater)
}
}

var body: some Scene {
MenuBarExtra {
MenuBarView(
store: store,
updateMonitor: updateMonitor,
updaterController: updaterController
)
.preferredColorScheme(readmeColorScheme)
Expand Down
15 changes: 14 additions & 1 deletion TypeSwitch/Sources/Services/AppManagement/AppInfoService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,15 @@ import Foundation
/// 应用信息服务类,负责管理应用相关链接
enum AppInfoService {
/// GitHub 仓库信息
private static let githubRepository = "ygsgdbd/TypeSwitch"
private static let githubAccount = "ygsgdbd"
private static let githubRepository = "\(githubAccount)/TypeSwitch"
private static let githubBaseURL = "https://github.com"

/// 获取开发者 GitHub 主页 URL
static var githubProfileURL: URL? {
URL(string: "\(githubBaseURL)/\(githubAccount)")
}

/// 获取 GitHub 仓库 URL
static var githubRepositoryURL: URL? {
URL(string: "\(githubBaseURL)/\(githubRepository)")
Expand Down Expand Up @@ -42,6 +48,13 @@ enum AppInfoService {
NSWorkspace.shared.open(url)
}

/// 打开开发者 GitHub 主页
@MainActor
static func openGitHubProfile() {
guard let url = githubProfileURL else { return }
NSWorkspace.shared.open(url)
}

/// 打开 GitHub Releases 页面
@MainActor
static func openGitHubReleases() {
Expand Down
Loading
Loading