thatmike1/t3code · batch-2

fork of pingdotgg/t3code · installed build of 15 Sep 2026

T3 Code,
with the extras.

Everything the fork puts on top of upstream: a widget tray in the sidebar, bead ids that link to bd-board, Ctrl+Tab between recent threads, a chat column that sits left of the screen's middle, effort on the keyboard, and the fixes that came along.

14 commits · 54 files · +2,201 −44 · stacked on upstream main as of 15 Sep

t3 fork showcase pageccChat-general ctrl tab 3recent thread switcher
where did the short bead links land, and is next?
Worked for 2m 10s
Ran 3 commands

Short ids shipped as on batch-1, and the installed AppImage carries them. Yes, is next: row actions for widgets, since the contract in widgets.ts is still view only.

2bead id chips
Ask anything, @tag files/folders, $use skills, or / for commands
Opus 5 Medium 5alt+2 / alt+5 Full access

The widgets at the bottom left are live replicas of your real files: hover for the line, click for the rows. The numbered dots jump to each feature.

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.

where it lives
packages/contracts/src/widgets.ts
apps/server/src/widgets/Widgets.ts
apps/web/src/components/sidebar/SidebarWidgets.tsx
projects/t3-widgets/t3widget.py
~/.t3/userdata/widgets/
tally: codex workday pace38m ago
batch-2 polish: gutter and pin1h ago
stripe webhook retries1d ago
Edit the JSON and the tray on the right follows. Try

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.

where it lives
apps/web/src/markdown-bead-links.ts
apps/web/src/lib/beadBoardIds.ts
apps/web/src/components/chat/BeadChip.tsx
bd-board
hover a chip to see where it goes
Worked for 1m 42s

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.

where it lives
apps/web/src/components/ThreadSwitcher.tsx
apps/web/src/thread-switcher.logic.ts
apps/desktop/src/window/ThreadSwitcherInput.ts

+ +

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.

where it lives
packages/contracts/src/settings.ts · chatAlignment, chatLeftGutter
apps/web/src/index.css · .chat-reading-column
apps/web/src/components/settings/SettingsPanels.tsx
gutter 320px
middle of the screen
column starts at width gutter in use

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.

where it lives
apps/web/src/components/chat/TraitsPicker.tsx · stepEffortDescriptors
packages/shared/src/keybindings.ts
Ask anything, @tag files/folders, $use skills, or / for commands
Opus 5 Medium Full access

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.

where it lives
apps/web/src/components/Sidebar.tsx · SidebarDragBoundary

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.

where it lives
apps/server/src/http.ts

before · any page over 1 KB

127.0.0.1/api/assets/…/tally-report.htmlno content-type
<!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

127.0.0.1/api/assets/…/tally-report.htmltext/html
tally

5h window at 20%, you make it

15 Sep · fb9b5e9c7b

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.

15 Sep · 26897c863c

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.

15 Sep · 87c5214404

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.

output tokens T3 reported, 28 Aug to 3 Sep5.26M
what the transcripts hold (ccusage and cc-browse agree)8.14M
content blockmessage idoutput_tokens
thinkingmsg_01H…q7 · req_8f…1first wins
tool_usemsg_01H…q7 · req_8f…1,284largest wins
pingdotgg/t3code#9439 · keep the largest output count in a Claude dedupe group open

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.

batch-2installedswitcher, chat layout, polish and fixes
batch-1short bead ids, effort keys, pinned rule
bead-linksbead chips, asset content-type fix
widgetscontract, watcher, sidebar tray
origin/mainpingdotgg/t3code, rebased onto 15 Sep
status

How far upstream has moved and where each fork branch sits.

rebase

Tags the old heads as pre-rebase-<mmdd-hhmm>/<branch>, then moves the whole stack with --update-refs. Stops with exit 2 on a conflict.

verify

Typecheck plus the test files the fork touches.

push

Pushes the stack to thatmike1/t3code.

build

Builds the desktop AppImage, stages it as t3-code.AppImage.<stamp>-new and checks the fork's markers are inside.

$ projects/t3-widgets/t3-fork-update.sh status $ projects/t3-widgets/t3-fork-update.sh rebase $ projects/t3-widgets/t3-fork-update.sh verify $ projects/t3-widgets/t3-fork-update.sh push $ projects/t3-widgets/t3-fork-update.sh build