Skip to content

feat: img element — public surface, validation, and test suite - #142

Draft
natemoo-re wants to merge 3 commits into
feat/graphics-substratefrom
feat/img-element
Draft

natemoo-re wants to merge 3 commits into
feat/graphics-substratefrom
feat/img-element

Conversation

@natemoo-re

Copy link
Copy Markdown
Member

The Graphics Specification's public TypeScript API, stacked on the substrate PR (#141). Final PR of the stack: spec (#140) → implementation (#141) → img (this PR).

Public surface

term.setImage(1, { width: 128, height: 64, pixels: rgba8 });
term.render([img("cover", { image: 1, alt: "cover art" })]);
term.removeImage(1); // deletion bytes to write now
  • ops.ts — the void img() directive: pixel-surface registry id, required alt (empty = decorative), sizing axes (fit resolves to the §8.3 intrinsic size), evidence-gated variant override, element bg. The transfer encoding mirrors the C decode; img ops don't participate in open/close balance.
  • term.ts — Term.setImage/Term.removeImage over the two-phase native surface (begin validates + reserves at an absolute address → the host copies pixels → commit bumps the version; video's same-size path allocates nothing). Closed RangeErrors per §6.2's validation table. createTerm takes imagePoolBytes (default 4 MiB).
  • update() bridges — the kitty-graphics CapabilityEvent feeds the renderer's capability mirror (terminfo-spec §4.2's private renderer state); the engine's TINV-5 cleanup bytes (capability denial §11.2, resize §10.5) surface in update()'s return.
  • validate.ts — the Img op shape: registry ids in the protocol's positive range, required alt, the variant union.

Verification

Review notes

  • The move test documents a Clay constraint worth knowing: Clay forces the root element to the window box, so an img-as-root cannot move (its footprint is window-derived) — moves are exercised via a padded container.
  • While landing the tests, one cross-layer fix rode the substrate PR (feat: graphics substrate (registry, tier engine, cell integration) #141): the input parser's kitty APC recognition now requires the response shape (no a= action key), so the renderer's own quiet emissions can't surface as capability events (INV-I5).

@bombshell-cooper

bombshell-cooper Bot commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

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.

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

Adds the `img()` directive for declarative image rendering in terminal UIs.

The renderer walks a fidelity ladder — kitty graphics protocol, shape-aware ASCII character art, or alt text — resolved from `RuntimeCapabilities.kittyGraphics` evidence. Images are registered as caller-owned RGBA8 buffers via `setImage` / `removeImage`; transmission is lazy and re-transmission only occurs on data change, so steady-state renders are byte-silent.

```ts
term.setImage(1, { width: 128, height: 64, pixels: rgba8 });
term.removeImage(1); // returns deletion bytes to write immediately

term.render([img("cover", { image: 1, alt: "cover art" })]);
```

`createTerm` accepts an `imagePoolBytes` option (default 4 MiB) that sizes the fixed pixel pool. `update()` emits cleanup bytes immediately when a capability denial or resize invalidates live placements. Line mode caps the ladder at `ascii`. Errors surface as `IMAGE_NOT_FOUND`, `IMAGE_PLACEMENTS_EXCEEDED`, and `IMAGE_PLACEMENT_COLLISION`.

#### Migration

`@bomb.sh/tty` is pre-1.0; this minor bump is a breaking change to the `Term` interface — `setImage` and `removeImage` are new required methods. Code that constructs a `Term` directly (rather than via `createTerm` or `validated`) must add both methods:

```diff
  const myTerm: Term = {
    render(ops) { … },
    update(events) { … },
+   setImage(id, data) { … },
+   removeImage(id) { … },
    get capabilities() { … },
  };
```

Review this changeset manually

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

Comment thread .changeset/img-element.md
@@ -0,0 +1,15 @@
---

@bombshell-cooper bombshell-cooper Bot Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

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.

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

Adds the `img()` directive for declarative image rendering in terminal UIs.

The renderer walks a fidelity ladder — kitty graphics protocol, shape-aware ASCII character art, or alt text — resolved from `RuntimeCapabilities.kittyGraphics` evidence. Images are registered as caller-owned RGBA8 buffers via `setImage` / `removeImage`; transmission is lazy and re-transmission only occurs on data change, so steady-state renders are byte-silent.

```ts
term.setImage(1, { width: 128, height: 64, pixels: rgba8 });
term.removeImage(1); // returns deletion bytes to write immediately

term.render([img("cover", { image: 1, alt: "cover art" })]);
```

`createTerm` accepts an `imagePoolBytes` option (default 4 MiB) that sizes the fixed pixel pool. `update()` emits cleanup bytes immediately when a capability denial or resize invalidates live placements. Line mode caps the ladder at `ascii`. Errors surface as `IMAGE_NOT_FOUND`, `IMAGE_PLACEMENTS_EXCEEDED`, and `IMAGE_PLACEMENT_COLLISION`.

#### Migration

`@bomb.sh/tty` is pre-1.0; this minor bump is a breaking change to the `Term` interface — `setImage` and `removeImage` are new required methods. Code that constructs a `Term` directly (rather than via `createTerm` or `validated`) must add both methods:

```diff
  const myTerm: Term = {
    render(ops) { … },
    update(events) { … },
+   setImage(id, data) { … },
+   removeImage(id) { … },
    get capabilities() { … },
  };
```

@pkg-pr-new

pkg-pr-new Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

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

commit: 5b99b5a

@github-actions

github-actions Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Size Increased — +18.4 KB

147.9 KB unpacked

@natemoo-re
natemoo-re added this pull request to stack #143 October 9, 2026 20:10
@natemoo-re
natemoo-re marked this pull request as draft October 9, 2026 20:11
The Graphics Specification's public TypeScript API:

- ops.ts: the void img() directive (image registry id, required alt,
  sizing axes, evidence-gated variant override, element bg) and its
  transfer encoding, mirroring the C decode.
- term.ts: Term.setImage/removeImage - the two-phase native surface
  (begin validates and reserves at an absolute address, the host
  copies pixels, commit bumps the version) with closed RangeErrors;
  update() forwards the kitty-graphics capability into the renderer's
  mirror and surfaces the TINV-5 cleanup bytes the engine emits on
  denial and resize; createTerm takes imagePoolBytes.
- validate.ts: the Img op shape (registry id range, required alt,
  variant union).
- test/graphics.test.ts: the §14 suite - tier resolution, kitty
  conformance (chunking, replace cycles, steady-state silence, move
  re-places), cleanup/removal, capability invalidation, resize,
  ascii art determinism and alpha, alt tier, registry validation,
  input-stream cleanliness.
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