This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
- Use Xcode to build and run (
⌘R). Do not usexcodebuildfrom command line. - Tests:
swift test(runs model tests via SPM — seePackage.swiftfor theKeyStatsCoretest target) - Distribution:
./scripts/build_dmg.sh
KeyStats is a macOS menu bar app (LSUIElement) that tracks keyboard/mouse input statistics — counts and distances only, never content.
Data flow:
CGEventTap (InputMonitor) → StatsManager → MenuBarController (display)
→ UserDefaults (persistence, debounced 2s)
→ StatsPopoverViewController (detail panel)
Core singletons:
InputMonitor.shared— Global event tap atcgSessionEventTaplevel; monitors key presses, mouse clicks, movement (30Hz sampling), scroll. Requires Accessibility permission.StatsManager.shared— Aggregates daily stats, per-app stats, per-key counts, peak KPS/CPS (sliding window). Handles persistence, midnight auto-reset, and menu bar update callbacks.MenuBarController— NSStatusItem with compact dual-line display (SwiftUIMenuBarStatusSwiftUIViewhosted inMenuBarStatusView). Manages popover lifecycle and highlight state.
Data models (AppStats.swift, StatsModels.swift):
DailyStats— per-day aggregate with history dictionaryAppStats— per-bundle-ID breakdown- Both are
Codable, persisted as JSON in UserDefaults
Windows variant lives in KeyStats.Windows/ (C#/.NET, separate codebase).
- NEVER log actual keystrokes, mouse positions, or user input content — only aggregate counts and distances
- Event callbacks run on background CGEventTap thread — dispatch UI updates to
DispatchQueue.main - Three
NSLocks protect concurrent access:inputRateLock,statsStateLock,mouseDistanceCalibrationLock
CALayer.backgroundColor/borderColoruseCGColor(static snapshot) — they don't auto-follow appearance changes- Always resolve dynamic colors under current
effectiveAppearanceusingresolvedCGColor(color, alpha:, for:)helper - Re-assign layer colors on every theme change; never cache
CGColorvalues - Prefer dynamic colors (
NSColor.labelColor,NSColor.controlBackgroundColor) over hardcoded values
- Soft glass-card surfaces:
controlBackgroundColorwith alpha ~0.6–0.85 - Thin 0.5pt separators with low alpha, subtle shadows, 10–12pt corner radius for cards
- When adding a new page/window/popover, add matching PostHog analytics: a
pageviewevent andclickevents for key actions
- Localize user-facing strings with
NSLocalizedString()(English + Simplified Chinese) - Use
[weak self]in closures to prevent retain cycles - Maintain backward compatibility with existing UserDefaults keys when changing data models
- PostHog (posthog-ios) — Analytics, initialized in AppDelegate
- Sparkle — Auto-updates via GitHub releases appcast