Skip to content

Commit 93727a5

Browse files
authored
Merge pull request #5 from deeleeramone/feature/pyinsstaller-hook
Add Freeze Support
2 parents 67995c3 + 5defd9f commit 93727a5

17 files changed

Lines changed: 948 additions & 201 deletions

pywry/README.md

Lines changed: 60 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -1,218 +1,133 @@
11
<div align="center">
22

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

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

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

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

14-
</div>
13+
## Installation
1514

16-
---
17-
18-
PyWry is **not** a web dashboard framework. It is a **rendering engine** that targets three output paths from one unified API:
19-
20-
| Mode | Where It Runs | Backend |
21-
|------|---------------|---------|
22-
| `NEW_WINDOW` / `SINGLE_WINDOW` / `MULTI_WINDOW` | Native OS window | PyTauri (Tauri/Rust) subprocess using OS webview |
23-
| `NOTEBOOK` | Jupyter / VS Code / Colab | anywidget or IFrame + FastAPI + WebSocket |
24-
| `BROWSER` | System browser tab | FastAPI server + WebSocket + Redis |
25-
26-
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.
27-
28-
<details>
29-
<summary><strong>Features at a Glance</strong></summary>
30-
31-
| Feature | What It Does |
32-
|---------|--------------|
33-
| **Native Windows** | Lightweight OS webview windows (not Electron) |
34-
| **Jupyter Widgets** | Works in notebooks via anywidget with traitlet sync |
35-
| **Browser Mode** | Deploy to web with FastAPI + WebSocket |
36-
| **Toolbar System** | 18 declarative Pydantic components with 7 layout positions |
37-
| **Two-Way Events** | Python ↔ JavaScript with pre-wired Plotly/AgGrid events |
38-
| **Modals** | Overlay dialogs with toolbar components inside |
39-
| **AgGrid Tables** | Pandas → AgGrid conversion with pre-wired grid events |
40-
| **Plotly Charts** | Plotly rendering with custom modebar buttons and plot events |
41-
| **Toast Notifications** | Built-in alert system with configurable positioning |
42-
| **Theming & CSS** | Light/dark modes, 60+ CSS variables, hot reload |
43-
| **Secrets Handling** | Server-side password storage, never rendered in HTML |
44-
| **Security** | Token auth, CSP headers, production presets |
45-
| **Configuration** | Layered TOML files, env vars, security presets |
46-
| **Hot Reload** | Live CSS injection and JS updates during development |
47-
| **Deploy Mode** | Redis state backend for horizontal scaling |
48-
| **MCP Server** | AI agent integration via Model Context Protocol |
49-
50-
</details>
15+
Python 3.10–3.14, virtual environment recommended.
5116

52-
## Installation
17+
```bash
18+
pip install pywry
19+
```
5320

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

56-
<details>
57-
<summary><strong>Linux Prerequisites</strong></summary>
28+
**Linux only** — install system webview dependencies first:
5829

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

67-
</details>
68-
69-
```bash
70-
pip install pywry
71-
```
72-
73-
| Extra | Command | Description |
74-
|-------|---------|-------------|
75-
| **notebook** | `pip install 'pywry[notebook]'` | anywidget for Jupyter integration |
76-
| **mcp** | `pip install 'pywry[mcp]'` | Model Context Protocol server for AI agents |
77-
| **all** | `pip install 'pywry[all]'` | All optional dependencies |
78-
79-
> See [Installation Guide](https://deeleeramone.github.io/PyWry/getting-started/installation/) for full details.
80-
81-
---
82-
8337
## Quick Start
8438

85-
### Hello World
86-
8739
```python
8840
from pywry import PyWry
8941

9042
app = PyWry()
91-
9243
app.show("Hello World!")
93-
94-
app.block() # block the main thread until the window closes
44+
app.block()
9545
```
9646

97-
### Interactive Toolbar
47+
### Toolbar + callbacks
9848

9949
```python
10050
from pywry import PyWry, Toolbar, Button
10151

10252
app = PyWry()
10353

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

107-
toolbar = Toolbar(
108-
position="top",
109-
items=[Button(label="Update Text", event="app:click")]
110-
)
111-
112-
handle = app.show(
113-
"<h1>Hello, World!</h1>",
114-
toolbars=[toolbar],
57+
app.show(
58+
"<h1>Hello</h1>",
59+
toolbars=[Toolbar(position="top", items=[Button(label="Click me", event="app:click")])],
11560
callbacks={"app:click": on_click},
11661
)
62+
app.block()
11763
```
11864

119-
### DataFrame → AgGrid
65+
### Pandas DataFrame → AgGrid
12066

12167
```python
12268
from pywry import PyWry
12369
import pandas as pd
12470

12571
app = PyWry()
126-
12772
df = pd.DataFrame({"name": ["Alice", "Bob", "Carol"], "age": [30, 25, 35]})
12873

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

133-
handle = app.show_dataframe(df, callbacks={"grid:row-selected": on_select})
78+
app.show_dataframe(df, callbacks={"grid:row-selected": on_select})
79+
app.block()
13480
```
13581

136-
### Plotly Chart
82+
### Plotly chart
13783

13884
```python
139-
from pywry import PyWry, Toolbar, Button
85+
from pywry import PyWry
14086
import plotly.express as px
14187

14288
app = PyWry(theme="light")
143-
14489
fig = px.scatter(px.data.iris(), x="sepal_width", y="sepal_length", color="species")
145-
146-
handle = app.show_plotly(
147-
fig,
148-
toolbars=[Toolbar(position="top", items=[Button(label="Reset Zoom", event="app:reset")])],
149-
callbacks={"app:reset": lambda d, e, l: app.emit("plotly:reset-zoom", {}, l)},
150-
)
90+
app.show_plotly(fig)
91+
app.block()
15192
```
15293

153-
> See [Quick Start Guide](https://deeleeramone.github.io/PyWry/getting-started/quickstart/) and [Examples](https://deeleeramone.github.io/PyWry/examples/) for more.
154-
155-
---
156-
157-
## Components
158-
159-
PyWry includes 18 declarative toolbar components, all Pydantic models with 7 layout positions (`header`, `footer`, `top`, `bottom`, `left`, `right`, `inside`):
160-
161-
| Component | Description |
162-
|-----------|-------------|
163-
| **Button** | Clickable button — primary, secondary, neutral, ghost, outline, danger, warning, icon |
164-
| **Select** | Dropdown select with `Option` items |
165-
| **MultiSelect** | Multi-select dropdown with checkboxes |
166-
| **TextInput** | Text input with debounce support |
167-
| **SecretInput** | Secure password input — values stored server-side, never in HTML |
168-
| **TextArea** | Multi-line text area |
169-
| **SearchInput** | Search input with debounce |
170-
| **NumberInput** | Numeric input with min/max/step |
171-
| **DateInput** | Date picker |
172-
| **SliderInput** | Slider with optional value display |
173-
| **RangeInput** | Dual-handle range slider |
174-
| **Toggle** | Boolean toggle switch |
175-
| **Checkbox** | Boolean checkbox |
176-
| **RadioGroup** | Radio button group |
177-
| **TabGroup** | Tab navigation |
178-
| **Div** | Container element for content/HTML |
179-
| **Marquee** | Scrolling ticker — scroll, alternate, slide, static |
180-
| **Modal** | Overlay dialog supporting all toolbar components |
181-
182-
> See [Components Documentation](https://deeleeramone.github.io/PyWry/components/) for live previews, attributes, and usage examples.
183-
184-
---
185-
186-
## Documentation
187-
188-
Full documentation is available at **[deeleeramone.github.io/PyWry](https://deeleeramone.github.io/PyWry/)**.
189-
190-
| Section | Topics |
191-
|---------|--------|
192-
| [Getting Started](https://deeleeramone.github.io/PyWry/getting-started/) | Installation, Quick Start, Rendering Paths |
193-
| [Concepts](https://deeleeramone.github.io/PyWry/getting-started/) | `app.show()`, HtmlContent, Events, Configuration, State & RBAC, Hot Reload |
194-
| [UI](https://deeleeramone.github.io/PyWry/getting-started/) | Toolbar System, Modals, Toasts & Alerts, Theming & CSS |
195-
| [Integrations](https://deeleeramone.github.io/PyWry/getting-started/) | Plotly Charts, AgGrid Tables |
196-
| [Hosting](https://deeleeramone.github.io/PyWry/getting-started/) | Browser Mode, Deploy Mode |
197-
| [Components](https://deeleeramone.github.io/PyWry/components/) | Live previews for all 18 toolbar components + Modal |
198-
| [API Reference](https://deeleeramone.github.io/PyWry/reference/) | Auto-generated API docs for every class and function |
199-
| [MCP Server](https://deeleeramone.github.io/PyWry/mcp/) | AI agent integration via Model Context Protocol |
94+
## Features
20095

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

203103
## MCP Server
204104

205-
PyWry includes a built-in [Model Context Protocol](https://modelcontextprotocol.io/) server for AI agent integration — 25 tools, 8 skills, and 20+ resources.
206-
207105
```bash
208106
pip install 'pywry[mcp]'
209107
pywry mcp --transport stdio
210108
```
211109

212-
> See [MCP Documentation](https://deeleeramone.github.io/PyWry/mcp/) for setup with Claude Desktop, tool reference, and examples.
110+
See the [MCP docs](https://deeleeramone.github.io/PyWry/mcp/) for Claude Desktop setup and tool reference.
111+
112+
## Standalone Executables
113+
114+
```bash
115+
pip install 'pywry[freeze]'
116+
pyinstaller --windowed --name MyApp my_app.py
117+
```
118+
119+
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).
120+
121+
## Documentation
122+
123+
**[deeleeramone.github.io/PyWry](https://deeleeramone.github.io/PyWry/)**
213124

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

216131
## License
217132

218-
Apache 2.0 — see [LICENSE](LICENSE) for details.
133+
Apache 2.0 — see [LICENSE](LICENSE).

pywry/docs/docs/components/secretinput.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,17 @@ Action buttons (left-to-right): Edit (pencil) enters edit mode, Copy (clipboard)
2222

2323
### Edit Mode
2424

25-
Clicking the edit button switches the masked input to a resizable textarea with confirm/cancel buttons. Confirm sends the value; cancel restores the mask.
25+
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.
26+
27+
- **Confirm**`Ctrl+Enter` or click ✓ — transmits the new value (base64-encoded) and restores the mask.
28+
- **Cancel**`Escape` or click ✗ — discards the input and restores the previous mask.
2629

2730
<div class="component-preview">
2831
<span class="pywry-input-group pywry-input-inline">
2932
<span class="pywry-input-label">API Key:</span>
30-
<span class="pywry-secret-wrapper">
31-
<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>
32-
<span class="pywry-secret-actions pywry-secret-edit-actions" style="opacity: 1; pointer-events: auto;">
33+
<span class="pywry-secret-wrapper" style="position: relative;">
34+
<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>
35+
<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;">
3336
<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>
3437
<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>
3538
</span>

0 commit comments

Comments
 (0)