md2star.gui_server module
md2star local GUI server, Overleaf-style Markdown editor with live PDF preview.
Launches a localhost-only HTTP server that fronts md2star’s conversion
engine: the browser sends Markdown + a small JSON options blob;
the server invokes convert(fmt, ...) from md2star.pipeline against
a temp file and streams the resulting bytes back. The frontend renders
the PDF result inline via PDF.js (no DOCX/PPTX rendering in the browser;
those formats are simply offered as a download).
Endpoints
GET /→index.html(shipped undermd2star/data/gui/).GET /app.js→ the frontend ES module.GET /favicon.svg→ tiny inline SVG, no network calls.POST /render→ run the converter, stream back the bytes.POST /shutdown→ clean process exit (used by the in-page“Quit server” button so users do not have to find the terminal).
The server binds to 127.0.0.1 only (no LAN exposure) and uses
ThreadingHTTPServer so a slow /render does not block fast
/preview reads. There is no auth and no sandbox, it is intended
to run on the same machine as the user invoking it, just like Jupyter
or Vite’s dev server.
Security note
Two different questions, and both have to be answered.
What may a request touch? Every /fs/* path is funnelled through
_safe_within_root(), which rejects .. segments, absolute paths and
symlink escapes, so a request can never read or write outside the single
folder root the user opened.
Who is asking? Binding to loopback keeps the port off the network, but it
does not keep it away from the user’s own browser: any page they have open
can post to 127.0.0.1:8765. A cross-origin POST with a CORS-safelisted
content type is a simple request, so there is no preflight to refuse, and an
attacker never has to read a reply — /fs/open with {"path": "~"}
followed by /fs/delete is pure side effect. So every request first passes
_Handler._caller_is_this_page(), which checks Origin (cross-site
calls) and Host (DNS rebinding) before a handler runs.
Both halves are unit-tested in tests/test_gui_security.py, the second with
a live server and the exact headers a browser would send.
- md2star.gui_server.LINT_DEADLINE_S = 20.0
Every string in
index.htmlthis server will translate, listed rather than discovered. A declared list is reviewable, is what the.pois extracted from, and cannot quietly rewrite a class name or a bit of JavaScript the way a clever scan eventually would. Format names, file names and the product’s own name are deliberately absent: they are the same in every language.tests/test_gui_i18n.pychecks each one still occurs in the page, so the list cannot rot while the interface moves on. How long the GUI waits for the optional AI lint before answering without it. The CLI has no such limit on purpose: a conversion is not interactive, and a model that takes two minutes to load is still worth waiting for there. A browser is not — found when a resident vision model had to be swapped out and a lint request blocked for over a minute with the editor spinning.
- md2star.gui_server.localise(html, accept_language)[source]
Serve the page in the visitor’s language, when there is a catalogue.
A browser’s
Accept-Languageis a person stating a preference, so unlike the CLI — which answers toMD2STAR_LANGand nothing else, seemd2star.i18n— the GUI honours it. Strings with no translation keep their English source, so a partial catalogue gives a partly translated interface rather than a broken one, and thelangattribute is updated with them so a screen reader pronounces what it is actually reading.
- md2star.gui_server.serve(*, port=8765, no_browser=False, bind='127.0.0.1')[source]
Serve the editor until Ctrl-C. The body of
md2star gui.The flags arrive parsed: click owns this package’s only command line (see
md2star.cli), so there is no argv to re-read here.- Parameters:
port (int, optional) – Preferred port. On a loopback bind an in-use port falls back to the next free one; on a non-loopback bind the request is honoured as given, because an operator exposing a port on purpose means that port.
no_browser (bool, optional) – Do not auto-open a browser. The URL is still printed.
bind (str, optional) – Bind address.
0.0.0.0exposes the server to the LAN with no authentication, which is why it prints the banner below.
- Returns:
0once the server has been stopped cleanly.- Return type: