Skip to content

feat: term.update() folds CapabilityEvents into runtime capabilities - #133

Draft
natemoo-re wants to merge 3 commits into
feat/input-probe-eventsfrom
feat/term-capability-sink
Draft

natemoo-re wants to merge 3 commits into
feat/input-probe-eventsfrom
feat/term-capability-sink

Conversation

@natemoo-re

@natemoo-re natemoo-re commented Sep 19, 2026 •

Copy link
Copy Markdown
Member

Part 4/4 (final) of the terminfo foundation stack. Requires #132; draft until the spec (#130) is approved.

What this adds

  • term.ts: update() becomes the universal sink for resize and capability changes (renderer-spec §7.7, §8.6) — Update = { width, height } | CapabilityEvent, folded in order; returns bytes to write immediately (empty when none). New term.capabilities exposes the frozen RuntimeCapabilities snapshot (static Capabilities + folded event values); createTerm({ detection }) seeds the static baseline (256-color/xterm defaults when omitted).
  • validate.ts: validated() forwards the new capabilities getter.
  • test/term.test.ts: batch folding, capability events, resize no-op semantics.
  • .changeset/terminfo-capability-layer.md: minor bump; documents the cumulative API surface and migration for the whole stack (InputOptions.terminfo → detection, update({ events }) → per-event update()).

Explicitly out of scope

No renderer byte output changes (renderer-spec §7.8 defers all gated emission). First focused consumer: color encoding modes (#60). Other protocol follow-ups: #129.


Split from #106 for reviewability; #106 will be closed when this stack lands.

@natemoo-re
natemoo-re added this pull request to stack #134 September 19, 2026 04:29
@bombshell-cooper

bombshell-cooper Bot commented Sep 19, 2026 •

Copy link
Copy Markdown

Changeset suggestion

The current changeset no longer matches this PR. This review contains a corrected replacement.

Why: Changeset package scope does not match the affected packages confidently. Changeset bump does not match the consumer-visible impact. Changeset contains implementation or roadmap noise. Changeset contains unsupported consumer-facing claims.

View the proposed changeset
---
'@bomb.sh/tty': minor
---

Adds `detectTerminal`, `Capabilities`, `Detection`, `CapabilityEvent`, and `KeyTable` to the public API.

`detectTerminal()` reads a compiled terminfo binary (from disk or injected bytes), applies environment evidence (`COLORTERM`), and returns a frozen `Detection` carrying static `Capabilities`, a `probe` query batch to write to stdout, and opaque `keys` bytes for the input parser.

Pass `detection` to `createInput` to seed its escape-sequence trie with terminal-specific `key_*` sequences. Pass capability events from `scan()` to `term.update()` to keep the renderer's runtime capability snapshot current.

`scan()` now recognizes probe responses — OSC 10/11/12/21/22, XTGETTCAP, DECRPM mode 2026, kitty keyboard, kitty graphics APC, and the DA1 fence — and surfaces them as `CapabilityEvent` values interleaved with key and mouse events. Route these to `term.update()`, which returns a `Uint8Array` of bytes to write immediately (empty when no output is needed).

`InputOptions.terminfo` is replaced by `InputOptions.detection`. Both parsers remain usable without a `Detection`; the 256-color baseline and xterm default key sequences apply when it is omitted.

#### Migration

```diff
-const input = await createInput({ terminfo: await Deno.readFile(terminfoPath) });
+const detection = await detectTerminal({ env: process.env });
+const input = await createInput({ detection });
+process.stdout.write(detection.probe);
```

```diff
-term.update({ events: resizeEvents });
+for (const event of input.scan(bytes).events) {
+  if (event.type === "resize" || event.type === "capability") {
+    const out = term.update(event);
+    if (out.length) process.stdout.write(out);
+  }
+}
```

Review this changeset manually

If this draft is correct, react with 🚀 and Cooper will commit it to this branch.

@@ -0,0 +1,32 @@
---

@bombshell-cooper bombshell-cooper Bot Sep 19, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changeset needs revision.

Changeset package scope does not match the affected packages confidently. Changeset bump does not match the consumer-visible impact. Changeset contains implementation or roadmap noise. Changeset contains unsupported consumer-facing claims.

View the proposed replacement
---
'@bomb.sh/tty': minor
---

Adds `detectTerminal`, `Capabilities`, `Detection`, `CapabilityEvent`, and `KeyTable` to the public API.

`detectTerminal()` reads a compiled terminfo binary (from disk or injected bytes), applies environment evidence (`COLORTERM`), and returns a frozen `Detection` carrying static `Capabilities`, a `probe` query batch to write to stdout, and opaque `keys` bytes for the input parser.

Pass `detection` to `createInput` to seed its escape-sequence trie with terminal-specific `key_*` sequences. Pass capability events from `scan()` to `term.update()` to keep the renderer's runtime capability snapshot current.

`scan()` now recognizes probe responses — OSC 10/11/12/21/22, XTGETTCAP, DECRPM mode 2026, kitty keyboard, kitty graphics APC, and the DA1 fence — and surfaces them as `CapabilityEvent` values interleaved with key and mouse events. Route these to `term.update()`, which returns a `Uint8Array` of bytes to write immediately (empty when no output is needed).

`InputOptions.terminfo` is replaced by `InputOptions.detection`. Both parsers remain usable without a `Detection`; the 256-color baseline and xterm default key sequences apply when it is omitted.

#### Migration

```diff
-const input = await createInput({ terminfo: await Deno.readFile(terminfoPath) });
+const detection = await detectTerminal({ env: process.env });
+const input = await createInput({ detection });
+process.stdout.write(detection.probe);
```

```diff
-term.update({ events: resizeEvents });
+for (const event of input.scan(bytes).events) {
+  if (event.type === "resize" || event.type === "capability") {
+    const out = term.update(event);
+    if (out.length) process.stdout.write(out);
+  }
+}
```

@github-actions

github-actions Bot commented Sep 19, 2026 •

Copy link
Copy Markdown

Size Increased — +18.3 KB

121.1 KB unpacked

@pkg-pr-new

pkg-pr-new Bot commented Sep 19, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@bomb.sh/tty@133

commit: 5be02ae

@natemoo-re natemoo-re changed the title feat: term.update() folds CapabilityEvents into runtime capabilities (renderer-spec §7.7, §8.6) feat: term.update() folds CapabilityEvents into runtime capabilities Sep 19, 2026
@codspeed

codspeed Bot commented Sep 19, 2026 •

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 10 untouched benchmarks


Comparing feat/term-capability-sink (5be02ae) with feat/input-probe-events (299df82)1

Open in CodSpeed

Footnotes

  1. No successful run was found on feat/input-probe-events (4b430b6) during the generation of this report, so fd078c8 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report. ↩

@natemoo-re
natemoo-re force-pushed the feat/term-capability-sink branch from c64972d to d5be56e Compare September 30, 2026 01:50
natemoo-re added a commit that referenced this pull request Sep 30, 2026
Widen Update from { width, height } | CapabilityEvent to
{ width, height } | InputEvent. Events other than resize and capability
are no-op steps, so hosts can pass the full scan() events array without
filtering. Only ResizeEvent carries width/height, so the shapes stay
unambiguous. Matches the implementation in #133.
@natemoo-re
natemoo-re force-pushed the feat/term-capability-sink branch from 42fa5a4 to f124b5d Compare September 30, 2026 02:01
The terminfo spec no longer lists applyUpdate as public API; update()
is the only entry point, and tests already exercise it through
term.update().
The renderer spec now accepts any InputEvent as an Update and requires
non-resize, non-capability events to change no state and emit no bytes.
@natemoo-re
natemoo-re force-pushed the feat/term-capability-sink branch from f124b5d to 5be02ae Compare September 30, 2026 02:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant