Plugins
Settings, events and services
How a plugin stores settings, listens to events, and shares features with other plugins through services.
Three tools connect a plugin to the rest of Loam. Settings are options the user can change. Events tell a plugin that something happened. Services let a plugin use a feature that another plugin provides. Use settings for choices the user makes, events to react to a change, and services when another plugin needs a feature.
Settings
Describe your settings as one Codable struct with default values. Read it with context.settings.value(default:), and save it with context.settings.update(_:). Loam stores the values in .loam/config.json, under plugins.<id>.settings.
struct Settings: Codable, Equatable {
var folder = "Habits"
var week = "monday"
}
let settings = context.settings.value(default: Settings())Settings > Plugins shows every key with its current value, without any UI code. For titles, help text and a fixed list of choices, describe the keys in the manifest:
PluginManifest(id: "word-count", name: "Word Count", version: "0.1.0", settings: [
SettingDescription("folder", title: "Folder", help: "Where the new notes go.", default: .string("Habits")),
SettingDescription("week", title: "Week starts on", choices: [
SettingChoice(.string("monday"), title: "Monday"), SettingChoice(.string("sunday"), title: "Sunday"),
]),
])- A key missing from the file takes its default, so adding a setting never breaks an existing vault.
- An agent changes a setting with
loam plugins.set-setting id=<id> key=<key> value=<JSON>, and lists them withloam plugins.settings id=<id>. - A value of the wrong type is refused, and nothing is written.
- Listen for
PluginSettingsChangedand read the settings again, so a change applies at once.
Events
An event is a struct that conforms to PluginEvent. Listen with context.on. Use context.intercept to change or stop an event: call next(event) to continue, and return without calling it to stop. Send your own with context.emit.
context.on(VaultChanged.self) { event in
for change in event.noteChanges {
// change.path is a note that appeared, changed or disappeared.
}
}VaultChanged: files appeared, changed or disappeared on disk.NoteWillChangeandNoteDidChange: sent around a write bycontext.vault.NotesMoved: notes moved, so open views can follow their new paths.PluginSettingsChanged: the settings of a plugin changed.
Services
A service is a protocol that one plugin provides and others use. A plugin never imports another plugin. It names the service by id, and Loam connects the two:
public protocol Greeter: AnyObject {
func greet(_ name: String) -> String
}
extension ServiceKey where Service == Greeter {
static var greeter: ServiceKey<Greeter> { ServiceKey("greeter") }
}- Provider: list
"greeter"inprovides, then calltry context.provide(.greeter, implementation)inactivate. - User: list
"greeter"inrequires, then callcontext.service(.greeter). Loam starts the provider first. optionalRequires: use the service only when it exists. Your plugin restarts when the provider comes back.- Two plugins that provide the same service are refused, so a plugin can be replaced by another one that provides the same service.
Loam itself provides services such as note-graph, note-query, tasks, daily-notes, files and attachments. loam plugin list shows what each plugin provides and requires.
loam guide plugins shows these APIs in context. loam plugins.settings id=<id> lists the settings of one plugin.