Skip to content

Proposal: auto-generate autosaveName for hs.menubar items #3864

@johntrandall

Description

@johntrandall

Problem

hs.menubar.new() creates NSStatusItems without setting autosaveName unless the caller explicitly passes one. This means macOS cannot persist the item's user-chosen menubar position across destroy/recreate cycles.

This causes problems with third-party menubar managers — when Hammerspoon reloads its config, all menubar items are destroyed and recreated as brand-new NSStatusItems. Menubar managers lose track of them and misclassify them:

  • Ice — verified: items fall into "always hidden" section on every reload
  • Bartender — reported by Hammerspoon maintainer cmsj in macOS Catalina 10.15 - hs.menubar ordering out of whack #2197: "wonky" behavior with dynamic icons
  • Hidden Bar, Vanilla — likely affected by the same root cause (not directly verified, but they use similar position-based tracking)

The autosaveName parameter was added to hs.menubar.new() (thank you!), but since it's opt-in, existing Spoons and user scripts don't benefit unless they're updated individually. I've submitted Hammerspoon/Spoons#362 to fix all 14 affected Spoons in the official repo, but this doesn't help user scripts or third-party Spoons.

Proposal

Consider auto-generating an autosaveName when none is provided. Possible approaches:

Option A: Derive from calling source (preferred)

Use debug.getinfo(2, "S").source in the hs.menubar.new() Lua wrapper to derive a stable name from the calling module:

-- Pseudocode for the wrapper
function hs.menubar.new(inMenuBar, autosaveName)
    if autosaveName == nil then
        local info = debug.getinfo(2, "S")
        -- e.g., "@/Users/foo/.hammerspoon/Spoons/Caffeine.spoon/init.lua"
        -- Extract: "Caffeine.spoon" or hash the full path
        autosaveName = deriveNameFromSource(info.source)
    end
    return _core_menubar_new(inMenuBar, autosaveName)
end

For Spoons, this would naturally produce names like "Caffeine.spoon". For user scripts, it would use the filename. A counter suffix handles multiple items from the same source file.

Pros: Fixes all existing code retroactively. Stable across reloads (same source = same name).
Cons: Somewhat magical. Counter-based disambiguation is fragile if creation order changes.

Option B: Log a warning when autosaveName is nil

Less invasive — just emit a console warning encouraging authors to set it:

hs.menubar: no autosaveName set. Menubar managers may lose track of this item on reload.
Consider: hs.menubar.new(true, "MyItemName")

Pros: Non-breaking, educational.
Cons: Doesn't fix anything automatically.

Option C: Add it to the Spoon infrastructure

If hs.loadSpoon() / hs.spoons.use() set a context variable, hs.menubar.new() could auto-name items created within a Spoon context:

-- hs.menubar.new() checks:
if autosaveName == nil and hs.spoons._currentLoadingSpoon then
    autosaveName = "Hammerspoon." .. hs.spoons._currentLoadingSpoon
end

Pros: Clean for Spoons, no magic for user scripts.
Cons: Only helps Spoons, not standalone scripts. Doesn't help items created after init (e.g., TimeMachineProgress creates its item dynamically on backup start).

Context

  • NSStatusItem.autosaveName has been available since macOS 10.12
  • Every well-behaved native macOS app sets it
  • The menubar manager ecosystem is large and growing (Ice alone has 15k+ GitHub stars)
  • This was discussed previously in the context of menubar ordering issues, where @asmagill noted the property and added support for it

Related

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions