Commits
Introduces specs/scroll-spec.md (v0.2) with caller-owned scroll model.
Clip API changes from { horizontal, vertical } to { x?, y? } with
numeric offsets. Adds scrollDelta on ElementInfo for wheel delta
reporting. Adds RenderOptions.event for input event integration.
Includes scroll demo with ~10k lines of lorem markdown.
📄 Add MIT license
Surface Clay layout errors on RenderResult
Clay reports errors (duplicate IDs, capacity exceeded, etc.) via a
callback during layout. These were previously silently discarded.
Collect them per-render on the Clayterm struct and expose them as
`errors: ClayError[]` on RenderResult.
Add RenderInfo with lazy element bounds lookup
Adds an `info: RenderInfo` field to RenderResult that provides
on-demand access to element bounding boxes after layout. Callers
query by element id via `info.get(id)`, which calls into WASM to
hash the id and look up the computed bounds via Clay_GetElementData().
A single WASM export handles the lookup — no tables or globals.
Uses the typedef struct infrastructure for BoundingBox field offsets.
Rename Op discriminant to directive, open() name to id
Restructures the Op type so the discriminant field is `directive`
instead of `id`, freeing `id` for the element identity string. The
`name` parameter on `open()` and `OpenElement` becomes `id`, aligning
with Clay's terminology.
Drops the `idx++` seed from Clay_HashString — element IDs are now
hashed with seed 0 and uniqueness is the caller's responsibility.
Duplicate IDs within a frame are undefined behavior.
Removes INV-7 (element identity disambiguation) from the spec and
updates Sections 8.3.1, 9.3, and 10.3 accordingly.
Closes #16
Add Clayterm current-state specification
Separate the combined spec into two documents aligned with INV-8
(architectural independence of rendering and input parsing):
- renderer-spec.md: rendering contract, directive model, rendering
modes (cursor update + line mode), pointer hit-testing
- input-spec.md: input parser API, scan model, InputEvent types
Also adds normative specification of cursor update mode (default)
and line mode with row offset to the renderer spec.
- Replace "descriptor" terminology with "directive" throughout
- Rename open() `name` parameter to `id` in spec
- Fix buffer diffing description: cells are copied, not swapped
- Add note that pointer hit detection requires render loop participation
- Reframe border-layout interaction as Clay's intended behavior
- Update WASM loading to reflect inlined base64 binary
- Move output buffer lifetime guarantee to normative section
- Clarify pack() is internal, validate() is public API
Decouple alternate buffer switching from clearing
Add `{ clear?: boolean }` option to `ALTSCREEN()` and
`alternateBuffer()`. When `clear` is false, uses xterm private mode 47
to switch without clearing the alternate buffer contents.
Closes #13
Adds the v0.1 draft specification covering the architectural model,
core invariants, public rendering API, descriptor model, and boundary
responsibilities. Non-normative sections document settling surfaces
(transfer encoding, input parsing, pointer events) separately from
the stable rendering core.
Add line rendering mode, termcodes, and inline region demo
Rename csi.ts to termcodes.ts and add ESC(), SHOWCURSOR(),
HIDECURSOR(), ALTSCREEN(), and MAINSCREEN() helpers. Make
CursorEvent.row/column 1-based to match DSR native format.
Replace all raw escape sequences in the demo with termcodes.
Use DECSC/DECRC (ESC 7/8) for cursor save/restore instead of
SCO (CSI s/u).
Move the row offset from a constructor parameter to a render-time
option. Row is now 1-based (matching ECMA-48 DSR/CPR format) so
callers can pass the queried cursor position directly without
conversion. Remove line mode from the inline region demo in favor
of raw newline allocation followed by CUP rendering for all frames.
This forces a render of all cells on the very first frame, which is
perfectly acceptable.
Add { mode: "line" } option to render() that emits newline-separated
rows instead of CUP positioning sequences. Line mode writes every cell
and primes the front buffer, so subsequent diff renders work without
a full redraw.
Inline WASM binary into generated TypeScript module
Replace runtime filesystem read of clayterm.wasm with a build step
that base64-encodes the binary into wasm.ts. This removes the
node:fs/promises dependency and --allow-read requirement for the
WASM file.
Accept signed int32 colors in validator
The rgba() function returns signed int32 when alpha >= 128 (bit 31
set). The validator's u32 type rejected these negative values. Add
a color type that accepts the full signed-to-unsigned 32-bit range.
The rgba() function returns signed int32 when alpha >= 128 (bit 31
set). The validator's u32 type rejected these negative values. Add
a color type that accepts the full signed-to-unsigned 32-bit range.
Add CursorEvent with 0-based top/left coords by parsing the terminal's
DSR response (\x1b[row;colR). Rename createTerm's row option to top
so cursor.top feeds directly into createTerm({ top: cursor.top }).
Add a `row` option to TermOptions that offsets all CUP sequences by
the given number of rows, enabling rendering into a region below
existing terminal content rather than always starting at row 1.
Introduce a Setting type that pairs apply/revert escape sequences,
ensuring terminal state is always cleanly restored. Refactor the
keyboard demo to use settings() for alternate buffer, cursor, mouse
tracking, and progressive input.
Clay's pointer hit-testing is now wired through render() via RenderOptions,
returning pointerenter/pointerleave/pointerclick events by diffing
Clay_GetPointerOverIds() across frames. The demo keyboard shows hover
highlights on keys, semantic element IDs, and configurable event log filters.
Correct modifier key codepoint mapping to match the spec (all-lefts-first
57441–57446, then all-rights 57447–57452). Add parser for Kitty-enhanced
legacy CSI sequences (arrows, function keys, tilde-style) that include
`:action` sub-parameters.
Replace CharEvent/KeyEvent with keydown/keyrepeat/keyup discriminated
union. Rewrite the CSI u parser to extract sub-fields: action type
(press/repeat/release), alternate keys (shifted/base), and associated
text codepoints. Legacy terminals emit keydown only; enhanced terminals
add keyrepeat and keyup. Text is populated for printable keys at all
enhancement levels.
Add typedef.ts (ported from clayscript) for declarative C struct
layout computation. Replace hand-written offset constants in
input-native.ts with offsets derived from the struct definition,
eliminating manual offset math while keeping direct DataView
access for V8 performance.
Parse CSI codepoint;modifiers u sequences from terminals that support
the Kitty keyboard protocol (Kitty, Ghostty, WezTerm, iTerm2, etc).
Maps special key codepoints (Tab, Enter, Escape, Backspace, F1-F12,
arrows, etc.) to existing KEY_* constants, and decodes modifier
bitfields to MOD_* flags.
Protocol overview: https://sw.kovidgoyal.net/kitty/keyboard-protocol/
Key encoding: https://sw.kovidgoyal.net/kitty/keyboard-protocol/#key-event-encoding
Functional keys: https://sw.kovidgoyal.net/kitty/keyboard-protocol/#functional-key-definitions
Modifier encoding: https://sw.kovidgoyal.net/kitty/keyboard-protocol/#modifiers
In setcell(), skip overwriting bg when the incoming value is
ATTR_DEFAULT so that backgrounds painted by render_rect() flow
through to text cells.
Fixes #1
Size memory dynamically based on clayterm_size() instead of hardcoding
256 pages, which was too small for large terminals.
Prevent silent memory corruption with a bounds check in pack() to make
sure we do not pack more operations that can fit in the buffer.
1MB should fit thousands of elements.
Add a complete VT/ANSI terminal input parser that converts VT200/SGR/urxvt
mouse protocols, UTF-8 decoding, and escape sequence into input events
Introduce a `validate` module that uses @sinclair/typebox to
enforce correct op structure and numeric ranges (u8/u16/u32)
before rendering. Exports `validate`, `assert`, and a `validated`
Term wrapper. Adds the `./validate` subpath export.
Use explicit byte-loop implementations instead of __builtin_mem*
to avoid infinite recursion when clang lowers builtins back to
libc calls on some versions.
Introduces specs/scroll-spec.md (v0.2) with caller-owned scroll model.
Clip API changes from { horizontal, vertical } to { x?, y? } with
numeric offsets. Adds scrollDelta on ElementInfo for wheel delta
reporting. Adds RenderOptions.event for input event integration.
Includes scroll demo with ~10k lines of lorem markdown.
Adds an `info: RenderInfo` field to RenderResult that provides
on-demand access to element bounding boxes after layout. Callers
query by element id via `info.get(id)`, which calls into WASM to
hash the id and look up the computed bounds via Clay_GetElementData().
A single WASM export handles the lookup — no tables or globals.
Uses the typedef struct infrastructure for BoundingBox field offsets.
Restructures the Op type so the discriminant field is `directive`
instead of `id`, freeing `id` for the element identity string. The
`name` parameter on `open()` and `OpenElement` becomes `id`, aligning
with Clay's terminology.
Drops the `idx++` seed from Clay_HashString — element IDs are now
hashed with seed 0 and uniqueness is the caller's responsibility.
Duplicate IDs within a frame are undefined behavior.
Removes INV-7 (element identity disambiguation) from the spec and
updates Sections 8.3.1, 9.3, and 10.3 accordingly.
Closes #16
Separate the combined spec into two documents aligned with INV-8
(architectural independence of rendering and input parsing):
- renderer-spec.md: rendering contract, directive model, rendering
modes (cursor update + line mode), pointer hit-testing
- input-spec.md: input parser API, scan model, InputEvent types
Also adds normative specification of cursor update mode (default)
and line mode with row offset to the renderer spec.
- Replace "descriptor" terminology with "directive" throughout
- Rename open() `name` parameter to `id` in spec
- Fix buffer diffing description: cells are copied, not swapped
- Add note that pointer hit detection requires render loop participation
- Reframe border-layout interaction as Clay's intended behavior
- Update WASM loading to reflect inlined base64 binary
- Move output buffer lifetime guarantee to normative section
- Clarify pack() is internal, validate() is public API
Add `{ clear?: boolean }` option to `ALTSCREEN()` and
`alternateBuffer()`. When `clear` is false, uses xterm private mode 47
to switch without clearing the alternate buffer contents.
Closes #13
Rename csi.ts to termcodes.ts and add ESC(), SHOWCURSOR(),
HIDECURSOR(), ALTSCREEN(), and MAINSCREEN() helpers. Make
CursorEvent.row/column 1-based to match DSR native format.
Replace all raw escape sequences in the demo with termcodes.
Use DECSC/DECRC (ESC 7/8) for cursor save/restore instead of
SCO (CSI s/u).
Move the row offset from a constructor parameter to a render-time
option. Row is now 1-based (matching ECMA-48 DSR/CPR format) so
callers can pass the queried cursor position directly without
conversion. Remove line mode from the inline region demo in favor
of raw newline allocation followed by CUP rendering for all frames.
The rgba() function returns signed int32 when alpha >= 128 (bit 31
set). The validator's u32 type rejected these negative values. Add
a color type that accepts the full signed-to-unsigned 32-bit range.
Replace CharEvent/KeyEvent with keydown/keyrepeat/keyup discriminated
union. Rewrite the CSI u parser to extract sub-fields: action type
(press/repeat/release), alternate keys (shifted/base), and associated
text codepoints. Legacy terminals emit keydown only; enhanced terminals
add keyrepeat and keyup. Text is populated for printable keys at all
enhancement levels.
Parse CSI codepoint;modifiers u sequences from terminals that support
the Kitty keyboard protocol (Kitty, Ghostty, WezTerm, iTerm2, etc).
Maps special key codepoints (Tab, Enter, Escape, Backspace, F1-F12,
arrows, etc.) to existing KEY_* constants, and decodes modifier
bitfields to MOD_* flags.
Protocol overview: https://sw.kovidgoyal.net/kitty/keyboard-protocol/
Key encoding: https://sw.kovidgoyal.net/kitty/keyboard-protocol/#key-event-encoding
Functional keys: https://sw.kovidgoyal.net/kitty/keyboard-protocol/#functional-key-definitions
Modifier encoding: https://sw.kovidgoyal.net/kitty/keyboard-protocol/#modifiers
Add a complete VT/ANSI terminal input parser that converts VT200/SGR/urxvt
mouse protocols, UTF-8 decoding, and escape sequence into input events