Write your first plugin
Make a HikariSub plugin in three steps, a command, a panel and an edit, then learn the manifest, permissions and tiers, and how to sign and publish it.
This guide makes a small plugin in three steps: a command, then a panel, then an edit to the subtitles. Then it explains the manifest, permissions and tiers, and how to sign and publish a plugin.
Plugins is the full contract, and the Hikari API reference lists every operation and event. Each step below is a complete plugin in examples/first-plugin, and a test runs all three.
Before you start
- Software. You need HikariSub, a text editor and a zip tool. Plugins are written in Lua 5.1 and run on LuaJIT.
- Folders. A plugin is a folder with two files:
hikari-plugin.json, the manifest, and the Lua file that the manifest names asmain. - Packages. To install it, you zip the folder's contents into a
.hikaripluginfile.
Step 1: a command
Make a folder first-plugin with this manifest:
{
"$schema": "../../../schemas/hikari-plugin.schema.json",
"id": "com.example.first-plugin",
"name": "First plugin",
"version": "0.1.0",
"api": 1,
"main": "main.lua",
"authors": ["Your name"],
"license": "MIT",
"contributes": {
"commands": [{"id": "count", "title": "Count lines"}]
}
}idis reverse-DNS and lowercase. It names the plugin's folders, so don't change it later.apiis the lowest Hikari API version the plugin needs. A HikariSub with an older API refuses the plugin, and the message names both numbers.$schemalets an editor check the file against the published schema. Point it at your own copy of the schema, or remove it.contributes.commandsdeclares each command that the plugin adds.
Then add main.lua:
hikari.commands.register("count", function()
local info = hikari.call("documents.get", {})
hikari.notify(info.title .. " has " .. info.lineCount .. " lines.")
end)hikari.commands.registergives the command from the manifest its handler.hikari.callcalls a Hikari API operation, heredocuments.get. It returns the result, orniland an error with acode, amessageand sometimesdata.hikari.notifyshows a toast and writes a Log panel entry under the plugin's name.
To try it:
-
Zip the folder's contents, not the folder itself. On Linux or macOS:
cd first-plugin && zip -r ../first-plugin.hikariplugin .On Windows, in a terminal in that folder:
tar --format zip -cf ..\first-plugin.hikariplugin * -
In HikariSub, choose Automation › Plugins…, then Install from file…, and choose the package.
-
Open a subtitle file, then choose Automation › First plugin › Count lines.
After a change, raise version and choose Update from file… in the plugin's details. If the plugin fails to start, its details show the error, with the file and line inside the package.
Step 2: a panel
A panel is drawn from a tree of widgets that the plugin renders. Declare it in the manifest:
"commands": [{"id": "count", "title": "Count lines"}],
"panels": [{"id": "summary", "title": "Summary", "area": "right"}]Then draw it, and draw it again whenever the subtitles change:
local PANEL = "summary"
local function lineCount()
return hikari.call("documents.get", {}).lineCount
end
local function draw()
hikari.panels.render(PANEL, {
kind = "column",
children = {
{ kind = "label", style = "heading", text = lineCount() .. " lines" },
},
})
endhikari.panels.on(PANEL, draw)
hikari.events.on("document.changed", draw)hikari.panels.ongets the panel's events. The first one,show, comes when the user opens the panel.hikari.events.onlistens to a Hikari API event.document.changedcomes after every edit, undo and redo.
A plugin panel stays closed until the user opens it from View › Panels. Then it docks in the manifest's area and behaves like HikariSub's own panels. Widgets lists every kind of widget.
Step 3: an edit
The last step adds a command and a button that join the line breaks (\N) in the selected lines. Add the command to the manifest:
{"id": "unbreak", "title": "Join line breaks in the selected lines"}Make the change with hikari.document.edit:
local function unbreak()
local chosen = {}
for _, id in ipairs(hikari.selection.get().selected) do
chosen[id] = true
end
return hikari.document.edit("Join line breaks", function(doc, change)
for _, line in ipairs(doc.lines) do
if chosen[line.id] and line.text:find("\\N", 1, true) then
change:set_line(line.id, { text = (line.text:gsub("\\N", " ")) })
end
end
end)
end- The snapshot. The function gets a snapshot of the subtitles,
doc, andchange, where it stages edits. - One undo step. HikariSub applies the staged edits as one undo step, named "First plugin: Join line breaks".
- Line ids. Lines are named by their
id, which survives edits and moves. A row number doesn't. - A stale snapshot. If the user changes the subtitles while the function runs, nothing is applied, and the error is
stale_revision.
Give the command its handler, and say what happened:
hikari.commands.register("unbreak", function()
local done, err = unbreak()
if not done then
hikari.notify("Nothing was joined: " .. err.message, "warning")
elseif not done.changed then
hikari.notify("The selected lines have no line breaks.")
end
end)Then add the button to the panel, and answer its click:
{ kind = "button", id = "unbreak", text = "Join line breaks", style = "primary" },hikari.panels.on(PANEL, function(event)
if event.kind == "click" and event.widget == "unbreak" then
unbreak()
end
draw()
end)Every widget that sends events needs an id. Each event says which widget sent it, in event.widget.
The manifest
The manifest lists every field. Besides the fields above:
description,homepage. Shown in the plugin manager.contributes.settings. Settings that the user changes in the plugin manager and that the plugin reads withhikari.settings.get.permissions. What the plugin asks for, each with a reason that the install question shows. See below.summary,icon,screenshots. What the marketplace shows. AREADME.mdin the package is its page.- Translations.
i18n/<locale>.jsonfiles are read byhikari.i18n.t.
Permissions and tiers
A plugin gets only what its manifest asks for. The user sees each request in the install question, and again when an update asks for more.
- The active document. Every plugin can read and edit the subtitle file that's active.
documents.alllets it see the other tabs too. - Files. A plugin has its own data and cache folders. It can open files that the user picks or drops on its panel, and folders that it names in
permissions.files, after the user chooses them. It gets handles, never paths. See Files. - Network. HikariSub makes each request for the plugin, over HTTPS, and only to the hosts in
permissions.network. See Network. - Restricted, the default tier. The plugin's Lua has no
io,os,ffiordebug. Its helper runs in the operating system's sandbox where one is available. - Full trust.
"tier": "unrestricted"gives the full LuaJIT libraries, no sandbox, and QML panels. The user must agree to give the plugin full access to the computer. Ask for it only when the restricted tier can't do the job.
A call that needs a permission the plugin doesn't have fails with the error not_granted. The Hikari API lists every error code.
Sign it
A signature tells users and HikariSub that a package comes from you, and lets updates prove they come from the same author.
-
Make a minisign key once. Keep the secret key safe: you can't change keys without asking the marketplace.
minisign -G -p you.pub -s ~/.minisign/you.key -
Put the public key, the base64 line of
you.pub, in the manifest aspublicKey. -
Sign each package. Ship the
.minisigfile beside the package, with the same name plus.minisig.minisign -S -s ~/.minisign/you.key -m first-plugin.hikariplugin
HikariSub checks a signed package against its publicKey at the first install, then pins that key. A later update signed by another key needs the user to trust the new key.
Publish it
The marketplace lists plugins from altqx/hikari-plugins. Its README is the full process.
- The id. It starts with your GitHub handle, for example
alice.line-tools. - What a listing needs. A
summary, an SPDXlicenseand aREADME.mdat the package root. - The listing. Open a pull request there that adds
plugins/<id>.json, with your public key. The first listing is reviewed by hand. - Releases. Publish a GitHub release with
<id>-<version>.hikaripluginand its.minisig. The registry checks it, publishes it within half an hour, and uses the release notes as its changelog. Users who installed it from the marketplace get the update automatically. - More permissions. An update that asks for more permissions waits for review. The users who installed the plugin then confirm the new permissions.
Next
- Using plugins. Installing, updating and running plugins, as your users see them.
- Plugins. Dialogs, events, files, the network, QML panels and the plugin manager.
- Hikari API reference. Every operation, event and type, with its parameters and results. It is generated from HikariSub itself, so it matches the API version that HikariSub reports in
hikari.api_version. - Line tools. A larger example with a chart, a list, a menu and a dialog.
Using plugins
Install HikariSub plugins from the marketplace or a .hikariplugin file, see what each asks for and who signed it, keep them up to date, choose their folders, and use their commands and panels.
Hikari API reference
Every operation, event and type of the Hikari API that plugins call, generated from HikariSub itself.