Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions architecture/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Architecture Docs

Implementation-backed architecture notes for Termisu.

- [Core Architecture](./core-architecture.md)
- [JS Runtime and FFI Architecture](./js-runtime-ffi-architecture.md)

These documents are code-backed planning references and should describe the
current repository shape rather than speculative package layers.
206 changes: 206 additions & 0 deletions architecture/core-architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
# Termisu Architecture

Last verified: 2026-02-28

This document describes the current implementation architecture for Termisu (Crystal core + optional C FFI boundary).

## System Context

```mermaid
flowchart TD
APP[User Application]

subgraph Facade
T[Termisu]
end

subgraph Core[Core Runtime]
TERM[Terminal]
BUF[Buffer]
RSTATE[RenderState]
READER[Reader]
PARSER[Input::Parser]
LOOP[Event::Loop]
INPUT[Event::Source::Input]
RESIZE[Event::Source::Resize]
TIMER[Event::Source::Timer or SystemTimer]
end

subgraph IO[Terminal IO]
BACKEND[Terminal::Backend]
TERMINFO[Terminfo]
end

subgraph OS[OS and Kernel]
TTY[TTY/termios/ioctl]
POLLER[epoll or kqueue or poll]
SIGNAL[SIGWINCH]
end

APP --> T
T --> TERM
T --> READER
T --> PARSER
T --> LOOP

TERM --> BUF
BUF --> RSTATE
TERM --> BACKEND
TERM --> TERMINFO

READER --> BACKEND
PARSER --> READER

LOOP --> INPUT
LOOP --> RESIZE
LOOP --> TIMER

INPUT --> PARSER
RESIZE --> SIGNAL
TIMER --> POLLER

BACKEND --> TTY
POLLER --> OS
```

## Lifecycle Sequence

```mermaid
sequenceDiagram
participant App
participant Termisu
participant Terminal
participant Loop as Event::Loop

App->>Termisu: new(sync_updates: true)
Termisu->>Terminal: initialize backend/terminfo/buffer
Termisu->>Terminal: enable_raw_mode
Termisu->>Loop: add input + resize sources
Termisu->>Loop: start
Termisu->>Terminal: enter_alternate_screen
Termisu-->>App: ready

App->>Termisu: close
Termisu->>Loop: stop all sources + close channel
Termisu->>Terminal: exit_alternate_screen
Termisu->>Terminal: disable_raw_mode
Termisu->>Terminal: close backend
Termisu-->>App: closed
```

## Rendering Pipeline

```mermaid
flowchart TD
A[set_cell or clear] --> B[Buffer.back updated]
B --> C[Dirty row tracking]
C --> D[Termisu.render]
D --> E{sync_updates enabled?}
E -- yes --> F[Emit BSU (Begin Synchronized Update)]
E -- no --> G[Skip BSU (Begin Synchronized Update)]
F --> H[Buffer.render_to diff front vs back]
G --> H
H --> I[Row batch rendering]
I --> J[RenderState apply style/cursor deltas]
J --> K[Terminal writes escape sequences + graphemes]
K --> L[Render cursor visibility/position]
L --> M{sync_updates enabled?}
M -- yes --> N[Emit ESU (End Synchronized Update) + flush]
M -- no --> O[Flush]
N --> P[front buffer synchronized]
O --> P
```

Notes:
- Buffer enforces wide-cell occupancy invariants and skips continuation cells during rendering.
- Cursor tracking is width-aware (`columns_advanced`) so wide graphemes stay aligned.

## Event Pipeline

```mermaid
flowchart LR
subgraph Sources
IN[Input Source]
RS[Resize Source]
TM[Timer Source]
end

IN --> CH[Loop output channel]
RS --> CH
TM --> CH
CH --> PE[poll_event / try_poll_event]
```

Source specifics:
- Input source drains parser events in bounded bursts (`MAX_DRAIN_PER_CYCLE`) to preserve fairness.
- Resize source combines SIGWINCH with periodic polling.
- Timer source options:
- `Timer`: sleep-based, non-blocking send with backpressure accounting.
- `SystemTimer`: poller-based kernel timer, expirations folded into `missed_ticks`.

## Platform Backend Selection

```mermaid
flowchart TD
S[Poller.create]
S --> L{linux?}
L -- yes --> EP[Poller::Linux\nepoll + timerfd]
L -- no --> B{darwin/freebsd/openbsd?}
B -- yes --> KQ[Poller::Kqueue\nEVFILT_READ + EVFILT_TIMER]
B -- no --> PL[Poller::Poll\npoll + monotonic timers]
```

Other platform branches:
- `TTY` uses a FreeBSD/OpenBSD-specific open mode branch.
- terminal size ioctl constants are platform-specific in `Terminal::Backend`.

## Optional C FFI Boundary

```mermaid
flowchart TD
CAPP[C or JS host]
EXPORTS[ffi exports\ntermisu_* symbols]
GUARDS[FFI::Guards]
CORE[FFI::core]
REG[FFI::Registry]
CTX[FFI::Context]
TERM[Termisu instance]

CAPP --> EXPORTS
EXPORTS --> GUARDS
GUARDS --> CORE
CORE --> REG
REG --> CTX
CTX --> TERM
```

Safety model:
- `FFI::Runtime.ensure_initialized` bootstraps shared-library runtime.
- exceptions are translated into status codes + thread-local error text.
- ABI compatibility is checked via `termisu_abi_version` and `termisu_layout_signature`.

## Key Invariants

- Terminal semantics live in Crystal core; wrappers should not reimplement them.
- Buffer front/back state is the source of render diff truth.
- Continuation cells are storage-only; never rendered directly.
- Event loop start/stop is idempotent and source-driven.
- FFI callers must treat non-`OK` statuses as authoritative and read last error text when needed.

## Source Anchors

- [src/termisu.cr](../src/termisu.cr)
- [src/termisu/terminal.cr](../src/termisu/terminal.cr)
- [src/termisu/buffer.cr](../src/termisu/buffer.cr)
- [src/termisu/render_state.cr](../src/termisu/render_state.cr)
- [src/termisu/event/loop.cr](../src/termisu/event/loop.cr)
- [src/termisu/event/source/input.cr](../src/termisu/event/source/input.cr)
- [src/termisu/event/source/resize.cr](../src/termisu/event/source/resize.cr)
- [src/termisu/event/source/timer.cr](../src/termisu/event/source/timer.cr)
- [src/termisu/event/source/system_timer.cr](../src/termisu/event/source/system_timer.cr)
- [src/termisu/event/poller.cr](../src/termisu/event/poller.cr)
- [src/termisu/ffi/exports.cr](../src/termisu/ffi/exports.cr)
- [src/termisu/ffi/core.cr](../src/termisu/ffi/core.cr)
- [src/termisu/ffi/runtime.cr](../src/termisu/ffi/runtime.cr)
- [src/termisu/ffi/layout.cr](../src/termisu/ffi/layout.cr)
- [include/termisu/ffi.h](../include/termisu/ffi.h)
157 changes: 157 additions & 0 deletions architecture/js-runtime-ffi-architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
# Termisu JS Runtime and FFI Architecture

Last verified: 2026-02-28

This document captures the current minimal package architecture for JS bindings
and how it maps to the Crystal core and native artifacts.

## Goals

- Keep rendering, input parsing, Unicode width, and terminal-mode semantics in Crystal.
- Keep the JS surface centered on `@termisu/core`.
- Make platform-specific native delivery possible without exposing multiple
public JS entrypoints.
- Keep behavior consistent across Linux, macOS, and BSD targets.
- Isolate platform differences to native artifact loading and capability reporting.

## Non-Goals

- Reimplementing core terminal semantics in TypeScript.
- Framework-specific adapters and starter templates.

## Ground Truth From Crystal

Platform behavior is compile-time selected in Crystal and already abstracted behind stable APIs.

- `Poller.create` selects `Linux`, `Kqueue`, or `Poll` backend.
- Linux uses `epoll + timerfd`.
- Darwin/FreeBSD/OpenBSD uses `kqueue` timers.
- Fallback `poll` path handles ABI-specific `nfds_t` differences.
- TTY and terminal-size ioctl handling have platform branches.
- FFI exports include ABI version and layout signature checks.

These differences belong in native code, not in JS behavior logic.

## Package Topology

```mermaid
flowchart TD
subgraph App
APP[User app]
end

subgraph JS
CORE[@termisu/core]
end

subgraph NativePkgs
N1[@termisu/native-linux-x64-gnu]
N2[@termisu/native-linux-arm64-gnu]
N3[@termisu/native-linux-x64-musl]
N4[@termisu/native-linux-arm64-musl]
N5[@termisu/native-darwin-x64]
N6[@termisu/native-darwin-arm64]
N7[@termisu/native-freebsd-x64]
N8[@termisu/native-freebsd-arm64]
end

subgraph NativeLib
SO[libtermisu.so or libtermisu.dylib]
end

APP --> CORE
CORE --> SO

CORE -. resolves target package .-> N1
CORE -. resolves target package .-> N2
CORE -. resolves target package .-> N3
CORE -. resolves target package .-> N4
CORE -. resolves target package .-> N5
CORE -. resolves target package .-> N6
CORE -. resolves target package .-> N7
CORE -. resolves target package .-> N8
```

## Native Load Contract

```mermaid
sequenceDiagram
participant App
participant Core as @termisu/core
participant Native as libtermisu

App->>Core: new Termisu({ libraryPath? })
Core->>Core: detectTarget()/resolve package name
Core->>Native: dlopen(symbols)
Core->>Native: termisu_abi_version()
Core->>Native: termisu_layout_signature()
Native-->>Core: ABI + layout signature
Core-->>App: initialized handle
```

## Library Path Resolution

Resolution precedence should be deterministic:

1. explicit `libraryPath` option
2. `TERMISU_LIB_PATH`
3. platform resolver mapping (`os/arch/libc` -> native package)
4. actionable error with target and checked paths

```mermaid
flowchart TD
S[loadNative start] --> E{libraryPath option?}
E -- yes --> P1[resolve explicit path]
E -- no --> V{TERMISU_LIB_PATH set?}
V -- yes --> P2[resolve env path]
V -- no --> M[platform detection and package mapping]
M --> P3[resolve bundled native path]
P1 --> X{exists and loadable?}
P2 --> X
P3 --> X
X -- yes --> ABI[ABI/layout validation]
ABI --> OK[return NativeLibrary]
X -- no --> ERR[throw target-specific install error]
```

## Responsibility Matrix

| Package | Owns | Must not own |
| --- | --- | --- |
| `@termisu/core` | target detection, native package mapping, path resolution, FFI symbol binding, ABI/layout validation, native call wrappers | framework policy |
| `@termisu/native-*` | platform-specific package metadata and artifact delivery | runtime behavior semantics |

## Capability Model

Runtime should consume one capability snapshot at startup.

Suggested fields:
- `platform` (`linux`, `darwin`, `freebsd`, ...)
- `poller_backend` (`linux`, `kqueue`, `poll`)
- `feature_bits` (mouse, enhanced keyboard, system timer, etc.)

Behavior contract:
- unsupported capabilities are exposed as `off` flags
- API semantics do not drift by platform

## Current Implementation Notes

- `@termisu/core` already validates ABI and struct layout signature.
- `@termisu/core` currently resolves explicit paths, `TERMISU_LIB_PATH`,
platform-package candidates, and repository-local `bin/` candidates.
- native packages currently expose manifest metadata and still need artifact
payload and release wiring before they can be auto-loaded end to end.

## Source Anchors

- [src/termisu/event/poller.cr](../src/termisu/event/poller.cr)
- [src/termisu/event/poller/linux.cr](../src/termisu/event/poller/linux.cr)
- [src/termisu/event/poller/kqueue.cr](../src/termisu/event/poller/kqueue.cr)
- [src/termisu/event/poller/poll.cr](../src/termisu/event/poller/poll.cr)
- [src/termisu/tty.cr](../src/termisu/tty.cr)
- [src/termisu/terminal/backend.cr](../src/termisu/terminal/backend.cr)
- [src/termisu/ffi/exports.cr](../src/termisu/ffi/exports.cr)
- [src/termisu/ffi/layout.cr](../src/termisu/ffi/layout.cr)
- [javascript/core/src/native.ts](../javascript/core/src/native.ts)
- [javascript/core/src/termisu.ts](../javascript/core/src/termisu.ts)
- [javascript/core/src/platform.ts](../javascript/core/src/platform.ts)
Loading
Loading