Core Library0.xAPI Reference

Choose documentation 1

latest

Current version

latest
English

Early release libtmux is released, and its APIs may still change between releases. This site's structure and URLs may change too.

module libtmux-raku latest

LibTmux

Source and package
Qualified name
LibTmux
Package
LibTmux
Source
lib/LibTmux.rakumod
Anonymous module

Description#

LibTmux

Typed tmux snapshots, local selections and explicit commands for Raku programs.

Create a session and split a pane

Server starts and owns a private daemon. This program splits the first pane and titles both; each runs cat. LEAVE closes the owner on any exit.

use LibTmux;
my $server = Server.start(:name<work>, :shell-command('exec cat'));
LEAVE $server.close if $server.defined;
my $before = $server.snapshot;
my $left = $before.panes.single.ref;
my $right = $left.split(:horizontal, :shell-command('exec cat'));
$left.set-title('editor');
$right.set-title('logs');
my $after = $server.snapshot;
my $editor = $after.panes.filter(*.title eq 'editor').single;
say "{$editor.title}: {$after.panes.elems} panes";

It prints editor: 2 panes.

  • The earlier snapshot still has one pane: mutation does not change an

observation.

  • .ref builds a command target. snapshot performs I/O; reading a

captured field or relationship stays local.

Follow the captured graph

A Snapshot holds sessions, session groups, physical windows, window links, panes, clients and buffer metadata.

  • A physical window can appear at several indexes, even twice in one

session. Its WindowLink owns the session-relative index and active state.

  • windows and panes list each physical identity once; window-links

keeps every placement.

  • A pane's window and a window's panes resolve inside the snapshot.

Field reads, traversal and selection make no tmux calls, even after the acquisition owner closes. Use .ref.inspect or a new snapshot for fresh data.

  • References carry daemon incarnation evidence, so a replacement daemon that

reuses an ID does not inherit an old pane reference.

Query captured values

Combine validated query data with native Raku predicates and transformations:

use v6.d;
use LibTmux;
sub MAIN(Str:D :$tmux = 'tmux') {
my $server = Server.start(:binary($tmux), :name<work>,
:width(160), :height(40), :shell-command('exec cat'));
LEAVE $server.close if $server.defined;
my $editor = $server.snapshot.panes.single.ref;
my $tests = $editor.split(:horizontal, :shell-command('exec cat'));
my $logs = $tests.split(:shell-command('exec cat'));
$editor.set-title('editor');
$tests.set-title('tests');
$logs.set-title('logs');
my $snapshot = $server.snapshot;
my @wide := $snapshot.panes.where(%(width => %(gte => 60)));
my $selected = @wide.filter(*.title eq 'editor').single;
say "Wide pane: {$selected.title}";
my @others = $snapshot.panes.grep(*.id ne $selected.id).map(*.title).sort;
say 'Other panes: ' ~ @others.join(', ');
}

It prints Wide pane: editor and Other panes: logs, tests.

  • Bind with := to keep a typed selection. Array assignment copies

ordinary Raku values; scalar assignment itemizes the collection.

  • where validates fields and operators even on an empty selection.
  • filter runs its callback eagerly and returns the same selection type.

A Failure returned directly from the callback throws its wrapped exception before Boolean coercion.

  • grep (including :k, :kv and :p) returns a native Seq.

map, sort, first and junctions keep plain Raku behavior; mapping panes to titles gives strings, not a selection.

  • single wants exactly one member and single-or-nil allows none.

Several matches throw Cardinality. one stays a junction.

Compile reusable criteria with Filter:

  • ACCEPTS permits native matching; to-json and from-json keep the

bounded portable data grammar.

  • Closures, regexes and junctions stay local Raku matchers. Data equality

follows field types, not generic smartmatch.

  • Relations use some, every, none, is and is-not. An unloaded

relation raises MissingData instead of acting like an empty one.

Choose where a query runs

query-panes takes where and a pushdown policy:

  • auto has tmux apply the restrictions it can match exactly and checks

the rest locally.

  • never evaluates locally after acquisition.
  • require refuses a query that cannot run exactly in tmux.

explain-panes describes projections, scope, pushed conditions and residual work. Arbitrary callbacks are never translated to tmux.

use v6.d;
use LibTmux;
sub MAIN(Str:D :$tmux = 'tmux') {
my $server = Server.start(:binary($tmux), :name<work>, :shell-command('exec cat'));
LEAVE $server.close if $server.defined;
$server.snapshot.panes.single.ref.set-title('editor');
my %wide = width => %(gte => 60);
my %titled = AND => [%wide, %(title => %(contains => 'edit'))];
say 'width: fully pushed = ' ~ $server.explain-panes(:where(%wide)).fully-pushed;
my $plan = $server.explain-panes(:where(%titled));
say 'width and title: fully pushed = ' ~ $plan.fully-pushed;
say 'tmux filter: ' ~ $plan.source-filter;
say 'Matches: ' ~ $server.query-panes(:where(%titled)).map(*.title).join(', ');
try {
$server.query-panes(:where(%titled), :pushdown<require>);
CATCH { when X::LibTmux::Unsupported { say 'require: refused, title stays local' } }
}
}

It prints whether each query is fully pushed to tmux, the filter tmux gets, the match, and the refusal.

Choose an operation from capabilities

Capabilities describe the daemon, not the client executable. Ask supports before offering a newer operation, or call require for a typed refusal with its reason. This program creates an empty status pane, floating when supported and tiled otherwise:

use v6.d;
use LibTmux;
sub MAIN(Str:D :$tmux = 'tmux') {
my $server = Server.start(:binary($tmux), :name<work>,
:width(120), :height(36), :shell-command('exec cat'));
LEAVE $server.close if $server.defined;
my $snapshot = $server.snapshot;
my $main = $snapshot.panes.single.ref;
my $floating = $snapshot.capabilities.supports('pane-floating');
my $auxiliary = $floating
?? $main.new-floating(:width(40), :height(12), :x(2), :y(2), :empty)
!! $main.split(:horizontal, :empty);
$auxiliary.set-title('status');
my $after = $server.snapshot;
die 'The auxiliary pane was not captured' unless $after.panes.elems == 2;
say 'Status pane: ' ~ ($floating ?? 'floating' !! 'tiled');
say $after.panes.where(%(id => $auxiliary.id)).single.title;
}

It prints the placement, then status.

  • An empty pane can have no child PID. Its pid is then null, not a made-up

integer; state('pid') tells known null from a field that was not acquired.

  • Unknown tmux releases are refused. Field, codec, layout and command

decisions come from one packaged catalogue.

React to terminal output

An -async method returns a Promise of the same result. A ControlConnection owns one persistent client; its event Supply is shared by subscribers. Native react and whenever consume output without polling:

use v6.d;
use LibTmux;
use LibTmux::Control;
sub MAIN(Str:D :$tmux = 'tmux') {
my $server = Server.start(:binary($tmux), :name<output>, :shell-command('exec cat'));
LEAVE $server.close if $server.defined;
my $snapshot = await $server.snapshot-async;
my $pane = $snapshot.panes.single.ref;
my $connection = await $server.open-control-async(:session-id($snapshot.sessions.single.id));
LEAVE $connection.close if $connection.defined;
my $message = 'hello from Raku';
my $received = '';
react {
whenever $connection.events.grep(PaneOutput) -> $event {
if $event.pane-id eq $pane.id {
$received ~= $event.data.decode('utf8-c8');
die 'Pane output exceeded its bound' if $received.chars > 65536;
done if $received.contains($message);
}
}
whenever start { $pane.send-text($message); $pane.send-keys('Enter') } { }
whenever Promise.in(2) { die 'Pane output timed out' }
}
die 'The captured screen did not contain the message'
unless $pane.capture.decode('utf8').contains($message);
say $message;
say 'Screen capture confirmed';
}

It prints hello from Raku and Screen capture confirmed.

  • Subscribe before sending input; the deadline bounds the wait.
  • send-text never appends Enter.
  • Output stays bytes until you pick a decoder, so fragmented or invalid

UTF-8 survives. Unknown notification types keep their raw form.

Close each connection:

  • Cancellation stops and joins owned client work. It does not undo commands

the daemon already ran.

  • After reconnecting, take a fresh observation; events do not add up to a

complete current graph.

Recover from partial completion

Batch reports each operation's completion scope and outcome. The batch recovery program shows Complete, Failed and Skipped, then removes the first effect itself. Acknowledgement alone does not prove an application effect, and a failed batch is not rolled back.

The session workflow program combines creation, control output, capture, local filtering and an explained source query.

Ownership and operational errors

  • Server.start owns its private daemon. Server.new configures a

borrowed endpoint; closing that facade leaves the daemon running. Close both.

  • A connection or terminal attachment has its own lifetime; close it too.
  • An observation keeps values and the captured graph, not a live

acquisition process.

  • Commands pass an argument vector. Literal pane text, key presses, FORMAT

expressions, tmux command templates and deliberate shell commands have separate entry points.

  • Errors keep typed context and captured bytes. A missing optional lookup

never disguises a failed command.

  • Fields keep known null, false, zero, empty text, unsupported, unavailable

and unloaded data as distinct states.

Members18#

Common operations1#

Other members17#

Esc

Type to search.

Reference previewOpen reference