feat: macOS menu bar app (Wails v3 + Svelte 5 + Tailwind 4)#4
Merged
nahime0 merged 10 commits intoillegalstudio:mainfrom Mar 7, 2026
Merged
Conversation
Wails v3 + Svelte 5 + Tailwind 4 architecture with shared Go core. 6 phases: core extraction, scaffold, backend, frontend, tray, distribution. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Decisions: Wails v3 alpha pinned, arm64-only, single brand name, config.yaml in Phase 0, Launch at Login in Phase 4. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Extract shared core library (internal/core/): watcher, activity state machine, session manager, config, helpers - Add Wails v3 macOS app (cmd/app/): system tray, frameless floating panel, SessionService with 9 bound methods, FSEvents + event push - Add Svelte 5 frontend: SessionList, SessionDetail, Sparkline, ActivityBadge, Catppuccin Mocha theme via @theme - Refactor TUI (internal/ui/) to import from core instead of local code - Add dedicated entry points: cmd/tui/ and cmd/app/ - Add Makefile with tui, app, frontend, bindings, dev, clean targets - Add shared config system (~/.config/lazyagent/config.json) - Update README with dual-interface docs, architecture, and dev guide Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- CRITICAL: Add sync.RWMutex to SessionManager to prevent data races between watchLoop goroutine and Wails IPC handlers - Add context-based shutdown to watchLoop (select on ctx.Done()) - UpdateActivities returns bool; 1s ticker only emits when changed - Extract EffectiveCost() helper, deduplicate cost fallback pattern - Extract buildSessionItem() in service.go, remove duplication - OpenInEditor now checks Config.Editor before env vars - Centralize window-minutes clamping in core.SetWindowMinutes() - Remove dead SessionView struct - Unify Sparkline SVG coordinate computation into single derived Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move cmd/app/ service into internal/tray/ package with Run() entry point. Single main.go dispatches between TUI (default) and tray (--tray flag). Auto-detach on --tray so the terminal returns immediately. Single-instance enforcement via PID file with automatic kill of previous. Add HasFrontend() check to assets package. Update Makefile: 'make build' for full, 'make tui' for TUI-only. Update frontend imports to new bindings path.
Add build tag 'notray' to exclude Wails dependencies on platforms without CGo. Linux builds use notray tag (CGO_ENABLED=0), macOS builds include full tray support (CGO_ENABLED=1). Add before.hooks to goreleaser for frontend build step.
Replace default Wails template icon with a custom scan-eye icon embedded via go:embed. Icon is rendered at 36x36 with padding for proper macOS menu bar sizing.
- lazyagent: TUI (default) - lazyagent --tui: TUI (explicit) - lazyagent --tray: tray only (detached) - lazyagent --tui --tray: spawn tray in background, then run TUI
- Document --tui, --tray, --tui --tray flags - Update architecture section (single binary, internal/tray/) - Add tray.png screenshot, switch to screenshot.png - Update build instructions (make build/tui)
Member
|
@amargiovanni I'd prefer having a single binary with flags for the different modes rather than two separate binaries. I've refactored the branch so that:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
lazyagent-app) using Wails v3 + Svelte 5 + Tailwind 4, living alongside the existing TUIinternal/core/) — shared session management, file watcher, activity state machine, config system, and helpers used by both TUI and appinternal/core/instead of local implementations — zero behavior changes, same binary sizego.mod:go build ./cmd/tui/(5MB) andgo build ./cmd/app/(18MB)What the menu bar app does
@themeArchitecture
Code quality fixes included
After the initial implementation, I ran a three-way review (reuse, quality, efficiency) and fixed:
sync.RWMutextoSessionManager— prevents data races between the backgroundwatchLoopgoroutine and Wails IPC handler goroutineswatchLoopnow selects onctx.Done()for clean goroutine shutdownsessions:updatedwhen activity states actually changed (was unconditional)EffectiveCost()helper — cost fallback pattern was duplicated 3xbuildSessionItem()—SessionItemconstruction was copy-pasted betweenGetSessionsandGetSessionDetailOpenInEditornow checksConfig.Editorbefore falling back to$VISUAL/$EDITORcore.SetWindowMinutes()viaClamp()— was magic numbers in 3 files$derivedIntentionally left out of scope (and why)
formatTokens/formatCostduplicated in Go and TypeScriptAllActivitiesin Go. Generating from bindings would add build complexity for a list that changes ~never..catch(() => {})on frontend IPC callsEvents.Onlistener not cleaned up inonDestroyGetActiveCountbinding unused by frontendagovalues in tool listsessions:updatedevent. Acceptable staleness for a monitoring tool.GetConfigre-reads from disk every callDiscoverSessionsre-parses all files on reload. For typical session counts (5-20) this is fast enough. Worth optimizing later if session counts grow.git rev-parsecache promotion to SessionManagerDiscoverSessionsis rebuilt every reload. Fine for now, could be promoted if git subprocess cost becomes noticeable.Test plan
go vet ./...passes cleango build ./cmd/tui/produces working TUI (verify sparkline, cost, spinner, filter, search, open)go build ./cmd/app/produces 18MB arm64 binarygo build .(root alias) still works🤖 Generated with Claude Code