zagred 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- zagred-0.1.0/LICENSE +21 -0
- zagred-0.1.0/PKG-INFO +467 -0
- zagred-0.1.0/README.md +445 -0
- zagred-0.1.0/pyproject.toml +33 -0
- zagred-0.1.0/pyproject.toml.orig +34 -0
- zagred-0.1.0/src/zagred/__init__.py +17 -0
- zagred-0.1.0/src/zagred/app.py +530 -0
- zagred-0.1.0/src/zagred/assets.py +99 -0
- zagred-0.1.0/src/zagred/bridge.py +136 -0
- zagred-0.1.0/src/zagred/build.py +295 -0
- zagred-0.1.0/src/zagred/cli.py +352 -0
- zagred-0.1.0/src/zagred/manifest.py +136 -0
- zagred-0.1.0/src/zagred/runtime.py +33 -0
- zagred-0.1.0/src/zagred/scaffold.py +250 -0
- zagred-0.1.0/src/zagred/templates.py +637 -0
zagred-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Muhammad Arief Minardi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
zagred-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: zagred
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Desktop apps from Python backends and web frontends, built with pywebview and Nuitka
|
|
5
|
+
Author: Muhammad Arief Minardi
|
|
6
|
+
Author-email: Muhammad Arief Minardi <ariefminardi005@gmail.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
12
|
+
Classifier: Topic :: Software Development :: User Interfaces
|
|
13
|
+
Classifier: Environment :: X11 Applications :: GTK
|
|
14
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
15
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
16
|
+
Classifier: Operating System :: MacOS
|
|
17
|
+
Requires-Dist: pywebview>=6.0
|
|
18
|
+
Requires-Dist: nuitka>=4.2 ; extra == 'build'
|
|
19
|
+
Requires-Python: >=3.12
|
|
20
|
+
Provides-Extra: build
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# Zagred
|
|
24
|
+
|
|
25
|
+
**Desktop apps with a Python backend and a web frontend.**
|
|
26
|
+
|
|
27
|
+
Your app is a normal Python package. Your UI is a folder next to it. Zagred puts
|
|
28
|
+
the two together in a native window (via [pywebview](https://pywebview.flowrl.com))
|
|
29
|
+
and compiles the result into a `.AppImage`, `.exe`, `.app` or `.dmg`
|
|
30
|
+
(via [Nuitka](https://nuitka.net)).
|
|
31
|
+
|
|
32
|
+
- **Python 3.12+**, dependencies managed by [uv](https://docs.astral.sh/uv/)
|
|
33
|
+
- No Rust. No Node.js unless you want a bundler.
|
|
34
|
+
- Frontend: plain HTML/JS, or React / Vue / Solid with Vite
|
|
35
|
+
- Bridge with typed errors, async commands, and Python→JS events
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Quick start
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# 1. install the CLI (not on PyPI yet — see "Local development")
|
|
43
|
+
uv tool install zagred
|
|
44
|
+
|
|
45
|
+
# 2. create a project — interactive: name, framework, JS or TS
|
|
46
|
+
zagred new
|
|
47
|
+
|
|
48
|
+
# 3. install everything
|
|
49
|
+
cd my-app
|
|
50
|
+
uv sync
|
|
51
|
+
(cd web && npm install) # only if you picked a bundler
|
|
52
|
+
|
|
53
|
+
# 4. run it
|
|
54
|
+
uv run zagred dev
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Skip the questions in scripts:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
zagred new my-app --template react --lang ts -y
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Linux: one extra step
|
|
64
|
+
|
|
65
|
+
uv's Python cannot import the distro's `python3-gi`, so build the venv from the
|
|
66
|
+
system interpreter **before** `uv sync`:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
sudo apt install python3-gi gir1.2-webkit2-4.1
|
|
70
|
+
uv venv --python /usr/bin/python3 --system-site-packages
|
|
71
|
+
uv sync
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Verify with `uv run zagred doctor` — it should report `renderer gtk`.
|
|
75
|
+
|
|
76
|
+
### What you get
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
my-app/
|
|
80
|
+
├── zagred.toml # [app] identity, [ui] dirs, [build] options
|
|
81
|
+
├── pyproject.toml # uv project, depends on zagred
|
|
82
|
+
├── .python-version # pinned to 3.12
|
|
83
|
+
├── web/ # your UI
|
|
84
|
+
│ ├── package.json # only when a bundler is used
|
|
85
|
+
│ └── src/
|
|
86
|
+
└── my_app/
|
|
87
|
+
└── app.py # App, @expose, lifecycle
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## The backend
|
|
93
|
+
|
|
94
|
+
Windows and lifecycle live in Python, not in JSON:
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
# my_app/app.py
|
|
98
|
+
import zagred
|
|
99
|
+
|
|
100
|
+
app = zagred.App(title="My App", size=(1024, 700))
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@app.expose
|
|
104
|
+
def greet(name: str) -> str:
|
|
105
|
+
"""Callable from the UI as zagred.call("greet", { name })."""
|
|
106
|
+
return f"Hello, {name}!"
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@app.expose
|
|
110
|
+
async def scan(folder: str) -> list[str]:
|
|
111
|
+
...
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@app.expose
|
|
115
|
+
def pick_file() -> str | None:
|
|
116
|
+
files = app.create_file_dialog()
|
|
117
|
+
return files[0] if files else None
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Rules worth knowing:
|
|
121
|
+
|
|
122
|
+
- A function is registered under its `__name__`; exposing two functions with the
|
|
123
|
+
same name silently keeps the last one.
|
|
124
|
+
- Arguments are keyword arguments from JS. Wrong ones give `bad_arguments`.
|
|
125
|
+
- Every return value must be JSON-serialisable; that is checked before it leaves
|
|
126
|
+
Python, so you get an error instead of a silent blank window.
|
|
127
|
+
- `async def` works — it is awaited on a worker thread.
|
|
128
|
+
|
|
129
|
+
## The frontend
|
|
130
|
+
|
|
131
|
+
```js
|
|
132
|
+
const msg = await zagred.call("greet", { name: "world" });
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
```js
|
|
136
|
+
zagred.call("greet").catch((err) => {
|
|
137
|
+
console.error(err.code, err.message); // e.g. "bad_arguments"
|
|
138
|
+
});
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Events, Python → JS:
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
@app.expose
|
|
145
|
+
def start_job() -> None:
|
|
146
|
+
for done, total in work():
|
|
147
|
+
app.emit("job:progress", {"done": done, "total": total})
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
```js
|
|
151
|
+
const off = zagred.on("job:progress", (p) => console.log(p.done));
|
|
152
|
+
off(); // unsubscribe
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
`zagred.ready` is a promise that resolves once the bridge is live. Top-level
|
|
156
|
+
script code should `await zagred.ready` (or call from inside an event handler)
|
|
157
|
+
because the bridge is injected around page-load time.
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## CLI
|
|
162
|
+
|
|
163
|
+
| Command | What it does |
|
|
164
|
+
|---|---|
|
|
165
|
+
| `zagred new [name]` | Interactive scaffold. `--template`, `--lang`, `-y` for scripts |
|
|
166
|
+
| `zagred dev [--debug]` | Start the frontend dev server, wait for it, open the window |
|
|
167
|
+
| `zagred build [--target T] [--jobs N] [--debug]` | Build the UI, compile with Nuitka |
|
|
168
|
+
| `zagred doctor` | Check python, pywebview, renderer, nuitka, patchelf, appimagetool |
|
|
169
|
+
|
|
170
|
+
`zagred dev` cleans up after itself: closing the window kills the whole frontend
|
|
171
|
+
process group, so no orphaned `vite` holding your terminal.
|
|
172
|
+
|
|
173
|
+
### Templates
|
|
174
|
+
|
|
175
|
+
| `--template` | `--lang js` | `--lang ts` | Bundler |
|
|
176
|
+
|---|---|---|---|
|
|
177
|
+
| `vanilla` *(default)* | plain files, **no npm** | Vite | optional |
|
|
178
|
+
| `react` | Vite | Vite | yes |
|
|
179
|
+
| `vue` | Vite | Vite | yes |
|
|
180
|
+
| `solid` | Vite | Vite | yes |
|
|
181
|
+
|
|
182
|
+
TypeScript projects ship `web/src/zagred.d.ts`:
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
const msg = await zagred.call<string>("greet", { name });
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Bundler projects get `npm run dev -- --port 5173 --strictPort` in `zagred.toml`,
|
|
189
|
+
so `zagred dev` always finds the server on the port it polls.
|
|
190
|
+
|
|
191
|
+
### Window options
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
app = zagred.App(
|
|
195
|
+
title="My App",
|
|
196
|
+
size=(1200, 800),
|
|
197
|
+
min_size=(640, 480),
|
|
198
|
+
position=(100, 100), # omit to let the OS place it
|
|
199
|
+
resizable=True,
|
|
200
|
+
fullscreen=False,
|
|
201
|
+
maximized=False,
|
|
202
|
+
frameless=False, # no title bar
|
|
203
|
+
easy_drag=True, # frameless windows stay draggable
|
|
204
|
+
on_top=False,
|
|
205
|
+
focus=True,
|
|
206
|
+
shadow=False,
|
|
207
|
+
confirm_close=False, # ask before closing
|
|
208
|
+
transparent=False,
|
|
209
|
+
text_select=False,
|
|
210
|
+
zoomable=False,
|
|
211
|
+
vibrancy=False, # macOS
|
|
212
|
+
background_color="#ffffff",
|
|
213
|
+
icon="my_app/resources/icon.png",
|
|
214
|
+
menu=[],
|
|
215
|
+
)
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Control it at runtime:
|
|
219
|
+
|
|
220
|
+
```python
|
|
221
|
+
app.minimize() / app.maximize() / app.restore()
|
|
222
|
+
app.toggle_fullscreen()
|
|
223
|
+
app.resize(900, 600) / app.move(50, 50)
|
|
224
|
+
app.set_title("Renamed")
|
|
225
|
+
app.show() / app.hide() / app.destroy()
|
|
226
|
+
app.load_url("http://localhost:5173")
|
|
227
|
+
app.evaluate_js("document.title")
|
|
228
|
+
app.create_file_dialog(allow_multiple=False) # native picker
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Lifecycle hooks:
|
|
232
|
+
|
|
233
|
+
```python
|
|
234
|
+
@app.on_start
|
|
235
|
+
def boot(): ... # GUI loop is up
|
|
236
|
+
|
|
237
|
+
@app.on_loaded
|
|
238
|
+
def ready(): ... # page finished loading
|
|
239
|
+
|
|
240
|
+
@app.on_closing
|
|
241
|
+
def guard():
|
|
242
|
+
return False # cancel the close
|
|
243
|
+
|
|
244
|
+
@app.on_closed
|
|
245
|
+
def bye(): ...
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## Building
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
uv sync --group build # adds Nuitka (+ patchelf on Linux)
|
|
254
|
+
uv run zagred build # auto target for this OS
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
| `--target` | Nuitka flags | Output |
|
|
258
|
+
|---|---|---|
|
|
259
|
+
| `auto` *(default)* | picks by OS | `.AppImage` / `.exe` / `.app` |
|
|
260
|
+
| `onefile` | `--mode=onefile` | single binary |
|
|
261
|
+
| `standalone` | `--mode=standalone` | folder with binary + libs |
|
|
262
|
+
| `exe` | `--mode=standalone --windows-create-installer` | NSIS `-setup.exe` |
|
|
263
|
+
| `appimage` | `--mode=app-dist --linux-create-installer` | `.AppImage` |
|
|
264
|
+
| `app` | `--mode=app` | macOS `.app` |
|
|
265
|
+
| `dmg` | `--mode=app --macos-create-installer` | `.dmg` |
|
|
266
|
+
|
|
267
|
+
Each target writes to its own directory, so building one never overwrites another:
|
|
268
|
+
|
|
269
|
+
```
|
|
270
|
+
dist/
|
|
271
|
+
├── appimage/
|
|
272
|
+
│ ├── my-app.AppImage
|
|
273
|
+
│ └── my-app.dist/
|
|
274
|
+
├── onefile/
|
|
275
|
+
│ └── my-app
|
|
276
|
+
└── exe/
|
|
277
|
+
└── my-app-setup.exe
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
On Windows, some antivirus engines flag the `onefile` stub as suspicious. If
|
|
281
|
+
that happens, ship `--target standalone` instead — same app, packaged as an
|
|
282
|
+
installer, and it does not trigger the heuristic.
|
|
283
|
+
|
|
284
|
+
### What `zagred build` does
|
|
285
|
+
|
|
286
|
+
```
|
|
287
|
+
1. npm run build (in web/, if [ui] build is set)
|
|
288
|
+
2. write .zagred/build/<name>.py static import of your App + boot()
|
|
289
|
+
3. nuitka --include-data-dir=web/dist=ui
|
|
290
|
+
4. wrap the .dist folder into the installer for the chosen target
|
|
291
|
+
5. write everything to dist/<target>/
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
Step 2 imports your App statically on purpose. Nuitka bundles only what it can
|
|
295
|
+
see through imports, so a dynamic `importlib.import_module("my_app.app")` would
|
|
296
|
+
leave your package out and crash at startup.
|
|
297
|
+
|
|
298
|
+
### Keeping the machine usable
|
|
299
|
+
|
|
300
|
+
Compilation is CPU-hungry. Default is every core.
|
|
301
|
+
|
|
302
|
+
```toml
|
|
303
|
+
[build]
|
|
304
|
+
jobs = 2 # at most 2 parallel C compiles
|
|
305
|
+
low_memory = true # optional: fewer jobs + lighter compiler options
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
```bash
|
|
309
|
+
uv run zagred build --jobs 2 # one-off override
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
| `--jobs` | Effect |
|
|
313
|
+
|---|---|
|
|
314
|
+
| unset | every core (Nuitka default) |
|
|
315
|
+
| `2` | 2 parallel C compiles |
|
|
316
|
+
| `-1` | CPU count minus 1 |
|
|
317
|
+
| `1` | serial |
|
|
318
|
+
|
|
319
|
+
Measured on an 8-core laptop: default ≈ 6m47s wall / 39m30s CPU (~6 cores busy);
|
|
320
|
+
`--jobs=2` ≈ 11 min with load ~3.
|
|
321
|
+
|
|
322
|
+
### `--debug` is for development only
|
|
323
|
+
|
|
324
|
+
`zagred build --debug` bakes `debug=True` into the compiled app. pywebview then
|
|
325
|
+
enables developer extras, opens the WebView inspector, and stops suppressing the
|
|
326
|
+
right-click context menu. Fine for testing a packaged build, never for release.
|
|
327
|
+
The command warns when you do it.
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
## How it works
|
|
332
|
+
|
|
333
|
+
Every JS → Python call goes through one door:
|
|
334
|
+
|
|
335
|
+
```
|
|
336
|
+
zagred.call(name, args)
|
|
337
|
+
→ window.pywebview.api.call(name, args) # the only exposed method
|
|
338
|
+
→ Bridge.call() → your @expose function
|
|
339
|
+
→ {ok: true, data} | {ok: false, error: {code, message}}
|
|
340
|
+
→ resolved value, or a thrown Error(code, message)
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
Your code never touches `window.pywebview`. The `window.zagred` shim is injected
|
|
344
|
+
on `before_load` from a worker thread — calling `evaluate_js` directly on that
|
|
345
|
+
blocking event deadlocks the GTK main loop and leaves the window white.
|
|
346
|
+
|
|
347
|
+
## Requirements
|
|
348
|
+
|
|
349
|
+
| OS | Needs |
|
|
350
|
+
|---|---|
|
|
351
|
+
| Linux | `libwebkit2gtk-4.1`, `python3-gi`, venv created with `--system-site-packages`; `patchelf` comes with `--group build` |
|
|
352
|
+
| Windows | WebView2 runtime (preinstalled on Windows 11) |
|
|
353
|
+
| macOS | nothing extra |
|
|
354
|
+
|
|
355
|
+
Linux uses the GTK backend on purpose: Qt adds 80–150 MB to every build.
|
|
356
|
+
`appimagetool` is downloaded and cached by Nuitka when missing.
|
|
357
|
+
Cross-compiling is impossible — build each OS on that OS, or in CI.
|
|
358
|
+
|
|
359
|
+
## Troubleshooting
|
|
360
|
+
|
|
361
|
+
**`uv sync` says zagred is not found in the registry** — not on PyPI yet. Projects
|
|
362
|
+
created by `zagred new` get a `[tool.uv.sources]` entry pointing at the local
|
|
363
|
+
checkout. If you copied a project from elsewhere:
|
|
364
|
+
|
|
365
|
+
```toml
|
|
366
|
+
[tool.uv.sources]
|
|
367
|
+
zagred = { path = "/path/to/zagred", editable = true }
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
**`zagred dev` says GTK/QT cannot be loaded** — the bindings exist on the system
|
|
371
|
+
but this interpreter cannot see them:
|
|
372
|
+
|
|
373
|
+
```bash
|
|
374
|
+
sudo apt install python3-gi gir1.2-webkit2-4.1
|
|
375
|
+
rm -rf .venv
|
|
376
|
+
uv venv --python /usr/bin/python3 --system-site-packages
|
|
377
|
+
uv sync
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
**`uv` warns that `VIRTUAL_ENV` does not match** — you activated another project's
|
|
381
|
+
venv. `deactivate`; `uv run` picks the right one.
|
|
382
|
+
|
|
383
|
+
**`zagred doctor` says nuitka MISSING** — `uv sync --group build`.
|
|
384
|
+
|
|
385
|
+
**A built app opens the inspector** — it was built with `--debug`. Rebuild without it.
|
|
386
|
+
|
|
387
|
+
---
|
|
388
|
+
|
|
389
|
+
## Milestones
|
|
390
|
+
|
|
391
|
+
### ✅ M0 — Spike
|
|
392
|
+
Bridge roundtrip, error envelope, async commands, events via `run_js`,
|
|
393
|
+
working `--mode=onefile` binary, assets resolved from the onefile temp dir.
|
|
394
|
+
|
|
395
|
+
### ✅ M1 — Runtime
|
|
396
|
+
`zagred.toml` parser, `@expose`, JS shim, event bus, scaffolds that run,
|
|
397
|
+
`zagred dev`, actionable errors instead of raw tracebacks, 38 unit tests.
|
|
398
|
+
|
|
399
|
+
### ✅ M2 — uv toolchain
|
|
400
|
+
Generated projects are uv projects; zagred resolved from the local checkout via
|
|
401
|
+
`[tool.uv.sources]` (editable); `.python-version` pinned to 3.12; `build`
|
|
402
|
+
dependency group carries nuitka + patchelf; `zagred new` prints next steps.
|
|
403
|
+
|
|
404
|
+
### ✅ M3 — Build
|
|
405
|
+
Six Nuitka targets, asset resolver for dev/standalone/onefile, per-target output
|
|
406
|
+
directories, `--jobs` / `low_memory`, `zagred doctor`, artifacts named after the app.
|
|
407
|
+
|
|
408
|
+
### 🟡 M4 — Ecosystem
|
|
409
|
+
- [x] `zagred new` wizard, `--template` / `--lang` / `-y`
|
|
410
|
+
- [x] Templates: vanilla, React, Vue, Solid × JS/TS
|
|
411
|
+
- [x] Vite HMR dev path, verified against a real React project
|
|
412
|
+
- [x] Typed bridge for TypeScript
|
|
413
|
+
- [x] Full window option set, lifecycle hooks, window control methods
|
|
414
|
+
- [ ] Multi-window (one `Bridge` per window)
|
|
415
|
+
- [ ] Plugin registry via `entry_points`
|
|
416
|
+
- [ ] `zagred add ui <kit>`
|
|
417
|
+
|
|
418
|
+
### ⬜ M5 — Security
|
|
419
|
+
- [ ] `capabilities.toml` + permission check before dispatch
|
|
420
|
+
- [ ] Default CSP
|
|
421
|
+
- [ ] Path traversal guard on the resource resolver
|
|
422
|
+
|
|
423
|
+
### ⬜ M6 — Release
|
|
424
|
+
- [ ] Publish to PyPI
|
|
425
|
+
- [ ] AppImage test on a clean VM (GTK typelibs)
|
|
426
|
+
- [ ] CI matrix (GitHub Actions, `Nuitka-Action`)
|
|
427
|
+
- [ ] Docs site, example apps, stable semver
|
|
428
|
+
|
|
429
|
+
---
|
|
430
|
+
|
|
431
|
+
## Fixed so far
|
|
432
|
+
|
|
433
|
+
22 bugs found and fixed while building this. The ones that would have shipped:
|
|
434
|
+
|
|
435
|
+
| # | Bug |
|
|
436
|
+
|---|---|
|
|
437
|
+
| B4 | Blank window from a built binary — onefile assets resolved from the wrong directory |
|
|
438
|
+
| B7 | `uv sync` failed in every generated project — zagred was declared from PyPI |
|
|
439
|
+
| B11 | GTK not visible from uv's Python — venv must use `--system-site-packages` |
|
|
440
|
+
| B12 | White window for seconds — `evaluate_js` deadlocked the blocking `before_load` event |
|
|
441
|
+
| B15 | Terminal broken after closing the window — orphaned `vite` held the TTY |
|
|
442
|
+
| B19 | Packaged app crashed: `No module named my_app` — dynamic import invisible to Nuitka |
|
|
443
|
+
| B21 | Building one target deleted another's artifacts — shared output dir |
|
|
444
|
+
| B22 | Release AppImage opened the WebView inspector — `--debug` baked into the build |
|
|
445
|
+
|
|
446
|
+
Full list with causes and fixes: `git log` and the test suite in `tests/test_core.py`.
|
|
447
|
+
|
|
448
|
+
---
|
|
449
|
+
|
|
450
|
+
## Development
|
|
451
|
+
|
|
452
|
+
```bash
|
|
453
|
+
uv sync --extra build
|
|
454
|
+
uv run python -m unittest discover -s tests # 38 tests
|
|
455
|
+
uv run zagred doctor
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
## License
|
|
459
|
+
|
|
460
|
+
MIT — see [LICENSE](LICENSE). Use it, fork it, ship it in commercial closed-source
|
|
461
|
+
apps; just keep the copyright notice.
|
|
462
|
+
|
|
463
|
+
Apps built with `zagred build` compile Zagred, pywebview, bottle, proxy_tools and
|
|
464
|
+
CPython into the binary, so your distribution should also include
|
|
465
|
+
[THIRD-PARTY-NOTICES.md](THIRD-PARTY-NOTICES.md) and the PSF licence (shipped with
|
|
466
|
+
Python). Nuitka is AGPL-3.0, but its Runtime Library Exception explicitly allows
|
|
467
|
+
closed-source output, and Zagred never bundles it.
|