Commit Graph

9 Commits

Author SHA1 Message Date
BFlat 76df2d4136
M9: hosting figcraft-server: wss in the desktop app, server releases, deployment guide (#125)
The desktop app joins wss:// sessions (rustls with webpki roots, certificates always checked, clear errors; FIGCRAFT_CA_FILE for private authorities), with one TLS session shared by the reader and writer threads. figcraft-server stops gracefully on SIGTERM/SIGINT/SIGHUP/Ctrl+C, ships in Linux x86_64/aarch64 release archives, and docs/server.md gains a Deploy on a Linux server guide with a hardened systemd unit, Caddyfile, nginx config and Dockerfile in packaging/server.
2026-10-11 15:52:27 +07:00
BFlat 09600d6da2
M9: team libraries on the server; swap library (#117)
A file's library can be published to figcraft-server and enabled in other files by a share link (view access is enough); placing, styles, variables and library updates with overrides kept work as for local libraries, with a background check while the Assets panel is open. library.swap re-points instances, styles and variables to another library by name (one undo step, unmatched items listed). The server keeps per-file usage reports (library.usage).
2026-10-11 13:05:46 +07:00
BFlat 07fa4d92ed
M9: standalone collaboration server with share links and permissions (#112)
figcraft-server hosts many files over the same hub the app uses for sessions, persists each as a checksummed snapshot plus an append-only log (synced before replies, compacted, damaged files skipped and reported) so restarts lose nothing confirmed, and gives per-file share links with edit or view access (hashed 128-bit tokens, revocable). The app can publish a file to a server and join server-hosted files; view-only tabs refuse edits. Protocol version 2.
2026-10-11 10:19:03 +07:00
BFlat 1444754f66
M9: collaboration polish: unique ids for styles, variables and comments; live text carets (#107)
- Styles, variables, collections, modes and comment threads created in a live
  session take ids from the creator's client space (figcraft_doc::ids: a
  number from the node id allocator, client_id << 32 | counter); outside a
  session ids are made exactly as before. The server refuses a new entry in
  another connected client's space (removed entries can be put back by undo),
  keeps removed entries in its saved state, and starts client ids above every
  space used by any kind of id.
- Others' text carets and selected characters show on the canvas in their
  colour at the glyph positions, with their name above the caret (no blinking,
  no frames of their own). Typing in a layer someone else is typing in says so
  (toast and tag); collab.peers reports who types where.
- Toasts wrap at a readable width.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 09:29:30 +07:00
BFlat 435415770f
M9: live sessions in the browser; join links and dialog polish (#106)
- Browser client (step 4): the web build joins sessions a desktop app hosts
  through the browser's WebSocket API (web-sys, binary frames, the same
  message encoding). Its connection logic is a target-independent state
  machine (collab-net driver.rs): hello on open, changes and presence held
  until the welcome, throttled presence, keep-alive pings, a silent host
  counted as dropped, reconnect with backoff, final refusals. Tested natively
  against a scripted socket and a real host over loopback. Hosting stays
  desktop-only (LiveLink::can_host; collab.peers canHost). https pages are
  told up front that they can only reach ws:// sessions on this computer.
- Shared join logic (join.rs: GuestConfig, outcomes, taking in host messages)
  and transport limits (limits.rs) used by the native guest and the browser.
- Invite links: `figcraft figcraft://join?...` joins on start (a bad link
  opens the Join dialog with it); file:// arguments are paths. Windows
  registers a per-user figcraft:// opener (HKCU\Software\Classes\figcraft)
  behind a Registry trait (tests use a fake): collab.registerLinks {on?} and a
  Preferences checkbox, default on for installed builds, never for builds
  from source. macOS Info.plist declares CFBundleURLTypes; the Linux desktop
  entry declares x-scheme-handler/figcraft and passes %U.
- Join dialog: Escape closes it and no longer reaches the canvas (selection
  kept); its field has the keyboard when it opens.
- Docs: collab-protocol (browser client, links, mixed content), mcp,
  control-protocol, README, web hosting notes, collaboration wording in the
  progress docs (numbers unchanged).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 09:10:59 +07:00
BFlat 94ff3d4df6
M9: collaboration: per-user undo, rejections, reconnect (#105)
* M9: collaboration: per-user undo, rejections, reconnect

Step 3 of docs/collab-protocol.md, the parts that don't need the transport:

- Per-user undo and redo (figcraft_collab::Client::{undo, redo}): each undoable batch is a step
  (values before/after by target, nodes created/deleted). Others' batches ordered after the step
  take its targets for good; creates invert to deletes (not when others built inside), deletes
  re-create the subtree. An undo or redo waits for every earlier batch and is remade from the
  confirmed state when it goes out; derived data follows as a follow-up batch.
- Rejections: refused batches leave the history (with an undo waiting on them) and queue a
  Notice; the engine shows it through Session::message (the UI's toast).
- Offline queue and reconnect: Hello.resume {session, client_id, seq}; the server keeps a
  bounded log and answers with the missed batches or a snapshot, plus what it did with the
  client's batches (processed {last_batch, refused}); queued batches then go out.
  Server::state / restore persist the session (client-id counter, log, session id).
- Presence carries editing_text {node, caret, selection}.
- Engine: documents in a live session (LiveDoc, Session::collab_*): commands, gestures and
  groups sync as one step each; edit.undo / edit.redo route to the per-user history; remote
  edits show under a gesture in progress. Outside a session snapshot undo is unchanged.
- Sim: dropped connections, undo/redo, and a checker that no undo reverts a change by someone
  else its author had seen. New tests in undo_tests, reconnect_tests, collab_live_tests,
  collab_undo_tests.
- CLAUDE.md / AGENTS.md: collab in the L2 layer list.

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

* M9: collaboration wording in the progress docs after per-user undo and reconnect

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

* M9: protocol doc: what the engine's live-session tests cover

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 03:19:24 +07:00
BFlat 4d7a9187ff
M9: live sessions: host from the app, invite links, live cursors and presence (#104)
* M9: real-time collaboration protocol (design)

docs/collab-protocol.md: WebSocket sessions with a server-ordered,
per-property last-writer-wins document sync, client-scoped ids,
fractional positions for children, ephemeral presence (cursors,
selections, follow), per-user undo, hosting from the app first and a
self-hosted server next, and the implementation plan. ROADMAP gains M9.

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

* M9: collaboration core: ids, change sets, server ordering, convergence

Step 1 of docs/collab-protocol.md, no networking:

- Client-scoped node ids: Document::set_client_id makes alloc_id hand out
  client << 32 | counter; single-user files allocate as before; the id
  counter is no longer part of document equality.
- crates/collab (layer 2): the synced object/property model (every saved
  Node field, plus document-level styles, comments, variables, images,
  dev snapshots, library data), Change batches, an O(changed) diff, strict
  and lenient apply, fractional sibling positions, snapshots.
- Server (ordering, validation, rejects with fixes) and Client (optimistic
  view, replay under remote batches, author-side repair of derived data)
  as pure state machines; binary (version byte + postcard, JSON values) and
  JSON message forms, fuzzed decoders.
- Engine: normalize as the recompute hook, collab.diff.
- Tests: round trips over random real commands, an N-client convergence
  fuzz test over delayed in-order links, junk batches.

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

* M9: fmt after merge

* M9: live sessions: host from the app, invite links, live cursors and presence

Step 2 of docs/collab-protocol.md, networking and presence half:

- crates/collab-net (layer 6): WebSocket transport (sync tungstenite, a
  reader and a writer thread per connection, no async runtime), the
  session host the app embeds (runs figcraft_collab::Server as the
  authority, carries its messages), invite links, presence throttling
  (~30 Hz, latest wins), limits (sockets, hello/idle timeouts, message
  size, rate, slow clients), keep-alive, clean close, reconnect with
  backoff; a browser stand-in until the web-sys client (step 4).
- engine: live session state (status, peers, presence, follow) and the
  collab.host/stop/join/leave/peers/follow/cursor commands; the network
  is reached only through the LiveLink the app installs.
- ui-egui: remote cursors (pointer drawn in code, name tag, gliding),
  selection outlines (once documents sync), avatars in the right panel
  with follow, the Share popover (start, invite link + URL with Copy,
  stop), File > Join live session.
- desktop app and MCP headless session install the link; xtask layers
  keeps networking crates out of L0-L5.
- Documents don't sync between apps yet (follow-up).

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

* M9: live sessions: document sync between apps

- The host's session runs figcraft_collab::Server seeded with the shared
  document; every app (the host's too, through the hub without a socket)
  is a collab Client. Guests open the host's file from the welcome as a
  new tab; leaving keeps it as a local copy.
- Engine: after each local action (a gesture once it ends; nothing during
  version or text previews) the session document is diffed into one
  Changes batch; server batches go through Client::receive with
  EngineRecompute and are installed with Session::apply_remote, which
  never touches the undo history. Batches arriving mid-gesture wait.
- Transport: Changes/Reject/Welcome reach the engine as Event::Sync;
  LiveLink::send carries batches in order; hellos are limited to 64 KiB,
  joined clients to the core's message size.
- Tests: engine sync against a real Server (ids in the client's space,
  one batch per gesture, remote edits skip undo, deferral, closing the
  file leaves), three apps over loopback converging (create, move,
  recolour, delete, rename) and a late joiner getting the server's state.
- Docs: collab-protocol, mcp, README, roadmap docs; the UI's
  "cursors only" note is gone, replaced by the per-person undo caveat.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 02:40:59 +07:00
BFlat 4924512f4e
M9: collaboration core: ids, change sets, server ordering, convergence (#103)
* M9: real-time collaboration protocol (design)

docs/collab-protocol.md: WebSocket sessions with a server-ordered,
per-property last-writer-wins document sync, client-scoped ids,
fractional positions for children, ephemeral presence (cursors,
selections, follow), per-user undo, hosting from the app first and a
self-hosted server next, and the implementation plan. ROADMAP gains M9.

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

* M9: collaboration core: ids, change sets, server ordering, convergence

Step 1 of docs/collab-protocol.md, no networking:

- Client-scoped node ids: Document::set_client_id makes alloc_id hand out
  client << 32 | counter; single-user files allocate as before; the id
  counter is no longer part of document equality.
- crates/collab (layer 2): the synced object/property model (every saved
  Node field, plus document-level styles, comments, variables, images,
  dev snapshots, library data), Change batches, an O(changed) diff, strict
  and lenient apply, fractional sibling positions, snapshots.
- Server (ordering, validation, rejects with fixes) and Client (optimistic
  view, replay under remote batches, author-side repair of derived data)
  as pure state machines; binary (version byte + postcard, JSON values) and
  JSON message forms, fuzzed decoders.
- Engine: normalize as the recompute hook, collab.diff.
- Tests: round trips over random real commands, an N-client convergence
  fuzz test over delayed in-order links, junk batches.

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

* M9: fmt after merge

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 01:03:03 +07:00
BFlat bc587ad28a
M9: real-time collaboration protocol (design) (#98)
docs/collab-protocol.md: WebSocket sessions with a server-ordered,
per-property last-writer-wins document sync, client-scoped ids,
fractional positions for children, ephemeral presence (cursors,
selections, follow), per-user undo, hosting from the app first and a
self-hosted server next, and the implementation plan. ROADMAP gains M9.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 23:46:03 +07:00