Commit Graph
13 Commits
Author SHA1 Message Date
bded38dd23 Control port: require a per-window key (split from #54) (#124)
* MCP bridge: send the control key, to loopback addresses only

`wordcraft-cli mcp --connect` now sends the app's control key with every
request. It takes the key from WORDCRAFT_CONTROL_KEY, or else from the
key file the app writes for that port, and reads it again on every new
connection, also after an `unauthorized` reply (the app restarted with
a new key).

The key goes only to loopback addresses: 127.0.0.0/8, ::1 and
`localhost`, and only when the connection really leads to a loopback
address. For any other host the bridge sends no key, prints why at
start, and turns the app's `unauthorized` into an error that says the
key was not sent and how to reach the app (through 127.0.0.1, for
example an SSH tunnel). Other host names are never resolved for this.

The new `wordcraft_mcp::control_key` module holds the rule the app and
the bridge share: the settings folder (the one with ui.json), the
Flatpak instance id and `<settings>/control-key.<instance>`
(`local-<port>` outside Flatpak). No new dependency; wasm-safe.

* Control port: every request needs the window's key

Any local program, and possibly a web page, could drive the app through
its loopback control port. Each window started with --control now has
a key:

- once the port is bound, the app writes a random key (32 bytes from
  the OS, as hex) to <settings>/control-key.<instance>, mode 0600 on
  Unix, replacing a stale file; it removes the file on a clean exit. A
  failed bind writes nothing. <instance> is the Flatpak instance id, or
  local-<port> outside Flatpak (wordcraft_mcp::control_key, the rule
  the MCP bridge reads it with). The settings folder is the one that
  holds ui.json; prefs_path now uses the same helper.
- every request needs the key: top-level "key", or
  {"method":"auth","key":...} once per connection. Only auth reads
  params.key; elsewhere it stays an ordinary parameter (ui.key). The key
  is checked on every request, in time that does not depend on where a
  wrong key differs. A missing or wrong key gets
  {"ok":false,"error":"unauthorized"} and the connection is closed.
- a line over 1 MiB gets "line too long" and the connection is closed
  (the rest of the line is drained first, so the client reads the
  error).
- a connection that sends no authorised request within 30 s of being
  accepted is closed; blank lines and bad JSON do not extend that
  limit. After that it may stay idle.

* Docs: control-port keys

docs/control-protocol.md gets a Keys section: where the key file is
(per platform and per instance), top-level "key" or auth once per
connection, `unauthorized` and the closed connection, the 1 MiB line
limit, the 30 s limit for the first authorised request, and how the MCP
bridge finds the key and sends it only to loopback addresses (an SSH
tunnel for an app on another machine). docs/mcp.md points there, and
the agent guides (AGENTS.md, CLAUDE.md) show the key in their control
channel example.

* Control key: the key-file helpers in their own small crate

wordcraft-control-key (standard library only, Standalone in the layering
table) holds where the key file lives and how to read it. The app and the
MCP bridge both use it, so the app no longer depends on wordcraft-mcp.

* Cargo.lock: wordcraft-control-key follows the workspace version (0.4.0)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: lpgn <2556951+lpgn@users.noreply.github.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 06:56:41 -04:00
74f02a411e M2: styles.apply (Apply Styles) command; parity 355/405 (#114)
* M2: styles.apply (Apply Styles) command; parity 355/405

Add Home > Styles > Apply Styles as a pure command. With a style name
or id it runs para.style (one undo step); with no params it opens the
styles pane and returns {"pane": true}. Non-string, empty or
whitespace style values return CmdError::Params. Move Mod+Shift+S
from para.style to styles.apply. Add 8 tests, ja/zh labels, and
regenerate docs/parity.md.

* i18n: pt-BR label for Apply Styles

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 06:34:58 -04:00
Eduardo Soares ae79726ab6 i18n: add Brazilian Portuguese (pt-br) interface translation (#127) (#128)
771-entry pt-br.tsv catalog written clean-room from the English labels,
one LANGUAGES row, locale/persistence/coverage/font regression tests,
and README, ROADMAP, control-protocol and ATTRIBUTION updates.
2026-10-10 06:17:23 -04:00
780fd7a5af Control server closes on non-JSON input; hyperlinks follow only http/https/mailto (#34) (#72)
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>
2026-10-10 06:16:28 -04:00
Brandon ThomasandClaude Opus 5.5 869ac16110 Typing parity with Word: lists, AutoFormat and AutoCorrect as you type (#204)
* Typing parity with Word: lists, AutoFormat and AutoCorrect as you type

Typed side by side with Word (same keystrokes, results read back) and matched it:

- Enter after a list no longer gets stuck: the paragraph that leaves a list goes back to
  Normal, and later Enters make blank paragraphs (the `num: 0` marker counted as a list item,
  so every Enter re-ran "leave the list" and did nothing; the list indent also stuck to every
  following paragraph).
- Enter on an empty nested item moves it up a level before leaving the list.
- Second Backspace at the start of an un-numbered list paragraph returns it to Normal
  instead of joining it to the paragraph above.
- "1. " always starts numbering at 1; "a. " and "I. " start lettered/roman lists; "- " makes
  a dash bullet.
- Undo right after an AutoFormat/AutoCorrect change undoes only that change.
- AutoCorrect: sentence capitals (with abbreviation/initial exceptions), runs on Enter too,
  "word -- word" en dash, "wait..." ellipsis, 1/2 1/4 3/4, ordinal superscripts, web
  addresses become links.
- Border lines: Enter after ---, ===, ***, ___, ~~~, ### draws a bottom border.
- Layout: no line break right after a slash ("and/or", URLs), as in Word.
- ui_shot: UI_SHOT_FULL=1 prints whole replies for scripted checks.

Regression tests in crates/engine/src/tests_typing.rs and the layout tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Typing parity: record Word's observed typing rules, two more tests

docs/typing-parity.md lists what Word does for each key sequence (lists, Backspace, Tab,
AutoCorrect, AutoFormat, slash line breaks) with the test that pins it, the known gaps, and how
to compare without taking over the shared machine. CLAUDE.md/AGENTS.md point at it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 04:52:10 -04:00
d7d9702bda Interface languages: follow the system, or pick one in Options (#8) (#12)
WordCraft's interface can now be shown in Simplified Chinese, Traditional
Chinese or Japanese as well as English. The new `language` setting is
`auto` by default: the UI follows the operating system's preferred
languages (Windows display language, macOS preferred languages,
LANG/LC_* on Unix, via sys-locale; WORDCRAFT_LANGUAGE overrides) when
there is a catalog for one of them, and English otherwise. File > Options
> Interface language picks one by hand, and the `ui.language` command does
the same for scripts and agents. The setting persists with the other UI
preferences.

The i18n module is PdfCraft's (catalog format, parser, locale matching),
trimmed to plain entries. English strings in the code are the keys; the
ribbon helpers (group, big, small, split, menu_button, menu items,
tooltips and ribbon locations), tabs, Backstage, Options, dialogs, panes,
the status bar, the context menu and command search translate at draw
time. Command ids, documents, the CLI, MCP and the control channel stay
English. Catalogs hold ~780 entries per language, written clean-room from
the English meaning.

Chinese interfaces put the craft-fonts Chinese face before the Japanese
one so a line never mixes glyph styles; fonts are reinstalled when the
language changes.

Fixes #8.


Claude-Session: https://claude.ai/code/session_019FczntrDNYd6DupyBdpbL5

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <shared2@storyteller.ai>
2026-10-09 09:22:42 -04:00
b830fae017 Release: Flatpak x86_64 + aarch64 bundles, AppImage zsync auto-update, README Downloads (#22)
- packaging/linux/package.sh: embed gh-releases-zsync update information in each AppImage
  (appimagetool -u) and write the .zsync beside it; the Linux job installs zsync and checks both.
- New flatpak job (x86_64 on ubuntu-24.04, aarch64 on ubuntu-24.04-arm) repackages the Linux
  tarball with packaging/linux/flatpak-bundle.sh and ai.storyteller.wordcraft.bundle.yml; no Rust
  build, no release environment. The release job needs it.
- version, Linux and web jobs drop `environment: release` (they sign nothing), so a branch
  workflow_dispatch dry-runs every unsigned job; the release job still needs the signed jobs.
- FreeBSD release build embeds craft-fonts like the other desktop builds and ships its licences.
- Flathub manifest: same finish-args as the bundle (adds xdg-documents), builds the CLI too;
  packaging-lint checks both manifests agree.
- README: Downloads section; docs/releasing.md updated.

Co-authored-by: Brandon Thomas <shared2@storyteller.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 09:38:25 -04:00
Brandon ThomasandClaude Opus 5.5 906d4faca3 About: contributor and model credits compiled in (grab bag + table, self-submitted names) (#6)
* About: contributors compiled in from contributors.json (grab bag + table, sortable, name toggle)

Adds About ▸ Contributors and About ▸ Models tabs per the craftrules standard
(standards/contributors.md): contributors/contributors.json is generated by
craftrules/scripts/contributors.py and baked into the binary by crates/ui-egui/build.rs as
static tables. Each contributor line: @username, opt-in display/real name, PRs, commits,
lines +/−/Δ, binary assets +/−, first/last commit. Sortable by every column; alphabetical
ignores case and the leading @. `ui.dialog` also accepts `contributors` / `models` to open
the About window on that tab.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Contributors: self-submitted names in contributors/people.toml; AGENTS.md credit-yourself section

build.rs reads contributors/people.toml next to the generated contributors.json and shows a name only
for entries the generator verified as written by that person (craftrules standards/contributors.md).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Contributors: regenerate (verified people.toml entries)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 21:17:02 -04:00
Brandon ThomasandClaude Opus 5.5 6e9dc4bab4 M7/M14: comment balloons in the margin; ROADMAP alpha and parity estimates; README Review and Layout screenshots; cargo xtask alias
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 01:38:55 -04:00
Brandon ThomasandClaude Opus 5.5 702aeb4ff4 Release: FreeBSD artifact job, docs/releasing.md
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 00:51:20 -04:00
Brandon ThomasandClaude Opus 5.5 f3dbc31a57 README with screenshots, Crafting Apps table, community links; proofing ignores deletions/objects; comment shading; hhea line metrics; watermark tuning
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 00:18:42 -04:00
Brandon ThomasandClaude Opus 5.5 435194f7c2 M0.9–M0.12: CLI, MCP server, xtask (layers/assets/parity/ci), icon placeholder, AGENTS/ATTRIBUTION/NOTICE, protocol docs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 23:21:35 -04:00
Brandon ThomasandClaude Opus 5.5 d1e80df4da M0.1–M0.6: workspace, geom, document model, fonts, layout, render
Clean-room foundations for WordCraft: Arc-shared document model with
paragraph/run editing, styles, lists, sections and tables; Word-style
font substitution; first-fit line breaking with tabs, lists, alignment,
pagination, tables, headers/footers and hit testing; vello_cpu page
rasteriser.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 22:30:16 -04:00