added 10 Sep
A tray in the sidebar
Any script on the machine can put a live icon in T3's sidebar by writing one JSON file.
The server watches ~/.t3/userdata/widgets/ and streams every <id>.json in it to the client over the subscribeWidgets RPC. A widget is an icon, an optional short label, a hover line, and optional rows that open in a popover. tally, phone-mic and warden publish today, through t3widget.py, which writes atomically and skips a write when nothing changed.
The filename is the id. A file that fails to decode is simply not shown, and never empties the rest of the tray. A widget whose updatedAt is older than staleAfterSeconds (120 by default) greys out, so a dead publisher is visible.
It is view only for now. Row actions, like opening a URL or starting a unit, are bead u4f.
The four states a widget can report, plus stale, which the client works out on its own. ok and off keep the sidebar's icon colour; attention adds a dot.
added 10 Sep, short ids 14 Sep
Bead ids become links
An issue id in any message turns into an amber chip that opens bd-board on that issue.
Full ids like ccChat-general-4y6 link anywhere, in prose or in code, matched against a prefix allowlist. Child ids like zye.6 link the child rather than the parent. Chips open 127.0.0.1:1338/#<id>.
Short ids only count inside backticks, and only when bd-board's /api/issue-ids lists them. That is what keeps an ordinary word like ttl as code. The id list is fetched once and refetched after 60 seconds; with bd-board stopped, short ids fall back to plain code and full ids still link.
added 15 Sep
Ctrl+Tab through recent threads
Hold Ctrl and tap Tab to walk threads in the order you last used them; let go to open the highlighted one.
A single tap flips between the last two threads. Shift walks the other way, Escape cancels. Each row shows the title, project, model, status and last activity, so the walk is never blind. The model label came in the polish pass, with the provider name as the fallback.
On the desktop build the chord is read in Electron's main process, because preventing the keydown in the renderer on X11 swallowed the Ctrl release and the switch never committed. The recency list lives in memory, so it starts fresh after a restart.
Browsers keep Ctrl+Tab for themselves, so these keys are clickable instead. Click ctrl to hold it, tab to walk, ctrl again to let go. Your real keyboard works too: hold q for ctrl and press w for tab.
added 15 Sep, range raised to 500px the same evening
The chat sits where you look
On the ultrawide, a left-anchored column with an adjustable gutter keeps the conversation left of the screen's middle instead of well right of it.
Settings has a new Chat layout section: alignment Centered or Left, and a left gutter from 0 to 500px in 4px steps. You run Left with a 320px gutter. The gutter gives way first when the pane narrows, so the column keeps its 48rem reading width for as long as it can.
The column also sizes around the timeline's scrollbar, so the messages, the scroll-to-end pill and the composer share one left edge.
Modelled at 2560px with a 260px sidebar, 20px edge padding and a 768px column, so the numbers are close rather than exact.
added 14 Sep
Effort on the keyboard
Alt+2 steps reasoning effort down and Alt+5 steps it up, without opening the traits menu.
It walks whichever effort control the current model has (effort on Claude, reasoningEffort on Codex, reasoning, variant) and stops at the ends. Ultracode and Ultrathink are modes rather than levels, so a step never lands on them, and while ultrathink is typed in the prompt the keys do nothing.
A step sticks like a pick from the menu. The keys are rebindable as composer.effort.decrease and composer.effort.increase, and stand down while a terminal has focus.
Clickable here, and 2 / 5 work while this section is on screen (Alt+digit switches browser tabs).
improved 14 Sep, spacing fixed 15 Sep
Pinned threads get their own block
A hairline under the pinned group separates it from the rest of the list at a glance.
The rule is sidebar-foreground at 20%, so it follows both themes. It hides during a drag, where the existing Pinned and Active labels take over, and it now sits 4px clear of the first unpinned row's hover highlight, which used to overlap it by a pixel.
upstream
fork
Hover the first thread under the rule to see the clearance.
fixed 11 to 15 Sep
Fixes that came along
HTML previews render as pages again, and three rough edges in the fork's own features were smoothed before you used them for long.
The preview bug is upstream's. Every /api/assets/ response over 1024 bytes lost its Content-Type, because Effect's compression middleware rebuilds the body from body.contentType, which a file response leaves undefined, and then drops the header. Adding no-transform to the asset Cache-Control makes the middleware skip those responses.
The proper fix belongs in Effect itself and has not been written.
before · any page over 1 KB
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>tally · what ate the 5h limit</title>
<style>
body { font-family: Figtree, sans-serif;
background: #f6f1ea; margin: 0 }
.bar { height: 12px; border-radius: 6px }
</style>
</head>
<body>
<h1>tally</h1>
<p>5h window at 20%, you make it</p>
after · Cache-Control: …, no-transform
tally
5h window at 20%, you make it
Ctrl release is not lost
The first switcher build opened the overlay and then never switched on X11. The chord is now handled in the desktop main process, where the key-up still arrives.
Left chat lines up with its scrollbar
In Left mode the timeline reserves a stable scrollbar gutter, and the composer subtracts the same width, so the two columns no longer drift apart by a scrollbar.
Pinned rule clears the hover
The hover highlight on the first unpinned row covered the rule by one pixel. Measured in the packaged app, it now clears it by four.
sent upstream 3 Sep
Output tokens, counted right
PR #9439 fixes T3's usage page under-counting Claude output tokens by about a third. It is still open.
Claude Code writes one transcript line per content block. Usually every line repeats the finished usage, but sometimes the thinking block says output_tokens: 1 and the tool call after it carries the real total, under the same message id. T3 kept the first record it saw. The fix keeps the one with the largest output count and bumps the scan cache version so old results re-parse.
It is not part of the fork build; it lives on its own branch, fix/usage-progressive-output-tokens.
The token counts in the example rows are illustrative; the weekly totals are from the PR.
upkeep
How the fork keeps up
One script rebases the whole branch stack onto upstream, checks it, and stages a new AppImage for you to swap in.
The fork has no auto-update feed, so upstream changes arrive when you run t3-fork-update.sh, step by step. The spots that conflict on every rebase are server.test.ts, the a renderer in ChatMarkdown.tsx, and the command list in keybindings.ts. A typecheck after the rebase catches the one that never shows up as a conflict: the test layer pipe sits at Effect's 20-argument cap.
Seven older AppImages sit next to the live one in ~/Applications, back to upstream 0.0.40, if you ever need to roll back or free a gigabyte.
Next in line is u4f: row actions for widgets, so tally can open its pages and phone-mic can start and stop.
statusHow far upstream has moved and where each fork branch sits.
rebaseTags the old heads as pre-rebase-<mmdd-hhmm>/<branch>, then moves the whole stack with --update-refs. Stops with exit 2 on a conflict.
verifyTypecheck plus the test files the fork touches.
pushPushes the stack to thatmike1/t3code.
buildBuilds the desktop AppImage, stages it as t3-code.AppImage.<stamp>-new and checks the fork's markers are inside.