mirror of
https://github.com/storytold/wordcraft.git
synced 2026-10-24 10:27:44 -06:00
The control server answered a line that wasn't JSON with an error and kept reading, so an HTTP request to the port (for example a web page's fetch to 127.0.0.1) could carry a command such as file.saveAs in its body. It now replies once and closes the connection on the first bad line; HTTP request lines never parse, so nothing after them runs. Ctrl/Cmd+click on a hyperlink passed any URL to the system. It now follows only http, https and mailto links, and shows a status message otherwise, so a document can't launch file: paths or custom-scheme handlers. Tests cover JSON requests, closing on bad JSON, an HTTP POST carrying a command, and the link allow-list. The control protocol docs note the closing behaviour. Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2.1 KiB
2.1 KiB
Control protocol
Start the app with a control port: wordcraft --control 7981 (or WORDCRAFT_CONTROL_PORT=7981).
It listens on 127.0.0.1 only. Send one JSON object per line; each gets one reply line:
{"id": 1, "method": "engine.execute", "params": {"command": "text.insert", "params": {"text": "Hello"}}}
{"id": 1, "ok": true, "result": {"anchor": {...}, "focus": {...}}}
Errors come back as {"id": …, "ok": false, "error": "message"}. A failed command leaves the
document unchanged. A line that isn't valid JSON gets one error reply, then the server closes the
connection (so an HTTP request sent to the port can't run a command); reconnect to continue.
Methods
| Method | Params | Result |
|---|---|---|
engine.execute |
command, params |
the command's result (any id from engine.commands, including UI commands ui.tab, ui.dialog, ui.backstage, ui.language (`{"value": "auto" |
engine.commands |
— | every command: id, label, location, shortcut, params, enabled |
document.inspect |
text? |
blocks (text, style, runs, lists, tables), parts, sections, selection, pages |
ui.inspect |
— | UI state, view, dialog, window size, page rects on screen, caret, perf |
ui.click |
x, y, button?, count?, shift?, cmd?, alt? |
real pointer input at window coordinates |
ui.clickText |
page (0-based), x, y (points from the page's top-left), count? |
click inside a page |
ui.move / ui.drag |
x,y / x,y,toX,toY,steps? |
pointer move / drag |
ui.key |
key (B, Enter, Left…), shift?, alt?, cmd? |
key press through egui (shortcuts apply) |
ui.text |
text |
typed text through egui |
ui.screenshot |
path? |
PNG of the window |
ui.render |
path, page? (1-based), scale? |
PNG of a page, rendered by the engine |
ui.parity |
— | feature-catalog parity |
ui.resize / ui.focus / app.quit |
window control |
Any other method name is treated as a command id: {"method": "format.bold"} works.
Positions are {"story": "body" | {"part": n}, "path": [block, row, cell, block, …], "off": byteOffset}.