first commit: project README, high-level design and detailed implementation docs
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
commit
a9c90f64b9
|
|
@ -0,0 +1,14 @@
|
||||||
|
# Bugger for macOS
|
||||||
|
|
||||||
|
A lightweight macOS menu bar app that surfaces bugs assigned to you from a [Feishu Bitable](https://www.feishu.cn/). Shows a badge in the menu bar, a popover with your active bugs, and native notifications when bugs are assigned or changed.
|
||||||
|
|
||||||
|
Inspired by the Windows Bugger tool.
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Early design phase. See [docs/](docs/) for the high-level design and detailed implementation plan.
|
||||||
|
|
||||||
|
## Docs
|
||||||
|
|
||||||
|
- [High-Level Design](docs/HIGH_LEVEL_DESIGN.md)
|
||||||
|
- [Detailed Design & Implementation](docs/DETAILED_DESIGN.md)
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,147 @@
|
||||||
|
# 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+
|
||||||
Loading…
Reference in New Issue