fleury_wire library

Fleury's explicitly unstable, platform-neutral remote wire.

This library exposes the frame protocol, codecs, and transport contracts used by first-party browser and agent peers that ship in lockstep with Fleury. It is correctness-tested, but it is not a semver-stable integration surface: frame shapes and negotiation rules may change between releases.

Most platform hosts should use fleury_host.dart. A peer that deliberately speaks this wire must follow docs/implementation/wire-protocol.md and use a matching Fleury build.

Classes

ByeFrame
Either side signals a clean shutdown. Empty payload.
CaretFrame
The focused editable's caret rectangle in cell space, or absent when nothing editable is focused. The peer positions its hidden IME capture element there so composition candidate windows appear at the caret. App → peer, sent when the rect changes.
ClipboardResultFrame
The peer's answer to a ClipboardWriteFrame. Peer → app.
ClipboardWriteFrame
The app asks the peer to place text on the PEER's clipboard — the machine the user is actually sitting at. Sequenced so the app can match the peer's ClipboardResultFrame. App → peer. Without this, copy in a served session lands on the server's clipboard (or nowhere).
DebugRequestFrame
A pull-style debug query from the peer (agent bridge, future browser DevTools): "send me your recent kind records". Peer → app. seq correlates the DebugResponseFrame; limit bounds how many records the app returns (newest last). Kinds are strings so the set can grow without a protocol change; unknown kinds get an empty response.
DebugResponseFrame
The app's answer to a DebugRequestFrame: the seq and kind echoed back with a JSON document of records (shape is per-kind; see DebugFrameLog.toJson / runApp's error serialization). App → peer.
FrameDecoder
Streaming frame decoder. Feed bytes as they arrive from the transport; pull out complete frames with drain. The decoder holds partial-frame state across calls so a fragmented socket read or websocket message boundary doesn't lose data.
ImagePlacement
Where an inline image sits: its content-hash id, visible cell window, and original fit box. Bytes travel out-of-band (an InlineImageFrame), shipped once per id.
InitFrame
The handshake. The peer sends one before any InputFrame, carrying the remote display's size and the capabilities the app should plan against (color mode, image protocol, multiplexer wrapping), and the protocol it speaks. The app answers with one of its own (see protocolVersion and terminalInput).
InlineImageCacheLedger
Insertion-ordered inline-image cache accounting.
InlineImageCachePolicy
Shared cache accounting for remote inline-image hosts.
InlineImageFrame
The bytes of one inline image (browser surface), keyed by content-hash id. App → peer, sent once before the first PlanFrame that places it; the client caches it by id and renders an <img> overlay wherever a plan's placements reference it. Decoupling bytes from placement keeps the image off the cell-grid wire and ships it a single time.
InputEventFrame
A structured input event from the peer — keystroke, mouse, paste, resize, composition — dispatched server-side as a TuiEvent. Replaces the raw InputFrame byte stream on the structured path. Peer → app.
InputFrame
Raw input bytes from the remote display — keystrokes, escape sequences, paste contents. Routed through the same InputParser pipeline as a local POSIX driver.
OutputFrame
Outbound ANSI bytes — the app's diff render. Only ever flows app → peer. Legacy host; the structured serve host emits PlanFrame.
PlanFrame
A presentation plan — the structured serve host's per-frame output, driving a visual surface on the peer instead of ANSI. App → peer.
RemoteFrame
One decoded frame off the wire. Sealed so callers exhaustively switch in dispatch.
RemoteFrameTransport
Bidirectional frame channel between the app and a peer (a local shell terminal, a browser, a test harness). Frames flow in both directions; lifetime is owned by the caller (typically the driver).
RemotePatchRun
A run of same-style cells within a patch.
RemotePlan
A frame reduced to its changed cells for the wire: the grid size, repaint/scroll flags, a per-frame style table, and the changed column-range patches. The client applies the patches to a CellBuffer mirror and rebuilds the dirty rows from it (reusing the span builder), so only changed cells travel — the efficiency ANSI gets implicitly.
RemoteRowPatch
One contiguous changed column range in a row.
ResizeFrame
The remote display resized. Surfaced as a ResizeEvent on the driver's event stream.
SemanticActionFrame
The peer activated a node in its accessible DOM (a screen reader or agent driving the semantics, not the visual grid). The host invokes action on the live node id. Peer → app; the structured counterpart to the app → peer SemanticsFrame.
SemanticActionResultFrame
The app's answer to a SemanticActionFrame: the node id and action echoed back with the invocation status, so the peer (browser AT mirror, agent bridge) can distinguish "handler ran", "disabled", "not found", "unsupported", and "handler threw" instead of guessing from tree diffs. App → peer.
SemanticsFrame
A semantic snapshot (UTF-8 JSON, the SemanticInspectionSnapshot shape) for the just-rendered frame. App → peer; lets a served session stay agent-drivable and accessible.
SemanticsWireDecoder
Client side: replays the encoder's frames, holding the flat node state and rebuilding a SemanticTree after each. One instance per session.
SemanticsWireEncoder
Server side: turns semantic frames into compact wire payloads, emitting a full frame once per connection and patches thereafter. One instance per served session (it holds the last-sent state). Two entry points share that state: encodeTree (the serve render loop's path — redacts only the changed nodes on demand, O(changed)) and encode (for consumers that already hold a pre-redacted SemanticInspectionSnapshot, e.g. inspection/agent bridges).
SemanticWireDelta
The set of node ids whose wire form may have changed since the last present, in WIRE-id space (already sanitized). Derived from SemanticsOwner's per-frame diff: changed is added ∪ updated, removed is removed. The owner's node equality compares exactly the fields the wire serializes — including bounds and child order — so a node lands in changed iff its wire form could differ. Passing this lets the encoder re-serialize only the changed nodes instead of re-flattening the whole tree every frame.
TerminalInputModes
The input half of a TerminalMode: which reports the terminal sends (mouse, pastes, focus changes) and how much of the Kitty keyboard protocol encodes keys.

Enums

FrameType
Frame type discriminator.
RemoteClipboardStatus
How a peer handled an app-requested clipboard write.
RemoteWireProtocol
The two protocols the remote wire carries. They share the frame envelope and the INIT handshake and version independently; an INIT declares exactly one, and the session speaks it throughout.

Mixins

SynchronousSendTransport
Mixin for transports whose send hands bytes off synchronously (an in-memory pair, a test fake): they can never back up.

Constants

defaultInlineImageCachePolicy → const InlineImageCachePolicy
The production cache policy shared by the remote sender and browser host.
defaultMaxRemoteFramePayloadLength → const int
Default remote frame payload cap.
maxRemoteControlFramePayloadLength → const int
Small handshake/control frames should never carry document-sized payloads.
maxRemoteDocumentFramePayloadLength → const int
Structured plans, semantic snapshots, debug records, and ANSI output.
maxRemoteImageFramePayloadLength → const int
One encoded inline image. Images remain the only frame allowed up to the global cap.
maxRemoteInputFramePayloadLength → const int
Text, paste, clipboard, and semantic-action input accepted in one event.
maxRemoteSemanticNodeIdBytes → const int
Maximum UTF-8 byte length of a semantic node id on the remote wire.
maxSemanticTreeDepth → const int
Maximum tree depth the decoder will reconstruct. The frame payload cap already bounds total bytes (and thus node count), but a hostile or corrupt patch could still encode a very deep childIds chain; capping recursion depth turns that into a pruned subtree instead of a stack overflow. Far beyond any real UI nesting.
maxSemanticWireEdges → const int
Maximum child references in one decoded semantic mirror.
maxSemanticWireNodes → const int
Maximum retained nodes in one decoded semantic mirror.
remoteProtocolVersion → const int
The structured wire protocol version (RemoteWireProtocol.structured). Bumped on any change to a frame the structured protocol uses; declared in INIT as v=<n> and answered app → peer. The protocol is lockstep: a structured peer and the app must speak exactly this version.
semanticsWireVersion → const int
Wire envelope schema version. Bumped only on a breaking shape change; the decoder rejects an unknown version rather than misreading it.
serveSessionBusyCloseCode → const int
WebSocket close code fleury serve (bridge mode) turns a second browser away with while a session is live: bridge mode pairs one browser with the one app it does not own. Private-use range, same reasoning as serveSessionLimitCloseCode; the reason names --spawn as the way to get a session per tab.
serveSessionLimitCloseCode → const int
WebSocket close code fleury serve turns a browser away with when the admission cap (--max-sessions) is already full.
shellProtocolVersion → const int
The version of the protocol fleury shell speaks (RemoteWireProtocol.shell), declared in INIT as shell=<n>.

Functions

applyRemotePlanToBuffer(RemotePlan plan, CellBuffer mirror) → Set<int>
Applies a decoded plan to a CellBuffer mirror, reproducing the server's frame. The client rebuilds the dirty DOM rows from the mirror afterward. Returns the row indices the plan touched.
buildRemotePlan(CellBuffer prev, CellBuffer next, {required bool fullRepaint, TuiDirtyRows? dirtyRows}) → RemotePlan
Builds a RemotePlan from the rendered prev/next buffers — the changed column ranges only, grouped into same-style runs with a per-frame style table. On fullRepaint (or a size change) every row is treated as changed.
decodeInputEvent(Uint8List bytes) → TuiEvent
Decodes a TuiEvent from wire bytes.
decodeRemotePlan(Uint8List bytes) → RemotePlan
Decodes a RemotePlan from wire bytes.
decodeSemanticAction(Uint8List bytes) → ({SemanticAction action, SemanticNodeId id, String? targetToken, Object? value})
Decodes a semantic-action request. Throws RemoteCodecException on an unrecognized action name or a malformed payload so the caller rejects it rather than misinterpreting it.
encodeFrame(RemoteFrame frame) → Uint8List
Encodes a frame into wire bytes ready for the transport.
encodeInputEvent(TuiEvent event) → Uint8List
Encodes a TuiEvent to wire bytes. Throws RemoteCodecException for an event kind not carried by the serve protocol.
encodeRemotePlan(RemotePlan plan) → Uint8List
Encodes a RemotePlan to wire bytes.
encodeSemanticAction(SemanticNodeId id, SemanticAction action, {Object? value, String? targetToken}) → Uint8List
Encodes a peer's semantic-action request — the browser or an agent activating a node in its accessible DOM — as the node id, the action name, and an optional JSON-encoded payload (carried only by setValue).
maxRemoteDebugResponseJsonLength(String kind) → int
Maximum raw JSON bytes that fit in a DebugResponseFrame for kind.
remoteFramePayloadLimit(FrameType? type) → int
The maximum payload accepted for type by the default remote wire.

Exceptions / Errors

RemoteCodecException
Thrown when a structured frame payload is malformed.
RemoteProtocolException
A malformed frame or payload was received from a remote peer.