menuet

package module
v2.10.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 14, 2026 License: MIT Imports: 22 Imported by: 0

README

Menuet

Golang library to create menubar apps- programs that live only in OSX's NSStatusBar

Development Status

Under active development. API still changing rapidly.

Installation

menuet requires OS X.

go get github.com/caseymrm/menuet/v2

Documentation

https://godoc.org/github.com/caseymrm/menuet

Left-click handler

Set Application.Clicked to intercept left clicks on the menubar icon instead of opening the menu. The menu still opens on right click (or Ctrl-left-click). Useful for toggle-style apps — mute audio, pause a timer, etc. — where the menu is the secondary UI:

menuet.App().Clicked = func() {
    // toggle whatever state your app exposes
}

Leave Clicked as nil (the default) for the standard behavior where any click opens the menu. Safe to set or clear at runtime; the next click reflects the current value.

Running as a real macOS app

go run is fine for early development, but several menuet features only work when the binary is launched from inside a proper macOS .app bundle. These requirements are enforced by macOS, not by menuet:

  • Notifications require a bundle. UNUserNotificationCenter will silently no-op for a loose executable. The app also needs to be code-signed — ad-hoc signing is not enough; you need a Developer ID signature (or full notarization for distribution).
  • Start at Login prefers the macOS 13+ Service Management framework (SMAppService) when available, which puts the app under System Settings → Login Items so the user can revoke it from the standard place. For older macOS or unsigned dev builds the older LaunchAgent plist path is used as a fallback. Either backend wants the app to be bundled.
  • Auto-update moves a new .app bundle on top of the running one, so it obviously needs a bundle to update.

The shared menuet.mk Makefile assembles a minimal bundle for you. From your app directory, create a Makefile like:

APP=My App
IDENTIFIER=com.example.myapp
include $(GOPATH)/src/github.com/caseymrm/menuet/menuet.mk

Then make run builds the binary into My App.app/Contents/MacOS/myapp, generates My App.app/Contents/Info.plist with your CFBundleIdentifier, and launches it. The cmd/catalog example uses this pattern.

To sign the bundle for notifications and distribution, set IDENTITY to a Developer ID Application certificate from your Keychain and run make sign.

Apps built with Menuet

  • Why Awake? - shows why your Mac can't sleep, and lets you force it awake

  • Not a Fan - shows your Mac's temperature and fan speed, notifies you when your CPU is being throttled due to excessive heat

  • Traytter - minimalist Twitter client for following a few users

Hello World

package main

import (
	"time"

	"github.com/caseymrm/menuet/v2"
)

func helloClock() {
	for {
		menuet.App().SetMenuState(&menuet.MenuState{
			Title: "Hello World " + time.Now().Format(":05"),
		})
		time.Sleep(time.Second)
	}
}

func main() {
	go helloClock()
	menuet.App().RunApplication()
}

Output

Menu items

MenuItem is an interface; the concrete types are Regular (a normal row) and Separator (a horizontal divider). Construct a menu by returning a []menuet.MenuItem containing whichever concrete types you need:

menuet.App().Children = func() []menuet.MenuItem {
    return []menuet.MenuItem{
        menuet.Regular{Text: "Status: Active"},
        menuet.Separator{},
        menuet.Regular{Text: "Refresh", Clicked: refresh},
        menuet.Regular{Text: "Submenu", Children: subItems},
    }
}

Regular carries the familiar fields — Text, Image, FontSize, FontWeight, State, Clicked, Children. Setting Clicked makes it clickable; setting Children makes it a submenu.

Toggle-style apps

For apps where the primary action is a toggle (mute audio, pause a timer, hide notifications…), macOS menus dismiss the moment the user clicks an item — there's no public API to "click without closing." Two patterns work around it:

Left click toggles, right click opens the menu. Set Application.Clicked to a callback; left clicks fire the callback without opening the menu, right clicks (and Ctrl-left-clicks) still open the menu for secondary actions:

menuet.App().Clicked = func() { toggleMuted() }

Stateful menu items with checkmarks. For toggles you do want inside the menu, set MenuItem.State = true to show a checkmark, and update your app state from the Clicked callback. The menu will dismiss on click as usual (OS standard); on the next open, return the items with the new State:

menuet.Regular{
    Text:    "Notifications enabled",
    State:   prefs.NotificationsEnabled,
    Clicked: func() { prefs.NotificationsEnabled = !prefs.NotificationsEnabled },
}

Catalog

The catalog app is useful for trying many of the possible combinations of features.

Advanced Features

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"sort"
	"strconv"
	"time"

	"github.com/caseymrm/menuet/v2"
)

func temperature(woeid string) (temp, unit, text string) {
	url := "https://query.yahooapis.com/v1/public/yql?format=json&q=select%20item.condition%20from%20weather.forecast%20where%20woeid%20%3D%20" + woeid
	resp, err := http.Get(url)
	if err != nil {
		log.Fatal(err)
	}
	var response struct {
		Query struct {
			Results struct {
				Channel struct {
					Item struct {
						Condition struct {
							Temp string `json:"temp"`
							Text string `json:"text"`
						} `json:"condition"`
					} `json:"item"`
					Units struct {
						Temperature string `json:"temperature"`
					} `json:"units"`
				} `json:"channel"`
			} `json:"results"`
		} `json:"query"`
	}
	dec := json.NewDecoder(resp.Body)
	err = dec.Decode(&response)
	if err != nil {
		log.Fatal(err)
	}
	return response.Query.Results.Channel.Item.Condition.Temp, response.Query.Results.Channel.Units.Temperature, response.Query.Results.Channel.Item.Condition.Text
}

func location(query string) (string, string) {
	url := "https://query.yahooapis.com/v1/public/yql?format=json&q=select%20woeid,name%20from%20geo.places%20where%20text%3D%22" + url.QueryEscape(query) + "%22"
	resp, err := http.Get(url)
	if err != nil {
		log.Printf("Get: %v", err)
		menuet.App().Alert(menuet.Alert{
			MessageText:     "Could not get the weather",
			InformativeText: err.Error(),
		})
		return "", ""
	}
	var response struct {
		Query struct {
			Results struct {
				Place struct {
					Name  string `json:"name"`
					WoeID string `json:"woeid"`
				} `json:"place"`
			} `json:"results"`
		} `json:"query"`
	}
	dec := json.NewDecoder(resp.Body)
	err = dec.Decode(&response)
	if err != nil {
		log.Printf("Decode: %v", err)
		menuet.App().Alert(menuet.Alert{
			MessageText:     "Could not search for location",
			InformativeText: err.Error(),
		})
		return "", ""
	}
	return response.Query.Results.Place.Name, response.Query.Results.Place.WoeID
}

func temperatureString(woeid string) string {
	temp, unit, text := temperature(woeid)
	return fmt.Sprintf("%s°%s and %s", temp, unit, text)
}

func setWeather() {
	menuet.App().SetMenuState(&menuet.MenuState{
		Title: temperatureString(menuet.Defaults().String("loc")),
	})
}

var woeids = map[int]string{
	2442047: "Los Angeles",
	2487956: "San Francisco",
	2459115: "New York",
}

func menuPreview(woeid string) func() []menuet.MenuItem {
	return func() []menuet.MenuItem {
		return []menuet.MenuItem{
			menuet.Regular{
				Text: temperatureString(woeid),
				Clicked: func() {
					setLocation(woeid)
				},
			},
		}
	}
}

func menuItems() []menuet.MenuItem {
	items := []menuet.MenuItem{}

	currentWoeid := menuet.Defaults().String("loc")
	currentNumber, err := strconv.Atoi(currentWoeid)
	if err != nil {
		log.Printf("Atoi: %v", err)
	}
	found := false
	for woeid, name := range woeids {
		woeStr := strconv.Itoa(woeid)
		items = append(items, menuet.Regular{
			Text: name,
			Clicked: func() {
				setLocation(woeStr)
			},
			State:    woeStr == menuet.Defaults().String("loc"),
			Children: menuPreview(woeStr),
		})
		if woeid == currentNumber {
			found = true
		}
	}
	if !found {
		items = append(items, menuet.Regular{
			Text: menuet.Defaults().String("name"),
			Clicked: func() {
				setLocation(currentWoeid)
			},
			Children: menuPreview(currentWoeid),
			State:    true,
		})
	}
	sort.Slice(items, func(i, j int) bool {
		return items[i].(menuet.Regular).Text < items[j].(menuet.Regular).Text
	})
	items = append(items, menuet.Regular{
		Text: "Other...",
		Clicked: func() {
			response := menuet.App().Alert(menuet.Alert{
				MessageText: "Where would you like to display the weather for?",
				Inputs:      []menuet.AlertInput{{Placeholder: "Location"}},
				Buttons:     []string{"Search", "Cancel"},
			})
			if response.Button == 0 && len(response.Inputs) == 1 && response.Inputs[0] != "" {
				newName, newWoeid := location(response.Inputs[0])
				if newWoeid != "" && newName != "" {
					menuet.Defaults().SetString("loc", newWoeid)
					menuet.Defaults().SetString("name", newName)
					menuet.App().Notification(menuet.Notification{
						Title:    fmt.Sprintf("Showing weather for %s", newName),
						Subtitle: temperatureString(newWoeid),
					})
					setWeather()
				}
			}
		},
	})
	return items
}

func hourlyWeather() {
	for {
		setWeather()
		time.Sleep(time.Hour)
	}
}

func setLocation(woeid string) {
	menuet.Defaults().SetString("loc", woeid)
	setWeather()
}

func main() {
	// Load the location from last time
	woeid := menuet.Defaults().String("loc")
	if woeid == "" {
		menuet.Defaults().SetString("loc", "2442047")
	}

	// Start the hourly check, and set the first value
	go hourlyWeather()

	// Configure the application
	menuet.App().Label = "com.github.caseymrm.menuet.weather"

	// Hook up the on-click to populate the menu
	menuet.App().Children = menuItems

	// Run the app (does not return)
	menuet.App().RunApplication()
}

Output

License

Menuet is licensed under the MIT license, so you are welcome to make closed source menubar apps with it as long as you preserve the copyright. For details see the LICENSE file.

Documentation

Index

Constants

View Source
const (
	// WeightUltraLight is equivalent to NSFontWeightUltraLight
	WeightUltraLight FontWeight = -0.8
	// WeightThin is equivalent to NSFontWeightThin
	WeightThin = -0.6
	// WeightLight is equivalent to NSFontWeightLight
	WeightLight = -0.4
	// WeightRegular is equivalent to NSFontWeightRegular, and is the default
	WeightRegular = 0
	// WeightMedium is equivalent to NSFontWeightMedium
	WeightMedium = 0.23
	// WeightSemibold is equivalent to NSFontWeightSemibold
	WeightSemibold = 0.3
	// WeightBold is equivalent to NSFontWeightBold
	WeightBold = 0.4
	// WeightHeavy is equivalent to NSFontWeightHeavy
	WeightHeavy = 0.56
	// WeightBlack is equivalent to NSFontWeightBlack
	WeightBlack = 0.62
)
View Source
const (
	KeyA = 0
	KeyB = 11
	KeyC = 8
	KeyD = 2
	KeyE = 14
	KeyF = 3
	KeyG = 5
	KeyH = 4
	KeyI = 34
	KeyJ = 38
	KeyK = 40
	KeyL = 37
	KeyM = 46
	KeyN = 45
	KeyO = 31
	KeyP = 35
	KeyQ = 12
	KeyR = 15
	KeyS = 1
	KeyT = 17
	KeyU = 32
	KeyV = 9
	KeyW = 13
	KeyX = 7
	KeyY = 16
	KeyZ = 6

	Key0 = 29
	Key1 = 18
	Key2 = 19
	Key3 = 20
	Key4 = 21
	Key5 = 23
	Key6 = 22
	Key7 = 26
	Key8 = 28
	Key9 = 25

	KeySpace  = 49
	KeyReturn = 36
	KeyTab    = 48
	KeyEsc    = 53

	KeyF1  = 122
	KeyF2  = 120
	KeyF3  = 99
	KeyF4  = 118
	KeyF5  = 96
	KeyF6  = 97
	KeyF7  = 98
	KeyF8  = 100
	KeyF9  = 101
	KeyF10 = 109
	KeyF11 = 103
	KeyF12 = 111

	KeyLeft  = 123
	KeyRight = 124
	KeyDown  = 125
	KeyUp    = 126
)

macOS virtual key codes. Lowercase letters in QWERTY layout. Not exhaustive — add as needed.

View Source
const SnapshotSchema = "menuet-snapshot/v1"

SnapshotSchema is the version string embedded in every snapshot file. Bump this when the snapshot JSON shape changes so consumers can detect incompatible versions. Existing fields should never change meaning.

Variables

View Source
var (
	Red    = Color{R: 220, G: 50, B: 50, A: 255}
	Green  = Color{R: 30, G: 160, B: 60, A: 255}
	Yellow = Color{R: 200, G: 160, B: 0, A: 255}
	Blue   = Color{R: 0, G: 110, B: 200, A: 255}
	Gray   = Color{R: 128, G: 128, B: 128, A: 255}
)

Fixed-RGBA named colors. These don't adapt to dark / light mode; for hierarchy and accent colors that should adapt, use the semantic constants (LabelPrimary etc.) below.

View Source
var (
	LabelPrimary    = Color{Semantic: "labelColor"}           // default text
	LabelSecondary  = Color{Semantic: "secondaryLabelColor"}  // less prominent
	LabelTertiary   = Color{Semantic: "tertiaryLabelColor"}   // metadata / fine print
	LabelQuaternary = Color{Semantic: "quaternaryLabelColor"} // very faint, e.g. spoiler veil

	SystemRed    = Color{Semantic: "systemRedColor"}
	SystemGreen  = Color{Semantic: "systemGreenColor"}
	SystemYellow = Color{Semantic: "systemYellowColor"}
	SystemBlue   = Color{Semantic: "systemBlueColor"}
	SystemOrange = Color{Semantic: "systemOrangeColor"}
	SystemPurple = Color{Semantic: "systemPurpleColor"}
	SystemPink   = Color{Semantic: "systemPinkColor"}
	SystemGray   = Color{Semantic: "systemGrayColor"}
	SystemBrown  = Color{Semantic: "systemBrownColor"}
	SystemTeal   = Color{Semantic: "systemTealColor"}
	SystemIndigo = Color{Semantic: "systemIndigoColor"}
	SystemMint   = Color{Semantic: "systemMintColor"}
	SystemCyan   = Color{Semantic: "systemCyanColor"}
)

Semantic colors map onto AppKit dynamic NSColors and re-resolve per appearance at draw time. Use these for text hierarchy and accents that should adapt across light and dark mode.

Functions

This section is empty.

Types

type Alert

type Alert struct {
	MessageText     string
	InformativeText string
	Buttons         []string
	Inputs          []AlertInput
}

Alert represents an NSAlert

type AlertClicked

type AlertClicked struct {
	Button int
	Inputs []string
}

AlertClicked represents a selected alert button

type AlertInput

type AlertInput struct {
	Placeholder string
	Type        InputType
}

AlertInput defines an input field in an alert

type Application

type Application struct {
	Name  string
	Label string

	// Children returns the top level children
	Children func() []MenuItem

	// If Version and Repo are set, checks for updates every day
	AutoUpdate struct {
		Version string
		Repo    string // For example "caseymrm/menuet"
		// AllowPrerelease opts in to GitHub releases marked as prereleases.
		// When false (the default), prereleases are filtered out before
		// choosing what to update to. Apps on a prerelease are not
		// downgraded to the latest stable — switch channels manually.
		AllowPrerelease bool
	}

	// NotificationResponder is a handler called when notification respond
	NotificationResponder func(id, response string)

	// Clicked, if set, is called when the user left-clicks the menubar
	// icon. The menu does not auto-open on left click in this case —
	// right click (or Ctrl-left-click) opens it instead. Useful for
	// toggle-style apps (mute, pause, etc.) where the menu is the
	// secondary UI. Safe to set or clear at runtime.
	Clicked func()

	// StartAtLoginLabel overrides the default "Start at Login" menu item text
	StartAtLoginLabel string
	// QuitLabel overrides the default "Quit" menu item text
	QuitLabel string
	// contains filtered or unexported fields
}

Application represents the OSX application

func App

func App() *Application

App returns the application singleton

func (*Application) Alert

func (a *Application) Alert(alert Alert) AlertClicked

Alert shows an alert, and returns the index of the button pressed, or -1 if none

func (*Application) GracefulShutdownHandles

func (a *Application) GracefulShutdownHandles() (*sync.WaitGroup, context.Context)

GracefulShutdownHandles returns a WaitGroup and Context that can be used to manage graceful shutdown of go resources when the menuabar app is terminated. Use the WaitGroup to track your running goroutines, then shut them down when the context is Done.

func (*Application) HideStartup

func (a *Application) HideStartup()

HideStartup prevents the Start at Login menu item from being displayed

func (*Application) MenuChanged

func (a *Application) MenuChanged()

MenuChanged refreshes any open menus

func (*Application) Notification

func (a *Application) Notification(notification Notification)

Notification shows a notification to the user. Note that you have to be part of a proper application bundle for them to show up.

func (*Application) RunApplication

func (a *Application) RunApplication()

RunApplication does not return, unless MENUET_SNAPSHOT_PATH is set — in which case it writes a JSON snapshot of the current MenuState and resolved top-level Children (with submenus recursively expanded) and returns without entering the AppKit run loop. See snapshot.go for the snapshot format; the `web-preview` Makefile target is the intended driver. Also honors MENUET_SNAPSHOT_DELAY (default 2s) to give startup goroutines time to populate state.

func (*Application) SetMenuState

func (a *Application) SetMenuState(state *MenuState)

SetMenuState changes what is shown in the dropdown

type Color added in v2.4.0

type Color struct {
	R, G, B, A uint8
	Semantic   string `json:",omitempty"`
}

Color is a color for menu-item text. The zero value means "use the system default" — whatever color the menu item would normally render in (adapts to dark vs. light mode automatically).

Two ways to specify a color:

  • RGBA via R, G, B, A. Fixed pixel values. Does not adapt to appearance — usually fine for branded colors that need to match across light and dark mode.
  • Semantic: a name like "labelColor" or "systemRed" that the ObjC bridge resolves to an AppKit dynamic NSColor at draw time, so the value shifts with appearance automatically. Prefer this for hierarchy and accent colors.

When Semantic is non-empty it takes precedence over RGBA.

func (Color) IsZero added in v2.4.0

func (c Color) IsZero() bool

IsZero reports whether c is the zero value.

type FontWeight

type FontWeight float64

FontWeight represents the weight of the font

type InputType

type InputType int

InputType specifies the type of input field in an alert

const (
	InputText     InputType = iota // regular text field
	InputPassword                  // masked password field (NSSecureTextField)
)
type MenuItem interface {
	// contains filtered or unexported methods
}

MenuItem is the marker interface for items in a menu. The concrete types in this package implement it: Regular and Separator. A future Search type for in-menu search fields will implement it too.

type MenuState struct {
	Title string
	// Runs, when non-empty, overrides Title with per-segment styled text
	// — the same TextRun shape used by Regular.Runs. Useful for a status-
	// item title that mixes weights, sizes, colors, or monospaced digits.
	Runs  []TextRun
	Image string // In Resources dir or URL, should have height 22
}

MenuState represents the title and drop down,

type ModifierMask added in v2.5.0

type ModifierMask uint32

ModifierMask is a bitmask of keyboard modifier keys for a Shortcut. OR the constants together: ModCmd|ModShift.

const (
	ModCmd   ModifierMask = 1 << 8  // cmdKey
	ModShift ModifierMask = 1 << 9  // shiftKey
	ModAlt   ModifierMask = 1 << 11 // optionKey
	ModCtrl  ModifierMask = 1 << 12 // controlKey
)

Modifier flags. Match the values Carbon's RegisterEventHotKey expects.

type Notification

type Notification struct {
	// The basic text of the notification
	Title    string
	Subtitle string
	Message  string

	// These add an optional action button, configure dismiss behavior, and add an in-line reply.
	// Note: on macOS 11+, CloseButton still causes the dismiss action to trigger the
	// NotificationResponder callback, but custom button text is not supported by the
	// UserNotifications framework — the system default text is used instead.
	ActionButton        string
	CloseButton         string
	ResponsePlaceholder string

	// Duplicate identifiers do not re-display, but instead update the notification center
	Identifier string

	// If true, the notification is shown, but then deleted from the notification center
	RemoveFromNotificationCenter bool
}

Notification represents a macOS user notification.

type Regular

type Regular struct {
	Text       string
	Runs       []TextRun // when non-empty, overrides Text
	Image      string    // In Resources dir or URL, should have height 16
	FontSize   int       // Default: 14
	FontWeight FontWeight
	Color      Color // zero = system default
	Monospaced bool
	State      bool // shows checkmark when set

	// Shortcut, when non-nil, displays in the menu like a standard Apple
	// shortcut (⌘N etc.) AND registers a system-wide hotkey: pressing the
	// key combination triggers Clicked even when this app isn't frontmost.
	// Identical Shortcuts across multiple Regular items: only the first
	// wins; subsequent registrations are silently ignored.
	Shortcut *Shortcut

	// Subtitle, when non-empty, renders as a dimmer second line below the
	// main title — Apple's native NSMenuItem.subtitle on macOS 14+. The
	// runs' per-segment colors aren't honored on this path (NSMenuItem.subtitle
	// is a plain string property); only the concatenated text appears in
	// the system's standard subtitle styling.
	Subtitle []TextRun

	Clicked  func()
	Children func() []MenuItem
}

Regular is a standard menu row. Set Text and optionally Clicked (callback when activated) and Children (returns a submenu).

For mixed styling within a single row — e.g. "Status: FAILED" where FAILED is red and bold — set Runs to a slice of TextRun. Runs takes precedence over Text when non-empty.

type Search struct {
	Placeholder string
	Results     func(query string) []MenuItem
}

Search is an in-menu search field. The Results callback fires on every keystroke with the current query (empty string when the menu first opens). Returned items appear immediately below the search field and replace whatever was there before. The last query is remembered across menu opens.

Press Enter to activate the top result, Esc to dismiss, or click any result. Arrow keys do not navigate result items — NSMenu's tracking loop owns them at a layer outside this library's reach.

Apps that use Search cannot be distributed via the Mac App Store — the implementation uses a private NSPopupMenuWindow selector (setKeyOverride:) to engage the text field's input context during menu tracking. Stable on current macOS but private-API-rejection material for the App Store. Direct-distribution apps are unaffected.

type Separator

type Separator struct{}

Separator is a horizontal divider between menu rows.

type Shadow added in v2.8.0

type Shadow struct {
	Color   Color
	Blur    float64 // blur radius in points; 0 = sharp
	OffsetX float64 // horizontal offset in points
	OffsetY float64 // vertical offset in points (AppKit: positive = up)
}

Shadow is a drop-shadow or glow rendered behind a TextRun. Set Blur alone (with the default zero offset) for a glow effect; set OffsetX and OffsetY for a directional drop shadow. Color of zero defaults to translucent black at draw time.

type Shortcut added in v2.5.0

type Shortcut struct {
	KeyCode   uint16
	Modifiers ModifierMask
}

Shortcut is a global keyboard shortcut. When set on a Regular menu item, the shortcut both:

  • displays in the menu like a standard Apple shortcut (⌘N etc.), and
  • fires the item's Clicked callback when pressed system-wide, even when this app isn't frontmost

Identical (KeyCode, Modifiers) tuples across multiple menu items are not allowed — only the first registered wins; subsequent ones are silently ignored. Pick distinct shortcuts per action.

func (Shortcut) IsZero added in v2.5.0

func (s Shortcut) IsZero() bool

IsZero reports whether s is the zero value (no key and no modifiers).

type Snapshot added in v2.10.0

type Snapshot struct {
	Schema string         `json:"schema"`
	State  *MenuState     `json:"state,omitempty"`
	Items  []SnapshotItem `json:"items"`
}

Snapshot is a JSON-serializable picture of an App's current menu — MenuState plus the resolved top-level items, with submenus recursively expanded. It's produced by `MENUET_SNAPSHOT_PATH=… ./app` and consumed by the HTML renderer to draw a faithful mockup outside AppKit (for websites, README screenshots, regression tests).

The snapshot is pure data — there is no code path back to the app from a snapshot file, so rendering one is safe even if the file came from a third party. The renderer's job is to honor the data and reject any shape outside the schema.

type SnapshotItem added in v2.10.0

type SnapshotItem struct {
	// Type is "regular", "separator", or "search". Empty means regular for
	// backward-compatibility when the field is omitted.
	Type string `json:"type,omitempty"`

	Text       string     `json:"text,omitempty"`
	Runs       []TextRun  `json:"runs,omitempty"`
	Subtitle   []TextRun  `json:"subtitle,omitempty"`
	Image      string     `json:"image,omitempty"`
	FontSize   int        `json:"fontSize,omitempty"`
	FontWeight FontWeight `json:"fontWeight,omitempty"`
	Color      Color      `json:"color,omitempty"`
	Monospaced bool       `json:"monospaced,omitempty"`
	Shortcut   *Shortcut  `json:"shortcut,omitempty"`
	State      bool       `json:"state,omitempty"`

	// Children are the expanded submenu, populated by the snapshotter.
	Children []SnapshotItem `json:"children,omitempty"`
}

SnapshotItem is the snapshot-only mirror of MenuItem. It is intentionally separate from internalItem (which carries cgo bookkeeping fields like Unique/ParentUnique and a live *MenuItem pointer) so the snapshot stays a self-contained data payload with no live references.

type TextRun added in v2.4.0

type TextRun struct {
	Text       string
	Color      Color      // zero = system default
	FontSize   int        // 0 = inherit from item
	FontWeight FontWeight // 0 = default
	Monospaced bool       // true = system monospace font
	Badge      bool       // true = render as rounded-pill badge

	// Underline draws a single underline. By default it uses the run's
	// foreground Color; set UnderlineColor for an independent color.
	Underline      bool
	UnderlineColor Color // zero = follow foreground

	// Strikethrough draws a single strike. By default it uses the run's
	// foreground Color; set StrikethroughColor for an independent color.
	Strikethrough      bool
	StrikethroughColor Color // zero = follow foreground

	Background Color   // zero = none; non-zero = colored highlight behind text
	Shadow     *Shadow // nil = no shadow; set for a drop-shadow or glow
}

TextRun is one segment of a styled menu-item title. Multiple runs are concatenated to form the full title with per-segment styling — see Regular.Runs. The zero value of each style field means "inherit" so a run can change just one attribute (e.g. only Color) without disturbing the others.

When Badge is true the run renders as a filled, rounded pill rather than text: Color becomes the fill color and Text appears in white-on- fill at small size. Useful for "LIVE" / "NEW" pills next to a row.

Underline, Strikethrough, Background, and Shadow are common emphasis attributes — useful for marking winners/losers, "marker-pen" style highlights, and trophy-glow celebrations.

type UserDefaults

type UserDefaults struct {
	// contains filtered or unexported fields
}

UserDefaults represents values stored in NSUserDefaults

func Defaults

func Defaults() *UserDefaults

Defaults returns the userDefaults singleton

func (*UserDefaults) Boolean

func (u *UserDefaults) Boolean(key string) bool

Boolean gets a boolean default, 0 if not set

func (*UserDefaults) Integer

func (u *UserDefaults) Integer(key string) int

Integer gets an integer default, 0 if not set

func (*UserDefaults) Marshal

func (u *UserDefaults) Marshal(key string, v interface{}) error

Marshal marshals an object into JSON and stores it in user defaults, see json.Marshal docs

func (*UserDefaults) SetBoolean

func (u *UserDefaults) SetBoolean(key string, value bool)

SetBoolean sets a boolean default

func (*UserDefaults) SetInteger

func (u *UserDefaults) SetInteger(key string, value int)

SetInteger sets an integer default

func (*UserDefaults) SetString

func (u *UserDefaults) SetString(key, value string)

SetString sets a string default

func (*UserDefaults) String

func (u *UserDefaults) String(key string) string

String gets a string default, "" if not set

func (*UserDefaults) Unmarshal

func (u *UserDefaults) Unmarshal(key string, v interface{}) error

Unmarshal unmarshals an object from JSON that was stored in user defaults, see json.Unmarshal docs

Directories

Path Synopsis
cmd
catalog command
helloworld command
slowquit command
SlowQuit is an example application that demonstrates how to use the graceful shutdown handles of a menuet app.
SlowQuit is an example application that demonstrates how to use the graceful shutdown handles of a menuet app.
weather command
Package html renders a menuet.Snapshot to a self-contained HTML fragment that approximates how the menu looks in the macOS menu bar.
Package html renders a menuet.Snapshot to a self-contained HTML fragment that approximates how the menu looks in the macOS menu bar.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL