Skip to main content

tern_sdk/
lib.rs

1//! Talk to Tern over the Tern Surface Protocol (TSP).
2//!
3//! A program running in a terminal pane describes its UI as a tree of
4//! nodes; Tern lays it out, draws and animates it natively. Outside Tern the
5//! same program falls back to plain text. Six layers, each usable without
6//! the ones above it:
7//!
8//! 1. [`wire`]: constants, typed messages both ways, the encoder (framing,
9//!    chunking, blobs) and the decoder.
10//! 2. [`input`] and [`keys`]: the streaming input parser that splits TSP out of
11//!    the pty's input, and the key decoder.
12//! 3. [`ui`]: typed builders for every kind and prop, styled spans, and
13//!    handlers mapping events to the program's messages.
14//! 4. [`reconcile`]: views diffed into frame ops with derived ids, exactly as
15//!    Tern's plugin worker does it.
16//! 5. [`Session`]: the handshake, raw mode, surfaces, flow control, blobs,
17//!    event routing, recording and a clean exit.
18//! 6. [`print`], [`ask`] and [`plain`]: one-call helpers and the plain-text
19//!    fallback.
20//!
21//! Process-tty APIs (`term::Tty`, `Session::connect`, `print` and `ask`)
22//! require Unix or Windows. The wire, input, node and reconcile layers
23//! also build on `wasm32-unknown-unknown`.
24//!
25//! # Example
26//! ```no_run
27//! use tern_sdk::{PrintOptions, ui, ui::View};
28//!
29//! let view: View = View::new().main([ui::card().head("Deploy").status(ui::Status::Done).child(
30//! 	ui::kv()
31//! 		.item("service", "api-gateway")
32//! 		.item("region", "eu-west-1"),
33//! )]);
34//! // A native card in Tern; plain text anywhere else.
35//! tern_sdk::print(view, PrintOptions::new())?;
36//! # Ok::<(), tern_sdk::Error>(())
37//! ```
38
39#[cfg(any(unix, windows))]
40mod helpers;
41pub mod input;
42pub mod keys;
43mod plain;
44pub mod reconcile;
45mod record;
46mod session;
47pub mod term;
48pub mod ui;
49pub mod wire;
50
51#[cfg(any(unix, windows))]
52pub use helpers::{Answer, AskOptions, PrintOptions, Submission, ask, ask_with, print};
53pub use keys::Key;
54pub use plain::plain;
55pub use session::{Capabilities, Input, Options, Session, Surface, SurfaceOptions};
56pub use term::Waker;
57pub use ui::{Node, View};
58pub use wire::{Event, Font, Mode, Op};
59
60/// What can go wrong talking TSP.
61#[derive(Debug, thiserror::Error)]
62pub enum Error {
63	/// Terminal or file I/O failed.
64	#[error("i/o: {0}")]
65	Io(#[from] std::io::Error),
66	/// JSON could not be encoded or decoded.
67	#[error("json: {0}")]
68	Json(#[from] serde_json::Error),
69	/// Two siblings of a view share a key; nothing was sent.
70	#[error("{path}: duplicate child key {key:?}")]
71	DuplicateId {
72		/// The parent node's derived id.
73		path: String,
74		/// The duplicate key (explicit or positional).
75		key:  String,
76	},
77	/// A view or node in wire form is malformed.
78	#[error("{0}")]
79	InvalidView(String),
80	/// A blob over 16 MiB.
81	#[error("a blob of {0} bytes is over the 16 MiB limit")]
82	BlobTooLarge(usize),
83	/// The surface is closed (by the program, or `gone`).
84	#[error("surface {0:?} is closed")]
85	SurfaceClosed(String),
86	/// No answer came in time.
87	#[error("timed out waiting for {0}")]
88	Timeout(&'static str),
89	/// The terminal closed its input.
90	#[error("the terminal closed its input")]
91	InputClosed,
92}