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
205 changes: 60 additions & 145 deletions pywry/README.md
Original file line number Diff line number Diff line change
@@ -1,218 +1,133 @@
<div align="center">

![PyWry](./pywry/frontend/assets/PyWry.png)
![PyWry](https://github.com/deeleeramone/PyWry/blob/82db0c977a8ec812bf8652c0be14bf62b66b66a1/pywry/pywry/frontend/assets/PyWry.png?raw=true)

**Blazingly fast rendering library for native desktop windows, Jupyter widgets, and browser tabs.**
</div>

Full bidirectional Python ↔ JavaScript communication. Get started in minutes, not hours.
PyWry is a cross-platform rendering engine and desktop UI toolkit for Python. One API, three output targets:

[![PyPI](https://img.shields.io/pypi/v/pywry?color=blue)](https://pypi.org/project/pywry/)
[![Python](https://img.shields.io/pypi/pyversions/pywry)](https://pypi.org/project/pywry/)
[![License](https://img.shields.io/github/license/deeleeramone/PyWry)](LICENSE)
[![Docs](https://img.shields.io/badge/docs-live-brightgreen)](https://deeleeramone.github.io/PyWry/)
- **Native window** — OS webview via [PyTauri](https://pypi.org/project/pytauri/). Not Qt, not Electron.
- **Jupyter widget** — anywidget + FastAPI + WebSocket, works in JupyterLab, VS Code, and Colab.
- **Browser tab** — FastAPI server with Redis state backend for horizontal scaling.

</div>
## Installation

---

PyWry is **not** a web dashboard framework. It is a **rendering engine** that targets three output paths from one unified API:

| Mode | Where It Runs | Backend |
|------|---------------|---------|
| `NEW_WINDOW` / `SINGLE_WINDOW` / `MULTI_WINDOW` | Native OS window | PyTauri (Tauri/Rust) subprocess using OS webview |
| `NOTEBOOK` | Jupyter / VS Code / Colab | anywidget or IFrame + FastAPI + WebSocket |
| `BROWSER` | System browser tab | FastAPI server + WebSocket + Redis |

Built on [PyTauri](https://pypi.org/project/pytauri/) (Rust's [Tauri](https://tauri.app/) framework), it uses the OS webview instead of bundling a browser engine — a few MBs versus Electron's 150MB+ overhead.

<details>
<summary><strong>Features at a Glance</strong></summary>

| Feature | What It Does |
|---------|--------------|
| **Native Windows** | Lightweight OS webview windows (not Electron) |
| **Jupyter Widgets** | Works in notebooks via anywidget with traitlet sync |
| **Browser Mode** | Deploy to web with FastAPI + WebSocket |
| **Toolbar System** | 18 declarative Pydantic components with 7 layout positions |
| **Two-Way Events** | Python ↔ JavaScript with pre-wired Plotly/AgGrid events |
| **Modals** | Overlay dialogs with toolbar components inside |
| **AgGrid Tables** | Pandas → AgGrid conversion with pre-wired grid events |
| **Plotly Charts** | Plotly rendering with custom modebar buttons and plot events |
| **Toast Notifications** | Built-in alert system with configurable positioning |
| **Theming & CSS** | Light/dark modes, 60+ CSS variables, hot reload |
| **Secrets Handling** | Server-side password storage, never rendered in HTML |
| **Security** | Token auth, CSP headers, production presets |
| **Configuration** | Layered TOML files, env vars, security presets |
| **Hot Reload** | Live CSS injection and JS updates during development |
| **Deploy Mode** | Redis state backend for horizontal scaling |
| **MCP Server** | AI agent integration via Model Context Protocol |

</details>
Python 3.10–3.14, virtual environment recommended.

## Installation
```bash
pip install pywry
```

Requires Python 3.10–3.14. Install in a virtual environment.
| Extra | When to use |
|-------|-------------|
| `pip install 'pywry[notebook]'` | Jupyter / anywidget integration |
| `pip install 'pywry[mcp]'` | MCP server for AI agents |
| `pip install 'pywry[freeze]'` | PyInstaller hook for standalone executables |
| `pip install 'pywry[all]'` | Everything above |

<details>
<summary><strong>Linux Prerequisites</strong></summary>
**Linux only** — install system webview dependencies first:

```bash
# Ubuntu/Debian
sudo apt-get install libwebkit2gtk-4.1-dev libgtk-3-dev libglib2.0-dev \
libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 \
libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-xfixes0 \
libxcb-shape0 libgl1 libegl1
```

</details>

```bash
pip install pywry
```

| Extra | Command | Description |
|-------|---------|-------------|
| **notebook** | `pip install 'pywry[notebook]'` | anywidget for Jupyter integration |
| **mcp** | `pip install 'pywry[mcp]'` | Model Context Protocol server for AI agents |
| **all** | `pip install 'pywry[all]'` | All optional dependencies |

> See [Installation Guide](https://deeleeramone.github.io/PyWry/getting-started/installation/) for full details.

---

## Quick Start

### Hello World

```python
from pywry import PyWry

app = PyWry()

app.show("Hello World!")

app.block() # block the main thread until the window closes
app.block()
```

### Interactive Toolbar
### Toolbar + callbacks

```python
from pywry import PyWry, Toolbar, Button

app = PyWry()

def on_click(data, event_type, label):
app.emit("pywry:set-content", {"selector": "h1", "text": "Toolbar Works!"}, label)
app.emit("pywry:set-content", {"selector": "h1", "text": "Clicked!"}, label)

toolbar = Toolbar(
position="top",
items=[Button(label="Update Text", event="app:click")]
)

handle = app.show(
"<h1>Hello, World!</h1>",
toolbars=[toolbar],
app.show(
"<h1>Hello</h1>",
toolbars=[Toolbar(position="top", items=[Button(label="Click me", event="app:click")])],
callbacks={"app:click": on_click},
)
app.block()
```

### DataFrame → AgGrid
### Pandas DataFrame → AgGrid

```python
from pywry import PyWry
import pandas as pd

app = PyWry()

df = pd.DataFrame({"name": ["Alice", "Bob", "Carol"], "age": [30, 25, 35]})

def on_select(data, event_type, label):
names = ", ".join(row["name"] for row in data["rows"])
app.emit("pywry:alert", {"message": f"Selected: {names}" if names else "None selected"}, label)
app.emit("pywry:alert", {"message": f"Selected: {names}"}, label)

handle = app.show_dataframe(df, callbacks={"grid:row-selected": on_select})
app.show_dataframe(df, callbacks={"grid:row-selected": on_select})
app.block()
```

### Plotly Chart
### Plotly chart

```python
from pywry import PyWry, Toolbar, Button
from pywry import PyWry
import plotly.express as px

app = PyWry(theme="light")

fig = px.scatter(px.data.iris(), x="sepal_width", y="sepal_length", color="species")

handle = app.show_plotly(
fig,
toolbars=[Toolbar(position="top", items=[Button(label="Reset Zoom", event="app:reset")])],
callbacks={"app:reset": lambda d, e, l: app.emit("plotly:reset-zoom", {}, l)},
)
app.show_plotly(fig)
app.block()
```

> See [Quick Start Guide](https://deeleeramone.github.io/PyWry/getting-started/quickstart/) and [Examples](https://deeleeramone.github.io/PyWry/examples/) for more.

---

## Components

PyWry includes 18 declarative toolbar components, all Pydantic models with 7 layout positions (`header`, `footer`, `top`, `bottom`, `left`, `right`, `inside`):

| Component | Description |
|-----------|-------------|
| **Button** | Clickable button — primary, secondary, neutral, ghost, outline, danger, warning, icon |
| **Select** | Dropdown select with `Option` items |
| **MultiSelect** | Multi-select dropdown with checkboxes |
| **TextInput** | Text input with debounce support |
| **SecretInput** | Secure password input — values stored server-side, never in HTML |
| **TextArea** | Multi-line text area |
| **SearchInput** | Search input with debounce |
| **NumberInput** | Numeric input with min/max/step |
| **DateInput** | Date picker |
| **SliderInput** | Slider with optional value display |
| **RangeInput** | Dual-handle range slider |
| **Toggle** | Boolean toggle switch |
| **Checkbox** | Boolean checkbox |
| **RadioGroup** | Radio button group |
| **TabGroup** | Tab navigation |
| **Div** | Container element for content/HTML |
| **Marquee** | Scrolling ticker — scroll, alternate, slide, static |
| **Modal** | Overlay dialog supporting all toolbar components |

> See [Components Documentation](https://deeleeramone.github.io/PyWry/components/) for live previews, attributes, and usage examples.

---

## Documentation

Full documentation is available at **[deeleeramone.github.io/PyWry](https://deeleeramone.github.io/PyWry/)**.

| Section | Topics |
|---------|--------|
| [Getting Started](https://deeleeramone.github.io/PyWry/getting-started/) | Installation, Quick Start, Rendering Paths |
| [Concepts](https://deeleeramone.github.io/PyWry/getting-started/) | `app.show()`, HtmlContent, Events, Configuration, State & RBAC, Hot Reload |
| [UI](https://deeleeramone.github.io/PyWry/getting-started/) | Toolbar System, Modals, Toasts & Alerts, Theming & CSS |
| [Integrations](https://deeleeramone.github.io/PyWry/getting-started/) | Plotly Charts, AgGrid Tables |
| [Hosting](https://deeleeramone.github.io/PyWry/getting-started/) | Browser Mode, Deploy Mode |
| [Components](https://deeleeramone.github.io/PyWry/components/) | Live previews for all 18 toolbar components + Modal |
| [API Reference](https://deeleeramone.github.io/PyWry/reference/) | Auto-generated API docs for every class and function |
| [MCP Server](https://deeleeramone.github.io/PyWry/mcp/) | AI agent integration via Model Context Protocol |
## Features

---
- **18 toolbar components** — `Button`, `Select`, `MultiSelect`, `TextInput`, `SecretInput`, `SliderInput`, `RangeInput`, `Toggle`, `Checkbox`, `RadioGroup`, `TabGroup`, `Marquee`, `Modal`, and more. All Pydantic models, 7 layout positions.
- **Two-way events** — `app.emit()` and `app.on()` bridge Python and JavaScript in both directions. Pre-wired Plotly and AgGrid events included.
- **Theming** — light/dark modes, 60+ CSS variables, hot reload during development.
- **Security** — token auth, CSP headers, `SecuritySettings.strict()` / `.permissive()` / `.localhost()` presets. `SecretInput` stores values server-side, never in HTML.
- **Standalone executables** — PyInstaller hook ships with `pywry[freeze]`. No `.spec` edits or `--hidden-import` flags required.
- **MCP server** — 25 tools, 8 skills, 20+ resources for AI agent integration.

## MCP Server

PyWry includes a built-in [Model Context Protocol](https://modelcontextprotocol.io/) server for AI agent integration — 25 tools, 8 skills, and 20+ resources.

```bash
pip install 'pywry[mcp]'
pywry mcp --transport stdio
```

> See [MCP Documentation](https://deeleeramone.github.io/PyWry/mcp/) for setup with Claude Desktop, tool reference, and examples.
See the [MCP docs](https://deeleeramone.github.io/PyWry/mcp/) for Claude Desktop setup and tool reference.

## Standalone Executables

```bash
pip install 'pywry[freeze]'
pyinstaller --windowed --name MyApp my_app.py
```

The output in `dist/MyApp/` is fully self-contained. Target machines need no Python installation — only the OS webview (WebView2 on Windows 10 1803+, WKWebView on macOS, libwebkit2gtk on Linux).

## Documentation

**[deeleeramone.github.io/PyWry](https://deeleeramone.github.io/PyWry/)**

---
- [Getting Started](https://deeleeramone.github.io/PyWry/getting-started/) — installation, quick start, rendering paths
- [Concepts](https://deeleeramone.github.io/PyWry/getting-started/) — events, configuration, state, hot reload, RBAC
- [Components](https://deeleeramone.github.io/PyWry/components/) — live previews for all toolbar components
- [API Reference](https://deeleeramone.github.io/PyWry/reference/) — auto-generated docs for every class and function
- [MCP Server](https://deeleeramone.github.io/PyWry/mcp/) — AI agent integration

## License

Apache 2.0 — see [LICENSE](LICENSE) for details.
Apache 2.0 — see [LICENSE](LICENSE).
11 changes: 7 additions & 4 deletions pywry/docs/docs/components/secretinput.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,17 @@ Action buttons (left-to-right): Edit (pencil) enters edit mode, Copy (clipboard)

### Edit Mode

Clicking the edit button switches the masked input to a resizable textarea with confirm/cancel buttons. Confirm sends the value; cancel restores the mask.
Clicking the edit button hides the masked input and replaces it with a resizable textarea — always **empty** (the current secret is never pre-filled). Confirm (✓) and cancel (✗) buttons appear overlaid at the top-right corner of the textarea.

- **Confirm** — `Ctrl+Enter` or click ✓ — transmits the new value (base64-encoded) and restores the mask.
- **Cancel** — `Escape` or click ✗ — discards the input and restores the previous mask.

<div class="component-preview">
<span class="pywry-input-group pywry-input-inline">
<span class="pywry-input-label">API Key:</span>
<span class="pywry-secret-wrapper">
<textarea class="pywry-input pywry-secret-textarea" placeholder="Enter API key..." style="width: 180px; min-width: 180px; height: 28px; min-height: 28px;">sk-abc123xyz-new-value</textarea>
<span class="pywry-secret-actions pywry-secret-edit-actions" style="opacity: 1; pointer-events: auto;">
<span class="pywry-secret-wrapper" style="position: relative;">
<textarea class="pywry-input pywry-secret-textarea" placeholder="Enter API key..." style="width: 180px; min-width: 180px; height: 28px; min-height: 28px; padding-right: 60px; resize: both;"></textarea>
<span class="pywry-secret-actions pywry-secret-edit-actions" style="position: absolute; right: 4px; top: 4px; display: flex; align-items: center; opacity: 1; pointer-events: auto;">
<button type="button" class="pywry-secret-btn pywry-secret-confirm" data-tooltip="Confirm (Ctrl+Enter)"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg></button>
<button type="button" class="pywry-secret-btn pywry-secret-cancel" data-tooltip="Cancel (Escape)"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg></button>
</span>
Expand Down
Loading