# Apptag — Implementation Plan ## Goal A native macOS menubar app that displays a full-screen overlay (Spotlight/Launchpad style) showing installed apps grouped by their Finder tags, with user-customizable group names. ## Technical Architecture ``` Apptag.app ├── ApptagApp.swift — @main entry point, menubar setup ├── TagStore.swift — reads Finder tags from .app bundles ├── AppIndexer.swift — scans /Applications, ~/Applications for .app bundles ├── ContentView.swift — full-screen overlay: search bar + tag-grouped grid ├── AppGridItem.swift — individual app icon + name card ├── TagGroupView.swift — collapsible group section with header ├── HotkeyManager.swift — global keyboard shortcut (opt+space or custom) ├── Info.plist — LSUIElement = YES (menubar-only, no dock icon) └── Assets.xcassets — app icon ``` ## Data Flow 1. **On launch**: `AppIndexer` scans standard app directories, collects all .app paths 2. **Tag reading**: For each app, `TagStore` reads `com.apple.metadata:_kMDItemUserTags` via `NSURL` resource values or `xattr` 3. **Grouping**: Apps grouped by tag name; untagged apps go to a configurable default group 4. **Display**: `ContentView` renders full-screen overlay with: - Top: search bar (filters apps by name in real-time) - Body: scrollable grid of tag-grouped app icons 5. **Trigger**: `HotkeyManager` listens for global hotkey; menu bar icon as fallback ## Key Technical Decisions ### Reading Finder Tags - Use `URLResourceValues` with `.tagNamesKey` — this is the proper Cocoa API - Falls back to parsing `xattr` binary plist if needed - Tags from SIP-protected paths may fail — gracefully skip ### Scanning Apps - Standard locations: `/Applications`, `~/Applications`, `/System/Applications` - Use `FileManager.enumerator` for recursive .app discovery - Filter: only bundles ending in `.app` with valid Info.plist ### UI Design - Full-screen NSWindow (or SwiftUI WindowGroup with `.windowStyle(.hiddenTitleBar)`) - Semi-transparent blurred background (`.regularMaterial` or `.ultraThinMaterial`) - Grid layout: 5-6 columns, adaptive to screen width - Each app: large icon (64pt) + name below (SF Pro, 12pt) - Group header: tag name in SF Pro Display, 17pt semibold - Search: top-center text field, filters in real time ### Hotkey - Register with `RegisterEventHotKey` (Carbon API) or `NSEvent.addGlobalMonitorForEvents` - Default: Option+Space (⌥+Space) — doesn't conflict with Spotlight (⌘+Space) - Configurable in preferences ### Persistence - UserDefaults for: custom group name mappings, hotkey preference, excluded apps - Tag ↔ group name mapping stored as a dictionary ## Build Phases ### Phase 1: Core tag reading + app scanning - `AppIndexer.swift` — directory scanning - `TagStore.swift` — tag extraction - CLI test target to validate ### Phase 2: UI skeleton - `ApptagApp.swift` — menubar entry point - `ContentView.swift` — full-screen overlay - Search bar + grid layout ### Phase 3: Group rendering + polish - `TagGroupView.swift` — group sections - `AppGridItem.swift` — app cards - Animations, keyboard navigation, accessibility ### Phase 4: Hotkey + preferences - Global hotkey registration - Preferences window (hotkey config, default group name) - Custom tag-to-group-name mappings ## Risks 1. **SIP**: System apps can't be tagged — handled by graceful skip 2. **macOS 26 API changes**: Full-screen overlay behavior may differ — test on target OS 3. **Hotkey registration**: Carbon hotkey API is deprecated but still works; NSEvent monitor is the modern alternative but requires Accessibility permission 4. **Performance**: Scanning 200+ apps on every toggle is slow — implement filesystem watch (FSEvents) for incremental updates ## Files to Create All under `~/Projects/Apptag/Apptag/`: - `ApptagApp.swift` - `TagStore.swift` - `AppIndexer.swift` - `ContentView.swift` - `AppGridItem.swift` - `TagGroupView.swift` - `HotkeyManager.swift` - `Info.plist`