A sleek GUI for pi
The band is sticky and zero-height, so it paints over the row instead of taking its space.
164 <div className="sticky top-0 z-20 h-0" aria-hidden="true" data-testid="session-tree-pinned">
It paints over the row instead of moving it: the band is an overlay, and nothing reserves its height.
The band holds its own height now, so the rows move up by exactly that much while one is pinned.
Your pi, unchanged
It is the pi you already have. Models, skills, extensions and sessions are the ones already on the machine, read from pi’s own configuration and session directory — no import step, no second setup, nothing to keep in sync.
View modes
One transcript, three densities. Minimal keeps the timer and the answer, Compact folds consecutive read-only calls into one line, and Show All is the native pi log — pick one from the filter menu and the same turn is drawn three ways.
Instant session switch
A long session is not a loading screen. Sessions open as fast as the window does, parsed in their own process and drawn as a windowed list that measures a row once — and the minimap takes you anywhere in it.
Fork
Branch off any message. Forking copies the conversation up to that point into a chat of its own, with the same model and thinking level; the original stays where it was, and the fork is listed beneath it.
Tree
Rewind without losing what you had. Going back moves the session — nothing is deleted and nothing has to be summarised, every branch is still in the tree, and the ? beside its search field says what a jump really does.
Session switcher
Every session is one line away. One field over all of pigi — every session, in every project, on Ctrl + R — and the letters you typed are marked in the titles, so you can see why a row is there.
Terminal
A shell in the same window. ⌘J opens a real terminal along the bottom, already in the project the agent is working in: run the test it just broke without leaving the conversation.
The signing identity is in the config, so the build is finding the config and not the keychain. The bump moved the identity lookup behind an environment flag.
14 identity: Developer ID Application: Earendil Inc. (7X2K4M9QLB) 22 notarize: true
The identity is still in the config; the bump resolves it from the environment now, and there is nothing exported in the release job.
26.0.12
Pinned to 26.0.12, which reads the identity from the config again, and left the version in the release skill so the next bump checks it before tagging.
Clean tree, and the lockfile is the one the tag will build from.
31 export function lineageDepth(parentPath: string | null): number {
54 const isLastChild = index === siblings.length - 1;It does. Depth is read from the session file, so a fork is drawn under its parent wherever it is opened from.
The scroll position is restored before the rows exist, so the browser clamps it to a list that is still empty.
It restores before the first window of rows is laid out, and a scroll into an empty list is clamped to nothing. Restored after the first frame, it lands where it left.
Every row is measured on mount, so a session with thousands of entries spends the first frames laying all of them out.
86 * Row heights are constants and headers are measured, so
Rows are a fixed height and the list draws a window over them, so it never measures the whole tree.
One bar per user message, sized by how long the message is.
78 const [maxLineWidth, setMaxLineWidth] = useState(
One bar per user message. Here is the plan.
Width now tracks the message length, clamped so a two-word message is still a target.
Added — the list beside the bars reads the message and its time.
Only the bar nearest the middle of the viewport is lit now.
Tree: a time machine for this session
Hover a row for a preview, search or filter to find one, then click it (or press Enter) to move the session there. Searching picks out the first match, and the up and down arrows move that pick without leaving the search box.
One bar per user message, sized by how long the message is.
78 const [maxLineWidth, setMaxLineWidth] = useState(
One bar per user message. Here is the plan.
Width now tracks the message length, clamped so a two-word message is still a target.
Added — the list beside the bars reads the message and its time.
Only the bar nearest the middle of the viewport is lit now.
The band is sticky and zero-height, so it paints over the row instead of taking its space.
164 <div className="sticky top-0 z-20 h-0" aria-hidden="true" data-testid="session-tree-pinned">
It paints over the row instead of moving it: the band is an overlay, and nothing reserves its height.
The band holds its own height now, so the rows move up by exactly that much while one is pinned.
Test Files 3 passed (3)
Tests 49 passed (49)Green. The panel is open on the run if you want the full output.