Keyseq_terminalSourceTerminal renderer for Keyseq event sequences.
Executes a Keyseq.event list against an out_channel, using real time for Sleep and Idle and ANSI control sequences for Delete and cursor blinking during Idle. This is one possible renderer; the contract Keyseq itself makes no commitment to how Idle is displayed — the choices below are specific to this module.
run events executes every event in events in order and returns () once the last one has completed.
Insert writes the codepoint's UTF-8 encoding to out.
Delete erases exactly one previously written codepoint: moves the cursor back one terminal column, erases that column, moves back again.
Sleep d waits d seconds in real time before continuing; no output is produced.
Idle d waits d seconds in real time, during which the cursor blinks at a fixed 2 Hz rate (500ms visible, 500ms hidden), starting visible. A final partial half-period shorter than 500ms is a plain wait with no further toggle. The cursor is left visible once Idle ends, before the next event runs.
Sound s calls on_sound s. on_sound defaults to a no-op — this module never produces audible output on its own.
out defaults to stdout. No check is made that out refers to a terminal; against a non-tty channel (e.g. a redirected file), the ANSI sequences for Delete and cursor blinking are written as literal bytes rather than interpreted, which is harmless but will look like escape-code noise if inspected directly.
Failure. IO failures on out (a closed channel, a broken pipe) are not caught here; they propagate as Sys_error. This function otherwise assumes its input satisfies the invariants documented on Keyseq.event — in particular, that Delete is never applied to an empty buffer — and performs no defensive check for a violation that only a bug in Keyseq.plan, not a runtime condition, could produce.
wrap ~width text reflows text to fit within width terminal columns, breaking only at whitespace — analogous to CSS's overflow-wrap: normal. A single word longer than width is left on its own line and allowed to overflow rather than being split.
Existing whitespace (spaces, tabs, newlines, carriage returns) is treated purely as word separation: runs of whitespace collapse to a single space or line break in the output, and any original line breaks are not preserved. This function does not call run or perform any IO; feed its result into Keyseq.plan before executing. Terminal width itself is an environment concern this module does not query — the caller determines width (e.g. via tput cols) and supplies it.
Column width is approximated as one column per Unicode codepoint, consistent with the same simplification Keyseq makes elsewhere; this under-counts genuinely wide (e.g. East Asian) characters, which is out of scope. Malformed UTF-8 is not rejected here — unlike Keyseq.plan, wrap is a display convenience, not the point where input is validated; on malformed input the column count for the affected word may be inaccurate, but wrap will not raise on that account.
Raises Invalid_argument if width <= 0.