M9: a joined live session keeps reconnecting and says when it's disconnected

A session you joined could end for reasons that pass, or after about 85 s of
retries, and its tab then looked like an unsaved local file. Work done there
reached no one, with only a 3 s toast to say so.

A live connection now reconnects until you leave, pausing a minute after each
12 attempts. Only refusals that are for good stop it, so "too many messages",
"busy" and the like retry. Messages queued while offline go out paced so a long
queue can't trip the server's rate limit. A session that ended stays on its tab:
the tab shows a red mark and a strip across the canvas offers Retry now or Try
again, Save a copy and Dismiss. Joining again uses the same tab, and what changed
offline opens beside it as an unsaved copy. A session that gets out of step
joins again by itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
fluid-chey
2026-11-04 12:17:25 -07:00
co-authored by Claude Opus 5.5
parent 6f780211dd
commit fccaa43101
30 changed files with 687 additions and 59 deletions
+15 -3
View File
@@ -535,9 +535,21 @@ resuming with its offline edits, and leaving the session going back to snapshot
`ws://<ip:port>/` and sends the token only inside its `Hello`. Links are pasted into the Join
dialog (its field has the keyboard when it opens; Escape closes it), or opened from anywhere:
see *Opening invite links* below.
- Reconnect: a connection that drops after it was live reconnects with growing pauses (0.5 s,
doubling up to 10 s, 12 attempts); a refusal (wrong token, full) or the host ending the session
is final; a first join that can't reach the host fails at once. A reconnect resumes as the same
- Reconnect: a connection that drops after it was live reconnects until you leave, with growing
pauses (0.5 s, doubling up to 10 s, 12 attempts), then a minute's pause and as many again. A
server turning a live session away is taken as passing (busy, a limit, a file it couldn't read
just now) unless it says why in words that are for good: the link, the file or your place in it
is gone, or the versions can't talk (`FINAL_REFUSALS` in `crates/collab-net/src/join.rs`). Those
and the host ending the session are final; a first join that can't reach the host fails at once.
Messages queued while offline go out paced, so they stay within what a server takes from a
connection.
- Disconnected: while it reconnects, and after it can't go on, the tab shows a red mark and a strip
across the top of the canvas says so, with **Retry now** (`collab.reconnect`). The tab can still
be edited. A session you joined that ended stays on its tab with **Try again**, **Save a copy…**
and **Dismiss** (`collab.leave`). **Try again** joins it in the same tab as someone new; if the
tab changed since it lost the server, what changed opens beside it as an unsaved
"offline copy" and the tab shows the server's version. A session that gets out of step does the
same by itself. A reconnect resumes as the same
client: the guest's connection keeps the session id, client id and last sequence number from
the messages it read and sends them in its next `Hello` (see Offline and reconnect).
- The browser build joins and publishes to a server (see *Built (the browser client)*); hosting
+1
View File
@@ -26,6 +26,7 @@ cursors, selections, text carets, cursor chat and spotlights shown live ([`colla
| Date | Change | Summary |
|---|---|---|
| 2026-11-04 | minor | M9: a session you joined no longer turns into a local file by itself. A dropped connection reconnects until you leave (a minute's pause after each 12 attempts, a pause Retry now skips); only refusals that are for good stop it; queued messages go out paced. While disconnected the tab shows a red mark and a strip across the canvas says so; a session that ended stays on its tab with Try again, Save a copy and Dismiss, and what changed offline opens beside it when it joins again |
| 2026-10-31 | patch | M9, #1665: the document in a live session's welcome goes compressed (deflate) to clients whose hello announces `packed`, about seven times smaller on the wire (a synthetic file of 4,040 layers: 397 KB to 53 KB); older clients keep getting it uncompressed, and the client reads a packed document only up to the size an uncompressed welcome could have had. |
| 2026-10-30 | minor | M9, part of #742: a team list on `figcraft-server`. With `team_files = "edit"` (or `comment`, `view`) everyone who reaches the server sees the files shared from it now (a link of theirs has a join code: Share ▸ Start sharing) and joins them by a team list link, `figcraft://join?server=…&file=…&team=1`, which carries no secret: the server hands out the file's team link while it stays shared (`ResolveTeam`, answered like a join code), and Stop sharing revokes it with the others, disconnecting whoever joined with it. Turning the list off or changing its access refuses the old team links at once. `collab.files {refresh}` asks the server in Preferences for its list (`Admin::Team`; not the public server FigCraft ships with), and Home ▸ Online shows it below your own files: a row joins its file, its menu copies the link for the app or, with the server's `web_url`, for the browser build (`<web_url>#join=…`). A browser build served beside its server names it with `<meta name="figcraft-server">`, the page's default for sharing, codes and the team list. No accounts: the list is for a server only your team reaches. Tests: `team_tests` (the server and apps over loopback), `code_tests` (links and the browser's driver), `online_tests` (the Online section), `query_tests` (the page's server). Percentages and hours unchanged |
| 2026-10-30 | minor | M9, #1130: `figcraft-server` compares the SHA-256 digests of the presented and the kept server key instead of the raw strings, as `docs/server.md` says; the key file, `figcraft-server key` and rotation are unchanged. Numbers unchanged. |
+4 -1
View File
@@ -1478,7 +1478,10 @@ only: a host stops hosting it, a guest leaves it.
see `FIGCRAFT_CA_FILE` in [`server.md`](server.md#behind-a-reverse-proxy-tls)). `collab.leave {}`
leaves the active document's session (or ends a join started from it that is still on its way
or failed), and `collab.leave {session}` the session with that id (from `sessions`), whichever
document is active; the synced document stays open as your copy.
document is active; the synced document stays open as your copy. `collab.reconnect {}` acts on
the active document's lost session: a reconnect that is waiting tries now, and a session you
joined that couldn't go on is joined again in its tab. A dropped connection keeps reconnecting
until you leave (see *Reconnect* in [`collab-protocol.md`](collab-protocol.md)).
- `collab.publish {server, name?, key?}` puts the active document on a `figcraft-server`
([`server.md`](server.md)) and syncs it from there (as that document's session; others go on): `server` is the address
`figcraft-server key` prints (`ws://host:47900/?key=…`; or give `key` separately), `name` the
+1 -1
View File
@@ -43,7 +43,7 @@ Releases also ship it ready-built for Linux (`figcraft-server-<version>-linux-x8
tells everyone it is restarting. Every change it accepted is on disk before anyone is told about
it, so even a hard kill loses nothing confirmed (a file's log is then folded into a new snapshot
the next time everyone leaves it). Apps that were in a file keep editing and reconnect once the
server is back (for about a minute and a half), as the same participants.
server is back, as the same participants.
## Publishing a file