Applescript run from menu bar? I am aware of the Script Editor menu. That is not what I'm looking for.

Is there a way to run an Applescript from the menu bar? I can save it as a file and run it from the Script Editor menu, and I can save it as an application and stick it in my dock. Is there a file format that allows it to run from the menu bar, as many applications do? Or a way to edit the code that does the same thing?
Cocoa-AppleScript as a script or AppleScript application created in Script Editor supports Menu Bar app. This script runs on macOS 10.12 (maybe runs on 10.10.x, 10.11.x). This script can be saved as application.

--AppleScript: menu bar script -- Created 2017-03-03 by Takaaki Naganoya
--2017 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"
--http://piyocast.com/as/archives/4502

property aStatusItem : missing value

on run
    init() of me
end run

on init()
    set aList to {"Piyomaru", "Software", "", "Takaaki", "Naganoya", "", "Quit"}
    set aStatusItem to current application's NSStatusBar's systemStatusBar()'s statusItemWithLength:(current application's NSVariableStatusItemLength)

    aStatusItem's setTitle:""
    aStatusItem's setHighlightMode:true
    aStatusItem's setMenu:(createMenu(aList) of me)
end init

on createMenu(aList)
    set aMenu to current application's NSMenu's alloc()'s init()
    set aCount to 1
    repeat with i in aList
        set j to contents of i
        if j is not equal to "" then
            set aMenuItem to (current application's NSMenuItem's alloc()'s initWithTitle:j action:"actionHandler:" keyEquivalent:"")
        else
            set aMenuItem to (current application's NSMenuItem's separatorItem())
        end if
        (aMenuItem's setTarget:me)
        (aMenuItem's setTag:aCount)
        (aMenu's addItem:aMenuItem)
        if j is not equal to "" then
            set aCount to aCount + 1
        end if
    end repeat

    return aMenu
end createMenu

on actionHandler:sender
    set aTag to tag of sender as integer
    set aTitle to title of sender as string

    if aTitle is not equal to "Quit" then
        display dialog aTag as string
    else
        current application's NSStatusBar's systemStatusBar()'s removeStatusItem:aStatusItem
    end if
end actionHandler:




I previously edited the original code, posted by Piyomaru, just to the point it would compile and wanted to add some additional information to make the example code more useful for anyone relatively new to AppleScript.

Presently in the example code, if saved as an application it has no code to quit the application, it only removes the menu bar object. So, in the actionHandler handler of the script, add the following to the else branch of the if statement, after the existing code:

        if (name of current application) is not "Script Editor" then
            tell current application to quit
        end if


It's coded so if/when run from within Script Editor it will not attempt to close Script Editor. You can use it without the if statement block and use just tell current application to quit if you're not going to run it from within Script Editor.

Because this script is using Cocoa-AppleScript, once run from within Script Editor, it must be compiled again just before saving it in order to flush the C and Objective-C pointers created by the Cocoa-Applescript code, since pointers cannot be saved in the script. Just click the hammer icon on the Toolbar, or: Script Editor > Script > Compile   ⌘K

Additionally, when saved as an application you must check the Stay open after run handler check-box when saving it, and by default the app's Dock Tile will show in the Dock when running. As this might be unwanted behavior for a menu bar app, use the following command in Terminal to add the necessary key to the app's Info.plist file so it will not show in the Dock.

defaults write /Applications/name_of_app.app/Contents/Info.plist LSUIElement -bool yes



Replace name_of_app with the actual name you saved it as, and if necessary change the path if not in /Applications.
Note that if you drag the app to the Dock, its Dock Tile will still show on the Dock, just won't have a open indicator under it when open. This added key to the Info.plist file is to keep the Dock Tile from showing on the Dock when running, when the app has not been dragged to the Dock in the first place.


In the actionHandler handler of the original example code, it just displays a dialog box with the menu item number that was clicked, which certainly fine for example code, however, I'd like to show a different example how to have separate code execute based on which menu item was clicked. The following example code could replace the entire actionHandler handler in the original script:

on actionHandler:sender
    set aTitle to title of sender as string
    if aTitle is equal to "Quit" then
        current application's NSStatusBar's systemStatusBar()'s removeStatusItem:aStatusItem
        if (name of current application) is not "Script Editor" then
            tell current application to quit
        end if
    else if aTitle is equal to "Piyomaru" then

        --  # Your code to run for this menu choice goes here:

    else if aTitle is equal to "Software" then

        --  # Your code to run for this menu choice goes here:

    else if aTitle is equal to "Takaaki" then

        --  # Your code to run for this menu choice goes here:

    else if aTitle is equal to "Naganoya" then

        --  # Your code to run for this menu choice goes here:

    end if
end actionHandler:


Now this could be written differently, i.e. "Quit" as an else or else if at the bottom, but I did it this way because I want all other menu commands to be an else if choice, even though logically any other given menu command is more likely to be clicked then "Quit". For me, I think it's easier to add/edit/remove the menu command's code when written this way. It's just a personal preference, so do it as you feel what's right for you.

A discussion note from the AppleScript Language Guide:


  An if statement can contain any number of else if clauses; AppleScript looks for the first Boolean expression contained in an if or else if clause that is true, executes the statements contained in its block (the statements between one else if and the following else if or else clause), and then exits the if statement.
  
  An if statement can also include a final else clause. The statements in its block are executed if no other test in the if statement passes.


For the most part of the original example code, you should only have to modify two line of code in the init handler:

set aList to {"Piyomaru", "Software", "", "Takaaki", "Naganoya", "", "Quit"}



This sets the name of the menu commands in their order, and "" is for a separator.


For a different icon:

aStatusItem's setTitle:""



Change the "" to something else you prefer, from: Script Editor > Edit > Emoji &amp; Symbols


Hopefully it's obvious that the if statement block in the actionHandler handler will correspond to the aList in the init handler, so match the names in the aTitle is equal to  in the actionHandler handler to the names in the aList in the init handler.

The rest of the code should not need to be modified, although it certainly can if need be or wanted.