mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-30 21:36:46 -06:00
Live sessions are per document instead of one for the whole app.
- **Engine** (`crates/engine/src/live.rs`): `Session.live` is now `Lives`: the app-wide parts (the
installed link, the invite-link registration) plus one `Live` per session. One place resolves
"the session of this document": `Session::live()` (the active document's; status off when it
has none), `live_mut()`, `live_of(uid)`. Sync, undo, read-only checks, follow, cursor chat,
spotlight and presence all go through the session of the document they are about, so edits and
presence never cross. Sessions behind the active tab keep syncing; your presence there is empty.
- **Link** (`LiveLink`): every call takes the `SessionId` the engine picked and `poll` returns
`(SessionId, Event)`, so one link carries several sessions. The desktop link and the browser
link keep them in a shared table with one mailbox per session
(`crates/collab-net/src/sessions.rs`); each hosted document listens on its own port.
- **Commands**: `collab.*` act on the active document's session; no parameters changed.
`collab.peers` keeps its shape and gains `session` (its id, null when the document isn't in
one) and `sessions` (every session: `{session, document, status, reason, role, fileName, link,
peers}`). `collab.join` returns the new session and opens its file in a tab of its own without
touching other sessions; until then (or if it fails) it shows on the tab it was started from.
Starting a session for a document replaces only that document's own session. Closing a document
ends its session only.
- **UI** (`crates/ui-egui/src/live.rs`): the Share popover, avatars, cursors, chat, spotlight
banner, follow frame and read-only reason read the session of the tab in front, per window. What
belongs to one session in the window (cursors shown, your chat box, a report being written) is
dropped when another tab comes to the front. `ui.inspect`'s `live` gains `session` and
`sessions`. No new strings or command ids.
- **Docs**: `docs/collab-protocol.md`, `docs/mcp.md`, `docs/control-protocol.md`,
`docs/collaboration-parity.md`, `docs/gaps.md` (gap 2), `ROADMAP.md`.
53 lines
1.8 KiB
Rust
53 lines
1.8 KiB
Rust
//! The sessions a link carries at once (the engine has one per document): each under the id the
|
|
//! engine gave it, with its own [`Inbox`], so what happens in one never reaches another and a
|
|
//! session that ended can't reach the next. The desktop link and the browser link both keep
|
|
//! theirs here.
|
|
|
|
use std::collections::BTreeMap;
|
|
|
|
use figcraft_engine::live::{Event, SessionId};
|
|
|
|
use crate::inbox::{Inbox, Waker};
|
|
|
|
/// Running sessions of kind `T` (a host or a guest connection) by id.
|
|
pub(crate) struct Sessions<T> {
|
|
wake: Waker,
|
|
running: BTreeMap<SessionId, (T, Inbox)>,
|
|
}
|
|
|
|
impl<T> Sessions<T> {
|
|
pub(crate) fn new(wake: Waker) -> Self {
|
|
Self { wake, running: BTreeMap::new() }
|
|
}
|
|
|
|
/// A mailbox for a session about to start.
|
|
pub(crate) fn inbox(&self) -> Inbox {
|
|
Inbox::new(self.wake.clone())
|
|
}
|
|
|
|
/// Session `id` started, with the mailbox its threads fill. Returns the session that had
|
|
/// the id, to be ended.
|
|
pub(crate) fn insert(&mut self, id: SessionId, session: T, inbox: Inbox) -> Option<T> {
|
|
self.running.insert(id, (session, inbox)).map(|(old, _)| old)
|
|
}
|
|
|
|
/// Session `id` is over: it comes back to be ended, and its mailbox is dropped.
|
|
pub(crate) fn remove(&mut self, id: SessionId) -> Option<T> {
|
|
self.running.remove(&id).map(|(session, _)| session)
|
|
}
|
|
|
|
pub(crate) fn get(&self, id: SessionId) -> Option<&T> {
|
|
self.running.get(&id).map(|(session, _)| session)
|
|
}
|
|
|
|
/// Every session's id, oldest first.
|
|
pub(crate) fn ids(&self) -> Vec<SessionId> {
|
|
self.running.keys().copied().collect()
|
|
}
|
|
|
|
/// What happened in each session since the last call, with its id.
|
|
pub(crate) fn poll(&self) -> Vec<(SessionId, Event)> {
|
|
self.running.iter().flat_map(|(id, (_, inbox))| inbox.drain().into_iter().map(|e| (*id, e))).collect()
|
|
}
|
|
}
|