pub enum ClientMessage {
Show 27 variants
Ping,
Subscribe {
what: Subject,
answering: bool,
review: bool,
},
Unsubscribe {
what: Subject,
},
Showing {
seat: Option<SessionSlot>,
},
Command {
command: Box<Command>,
reply_to: Option<u64>,
},
More {
conversation: SessionSlot,
before: Option<String>,
turns: u32,
},
SeatRange {
slot: SessionSlot,
epoch: Option<SessionId>,
from: u64,
to: u64,
},
CommitDiff {
session: SessionSlot,
sha: String,
},
BrowserView {
seat: SessionSlot,
tab: Option<u16>,
reply_to: Option<u64>,
},
BrowserViewPage {
seat: SessionSlot,
target: String,
reply_to: Option<u64>,
},
BrowserFileAnswer {
id: u64,
files: Vec<BrowserFileWire>,
},
BrowserDetach,
TerminalView {
seat: SessionSlot,
id: Option<u16>,
cols: Option<u16>,
rows: Option<u16>,
reply_to: Option<u64>,
},
TerminalDetach,
TerminalResize {
seat: SessionSlot,
id: u16,
cols: u16,
rows: u16,
},
TerminalPause {
paused: bool,
},
TerminalControl {
seat: SessionSlot,
id: u16,
take: bool,
},
TerminalClose {
seat: SessionSlot,
id: u16,
},
BrowserInput {
seq: u64,
event: BrowserInputEvent,
},
BrowserControl {
take: bool,
},
BrowserNav {
back: bool,
},
BrowserResize {
width: u32,
height: u32,
scale: f64,
},
BrowserTabs {
seat: SessionSlot,
reply_to: Option<u64>,
},
DocumentRead {
session: SessionSlot,
path: String,
reviewable: bool,
},
FileContent {
session: SessionSlot,
path: String,
sha: Option<String>,
},
ClientPerf {
records: Vec<Value>,
},
SettingsEdit {
fingerprint: String,
edits: Vec<SettingsEditRequest>,
id: u64,
},
}Expand description
What a client sends.
Variants§
Ping
A liveness probe, sent by a transport after a quiet spell and by the page on a return from the background.
Application-level rather than a WebSocket control frame: a browser page cannot send a Ping, so control frames would leave the web build with no probe at all. Any inbound frame answers one; this exists so a quiet peer is always asked something.
Subscribe
Fields
answering: boolWhether this client can answer the prompts it is shown.
Off unless the client says otherwise, because the core parks a turn on the reply of whoever registered as answering: a client counted as able to answer a prompt it has no surface to draw hangs the turn rather than failing it. A client with a dock to answer from says so here; the connection registers with the core accordingly before it forwards anything.
It is the CONNECTION’s role, not the subject’s. The core keeps one role per connection and it only ever rises, so a client that can answer states it on the first subscribe it makes - usually the home - and not only while a seat it could draw is showing.
What it guards is an install with no answerer anywhere. Every update is delivered to every subscriber regardless of role; the role decides only what the core may PARK on. A forge with an answering client attached already has an answerer, so a second client that says nothing is a guest beside one that can. Where nobody can answer, an ask is cancelled at birth rather than held for a reply that could not come. Declaring this means that case never arises while this client is attached.
review: boolThe review page’s own read: refresh the branch diff’s base before the record is encoded, and pin the stats and content reads to the sha that refresh resolved.
Off for every other read. The ordinary record read is the one behind every seat page and must not wait on the network; a page that draws a diff asks again with this set when it opens, so the state beside that diff belongs to the moment it was read.
Unsubscribe
Showing
Which seat this connection has on screen, None when it shows none.
A state assertion, not an event: the last one wins and repeating it is a no-op. A showing spends a seat’s diamond, its failure mark and its death trace - once the seat is also subscribed, so a client that keeps every visited seat subscribed spends nothing until the reader opens one. Nothing answers it.
Fields
seat: Option<SessionSlot>Command
reply_to is set only when the client wants an answer - which is the
eight commands whose reply rides a oneshot, and nothing else.
Boxed because a command is 360 bytes and every other variant here is a fraction of that (clippy::large_enum_variant). Serde writes a box as the value inside it, so the wire form is unchanged.
More
SeatRange
The frames a seat’s stream emitted between two numbers, for a client that read a gap in them.
The numbers are the seat’s own, minted as each frame crossed, so a
repair folds into the client’s stream exactly where the frames fell
rather than reading as a second conversation. epoch is the occupant
those numbers belong to: a seat whose occupant has changed has started
counting again, and a repair across the two is refused rather than
answered.
CommitDiff
One commit’s own diff, for the review canvas’ commit scopes.
Asked on demand rather than carried on the record: a branch can hold dozens of commits, and every reader of a record would pay their content on every read for a scope only one page draws.
BrowserView
Attach this connection to one of a seat’s browser tabs, as a viewer.
tab is the tab to drive; None is the asking seat’s current tab,
else its profile’s first. The answer is a browser_view naming the
profile’s wire id and the tab, then a browser_nav_state, then frames
as they paint. Attaching again replaces what this connection was
viewing.
Fields
seat: SessionSlotreply_to: Option<u64>Answers the attach: {"ok": true, "profile": <the one attached>, "tab": <the tab>}, or {"ok": false, "why": ...} with the
server’s own words. A raise that cannot be answered is a dark
surface, so a client that can wait sends one; the profile and tab
are what the page’s Done lowers.
BrowserViewPage
Open the page a parked hand-off named: the same attach, addressed by the page’s CDP target instead of a tab number.
A number is not an identity across a host restart. The successor host’s first tab takes number 1 again, so an ask parked through the replacement must name its page; a page that is gone is refused with the tabs that are open now.
Fields
seat: SessionSlotBrowserFileAnswer
The files a person chose for a page’s dialog, or none for a dismiss.
The bytes cross base64 because the wire is JSON; the server writes them to its own temp directory and hands the host the paths, because the host reads a file’s content at the moment it is answered.
BrowserDetach
Stop viewing the attached profile. Lowering the takeover is a detach: the idle clock counts an attached viewer as a use, and a connection that switched profiles must not leave a phantom viewer on the old one.
TerminalView
Attach this connection to one of a seat’s terminals, as a viewer. The
answer is a terminal_view carrying the screen as a VT stream, then
output frames as the shell writes them.
Fields
seat: SessionSlotid: Option<u16>The terminal id, or None to open the seat’s terminal - creating
it when the seat has none - and attach to it. The person’s door
on a seat every agent has left alone.
TerminalDetach
Stop viewing the attached terminal: the idle clock and the resize arbitration both read the truth from it.
TerminalResize
The attached surface changed size: the PTY and the model resize.
TerminalPause
Client-driven flow control, so a phone that backgrounded stops being
written to rather than being dropped. A resume is answered with a
fresh terminal_view, because the frames it missed are not replayed.
TerminalControl
Take the person’s control of an attached terminal, or give it back. One controller at a time, like the browser’s role: a take always lands, and the previous holder hears it lost the role.
TerminalClose
The person’s close door: end the terminal’s process group.
BrowserInput
One input event from the controlling viewer.
seq is the viewer’s own ordering: the server drops an event at or
below the last one the role carried, so a superseded gesture never
lands late. The event is the gesture, not CDP - the server maps it.
BrowserControl
Take the controller role, or give it back.
Move through the page’s history: the takeover bar’s arrows.
BrowserResize
The controller’s surface changed size: the page is laid out to it.
width and height are the surface’s DEVICE pixels - the client
multiplies its CSS box by the ratio it draws at - and scale is that
ratio, which the host gives CEF.
BrowserTabs
The tabs of this seat’s profile, for a client’s strip. Answered
through reply_to with {"ok": true, "tabs": { "profile", "id", "up", "connected", "closing_at_ms", "rows" }}, and
then pushed as a browser_tabs whenever they change.
DocumentRead
One document, read out of a seat’s own tree for a viewer’s route.
path resolves against the seat’s working directory, and reviewable
is what the hand-off asked for - the hand-off’s own fact, refused on a
PDF because that kind can never carry a review. Whether the DOCUMENT
is reviewable is the read’s own answer, not this flag’s echo. The
answer is a document_read message or an error naming it; those are
its only two.
FileContent
One file’s whole content at a revision, for the review page’s between-hunk expansion.
The record’s diff carries bounded hunks, so the lines BETWEEN them
are exactly what it does not carry - and only a page whose reader
opened a gap needs them, which is why this is asked for on demand
rather than carried on every record. sha names the scope’s own
revision: a commit’s tree for a commit scope, and None for the
seat’s branch tip, which is the new side of the all-changes diff.
The answer is a file_content message; a read that could not run
answers as itself rather than as an error.
ClientPerf
One page’s own perf records, for a client with no shell to write them.
The records stay JSON, unparsed here: they are the page’s instrument
(client/src/perf.ts), and the server’s part is the file - written
beside forge.log so a web client produces diagnostics like the shells
do. Nothing answers it; a batch that could not be written is a debug
record, not the client’s failure.
SettingsEdit
A batched save of the effective config. A save that writes is
answered with ServerMessage::SettingsSaved; one that does not is
answered with ServerMessage::SettingsRefused. A refused save
writes nothing, so no push follows it.
Fields
edits: Vec<SettingsEditRequest>id: u64The page’s own number for this save, echoed by
ServerMessage::SettingsSaved and
ServerMessage::SettingsRefused so a reply is matched to the
request that made it. Required: a client that forgets it is
refused rather than silently answered as id 0.
Trait Implementations§
Source§impl Debug for ClientMessage
impl Debug for ClientMessage
Source§impl<'de> Deserialize<'de> for ClientMessage
impl<'de> Deserialize<'de> for ClientMessage
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Auto Trait Implementations§
impl !RefUnwindSafe for ClientMessage
impl !UnwindSafe for ClientMessage
impl Freeze for ClientMessage
impl Send for ClientMessage
impl Sync for ClientMessage
impl Unpin for ClientMessage
impl UnsafeUnpin for ClientMessage
Blanket Implementations§
§impl<T> AnySync for T
impl<T> AnySync for T
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
impl<S, T> Duplex<S> for Twhere
T: FromSample<S> + ToSample<S>,
§impl<S> FromSample<S> for S
impl<S> FromSample<S> for S
fn from_sample_(s: S) -> S
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more