# WM Keyboard plugins

> Plugins are small sandboxed Python scripts that add tools to the keyboard.

URL: https://wmkeyboard.pages.dev/plugins/overview/
Section: Plugins

A plugin is a small Python script that adds a tool to the keyboard. It draws a
panel (some text, a few buttons, maybe a text box) and does whatever you write
it to do.

```python title="main.py"
def render():
    return ui.column(
        ui.label("Hello!", style="title"),
        ui.button("wave", "Wave back"),
    )

def on_event(e):
    if e.id == "wave":
        wm.log("👋")
```

That is a complete plugin, minus its `plugin.json`.

You can also write one on the phone. Tools / Plugins / Write a plugin opens an in-app editor with a live preview and starter templates, and its Install button installs the result directly, with no ZIP file.

## Start here

	Getting started — Build a working cipher tool from an empty folder, in plain Python. — /plugins/getting-started/
	API reference — The manifest, every widget, every event, every wm.* function, and every limit. — /plugins/api-reference/
	Permissions — The one thing a plugin can ask for, and the many it cannot. — /plugins/permissions/
	Security — What the sandbox does, what it leaves out on purpose, and where the limits really are. — /plugins/security/

## What a plugin can do

Compute things, draw its own panel, and add buttons to the bar over selected
text. That is the whole list. A plugin can also be nothing but those
buttons: a [selection action](https://wmkeyboard.pages.dev/plugins/api-reference/#selection-actions) that
turns the text you selected into something else, which is also how a
[Clink action](https://wmkeyboard.pages.dev/reference/importing-other-keyboards/#clink-actions) installs.

## What a plugin cannot do

- **See what you type.** There is no key event API. A plugin cannot observe,
  change or delay a single keystroke going to your app.
- **Read the text you are writing in.** No API returns the contents of the field.
  A selection action is handed the text you selected, when you tap it, and
  nothing else.
- **Read your clipboard.** No API.
- **Use the internet.** No HTTP, no sockets, no URLs.
- **Reach Android.** No files, no reflection, no other apps, no device
  information, no permissions of its own.
- **Run in the background.** A plugin only runs while its panel is open. A
  plugin with the `reminders` permission can ask the app to show a line of text
  at a set time, and the app shows it; the plugin does not run for that.

No version of a plugin gets around them. The interpreter has no function for
any of it. See [Security](https://wmkeyboard.pages.dev/plugins/security/) for how that is enforced, and what the
residual risks honestly are.

Text still gets into a plugin: you type into its box, tap **Paste** on it, or
select text and tap one of its buttons on the selection bar. Results come back
out when you tap **Insert** under a result, or in place of the selection. Every
one of those is a deliberate tap, on a control the keyboard drew.

## Trying the examples

The five demo plugins in the
[addon repository](https://github.com/wasi-master/wmkeyboard-addon-repository)
are meant to be read. Each one is a single `main.py`:

| Plugin | Shows |
|---|---|
| **Cipher Tool** | Tabs, inputs, buttons, insertable output. The getting-started build. |
| **UI Kitchen Sink** | Every widget and every event, with a live event log. |
| **Todo List** | `wm.storage`, `wm.reminders`, `json` and the date widget: items with due dates, and a reminder when one is due. |
| **Text Tools** | The paste-in, transform, insert-out flow. |
| **Math Mode** | A real text engine: a tokenizer, symbol tables, a live result on every keystroke, and options kept in `wm.storage`. |

Plugins are **off until you ask for them**. Turn them on here, with **Allow
plugins**. The first time, the app fetches the library that runs them, from
Google Play on a Play install and from the project's own data repository
otherwise. Then install one from the Addons screen, or open a `.wmplugin` file:

Tools / Plugins / Manage plugins

## Lua plugins were retired

Plugins were written in Lua until version 0.6.6, which replaced Lua with
Python. When you update to 0.6.6, every Lua plugin on the phone is removed,
since nothing is left to run it. The keyboard's plugin list and the Plugins
screen say which ones went.

What each one saved is kept. Install a Python plugin with the same id and its
data comes back as it was. The five demos above kept their ids in the move, so
installing the Python **Todo List** brings your list back. Only what a plugin
saved with `wm.storage` is kept; its log and any reminders it had set are not.

Data that no plugin has claimed is listed on the Plugins screen, each with a
button to delete it now. Anything still unclaimed 90 days after the update is
deleted on its own.

Tools / Plugins / Manage plugins

A Lua plugin file opened after the update is refused with a reason that says it
was written in Lua; [Lua is refused](https://wmkeyboard.pages.dev/plugins/api-reference/#lua-is-refused) has
the exact rule.
