mirror of
https://github.com/storytold/wordcraft.git
synced 2026-10-19 02:10:15 -05:00
main
2
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
a9eb233a9d |
Track changes: keep other authors' deletions, credit paragraph splits, exact edit snapshots (split from #54) (#125)
* Track changes: a tracked delete keeps other authors' deletions Deleting a range with Track Changes on marked every run in it as deleted by the current author, also text another author had already deleted. That author's deletion was lost: the change list showed it under the wrong name, and rejecting the second author's changes brought back text the first author had deleted. Text that is already deleted now keeps its deletion and its author, like Word. * Track changes: a tracked paragraph split credits the new mark to its author Pressing Enter with Track Changes on inserts a paragraph mark by the current author, but the paragraph after the split took its mark's revisions from the text at the caret. Inside or at the end of another author's insertion, the original paragraph mark then showed as inserted by that author. The paragraph after the split now keeps the original mark's revisions; only the new mark is the splitting author's. * Engine: edit snapshots that take commands back exactly Session::edit_snapshot and Session::restore let a caller run a command and take it back without a trace: the document, the selection, the undo and redo stacks, the open typing group, a pending join_next_undo and the dirty flag come back as they were, and the layout is recomputed. Edit > Undo cannot do this: it leaves a redo step, loses the redo stack the command cleared, and closes the typing group. At the undo limit (500 steps) a command pushes the oldest step out. The snapshot keeps the oldest steps when the stack is near the limit and restore puts them back, so the stack is exact there too (up to four commands between snapshot and restore). Restore is exact for commands that add undo steps; edit.undo and file.new/file.open in between are outside its contract (documented). The snapshot destructures every Session field, so a new field does not build until someone decides whether a snapshot must cover it. Session::undo_depth and the snapshot's doc/dirty/undo_depth accessors tell a caller whether the command changed anything. * Tests: hostile_params_never_panic leaves out review.readAloud Read Aloud hands the document's text to the system's speech synthesizer (say, PowerShell, spd-say or espeak), so running the test made the computer speak the sample document. Like the file commands, it is left out of the sweep. * Undo: evictions at the limit are counted everywhere edit snapshots rely on them Merging main brought two paths #125's eviction counter didn't know: push_undo (typing parity, #204) dropped the oldest step without counting it, and a failed command (#153) put the whole stacks back but left the count raised. Either made restore() bring back a step that had never left. Both now keep the count right; restore_is_exact_at_the_undo_limit covers each (both fail without the fix). 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> |
||
|
|
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>
|