diff --git a/README.md b/README.md
index 989ac88..cb6941a 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@ tono is a node-based SPM image processing and analysis tool. The main focus is o
It is heavily inspired by [Gwyddion](https://gwyddion.net/), one of my favorite scientific FOSS programs on the web.
-
+
## Quick start
Install a local binary from the Releases section, or run locally:
@@ -28,9 +28,10 @@ npm run dev # terminal 2 — Vite dev server, open the URL it prints
```bash
git clone https://github.com/VIPQualityPost/tono.git && cd tono
+python -m venv .venv && source .venv/bin/activate
pip install -e .
cd frontend && npm ci && npm run build && cd ..
-TONO_HOST=0.0.0.0 tono
+TONO_HOST=0.0.0.0 python -m backend.main
```
See [Self-Hosting](docs/self-hosting.md) for reverse proxy setup, environment variables, and configuration.
diff --git a/backend/server.py b/backend/server.py
index 5cff300..706b8c3 100644
--- a/backend/server.py
+++ b/backend/server.py
@@ -715,10 +715,18 @@ def create_app(
app.router.add_get("/check-update", check_update)
app.router.add_get("/ws", websocket_handler)
+ async def dist_file(request: web.Request) -> web.Response:
+ filename = request.match_info["filename"]
+ path = (DIST_DIR / filename).resolve()
+ if not path.is_relative_to(DIST_DIR.resolve()) or not path.is_file():
+ raise web.HTTPNotFound()
+ return web.FileResponse(path)
+
if (DIST_DIR / "assets").exists():
app.router.add_static("/assets", DIST_DIR / "assets")
if FRONTEND_DIR.exists():
app.router.add_static("/static", FRONTEND_DIR)
+ app.router.add_get("/{filename}", dist_file)
async def _cors_middleware(app_, handler):
async def middleware(request):
diff --git a/docs/self-hosting.md b/docs/self-hosting.md
index 19971c3..de7c92d 100644
--- a/docs/self-hosting.md
+++ b/docs/self-hosting.md
@@ -9,7 +9,7 @@ git clone https://github.com/VIPQualityPost/tono.git && cd tono
python -m venv .venv && source .venv/bin/activate
pip install -e .
cd frontend && npm ci && npm run build && cd ..
-TONO_HOST=0.0.0.0 tono
+TONO_HOST=0.0.0.0 python -m backend.main
```
The server will be available at `http://:8188`.
@@ -48,7 +48,7 @@ After=network.target
Type=simple
User=tono
WorkingDirectory=/opt/tono
-ExecStart=/opt/tono/.venv/bin/tono
+ExecStart=/opt/tono/.venv/bin/python -m backend.main
Environment=TONO_HOST=127.0.0.1
Environment=TONO_PORT=8188
Environment=TONO_APPDATA=/var/lib/tono
diff --git a/frontend/src/HelpPanelManager.tsx b/frontend/src/HelpPanelManager.tsx
index c685f99..03ef50b 100644
--- a/frontend/src/HelpPanelManager.tsx
+++ b/frontend/src/HelpPanelManager.tsx
@@ -4,16 +4,18 @@ import { marked } from 'marked';
import DOMPurify from 'dompurify';
// Open external links in new tabs
-const renderer = new marked.Renderer();
-const defaultLinkRenderer = renderer.link.bind(renderer);
-renderer.link = function (token) {
- const html = defaultLinkRenderer(token);
- if (token.href && /^https?:\/\//.test(token.href)) {
- return html.replace(/^${text}`;
+ }
+ return `${text}`;
+ },
+ },
+});
interface Heading {
level: number;
diff --git a/frontend/src/styles.css b/frontend/src/styles.css
index 3f40027..4de3eea 100644
--- a/frontend/src/styles.css
+++ b/frontend/src/styles.css
@@ -608,10 +608,10 @@ html, body, #root {
.node-help-panel {
position: fixed;
- top: 60px;
+ top: 12px;
right: 20px;
width: 620px;
- max-height: calc(100vh - 80px);
+ max-height: calc(100vh - 32px);
background: #1e293b;
border: 1px solid #334155;
border-radius: 8px;