@@ -89,8 +89,8 @@ renderer and input parser each receive a copy at construction.
8989terminal's responses arrive on the input stream, where the input parser
9090recognizes them and emits ` CapabilityEvent ` values alongside keys and mouse
9191events. The host loop routes these events to ` term.update() ` , which folds them
92- into the renderer's ` RuntimeCapabilities ` and returns any bytes to write
93- immediately .
92+ into the renderer's ` RuntimeCapabilities ` synchronously. Changes are reflected
93+ in the next ` render() ` call .
9494
9595### 4.2 Data flow
9696
@@ -102,16 +102,19 @@ terminfo file bytes ──▶ terminfo_parse() ─▶ Capabilities (frozen)
102102 createInput() createTerm()
103103 │ │
104104probe bytes ──▶ terminal ──▶ stdin │
105- scan() ─▶ CapabilityEvent ─▶ update() ─▶ Uint8Array (write now)
105+ scan() ─▶ CapabilityEvent ─▶ update(): void
106106 │
107107 RuntimeCapabilities
108+ │
109+ render(ops).output ─▶ Uint8Array (host writes)
108110```
109111
110112- ` detectTerminal() ` parses raw bytes into ` Capabilities ` . It performs no IO.
111113- The input parser is the only source of ` CapabilityEvent ` values. It writes
112114 nothing; it emits.
113115- The renderer holds its own private capability state. ` update() ` is the only
114116 path that changes it.
117+ - Changes made by ` update() ` are reflected in the next ` render() ` call.
115118- The renderer and the input parser share no memory and hold no reference to
116119 each other. The host loop is the only place they meet.
117120
@@ -154,9 +157,8 @@ missing, oversized, or malformed terminfo file, or abort. Environmental
154157conditions degrade to the §7.1 baseline. The only rejection is caller error
155158(§10.1).
156159
157- ** TINV-5. Immediate update output.** ` term.update() ` returns a ` Uint8Array ` of
158- bytes to write now. It MUST NOT defer output to the next ` render() ` call. An
159- empty array is a valid return when the update changes no rendered state.
160+ ** TINV-5. Update effects.** ` term.update() ` returns ` void ` . The caller must
161+ invoke ` render() ` to see the effects.
160162
161163** TINV-6. Event-only probe surface.** The input parser MUST NOT write to any
162164shared state when it recognizes a probe response. It surfaces the result as a
@@ -324,10 +326,11 @@ not merge; each response yields its own event.
324326** OSC 22.** An OSC 22 reply sets ` key: "pointer-shape", value: true ` . The shape
325327name in the reply is discarded in v1; only protocol support is recorded. The
326328renderer emits pointer shapes only while ` pointerShape ` is ` true ` (Renderer
327- Specification §7.10). Terminals that implement OSC 22 set but not its query
328- never reply; ` detectTerminal() ` supplies that evidence itself from the
329- known-support table (§6.1), and a caller may still override it with
330- ` term.update([{ type: "capability", key: "pointer-shape", value: true }]) ` .
329+ Specification §7.10). Setting ` pointerShape ` to ` false ` suppresses pointer-shape
330+ output; it does not reset the pointer to ` default ` . Terminals that implement OSC
331+ 22 set but not its query never reply; ` detectTerminal() ` supplies that evidence
332+ itself from the known-support table (§6.1), and a caller may still override it
333+ with ` term.update([{ type: "capability", key: "pointer-shape", value: true }]) ` .
331334
332335** DA1.** The DA1 reply is recognized internally as the probe fence and MUST NOT
333336surface as a ` CapabilityEvent ` .
@@ -476,9 +479,10 @@ and mouse events in arrival order. The host loop routes them to `term.update()`.
476479### 9.3 Capability change over time
477480
478481A capability event can arrive after the renderer has already emitted frames.
479- `term.update()` folds it into the runtime snapshot. Any renderer-side output
480- invalidation or immediate bytes are defined by the renderer section that
481- consumes that capability (Renderer Specification §7.8).
482+ `term.update()` folds it into the runtime snapshot synchronously. Subsequent
483+ renders consume that snapshot. Any renderer-side output invalidation is defined
484+ by the renderer section that consumes that capability (Renderer Specification
485+ §7.8).
482486
483487---
484488
@@ -566,18 +570,19 @@ When omitted, the parser uses built-in xterm key sequences.
566570``` ts
567571interface Term {
568572 render(ops : Op [], options ? : RenderOptions ): RenderResult ;
569- update(events : readonly InputEvent []): Uint8Array ;
573+ update(events : readonly InputEvent []): void ;
570574 readonly capabilities: RuntimeCapabilities ;
571575}
572576```
573577
574578` update() ` accepts an array of ` InputEvent ` values. Events other than
575579` ResizeEvent ` and ` CapabilityEvent ` are no-op steps, so the full ` events ` array
576580from ` scan() ` can be passed without filtering. Events are folded in order: each
577- event produces the next ` RuntimeCapabilities ` and any bytes defined by the
578- consuming feature, and the returned bytes are concatenated. The foundation
579- defines no such bytes. The return value is always a ` Uint8Array ` ; callers write
580- it to their output stream when non-empty (TINV-5).
581+ resize updates the Term's dimensions and each capability event updates its
582+ ` RuntimeCapabilities ` . These changes take effect before ` update() ` returns. The
583+ method returns ` void ` ; the caller must invoke ` render() ` to see the effects
584+ (TINV-5). Multiple calls contribute to the next render as defined in Renderer
585+ Specification §7.7.
581586
582587` term.capabilities ` is a frozen snapshot of the current ` RuntimeCapabilities ` .
583588
@@ -594,19 +599,28 @@ const input = await createInput({ terminfo });
594599
595600process .stdout .write (terminfo .probe );
596601
602+ function draw() {
603+ const { output } = term .render (view ());
604+ process .stdout .write (output );
605+ }
606+
597607process .stdin .on (" data" , (bytes : Uint8Array ) => {
598608 const { events } = input .scan (bytes );
599- const out = term .update (events );
600- if (out .length ) process .stdout .write (out );
609+ term .update (events );
601610 for (const event of events ) dispatch (event );
611+ requestRender (draw );
602612});
603613
604614process .on (" SIGWINCH" , () => {
605- const out = term .update ([{ type: " resize" , width: cols (), height: rows () }]);
606- if ( out . length ) process . stdout . write ( out );
615+ term .update ([{ type: " resize" , width: cols (), height: rows () }]);
616+ requestRender ( draw );
607617});
608618```
609619
620+ Here ` view() ` , ` dispatch() ` , and ` requestRender() ` belong to the host
621+ application. ` requestRender() ` schedules the supplied rendering callback; the
622+ host can coalesce requests into one render after a batch of events.
623+
610624` createTerm ` and ` createInput ` each take ` terminfo ` and build their own private
611625state from it. Passing the same ` TerminalInfo ` to both passes the same plain
612626value twice. There is no shared memory and no attachment guard.
0 commit comments