Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions pywry/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -1027,6 +1027,67 @@ app.emit("toolbar:set-values", {"values": {"select-1": "A", "toggle-1": True}},
| `asset` | `PYWRY_ASSET__` | Library versions |
| `deploy` | `PYWRY_DEPLOY__` | Deploy mode and state backend |

### Tauri Plugins

PyWry bundles 19 Tauri plugins via `pytauri_wheel`. By default, only `dialog` and `fs` are enabled. Developers can enable additional plugins through configuration — **no Rust compilation required**.

#### Enabling Plugins

```python
# Via PyWrySettings constructor
from pywry import PyWry
from pywry.config import PyWrySettings

settings = PyWrySettings(tauri_plugins=["dialog", "fs", "notification", "http"])
app = PyWry(settings=settings)
```

```toml
# Via pywry.toml or pyproject.toml [tool.pywry]
tauri_plugins = ["dialog", "fs", "notification", "http"]
extra_capabilities = ["shell:allow-execute"]
```

```bash
# Via environment variables
export PYWRY_TAURI_PLUGINS="dialog,fs,notification,http"
export PYWRY_EXTRA_CAPABILITIES="shell:allow-execute"
```

#### Available Plugins

| Plugin Name | JS API | Description |
|-------------|--------|-------------|
| `autostart` | - | Launch app on system startup |
| `clipboard_manager` | `window.__TAURI__.clipboardManager` | Read/write system clipboard |
| `deep_link` | - | Handle custom URL schemes |
| `dialog` | `window.__TAURI__.dialog` | Native file/message dialogs |
| `fs` | `window.__TAURI__.fs` | Filesystem read/write |
| `global_shortcut` | `window.__TAURI__.globalShortcut` | System-wide keyboard shortcuts |
| `http` | `window.__TAURI__.http` | HTTP client from webview |
| `notification` | `window.__TAURI__.notification` | Desktop notifications |
| `opener` | `window.__TAURI__.opener` | Open URLs/files with default app |
| `os` | `window.__TAURI__.os` | OS info (platform, arch, etc.) |
| `persisted_scope` | - | Persist filesystem scopes |
| `positioner` | `window.__TAURI__.positioner` | Position windows on screen |
| `process` | `window.__TAURI__.process` | Process management |
| `shell` | `window.__TAURI__.shell` | Execute system commands |
| `single_instance` | - | Prevent duplicate app instances |
| `updater` | `window.__TAURI__.updater` | Auto-update support |
| `upload` | `window.__TAURI__.upload` | File upload with progress |
| `websocket` | `window.__TAURI__.websocket` | WebSocket client |
| `window_state` | - | Persist/restore window size and position |

#### How It Works

1. `PyWrySettings.tauri_plugins` holds the list of plugin names to activate
2. The parent process passes this list to the subprocess via `PYWRY_TAURI_PLUGINS` env var
3. In `__main__.py`, `_load_plugins()` dynamically imports each `pytauri_plugins.<name>` module and calls `.init()`
4. The `capabilities/default.toml` pre-grants `<plugin>:default` permissions for all 19 plugins (unused permissions are harmless)
5. For fine-grained permission control, use `extra_capabilities` to add specific permission strings (e.g., `shell:allow-execute`)

Each plugin has a `PLUGIN_*` compile-time feature flag in `pytauri_plugins` that is checked before initialization. If a plugin is not compiled into the bundled `pytauri_wheel`, a clear `RuntimeError` is raised.

### Example pywry.toml

```toml
Expand Down
1 change: 1 addition & 0 deletions pywry/docs/docs/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ One API, three output targets — PyWry automatically selects the right one:
| **[Configuration](guides/configuration.md)** | TOML files, env vars, layered precedence |
| **[Hot Reload](guides/hot-reload.md)** | Live CSS/JS updates during development |
| **[Deploy Mode](guides/deploy-mode.md)** | Redis backend for horizontal scaling |
| **[Tauri Plugins](guides/tauri-plugins.md)** | 19 bundled plugins — clipboard, notifications, HTTP, and more |

## Platform Support

Expand Down
3 changes: 3 additions & 0 deletions pywry/docs/docs/guides/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ export PYWRY_LOG__LEVEL=DEBUG
| `hot_reload` | `PYWRY_HOT_RELOAD__` | Hot reload behavior |
| `server` | `PYWRY_SERVER__` | Inline server settings |
| `deploy` | `PYWRY_DEPLOY__` | Deploy mode settings |
| `tauri_plugins` | `PYWRY_TAURI_PLUGINS` | Tauri plugins to load (comma-separated) |
| `extra_capabilities` | `PYWRY_EXTRA_CAPABILITIES` | Extra Tauri permission strings |

## Programmatic Configuration

Expand Down Expand Up @@ -192,5 +194,6 @@ pywry init
## Next Steps

- **[Configuration Reference](../reference/config.md)** — Complete `PyWrySettings` API
- **[Tauri Plugins](tauri-plugins.md)** — Enable clipboard, notifications, HTTP & more
- **[Deploy Mode](deploy-mode.md)** — Production server configuration
- **[Browser Mode](browser-mode.md)** — Server settings for browser mode
215 changes: 215 additions & 0 deletions pywry/docs/docs/guides/tauri-plugins.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
# Tauri Plugins

PyWry's native window mode runs on [Tauri](https://tauri.app) via the [PyTauri](https://pytauri.github.io/pytauri/) Python bindings. Tauri ships a rich ecosystem of **plugins** — clipboard access, notifications, HTTP requests, filesystem operations, global shortcuts, and more. PyWry exposes a configuration-driven system for enabling any of the 19 bundled plugins.

## How It Works

```mermaid
sequenceDiagram
participant App as PyWry (Python)
participant RT as runtime.py
participant Sub as __main__.py (subprocess)
participant Tauri as Tauri Engine

App->>RT: set_tauri_plugins(["dialog", "fs", "notification"])
RT->>Sub: env PYWRY_TAURI_PLUGINS="dialog,fs,notification"
Sub->>Sub: _load_plugins() — check flags, import modules
Sub->>Tauri: builder.build(plugins=[dialog.init(), fs.init(), notification.init()])
Tauri->>Tauri: Register plugins + grant capabilities
```

1. You list the plugins you want in config (Python, TOML, or env var).
2. `PyWry.__init__()` forwards them to the runtime module.
3. The runtime passes `PYWRY_TAURI_PLUGINS` as an environment variable to the subprocess.
4. The subprocess dynamically imports and initialises only the requested plugins.
5. Tauri's capability system (via `capabilities/default.toml`) pre-grants `:default` permissions for all bundled plugins, so capabilities don't need separate configuration.

---

## Quick Start

### Python

```python
from pywry import PyWry, PyWrySettings

settings = PyWrySettings(
tauri_plugins=["dialog", "fs", "notification", "clipboard_manager"],
)

app = PyWry(settings=settings)
app.show("<h1>Hello with plugins!</h1>")
```

### pywry.toml

```toml
tauri_plugins = ["dialog", "fs", "notification", "clipboard_manager"]
```

### pyproject.toml

```toml
[tool.pywry]
tauri_plugins = ["dialog", "fs", "notification", "clipboard_manager"]
```

### Environment Variable

```bash
# Comma-separated plugin names
export PYWRY_TAURI_PLUGINS="dialog,fs,notification,clipboard_manager"
```

```powershell
# PowerShell
$env:PYWRY_TAURI_PLUGINS = "dialog,fs,notification,clipboard_manager"
```

!!! info "Defaults"
If you don't configure anything, `dialog` and `fs` are enabled — matching PyWry's original behaviour.

---

## Available Plugins

All 19 plugins bundled in the `pytauri_wheel` binary are listed below. Each plugin exposes a JavaScript (`window.__TAURI__.*`) and/or Python API once enabled.

| Plugin name | JS API | Description |
|:---|:---|:---|
| `autostart` | — | Launch at OS login |
| `clipboard_manager` | `clipboard` | Read/write system clipboard |
| `deep_link` | `deepLink` | Handle custom URL schemes |
| `dialog` | `dialog` | Native open/save/message dialogs |
| `fs` | `fs` | Filesystem read/write/watch |
| `global_shortcut` | `globalShortcut` | System-wide keyboard shortcuts |
| `http` | `http` | HTTP client (fetch replacement) |
| `notification` | `notification` | OS notification centre |
| `opener` | `opener` | Open URLs / files with default app |
| `os` | `os` | Platform, arch, locale info |
| `persisted_scope` | — | Persist runtime FS scopes across restarts |
| `positioner` | — | Position windows (centre, tray, etc.) |
| `process` | `process` | Restart / exit app |
| `shell` | `shell` | Execute system commands |
| `single_instance` | — | Prevent duplicate app instances |
| `updater` | `updater` | Auto-update from remote server |
| `upload` | `upload` | Upload files with progress |
| `websocket` | `websocket` | WebSocket client |
| `window_state` | — | Save/restore window size & position |

!!! note "Feature flags"
Each plugin has a compile-time feature flag (e.g. `PLUGIN_NOTIFICATION`). If the bundled `pytauri_wheel` was not compiled with a particular feature, enabling that plugin at runtime will raise a `RuntimeError` with a clear message. The default PyWry wheel includes all 19.

---

## Using Plugin APIs in JavaScript

Once a plugin is enabled, its JavaScript API is available through the Tauri bridge. Use the [JavaScript Bridge](javascript-bridge.md) to interact:

```python
app = PyWry(settings=PyWrySettings(
tauri_plugins=["dialog", "fs", "notification", "clipboard_manager"],
))

html = """
<button onclick="showNotification()">Notify</button>
<script>
async function showNotification() {
// Tauri notification plugin API
const { sendNotification } = window.__TAURI__.notification;
sendNotification({
title: "PyWry",
body: "Hello from a Tauri plugin!",
});
}
</script>
"""
app.show(html)
```

!!! tip "Plugin documentation"
For the full JavaScript API of each plugin, see the [Tauri Plugins documentation](https://tauri.app/plugin/).

---

## Extra Capabilities

Tauri's [capability system](https://tauri.app/security/capabilities/) controls which APIs a window is allowed to call. By default, PyWry grants `:default` permissions for every bundled plugin. This is sufficient for most use cases.

If you need **fine-grained permissions** beyond the defaults (e.g. `shell:allow-execute`, `fs:allow-read-file`), use the `extra_capabilities` setting:

```python
settings = PyWrySettings(
tauri_plugins=["shell", "fs"],
extra_capabilities=["shell:allow-execute", "fs:allow-read-file"],
)
```

Or via TOML:

```toml
tauri_plugins = ["shell", "fs"]
extra_capabilities = ["shell:allow-execute", "fs:allow-read-file"]
```

Or the environment variable:

```bash
export PYWRY_EXTRA_CAPABILITIES="shell:allow-execute,fs:allow-read-file"
```

!!! warning "Capability names use hyphens"
Tauri permission strings use **hyphens**, not underscores: `clipboard-manager:default`, not `clipboard_manager:default`. Plugin *config names* use underscores (Python identifiers), but capability *permission strings* use the Tauri convention.

---

## Capabilities That Don't Exist

Two plugins — `persisted_scope` and `single_instance` — do **not** register Tauri capability manifests. They are Rust-only plugins without permission-gated functionality. Including `persisted-scope:default` or `single-instance:default` in a capabilities file will cause a **Tauri panic** at startup. PyWry's `capabilities/default.toml` intentionally omits them.

You can still _enable_ these plugins (they'll be initialised at the Rust level), but do not add capability strings for them.

---

## Architecture Details

### Plugin loading flow

1. **`config.py`** — `PyWrySettings.tauri_plugins` validates names against `AVAILABLE_TAURI_PLUGINS`.
2. **`app.py`** — `PyWry.__init__()` calls `runtime.set_tauri_plugins(settings.tauri_plugins)` and `runtime.set_extra_capabilities(settings.extra_capabilities)`.
3. **`runtime.py`** — Stores both lists and passes them as `PYWRY_TAURI_PLUGINS` and `PYWRY_EXTRA_CAPABILITIES` environment variables when starting the subprocess.
4. **`__main__.py`** — The subprocess:
- Reads `PYWRY_TAURI_PLUGINS`, calls `_load_plugins()` which validates names, checks `PLUGIN_*` feature flags, dynamically imports modules, and calls `.init()`.
- Reads `PYWRY_EXTRA_CAPABILITIES`. If non-empty, copies the package directory to a temp staging dir and writes an `extra.toml` capability file with the requested permissions. This is necessary because `context_factory()` reads capabilities from static TOML files, and installed packages may be read-only.
5. **Tauri build** — `context_factory(ctx_dir)` reads all `.toml` files under `capabilities/` (including the staged `extra.toml` if present), then `builder_factory().build(plugins=plugins)` registers all initialised plugins with the Tauri engine.

### Capability files

Base permissions live in [`pywry/capabilities/default.toml`](https://github.com/deeleeramone/PyWry/blob/main/pywry/pywry/capabilities/default.toml). It pre-grants `:default` for all 17 plugins that have valid Tauri capability manifests. Permissions for plugins that aren't initialised are simply unused — they don't cause errors or security issues.

When `extra_capabilities` is configured, an additional `extra.toml` is generated at runtime in a temporary directory with just the extra permissions. The temp directory is cleaned up when the subprocess exits.

---

## Troubleshooting

### "Unknown Tauri plugin 'xyz'"

The plugin name isn't in the registry. Check spelling — use underscores (e.g. `clipboard_manager`, not `clipboard-manager`).

### "PLUGIN_XYZ feature was not compiled"

The `pytauri_wheel` binary was compiled without that plugin's feature flag. This shouldn't happen with the default PyWry distribution, but custom builds may exclude plugins.

### App crashes on startup after adding capabilities

If you get a `PanicException` mentioning `UnknownManifest`, you've added a capability string for a plugin that doesn't register one (`persisted_scope` or `single_instance`). Remove the offending string from `extra_capabilities`.

---

## Next Steps

- **[Configuration Guide](configuration.md)** — Full settings reference
- **[JavaScript Bridge](javascript-bridge.md)** — Calling Tauri APIs from JS
- **[Events](events.md)** — Python↔JS event system
- **[PyTauri Plugin Docs](https://pytauri.github.io/pytauri/latest/usage/tutorial/using-plugins/)** — Upstream plugin tutorial
19 changes: 19 additions & 0 deletions pywry/docs/docs/reference/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,25 @@ Content Security Policy configuration.

---

## Tauri Plugin Constants

::: pywry.config.TAURI_PLUGIN_REGISTRY
options:
show_root_heading: true
heading_level: 2

::: pywry.config.AVAILABLE_TAURI_PLUGINS
options:
show_root_heading: true
heading_level: 2

::: pywry.config.DEFAULT_TAURI_PLUGINS
options:
show_root_heading: true
heading_level: 2

---

## Settings Functions

::: pywry.config.get_settings
Expand Down
10 changes: 10 additions & 0 deletions pywry/docs/docs/reference/runtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,16 @@ Low-level PyTauri subprocess management and IPC communication.
show_root_heading: true
heading_level: 2

::: pywry.runtime.set_tauri_plugins
options:
show_root_heading: true
heading_level: 2

::: pywry.runtime.set_extra_capabilities
options:
show_root_heading: true
heading_level: 2

---

## Command IPC
Expand Down
1 change: 1 addition & 0 deletions pywry/docs/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ nav:
- Plotly Charts: guides/plotly.md
- AgGrid Tables: guides/aggrid.md
- Multi-Widget Pages: guides/multi-widget.md
- Tauri Plugins: guides/tauri-plugins.md
- Hosting:
- Browser Mode: guides/browser-mode.md
- Deploy Mode: guides/deploy-mode.md
Expand Down
Loading