Releases: caseymrm/menuet
v2.9.0 — UnderlineColor / StrikethroughColor
Exposes NSAttributedString's independent underline and strikethrough color attributes via two new TextRun fields. Zero value follows the foreground (preserving v2.8 behavior); set them when you want a red squiggle on gray text or similar emphasis. Catalog adds a side-by-side demo. Merged via #41.
v2.8.0 — TextRun emphasis (Underline / Strikethrough / Background / Shadow)
Four more attributed-string attributes on TextRun:
- Underline — single underline in the run's foreground color
- Strikethrough — for eliminated / loss treatments
- Background — marker-pen colored highlight (different from Badge; text stays normal-color)
- Shadow — drop shadow or glow; set Blur alone for a trophy-celebration halo
All additive. No new bridge mechanism or private API — each maps to its standard NSAttributedString key. cmd/catalog gains a 🏆 LAL WINS celebration row plus a side-by-side emphasis showcase in the Rich text submenu.
Merged via #40.
v2.7.0 — sportsbar rich-text additions
Four additions specified by the sportsbar design handoff:
- MenuState.Runs — rich text in the menubar title (same TextRun shape as rows)
- Color.Semantic — appearance-adaptive colors via AppKit dynamic NSColors (LabelPrimary/Secondary/Tertiary/Quaternary + SystemRed/Green/Yellow/Blue/Orange/Purple/Pink/Gray/Brown/Teal/Indigo/Mint/Cyan)
- Regular.Subtitle — two-line items via NSMenuItem.subtitle on macOS 14+ (silently dropped on older)
- TextRun.Badge — rounded-pill rendering for LIVE/NEW-style indicators (h14, padX5, radius7; white-on-fill 9pt bold)
All additive. Backward compatible. The sportsbar redesign now has everything it needs to compile against menuet.
Merged via #39.
v2.6.0 — Modern login items (SMAppService)
Start at Login now uses macOS 13's SMAppService API when available — apps show up under System Settings → Login Items for the user to revoke from the standard place. LaunchAgent plist remains the fallback for older macOS and unsigned dev builds. API surface unchanged; backend choice is internal. Merged via #38.
v2.5.0 — Global hotkeys
Adds Regular.Shortcut for system-wide hotkeys that also display in the menu (⌘⇧Space etc.). Powered by Carbon RegisterEventHotKey + NSMenuItem.keyEquivalent. Backward compatible. Merged via #37.
v2.4.0 — RichText
Adds mixed-style text in menu items: per-item Color/Monospaced and per-run styling via Regular.Runs. Backward compatible — existing items unchanged. Merged via #36.
v2.3.1 — Drop cursor warp; fix elision + query persistence
Follow-up to v2.3.0. Merged via #33.
The big change
Removes the CGEventPost click-sim path from Search. That mechanism was engaging NSMenu's "field actively selected" state so arrow keys could navigate result items — but it cost an Accessibility-permission prompt on first run, a visible mouse-cursor warp on every menu open, and a more disorienting second warp on menu close.
Net result: no more Accessibility prompt, no more cursor warp. Trade-off is arrow keys no longer navigate result items — you click to pick instead. Documented in the `Search` struct's doc comment.
Everything else still works:
- Type to filter (live)
- Enter activates the first result
- Esc closes the menu
- Click any result to pick
- Remembered query across menu opens
Bug fixes
- Mid-tracking elision of long result strings. `Massachusetts` no longer center-elides to `Mass…usetts` when filtering mid-tracking. Fix: `NSLineBreakByClipping` per item + dynamic `minimumWidth` from `NSMenuItemCell.cellSize`.
- Query not persisting when user moused away from the submenu and back. Added a save in `viewDidMoveToWindow` on window=nil (submenu close), not just on root-menu end-tracking.
Docs
`docs/search-bar-research.md` updated to reflect the shipped design; the click-sim investigation is preserved as historical record in case a cleaner approach surfaces later.
v2.3.0 — Search MenuItem
Adds a new `Search` concrete type implementing `MenuItem` — an in-menu Apple-Help-style search field.
```go
menuet.Search{
Placeholder: "Search files…",
Results: func(query string) []menuet.MenuItem {
if query == "" { return recentFiles() }
return matches(query)
},
}
```
`Results` fires on every keystroke (and once with `""` on menu open). Returned items render below the field and update live. The last query persists across menu opens and is preselected so typing replaces it. Enter activates the first result, Esc closes.
⚠ App Store
Uses two private AppKit hooks (`setKeyOverride:` and a synthesized hardware-level click via `CGEventPost`) — apps using `menuet.Search` cannot be distributed via the Mac App Store. Direct-distribution apps are unaffected.
Known limitations
- Mouse cursor briefly jumps to the field on menu open; mitigated to a few pixels at the field's left edge and immediately hidden via `setHiddenUntilMouseMoves:` so keyboard-only users don't see it
- Mid-tracking elision (long result names get center-elided when the menu's content rect doesn't grow) — separate issue, follow-up planned
Investigation notes
`docs/search-bar-research.md` captures the architectural story (every approach tried + why it failed + the structural constraints inside AppKit's menu tracking + untried future avenues).
Merged via #32.
v2.2.0 — MenuItem becomes an interface
Breaking change. Sets up the type shape for the upcoming `Search` menu item type.
`MenuItem` is now a marker interface. `Regular` and `Separator` are the first two concrete types implementing it.
Migration
```go
// Before
[]menuet.MenuItem{
menuet.MenuItem{Text: "Refresh", Clicked: refresh},
menuet.MenuItem{Type: menuet.Separator},
}
// After
[]menuet.MenuItem{
menuet.Regular{Text: "Refresh", Clicked: refresh},
menuet.Separator{},
}
```
Bare struct literals inside `[]menuet.MenuItem{...}` slices must qualify the concrete type (Go can't infer interface elements). Sorting by a field needs a type assertion: `items[i].(menuet.Regular).Text`.
Retraction
`go.mod` retracts v2.0.0, v2.1.0 (both unimportable due to the missing `/v2` module path), and v2.1.1 (superseded by this change). v2.2.0 is the de-facto first usable v2.
Why
`Search` is coming next and needs its own field (the query handler). With the existing struct-with-Type pattern, that would have been a fourth function-type field on `MenuItem` with conditional meaning. The interface shape lets each concrete type carry only what makes sense and lets the compiler catch "set `Clicked` on a `Separator`" mistakes at the call site.
Merged via #31.
v2.1.1 -- First importable v2 release
First v2 release that is actually usable by Go module consumers.
v2.0.0 and v2.1.0 were tagged, but go.mod still declared module github.com/caseymrm/menuet (no /v2 suffix), which violates the Go modules rule that the module path must end in /vN for major version N >= 2. go get github.com/caseymrm/menuet/v2@v2.0.0 or @v2.1.0 failed with "module path does not match" -- effectively there was no importable v2 line.
v2.1.1 is functionally identical to v2.1.0. The only change is bumping the module path in go.mod to github.com/caseymrm/menuet/v2 (plus updating the example apps and README to use the new import). All v2 features ship unchanged:
- Secure password input fields on
Alert(v2.0.0 breaking change) - Customizable Start at Login / Quit menu item labels
- Optional prerelease autoupdates
- launchd / startup fixes
- Widened alert input fields (200px to 300px)
- Optional left-click handler on the menubar icon (
Application.Clicked)
Install
go get github.com/caseymrm/menuet/v2@v2.1.1
import "github.com/caseymrm/menuet/v2"