Windows, widget factories, toolbar items, song data, and preferences β everything you need to build plugin UI.
plugin.on(event: string, handler: function) β number
Subscribes to a named event. Returns a subscription ID you can pass to plugin.off to remove the subscription.
Most plugins subscribe in on_init and never need to unsubscribe.
local id = plugin.on("note", function(e)
-- e is a table of event fields
end) plugin.off(event: string, id: number)
Removes the subscription with the given ID from the named event. No-op if the ID does not exist.
Plugins can open any number of floating windows. Windows are automatically closed when the plugin shuts down.
plugin.new_window(title: string) β window
Creates a new floating window with the given title. The window is not visible until you call plugin.window_show.
Returns an opaque window handle used by the other window functions.
local win = plugin.new_window("My Plugin")
plugin.window_set_content(win, my_widget)
plugin.window_show(win) plugin.window_set_content(win: window, widget: widget)
Replaces the entire content of the window with the given widget. Can be called any number of times to swap content dynamically.
plugin.window_show(win: window)
Makes the window visible. On first call, this creates the underlying OS window. Safe to call multiple times β subsequent calls bring the window to the front.
plugin.window_hide(win: window)
Hides the window without destroying it. The window can be shown again later via plugin.window_show.
plugin.window_close(win: window)
Destroys the window. After calling this, the window handle is invalid and must not be used again.
plugin.window_on_closed(win: window, fn: function)
Registers a callback invoked when the user closes the window via the OS close button. Use this to track visibility state so your toolbar action can reopen the window.
local visible = true
plugin.window_on_closed(win, function()
visible = false
end)
plugin.add_toolbar_item("My Plugin", function()
visible = true
plugin.window_show(win)
end)
Widget factory functions return opaque widget handles. Pass them to plugin.window_set_content
or to a container like plugin.new_vbox.
plugin.new_label(text: string) β widget
Creates a read-only text label. Supports multi-line text β use \n to insert line breaks.
Update the text at any time with plugin.set_label_text.
local lbl = plugin.new_label("Hello, world!")
-- Later, update it:
plugin.set_label_text(lbl, "Updated text") plugin.set_label_text(label: widget, text: string)
Updates the text of a widget previously created by plugin.new_label.
The widget refreshes immediately. Calling this with a non-label widget is a no-op.
plugin.new_button(label: string, action: function) β widget
Creates a clickable button. action is called with no arguments when the button is tapped.
local btn = plugin.new_button("Reset", function()
plugin.set_label_text(my_label, "β")
end) plugin.new_entry(placeholder: string [, onChange: function]) β widget
Creates a single-line text input. onChange is optional β if provided it is called with the current string value every time the user types.
local entry = plugin.new_entry("Searchβ¦", function(text)
plugin.set_label_text(result_label, "Searching: " .. text)
end) plugin.new_vbox(widget1, widget2, β¦) β widget
Stacks any number of widgets vertically with equal spacing. The result is itself a widget and can be nested inside other containers or set as a window's content.
local layout = plugin.new_vbox(
plugin.new_label("Track"),
track_entry,
plugin.new_button("Apply", on_apply)
)
plugin.window_set_content(win, layout) plugin.new_hbox(widget1, widget2, β¦) β widget
Lays out any number of widgets horizontally side by side with equal spacing. Like new_vbox, the result is itself a widget that can be nested inside other containers.
local btn_row = plugin.new_hbox(
plugin.new_button("β Prev", on_prev),
plugin.new_button("βΆ Play", on_play),
plugin.new_button("Next βΆ", on_next)
)
local layout = plugin.new_vbox(content_label, btn_row)
plugin.window_set_content(win, layout) plugin.new_select(options: table, onChange: function) β widget
Creates a dropdown select widget. options is a Lua array of strings
(e.g. G).
The onChange callback receives the selected string whenever the user changes the selection.
The first option is selected by default.
local current_key = "Am"
local key_select = plugin.new_select(C, function(val)
current_key = val
render() -- update display for new key
end)
local key_row = plugin.new_hbox(
plugin.new_label("Key:"),
key_select
) plugin.new_slider(min: number, max: number, step: number, initial: number [, onChange: function]) β widget
Creates a slider for numeric input. The onChange callback is optional and receives the current numeric value whenever the user moves the slider.
local bpm_label = plugin.new_label("Manual BPM: 120")
local bpm_slider = plugin.new_slider(40, 240, 1, 120, function(value)
plugin.set_label_text(
bpm_label,
"Manual BPM: " .. tostring(math.floor(value + 0.5))
)
end)
local controls = plugin.new_vbox(bpm_label, bpm_slider) plugin.new_mono_label(text: string) β widget
Creates a label rendered in a monospace font with word-wrap disabled. Ideal for displaying structured text such as guitar tab notation, ASCII diagrams, or code output where column alignment matters.
Update the text with plugin.set_label_text.
local tab_display = plugin.new_mono_label("")
-- Later, update with formatted tab notation:
plugin.set_label_text(tab_display, [[
e|--5--8--|
B|--5--8--|
G|--5--7--|
D|--5--7--|
A|--5--7--|
E|--5--8--|]]) plugin.new_neck_diagram(toml: string) β widget, err
Creates a guitar fretboard diagram widget (~200Γ175 dp) that can be placed in any window layout alongside other controls.
The diagram shows 6 strings and up to 5 frets. Its content is described by a TOML string with a
starting_fret field, an optional
muted array, and one
[[dot]] table per marker.
Returns the widget on success, or nil, error_string on parse failure.
Top-level TOML fields:
| Field | Type | Description |
|---|---|---|
| starting_fret | int | First fret shown at the top (default 1). When > 1 the fret number appears beside the diagram and the nut line is not bolded. |
| muted | int[] | Optional list of string numbers (1β6) that are not played. Draws an Γ above the nut for each. E.g. muted = [5, 6]. |
Each [[dot]] supports:
| Field | Type | Description |
|---|---|---|
| string | int | String number β 1 = high e (thinnest), 6 = low E (thickest) |
| fret | int | Absolute fret number; must be within [starting_fret, starting_fret + 4] |
| color | string | Optional CSS hex color for the dot, e.g. "#E74C3C". Defaults to the theme primary color. |
| label | string | Optional short text label drawn inside the dot, e.g. "R" or "3b". |
| number | int | Optional numeric fallback label for backwards compatibility. Ignored when label is present. |
Note: Because TOML uses [[section]] syntax, the TOML string inside Lua must use
[==[ β¦ ]==] long-string delimiters instead of
[[ ]] to avoid a premature closing of the Lua string.
-- C major degrees (use [==[ ]==] delimiters, not [[ ]])
local diagram = plugin.new_neck_diagram([==[
starting_fret = 1
[[dot]]
string = 5
fret = 3
label = "R"
[[dot]]
string = 4
fret = 2
label = "3"
[[dot]]
string = 2
fret = 1
label = "5"
]==])
plugin.window_set_content(win, plugin.new_vbox(
plugin.new_label("C major"),
diagram
)) plugin.update_neck_diagram(diagram: widget, toml: string) β true, err
Replaces the content of a diagram previously created by
plugin.new_neck_diagram
and immediately redraws it. Safe to call from event callbacks (runs on the UI thread internally).
Returns true on success or
nil, error_string on parse failure.
-- Switch between chords when the user picks one from a dropdown local diagram = plugin.new_neck_diagram(C_TOML) local selector = plugin.new_select(G, function(name) plugin.update_neck_diagram(diagram, CHORDS[name]) end)
plugin.add_toolbar_item(tooltip: string, action: function)
Appends a button to the plugin toolbar strip in the Tabatura UI. tooltip is shown on hover and acts as the button's accessible label.
The typical use is to let users reopen a window they have closed.
plugin.add_toolbar_item("Note Display", function()
plugin.window_show(win)
end) plugin.current_song() β table | nil
Returns a snapshot of the currently loaded song's metadata, or nil if no file is open.
The snapshot is taken at call time and does not update β call this again after a file.open or playback.start event to get fresh data.
| Field | Type | Description |
|---|---|---|
| title | string | Song title |
| artist | string | Artist name |
| album | string | Album name |
| subtitle | string | Subtitle / transcriber |
| tempo | number | Tempo in BPM |
| time_signature | string | e.g. "4/4" |
| key_signature | string | e.g. "C Major" |
| track_names | string[] | Array of track name strings |
plugin.on("file.open", function(_)
local s = plugin.current_song()
if s then
for i, name in ipairs(s.track_names) do
print(i .. ": " .. name)
end
end
end) Each plugin gets its own namespaced preference store, backed by the host app's preference system. Keys and values are plain strings. Preferences persist across restarts.
plugin.get_pref(key: string) β string
Returns the stored preference value for key,
or an empty string if not set.
local count = tonumber(plugin.get_pref("open_count")) or 0 plugin.set_pref(key: string, value: string)
Stores a preference value. Non-string values must be serialised to a string first (e.g. tostring).
-- Increment a persistent counter.
local count = tonumber(plugin.get_pref("open_count")) or 0
count = count + 1
plugin.set_pref("open_count", tostring(count)) Plugins can ask the host to open a GP file from the filesystem. Once loaded, Tabatura displays the tab and the user can play it using the built-in playback controls β no audio code needed in the plugin.
plugin.open_file(path: string) β true | nil, error_string
Asks Tabatura to open the GP file at the absolute filesystem path.
On success returns true.
On failure returns nil plus an error string.
Use plugin.plugin_dir() to build paths relative to your plugin's directory.
-- Open a bundled GP4 file and let Tabatura handle playback.
local path = plugin.plugin_dir() .. "/scales/box1-am.gp4"
local ok, err = plugin.open_file(path)
if ok then
print("Tab loaded β press βΆ in Tabatura to play")
else
print("Failed to open file: " .. tostring(err))
end plugin.plugin_dir() β string
Returns the absolute filesystem path to this plugin's directory β the same directory that contains plugin.toml.
Use this to locate assets (icons, SoundFont files, data files) bundled with your plugin.
local sf_path = plugin.plugin_dir() .. "/sounds/guitar.sf2" plugin.load_soundfont(sf_path)