Commit Graph

14 Commits

Author SHA1 Message Date
tigerenwork 3f1367790e fix: timestamp auto-updates without triggering fetch on tray open
- Reverted auto-refresh on tray click (fetch only happens on schedule
  or manual Refresh button)
- Timestamp now uses TimelineView(.periodic) to auto-update every 30s:
  'Just now' → '1m ago' → '5m ago' → etc. without any user action

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-29 23:54:28 +08:00
tigerenwork 5b1811db10 fix: auto-refresh on tray click, widen 'Just now' to 60s
- Opening the popover now triggers a fetch (via PollerService.fetchNow
  which deduplicates if already in progress)
- 'Just now' now covers 0-60s, then switches to 'Xm ago'.
  Avoids the frozen-timer look since the text only updates on re-render.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-29 23:51:48 +08:00
tigerenwork 2f2a65f1f7 fix: show 'Just now' instead of '1s ago' after refresh
Replaced RelativeDateTimeFormatter with manual formatting:
<5s → 'Just now', <60s → 'Xs ago', <1h → 'Xm ago',
<1d → 'Xh ago', else 'Xd ago'.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-29 23:49:23 +08:00
tigerenwork 07ac29faa6 feat: show refresh icon + last-updated time in popover header
Header now always shows: ↻ 2m ago  (or 'Never' before first fetch).
Refresh spinner replaces the text while loading. More discoverable
than the bare relative timestamp that was there before.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-29 23:45:24 +08:00
tigerenwork 9154264ea7 fix: replace daily time text field with native DatePickers
- Daily schedule mode now shows macOS-native hour:minute DatePickers
  instead of a comma-separated text field
- Each time entry has its own picker with a remove button
- '+ Add time' button to add more entries
- Minimum 1 entry enforced (remove disabled when only 1 left)
- No hardcoded defaults — starts empty, user picks their times

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-29 21:52:37 +08:00
tigerenwork b81d3edf42 feat: add 8h/12h intervals + daily schedule refresh mode
- Added 8-hour and 12-hour polling interval options
- Added 'Daily schedule' mode (pollIntervalSeconds = -1): fires at
  specific wall-clock times configured in dailyRefreshTimes (e.g. 08:00, 14:00)
- PollerService: in daily mode, checks every 60s and triggers fetch when
  current time matches a scheduled time (with 2-min dedup window)
- Settings UI: shows HH:mm text field when daily schedule is selected
- AppConfig: added dailyRefreshTimes: [String] field

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-29 21:08:51 +08:00
tigerenwork fe6dddecf9 fix: move customer between status and age in bug row 2026-06-29 20:57:11 +08:00
tigerenwork 0ce557ac8e feat: add customer field to Bug model with configurable mapping
- Bug model: added optional 'customer' property
- FieldMappings: added 'customerField' (default: 客户名称)
- BugMapper: reads customer value from configured column
- BugRow: displays customer in blue when present
- Settings: Customer field text field in Field Mappings section

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-29 20:50:46 +08:00
tigerenwork 7590389a04 feat: add Quit button, extended polling intervals
- Popover footer now has a Quit button (power icon) to terminate the app
- Polling interval picker now includes 1h, 2h, 4h, and 1 day options

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-29 01:17:07 +08:00
tigerenwork 94a3d76af9 fix: badge shows active count, floating widget shows priorities, record URL deep link
1. Menu bar badge and floating widget now show total active bugs count
   (was showing unseen-only count). Unseen dot still works independently
   in the bug list rows.

2. Floating widget now shows priority breakdown:
   🐜 12  🔴3 🟡4 🔵2 3
   Each priority gets a colored dot + count. Only non-zero priorities shown.

3. Fixed Feishu record URL format: now uses query-parameter deep link
   /base/{appToken}?table={tableId}&record={recordId} instead of path
   segments, which opens the specific record row directly in Feishu.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-29 01:01:01 +08:00
tigerenwork 723c50fc9d feat: configurable status mappings, scrollable settings, case-insensitive assignee
- Added FieldMappings.statusMappings: [FeishuValue: BugStatus] dict
  mapping Feishu select-option text to standard Bugger statuses.
  Pre-populated with common Chinese/English values:
  开发进行中→inProgress, 验收完毕→resolved, Bug Triage→open, etc.
- BugMapper.mapStatus() looks up Feishu value in the mapping,
  falling back to .unknown (shown as active bug).
- Settings: status mapping section with one TextField per status
  showing comma-separated Feishu values. Editable and saved to config.
- Settings: wrapped Form in ScrollView, removed fixed height,
  use minHeight/maxHeight — no more truncated buttons.
- Assignee matching now uses case-insensitive contains instead of
  exact match, handling display name variations from Feishu API.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-29 00:51:03 +08:00
tigerenwork 74baa22d27 fix: wider settings window, better OAuth error handling, empty refresh token guard
- SettingsWindow: widened to 500pt min, connection result on its own line
  with multiline support (error messages no longer truncated)
- OAuthSetupView: clear statusMessage on error, added 'Try again' button
  when authorization fails, disable Connect button while in progress
- TokenManager: guard against empty refresh token strings (was stored as ''
  when Feishu didn't return refresh_token, causing confusing 'No Feishu
  session' error after access token expired)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-29 00:09:28 +08:00
tigerenwork 9f8fb52726 feat: switch OAuth redirect to localhost loopback (RFC 8252)
Feishu rejected the 'bugger://' custom URL scheme redirect with 参数不合法.
Switched to the standard desktop OAuth flow: a one-shot local HTTP server
on 127.0.0.1:18923 that receives the authorization code from the browser.

Changes:
- Added LocalOAuthServer (Network.framework, one-shot HTTP listener)
- FeishuAuthService now uses dynamic http://127.0.0.1:PORT/callback redirect
- Removed CFBundleURLTypes from Info.plist (no custom scheme needed)
- Removed NSAppleEventManager handler from AppDelegate
- TokenManager.handleCallback changed from URL to code string
- OAuthSetupView starts server before opening browser
- Added offline_access scope for refresh token support
- generate_xcode_project.py: removed dev cert requirements,
  auto-quotes paths with special chars

Builds successfully with xcodebuild.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-28 23:48:49 +08:00
tigerenwork d9d8ce3e46 feat: full macOS menu bar app implementation
SwiftUI macOS menu bar app for tracking Feishu Bitable bugs:
- Menu bar icon with active bug count badge
- Popover with bug list sorted by priority, click to open in Feishu
- Floating always-on-top widget (NSPanel, optional)
- OAuth 2.0 flow with Feishu Open API (user_access_token + refresh)
- PollerService with configurable interval (default 5 min)
- Diff engine detecting new bugs, status/priority/assignee changes
- Native macOS notifications (UserNotifications framework)
- Keychain-backed token storage
- Settings window with field mapping, polling interval, widget toggle
- Support for custom Bitable column name mappings
- Zero third-party dependencies

17 source files across Models/Services/Views/ViewModels/Utils layers.
Xcode project generated via scripts/generate_xcode_project.py.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-28 20:56:44 +08:00