Skip to content

Commit f8b3cb3

Browse files
committed
Create windows .exe packager
1 parent 9754154 commit f8b3cb3

6 files changed

Lines changed: 130 additions & 1 deletion

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ env/
1616
dist/
1717
build/
1818
.eggs/
19+
*.spec.bak
1920

2021
# Environment & secrets
2122
.env

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,21 @@ cursor-chat-browser-python/
160160
└── workspace.html
161161
```
162162

163+
## Desktop App (Windows .exe)
164+
165+
You can package the browser as a standalone desktop application with its own window - no Python installation required on the target machine.
166+
167+
### Build
168+
169+
```bash
170+
pip install pywebview pyinstaller
171+
pyinstaller cursor-browser.spec
172+
```
173+
174+
This produces `dist/CursorChatBrowser/` containing `CursorChatBrowser.exe` and its supporting files. Move the folder anywhere you like, then pin the `.exe` to Start or the taskbar.
175+
176+
The desktop app uses [pywebview](https://pywebview.flowrl.com/) to render the Flask UI inside a native window via Edge WebView2 (pre-installed on Windows 10/11). No HTTP server or port is opened - pywebview calls the WSGI app directly in-process.
177+
163178
## Technology Stack
164179

165180
- **Backend:** Python 3, Flask

app.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
from the Cursor editor's AI chat feature.
55
"""
66

7+
import sys
78
from datetime import datetime
9+
from pathlib import Path
810

911
from flask import Flask, render_template, send_from_directory
1012

@@ -18,8 +20,24 @@
1820
from utils.exclusion_rules import resolve_exclusion_rules_path, load_rules
1921

2022

23+
def _get_base_path():
24+
"""Return the directory that contains templates/ and static/.
25+
26+
In a PyInstaller bundle the files live under sys._MEIPASS;
27+
otherwise they sit next to this source file.
28+
"""
29+
if getattr(sys, "frozen", False):
30+
return Path(sys._MEIPASS)
31+
return Path(__file__).resolve().parent
32+
33+
2134
def create_app(exclusion_rules_path=None):
22-
app = Flask(__name__, static_folder="static", template_folder="templates")
35+
base = _get_base_path()
36+
app = Flask(
37+
__name__,
38+
static_folder=str(base / "static"),
39+
template_folder=str(base / "templates"),
40+
)
2341
app.config["JSON_SORT_KEYS"] = False
2442

2543
# Exclusion rules: optional path (CLI or default ~/.cursor-chat-browser/exclusion-rules.txt).

cursor-browser.spec

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# PyInstaller spec for Cursor Chat Browser (--onedir mode).
2+
# Build: pyinstaller cursor-browser.spec
3+
# Output: dist/CursorChatBrowser/CursorChatBrowser.exe
4+
5+
import sys
6+
from pathlib import Path
7+
8+
block_cipher = None
9+
src = Path(SPECPATH)
10+
11+
a = Analysis(
12+
[str(src / "launcher.py")],
13+
pathex=[str(src)],
14+
binaries=[],
15+
datas=[
16+
(str(src / "templates"), "templates"),
17+
(str(src / "static"), "static"),
18+
],
19+
hiddenimports=[
20+
"api.workspaces",
21+
"api.composers",
22+
"api.logs",
23+
"api.search",
24+
"api.export_api",
25+
"api.pdf",
26+
"api.config_api",
27+
"utils.exclusion_rules",
28+
"utils.workspace_path",
29+
"utils.path_helpers",
30+
"utils.text_extract",
31+
"utils.tool_parser",
32+
"utils.cli_chat_reader",
33+
"utils.cursor_md_exporter",
34+
],
35+
hookspath=[],
36+
hooksconfig={},
37+
runtime_hooks=[],
38+
excludes=[],
39+
win_no_prefer_redirects=False,
40+
win_private_assemblies=False,
41+
cipher=block_cipher,
42+
noarchive=False,
43+
)
44+
45+
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
46+
47+
exe = EXE(
48+
pyz,
49+
a.scripts,
50+
[],
51+
exclude_binaries=True,
52+
name="CursorChatBrowser",
53+
debug=False,
54+
bootloader_ignore_signals=False,
55+
strip=False,
56+
upx=True,
57+
console=False, # --windowed: no console window
58+
)
59+
60+
coll = COLLECT(
61+
exe,
62+
a.binaries,
63+
a.zipfiles,
64+
a.datas,
65+
strip=False,
66+
upx=True,
67+
upx_exclude=[],
68+
name="CursorChatBrowser",
69+
)

launcher.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
"""Desktop launcher for Cursor Chat Browser.
2+
3+
Opens the Flask app inside a native OS window via pywebview.
4+
No HTTP server or port is used; pywebview calls the WSGI app
5+
directly in-process.
6+
"""
7+
8+
import webview
9+
10+
from app import create_app
11+
12+
13+
def main():
14+
app = create_app()
15+
webview.create_window(
16+
"Cursor Chat Browser",
17+
app,
18+
width=1280,
19+
height=860,
20+
)
21+
webview.start()
22+
23+
24+
if __name__ == "__main__":
25+
main()

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
flask>=3.0
22
fpdf2>=2.7
3+
pywebview>=5.0

0 commit comments

Comments
 (0)