# 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`.

```swift
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:

```swift
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 with `loam plugins.settings id=<id>`.
- A value of the wrong type is refused, and nothing is written.
- Listen for `PluginSettingsChanged` and 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`.

```swift
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.
- `NoteWillChange` and `NoteDidChange`: sent around a write by `context.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:

```swift
public protocol Greeter: AnyObject {
    func greet(_ name: String) -> String
}

extension ServiceKey where Service == Greeter {
    static var greeter: ServiceKey<Greeter> { ServiceKey("greeter") }
}
```

- Provider: list `"greeter"` in `provides`, then call `try context.provide(.greeter, implementation)` in `activate`.
- User: list `"greeter"` in `requires`, then call `context.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.

> **Tip:** For AI agents: `loam guide plugins` shows these APIs in context. `loam plugins.settings id=<id>` lists the settings of one plugin.
