bugger/docs/HIGH_LEVEL_DESIGN.md

148 lines
8.0 KiB
Markdown

# Bugger for macOS — High-Level Design
## Overview
**Bugger** is a lightweight macOS menu bar application that surfaces bugs assigned to
you from a Feishu Bitable. It mirrors the experience of the Windows Bugger tool:
a glanceable badge in the menu bar, a popover with your active bugs, native
notifications when bugs are assigned or changed, and an optional floating widget.
## Architecture
```
┌──────────────────────────────────────────────────────┐
│ User's Mac │
│ │
│ ┌─────────────┐ ┌──────────────┐ │
│ │ Menu Bar │ │ Floating │ │
│ │ Icon+Badge │ │ Widget │ │
│ │ [🐛 3] │ │ (optional) │ │
│ └──────┬──────┘ └──────┬───────┘ │
│ │ │ │
│ ┌──────┴──────────────────┴───────┐ │
│ │ BugStore │ │
│ │ (ObservableObject) │ │
│ │ - bugs: [Bug] │ │
│ │ - unseenCount: Int │ │
│ │ - lastUpdated: Date │ │
│ └──────┬──────────────────────────┘ │
│ │ │
│ ┌──────┴──────────┐ ┌─────────────┐ │
│ │ PollerService │───▶│ FeishuService│ │
│ │ (Timer, 5min) │ │ │ │
│ └─────────────────┘ └──────┬──────┘ │
│ │ │
│ ┌──────────────┐ ┌────────┴───────┐ │
│ │ Notification │ │ TokenManager │ │
│ │ Service │ │ (Keychain) │ │
│ └──────────────┘ └────────────────┘ │
│ │
└──────────────────────────────────────────────────────┘
│ HTTPS (OAuth Bearer)
┌──────────────────────────────────────────────────────┐
│ Feishu Cloud │
│ ┌──────────────┐ ┌──────────────────┐ │
│ │ OAuth │ │ Bitable API │ │
│ │ /authen/v1/ │ │ /bitable/v1/ │ │
│ │ oidc/ │ │ apps/{token}/ │ │
│ │ access_token│ │ tables/{id}/ │ │
│ └──────────────┘ │ records │ │
│ └──────────────────┘ │
└──────────────────────────────────────────────────────┘
```
## Key Design Decisions
### 1. Native SwiftUI (not cross-platform)
The app targets macOS exclusively. SwiftUI + AppKit bridging gives the best
integration with macOS idioms — `MenuBarExtra`, `NSPanel` floating windows,
Keychain, and `UserNotifications` — while keeping the binary under 10 MB.
### 2. Poll-based sync, not push
Feishu webhooks require a public-facing HTTP endpoint. For a personal tool,
polling the Bitable API every N minutes is simpler, safer, and sufficient.
Default interval: 5 minutes.
### 3. OAuth user_access_token with refresh
Uses Feishu's OAuth 2.0 authorization code flow to get a `user_access_token`
(valid 2h) and `refresh_token` (valid 30 days). Tokens are stored in the macOS
Keychain. The app acts on behalf of the user — no admin approval needed.
### 4. No backend server
Everything runs locally. No database, no server, no cloud hosting. The app
stores only:
- OAuth tokens (Keychain)
- Configuration (UserDefaults: table ID, polling interval, etc.)
- Cached bug state (in-memory, rebuilt on launch)
### 5. Menu bar first, floating widget second
The menu bar popover is the primary interaction surface. The floating widget is
an optional companion — a small always-on-top window showing the count, which
clicks through to open the menu bar popover.
## Component Map
| Component | Responsibility |
|---|---|
| `App` | `@main` entry, sets activation policy to `.accessory` (no dock icon) |
| `AppDelegate` | Menu bar icon registration, window management |
| `BugStore` | Central observable state: bug list, unseen count, loading/error |
| `FeishuService` | HTTP client for Feishu Open API (bitable records, OAuth) |
| `TokenManager` | OAuth flow orchestration, Keychain read/write, token refresh |
| `PollerService` | Configurable timer driving the fetch cycle |
| `NotificationService` | Diff engine + `UNUserNotificationCenter` push |
| `MenuBarView` | Menu bar icon with badge count |
| `BugListPopover` | Popover containing the scrollable bug list |
| `BugRow` | Single bug: title, priority pill, age, clickable |
| `FloatingWidget` | Optional `NSPanel`-based always-on-top overlay |
| `SettingsWindow` | Preferences: table config, poll interval, widget toggle |
| `KeychainHelper` | Thin wrapper around Security framework |
## Data Flow
```
1. App Launch
├── Load tokens from Keychain
├── If no token → show OAuth setup window
├── If token expired → refresh via refresh_token
└── Start PollerService
2. Poll Cycle (every 5 min, configurable)
├── FeishuService.fetchRecords(table_id, filter=assignee=me)
├── Parse response → [Bug]
├── BugStore.diff(old, new)
│ ├── New bugs → NotificationService.fire()
│ ├── Status changes → NotificationService.fire()
│ └── Assignee changes → NotificationService.fire()
└── BugStore.bugs = new; BugStore.lastUpdated = now
3. User Interaction
├── Click menu bar icon → toggle popover
├── Click bug in popover → open in Feishu (browser / Feishu app URL)
├── "Mark all seen" → reset badge count
└── Floating widget click → open menu bar popover
```
## Feishu API Surface
| API | Method | Purpose |
|---|---|---|
| `/authen/v1/oidc/access_token` | POST | Exchange authorization code for user_access_token |
| `/authen/v1/refresh_access_token` | POST | Refresh expired user_access_token |
| `/bitable/v1/apps/{app_token}/tables/{table_id}/records` | GET | Fetch bug records with filter |
All calls use `Authorization: Bearer <user_access_token>` and go to
`https://open.feishu.cn`.
## Non-Functional Requirements
- **Memory**: < 80 MB at steady state
- **CPU**: idle < 0.1%, spike < 2% during poll
- **Binary**: < 15 MB
- **Startup**: < 2 seconds to menu bar icon visible
- **Offline**: graceful degradation (show stale data, retry on next interval)
- **macOS**: 14.0+ (Sonoma) `MenuBarExtra` is stable from 13.3, but 14.0 is a
reasonable floor for 2024+