io.worxbend.tui.runtime

Members list

Type members

Classlikes

object Async

Structured background work for a signals-driven app — glyphora's answer to bubbletea's Cmd/Msg.

Structured background work for a signals-driven app — glyphora's answer to bubbletea's Cmd/Msg.

The problem it solves: Signals may only be mutated on the render thread (RenderThread), so a naive new Thread { data.set(fetch()) } throws. Async runs the work off-thread, then marshals the continuation back onto the render thread (via RenderThread.runLater), where it may safely update signals. The runner drains that queue at the top of each loop iteration and repaints if a signal changed — so the result appears as an ordinary reactive update, no Msg plumbing.

All executor threads are daemons, so pending work never keeps the JVM alive after the app quits. Callbacks scheduled while no runner is active simply wait on the queue until one drains it (or are dropped when the process exits).

Attributes

Supertypes
class Object
trait Matchable
class Any
Self type
Async.type

How Async.run reports a failure of its background work. Provided as a using value so apps can install their own (log, toast, set an error signal) without changing call sites.

How Async.run reports a failure of its background work. Provided as a using value so apps can install their own (log, toast, set an error signal) without changing call sites.

Attributes

Companion
object
Supertypes
class Object
trait Matchable
class Any

Attributes

Companion
trait
Supertypes
class Object
trait Matchable
class Any
Self type
trait Cancelable

A handle to cancel a scheduled or repeating task started through Async.

A handle to cancel a scheduled or repeating task started through Async.

Attributes

Companion
object
Supertypes
class Object
trait Matchable
class Any
object Cancelable

Attributes

Companion
trait
Supertypes
class Object
trait Matchable
class Any
Self type
Cancelable.type
final class Computed[A] extends Reactive[A]

A value derived from other reactive values.

A value derived from other reactive values.

Lazily cached: set on a dependency only marks this stale (cascading to dependents); the thunk re-runs on the next read. Each recomputation first unsubscribes from the previous dependency set, then re-subscribes to exactly what the thunk reads this time — the mechanism that makes conditional dependencies correct.

The dependency graph must be acyclic. A thunk that reads the value it is itself computing — directly, or around a cycle through other computeds — throws IllegalStateException on the read that closes the loop, rather than recursing until the stack runs out.

Attributes

Companion
object
Supertypes
trait Reactive[A]
class Object
trait Matchable
class Any
object Computed

Attributes

Companion
class
Supertypes
class Object
trait Matchable
class Any
Self type
Computed.type
final class Derived[A] extends Reactive[A]

A transparent view of another reactive value: read re-runs on every access and nothing is cached.

A transparent view of another reactive value: read re-runs on every access and nothing is cached.

Unlike Computed this holds no subscription of its own — a tracked read passes the caller's scope straight through to the underlying values, so the caller subscribes to the source rather than to an intermediate. There is consequently no lifecycle and nothing to dispose: an instance created inside a view body and abandoned after one frame leaves no edge behind. The trade is that read is evaluated per access; use Computed when the derivation is expensive enough to be worth caching, and give that computed an owner that disposes it.

Holds no mutable state, so an instance may be read from any thread its source allows.

Attributes

Companion
object
Supertypes
trait Reactive[A]
class Object
trait Matchable
class Any
object Derived

Attributes

Companion
class
Supertypes
class Object
trait Matchable
class Any
Self type
Derived.type
enum Easing

Progress curves for effects and tweens: map linear time t ∈ [0, 1] to eased progress.

Progress curves for effects and tweens: map linear time t ∈ [0, 1] to eased progress.

The families follow the conventional Penner set; Back/Elastic/Bounce overshoot or oscillate, so they can leave [0, 1] mid-flight even though they start at 0 and end at 1.

Attributes

Companion
object
Supertypes
trait Enum
trait Serializable
trait Product
trait Equals
class Object
trait Matchable
class Any
Show all
object Easing

Attributes

Companion
enum
Supertypes
trait Sum
trait Mirror
class Object
trait Matchable
class Any
Self type
Easing.type
trait Effect

A post-render frame transform (the tachyonfx model, original implementation): widgets render normally, then active effects mutate the rendered cells based on elapsed time. Effects are stateless in wall-clock — the runtime tracks each effect's start and passes total elapsed, which keeps combinators pure and replayable.

A post-render frame transform (the tachyonfx model, original implementation): widgets render normally, then active effects mutate the rendered cells based on elapsed time. Effects are stateless in wall-clock — the runtime tracks each effect's start and passes total elapsed, which keeps combinators pure and replayable.

Attributes

Companion
object
Supertypes
class Object
trait Matchable
class Any
object Effect

Attributes

Companion
trait
Supertypes
class Object
trait Matchable
class Any
Self type
Effect.type
final class Frame(val area: Rect, val buffer: Buffer)

One frame being rendered: the drawable area plus the buffer widgets write into.

One frame being rendered: the drawable area plus the buffer widgets write into.

The buffer itself stays module-private — application render code goes through the widget contract, which keeps every write attributable to a widget and an area.

Attributes

Supertypes
class Object
trait Matchable
class Any
final class GenerationalScope extends ReactiveScope

A tracking scope for a repeatedly re-evaluated computation (an app's view): reads subscribe onInvalidate, and beginGeneration — called before each re-evaluation — unsubscribes from values that stopped being read, so signals owned by closed screens or discarded branches do not accumulate stale subscriptions.

A tracking scope for a repeatedly re-evaluated computation (an app's view): reads subscribe onInvalidate, and beginGeneration — called before each re-evaluation — unsubscribes from values that stopped being read, so signals owned by closed screens or discarded branches do not accumulate stale subscriptions.

Attributes

Supertypes
class Object
trait Matchable
class Any
final case class QueuedTaskFailures(first: Throwable, count: Int)

Every queued-body failure one run absorbed, collapsed into a single report.

Every queued-body failure one run absorbed, collapsed into a single report.

first is the throwable that started it and count how many failures there were in total; the later throwables are attached to first as suppressed exceptions, up to a cap — so a continuation that fails on every tick for a week neither reports as one incident nor accumulates a week of stack traces.

Attributes

Supertypes
trait Serializable
trait Product
trait Equals
class Object
trait Matchable
class Any
Show all
sealed trait Reactive[A]

A readable reactive value: a mutable Signal, a cached Computed, or a transparent Derived view.

A readable reactive value: a mutable Signal, a cached Computed, or a transparent Derived view.

Reads come in two flavors: get requires a ReactiveScope capability and subscribes the enclosing computation to future changes (automatic dependency tracking — no manual dependency arrays); peek reads untracked. Dependency edges are re-established on every recomputation, so conditional reads (if cond.get then a.get else b.get) subscribe exactly the branch that actually ran.

Attributes

Supertypes
class Object
trait Matchable
class Any
Known subtypes
class Computed[A]
class Derived[A]
class Signal[A]

The capability that makes a reactive read tracked: Reactive.get requires one and reports the read to it, so whoever owns the scope learns what was read and can subscribe to changes. Reads that should not subscribe anything use peek instead — or ReactiveScope.untracked when an API demands a scope.

The capability that makes a reactive read tracked: Reactive.get requires one and reports the read to it, so whoever owns the scope learns what was read and can subscribe to changes. Reads that should not subscribe anything use peek instead — or ReactiveScope.untracked when an API demands a scope.

Attributes

Companion
object
Supertypes
class Object
trait Matchable
class Any
Known subtypes
object ReactiveScope

Attributes

Companion
trait
Supertypes
class Object
trait Matchable
class Any
Self type

How a render loop reports a NonFatal throwable escaping a body queued with RenderThread.runLater — typically the continuation of some background work (see Async).

How a render loop reports a NonFatal throwable escaping a body queued with RenderThread.runLater — typically the continuation of some background work (see Async).

Installed per loop rather than passed at the call site the way AsyncErrorHandler is: each runner owns its own queue, and the queue is drained long after the caller that filled it is gone. It runs on the render thread, inside the drain; anything it throws propagates and stops that drain, which is exactly how RenderTaskErrorHandler.rethrow surfaces a failure on a loop no runner owns.

Attributes

Companion
object
Supertypes
class Object
trait Matchable
class Any

Attributes

Companion
trait
Supertypes
class Object
trait Matchable
class Any
Self type
object RenderThread

The single-render-thread model.

The single-render-thread model.

All UI state mutation must happen on a render thread — the thread running a Runner loop. The guard is deliberately a no-op while no render thread is registered, so unit tests of widgets and signals need no running runtime.

Each runner owns its own work queue rather than sharing one process-wide, so two runners in the same JVM (parallel test suites, an embedded app) never execute each other's queued work on the wrong thread.

Attributes

Supertypes
class Object
trait Matchable
class Any
Self type
trait Runner

The mid-level API tier: owns the event/render loop over a Backend.

The mid-level API tier: owns the event/render loop over a Backend.

handleEvent returns whether the UI should redraw; render fills the frame on each redraw. run blocks until the app quits (via RunnerHandle.quit) or the backend fails, and always restores the terminal on the way out. The calling thread becomes the render thread for the duration of run.

Attributes

Supertypes
class Object
trait Matchable
class Any
Known subtypes
final case class RunnerConfig(tickRate: Option[Duration], mouseCapture: Boolean, onTaskError: Option[RenderTaskErrorHandler])

Runner configuration: an optional tick rate (synthetic Event.Ticks for animation) and whether to capture mouse events.

Runner configuration: an optional tick rate (synthetic Event.Ticks for animation) and whether to capture mouse events.

onTaskError decides what happens when a body queued onto the render thread (an Async continuation, a timer body) throws. None — the default — accumulates them and returns them from Runner.run as RunnerError.QueuedTask once the app exits: the first throwable, the total count, and the later throwables attached to the first as suppressed exceptions. Installing a handler takes reporting over instead: it is called as each failure happens, and run then returns Right unless the backend itself failed. Either way the loop survives the failing body and the bodies queued behind it still run — but only as long as the reporting itself does not throw. A handler that throws is not isolated: it unwinds out of the drain, abandoning the bodies queued behind it, and out of Runner.run. Keep a handler total.

Attributes

Supertypes
trait Serializable
trait Product
trait Equals
class Object
trait Matchable
class Any
Show all

Attributes

Supertypes
trait Enum
trait Serializable
trait Product
trait Equals
class Object
trait Matchable
class Any
Show all
trait RunnerHandle

Handed to event handlers: request loop exit, marshal work onto the render thread, or reach the backend for out-of-band terminal operations like clipboard access.

Handed to event handlers: request loop exit, marshal work onto the render thread, or reach the backend for out-of-band terminal operations like clipboard access.

Attributes

Supertypes
class Object
trait Matchable
class Any
final class Signal[A] extends Reactive[A]

A mutable reactive variable.

A mutable reactive variable.

set/update mark dependents stale and (via the root scope) schedule a redraw; nothing recomputes eagerly. Setting an equal value notifies nobody — equality is ==, except that floating-point values compare by IEEE-754 total order (see unchanged). A value mutated in place is equal to itself, so set(sameInstance) never notifies: hold immutable values in a signal, or set a new instance. Must only be called from the render thread once one is registered — enforced by RenderThread.checkRenderThread(), which is a no-op in tests with no running runtime.

Writing is render-thread-only, but peek may be called from any thread: the value is @volatile, so a reader outside the render thread is guaranteed to see the most recently set value rather than an arbitrarily stale one. That guarantee is what makes a test harness sound — Pilot drives the app from the test thread while the runner mutates signals on its own, and asserting on peek from there would otherwise be reading a field with no happens-before edge to the write. The subscriber set is deliberately not published that way; it is touched only on the render thread.

Attributes

Companion
object
Supertypes
trait Reactive[A]
class Object
trait Matchable
class Any
object Signal

Attributes

Companion
class
Supertypes
class Object
trait Matchable
class Any
Self type
Signal.type
final case class Spring(frequency: Double, damping: Double, deltaTime: Double)

A damped-spring integrator (à la Charm's Harmonica) for physical, non-linear motion — scrolling, progress fills, layout transitions.

A damped-spring integrator (à la Charm's Harmonica) for physical, non-linear motion — scrolling, progress fills, layout transitions.

Unlike an Easing, a spring has no fixed duration: call step each tick with the current position and velocity and it eases toward target, overshooting or settling per frequency (stiffness) and damping (< 1 bouncy, 1 critically damped, > 1 sluggish). Integrated semi-implicitly, stable for the usual TUI tick rates.

deltaTime must be positive: a spring with a non-positive step can never advance, so the documented while !settled(...) do step(...) loop would hang. A defect in the caller, hence a construction-time throw.

Attributes

Supertypes
trait Serializable
trait Product
trait Equals
class Object
trait Matchable
class Any
Show all
final class Stopwatch(initial: FiniteDuration)

A count-up timer advanced from the app's tick loop.

A count-up timer advanced from the app's tick loop.

Caller-owned mutable state, like the widget states: call tick each frame with the elapsed real time (e.g. the runner's tickRate), and read elapsed / formatted in view. Time only accrues while isRunning.

Attributes

Supertypes
class Object
trait Matchable
class Any
final class TerminalRunner(backend: Backend, config: RunnerConfig, nanoTime: () => Long, redrawRequested: () => Boolean) extends Runner

The production Runner: raw mode + alternate screen setup, diff-driven redraws, tick emission, resize handling, and render-thread registration around a Backend.

The production Runner: raw mode + alternate screen setup, diff-driven redraws, tick emission, resize handling, and render-thread registration around a Backend.

nanoTime is injectable so tick scheduling is testable; production code uses the system clock.

Attributes

Supertypes
trait Runner
class Object
trait Matchable
class Any
final class Timer(val duration: FiniteDuration)

A count-down timer advanced from the app's tick loop; fires (via justExpired) once when it reaches zero.

A count-down timer advanced from the app's tick loop; fires (via justExpired) once when it reaches zero.

Caller-owned mutable state: call tick each frame with the elapsed real time, and read remaining / isExpired / formatted in view.

Attributes

Supertypes
class Object
trait Matchable
class Any
final case class Tween(from: Double, to: Double, duration: FiniteDuration, easing: Easing)

A value animated from from to to over duration with an easing curve — for animating gauge ratios, offsets, and the like from onTick state.

A value animated from from to to over duration with an easing curve — for animating gauge ratios, offsets, and the like from onTick state.

Attributes

Supertypes
trait Serializable
trait Product
trait Equals
class Object
trait Matchable
class Any
Show all

Value members

Concrete methods

def formatDuration(duration: FiniteDuration): String

hh:mm:ss (or mm:ss under an hour) for a non-negative duration — the usual readout for Stopwatch/Timer.

hh:mm:ss (or mm:ss under an hour) for a non-negative duration — the usual readout for Stopwatch/Timer.

Attributes