# GridCraft control protocol Start the desktop app with a control port: ```sh gridcraft --control 7979 # or GRIDCRAFT_CONTROL_PORT=7979 ``` Then send newline-delimited JSON to `127.0.0.1:7979`. Every request line gets one reply line: ```json {"id": 1, "method": "engine.execute", "params": {"command": "cell.set", "params": {"cell": "B2", "input": "=SUM(A1:A9)"}}} {"id": 1, "ok": true, "result": {"cell": "B2", "value": "45"}} ``` Errors come back as `{"id": …, "ok": false, "error": "…"}`. ## Methods | Method | Params | What it does | |---|---|---| | `engine.execute` | `{command, params}` | Runs any engine command (see `engine.commands` or `gridcraft-cli commands`) | | `engine.commands` | | Every command: id, label, ribbon path, shortcut, params doc, enabled | | `engine.journal` | | Commands run so far (replayable) | | `document.inspect` | | Workbook summary: sheets, used ranges, tables, charts, names, selection, undo labels | | `sheet.read` | `{range?, sheet?, formulas?, formatted?}` | Values (or formulas / displayed text) of a range | | `cell.get` | `{cell?}` | One cell: value, formula, displayed text, style, merge, comment, link | | `ui.inspect` | | UI state: ribbon tab, editor, open dialog, message, grid rect, scroll, perf | | `ui.click` / `ui.drag` / `ui.move` | `{x, y, toX?, toY?, button?, count?, shift?, cmd?, alt?}` | Real pointer input in window points | | `ui.cell.click` / `ui.cell.drag` | `{cell}` / `{from, to}` | Pointer input at a cell's centre (works in point mode while editing) | | `ui.key` / `ui.text` | `{key, shift?, cmd?, alt?, ctrl?}` / `{text}` | Keyboard input (typing in the grid starts editing) | | `ui.edit.begin` / `ui.edit.text` / `ui.edit.commit` / `ui.edit.cancel` | `{text?}` / `{move?: down\|right\|up\|left\|none}` | Drive the cell editor | | `ui.ribbon` | `{tab}` | Switch ribbon tab | | `ui.dialog` / `ui.dialog.set` / `ui.dialog.confirm` / `ui.dialog.cancel` | `{name}` / `{field, value}` | Open, fill in and confirm dialogs | | `ui.screenshot` | `{path?}` | PNG of the window (base64 when no path) | | `ui.set` | `{dark?, formulaBar?, ribbonCollapsed?}` | UI preferences | | `ui.resize` / `ui.focus` | `{width, height}` | Window control | | `app.open` / `app.save` / `app.quit` | `{path}` / `{path?}` | Files and lifetime | Any other method name that is a command id (e.g. `home.bold`) runs that command. Use `engine.execute` with `{command: "view.theme", params: {mode: "system"}}` to choose the display theme; `mode` accepts `"system"`, `"light"`, or `"dark"`. System follows live OS appearance changes and falls back to Light when the platform supplies no preference. The saved choice remains System; new users still start in Light. The legacy `view.darkMode` command and `ui.set {dark}` select a fixed Light or Dark theme. `{command: "app.language.set", params: {language: "ja"}}` switches the interface language (`code` is accepted as an alias for `language`); `app.language.english` and `app.language.japanese` are shortcuts. Only interface chrome is translated, never command ids or document text. The choice is saved in `ui.json`; with nothing saved the desktop app follows the system language. For a headless equivalent (no window), use `gridcraft-cli mcp` or `gridcraft-cli run`; see [`mcp.md`](mcp.md) and [`cli.md`](cli.md).