Skip to content

Releases: caseymrm/menuet

v2.9.0 — UnderlineColor / StrikethroughColor

11 Jun 15:37
28ed389

Choose a tag to compare

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)

11 Jun 14:39
aff229c

Choose a tag to compare

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

10 Jun 13:17
60366bd

Choose a tag to compare

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)

10 Jun 00:30
275e547

Choose a tag to compare

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

10 Jun 00:27
d848141

Choose a tag to compare

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

10 Jun 00:22
812ae49

Choose a tag to compare

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

09 Jun 13:20
38c75cc

Choose a tag to compare

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

08 Jun 00:24
0bb4637

Choose a tag to compare

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

07 Jun 02:03
23949d5

Choose a tag to compare

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

07 Jun 01:11
0f317e7

Choose a tag to compare

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"