Controlling Bunch With Frontmatter
- YAML-ish
- Available Keys
- Tagging
- Arbitrary Keys as Default Variable Values
- Flexible Frontmatter
- Applying Frontmatter to Multiple Bunches
You can use frontmatter at the top of your Bunch to do things like change the menu display title or schedule launches on a repeating basis.
Frontmatter goes at the very top of a Bunch, and is fenced off by three dashes (minus symbol) at the top and bottom. Within the fence is a series of keys and values separated by colons.
Example frontmatter:
---
title: 📞Comms
open at: 8am
startup: true
---
Bunch treats keys the way MultiMarkdown does: it allows spaces and is case insensitive, but they’re compressed and lowercased when read in, so Open at
becomes openat
. Feel free to make your keys as readable or as efficient as you like. Unrecognized keys have no effect and are saved as arbitrary variables available to snippets.
YAML-ish
Bunch frontmatter resembles YAML, but it’s a simplified system. Spaces are allowed in key names, but they are removed when the variable is read in. All keys are lowercased. If your line is First Name: Brett
, the variable would be accessed using ${firstname}
. However, spaces are also removed from variable placeholders when looking up the value, so ${first name}
will also match.
Bunch frontmatter doesn’t support the more complex structures of YAML, such as arrays and blocks. It’s simply single-line keys and string values.
Available Keys
Here are the available keys:
Key | Effect |
---|---|
close after | Automatically close after an interval (e.g. 1h ) |
close at | Set a time to close daily (e.g. 5pm ) |
close on
| Set a day and time to close weekly (e.g. Mon 5pm )
Multiple day/times can be combined with commas |
from file | A file path to load additional key/value pairs |
from script | A shell script path that returns YAML |
ignore | Boolean determines menu display of a Bunch |
ignore if | Logic to determine if Bunch displays in menu |
ignore unless | Negative version of ignore if
|
ignores state |
true allows open/close when already open/closed |
menu divider | Add a menu divider before or after
|
menu order | Menu order, 0 –99 at beginning, >100 at end |
only opens |
true has the same effect as toggles: false
|
open at | Set a time to open this bunch daily (e.g. 6:30am ) |
open every | Repeat open at intervals (e.g. 30m ) |
open on
| Set a day and time to open weekly (e.g. Tue 8am )
Multiple day/times can be combined with commas |
quits apps |
always close apps open in other Bunches |
quit browsers |
true quit browsers from browser:url on close |
run after | Comma-separated paths to scripts to run after opening |
run after close | Comma-separated paths to scripts to run after closing |
run before | Comma-separated paths to scripts to run before opening |
run before close | Comma-separated paths to scripts to run before closing |
schedule if | Only schedule on specific Mac(s) (UUID1) |
schedule unless | Negative version of schedule if
|
sequence |
parallel or sequential , determines execution order |
shortcut | Keyboard shortcut for opening the Bunch (e.g. @a ) |
single bunch mode |
ignore prevents closing |
startup
|
true , false , or ask to open on launch
Can also be a UUID1 run only on a specific Mac |
tags | Add tags to the Bunch for organization and batching |
title | Set the menu display title. Emojis OK. |
title prefix
| Set a prefix that goes before the menu title.
Prefixes set by folder/tag frontmatter are combined |
toggles |
false prevents toggling this Bunch open/closed |
Bunch Scheduling
See Scheduling Bunches for details on the open ...
and close ...
keys.
Bunch Behavior
To learn more about sequential and parallel execution with the sequence
key, see Execution Sequence.
The startup
key can be set to true
, false
, or ask
. (or yes
/no
/?
) and has the same effect as adding a bunch name to a *.startup
script. If set to ask
, a confirmation dialog will pop up before opening that Bunch.
Setting single bunch mode: ignore
will prevent the Bunch from affecting or being affected by the launch of other Bunches in Single Bunch Mode. This allows you to use Single Bunch Mode to quit one Bunch when opening the next, but have Bunches that can be always-on.
Setting toggles: false
will only allow the Bunch to open, but will never register it as Open, meaning it can’t be closed. It won’t get a check mark in the menu, it won’t be remembered as an open Bunch if “Preserve Open Bunches” is on, and other Bunches can’t close it. Apps it has open won’t be registered by other Bunches, and it won’t prevent other Bunches from quitting them. (If you like it better semantically, you can also use only opens: true
.)
Setting ignores state: true
will allow Bunches to re-open or re-close when scheduled. Normally if a schedule is set for a Bunch to open or close and it already is, that schedule is ignored. With this option set you can have the Bunch re-launch or re-close, restoring the launched or quit apps and running scripts on a schedule. This setting functions independently of toggles/only opens
.
Setting quit apps: always
will force the Bunch to quit apps it opened regardless of whether the app is open in another Bunch. This correlates with the “Quit Apps Open in Other Bunches” global preference, but allows enabling this on a per-Bunch basis.
Setting a Shortcut Key
Bunch has full keyboard control. Assign a Hotkey key in preferences to open the Bunch menu, and then use keyboard shortcuts to open your Bunches. By default Bunches are given numbers as shortcuts, from 1 to 9 and then 0, assigned in menu display order. Subsequent items are assigned Command-Number, from ⌘1 to ⌘0. You can customize these using the shortcut:
frontmatter key.
A shortcut can be anything other than a number, and can include modifiers (control, option, shift, command). A capital letter automatically implies the Shift modifier.
Modifiers are not required and single keystrokes work great for launching Bunches. Just assign
shortcut: a
, then hit the hotkey to open the menu and type “a” to open the Bunch.;
Modifiers can be specified using symbols:
@ | Command (⌘) |
$ | Shift (⇧) |
~ | Option (⌥) |
^ | Control (⌃) |
To use any of these symbols as the actual modifier key, use the Shift-equivalent, i.e. $
would be $6
(Shift-6), and @
would be $2
.
To set a menu shortcut of Command-T for your Bunch, you would include this in the frontmatter:
---
shortcut: @t
---
Shortcuts can also use words in the format option-command-t
.
If you duplicate a shortcut between two Bunches, only the first one (in menu order) will get the shortcut. The conflicting Bunch will fall back to the auto-assigned numeric shortcuts.
Customizing Menu Display Title
The “title” key changes the display title of the Bunch, as seen in the menu. When calling the Bunch from any other Bunch, you’ll still use the filename.
You can totally use emoji in the frontmatter title and get a sweet looking menu 😁. The menus are sorted alphabetically by display title, so changing the title:
key will change the sort order of the list. Sort order ignores emoji, so 😊Bunch A
still comes before ♥️Bunch B
.
You can also use the “title prefix” key to add a prefix to the display title. If no “title” key is set, this will prefix the filename in the menu title.
The “title prefix” key can be set in tag or folder frontmatter and will apply to all files affected. If multiple tag/folder prefixes apply to a single Bunch, the prefixes are concatenated. The order of concatenation can’t be controlled, but this allows you to use tags to actually “tag” menu items visually.
Customizing Menu Order
The “menu order” key defines the sort order of Bunches in the menu that Bunch displays.
Any Bunch with a “menu order” number between 1 and 99 will be sorted by number at the beginning of the menu. If multiple Bunches have the same number, they will be sorted alphanumerically (by display title) within that position.
Bunches without a menu order value will be sorted alphabetically after the numbered bunches.
Bunches with a menu order greater than 99 will be sorted by number and appended to the end of the list. Adding menu order: 100
to a Bunch will force it the the end of the list. Duplicate numbers are sorted alphanumerically.
Tip: If you increment your menu order numbers by 5 or 10 when first starting out, you’ll have room to stick new ones in or move them around without having to re-order everything. E.g. put your top menu item at 10, second one at 20. Then if in the future you want something else at the top of the list or between those two, you can just put it at position 5 or 15 and still have room to fit 4 more in either direction before you have to go through and renumber everything.
Adding Menu Dividers
You can use the menu divider
key to add separators to the Bunch menu. The key can be set to before
or after
, determining whether the inserted divider comes before or after the Bunch in the menu.
---
title: Just A Bunch
menu order: 10
menu divider: after
---
Hiding From the Menu
You can use the keys “ignore,” “ignore if,” and “ignore unless” to prevent a Bunch from displaying in the menu.
The “ignore” key is a boolean, either “true” or “false.” If it’s true, the Bunch will not show up in the menu (nor will it be able to be triggered by shortcut key, it’s ignored).
“ignore if” and “ignore unless” take a UUID or a condition.
- A UUID can be a single UUID or a comma-separated list of UUIDs. If the current machine’s UUID matches a UUID in the list, it will either be ignored (ignore if) or displayed (ignore unless).
- If the value of “ignore if” or “ignore unless” does not match a UUID format, it’s parsed as a condition. Any of the conditional logic tests can be used. (Because this comparison is running at the time the Bunch is loaded and not when it’s being executed, some conditions may evaluate differently than they would at runtime.)
A UUID is a unique identifier for each Mac. You can get the UUID for the current machine by opening Preferences and pressing “Copy UUID.” The value of startup if:
can be a single UUID, or multiple UUIDs separated by commas.
Before/After Scripts
You can use frontmatter to run scripts before and after opening and closing the Bunch. Because variables are defined before any script items are run within the Bunch, it may be useful to have some scripts run prior to processing variables that may read from that script’s output. For example, if you needed to pull some JSON data from an API and populate a text file that could then be read into a query dialog, you would want to run the script that populates the text file before the variable assignment that reads it. See this advanced script for an example.
To run scripts before doing anything else in a Bunch, use the run before:
key. The value should be a comma-separated list of shell or AppleScripts to execute. Each script can include arguments. If you don’t provide an absolute path to the script (tildes can be used for your home directory), Bunch will assume the script is relative to your Bunch Folder.
To run scripts after processing all of the items in a Bunch, use run after:
.
To run scripts when closing a Bunch but before processing any on-close items, use run before close:
.
To run scripts after performing all on-close operations and terminating any other running scripts, use run after close:
.
---
title: Script Example
run before: prescript.sh arg1, otherscript.sh arg2
run after close: postscript.sh
---
Tagging
You can add tags to a Bunch using the tags:
key:
---
title: My Tagged Bunch
tags: tag1, tag2
---
These tags can be used for organizing Bunches into groups, allowing you to control multiple Bunches at once from the Bunch menu.
You can also open and close tagged groups of Bunches from within a Bunch using \tagname
. See the documentation here.
Arbitrary Keys as Default Variable Values
You can add arbitrary key/value pairs in the frontmatter. These will be stored and passed as default values to Bunches, snippets, and scripts. For example, if your snippet had a variable ${say}
in it, and the calling Bunch had a say:
line in the frontmatter, that value would be passed unless specifically passed as a variable to the snippet.
---
say: anything
---
# Gets say=anything as variable
<speech.snippet
# Gets say=something as variable
<speech.snippet
- say=something
The order of precedence for snippet variables is: variable defined after the snippet line, then value found for matching key in the frontmatter, then anydefault value defined in the snippet.
These variables are available in Snippets and as environment variables in shell scripts. They are not passed to Automator Workflows because those will error out if given unexpected variables.
Flexible Frontmatter
In addition to hard coding frontmatter keys and values, you can set them on the fly using several flexible options.
Interactively Setting Variable Values
You can use dialogs to set values for frontmatter keys interactively when the Bunch opens. See Interactivity->Variables for details.
Setting Variables On Open
When calling a Bunch from another Bunch or via the URL handler, you can pass key/value pairs to override hard coded (or missing) frontmatter keys for use in ${variables}
.
If using ${variables}
in a Bunch, be sure to include default values (${variable:default value}
) or hard coded frontmatter values for the keys for when the Bunch is called directly from the menu and can’t have values passed to it.
Dynamic Frontmatter
You can use from file
and from script
to load in variables from external sources.
A frontmatter line such as from file: filename.txt
would read in additional values from filename.txt
. Paths are assumed to be relative to the configured Bunch folder unless they’re absolute paths.
Remember that relative paths in Bunches within subfolders are still relative to the Bunch Folder, so if you want to reference
filename.txt
located in a subfolder, you should usefrom file: Subfolder/filename.txt
, even if the Bunch itself is already inSubfolder
.
The contents of filename.txt
should be only colon-separated key-value pairs. This allows external automation to write data to files that affect your Bunch without having to modify the Bunch itself. A file called in this manner will be watched for updates, and the Bunch will be automatically updated if the file changes.
You can also run a shell script, which should also return just key: value
pairs. Most scripting languages have a YAML library that makes it pretty easy to easily output data in a suitable format. Lines with YAML separators (---
) will be ignored.
When one of these keys is detected, the file or script results will be merged with the other keys, if any, overwriting values for existing keys.
Frontmatter is only updated when a Bunch is opened, when a change is made to the Bunch file itself, or when an imported file is changed. Changing a referenced script will not trigger an update, but the new data will be parsed before any additional snippets or scripts are opened.
You can also incorporate dialogs in a frontmatter script. See advanced scripting for an example.
A Ridiculous Example
Just to demonstrate the capability of dynamic frontmatter, you could have a line in your frontmatter that reads additional data in from a script called frontmatter.rb
:
---
from script: frontmatter.rb
---
The file frontmatter.rb
would exist in your Bunch folder, so no path would be needed. It would do something like:
#!/usr/bin/env ruby
require 'yaml'
require 'time'
t = Time.now
names = %w(Jim Sara Jake Sue John Sally Joe)
data = {
'name' => names[rand(names.length)],
'date' => t.strftime('%c') }
puts YAML.dump(data)
The above script picks a random name and adds the current date. Now when the Bunch is read in, the frontmatter it actually stores looks like:
---
fromscript: frontmatter.rb
name: Sara
date: Sat Jan 30 15:09:31 2021
---
Which means that in any snippets that are called within the Bunch, you can reference the name
and date
variables. Let’s load a snippet called test.snippets
that contains:
---[Welcome]
* say "Hi ${name}, today is ${date}"
Reference that snippet in the Bunch but don’t define either of the variables:
<test.snippets#Welcome
When it runs, the name variable will be replaced with a random name, and the current date will be passed. It will update every time the Bunch opens. This is obviously stupid, as you could have just written a script that said this without populating variables and importing snippets, but it illustrates how dynamic frontmatter can work.
See Advanced Scripting for more crazy ideas.
Applying Frontmatter to Multiple Bunches
You can apply frontmatter to multiple Bunches using folder and tag frontmatter.
folder.frontmatter
A file called folder.frontmatter
can be included in the Bunch folder or any subfolder. This file is primarily designed for use in subfolders to control submenu display, but it can also contain keys that affect all Bunches in the folder (including the base Bunch Folder).
A folder.frontmatter
file is just a text file containing frontmatter keys and values. The keys title
, menu divider
, and ignore
only affect the submenu item, and the keys shortcut
and tags
are always ignored. All other keys are applied to every Bunch in the folder.
This means that you can create a subfolder for Bunches you want to launch at a specific time, and then any Bunch you put in that folder will get an open at
schedule applied.
If I have a folder called “Morning” and it contains three Bunches that I want launched at 8am, I could add a folder.frontmatter
file to the folder and have the open at
it contains applied to all three Bunches.
---
title: Good Morning
open at: 8am
single bunch mode: ignore
---
Note that it’s a good idea to ignore Single Bunch Mode if you’re going to be batch opening multiple Bunches on a schedule. If Single Bunch Mode is enabled, each one that opens will immediately close the one before it.
This allows for some batch manipulation of Bunches, and means that if you have a key you want applied to multiple Bunches, you don’t have to edit them all individually. It does mean that those Bunches have to appear in a submenu, but then modifying which Bunches have the key is as simple as moving a Bunch in or out of that folder, and changing the key for every Bunch is just a matter of editing one file.
This can also be used to apply arbitrary keys to groups of Bunches. An entire folder of Bunches can have a variable assigned, and then modifying that variable for all Bunches can be done just by editing folder.frontmatter
.
Assign an arbitrary key, in this case a default browser:
---
default browser: Safari
---
Then any Bunch in the folder (and any Snippets they contain) can reference that variable.
${defaultbrowser}
- https://example.com
Now if I want to change the browser for any of the Bunches in that folder using that variable, I just edit default browser
in folder.frontmatter
.
folder.frontmatter
files can exist in the root of your Bunch folder as well. Submenu keys likeignore
andtitle
are ignored, but arbitrary keys assigned in that file will apply to all Bunches in the root folder.
@tag.frontmatter
Another way to apply frontmatter to multiple Bunches is to use tag frontmatter. If a file beginning with @tagname.frontmatter
exists, its frontmatter will be applied to all Bunches tagged with tagname
.
Some keys such as “title prefix” and “ignore if/unless” are concatenated when they appear in multiple frontmatter. This allows you to assign an “icon” to each tag (with “title prefix”), and have menu items labelled with those icons, or to use tags to ignore (“ignore if/unless”) sets of Bunches on different machines.