wysebee 0.2.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.
wysebee-0.2.0/PKG-INFO ADDED
@@ -0,0 +1,185 @@
1
+ Metadata-Version: 2.4
2
+ Name: wysebee
3
+ Version: 0.2.0
4
+ Summary: A simple cross-platform framework for your AI project.
5
+ Author-email: Jeff Xu <zxu@wysebee.com>
6
+ Requires-Python: >=3.9
7
+ Description-Content-Type: text/markdown
8
+ License-Expression: MIT
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Dist: pyside6>=6.9.0
12
+ Requires-Dist: Jinja2>=3.1.2
13
+ Requires-Dist: typer>=0.15.2
14
+ Requires-Dist: colorama>=0.4.6
15
+ Requires-Dist: watchdog==6.0.0
16
+ Project-URL: Homepage, https://github.com/wysebee/wysebee
17
+ Project-URL: Issues, https://github.com/wysebee/wysebee/issues
18
+
19
+ # Wysebee
20
+
21
+ **Wysebee** is a cross-platform application framework for building modern apps with web-technology UIs and Python backends. Write your frontend in HTML/CSS/JS (React, Vanilla, or TypeScript via Vite) and your backend in Python — ship it as a native desktop app powered by **PySide (Qt)**, or as a **FastAPI** web service, from a single project layout.
22
+
23
+ ---
24
+
25
+ ## Features
26
+
27
+ - **One project, two targets.** Generate a desktop (PySide/Qt) or web (FastAPI) app with the same `wyse init` command.
28
+ - **Vite-powered frontends.** First-class support for `react`, `react-ts`, `vanilla`, and `vanilla-ts` templates.
29
+ - **Python ↔ JS bridge.** Desktop apps come wired with a `QWebChannel` bridge so your frontend can call Python methods and receive signals.
30
+ - **Native app menus.** Declare your menu bar in `menu/menu.json`; Wysebee wires each item to a backend method for you.
31
+ - **Drop-in backend.** Subclass `WysebeeBackend` and add `@Slot()` methods — they're instantly callable from JavaScript.
32
+ - **Hot reload in dev.** `wyse dev` watches your UI source and rebuilds/reloads the browser view automatically.
33
+
34
+ ---
35
+
36
+ ## Installation
37
+
38
+ ```bash
39
+ pip install wysebee
40
+ ```
41
+
42
+ This installs the `wyse` CLI alongside the `wysebee` Python package.
43
+
44
+ ---
45
+
46
+ ## Quick start
47
+
48
+ ### 1. Create a new app
49
+
50
+ ```bash
51
+ wyse init my-app
52
+ ```
53
+
54
+ You'll be prompted to choose `desktop` or `web`. To select a Vite template other than the default React:
55
+
56
+ ```bash
57
+ wyse init my-app --template vanilla-ts
58
+ ```
59
+
60
+ Supported templates: `react` (default), `react-ts`, `vanilla`, `vanilla-ts`.
61
+
62
+ ### 2. Run it
63
+
64
+ ```bash
65
+ cd my-app
66
+ python main.py
67
+ ```
68
+
69
+ For live reload during development:
70
+
71
+ ```bash
72
+ wyse dev
73
+ ```
74
+
75
+ ### 3. Build the UI
76
+
77
+ ```bash
78
+ wyse build --ui
79
+ ```
80
+
81
+ ---
82
+
83
+ ## Generated project layout
84
+
85
+ ### Desktop app
86
+
87
+ ```
88
+ my-app/
89
+ ├── main.py # Entry point (QApplication + Wysebee + WysebeeAppMenu)
90
+ ├── menu/
91
+ │ └── menu.json # Declarative menu bar wired to backend methods
92
+ ├── src/
93
+ │ ├── __init__.py
94
+ │ └── backend.py # MyBackend(WysebeeBackend) — customize Python-side logic
95
+ ├── ui/ # Vite project (your frontend)
96
+ │ ├── index.html
97
+ │ ├── src/
98
+ │ └── templates/ # Built output loaded by the desktop window
99
+ └── requirements.txt
100
+ ```
101
+
102
+ ### Web app
103
+
104
+ ```
105
+ my-app/
106
+ ├── main.py # FastAPI app serving the built UI
107
+ ├── ui/ # Vite project
108
+ │ └── templates/ # Built output mounted as static files
109
+ └── requirements.txt
110
+ ```
111
+
112
+ ---
113
+
114
+ ## Wiring a native menu (desktop)
115
+
116
+ `menu/menu.json` maps menu items to methods on your backend by name:
117
+
118
+ ```json
119
+ [
120
+ {
121
+ "label": "File",
122
+ "submenu": [
123
+ { "label": "Open", "action": "on_open" },
124
+ { "label": "Save", "action": "on_save" },
125
+ { "separator": true },
126
+ { "label": "Exit", "action": "on_exit" }
127
+ ]
128
+ }
129
+ ]
130
+ ```
131
+
132
+ Implement those methods on your backend:
133
+
134
+ ```python
135
+ from PySide6.QtWidgets import QApplication, QFileDialog
136
+ from PySide6.QtCore import Slot
137
+ from wysebee import WysebeeBackend
138
+
139
+ class MyBackend(WysebeeBackend):
140
+ @Slot()
141
+ def on_open(self):
142
+ file_path, _ = QFileDialog.getOpenFileName(None, "Open File")
143
+ # ...
144
+
145
+ @Slot()
146
+ def on_exit(self):
147
+ QApplication.quit()
148
+ ```
149
+
150
+ ---
151
+
152
+ ## Calling Python from JavaScript (desktop)
153
+
154
+ Wysebee exposes your backend to the frontend through `QWebChannel`. The generated `index.html` initializes it and attaches the backend to `window.wysebee`:
155
+
156
+ ```js
157
+ window.wysebee.sendMessage("Hello from the frontend!");
158
+ ```
159
+
160
+ Any `@Slot(str, result=str)` method on your backend is callable this way.
161
+
162
+ ---
163
+
164
+ ## CLI reference
165
+
166
+ | Command | Description |
167
+ | --- | --- |
168
+ | `wyse init <name> [--template ...]` | Scaffold a new desktop or web app. |
169
+ | `wyse dev` | Build the UI and run the app in development mode (hot reload). |
170
+ | `wyse build --ui` | Build only the frontend (`npm run build` inside `ui/`). |
171
+
172
+ ---
173
+
174
+ ## Links
175
+
176
+ - Homepage: [wysebee.io](https://wysebee.io)
177
+ - Source: [github.com/wysebee/wysebee-io](https://github.com/wysebee/wysebee-io)
178
+ - Issues: [github.com/wysebee/wysebee-io/issues](https://github.com/wysebee/wysebee-io/issues)
179
+
180
+ ---
181
+
182
+ ## License
183
+
184
+ MIT
185
+
@@ -0,0 +1,166 @@
1
+ # Wysebee
2
+
3
+ **Wysebee** is a cross-platform application framework for building modern apps with web-technology UIs and Python backends. Write your frontend in HTML/CSS/JS (React, Vanilla, or TypeScript via Vite) and your backend in Python — ship it as a native desktop app powered by **PySide (Qt)**, or as a **FastAPI** web service, from a single project layout.
4
+
5
+ ---
6
+
7
+ ## Features
8
+
9
+ - **One project, two targets.** Generate a desktop (PySide/Qt) or web (FastAPI) app with the same `wyse init` command.
10
+ - **Vite-powered frontends.** First-class support for `react`, `react-ts`, `vanilla`, and `vanilla-ts` templates.
11
+ - **Python ↔ JS bridge.** Desktop apps come wired with a `QWebChannel` bridge so your frontend can call Python methods and receive signals.
12
+ - **Native app menus.** Declare your menu bar in `menu/menu.json`; Wysebee wires each item to a backend method for you.
13
+ - **Drop-in backend.** Subclass `WysebeeBackend` and add `@Slot()` methods — they're instantly callable from JavaScript.
14
+ - **Hot reload in dev.** `wyse dev` watches your UI source and rebuilds/reloads the browser view automatically.
15
+
16
+ ---
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ pip install wysebee
22
+ ```
23
+
24
+ This installs the `wyse` CLI alongside the `wysebee` Python package.
25
+
26
+ ---
27
+
28
+ ## Quick start
29
+
30
+ ### 1. Create a new app
31
+
32
+ ```bash
33
+ wyse init my-app
34
+ ```
35
+
36
+ You'll be prompted to choose `desktop` or `web`. To select a Vite template other than the default React:
37
+
38
+ ```bash
39
+ wyse init my-app --template vanilla-ts
40
+ ```
41
+
42
+ Supported templates: `react` (default), `react-ts`, `vanilla`, `vanilla-ts`.
43
+
44
+ ### 2. Run it
45
+
46
+ ```bash
47
+ cd my-app
48
+ python main.py
49
+ ```
50
+
51
+ For live reload during development:
52
+
53
+ ```bash
54
+ wyse dev
55
+ ```
56
+
57
+ ### 3. Build the UI
58
+
59
+ ```bash
60
+ wyse build --ui
61
+ ```
62
+
63
+ ---
64
+
65
+ ## Generated project layout
66
+
67
+ ### Desktop app
68
+
69
+ ```
70
+ my-app/
71
+ ├── main.py # Entry point (QApplication + Wysebee + WysebeeAppMenu)
72
+ ├── menu/
73
+ │ └── menu.json # Declarative menu bar wired to backend methods
74
+ ├── src/
75
+ │ ├── __init__.py
76
+ │ └── backend.py # MyBackend(WysebeeBackend) — customize Python-side logic
77
+ ├── ui/ # Vite project (your frontend)
78
+ │ ├── index.html
79
+ │ ├── src/
80
+ │ └── templates/ # Built output loaded by the desktop window
81
+ └── requirements.txt
82
+ ```
83
+
84
+ ### Web app
85
+
86
+ ```
87
+ my-app/
88
+ ├── main.py # FastAPI app serving the built UI
89
+ ├── ui/ # Vite project
90
+ │ └── templates/ # Built output mounted as static files
91
+ └── requirements.txt
92
+ ```
93
+
94
+ ---
95
+
96
+ ## Wiring a native menu (desktop)
97
+
98
+ `menu/menu.json` maps menu items to methods on your backend by name:
99
+
100
+ ```json
101
+ [
102
+ {
103
+ "label": "File",
104
+ "submenu": [
105
+ { "label": "Open", "action": "on_open" },
106
+ { "label": "Save", "action": "on_save" },
107
+ { "separator": true },
108
+ { "label": "Exit", "action": "on_exit" }
109
+ ]
110
+ }
111
+ ]
112
+ ```
113
+
114
+ Implement those methods on your backend:
115
+
116
+ ```python
117
+ from PySide6.QtWidgets import QApplication, QFileDialog
118
+ from PySide6.QtCore import Slot
119
+ from wysebee import WysebeeBackend
120
+
121
+ class MyBackend(WysebeeBackend):
122
+ @Slot()
123
+ def on_open(self):
124
+ file_path, _ = QFileDialog.getOpenFileName(None, "Open File")
125
+ # ...
126
+
127
+ @Slot()
128
+ def on_exit(self):
129
+ QApplication.quit()
130
+ ```
131
+
132
+ ---
133
+
134
+ ## Calling Python from JavaScript (desktop)
135
+
136
+ Wysebee exposes your backend to the frontend through `QWebChannel`. The generated `index.html` initializes it and attaches the backend to `window.wysebee`:
137
+
138
+ ```js
139
+ window.wysebee.sendMessage("Hello from the frontend!");
140
+ ```
141
+
142
+ Any `@Slot(str, result=str)` method on your backend is callable this way.
143
+
144
+ ---
145
+
146
+ ## CLI reference
147
+
148
+ | Command | Description |
149
+ | --- | --- |
150
+ | `wyse init <name> [--template ...]` | Scaffold a new desktop or web app. |
151
+ | `wyse dev` | Build the UI and run the app in development mode (hot reload). |
152
+ | `wyse build --ui` | Build only the frontend (`npm run build` inside `ui/`). |
153
+
154
+ ---
155
+
156
+ ## Links
157
+
158
+ - Homepage: [wysebee.io](https://wysebee.io)
159
+ - Source: [github.com/wysebee/wysebee-io](https://github.com/wysebee/wysebee-io)
160
+ - Issues: [github.com/wysebee/wysebee-io/issues](https://github.com/wysebee/wysebee-io/issues)
161
+
162
+ ---
163
+
164
+ ## License
165
+
166
+ MIT
@@ -0,0 +1,38 @@
1
+ [project]
2
+ name = "wysebee"
3
+ version = "0.2.0"
4
+ authors = [
5
+ { name="Jeff Xu", email="zxu@wysebee.com" },
6
+ ]
7
+ description = "A simple cross-platform framework for your AI project."
8
+ readme = "README.md"
9
+ requires-python = ">=3.9"
10
+ classifiers = [
11
+ "Programming Language :: Python :: 3",
12
+ "Operating System :: OS Independent",
13
+ ]
14
+ license = "MIT"
15
+
16
+ dependencies = [
17
+ "pyside6>=6.9.0",
18
+ "Jinja2>=3.1.2",
19
+ "typer>=0.15.2",
20
+ "colorama>=0.4.6",
21
+ "watchdog==6.0.0"
22
+ ]
23
+
24
+ [project.urls]
25
+ Homepage = "https://github.com/wysebee/wysebee"
26
+ Issues = "https://github.com/wysebee/wysebee/issues"
27
+
28
+ [build-system]
29
+ requires = ["flit_core<4"]
30
+ build-backend = "flit_core.buildapi"
31
+
32
+ [tool.flit.module]
33
+ name = "wysebee"
34
+
35
+ [project.scripts]
36
+ wyse = "wysebee.cli.main:app"
37
+ wyse-web = "wysebee.cli.main:app_web"
38
+ wyse-desktop = "wysebee.cli.main:app_desktop"
@@ -0,0 +1,9 @@
1
+ from .desktop.wysebee import Wysebee
2
+ from .desktop.wysebee_webview import WysebeeWebView
3
+ from .desktop.wysebee_webengine_page import WysebeeWebEnginePage
4
+ from .desktop.wysebee_backend import WysebeeBackend
5
+ from .desktop.singleton import singleton
6
+ from .desktop.temp_helper import TempFileHelper
7
+ from .desktop.wysebee_webpopup import WysebeeWebPopup
8
+ from .desktop.wysebee_app_menu import WysebeeAppMenu
9
+ from .desktop.filesystem import *
@@ -0,0 +1,31 @@
1
+ import shutil
2
+ import subprocess
3
+ from pathlib import Path
4
+
5
+ from colorama import Fore
6
+
7
+
8
+ def ensure_uv_available() -> bool:
9
+ """Return True if `uv` is on PATH, otherwise print an error and return False."""
10
+ if shutil.which("uv") is None:
11
+ print(
12
+ Fore.RED
13
+ + "uv is not installed or not on PATH. Install it from https://docs.astral.sh/uv/ and re-run."
14
+ )
15
+ return False
16
+ return True
17
+
18
+
19
+ def bootstrap_uv_project(project_dir: Path) -> bool:
20
+ """Run `uv venv` and `uv sync` inside the given project directory."""
21
+ if not ensure_uv_available():
22
+ return False
23
+ try:
24
+ print(Fore.YELLOW + f"Creating virtualenv in {project_dir} with uv...")
25
+ subprocess.run(["uv", "venv"], cwd=project_dir, check=True)
26
+ print(Fore.YELLOW + f"Installing dependencies in {project_dir} with uv...")
27
+ subprocess.run(["uv", "sync"], cwd=project_dir, check=True)
28
+ return True
29
+ except subprocess.CalledProcessError as e:
30
+ print(Fore.RED + f"uv bootstrap failed: {e}")
31
+ return False
@@ -0,0 +1,74 @@
1
+ """Shared Vite setup helpers: fresh vite.config.js + multi-page entries."""
2
+
3
+ import subprocess
4
+ from pathlib import Path
5
+
6
+ from colorama import Fore
7
+
8
+ from .templates import (
9
+ render_page_html,
10
+ render_react_entry,
11
+ render_vanilla_settings_entry,
12
+ )
13
+
14
+
15
+ def _vite_config(template: str, platform: str) -> str:
16
+ """Return a fresh vite.config.js body for the given template + platform.
17
+
18
+ - Desktop uses base './' so the PySide WebView can load assets via file:// URLs.
19
+ - Web uses base '/' so both `/` and `/settings` can share absolute asset paths.
20
+ """
21
+ base = "./" if platform == "desktop" else "/"
22
+ is_react = template in ("react", "react-ts")
23
+
24
+ plugin_import = "import react from '@vitejs/plugin-react'\n" if is_react else ""
25
+ plugins_line = " plugins: [react()],\n" if is_react else ""
26
+
27
+ return f"""import {{ defineConfig }} from 'vite'
28
+ {plugin_import}
29
+ // https://vitejs.dev/config/
30
+ export default defineConfig({{
31
+ {plugins_line} base: '{base}',
32
+ build: {{
33
+ outDir: 'templates',
34
+ emptyOutDir: true,
35
+ rollupOptions: {{
36
+ input: {{
37
+ main: 'index.html',
38
+ settings: 'settings.html',
39
+ }},
40
+ }},
41
+ }},
42
+ }})
43
+ """
44
+
45
+
46
+ def setup_vite_pages(template: str, platform: str) -> None:
47
+ """Write vite.config.js, index.html, settings.html, and entry modules.
48
+
49
+ Assumes the current working directory is the `ui/` folder containing the
50
+ just-initialized Vite project.
51
+ """
52
+ Path("vite.config.js").write_text(_vite_config(template, platform))
53
+ print(Fore.YELLOW + f"Wrote vite.config.js (multi-page, base={'./'if platform == 'desktop' else '/'})")
54
+
55
+ Path("index.html").write_text(render_page_html(template, platform, "index"))
56
+ Path("settings.html").write_text(render_page_html(template, platform, "settings"))
57
+ print(Fore.YELLOW + f"Wrote index.html and settings.html for {platform} ({template})")
58
+
59
+ if template in ("react", "react-ts"):
60
+ ext = "tsx" if template == "react-ts" else "jsx"
61
+ Path(f"src/main.{ext}").write_text(render_react_entry(template, "index"))
62
+ Path(f"src/settings.{ext}").write_text(render_react_entry(template, "settings"))
63
+ print(Fore.YELLOW + f"Wrote src/main.{ext} and src/settings.{ext} with HashRouter")
64
+ elif template in ("vanilla", "vanilla-ts"):
65
+ ext = "ts" if template == "vanilla-ts" else "js"
66
+ Path(f"src/settings.{ext}").write_text(render_vanilla_settings_entry(template))
67
+ print(Fore.YELLOW + f"Wrote src/settings.{ext}")
68
+
69
+
70
+ def install_router_if_react(template: str) -> None:
71
+ """Install react-router-dom if the template is a react template."""
72
+ if template in ("react", "react-ts"):
73
+ print(Fore.YELLOW + "Installing react-router-dom...")
74
+ subprocess.run(["npm", "install", "react-router-dom"], check=True)
@@ -0,0 +1,188 @@
1
+ import json
2
+ import os
3
+ import subprocess
4
+ from pathlib import Path
5
+ from typing import Optional
6
+
7
+ from colorama import Fore
8
+
9
+ from ._uv import bootstrap_uv_project
10
+ from ._vite import install_router_if_react, setup_vite_pages
11
+
12
+
13
+ def _generate_ui_template(name: str, ui_dir: str, template: str):
14
+ # Change to UI directory and initialize vite
15
+ try:
16
+ print(Fore.YELLOW + f"Setting up Vite in UI folder...")
17
+ original_dir = os.getcwd()
18
+ os.chdir(ui_dir)
19
+ if template:
20
+ subprocess.run(
21
+ ["npm", "create", "vite@latest", ".", "--", "--template", template],
22
+ input="n\n",
23
+ text=True,
24
+ check=True,
25
+ )
26
+ else:
27
+ subprocess.run(
28
+ ["npm", "create", "vite@latest", "."],
29
+ input="n\n",
30
+ text=True,
31
+ check=True,
32
+ )
33
+
34
+ subprocess.run(["npm", "install"], check=True)
35
+ install_router_if_react(template)
36
+
37
+ # Write fresh vite.config.js + index/settings pages + entry modules
38
+ setup_vite_pages(template, platform="desktop")
39
+
40
+ subprocess.run(["npm", "run", "build"], check=True)
41
+ os.chdir(original_dir)
42
+ print(Fore.YELLOW + f"Successfully set up Vite in {name}/ui!")
43
+ except subprocess.CalledProcessError as e:
44
+ print(Fore.RED + f"Error running npm create vite: {e}")
45
+ os.chdir(original_dir)
46
+ except Exception as e:
47
+ print(Fore.RED + f"Unexpected error: {e}")
48
+ os.chdir(original_dir)
49
+
50
+
51
+ def _generate_app_menu_template(desktop_dir: Path):
52
+ """Create a menu folder with a sample menu.json inside the desktop folder."""
53
+ menu_dir = desktop_dir / "menu"
54
+ menu_dir.mkdir(exist_ok=True)
55
+ menu_file = menu_dir / "menu.json"
56
+ menu_file.write_text(
57
+ json.dumps(
58
+ [
59
+ {
60
+ "label": "File",
61
+ "submenu": [
62
+ {"label": "Open", "action": "on_open"},
63
+ {"label": "Save", "action": "on_save"},
64
+ {"separator": True},
65
+ {"label": "Exit", "action": "on_exit"},
66
+ ],
67
+ },
68
+ ],
69
+ indent=2,
70
+ )
71
+ )
72
+ print(Fore.YELLOW + f"Created menu/menu.json in {desktop_dir}")
73
+
74
+
75
+ def generate_desktop_app(name: str, app_dir: Path, ui_dir: Path, template: str, project_name: Optional[str] = None) -> None:
76
+ """Generate a complete desktop app: desktop/{main.py, src/, menu/, pyproject.toml}, UI at root."""
77
+ if project_name is None:
78
+ project_name = name
79
+ # Create the desktop/ subfolder that holds all Python files
80
+ desktop_dir = app_dir / "desktop"
81
+ desktop_dir.mkdir(exist_ok=True)
82
+ print(Fore.YELLOW + f"Created desktop folder in {name}")
83
+
84
+ # Create the src subfolder inside desktop/
85
+ src_dir = desktop_dir / "src"
86
+ src_dir.mkdir(exist_ok=True)
87
+ print(Fore.YELLOW + f"Created desktop/src folder in {name}")
88
+
89
+ # Generate menu template inside desktop/
90
+ _generate_app_menu_template(desktop_dir)
91
+
92
+ # Create desktop/src/__init__.py
93
+ init_file = src_dir / "__init__.py"
94
+ init_file.write_text("")
95
+
96
+ # Create desktop/src/backend.py
97
+ backend_file = src_dir / "backend.py"
98
+ backend_file.write_text(
99
+ """import logging
100
+ from PySide6.QtWidgets import QApplication, QFileDialog
101
+ from PySide6.QtCore import Slot
102
+ from wysebee import WysebeeBackend
103
+
104
+ logger = logging.getLogger("wysebee")
105
+
106
+
107
+ class MyBackend(WysebeeBackend):
108
+ def __init__(self, parent):
109
+ super().__init__(parent)
110
+
111
+ @Slot()
112
+ def on_open(self):
113
+ file_path, _ = QFileDialog.getOpenFileName(None, "Open File")
114
+ if file_path:
115
+ logger.info(f"Opened file: {file_path}")
116
+
117
+ @Slot()
118
+ def on_save(self):
119
+ file_path, _ = QFileDialog.getSaveFileName(None, "Save File")
120
+ if file_path:
121
+ logger.info(f"Saved file: {file_path}")
122
+
123
+ @Slot()
124
+ def on_exit(self):
125
+ logger.info("Exiting application")
126
+ QApplication.quit()
127
+ """
128
+ )
129
+ print(Fore.YELLOW + f"Created desktop/src/backend.py in {name}")
130
+
131
+ # Create desktop/main.py for desktop app. Paths are relative to this file:
132
+ # menu -> menu/menu.json (same dir)
133
+ # ui -> ../ui/templates/index.html (ui lives at project root)
134
+ main_file = desktop_dir / "main.py"
135
+ main_file.write_text(
136
+ """
137
+ import sys
138
+ import os
139
+ from PySide6.QtWidgets import QApplication
140
+ from wysebee import Wysebee, WysebeeAppMenu
141
+ from src.backend import MyBackend
142
+
143
+ def main():
144
+ app = QApplication(sys.argv)
145
+ wysebee = Wysebee(app)
146
+ backend = MyBackend(app)
147
+ browser = wysebee.initialize_window(width=1280, height=800, backend=backend)
148
+ menu_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "menu/menu.json"))
149
+ app_menu = WysebeeAppMenu(browser, backend, menu_path)
150
+ html_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "../ui/templates/index.html"))
151
+ wysebee.launch(html_path)
152
+ app_menu.show()
153
+
154
+ sys.exit(app.exec())
155
+
156
+ if __name__ == "__main__":
157
+ main()
158
+ """
159
+ )
160
+ print(Fore.YELLOW + f"Created desktop/main.py file in {name}")
161
+
162
+ # Write desktop/pyproject.toml (uv-managed)
163
+ pyproject_file = desktop_dir / "pyproject.toml"
164
+ pyproject_file.write_text(
165
+ f"""[project]
166
+ name = "{project_name}-desktop"
167
+ version = "0.1.0"
168
+ requires-python = ">=3.10"
169
+ dependencies = [
170
+ "PySide6",
171
+ "Wysebee",
172
+ ]
173
+ """
174
+ )
175
+ print(Fore.YELLOW + f"Created desktop/pyproject.toml in {name}")
176
+
177
+ # Build the UI first (ui/ stays at project root)
178
+ _generate_ui_template(name, ui_dir, template)
179
+
180
+ # Bootstrap the uv venv + install dependencies
181
+ if bootstrap_uv_project(desktop_dir):
182
+ print(Fore.GREEN + f"Now you are ready!")
183
+ print(Fore.GREEN + f" To run the app, use:")
184
+ if name != ".":
185
+ print(Fore.GREEN + f" cd {name}/desktop")
186
+ else:
187
+ print(Fore.GREEN + f" cd desktop")
188
+ print(Fore.GREEN + f" uv run python main.py")