# TagLauncher 当前底层信息架构
生成日期:2026-05-20
## 1. 总体架构图
```mermaid
flowchart TD
User["用户"]
MenuBar["菜单栏 / Dock / 全局快捷键"]
AppDelegate["AppDelegate
窗口、菜单、快捷键、生命周期"]
Overlay["ContentView 主覆盖层"]
Settings["PreferencesView 设置窗口"]
Scanner["AppIndexer
本机 App 扫描"]
Store["TagDatabase Store
tags.json"]
SmartStart["SmartStartService
本地智能分类"]
Search["QuickSearchEngine
快速搜索索引与排序"]
UIState["SwiftUI 展示状态
allApps / displayGroups / quickSearchDocuments"]
Resources["Bundle Resources
Localization / SmartStart catalog / icons"]
System["macOS APIs
NSWorkspace / Carbon Hotkey / LaunchAgent"]
User --> MenuBar
MenuBar --> AppDelegate
AppDelegate --> Overlay
AppDelegate --> Settings
AppDelegate --> System
Overlay --> Scanner
Scanner --> UIState
Overlay --> Store
Store --> UIState
Overlay --> SmartStart
SmartStart --> Store
SmartStart --> Resources
Overlay --> Search
Search --> UIState
Settings --> Store
Settings --> Resources
Resources --> Overlay
```
## 2. 架构分层
### 2.1 入口与系统集成层
主要文件:
- `Apptag/ApptagApp.swift`
- `Apptag/AppDefaults.swift`
职责:
- App 启动与生命周期。
- 菜单栏状态项。
- Dock 显示策略。
- 覆盖层窗口创建和隐藏。
- 设置窗口管理。
- Carbon 全局快捷键注册。
- LaunchAgent 开机登录。
- App 菜单裁剪和本地化刷新。
关键状态:
- `overlayWindow`
- `settingsWindow`
- `mainHotkeyRef`
- `quickSearchHotkeyRef`
- `isQuickSearchOpen`
- `isInEditMode`
- `isEditingAppNote`
- `isModalInteractionActive`
### 2.2 展示与交互层
主要文件:
- `Apptag/ContentView.swift`
- `Apptag/TagGroupView.swift`
- `Apptag/AppGridItem.swift`
- `Apptag/EditModeViews.swift`
- `Apptag/TagEditorView.swift`
- `Apptag/PreferencesView.swift`
职责:
- 主界面布局。
- 分组和 App 图标展示。
- 容器视图、网格容器视图和平铺视图。
- 标签导航、标签排序。
- 批量编辑 App 标签。
- App hover 气泡和备注编辑。
- Quick Search 浮层挂载。
- 设置页各模块。
核心展示状态:
| 状态 | 含义 |
|---|---|
| `allApps` | 已扫描并标注后的 App 列表 |
| `displayGroups` | 当前用于展示的分组结果 |
| `tagColors` | 标签到颜色索引的映射 |
| `draggedTagNames` | 当前标签显示顺序 |
| `quickSearchDocuments` | Quick Search 搜索文档 |
| `quickSearchResults` | 当前搜索结果 |
| `editPhase` | 当前编辑模式 |
| `hoveredBubble` / `editingBubble` | App 用途气泡状态 |
| `pendingUncategorizedDrop` | 拖到未分类后的待确认状态 |
### 2.3 本机 App 索引层
主要文件:
- `Apptag/DataLayer.swift`
核心对象:
```swift
struct AppInfo {
let name: String
let path: URL
let tags: [String]
let bundleIdentifier: String?
let localizedNames: [String]
let icon: NSImage
var isUncommon: Bool
var note: String?
}
```
职责:
- 扫描标准 App 目录。
- 收集 bundle identifier。
- 读取本地化 App 名称。
- 预加载图标。
- 识别 Apple App。
- 去重。
- 输出可被 TagEditor 标注的基础 App 列表。
### 2.4 本地数据存储层
主要文件:
- `Apptag/DataLayer.swift`
存储位置:
```text
~/Library/Application Support/Apptag/tags.json
```
备份目录:
```text
~/Library/Application Support/Apptag/SmartStartBackups
~/Library/Application Support/Apptag/CategorySchemeBackups
```
核心 store:
```swift
struct Store {
var version: Int
var tags: [String: TagDef]
var appTags: [String: [String]]
var tagOrder: [String]
var uncommonAppPaths: [String]
var uncommonSources: [String: UncommonSource]
var appOpenCounts: [String: Int]
var appLastOpenedAt: [String: Date]
var knownAppPaths: [String]
var appNotes: [String: String]
var disabledSystemCategoryIDs: [SmartCategoryID]
var smartStart: SmartStartState
var categoryScheme: CategorySchemeState
}
```
核心身份:
- App 身份:优先 path 存储用户数据。
- 标签身份:普通标签用名称;系统标签额外有 `SmartCategoryID`。
- 不常用身份:path 集合 + source。
- 分类方案身份:名称 + 创建时间 + previous backup path。
### 2.5 标签编辑领域层
主要文件:
- `Apptag/DataLayer.swift`
职责:
- 创建、重命名、删除标签。
- 调整标签颜色和顺序。
- 给 App 添加、移除、移动标签。
- 设置 App 备注。
- 记录从 TagLauncher 启动 App 的行为数据。
- 扫描后 reconcile 新旧 App。
- 维护分类方案自动快照。
领域约束:
- 用户数据写入都围绕 `TagDatabase.Store`。
- 影响分类方案的操作走 `saveUserCategorySchemeMutation`,用于记录 previous scheme。
- 新安装 App 的发现不触发分类方案快照。
- 记录打开历史不触发分类方案快照。
### 2.6 Smart Start 领域层
主要文件:
- `Apptag/SmartCategorization/SmartStartService.swift`
- `Apptag/SmartCategorization/SmartCategorizationDraft.swift`
- `Apptag/SmartCategorization/SmartCategory.swift`
内置资源:
```text
build/TagLauncher.app/Contents/Resources/SmartStartUltimateDefaultCatalog.json
build/TagLauncher.app/Contents/Resources/SmartStartUltimateDefaultCatalog.csv
```
源资源:
```text
Research/SmartStart/UltimateDefaultCatalog/SmartStart_UltimateDefaultCatalog.json
Research/SmartStart/UltimateDefaultCatalog/SmartStart_UltimateDefaultCatalog.csv
```
职责:
- 加载本地分类目录。
- 根据扫描 App 生成 `SmartCategorizationDraft`。
- 决定自动应用还是只提示建议。
- 应用系统分类和默认备注。
- 备份和恢复 Smart Start 前状态。
- 语言切换时重本地化默认备注。
核心数据:
```swift
SmartCategorizationDraft
SmartAppCategorizationAssignment
SmartUnassignedApp
SmartCategoryID
SmartStartState
```
### 2.7 Quick Search 领域层
主要文件:
- `Apptag/QuickSearch.swift`
- `Apptag/ContentView.swift`
核心对象:
```swift
struct QuickSearchDocument {
let app: AppInfo
let localizedNames: [String]
let internalBundleNames: [String]
let tagNames: [String]
let note: String
let bundleIdentifier: String
let lastOpenedAt: Date?
let openCount: Int
}
struct QuickSearchResult {
let document: QuickSearchDocument
let finalScore: Double
let textScore: Double
let bestFieldRank: Int
let matchedTagName: String?
let noteSnippet: String?
}
```
职责:
- 从已标注 App 和 store 生成搜索文档。
- 预计算规范化字段、缩写和拉丁化候选。
- 执行多字段多 token 搜索。
- 排序结果。
- 空查询时生成快速建议。
### 2.8 本地化资源层
主要文件:
- `Apptag/L10n.swift`
- `Apptag/Localization/*.json`
职责:
- 启动时加载语言。
- 切换语言。
- 提供 `tr(key)`。
- 读取其他语言中的系统标签文案,用于旧数据迁移和系统标签重本地化。
支持语言:
```text
ar, ar-Najdi, cs, da, de, en, es, fr, id, it, ja, ko, ms, nb, nl, nn, no,
pl, pt-BR, ro, ru, sr-Cyrl, sv, th, tr, uk, vi, zh-Hans, zh-Hant
```
## 3. 核心数据流
### 3.1 App 启动流
```mermaid
sequenceDiagram
participant App as TagLauncherApp
participant Delegate as AppDelegate
participant Defaults as AppDefaults
participant L10n as L10n
participant DB as TagDatabase
participant Hotkey as Carbon Hotkey
App->>Delegate: applicationDidFinishLaunching
Delegate->>Defaults: register()
Delegate->>L10n: setup()
Delegate->>DB: seedDefaultTags()
Delegate->>Delegate: setup menu bar / app menu
Delegate->>Hotkey: register main and quick search hotkeys
Delegate->>Delegate: install observers
Delegate->>Delegate: setup launch at login
```
### 3.2 主界面刷新流
```mermaid
sequenceDiagram
participant UI as ContentView
participant Scanner as AppIndexer
participant Editor as TagEditor
participant SS as SmartStartService
participant Search as QuickSearchEngine
participant Store as TagDatabase.Store
UI->>Scanner: scan()
Scanner-->>UI: raw AppInfo[]
UI->>Editor: reconcileScannedApps(apps)
Editor-->>UI: reconciled Store
UI->>SS: runIfNeeded(apps, store)
SS-->>UI: SmartStartRunResult
UI->>Editor: annotate(apps, store)
Editor-->>UI: annotated AppInfo[]
UI->>Search: makeDocuments(apps, store)
Search-->>UI: QuickSearchDocument[]
UI->>UI: rebuildDisplayGroups()
```
### 3.3 App 启动记录流
```mermaid
sequenceDiagram
participant UI as ContentView / Quick Search
participant System as NSWorkspace
participant Editor as TagEditor
participant Store as tags.json
UI->>System: openApplication(app.path)
System-->>UI: success
UI->>Editor: recordLauncherOpen(path)
Editor->>Store: openCount + 1, lastOpenedAt = now
Editor->>Store: auto uncommon count >= 100 时移除 auto uncommon
UI->>UI: close Quick Search / overlay
```
### 3.4 标签变更流
```mermaid
sequenceDiagram
participant UI as 编辑界面 / 拖拽
participant Editor as TagEditor
participant DB as TagDatabase
participant Store as tags.json
UI->>Editor: append/remove/move/rename/delete/reorder/setColor
Editor->>DB: load()
DB-->>Editor: previousStore
Editor->>Editor: mutate store
Editor->>DB: saveUserCategorySchemeMutation(store, previous)
DB->>DB: 必要时创建 previous scheme backup
DB->>Store: save()
UI->>UI: refreshApps()
```
### 3.5 Smart Start 应用流
```mermaid
sequenceDiagram
participant UI as ContentView / Settings
participant SS as SmartStartService
participant Catalog as SmartStart Catalog
participant DB as TagDatabase
UI->>SS: runIfNeeded 或 applySystemInitialScheme
SS->>Catalog: loadRuntimeCatalog()
SS->>SS: match by bundleIdentifier, fallback normalizedName
SS->>DB: backup(previous store)
SS->>DB: ensureSystemTag / assign appTags / seed notes
SS->>DB: save smartStart and categoryScheme state
SS-->>UI: summary and optional draft
```
### 3.6 Quick Search 搜索流
```mermaid
sequenceDiagram
participant UI as QuickSearchOverlay
participant Engine as QuickSearchEngine
participant Docs as QuickSearchDocument[]
participant App as NSWorkspace
UI->>Engine: search(query, documents)
Engine->>Docs: match fields and rank
Engine-->>UI: results
UI->>UI: select first or preserve manual selection
UI->>App: Enter / click launches selected app
```
### 3.7 语言切换流
```mermaid
sequenceDiagram
participant Settings as PreferencesView / Menu
participant L10n as L10n
participant DB as TagDatabase
participant SS as SmartStartService
participant UI as ContentView
Settings->>L10n: switchTo(code)
L10n->>DB: relocalizeSystemTagsForCurrentLanguage()
L10n->>Settings: post appLanguageDidChange
UI->>SS: relocalizeDefaultNotesForCurrentLanguage(apps)
UI->>UI: refreshApps(forceLayoutRefresh)
```
## 4. 持久化位置
### 4.1 UserDefaults
主要键:
| Key | 含义 |
|---|---|
| `appLanguage` | 用户选择的界面语言 |
| `tagFontSize` | 标签字号 |
| `iconSize` | 图标大小 |
| `tagPosition` | 标签位置 |
| `defaultGroupName` | 默认分组中性名称,逻辑上保持 `Other` |
| `displayMode` | App 列表样式 |
| `hideAppNames` | 是否隐藏 App 名称 |
| `showDockIcon` | 是否显示 Dock 图标 |
| `launchAtLogin` | 是否开机登录 |
| `showUncommonAppBubbles` | 是否仅不常用 App 显示气泡 |
| `mainHotkeyRegistrationState` | 主快捷键注册状态 |
| `quickSearchHotkeyRegistrationState` | Quick Search 快捷键注册状态 |
| `mainHotkeyRegistrationFailureCode` | 主快捷键失败码 |
| `quickSearchHotkeyRegistrationFailureCode` | Quick Search 快捷键失败码 |
### 4.2 tags.json
保存用户核心业务数据:
- 标签定义
- App 与标签关系
- 标签排序
- 不常用 App
- App 备注
- App 启动历史
- 已知 App 路径
- 禁用的系统分类
- Smart Start 状态
- 分类方案状态
### 4.3 Bundle Resources
打包进入 App 的资源:
- 本地化 JSON
- Smart Start 运行时 JSON
- Smart Start CSV
- App 图标
- 菜单栏 SVG 图标
## 5. 关键架构不变量
| 编号 | 不变量 |
|---|---|
| INV-01 | 扫描出的 App 是事实层,用户分类和备注来自 store 标注。 |
| INV-02 | App 用户数据以 path 为主要存储 key。 |
| INV-03 | 系统标签必须有稳定 `SmartCategoryID`,显示名可以随语言变化。 |
| INV-04 | “Mac 自带”是客观属性分组,不是用户普通标签。 |
| INV-05 | “未分类”是无普通标签状态,不是必须写入 App 的标签。 |
| INV-06 | 不常用是特殊 marker,不应混入普通 tag 语义。 |
| INV-07 | Smart Start 只能基于本地目录运行,不依赖在线服务。 |
| INV-08 | Quick Search 不改变 App 列表布局,是覆盖层中的第二启动入口。 |
| INV-09 | 启动历史只在成功从 TagLauncher 打开 App 后写入。 |
| INV-10 | 用户手写备注优先级高于系统默认备注。 |
| INV-11 | 构建过程不应回写源码 `Info.plist`。 |
## 6. 当前已知架构边界
- 没有账号系统。
- 没有云同步。
- 没有远程搜索。
- 没有文件、联系人、浏览器历史搜索。
- 没有用户自定义快捷键的当前落地能力。
- 没有独立数据库,使用 JSON 文件作为本地 store。
- Smart Start notes 当前仍在单一运行时 JSON 中,尚未按语言拆分资源。