Skip to content

Unify the preview rail onto one tab list - #72963

Merged
OutThisLife merged 3 commits into
mainfrom
bb/artifacts-real-preview
Jul 27, 2026
Merged

Unify the preview rail onto one tab list#72963
OutThisLife merged 3 commits into
mainfrom
bb/artifacts-real-preview

Conversation

@OutThisLife

Copy link
Copy Markdown
Collaborator

The right rail had two preview systems living in one tab strip: a list of
file tabs, plus a privileged "live preview" slot with a hardcoded preview
tab id backed by a separate session-keyed registry. Artifacts had recently
arrived as a third list with their own pane and renderers. This collapses
all three into one list, then fixes two bugs that only became visible once
there was a single path to look at.

One list, one way in

Everything the rail can show is now a PreviewTarget in $previewTabs,
and openPreview is the only entry point. $previewTarget is a computed
read of the active tab; the session registry and the effect that reconciled
it against the tabs are gone.

That reconciler is what made open_preview unreliable. The two systems
resolved "which session is this for" under different rules, so a tool
opening a preview in a session whose stored id hadn't landed yet would set
the target and then immediately clear it — the pane flashed and vanished.

Artifacts render in the real preview pane through the shared mode switcher
and source view rather than a parallel pane, with a version stepper in the
header's trailing slot. Their tabs stay memory-only since the registry
rebuilds from the transcript on load; file and URL tabs persist like they
did before.

Composer and transcript state follows its own session

$activeSessionId only ever holds the primary chat's session, but surfaces
that render once per transcript were reading it as if it meant "the session
on screen." A preview produced inside a session tile got recorded under the
main chat's key and showed up in the main chat's composer.

The tool row now records under its own SessionView, along with the other
per-surface readers of that atom: attachment pills and inline preview links
resolve relative paths against their session's cwd, composer voice and
auto-speak read and subscribe to their own transcript, and the thread's
compaction label, prompt-wait gate and turn timer follow the session that
mounted them.

Previews open without the file tree

The preview pane shares a collapsible column with the file tree, and
revealTreePane un-collapses a column through that column's bound store —
which on the right is $fileBrowserOpen, the tree's own Cmd+J toggle. Every
preview open was calling setFileBrowserOpen(true) and bringing the tree
with it. The reveal now un-collapses the column directly and leaves the
toggle alone.

Test plan

  • open_preview from the main chat opens the rail and it stays open
  • open_preview from a session tab opens the rail (the event used to be
    dropped, since it was gated on the primary chat's session id)
  • The preview chip lands in the composer of the tab that produced it
  • A preview opens without the file tree appearing; Cmd+J still brings the
    tree in beside it
  • Artifact cards open as named tabs, version stepper follows new versions
  • File, URL and artifact tabs coexist; re-opening re-fronts instead of
    duplicating
  • File and URL tabs survive relaunch; artifact tabs rebuild from the
    transcript
  • tsc, eslint, and the desktop suite green

Two related bugs are deliberately left out: native notifications and the
background-delegation notice read the same primary-only atom, so you get a
completion toast for the tile you're looking at. Fixing them means importing
$focusedRuntimeId into those leaf stores, which drags in session-states
and its module-level layout side effects — that hung unrelated suites. It
needs those side effects untangled first.

The right rail held two things at once: a list of file tabs, and a
privileged "live preview" slot with a hardcoded `preview` tab id backed by
a separate session-keyed registry. The two were written under different
session-id rules and reconciled against each other, so an `open_preview`
from a session whose stored id hadn't landed yet was set and then
immediately cleared — the pane flashed and vanished. Artifacts arrived as
a third list with their own pane and renderers.

Now everything the rail can show is a `PreviewTarget` in `$previewTabs`,
and `openPreview` is the only way in. `$previewTarget` is a computed read
of the active tab, the session registry and its reconciler are gone, and
artifacts render in the real preview pane through the shared mode switcher
and source view instead of a parallel one. Artifact tabs stay memory-only
since the registry rebuilds from the transcript.
`$activeSessionId` only ever holds the primary chat's session, but surfaces
that render once per transcript were reading it as if it meant "the session
on screen." A preview produced inside a session tile was recorded under the
main chat's key and surfaced in the main chat's composer, which is what
prompted this.

The tool row now records under its own `SessionView`, and the same fix
applies to the other readers of that atom that render per surface:
attachment pills and inline preview links resolve relative paths against
their session's cwd, composer voice and auto-speak read and subscribe to
their own transcript, and the thread's compaction label, prompt-wait gate
and turn timer follow the session that mounted them. `ComposerScope` now
carries a `$messages` atom rather than a read closure so both the
imperative read and the subscription come from one place.
The preview pane shares a collapsible column with the file tree, and
`revealTreePane` un-collapses a column through that column's bound store —
which on the right is `$fileBrowserOpen`, the tree's own ⌘J toggle. So
every preview open literally called `setFileBrowserOpen(true)` and the tree
came with it.

`revealPreview` now un-collapses the column directly and leaves the toggle
alone. The tree pane's visibility binding gains `$fileBrowserOpen` to match,
since its presence was tracking only the column's collapse — without that it
would still render the moment anything opened the column.
@OutThisLife
OutThisLife merged commit fbd8d1a into main Jul 27, 2026
31 checks passed
@github-actions

github-actions Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

૮ >ﻌ< ა ci review

ran on cd25a9f

ℹ️ Info

Desktop E2E visual evidence · View test artifacts · View job

1 visual diff.

inline evidence is publishing...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant