mirror of
https://github.com/storytold/wordcraft.git
synced 2026-10-26 11:27:10 -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>
36 lines
2.1 KiB
Markdown
36 lines
2.1 KiB
Markdown
# 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:
|
|
|
|
```json
|
|
{"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"|"en"|"zh-hans"|"zh-hant"|"ja"}`)…) |
|
|
| `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}`.
|