<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>TUIOS releases</title>
    <link>https://tuios.gaurav.zip/releases</link>
    <description>What changed in each release of TUIOS.</description>
    <language>en</language>
    <lastBuildDate>Tue, 22 Sep 2026 00:00:00 GMT</lastBuildDate>
    <atom:link href="https://tuios.gaurav.zip/releases/rss.xml" rel="self" type="application/rss+xml"/>
    <image>
      <url>https://tuios.gaurav.zip/tuios-icon.png</url>
      <title>TUIOS releases</title>
      <link>https://tuios.gaurav.zip/releases</link>
    </image>
    <item>
      <title>Everything since v0.7.0</title>
      <link>https://tuios.gaurav.zip/releases/since-v0-7-0</link>
      <guid isPermaLink="true">https://tuios.gaurav.zip/releases/since-v0-7-0</guid>
      <pubDate>Tue, 22 Sep 2026 00:00:00 GMT</pubDate>
      <dc:creator>Gaurav Gosain</dc:creator>
      <description>Unreleased work on main: session layouts that come back after a daemon restart, sessions and panes on other machines, agents you can watch and message, one layout for every client, an interface that answers the mouse, and a large cut in memory use.</description>
      <content:encoded><![CDATA[<p>1,805 commits since v0.7.0, across 1,817 files, up to 22 September 2026. None
of it is in a tagged release yet. It will ship as the next one.</p>
<p>The page is grouped by what you notice, most important first. If you already
run tuios, read <a href="https://tuios.gaurav.zip/releases/since-v0-7-0#before-you-upgrade">Before you upgrade</a> first.</p>
<h2>In short</h2>
<ul>
<li><a href="https://tuios.gaurav.zip/releases/since-v0-7-0#before-you-upgrade">Before you upgrade</a>: wire protocol 3, new defaults,
SSH key checks, hooks in the daemon.</li>
<li><a href="https://tuios.gaurav.zip/releases/since-v0-7-0#layouts-come-back-after-a-restart">Layouts come back</a> after a daemon
restart, with new shells in the old directories.</li>
<li><a href="https://tuios.gaurav.zip/releases/since-v0-7-0#other-machines">Other machines</a>: remote sessions, remote panes, global
sessions.</li>
<li><a href="https://tuios.gaurav.zip/releases/since-v0-7-0#agents">Agents</a>: agent state in the rail, mail, worktree fan-out.</li>
<li><a href="https://tuios.gaurav.zip/releases/since-v0-7-0#one-layout-for-every-client">One layout for every client</a>.</li>
<li><a href="https://tuios.gaurav.zip/releases/since-v0-7-0#the-mouse">The mouse</a> (including clickable links), <a href="https://tuios.gaurav.zip/releases/since-v0-7-0#the-rail">the rail</a>, and
<a href="https://tuios.gaurav.zip/releases/since-v0-7-0#tiling-and-zoom">tiling and zoom</a>.</li>
<li><a href="https://tuios.gaurav.zip/releases/since-v0-7-0#keys">Keys</a>: keybind manager, launcher, prefix repeat.</li>
<li><a href="https://tuios.gaurav.zip/releases/since-v0-7-0#look-and-feel">Look and feel</a>: themes, glyphs, spotlight, screen saver.</li>
<li><a href="https://tuios.gaurav.zip/releases/since-v0-7-0#graphics-and-screenshots">Graphics and screenshots</a> and
<a href="https://tuios.gaurav.zip/releases/since-v0-7-0#the-web-client">the web client</a>.</li>
<li><a href="https://tuios.gaurav.zip/releases/since-v0-7-0#scripting-and-control">Scripting and control</a>: 62 verbs, 156 options.</li>
<li><a href="https://tuios.gaurav.zip/releases/since-v0-7-0#project-tapes-and-startup">Project tapes and startup</a>.</li>
<li><a href="https://tuios.gaurav.zip/releases/since-v0-7-0#the-terminal-emulator">The terminal emulator</a>: a second backend and a
conformance suite.</li>
<li><a href="https://tuios.gaurav.zip/releases/since-v0-7-0#faster-and-smaller">Faster and smaller</a>, and
<a href="https://tuios.gaurav.zip/releases/since-v0-7-0#other-fixes">other fixes</a>.</li>
</ul>
<h2>Before you upgrade</h2>
<p>Most of these need nothing from you.</p>
<ul>
<li><strong>Restart the daemon.</strong> The wire protocol is version 3, and version 2 is
refused with a message that says which side is newer. (A message type added
after v0.7.0 shifted every type number without a version change, so mixed
builds misread each other.) Run <code>tuios kill-server</code> and start tuios again.
This ends every program running in your panes. The layout, window names and
working directories come back, each pane with a new shell; scrollback does
not. The JSON verb protocol stays at version 1.</li>
<li><strong>New defaults on a fresh install.</strong> A bare <code>tuios</code> starts or joins the
daemon (<code>startup.daemon = true</code>), starts tiled (<code>startup.tiled = true</code>), and
draws window controls as dots on the left of the title bar. An existing
config file keeps what it says: the <code>[startup]</code> booleans are only read from
the file, so a machine that ran standalone and floating still does. The two
window-button options are strings and do reach a config that never set them.
<code>tuios --standalone</code> or <code>TUIOS_NO_DAEMON=1</code> runs without the daemon. If the
daemon will not start, a bare <code>tuios</code> runs standalone for that run and says
so.</li>
<li><strong>The SSH server checks keys.</strong> <code>tuios ssh</code> used to give a shell to every
connection. It now admits the public keys in
<code>~/.config/tuios/authorized_keys</code>, or in <code>~/.ssh/authorized_keys</code> when the
first file is absent. <code>--authorized-keys</code> names another file. With no keys, a
loopback bind still runs unauthenticated and prints a warning, and any other
address is refused. <code>--no-auth</code> opens it on a network you trust. A keys file
that cannot be read, does not parse, or holds no key stops startup.</li>
<li><strong>Unnamed SSH connections.</strong> A connection that names no session, and whose
username maps to none, now gets a session called <code>ssh-session</code> when several
exist. It used to get whichever session was listed first. With exactly one
session, it still gets that one.</li>
<li><strong>Hooks run in the daemon.</strong> <code>after-new-window</code>, <code>after-close-window</code>,
<code>after-focus-change</code>, <code>after-workspace-switch</code> and <code>after-agent-state</code> now
run in the daemon, with the daemon's environment. They fire with nobody
attached, and once rather than once per client. <code>after-attach</code>,
<code>after-detach</code>, <code>after-resize</code> and <code>after-layout-change</code> stay in the client,
and <code>after-detach</code> now also fires when an SSH connection or a browser tab
goes away.</li>
<li><strong>Window-mode keys.</strong> Digits 1 to 4 select windows. They used to snap a
window to a corner, which is now <code>ctrl+b L</code> then 1 to 4. A config still
carrying the default corner-snap bindings is migrated on load. Bindings you
changed are left alone. With tiling on, <code>h</code> and <code>l</code> now focus the pane on
that side instead of doing nothing. In the layout picker, delete moved from
<code>d</code> to <code>ctrl+d</code>, so typing a filter no longer deletes a layout.</li>
<li><strong>Right-click.</strong> In window-management mode, a right-click on a pane opens the
pane menu and a right-drag resizes. It used to only resize. In terminal mode
the right button belongs to the pane: ctrl or shift with right-click opens
the menu, and <code>appearance.right_click_opens_menu = true</code> makes a plain
right-click open it too. A program that asked for the mouse always keeps the
plain right button.</li>
<li><strong>Frame rate.</strong> <code>appearance.max_fps</code> now tops out at 120, the renderer's own
ceiling. Values up to 240 were accepted before and changed nothing on screen.</li>
<li><strong>Selection colours.</strong> The selection, search matches and copy-mode cursor
are now settings under <code>[appearance.selection]</code>, with new defaults: a neutral
grey selection, amber search matches, and a cyan copy-mode cursor. Selected
text is no longer bold. <code>appearance.selection.bold = true</code> brings that back.</li>
<li><strong>Sidebar settings</strong> moved from flat <code>appearance.sidebar_*</code> keys into an
<code>[appearance.sidebar]</code> table. The old keys are still read, the table wins
where both are set, and the file is not rewritten in the old spelling.</li>
<li><strong>Removed.</strong> <code>appearance.separator_style</code>, which parsed and did nothing. The
<code>colorshift</code> screen saver effect: a config that names it reads as <code>random</code>.</li>
</ul>
<h2>Layouts come back after a restart</h2>
<p>The daemon saves each session's structure under
<code>$XDG_STATE_HOME/tuios/sessions</code> and restores it when it starts. This is on by
default. A detach loses nothing, because the daemon keeps the shells running.
A daemon restart or a reboot ends them: what comes back is the layout, not the
programs.</p>
<ul>
<li>Each window comes back with its workspace, its place in the layout, its name
and its working directory. The session keeps its display name and accent.</li>
<li>Each shell is new, started in the old directory. Scrollback and running
programs are not saved.</li>
<li>Restored shells get <code>TUIOS_RESTORED=1</code>, and <code>tuios ls</code> marks the session as
restored until you first attach.</li>
<li>A window whose shell will not start is dropped rather than restored empty.</li>
<li>A state file tuios cannot read is moved aside for two weeks instead of
stopping the daemon.</li>
<li>Working directories now come back on macOS too. The daemon used to read them
from <code>/proc</code>, which macOS does not have.</li>
</ul>
<p><code>tuios daemon --no-restore</code> turns restore off. <code>tuios resurrect</code> lists what is
saved, or brings one session back by name.</p>
<p>The daemon is also easier to reach. A bare <code>tuios</code> uses it by default (see
above). <code>tuios attach</code> starts the daemon instead of refusing. <code>tuios ls</code> with
no daemon running lists the sessions saved on disk, marked as saved, and exits
3, so a script can tell a stopped daemon from a running one that holds nothing.</p>
<p>Daemon panes now honour two settings they used to ignore:
<code>appearance.scrollback_lines</code> (the daemon kept 10,000 lines per pane whatever
the file said) and <code>appearance.preferred_shell</code>.</p>
<h2>Other machines</h2>
<p>tuios can now work across machines. Every link is one <code>ssh</code> process per host,
run with <code>BatchMode=yes</code>, so you need to be able to <code>ssh</code> to the machine
without typing anything. See <a href="https://tuios.gaurav.zip/docs/sessions">Sessions</a> for the full model.</p>
<h3>Adding a machine</h3>
<pre><code class="language-bash">tuios hosts add build gaurav@buildbox   # writes [hosts.build] in config.toml
tuios hosts test build                  # dials it now and says what it found
tuios hosts remove build
tuios hosts                             # every link and its state
</code></pre>
<ul>
<li>The daemon follows the config file, so none of these needs a restart. The
settings page has a Hosts section that does the same.</li>
<li>An address is anything <code>ssh</code> understands. Your <code>~/.ssh/config</code> aliases are
offered in shell completion and on the settings page.</li>
<li><code>tuios hosts tailnet</code> lists the machines on your tailnet and which ones it
would offer. <code>tuios hosts add NAME --tailnet</code> takes the address from the
machine of that name. It asks the <code>tailscaled</code> already running, with no root
and no auth key.</li>
<li>The link finds tuios on the far machine by itself: on the <code>PATH</code>, in the
usual install paths, then through the login shell. <code>tuios hosts test</code> prints
where it looked when it cannot find it.</li>
<li>A link that drops is dialled again with a backoff. <code>tuios hosts</code> shows each
link as <code>up</code>, <code>connecting</code>, <code>reconnecting</code>, <code>unreachable</code>, <code>no_daemon</code>,
<code>no_tuios</code> or <code>incompatible</code>, with how many times it dropped and why.</li>
</ul>
<h3>Sessions on another machine</h3>
<pre><code class="language-bash">tuios attach --host build api   # attach the session api on build
tuios new --host build          # create a session on build and attach it
</code></pre>
<p>The session is drawn by this client, with this machine's theme, config and
prefix key. Nothing is nested. If the link drops, the pane stays on screen, the
dock says it is reconnecting, and the client tries again for three minutes
before it returns to the session it left on this machine. <code>--ssh</code> runs the
tuios on the host inside this one instead, for a host whose tuios is too old.</p>
<p>In the rail, sessions are grouped under their machine. Enter on a session under
a host attaches it, and enter on the <code>+</code> beside a host creates one there. A
machine's header shows how many of its sessions want a person, and each row
shows what its agents are doing. A machine that is not answering is muted and
its rows cannot be clicked, because they are a cached listing.</p>
<h3>A pane on another machine</h3>
<pre><code class="language-bash">tuios new-window deploy --host build
</code></pre>
<p>The window belongs to this session and is laid out here. Only its process runs
on <code>build</code>. The title bar says <code>build:deploy</code>, and the rail row names the
machine, because the same typed line does different things on different
machines. The rail's file section and agent detection work for these panes. The
pane ends when the link does.</p>
<h3>Global sessions</h3>
<p>Once a second machine is reachable, the rail shows a <code>global</code> group above the
machines. A session in it holds panes from several machines, and every way of
making a window there asks which machine to run it on. Anywhere else, a new
window opens on the session's machine without asking.</p>
<pre><code class="language-bash">tuios new deploy --global
</code></pre>
<p><code>appearance.global_session = false</code> hides the group. Making a new session (<code>N</code>
in window mode, the new <code>+</code> at the start of the dock's session strip, or the
command palette) asks which machine when more than one is reachable.</p>
<h3>Addressing another machine from the CLI</h3>
<p>Verbs take a host-qualified target: <code>-s build:api</code> for a session, <code>-w build:api:0</code> for a window. <code>local:</code> addresses a local session whose name
contains a colon. <code>list-windows</code>, <code>capture-pane</code>, <code>send-keys</code>, <code>send-text</code>,
<code>wait-for</code>, <code>focus-window</code>, <code>kill-session</code>, <code>session-info</code>, <code>get-agent-state</code>,
the agent mail verbs and the stash all take it. Every pane gets <code>TUIOS_HOST</code>,
the hostname of the machine it runs on.</p>
<h2>Agents</h2>
<h3>The rail shows what your agents are doing</h3>
<p>The rail has an agents section. Every pane running a recognised coding agent
is listed with its state, ordered by who needs you: errored, then waiting for
input, then finished but not yet seen, then working. A finished agent you have
looked at moves down the list. Rows show the harness and the last thing the
agent said it was doing. The section header counts blocked and finished panes,
and clicking the count filters the list. In the command palette, <code>@n</code> finds
panes that need input and <code>@w</code> finds working ones, across every session.</p>
<p>When a state changes, tuios can tell you: a dock message, an OSC 9
notification (which reaches you over SSH), and an optional sound. The
<code>[notifications.agent]</code> table has per-state switches, a settle window so a
flapping state does not spam, and quiet hours. A blocked agent's alert now
includes the question it asked. The <code>after-agent-state</code> hook receives the
transition in environment variables, never in argv, so an agent's text cannot
become shell arguments.</p>
<h3>Detection</h3>
<ul>
<li>22 agent CLIs are described by TOML manifests. <code>tuios explain-agent-detect</code>
shows why a pane was or was not recognised.</li>
<li>A process is identified by what it calls itself: its name, <code>argv[0]</code>, its
executable, or the script an interpreter runs. A directory called <code>claude</code> or
<code>codex</code> is no longer an agent. An agent started through <code>sh -c</code>, <code>timeout</code>,
<code>npx</code>, <code>uvx</code>, <code>mise</code> or <code>nix</code> is found.</li>
<li>State comes from six ranked sources: the agent's own report, its transcript,
escape codes (including window-title rules, such as Codex's "Action
Required"), screen rules, the foreground process, and last of all an output
stall. A lower source never overwrites a higher one that is still fresh.</li>
<li>The transcript reader reads only the last 128KB, so a 151MB transcript costs
652 microseconds.</li>
<li>A state clears when the agent exits back to its shell. Agents in panes on
other machines are detected too.</li>
<li><code>get-agent-state</code> and <code>list-agents</code> report a confidence and whether the pane
needs you.</li>
</ul>
<h3>Agents can message each other, and you</h3>
<pre><code class="language-bash">tuios list-agents                      # every agent pane and its state
tuios send-agent-message -w build 'rebased onto main, please retest'
tuios read-agent-messages              # your own inbox
tuios ask-agent -w review 'which test is flaky?'   # ask and wait
</code></pre>
<ul>
<li>A message is at most 8KB, with a subject of up to 120 characters and up to
eight attachments, sent as paths, never as bytes. A session's mailbox holds
256 messages or 512KB. A sender gets a burst of ten, then thirty messages a
minute. Mail lives in memory and ends with the daemon.</li>
<li><code>--reply-to &#x3C;id></code> answers a message and joins its thread.
<code>read-agent-messages --thread</code> and <code>wait-for agent-message --thread</code> filter
by thread.</li>
<li><code>ask-agent</code> refuses a question that would close a loop of agents waiting on
each other, not only a question to yourself. Loop detection needs
<code>--from "$TUIOS_PANE_ID"</code>.</li>
<li>The person at the keyboard has an inbox called <code>human</code>. An agent writes to it
with <code>send-agent-message -w human</code>. The mail overlay opens on <code>ctrl+b M</code>,
from the palette, or with <code>i</code> on a rail row, and you can reply from it.
<code>list-agents</code> reports <code>human_unread</code>.</li>
<li><code>tuios stash put &#x3C;file></code> copies a file into a store owned by the session and
prints its path, ready for <code>--attach</code>. The file stays until the session
ends, even if the sender deletes the original. The limits are 16MB per file
and 256MB per session. <code>stash get</code> copies a file back out, also across a
link.</li>
<li>Mail from another machine is marked as such, and a session holds at most 32
unread messages and 32 notices from links.</li>
</ul>
<h3>One prompt, several worktrees</h3>
<pre><code class="language-bash">tuios worktree new fix/retry          # a git worktree and a session in it
tuios fan 3 --agent claude 'Add a retry with backoff to the HTTP client.'
tuios fan keep api-fan-add-a-retry-with-2
</code></pre>
<p><code>tuios worktree new</code> creates a worktree under the tuios data directory and a
session in it named <code>&#x3C;repo>-&#x3C;branch></code>. <code>worktree ls</code>, <code>worktree diff</code> and
<code>worktree rm</code> manage them. <code>rm</code> refuses a worktree with uncommitted changes
unless you pass <code>--stash</code> or <code>--force</code>, and it never deletes a branch. <code>tuios fan</code> creates several worktrees, starts the agent in each, and types the prompt
once each agent is ready. <code>fan keep</code> removes the others. The rail groups
worktree sessions under their repository.</p>
<h2>One layout for every client</h2>
<p>Two clients on one session used to disagree about pane sizes, because each
subtracted its own rail and dock and retiled. Now each client reports how much
space it reserves on each edge, and the daemon takes the largest reserve per
edge and hands every client one geometry. A client with less chrome draws a
blank band instead of moving the panes.</p>
<p>The same rule now covers the rest of the layout:</p>
<ul>
<li>A zoom belongs to the session, so every client draws it.</li>
<li>The master-stack ratio is kept per workspace and shared.</li>
<li>Where the scrolling layout's strip is scrolled to is shared, so a client
that scrolls takes the others with it, and a client that joins lands where
the session is looking.</li>
<li>A workspace you arranged by hand stays arranged when another client visits
it for the first time.</li>
<li>A BSP tree built on one client is adopted by the others.</li>
<li>Session display names and accent colours are kept by the daemon, so a rename
reaches every client and survives a reattach.</li>
<li>The rail's width is yours: switching to another session no longer changes it.</li>
</ul>
<p>Fixes found along the way: broadcasts now reach a client in the order they were
made, two clients resizing one pane can no longer leave its emulator at a size
nobody draws, and opening or closing a pane no longer leaves a neighbour at its
old size on another client. Reattaching to a full-screen program such as <code>top</code>
no longer draws its rows one blank line apart
(<a href="https://github.com/Gaurav-Gosain/tuios/issues/123">#123</a>).</p>
<h2>The mouse</h2>
<ul>
<li>Every overlay row highlights under the pointer and acts on a click: settings,
the command palette, the launcher, the theme, glyph and effect pickers, the
session and workspace lists, the layout menu, and dialogs. The keybind
manager's rows select but do not run, and the help panel only scrolls.</li>
<li>Right-click opens a menu for what is under the pointer: a pane, a dock
segment, a workspace pill, the dock, the desktop, a session row, or a
machine heading in the rail. Every row is an action from the keybind
registry, and the key it shows comes from the registry too.</li>
<li>Alt and the left button move a pane from anywhere in its content. Alt and the
right button resize it.</li>
<li>The left button selects text: double-click for a word, triple-click for a
line.</li>
<li>Dragging a tiled pane by its title bar moves it with the pointer in all three
layouts, and it swaps on release.</li>
<li>A click that only focuses a pane no longer resizes it, and a drag that puts a
pane back where it was resizes nothing, so your shell no longer reprints its
prompt.</li>
<li>Sideways trackpad drift during a vertical scroll no longer jumps lists or
walks the scrolling layout. <code>appearance.niri_scroll_cells</code> (default 8) sets
how far one scroll event moves the strip.</li>
<li>Links in a pane can be clicked. <code>appearance.links</code> sets which: <code>off</code>,
<code>marked</code> for OSC 8 links only, or <code>all</code> (the default), which adds bare URLs,
including one wrapped across rows. The link under the pointer is underlined,
the pointer turns into a hand, and a label shows the target. A click opens
it. A <code>file://</code> link opens in a new pane running <code>$EDITOR</code>, on the machine
the panes run on, and a directory opens in the rail's file view. An <code>http</code>
link opens a browser only for a local client. Under <code>tuios ssh</code> or
<code>tuios-web</code> it copies the URL to your clipboard instead.</li>
<li>A shake of the mouse can toggle the <a href="https://tuios.gaurav.zip/releases/since-v0-7-0#look-and-feel">spotlight</a>, with
<code>spotlight.shake = true</code>.</li>
</ul>
<p>The dock is now a status line without powerline caps. Its contents are a
<code>[dock]</code> table: <code>left</code>, <code>center</code> and <code>right</code> are ordered lists of component
names, <code>[dock.clock]</code> sets the time format, and a <code>[dock.custom.NAME]</code> entry
runs a shell command and shows what it prints. A command that hangs is killed
with its whole process tree at its timeout. Workspace pills can be dragged into
a display order, which does not renumber the workspaces.</p>
<h2>The rail</h2>
<p>The rail was rebuilt from a collapsible tree into flat sections. Which sections
it draws, in what order, and how tall each is, is one string,
<code>appearance.sidebar.sections</code>, with an editor in the settings panel. <code>spacer</code>
may repeat. Drag the line above a pinned section to move the split.</p>
<ul>
<li><strong>Sessions.</strong> Each session has its own colour, derived from its name, so it
is stable across clients and restarts. A session with a generated name is
labelled by its directory, and any session shows its git branch when there
is room. Sessions and workspaces can be renamed from one dialog, and session
rows can be dragged into an order. <code>appearance.session_border = true</code> tints
every pane border with the session's colour.</li>
<li><strong>Files.</strong> The working directory of the focused pane, following the pane as
focus moves, with nerd font icons coloured by file type.
<code>appearance.sidebar.folder_click</code> decides whether clicking a folder
navigates the listing, sends a <code>cd</code> to the pane, or both. Six file actions:
create, rename, delete, copy, cut and paste. Only delete asks first. Delete
moves the file to the freedesktop trash in <code>$XDG_DATA_HOME/Trash</code>, and
<code>appearance.sidebar.file_delete = "permanent"</code> skips it. A file on another
filesystem is refused, and on Windows a delete is permanent and says so.
Paste never overwrites: a name that exists gains a number.</li>
<li><strong>Git.</strong> Add <code>git</code> to the sections string to see the focused pane's
repository, branch, how far it is ahead of and behind its upstream, and,
with <code>appearance.git_dirty</code> (on by default), how many files are changed.</li>
<li><strong>Agents</strong>, described <a href="https://tuios.gaurav.zip/releases/since-v0-7-0#agents">above</a>.</li>
</ul>
<p>A program in a pane can print an OSC 7 escape claiming any directory. When the
kernel says the pane's shell is somewhere else, the file actions are switched
off and the rail says "read only: wrong folder", so a pane cannot aim a delete
at a folder it is not in. This now also works for daemon panes and on macOS.</p>
<p>The rail answers the mouse throughout: rows highlight on hover, a truncated row
scrolls so you can read it, the collapsed strip pops a label beside the row
under the pointer, and the rail's edge can be dragged to change its width.</p>
<h2>Tiling and zoom</h2>
<p>There are three tilers: BSP, master-stack, and a scrolling layout in the style
of niri, which puts columns on an endless horizontal strip. All three honour
<code>appearance.gap</code>. A pane can be floated out of a tiled layout on its own, or
pinned so it follows you across workspaces. "All windows" in the command
palette searches every window in every workspace and jumps to the one you pick.</p>
<p>Windows created in a daemon session now tile. Before, only windows the attached
client made itself were placed, so daemon-created windows stacked on each other
or floated over the tiles. BSP and master-stack tiles no longer overlap when
panes shrink.</p>
<p><em><a href="https://tuios.gaurav.zip/releases/since-v0-7-0">An interactive figure goes here. Open the page to use it.</a></em></p>
<ul>
<li><strong>Zoom</strong> slides into place instead of cutting (<code>appearance.zoom_animation</code>).
<code>appearance.zoom_size</code> below 100 zooms like a camera: the layout stays on
screen around the zoomed pane, with its neighbours running off the edges.
Moving focus hands the zoom to the pane you land on
(<code>appearance.zoom_follows_focus</code>). A tiled pane's title bar has a zoom
button (<code>appearance.window_button_zoom</code>).</li>
<li><strong>Scrolling layout.</strong> <code>appearance.scroll_column_max</code> (20 to 100, default 90)
lets a column fill the screen. Clicking, hovering with focus-follows-mouse,
or moving focus with the keyboard brings the whole column on screen
(<code>appearance.niri_click_reveals</code>, <code>appearance.niri_hover_reveals</code>). Column
widths and stacks survive a session switch
(<a href="https://tuios.gaurav.zip/blog/a-session-switch-forgot-my-column-widths">the post</a>), and the strip keeps its scroll
position across a workspace switch and a retile.</li>
<li><strong>Percentage resize.</strong> <code>ctrl+b L</code> then 5 to 9 sets the focused pane's width
to 50 to 90 percent of its region, and shift with 5 to 9 sets its height.</li>
<li><strong>New windows open where you are.</strong> A new window starts in the focused pane's
directory. <code>appearance.new_window_inherit_cwd = false</code> turns this off.</li>
<li><strong>Popups.</strong> <code>tuios popup -- fzf</code> runs a command in a floating pane centred
over the layout, and closes it when the command exits. <code>--width</code> and
<code>--height</code> take cells or percentages (default 80% and 60%). Esc in window
mode closes the focused popup. This is how fzf, gum and other pickers become
tuios overlays.</li>
<li>Turning tiling on after floating brings back the arrangement you had.
Floating windows are no longer drawn over the clock, the log viewer or other
overlays, and raising one floating window no longer reshuffles the others.</li>
</ul>
<p>Resize was rebuilt (<a href="https://tuios.gaurav.zip/blog/one-divider-five-windows">the post</a>): dragging a
shared border moves only the two panes that share it, an untouched split no
longer drifts, and a drag no longer eases toward a target that has moved.</p>
<h2>Keys</h2>
<ul>
<li><strong>One registry.</strong> Every key tuios answers to is in the keybind registry,
including the palette on <code>ctrl+p</code> and the launcher on <code>alt+space</code>, which sit
in a <code>[keybindings.global]</code> section consulted in every mode. Any action can
now be bound there.</li>
<li><strong>Keybind manager</strong> on <code>ctrl+b k</code>: what is bound, where tuios conflicts with
itself, where it conflicts with the pane's program, and a Record tab that
captures the next key you press. The same analysis runs as <code>tuios keybinds doctor</code> and <code>tuios keybinds explain &#x3C;key></code>. Each finding says how it knows:
<code>certain</code> from tuios routing, <code>observed</code> from a live pane, or <code>reference</code>
from a list of program defaults.</li>
<li><strong>Giving a key back.</strong> <code>ctrl+x</code> on a row, or <code>tuios keybinds free &#x3C;key></code>,
removes that key from every action in every scope so the program in the pane
receives it. <code>tuios keybinds unbind</code> removes one action from one key.</li>
<li><strong>Launcher</strong> on <code>alt+space</code> or <code>ctrl+b a</code>: anything on <code>$PATH</code>,
fuzzy-matched and ranked by frecency, with names and icons from desktop
entries where the platform has them. Enter runs the selection in a new pane.
Tab types it into the focused shell so you can add arguments. All four
search boxes in tuios now share one fuzzy matcher.</li>
<li><strong>Prefix repeat.</strong> After a command worth repeating, the prefix stays armed
for half a second, so <code>ctrl+b</code> then left left left walks three panes.
<code>appearance.prefix_repeat_time</code> sets the window, and 0 turns it off. New
prefix bindings: the arrows walk panes, <code>(</code> and <code>)</code> switch sessions, and <code>a</code>
opens the launcher. These work on macOS with no terminal settings.</li>
<li><strong>New window-mode keys.</strong> <code>N</code> makes a new session, and <code>b</code> toggles the
spotlight.</li>
<li><strong>Entering terminal mode on focus.</strong> <code>appearance.auto_enter_terminal_on_focus</code>
can be <code>off</code> (the default), <code>targeted</code> (focus by selection and arrows) or
<code>all</code> (including Tab).</li>
<li><strong>macOS.</strong> An <code>opt+</code> binding on a key that composes no character, such as
<code>opt+esc</code>, never fired. It does now, so Option+Escape leaves terminal mode as
documented. When Option+Left or Option+Right arrives as the readline word
motions, tuios says so and names the terminal setting to change. The default
config no longer warns about its own bindings.</li>
<li>Keys typed within 150ms of entering terminal mode are no longer dropped.</li>
</ul>
<h2>Look and feel</h2>
<ul>
<li><strong>Themes.</strong> <code>tuios import-theme</code> converts a kitty, ghostty, alacritty or
wezterm colour scheme, detecting the format from the file.
<code>tuios list-themes</code> lists the 342 built-in themes plus your themes
directory, and an imported theme works without a restart. Switching theme
recolours every open pane in place. A theme can describe itself: each colour
is measured against the background, and colours under the contrast floor are
named.</li>
<li><strong>Chrome colours.</strong> A theme file may carry a <code>chrome</code> object with <code>accent</code>,
<code>accent_bright</code>, <code>success</code>, <code>warning</code>, <code>error</code> and <code>info</code>, so the interface
can use an amber accent without turning every blue in every pane amber.
<code>chrome.surface</code> sets the colour dialogs are drawn on, and the rest of the
chrome is derived from it at readable contrast.</li>
<li><strong>Glyph sets.</strong> <code>appearance.glyphs</code> picks the shapes: borders, window
controls, rail marks, folders. Four ship (<code>default</code>, <code>unicode</code>, <code>heavy</code>,
<code>ascii</code>), each glyph with its own ASCII fallback, and a custom set is a file
that can inherit from a built-in. <code>tuios list-glyphs</code> lists them, and the
settings panel has a picker with a preview.</li>
<li><strong>Colour picker.</strong> Colour settings open a picker with a hue strip, a shades
grid, HSL and RGB sliders, a hex field, a harmony wheel, and the theme's own
colours as slots that follow the theme. Settings rows show a swatch of the
colour in force. Numeric settings open an editor with a slider and a typed
value.</li>
<li><strong>Zen mode.</strong> <code>appearance.zen_mode</code> hides unfocused borders always, or only
while the mouse is still. Content never shifts.</li>
<li><strong>Window controls</strong> can sit on the left, and a <code>dots</code> style draws them as
traffic lights. The scrollbar has two styles: a single stroke in the pane's
own ink, or a track with a half-cell thumb.</li>
<li><strong>Copy flash.</strong> A copy sweeps a band of light over the copied text.
<code>appearance.selection.flash</code> turns it off, and <code>flash_ms</code>, <code>flash_color</code> and
<code>flash_style</code> (<code>diagonal</code>, <code>diagonal-reverse</code>, <code>horizontal</code>, <code>vertical</code>) tune
it. Only the background changes, so the text stays readable.</li>
<li><strong>Spotlight.</strong> <code>b</code> in window mode dims everything outside a circle around the
mouse. The <code>[spotlight]</code> table sets the radius, how dark the rest goes
(<code>dim</code>, default 75), and whether it follows the mouse or the cursor.</li>
<li><strong>Screen saver.</strong> Off until you set <code>screensaver.enabled</code>. It starts after
<code>screensaver.idle_minutes</code> idle, with 36 effects ported from
terminaltexteffects (<code>screensaver.effect</code>, or <code>random</code>). The picker previews
each effect over your real screen. Effects now run at the right speed at any
frame rate, and on an unthemed session the saver asks the terminal for its
palette instead of guessing xterm's.</li>
<li><strong>Keycast.</strong> tuios can draw your keypresses in the bottom right as you type,
with repeats coalesced, for screen recordings. It toggles from the command
palette and the setting persists.</li>
</ul>
<p><em><a href="https://tuios.gaurav.zip/releases/since-v0-7-0">An interactive figure goes here. Open the page to use it.</a></em></p>
<h2>Graphics and screenshots</h2>
<p><code>tuios screenshot</code> writes a pane to a file as <code>png</code>, <code>svg</code>, <code>ansi</code>, <code>html</code> or
<code>txt</code>. It runs in the daemon and reads the emulator's cell grid, so it works
on a detached session from a script: <code>tuios screenshot -w build</code>. On macOS it
now finds your fonts without fontconfig, including fonts inside <code>.ttc</code>
collections, so Nerd Font icons draw instead of boxes.</p>
<ul>
<li><strong>Kitty graphics over SSH.</strong> Passthrough used to be decided by asking the
server's terminal, and a server has none, so graphics were off for every SSH
client. The client's capabilities now come from the SSH connection: <code>TERM</code>,
the forwarded environment, and the pixel size it reports.</li>
<li><strong>Animation.</strong> The kitty animation protocol, dropped in v0.7.0, is
implemented. Animated images send only the changed regions, which took one
measured page from 330MB forwarded in 21.6 seconds at 17fps to 22MB at
73fps. A flood of graphics is paced so it cannot starve the keyboard.</li>
<li><strong>Unicode placeholders.</strong> Images drawn with placeholder cells, such as those
in glow's pager, now show inside a pane and scroll and clip with the text.
They need a host that supports placeholders (kitty, Ghostty, WezTerm), so
not tuios-web. <code>appearance.kitty_placeholders</code> forces the choice.</li>
<li><strong>Covered images are cropped.</strong> A window over part of an image now cuts
that part away instead of hiding the whole image. An image no longer draws
over the rail or the dock, and an image in a pane's last rows no longer
scrolls the whole screen up.</li>
<li><strong>Bounded memory.</strong> An animated gif under chafa grew tuios to 5.3GB in two
minutes. The same run now stays at 189MB.</li>
</ul>
<h2>The web client</h2>
<ul>
<li><strong>Text shaping.</strong> The web client can use sip's WebGL renderer with HarfBuzz
shaping (opt-in). Arabic letters join, ligatures form, and combining marks
land where they belong. Grapheme widths now match a reference terminal, and
a freeze where synchronized output was left on is fixed.</li>
<li><strong>TLS and phones.</strong> <code>tuios-web</code> can serve a LAN address over TLS, and
<code>--auto-tls</code> creates and keeps a certificate for the hostname and every
interface address. <code>tuios-web cert</code> inspects or replaces it. On a phone, a
row of tuios commands sits near your thumb, built from the keybind registry,
a long press opens the pane menu, and pane dividers are big enough to grab.</li>
<li><strong>Your theme.</strong> A browser now shows the theme you picked, and the tab shows
tuios instead of "Sip".</li>
<li>Settings changed from a browser session apply to that session only, instead
of writing the server's config file.</li>
<li><code>tuios-web</code> takes every interface flag <code>tuios</code> takes.</li>
<li>The Docker image now includes <code>tuios-web</code>.</li>
</ul>
<p><em><a href="https://tuios.gaurav.zip/releases/since-v0-7-0">An interactive figure goes here. Open the page to use it.</a></em></p>
<h2>Scripting and control</h2>
<p>The daemon speaks 62 verbs over a line-delimited JSON socket, one JSON object
per line each way. <code>tuios list-verbs</code> lists them with every error code a verb
can return. See <a href="https://tuios.gaurav.zip/docs/control-protocol">the control protocol</a>.</p>
<ul>
<li><strong>Strict parameters.</strong> A parameter a verb does not take is refused with the
closest spelling and the accepted list, instead of being dropped.</li>
<li><strong>Options.</strong> One registry holds all 156 settable options. It backs
<code>set-option</code>, <code>get-option</code> and <code>list-options</code>, and the settings panel is
built from it. <code>set-option</code> refuses a path that does not exist, with
suggestions, and validates colour values. It also fixed a bug where a dozen
settings, from border style to the leader key, were saved and then read back
as defaults on every reload, and a boolean turned off could not be saved.</li>
<li><strong>Config reload.</strong> Editing <code>config.toml</code> reaches the running client, even
with editors like vim that save by renaming. A file with an error is
reported on screen and the running config stays.</li>
<li><strong><code>new-session</code></strong> creates a session and its first window from a program,
with no client attached.</li>
<li><strong><code>run-command</code></strong> refuses a command name it does not know, instead of
reporting success. <code>tuios run-command --list</code> lists the names.</li>
<li><strong>Hooks say what happened.</strong> <code>tuios list-hooks</code> shows each hook command's
run count, last exit code, last run and last error. A failing hook logs its
exit code and the tail of its stderr.</li>
<li><strong>Daemon logs.</strong> The daemon writes errors and basic events to
<code>$XDG_STATE_HOME/tuios/daemon.log</code>, so a crash leaves a record.
<code>tuios set-config daemon.log_level messages</code> raises the detail without a
restart. <code>tuios daemon --pprof</code> serves profiling data.</li>
<li><strong>Crash report.</strong> A panic in the interface now shows a full-screen report
with the build, terminal, layout and last actions. <code>c</code> copies it, and <code>g</code>
opens a prefilled issue. It never includes pane contents, titles, paths or
session names.</li>
<li><strong><code>tuios update</code></strong> replaces the binary in place, but only when it installed
it. It recognises a release download, the install script, <code>go install</code>,
Nix, Homebrew and system packages, and for the others it names the
installer that owns the binary.</li>
<li><strong>Agent skill.</strong> <code>tuios --skill</code> prints the recipes for driving tuios from a
pane. A test parses every command in it against the real command tree.</li>
<li><code>tuios send-keys</code> no longer pulls a pane you scrolled back to the bottom.</li>
<li><code>tuios stash put</code> and <code>stash get</code> print only the path on stdout, so
<code>path=$(tuios stash put FILE)</code> works.</li>
</ul>
<p>Most of this hardening came from driving the socket the way a program would,
which <a href="https://tuios.gaurav.zip/blog/the-daemon-said-yes-to-an-option-that-did-not-exist">got its own post</a>.</p>
<h2>Project tapes and startup</h2>
<p>A <code>.tuios.tape</code> in a project directory describes the session for that project:
which windows to open, what to run in them, and how to lay them out. When the
focused shell changes into a directory that has one, tuios notices, like direnv
for terminal layouts.</p>
<p>A tape runs commands, so nothing happens until you have read it. The review
dialog shows the whole file, and you choose Run once, Trust and run, or Never.
Trust is tied to the file's content hash, so any edit makes it ask again. By
default a dock badge and a notification tell you a tape is there.
<code>auto_review</code> opens the review dialog on detection. <code>autorun = "auto"</code> runs a
tape without asking, but only one you trusted and have not edited since.
Running a tape builds a session named after the project and switches to it.</p>
<p><em><a href="https://tuios.gaurav.zip/releases/since-v0-7-0">An interactive figure goes here. Open the page to use it.</a></em></p>
<p>Three startup settings remove the steps every session used to start with:
<code>startup.open_default_window</code>, <code>startup.tiled</code> and
<code>startup.start_in_terminal_mode</code>. Only <code>tiled</code> is on by default, and only for a
fresh install (see <a href="https://tuios.gaurav.zip/releases/since-v0-7-0#before-you-upgrade">Before you upgrade</a>). With all three
on, tuios opens tiled with one terminal and your next key goes to the shell.
Startup also asks the terminal everything in one round trip, which took the
time to the first frame from 566ms to 68ms.</p>
<p><em><a href="https://tuios.gaurav.zip/releases/since-v0-7-0">An interactive figure goes here. Open the page to use it.</a></em></p>
<p>The settings menu now covers everything the config file does, with an inline
editor for text values, and changes persist.</p>
<h2>The terminal emulator</h2>
<p>Behind a <code>ghostty</code> build tag, panes can run on libghostty-vt, the emulation
core of the Ghostty terminal, instead of the pure Go emulator. Both backends
implement one interface. A differential suite feeds both the same bytes and
compares screens, cursors, scrollback, modes and the output a host would
receive, after every chunk. The first version of that suite passed while the
ghostty backend painted <code>ls</code> on hot pink, which is
<a href="https://tuios.gaurav.zip/blog/my-differential-tests-passed-and-the-screen-was-pink">its own post</a>.
Releases ship ghostty builds beside the default ones.</p>
<p>A conformance corpus checks byte strings on a 6x4 screen against literal text
dumps, citing esctest and vttest, and can compare against a real tmux. A
grammar-based fuzzer feeds the emulator sequences that have broken terminals
before. Two more checks need no expected output: an emitted frame must redraw
the screen the emulator holds, and the same bytes must draw the same screen
however the reads are split. Bugs they found, any of which a program in a pane
could hit:</p>
<ul>
<li>The cursor position report had line and column swapped.</li>
<li>Under <code>TERM=screen</code>, <code>linux</code> or <code>vt100</code>, box drawing came out as <code>lqk</code>,
because SO and SI were unreachable.</li>
<li>OSC 8 hyperlinks had their fields swapped and never worked.</li>
<li>DECSTBM accepted a scroll region taller than the screen, and the next scroll
crashed the daemon and every session in it.</li>
<li>A mode map read without its lock, and two captures of one busy pane, could
each crash the daemon.</li>
<li>NEL, DECALN, DECSTR and DECRQSS were missing, and insert mode was recorded
but ignored.</li>
<li>ED 3 cleared the visible screen, REP repeated the wrong character, and a wide
character cut at a margin left half of itself behind.</li>
<li>A window title containing a character from the Dingbats block, which Claude
Code puts in its title, ended early and printed the rest into the pane,
because a byte inside the character was read as an eight-bit string
terminator. Invalid bytes in titles are now dropped.</li>
<li>A single shift (SS2, SS3) before a multi-byte character leaked into the next
character.</li>
<li>Underline and other attributes were dropped on the focused pane's render
path.</li>
</ul>
<p>Where the two backends still disagree, the difference is pinned as a known
divergence rather than left to pass quietly.</p>
<h2>Faster and smaller</h2>
<p>Every number here comes from the commit that made the change.</p>
<h3>Speed</h3>
<ul>
<li>Idle: with idle shells and the rail open, tuios schedules no timer-driven
renders. The render ticker now runs at <code>max_fps</code>, which at the default of 60
took an idle client from 1.0% to 0.6% of a core.</li>
<li>Typing a letter into a shell: 18.9us and 177 allocations down to 1.06us and
14. A keystroke over the socket: 4,009ns and four writes down to 967ns and
one.</li>
<li>Keypress to echo: the median went from 9.75ms to 1.78ms. Typing into one pane
while another floods went from an 18 to 28ms median round trip to 1 to 5ms,
against tmux's 1.</li>
<li>A pane that falls more than 4MiB behind drops to catch-up rate until it
recovers, and a busy pane can no longer starve the renderer.</li>
<li>Workspace switch: the client asks only for rows it does not have. At four
panes that is 11.5MB of encoding down to 0.6MB, and a caught-up switch went
from 47.6ms to 2.4ms per pane.</li>
<li>Drawing a pane's frame: 6.53ms to 1.58ms when dirty, 3.51ms to 62.7us when
cached. The pane renderer stopped re-wrapping content that was already the
right width, which was
<a href="https://tuios.gaurav.zip/blog/a-fifth-of-every-frame-went-to-changing-nothing">a fifth of every frame</a>.</li>
<li>The rail's rows: 82,000ns and 178 allocations per frame down to 288ns and
none.</li>
<li>Moving the pointer over plain text no longer composes a frame per cell.</li>
<li>A truecolor repaint: allocations down from 757,200 to 189,600 per run of the
benchmark. Scrolling short lines: 65 to 79 percent less CPU.</li>
</ul>
<h3>Memory</h3>
<ul>
<li>Scrollback is stored as text with styles marked where they change. A
175-character line went from 4,864 bytes to 216, and a full 10,000-line
history from 48MB to 4.2MB per pane, in the daemon and again in each client.</li>
<li>An empty 207x55 pane held 1.7MB before printing anything, 1.3MB of it the
screen grid. A row is now allocated only when something is printed on it,
and an empty pane's grid holds 28KB. The alternate screen is built on first
use.</li>
<li>Eight empty panes: heap from 47.5MiB to 14.9MiB.</li>
<li>On the ghostty backend, <code>scrollback_lines</code> now holds. The library pruned by
bytes first, so a pane asked for 10,000 lines kept about 400 at 207 columns.</li>
</ul>
<h2>Other fixes</h2>
<ul>
<li>The last output of a pane, often its prompt, sometimes stayed invisible until
the next key. It is now drawn at once.</li>
<li>A pane scrolled back stays on the line you stopped at while new output
arrives.</li>
<li>Ctrl+P opens the palette even when the terminal reports Num Lock in the key
event.</li>
<li>Quitting a session says "Killed", not "detached", and names the session you
were in.</li>
<li>A grapheme cluster split across two writes renders as one.</li>
<li>Programs in tuios are told graphics forwarding is available, so image tools
stop falling back to blocks.</li>
<li>Clipboard copy and paste work on GNOME Terminal and other VTE terminals, and
over a loopback SSH connection, using the native clipboard tool.</li>
<li>Ctrl+D in a pane on another machine closes it at once instead of on the next
key.</li>
<li>The log viewer's copy keys (<code>E</code> for errors, <code>A</code> for all) now work.</li>
<li>Help opened from the leader starts at the top. In help, <code>q</code> closes it
outside a search and can be typed inside one.</li>
<li>Mouse scroll speed and window title format are configurable, in the file and
in the menu.</li>
</ul>]]></content:encoded>
    </item>
    <item>
      <title>v0.7.0</title>
      <link>https://tuios.gaurav.zip/releases/v0-7-0</link>
      <guid isPermaLink="true">https://tuios.gaurav.zip/releases/v0-7-0</guid>
      <pubDate>Sun, 29 Mar 2026 00:00:00 GMT</pubDate>
      <dc:creator>Gaurav Gosain</dc:creator>
      <description>Video playback in a pane, the kitty keyboard protocol, a rebuilt copy mode with a real scrollbar, a command palette, and the hundred fixes it took to make graphics passthrough honest.</description>
      <content:encoded><![CDATA[<p>157 commits between v0.6.0 and v0.7.0: 34 features and over a hundred fixes.
The terminal inside tuios now speaks the protocols real programs use:</p>
<ul>
<li>Video plays in a pane.</li>
<li>fish 4.x's kitty keyboard handshake works.</li>
<li>Copy mode has a scrollbar you can grab.</li>
<li>A command palette lists every action.</li>
</ul>
<p>Most of the fixes are the cost of passing another terminal's protocol through a
multiplexer: clipping, positioning, flicker, and the gap between what a spec
says and what mpv sends.</p>
<h2>Changed behaviour</h2>
<ul>
<li>The mouse wheel now enters copy mode. The separate scrollback mode, which
could scroll but not select, is gone.</li>
<li>The dock's clock, CPU and RAM meters are off by default, and are only polled
when shown.</li>
<li>A new <code>--confirm-quit</code> flag and <code>confirm_quit</code> option always ask before
quitting.</li>
</ul>
<h2>Video playback</h2>
<p><code>mpv --vo=kitty</code> plays video in a tuios pane, and so does
<a href="https://github.com/Gaurav-Gosain/youterm">youterm</a>. Shared memory and base64
modes both work. Use <code>--vo-kitty-use-shm=yes</code>: it sends a file reference per
frame instead of streaming megabytes of pixels through the terminal.</p>
<p>Video is the hardest case for graphics passthrough, and most of the fixes were
here:</p>
<ul>
<li>mpv marks every frame "more data coming" and never sends the final-chunk
marker the protocol says to wait for, so passthrough that worked for static
images waited forever.</li>
<li>Each frame used to get a new image ID, forcing a delete and replace with a
visible flash. Frames now reuse the host's image ID, so the terminal swaps
the data in place.</li>
<li>File-based frames are forwarded as a path instead of being read, base64
encoded and chunked, which saves about 4MB of allocation per frame at 30fps.</li>
<li>All graphics output is wrapped in synchronized updates and flushed with the
render cycle. Text and graphics arriving as two unsynchronized streams caused
the tearing.</li>
</ul>
<p>Static images got the same care: icat and yazi previews clip at the viewport,
hide when their window leaves the screen, and follow the window during a drag.</p>
<h2>The keyboard protocol shells expect</h2>
<p>The VT emulator implements the kitty keyboard protocol: the full push, pop and
query stack, with keys sent in CSI u encoding when a program has the protocol
on. fish 4.x turns it on at startup, and before this release capital letters
arrived as escape sequences fish ignored. As the spec says, shifted printable
keys now send their text, and CSI u is kept for real modifier combinations.</p>
<p>The same work added:</p>
<ul>
<li>OSC 4 palette queries and OSC 52 clipboard.</li>
<li>Modes 2026 (synchronized output) and 2027 (unicode core).</li>
<li>Sixel in the DA1 reply.</li>
<li>OSC 66 text sizing, as an experimental passthrough (see
<a href="https://tuios.gaurav.zip/releases/v0-7-0#known-limitations">Known limitations</a>).</li>
</ul>
<h2>Copy mode</h2>
<ul>
<li>The mouse wheel drops you into copy mode, so scrolling up and selecting are
one gesture. It has full vim navigation, visual and visual-line selection,
search and yank.</li>
<li>The border is a scrollbar, drawn in the theme's border colours and sized to
the content. Click it to jump and drag it to scroll.</li>
<li>Dragging a selection past the edge of the pane keeps scrolling on a timer,
so the selection keeps growing while the mouse is still.</li>
</ul>
<p>A new scrollback browser splits your history into command blocks using OSC 133
markers, with a regex fallback for shells that do not emit them. Each block is
a command and its output, navigable with vim keys, and you can yank JSON
fragments and file paths out of the output. fish and a configured starship both
emit OSC 133.</p>
<p><em><a href="https://tuios.gaurav.zip/releases/v0-7-0">An interactive figure goes here. Open the page to use it.</a></em></p>
<h2>Command palette, sessions and layouts</h2>
<ul>
<li><strong>Command palette</strong> on <code>Ctrl+P</code>: every tuios action, fuzzy-searched, with its
key shown beside it. A match on the name ranks above a match on the
category. It floats at the top of the screen and works in both modes.</li>
<li><strong>Session switcher</strong> on <code>prefix+S</code> lists daemon sessions the same way.</li>
<li><strong>Layout templates</strong> save the BSP tree, each window's working directory (read
from <code>/proc</code>), startup commands, and the tiling scheme. Positions scale when
you load a template on a different screen size. Loading moves existing
windows into place instead of killing them, and minimizes extras. The CLI is
<code>tuios layout list</code>, <code>delete</code>, <code>dir</code> and <code>export</code>, and <code>export</code> turns a
template into a tape script. The layout submenu is on <code>Ctrl+B L</code>.</li>
<li><strong>Zoom</strong> on <code>z</code> fills the screen with the focused pane. Press it again to
restore. The dock shows a Z while zoomed.</li>
<li><strong>Shared borders.</strong> With <code>--shared-borders</code>, tiled windows are drawn without
borders and a single line with proper junctions separates them, which saves
two columns and two rows per split. It is opt-in because it took several
attempts: the feature was reverted and reimplemented three times before the
separator overlay stuck.</li>
<li><strong>Smart split</strong> picks the split direction from the focused pane's shape. It
is opt-in because as a default it made narrow slices, so spiral stays the
default.</li>
</ul>
<p><em><a href="https://tuios.gaurav.zip/releases/v0-7-0">An interactive figure goes here. Open the page to use it.</a></em></p>
<h2>Faster underneath</h2>
<ul>
<li>Rendering is driven by PTY output instead of a tick. The tick remains only
for animations and tape playback, and slows to an idle rate when nothing
moves. With the dock meters off by default, idle CPU is near zero.</li>
<li>Unfocused windows without a selection take a fast path that skips per-cell
styling. Windows on other workspaces skip render work, and tuios-web and SSH
clients do not even receive their output until you switch to them.</li>
<li>Base64 image data passes through without a decode and re-encode, which saves
about 8MB of memory traffic per 4MB image. <code>/dev/tty</code> is opened once instead
of per frame, and only placements that moved are placed again.</li>
<li>tuios moved to the stable Charm v2 releases: bubbletea, lipgloss, wish and
log.</li>
</ul>
<h2>Fixed</h2>
<p>Most graphics fixes are described above. The rest, grouped:</p>
<p><strong>Daemon mode</strong></p>
<ul>
<li>The daemon's emulator dropped kitty graphics capability queries, so icat and
chafa fell back to blocks. The daemon now answers them.</li>
<li>PTY sizes sync on reattach instead of keeping the previous client's size.</li>
<li>The client sends its pixel size and graphics capabilities to the daemon at
hello.</li>
<li>The subscriber buffer is 16 times larger, so video frames survive the socket
from daemon to client.</li>
</ul>
<p><strong>Input and mouse</strong></p>
<ul>
<li>Mouse motion is only sent to programs that asked for it. kakoune got
hundreds of unrequested motion events a second
(<a href="https://github.com/Gaurav-Gosain/tuios/issues/78">#78</a>).</li>
<li>Phantom keypresses from split mouse sequences are suppressed.</li>
<li>Visual line mode highlights at once instead of on the next cursor move.</li>
</ul>
<p><strong>Colours and light themes</strong></p>
<ul>
<li>Unstyled cells use the terminal's real default foreground instead of white,
and SGR 39 and 49 reset to transparent instead of a fixed default. This fixes
light themes (<a href="https://github.com/Gaurav-Gosain/tuios/issues/41">#41</a>,
<a href="https://github.com/Gaurav-Gosain/tuios/issues/72">#72</a>,
<a href="https://github.com/Gaurav-Gosain/tuios/issues/74">#74</a>).</li>
</ul>
<p><strong>Responsiveness</strong></p>
<ul>
<li>Closing a window with <code>exit</code> or <code>ctrl+d</code> no longer waits out a 500ms timeout
(now 10ms), and a race that lost the close notification about half the time
is gone.</li>
<li>Background windows no longer show stale content until you touch them.</li>
</ul>
<p><strong>Other</strong></p>
<ul>
<li>Tape scripts send CRLF on Windows, contributed by @dodorz.</li>
<li>Many smaller fixes to scrollback, tiling toggles and overlay positioning are
listed in the
<a href="https://github.com/Gaurav-Gosain/tuios/compare/v0.6.0...v0.7.0">full changelog</a>.</li>
</ul>
<h2>Known limitations</h2>
<ul>
<li>OSC 66 text sizing breaks with scrollback and when a window moves.</li>
<li>Sixel passthrough exists but is disabled until pixel-level clipping is
implemented, so sixel images cannot be cropped to a window. Use kitty
graphics.</li>
<li>The kitty animation protocol (<code>a=f</code>, <code>a=a</code>, <code>a=c</code>) was tried and dropped. It
needs image IDs to match between guest and host, and tuios remaps them. Run
programs that use it outside tuios for now.</li>
<li>Video without shared memory can flicker, depending on frame rate.</li>
</ul>]]></content:encoded>
    </item>
    <item>
      <title>v0.6.0</title>
      <link>https://tuios.gaurav.zip/releases/v0-6-0</link>
      <guid isPermaLink="true">https://tuios.gaurav.zip/releases/v0-6-0</guid>
      <pubDate>Sun, 25 Jan 2026 00:00:00 GMT</pubDate>
      <dc:creator>Gaurav Gosain</dc:creator>
      <description>Experimental Kitty graphics support, prefix-free window cycling in terminal mode, and a real terminal cursor with vi-mode shapes.</description>
      <content:encoded><![CDATA[<p>24 commits between v0.5.0 and v0.6.0, counting the v0.5.1 patch. Seven are
about one feature, kitty graphics: a program in a window can now show images
through the host terminal. It ships as experimental. The rest:</p>
<ul>
<li>Window cycling in terminal mode without the prefix key.</li>
<li>A real terminal cursor, so vi-mode cursor shapes work.</li>
<li>Compatibility fixes, several of them contributed.</li>
</ul>
<h2>Images, experimentally</h2>
<p>tuios redraws each window's content into its own grid, so an image cannot pass
through as is: its graphics commands carry positions that mean nothing to the
host terminal. tuios now translates them.</p>
<ul>
<li>The VT emulator reads the graphics commands, tuios tracks each placement
against its window, and forwards it to the host at the right cell.</li>
<li>A placement is moved when its window moves, resizes or scrolls, and hidden
when it is covered, dragged, or scrolled out of view.</li>
<li>Shared memory, file and direct transmission all work, with chunking for
large images.</li>
<li>Cell pixel sizes are reported to the PTY, so programs size images correctly.</li>
</ul>
<p><code>yazi</code> and <code>kitty icat</code> show real images in tuios windows on a host terminal
that supports them: Kitty, WezTerm, Ghostty, iTerm2, Foot or Contour.</p>
<p>Five fixes followed the feature in this cycle: clipping at window borders,
placements drifting against scrollback, image ID collisions between windows,
images left behind when a program leaves the alternate screen, and virtual
placements that assumed a placeholder grid tuios did not have. More edges were
found later.</p>
<h3>Known limits</h3>
<ul>
<li><code>TUIOS_KITTY_GRAPHICS=0</code> turns graphics off.</li>
<li>Images wider than the viewport are hidden rather than clipped.</li>
<li>Sixel is parsed but disabled until raster clipping exists.</li>
</ul>
<h2>Graphics detection by asking</h2>
<p>tuios used to guess graphics support from the terminal's name. Now it asks.</p>
<ul>
<li>A DA1 query detects sixel, and the kitty graphics query detects the kitty
protocol.</li>
<li>Reads poll with a timeout, so a terminal that never answers cannot hang
startup.</li>
<li><code>TUIOS_DEBUG_CAPS=1</code> logs what detection found. <code>TUIOS_KITTY_GRAPHICS</code> and
<code>TUIOS_SIXEL_GRAPHICS</code> override it either way.</li>
</ul>
<h2>Cycling without the prefix</h2>
<p>Window management used to be three keys away in terminal mode. Now it is one:</p>
<ul>
<li>macOS: <code>Opt+Tab</code> and <code>Opt+Shift+Tab</code> cycle windows.</li>
<li>Linux and Windows: <code>Alt+N</code> and <code>Alt+P</code>, because <code>Alt+Tab</code> belongs to the OS
window switcher.</li>
<li><code>Opt+Esc</code> or <code>Alt+Esc</code> leaves terminal mode.</li>
</ul>
<p>The bindings live in a new <code>[keybindings.terminal_mode]</code> section and show in
the help menu. This part of the release was contributed.</p>
<h2>A real cursor</h2>
<p>tuios used to paint its own cursor, a cyan block drawn into the grid. It now
moves and styles the real terminal cursor, so cursor shape sequences from
programs pass through. A shell in vi mode shows a block in normal mode and a
bar in insert mode, blinking included.</p>
<h2>Fixed</h2>
<ul>
<li>Paste works in daemon mode. Pasted text was written into the internal pipe
whose responses are discarded, so it vanished.</li>
<li>Arrow keys work in cmus and other ncurses programs. Programs that turn on
application cursor keys mode now get SS3 sequences instead of CSI.</li>
<li>Fast scrollback no longer crashes in colour comparison. Claude Code's output
was fast enough to trigger it. Contributed.</li>
<li>tuios builds on FreeBSD and OpenBSD.</li>
<li>macOS terminal handling is split from Linux, because the shared ioctl
constants were Linux-only.</li>
<li><code>--hide-clock</code> and <code>--window-title-position</code> survive opening new windows
instead of being overwritten by the config loader. These two shipped as
v0.5.1.</li>
<li>Animation bookkeeping is skipped when nothing is animating.</li>
</ul>
<p>Graphics stayed experimental well past this release, with horizontal clipping
and sixel still open. <code>TUIOS_KITTY_GRAPHICS=0</code> restores the old behaviour.</p>]]></content:encoded>
    </item>
    <item>
      <title>v0.5.0</title>
      <link>https://tuios.gaurav.zip/releases/v0-5-0</link>
      <guid isPermaLink="true">https://tuios.gaurav.zip/releases/v0-5-0</guid>
      <pubDate>Thu, 25 Dec 2025 00:00:00 GMT</pubDate>
      <dc:creator>Gaurav Gosain</dc:creator>
      <description>Persistent sessions backed by a daemon, multi-client attach over SSH, a remote control CLI, and BSP tiling with manual splits.</description>
      <content:encoded><![CDATA[<p>30 commits between v0.4.0 and v0.5.0, released on 25 December 2025. tuios now
runs as a session you attach to:</p>
<ul>
<li>A daemon keeps sessions alive when the client disconnects.</li>
<li>More than one client can attach to one session.</li>
<li>New CLI commands drive a running session from outside.</li>
<li>BSP tiling, with manual splits.</li>
</ul>
<p>Underneath, the code moved to the charm.land packages for Bubble Tea v2.</p>
<h2>Breaking changes</h2>
<ul>
<li><strong>Import paths.</strong> If you build on tuios as a library, the Charm packages
moved from <code>github.com/charmbracelet</code> to <code>charm.land</code>.</li>
<li><strong>Session state.</strong> State from before v0.5.0 does not carry over to the new
daemon format.</li>
<li><strong>CLI.</strong> <code>tuios new</code> and <code>tuios attach</code> take the session name as a
positional argument. The flags that did the same are gone.</li>
</ul>
<h2>Sessions that survive a disconnect</h2>
<pre><code class="language-bash">tuios new work      # create a session backed by a daemon
tuios ls            # list sessions
tuios attach work   # attach again
</code></pre>
<p>Detach, close the terminal, or lose the SSH connection, and the session keeps
running. Attaching again brings back windows, layouts, BSP trees, screen
contents and scrollback. A session lasts as long as the daemon process. The
daemon is started in-process.</p>
<p>This works on Windows too. The daemon uses Unix sockets and ConPTY, which
Windows has had since Windows 10 build 17063.</p>
<p>In daemon mode there are two VT emulators, the daemon's and the client's, and
both answered terminal queries. The extra answers printed as text, such as
<code>^[[?62;1;6;9;15;18;22c</code> on reattach. The first fix dropped every reply, which
made fish wait two seconds at startup for a Device Attributes reply that never
came. What shipped: the daemon's emulator answers queries, and the client's
drops its copies, so programs get their replies and nothing prints twice.</p>
<p><em><a href="https://tuios.gaurav.zip/releases/v0-5-0">An interactive figure goes here. Open the page to use it.</a></em></p>
<h2>More than one client</h2>
<ul>
<li>Several clients can attach to one session and see the same thing. Focus,
mode changes, workspace switches and window lifecycle sync in real time.</li>
<li>The session renders at the smallest attached client's size, so nobody gets a
cropped frame.</li>
<li>The SSH server maps the username to a session, so <code>ssh -p 2222 work@host</code>
attaches to <code>work</code>. Connecting without a name shows a session picker.</li>
</ul>
<p>A typical use is pair debugging: one person creates the session and the other
attaches from another machine.</p>
<p>Most of the fixes in this release made this stable. Client teardown, daemon
goroutines and window callbacks had races that only show with two clients on
one state. They got <code>sync.Once</code> on close, WaitGroup tracking, an atomic flag, a
five-second shutdown timeout, and tests under the race detector.</p>
<h2>A CLI for a running session</h2>
<ul>
<li><code>tuios send-keys</code> sends keys to tuios for window management, or with
<code>--literal --raw</code> straight to the focused PTY, one character at a time.</li>
<li><code>tuios run-command</code> runs any tape command by name.</li>
<li><code>tuios set-config</code> changes configuration at runtime.</li>
<li><code>tuios tape exec</code> runs a tape file against the session.</li>
<li><code>tuios list-windows</code>, <code>tuios get-window</code> and <code>tuios session-info</code> read daemon
state with no TUI attached, with <code>--json</code> for scripts.</li>
<li><code>tuios logs</code> tails the daemon.</li>
</ul>
<p>All of them have shell completion.</p>
<h2>BSP tiling</h2>
<p>A binary space partitioning layout. New windows follow a spiral, alternating
vertical and horizontal splits. When the spiral guesses wrong:</p>
<ul>
<li><code>Ctrl+B -</code> and <code>Ctrl+B |</code> split explicitly.</li>
<li><code>Ctrl+B R</code> rotates the split at the focused window.</li>
<li>Preselection chooses where the next window lands, and the dock shows the
direction of the next split.</li>
<li>An equalize command resets every ratio to 50/50.</li>
</ul>
<p>Trees are kept per workspace and saved in daemon state, so a layout survives a
detach and reattach.</p>
<h2>Snapping, appearance and animations</h2>
<ul>
<li>Dragging a window to a screen edge snaps it: top centre for fullscreen, the
left or right edge for a half, corners for quarters. A window has a minimum
size, so it cannot be dragged into nothing.</li>
<li><code>window_title_position</code> puts titles at the bottom, the top, or nowhere.</li>
<li><code>hide_clock</code> removes the clock overlay.</li>
<li>Titles show the window's custom name or the terminal's own title instead of
a default ID.</li>
<li>Animations can be turned off with <code>--no-animations</code> or with a keybind. Off
means off: snap, minimize and restore apply at once instead of playing a
zero-length tween that flashes.</li>
<li>Recorded tapes start with <code>DisableAnimations</code>, so playback looks the same on
any machine.</li>
</ul>
<h2>tuios as a library</h2>
<p>A first cut: <code>pkg/tuios</code> exposes the app as a Go package, with functional
options for theme, shell and animations, and the recommended <code>tea.Program</code>
settings for embedding it.</p>
<h2>Under the hood</h2>
<ul>
<li>The module moved to charm.land: Bubble Tea v2.0.0-rc.2, Lipgloss v2, Wish v2,
and sip v0.1.11 to match.</li>
<li>The CLI was split into one module per command.</li>
<li>The VT emulator synced with upstream: about forty deprecated mode constants
renamed, a nil colour panic fix ported, and grapheme width calculation
simplified enough to drop two dependencies.</li>
<li>The layout, pool and theme packages got tests, and staticcheck and go vet run
clean.</li>
</ul>
<h2>Fixed</h2>
<ul>
<li>Swapping windows in tiling mode updates the BSP tree, so a swap no longer
snaps back on the next layout pass.</li>
<li>Shift+Tab reaches the PTY as backtab even when the terminal reports extra
modifier bits.</li>
<li>On macOS, some <code>opt+</code> bindings were registered as <code>alt+</code>.</li>
<li>Reattaching to a tiled daemon session sized windows from the stale 80x24
handshake. Tiling now waits for the real size.</li>
<li>Remote commands send maps and slices correctly, and the inspection commands
work with no TUI attached.</li>
<li>tuios-web got a <code>--no-animations</code> flag, theme setup, and a curl install
script. An AUR conflict between <code>tuios</code> and <code>tuios-bin</code> was fixed.</li>
</ul>
<h2>Documentation</h2>
<p>Five new guides: BSP tiling, tape recording, the ShowKeys overlay, multi-client
sessions, and the library API.</p>
<p>A correction: the GitHub notes for v0.5.0 list tape recording and ShowKeys as
new features. Both shipped in v0.4.0. This release added their documentation.</p>]]></content:encoded>
    </item>
    <item>
      <title>v0.4.0</title>
      <link>https://tuios.gaurav.zip/releases/v0-4-0</link>
      <guid isPermaLink="true">https://tuios.gaurav.zip/releases/v0-4-0</guid>
      <pubDate>Wed, 03 Dec 2025 00:00:00 GMT</pubDate>
      <dc:creator>Gaurav Gosain</dc:creator>
      <description>The web terminal moves out into the sip library, tape scripting gets a recorder and a manager UI, and a ShowKeys overlay draws your keypresses for recordings.</description>
      <content:encoded><![CDATA[<p>51 commits between v0.3.0 and v0.4.0, with four patch releases along the way.
Three things are new:</p>
<ul>
<li>A web terminal, as the separate <code>tuios-web</code> binary built on the
<a href="https://github.com/Gaurav-Gosain/sip">sip</a> library.</li>
<li>Tape scripting, with a recorder and a manager UI.</li>
<li>ShowKeys, an overlay that draws your keypresses for screen recordings.</li>
</ul>
<p>The patch releases fixed resizing in floating mode and made the quit dialog ask
only when something is running.</p>
<h2>The web terminal</h2>
<p><code>tuios-web</code> serves tuios in a browser. It is a separate binary, so the main
<code>tuios</code> binary carries no server code, and people who never use a browser
never install one. It is a thin wrapper that hands tuios instances to sip's
Handler interface. sip serves any Bubble Tea app in a browser.</p>
<p>It went through three designs before the release. It started as a <code>tuios web</code>
subcommand: WebTransport with a WebSocket fallback, xterm.js with WebGL on the
client, and a bundled Nerd Font. That embedded about 10MB of web assets in the
repository and put an HTTP server in the window manager. The command then
became its own binary, and the server moved into sip, taking the 10MB with it.</p>
<p><code>tuios web</code> was added and removed within this cycle, so no tagged release ever
shipped it and there is nothing to migrate.</p>
<p>The rebuilds fixed two defects:</p>
<ul>
<li>Web sessions used to start the tuios binary as a subprocess. They now run
tuios in-process over pipes, like the SSH server.</li>
<li>The first frame was drawn at a fixed 80x24 before the browser reported its
size, which left artifacts. The session is now created after the browser's
first resize, so the first frame has the right size.</li>
</ul>
<h2>Tape scripting</h2>
<p>A <code>.tape</code> script drives tuios from a file: open terminals, type into them,
manage windows, switch workspaces and modes, and pause between steps.</p>
<ul>
<li><code>tuios tape validate</code> checks a script.</li>
<li><code>tuios tape play</code> runs it live with a progress indicator.</li>
<li>Playback goes through the same Bubble Tea update loop as live input, so a
tape and a person at the keyboard run the same code.</li>
<li>The recorder captures your starting state, window actions, mode switches and
workspace switches. Consecutive characters become one <code>Type</code> command, so a
recorded tape reads like one written by hand.</li>
<li>The tape manager on <code>Ctrl+B T m</code> browses, records and manages tapes without
leaving the session.</li>
</ul>
<p>A headless <code>tape run</code> mode existed during development. It never worked
properly and was removed rather than shipped.</p>
<h2>ShowKeys</h2>
<p>tuios can draw your keypresses in the bottom right as you type, inspired by
nvzone/showkeys. Turn it on with <code>--show-keys</code> or toggle it with <code>Ctrl+B D k</code>.</p>
<ul>
<li>Keys are drawn as pills styled like the window title, with modifiers spelled
out.</li>
<li>Repeats collapse into a count (<code>j ×3</code>).</li>
<li>Keys stay for three seconds. The first version used one, which was too short
to read.</li>
</ul>
<p>The leader key is now configurable as well. It was fixed at <code>Ctrl+B</code>, and now
comes from <code>leader_key</code> in the <code>[keybindings]</code> section. The help menu and
every chord follow it, and ShowKeys highlights it so a viewer can spot it in a
recording.</p>
<p><em><a href="https://tuios.gaurav.zip/releases/v0-4-0">An interactive figure goes here. Open the page to use it.</a></em></p>
<h2>Changed: the patch releases</h2>
<p>v0.3.1 to v0.3.4 shipped in the first week of November. Most of it fixed
resizing in floating mode.</p>
<ul>
<li>During a drag, the window geometry updates at once and the PTY resize waits
until release, so the shell no longer gets a stream of resize signals.</li>
<li>The emulator's size stays in sync with the window's, so content no longer
sticks at the old size while the border moves.</li>
<li>A "Resizing... WxH" indicator shows in the window during a drag. In tiling
mode, neighbours pulled along by the drag show it too.</li>
<li>After a drag, PTY buffers are flushed and polling waits for the shell to
redraw its prompt at the new width, which fixed half-drawn prompts.</li>
<li>If the cursor ends up below the view after a height reduction, the view
scrolls to keep the prompt visible.</li>
<li>Dragging right after a resize no longer freezes the window.</li>
</ul>
<p>Other changes in the patches:</p>
<ul>
<li>Quitting with open terminals shows a confirmation. It asks only when a
terminal's foreground process is running something, so quitting idle shells
just quits.</li>
<li>The welcome screen shows on any workspace with no visible windows, not only
when the whole session is empty.</li>
<li>The shell is configurable. This mattered most on Windows, where tuios always
started PowerShell.</li>
</ul>
<h2>Fixed</h2>
<ul>
<li>Decimal durations in tapes parse correctly, so <code>1.5s</code> no longer rounds.</li>
<li>The render cache is cleared when leaving resize mode, so the resize
indicator disappears at once.</li>
<li>ShowKeys compares modifiers, so <code>Ctrl+L</code> followed by <code>L</code> shows two keys
instead of one pill.</li>
</ul>
<h2>Project</h2>
<ul>
<li>A contributing guide.</li>
<li>Package doc comments across all internal packages, a golangci-lint cleanup,
and a modernization pass (<code>any</code> over <code>interface{}</code>, <code>slices.Delete</code>, built-in
<code>min</code> and <code>max</code>).</li>
<li>README keybindings use kbd tags.</li>
<li><code>tuios.exe</code> is gitignored.</li>
</ul>
<p>The <code>.tape</code> format introduced here is the one project tapes were later built
on.</p>]]></content:encoded>
    </item>
    <item>
      <title>v0.3.0</title>
      <link>https://tuios.gaurav.zip/releases/v0-3-0</link>
      <guid isPermaLink="true">https://tuios.gaurav.zip/releases/v0-3-0</guid>
      <pubDate>Wed, 05 Nov 2025 00:00:00 GMT</pubDate>
      <dc:creator>Gaurav Gosain</dc:creator>
      <description>Bubble Tea v2 with keyboard enhancements, configurable tiling layouts, theming turned opt-in, and a scrollback rewrite.</description>
      <content:encoded><![CDATA[<p>27 commits in the three days after v0.2.0, including the v0.2.1 and v0.2.2
patch releases. The main change is the move to Bubble Tea v2, which lets tuios
use the kitty keyboard protocol. Tiling also got real layout controls, themes
became opt-in, and scrollback got 10 to 20 times faster.</p>
<h2>Breaking changes</h2>
<ul>
<li><strong>Themes are off by default.</strong> v0.2.0 turned them on, and an opaque themed
background fights any program that wants to set its own. The terminal
background is now transparent and the default theme is none, so you get your
terminal's own colours. To get the v0.2.0 look back, add
<code>theme = "tokyonight"</code> (or any theme) to your config.</li>
</ul>
<h2>Keys that mean what you typed</h2>
<p>tuios moved to Bubble Tea v2.0.0-rc.1 and handles its keyboard enhancement
messages. On a terminal that supports the kitty keyboard protocol, tuios
detects it and turns it on, with nothing to configure.</p>
<p>A classic terminal cannot tell Shift+Enter from Enter, and cannot report
Super+Space at all. With the protocol on, these arrive as separate keys,
complex modifier combinations work, international layouts report correctly,
and the keys reach the program in the pane unchanged.</p>
<p>In terminal mode, tuios used to catch the tiling resize keys, so you could not
type <code>&#x3C;</code>, <code>></code>, <code>{</code> or <code>}</code> into a shell. Now only the <code>Ctrl+B</code> prefix is caught
in terminal mode, and every other key goes to the PTY.</p>
<h2>Tiling you can shape</h2>
<ul>
<li>The master window ratio is configurable from 30% to 70%.</li>
<li>Eight new resize operations, with directional variants.</li>
<li>Each workspace remembers its layout and restores it.</li>
</ul>
<h2>Appearance</h2>
<p>These live in a new <code>[appearance]</code> config section, with matching flags.</p>
<ul>
<li>The dock can sit at the bottom, at the top, or be hidden. Contributed by
Cjreek.</li>
<li>Nine border styles: rounded, normal, thick, double, hidden, block, ascii, and
two half-block variants.</li>
<li>The window control buttons can be hidden.</li>
<li>Windows follow the cursor visibility a program asks for, so cmatrix hides the
cursor and nvim shows it.</li>
</ul>
<h2>Scrollback</h2>
<p>Scrollback moved from a growing slice to a fixed-size ring buffer. The common
operations went from O(n) to O(1), which is 10 to 20 times faster once the
buffer is full. The size is configurable from 100 to 1,000,000 lines, with a
default of 10,000.</p>
<h2>Fixed</h2>
<ul>
<li>Switching workspace during an animation left windows where the animation had
reached, sometimes one fullscreen with the rest piled on top. Animations now
finish on a switch: the old workspace snaps to its tiled layout at once, and
the new one tiles with its usual animation.</li>
<li>Grid layouts with five or more windows no longer leave dead space at the
bottom.</li>
<li>With the dock at the top, mouse resizing no longer uses boundaries two lines
off.</li>
<li>The parent <code>TERM</code> variable is kept, with <code>xterm-256color</code> as a fallback.</li>
<li>tuios answers XTWINOPS queries (CSI 14, 16 and 18 t), so programs that ask
for the window size in pixels or cells get an answer.</li>
<li>PTY setup is split into platform-specific files, which fixes the Windows
build after Unix-only process attributes broke it.</li>
<li>Viewport clipping mis-parsed ANSI escapes because of a logical AND that
should have been OR.</li>
</ul>
<h2>Project</h2>
<ul>
<li>Contributor plumbing, mostly from cr2007: issue forms for bugs and feature
requests, an auto-labeler, a PR template, a security policy, and README
keybindings shown with kbd tags.</li>
<li>The README notes kitty protocol support, from ShalokShalom.</li>
</ul>
<p>v0.3.0 was tagged on 5 November 2025. Everything after it builds on the Bubble
Tea v2 migration.</p>]]></content:encoded>
    </item>
    <item>
      <title>v0.2.0</title>
      <link>https://tuios.gaurav.zip/releases/v0-2-0</link>
      <guid isPermaLink="true">https://tuios.gaurav.zip/releases/v0-2-0</guid>
      <pubDate>Sun, 02 Nov 2025 00:00:00 GMT</pubDate>
      <dc:creator>Gaurav Gosain</dc:creator>
      <description>340 color themes that restyle the UI and the terminal palette together, a new PTY layer, and windows that can no longer leave the screen.</description>
      <content:encoded><![CDATA[<p>Eight commits. Two are large: tuios got themes, and a new PTY layer under every
window. This is also the release where tuios restores your terminal properly
when it exits.</p>
<h2>Themes</h2>
<p>tuios ships over 340 colour themes from bubbletint. Pick one with <code>--theme</code>
(the default is <code>tokyonight</code>).</p>
<ul>
<li>A theme recolours the programs in your windows too, not only the chrome. A
custom SGR handler maps the 16 ANSI colours to the theme's palette, so <code>ls</code>,
vim and htop follow it.</li>
<li>256-colour and RGB sequences pass through unchanged, so a program that asks
for an exact colour still gets it.</li>
<li>Borders show state: an unfocused window uses the theme's red, a focused
window in window mode is bright cyan, and in terminal mode it is bright
green.</li>
<li><code>--list-themes</code> prints every theme, and <code>--preview-theme</code> shows a theme's 16
ANSI colours with their hex values.</li>
</ul>
<h2>A new PTY layer</h2>
<p>Every window runs on a pseudo-terminal, and this release swaps the library
that provides it: go-pty out, charmbracelet's xpty in, for a cleaner API and
better Windows ConPTY support.</p>
<p>The switch first broke shell startup. Processes no longer got the PTY as their
controlling terminal, which shells such as fish need, so windows opened and
died at once with exit status 1. The fix, in the same release, sets up the
process session and controlling terminal explicitly, as the old library did
implicitly.</p>
<h2>Changed and fixed</h2>
<ul>
<li>Windows can no longer be dragged or resized off screen. They stay inside the
viewport and out of the dock, which also fixes drawing artifacts at the
edges.</li>
<li>tuios exits cleanly. It handles SIGINT and SIGTERM, and on the way out it
turns off mouse tracking, restores the cursor, and leaves the alternate
screen, so your shell prompt is intact.</li>
<li>The README gave two background refresh rates. It now says 30Hz, not 20Hz.
Caught by ShalokShalom.</li>
<li>The Nix packaging workflow now runs on tag pushes, not only on release
events, so it runs for every version.</li>
</ul>
<p>v0.2.0 was tagged on 2 November 2025. Two patch releases followed within days.
Their changes are on the <a href="https://tuios.gaurav.zip/releases/v0-3-0">v0.3.0 page</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>v0.1.0</title>
      <link>https://tuios.gaurav.zip/releases/v0-1-0</link>
      <guid isPermaLink="true">https://tuios.gaurav.zip/releases/v0-1-0</guid>
      <pubDate>Fri, 31 Oct 2025 00:00:00 GMT</pubDate>
      <dc:creator>Gaurav Gosain</dc:creator>
      <description>The first tagged release. A floating and tiling terminal window manager with a vim-style copy mode, scrollback, a TOML config with rebindable keys, and packaging for most platforms.</description>
      <content:encoded><![CDATA[<p>The first release with a minor version number. The first commit, on 6
September 2025, was already a working prototype: floating windows, tiling,
workspaces, an SSH server and window animations, all in one flat Go package.
The 70 commits after it went through 26 v0.0.x tags in under two months and
added what you need to install tuios, configure it, and copy text out of it.</p>
<h2>Breaking changes</h2>
<ul>
<li>The CLI moved from flat flags to subcommands. <code>--ssh</code> became <code>tuios ssh</code>,
and the other flags moved the same way.</li>
</ul>
<h2>Scrollback and copy mode</h2>
<p>Before this release, text that scrolled off screen was gone, and text on
screen could not be copied. Both are fixed.</p>
<ul>
<li><strong>Scrollback.</strong> Each window keeps 10,000 lines. Enter it with <code>Ctrl+B [</code> or
by scrolling the mouse wheel up, and move with the arrows or vim keys. The
upstream VT library had no scrollback, so tuios runs on a fork, and the
change was sent upstream as a pull request to charmbracelet/x.</li>
<li><strong>Copy mode.</strong> Vim-style motions: <code>hjkl</code>, word motions, <code>gg</code> and <code>G</code>, count
prefixes (<code>10j</code>, <code>100G</code>), <code>f</code>, <code>F</code>, <code>t</code> and <code>T</code> character search that handles
wide characters, <code>/</code> and <code>?</code> search with highlighted matches, <code>%</code> for bracket
matching, and <code>v</code> or <code>V</code> visual selection yanked to the clipboard with <code>y</code>.</li>
<li><strong>Mouse selection.</strong> Click and drag, double-click for a word, triple-click
for a line.</li>
</ul>
<p>Copy mode started as one 2,137-line file and was split into six modules before
the tag.</p>
<h2>Configuration and the CLI</h2>
<ul>
<li><strong>A config file.</strong> <code>~/.config/tuios/config.toml</code> is created on first run with
comments that document it, and follows XDG. Every keybinding in every mode
can be rebound.</li>
<li><strong>Actions, not code.</strong> A key maps to an action name and an action name maps
to a handler, so rebinding needs no code change. The help overlay is built
from the config, so it shows the keys you actually have.</li>
<li><strong>Platform defaults.</strong> macOS gets <code>opt</code> where Linux gets <code>alt</code>.</li>
<li><strong>Subcommands.</strong> The CLI moved to Cobra, with Fang for help output: <code>tuios</code>
runs it, <code>tuios ssh</code> runs the SSH server, and <code>tuios config</code> and
<code>tuios keybinds</code> manage the two above. Shell completions and man pages are
generated.</li>
</ul>
<h2>The prefix key</h2>
<p>Everything hangs off a tmux-style <code>Ctrl+B</code> prefix, which now has submenus:</p>
<ul>
<li><code>w</code>: workspaces (switch to one, or move a window to one)</li>
<li><code>m</code>: minimize and restore</li>
<li><code>t</code>: window commands</li>
<li><code>[</code>: scrollback</li>
<li><code>q</code>: quit</li>
</ul>
<p>A which-key overlay appears after half a second and shows what each submenu
offers.</p>
<p>On macOS, Option plus a number types a character such as <code>¡</code> or <code>™</code> instead of
sending a modifier. tuios recognises those characters as workspace switches,
and the help text says "Opt" on a Mac and "Alt" elsewhere.</p>
<h2>Rendering is 30 times faster</h2>
<p>The prototype rendered every cell of every window through a new lipgloss
style on every frame. A 13.45-second CPU profile spent 4.85 seconds in
<code>renderTerminal</code>. Two changes fixed it:</p>
<ul>
<li>A style cache keyed on cell attributes. A hit costs about 99 nanoseconds and
no allocations.</li>
<li>Terminal content is written as ANSI escape codes directly, skipping a style
pipeline built for UI boxes. Neighbouring cells with the same style are
batched, which cut style renders per frame from about 2,000 to about 50.</li>
</ul>
<p>After both, <code>renderTerminal</code> took 0.16 seconds in the same profile, and total
CPU use fell from 53% to 24%. The input path also stopped clearing the render
cache on every keystroke.</p>
<p><em><a href="https://tuios.gaurav.zip/releases/v0-1-0">An interactive figure goes here. Open the page to use it.</a></em></p>
<h2>Terminal behaviour</h2>
<ul>
<li>Mouse events reach programs on the alternate screen, so drag-to-select works
in vim, scrolling in less, and clicking in htop.</li>
<li>Paste works through OSC 52 and bracketed paste, including <code>Cmd+V</code>.</li>
<li>The alternate screen is detected through VT callbacks instead of guessed.</li>
<li>nushell no longer clears its own output. The VT library answered cursor
position queries with stale coordinates, so tuios now corrects those
replies.</li>
<li>The PTY layer changed twice and ended on charmbracelet/x/xpty, for Windows
ConPTY support. The forked VT package is vendored, so plain <code>go install</code>
works.</li>
<li>In tiling mode, swapping, minimizing and retiling only touch windows in the
current workspace.</li>
</ul>
<h2>Installing</h2>
<ul>
<li>GoReleaser builds for Linux, macOS, Windows, FreeBSD and OpenBSD.</li>
<li>Releases publish to a Homebrew tap and the AUR (<code>tuios-bin</code>), and there is a
one-line curl installer.</li>
<li>The Nix package and devshell were the project's first outside contribution.</li>
<li>The binary is statically linked, so there is no glibc version to match.</li>
<li><code>--ascii-only</code> draws without Nerd Font icons, for machines that do not have
one. It was added four days before the tag.</li>
</ul>
<h2>Fixed</h2>
<ul>
<li>Notifications stayed up for about 17 days instead of 1.5 seconds. A duration
already in milliseconds was multiplied by <code>time.Millisecond</code>.</li>
<li>Comparing cell colours could panic on a nil pointer: a non-nil <code>color.Color</code>
interface can hold a nil value. This was the last fix before the tag.</li>
<li>Typing over SSH did nothing after the restructure, because the input handler
was registered for local mode only.</li>
<li>On macOS, <code>exit</code> did not close the window when the exit message was missed.
The tick loop now checks the process directly.</li>
<li>Visual selection highlighted the empty cells past the end of each line. It
now stops at the text.</li>
<li>Mouse selection in copy mode was off by one row.</li>
<li>Dragging a window a few pixels no longer corrupts the tiled layout.</li>
<li>The Windows build compiles again, after resize signalling was split into
platform-specific files. Nobody had reported running it there.</li>
</ul>]]></content:encoded>
    </item>
  </channel>
</rss>
