← the Atlas

The Code tab is a browser → @kolu/solid-browser

feature · seedling ·implemented ·

The real electricity hiding in the Code tab isn't a history stack — it's the browser. Extract the location + history + link-navigation shell that drives @kolu/solid-fileview over a resolver; back/forward falls out for free, and git becomes an injected resolver.

Plan of record · branch md-back-link · phases 1 + 2 shipped in #1191

The question that started this was small: what does it take to add back/forward navigation to the Code tab’s preview? The answer kept wanting to be “a history stack.” That answer is wrong — not incorrect, but mis-scoped. A history stack is a transformer: a working part inside a concept. The concept is the browser, and it has been hiding in the Code tab the whole time.

browse · docs/atlas/src/content/atlas/electricity.mdx#L18
rendered Markdown — click a repo-relative link → the browser navigates → ◀ goes back to where you were, scroll and highlight intact. Same engine renders source, HTML, SVG, PDF.
git is not in this picture. The browser resolves location → content through an injected resolver; kolu's resolver is the only part that knows "repo-relative path + mode."

The Code tab is already a browser

The reframe is one move — name the concept, not a mechanism inside it. Two observations make that concrete.

The tell: the renderers are already extracted; the shell isn’t

kolu already pulled the appliances out into packages — #1079 (@kolu/solid-markdown), #1082 (@kolu/solid-fileview), @kolu/solid-pierre (source). See solid-fileview — “invent the grid, slim the house.” What that effort extracted was how a single document renders. What it left behind, still smeared across the client, is the layer above a single document: how you move between documents. That layer is the browser, and today it is complected across five files:

Smeared acrossOwns (a browser organ)
right-panel/openInCodeTab.ts packages/client/src/right-panel/openInCodeTab.ts:78-85The address bar — the single “navigate to this location” front door.
right-panel/CodeTab.tsx packages/client/src/right-panel/CodeTab.tsx:515-542handleSelect — the navigation controller (apply a location to the view).
right-panel/markdownImageSrc.ts → now solid-browser/src/relativePath.ts packages/solid-browser/src/relativePath.tsRelative-link resolution (GitHub rules) — agnostic URI math.
right-panel/iframePreviewNav.ts → now solid-browser/src/previewPath.ts packages/solid-browser/src/previewPath.tsIn-frame link interception — map an iframe pathname back to a location.
right-panel/BrowseIframeRenderer.tsx packages/client/src/right-panel/BrowseIframeRenderer.tsx:56-64In-iframe link observation — a navigation edge out of rendered HTML (the iframe drawing is already @kolu/solid-fileview’s).

None of these is about git, and none is about how a file draws (that’s @kolu/solid-fileview). They are about locations, links, and history — the vocabulary of a browser.

OpenInCodeTabRequest is already a URL

Look at the front-door request shape packages/client/src/right-panel/openInCodeTab.ts:31-54:

{ ref: { path, startLine, endLine }, repoRoot, cwd, targetMode, allowBasenameFallback }

That is a URL. ref is the path + fragment; targetMode is which “site” (browse/local/branch); repoRoot/cwd are the base href. And openInCodeTab() is already location.assign() — the single producer-side entry every navigation routes through, which is why relative Markdown links (#1161, #1190 ) and terminal path:N links and the right-click Open path menu all funnel into it. The browser is half-built; it is missing only history.back()/forward() and the popstate semantics over a stack of these locations.

The electricity — @kolu/solid-browser

A standalone package. As shipped (phases 1 + 2) it depends on @kolu/url-shape + solid-js and nothing kolu — the dependency arrow points out of the client, never back in. It owns URIs, locations, links, and history (createBrowser is the reactive history controller). It knows nothing of git, repos, modes, terminals — nor of how any single format renders (that is fileview’s). The diagram below draws the full target shape, including the parts still deferred: a <Browser> component that composes @kolu/solid-fileview to draw whatever a location resolves to. Today the host (CodeTab) keeps both rendering paths and consumes only the history controller + the path utilities — so the package depends on @kolu/solid-fileview only once <Browser> lands, not now (see the phase-2 as-built callout for why <Browser> is deferred).

kolu app — the consumer@kolu/solid-browser — the electricity@kolu/solid-fileview — the viewport (one FileData + toggle)appliancesright-panel/CodeTab.tsxgitResolver.ts (git domain) — deferredlocationcreateBrowser (back / forward) — shippedlinkNav (resolve + intercept) — shipped<Browser> — deferred@kolu/solid-markdown@kolu/solid-pierre records / replays navigation (shipped) mounts <Browser>, injects renderers (deferred)resolve(location) -> FileData (deferred)composes (deferred)drawsdraws
Architectural connections — the target shape. SOLID edges are shipped (CodeTab drives createBrowser; the package depends only on @kolu/url-shape + solid-js). DASHED edges (mounts <Browser>, composes <FileView>, the gitResolver injection) are DEFERRED — see the phase-2 callout. git lives only in the injected resolver.

Once <Browser> lands, what the host injects is exactly the volatility kolu owns:

Does “browser” clear the electricity bar where “history” didn’t?

Against electricity’s own three tests:

Test”history” stack@kolu/solid-browser
domain-agnostic✓ (but T carried no meaning)✓ — URIs/locations/links/history; git is injected, rendering is fileview’s
hides a hard volatilityno back/forward is a bounded algorithm — nonempty-tier leafyes resource resolution (transport) + navigation across heterogeneous rendered content (DOM-anchor, in-iframe, tree → one location model) + GitHub-relative path semantics + history/popstate + the production-build effect-elision invariant the front door was built to dodge packages/client/src/right-panel/openInCodeTab.ts:9-19
graduates — proof is realmeaningless standalonea docs/wiki viewer, a drishti-style host inspector (logs + configs over ssh, links between them), an artifact viewer — each injects its own resolve and reuses the published renderers

The design

Two mechanisms carry the feature — one for moving (the seam), one for remembering where you were (history). Both live in the engine, never in CodeTab.

The seam — split assign from record

Back/forward hinges on one split the front door doesn’t yet make — separating applyLocation (assign) from navigate (assign and record):

applyLocation(loc)            // the existing batch(openCodeAt + reveal + setPending) — "assign"
navigate(loc)  = applyLocation(loc) + history.push(loc)   // address-bar navigation
back()         = applyLocation(history.back())            // traverse — NO push
forward()      = applyLocation(history.forward())         // traverse — NO push

This split is mandatory, not cosmetic: if back/forward went through the recording path you’d get the classic “going back creates new history” bug. Traversal must apply-without-recording — which is why the split is a phase-2 (history) concern, not what makes the engine extractable. The extraction (phase 1) leans on a different property of the same applyLocation: it’s the one organ that knows about modes and the pending-highlight signal, so it stays kolu-side while the rest of the engine graduates.

The feature that rides out for free — unified back/forward

Back/forward rides the extraction — the engine has history because a browser does, so phase 2 is mostly exposure, not invention. Settled design (from the originating discussion):

Building it

Files this touches

The electricity — a new isolated package:

PackageContents
packages/solid-browser phases 1 + 2Shipped: relativePath.ts + previewPath.ts (phase 1) and createBrowser.ts — the generic reactive history controller (phase 2) — with 42 unit tests + a standalone example/docsite second host. deps: @kolu/url-shape (the DOM-free hasOwnScheme leaf) + solid-js (createBrowser’s reactive stack). <Browser> composing <FileView> stays deferred (see the phase-2 as-built callout). Zero kolu imports; builds + tests standalone.

kolu — the consumer, now just git + chrome:

FileChange
right-panel/useRightPanel.ts phase 2Hosts a per-terminal Browser<BrowserLocation> (in-memory; seeded on restore, dropped on teardown) and exposes recordNavigation/navigateBack/navigateForward/canNavigateBack/canNavigateForward. selectedFileByMode stays the render + restore truth; history is additive over it.
right-panel/CodeTab.tsx phase 2Records every selection (tree click, in-iframe link, resolved front-door open) into history; ◀ ▶ toolbar buttons + a scoped Alt+←/→ listener re-apply earlier locations. Cheap-v1 re-highlight rides the existing front-door pipeline.
gitResolver.ts · <Browser> · unified navigate deferredThe resolve(location)→FileData injection, the <Browser> component, and folding the two nav paths into one navigate wait on a uniform viewport — diffs don’t fit FileData, so a <Browser> would wrap only browse mode (hollow). See the phase-2 as-built callout.
right-panel/markdownImageSrc.ts phase 1Delegates resolution to the package; keeps only the file-route URL build (real composition, not a pass-through).
right-panel/iframePreviewNav.ts deleted (phase 1)The agnostic inversion moved to the package; the kolu codec is bound inline at BrowseIframeRenderer’s call site — no thin wrapper (.agency/code-police.mdno-thin-wrapper-functions).
settings/tips.ts +navA discoverability tip for back/forward. (No input/actions.ts entry: the keybind is a Code-tab-scoped listener, not a global action — a global mod+[ would shadow the terminal’s ESC byte on Linux.)

Phasing — extract first, then light the feature

Extraction comes before the feature, deliberately. Phase 1 is a pure, behavior-preserving lift — the solid-fileview pattern (extract with kolu working throughout, #1082 ; let features ride the extraction after). The subtle point: what gets extracted in phase 1 is today’s navigation, which is already proven code — the safest possible thing to move. The new behavior (history) is then added to the clean package, never bolted onto the client that’s about to be gutted. There is no forced dependency either way; this ordering is chosen for risk isolation and single-purpose diffs (a pure-move PR, then a pure-addition PR).

PhaseShipsRisk
1 · Extract the primitives shipped #1191 @kolu/solid-browser = relativePath + previewPath over the @kolu/url-shape leaf. kolu re-consumes: markdownImageSrc delegates resolution, BrowseIframeRenderer binds the previewPathCodec, BrowseFileDispatcher imports resolveLinkHref. Behavior-preserving — the two nav paths stay distinct.Low — pure-move + delegation, unit- + e2e-covered.
2 · The browser proper + back/forward shipped #1191 createBrowser<L>() — the generic reactive history controller (navigate/back/forward/current/canBack/canForward; forward-truncation; idempotent on same entry). kolu wires it into useRightPanel/CodeTab: every selection is recorded, ◀ ▶ buttons + scoped Alt+←/→ re-apply, cheap-v1 re-highlight rides the front door. As-built deltas (callout below): history is additive over selectedFileByMode (still the render truth), mode-chips don’t record, <Browser>/gitResolver/unified navigate deferred.Medium — live-surface refactor; the audit + the #818 regression suite guard the selection invariants.
3 · Prove ③ in-repo demo landedexample/docsite — a standalone doc browser over its own { slug } location — reuses createBrowser unchanged (built + tested in CI). In-repo proof of reuse, exactly the bar @kolu/surface’s examples clear; a different application injecting its own resolve (drishti, an artifact viewer) is still the closing argument.Low — pure-logic reuse.

Bottom line: don’t extract a history stack — that’s a transformer. Extract the browser the Code tab already is. The renderers are packaged (@kolu/solid-fileview, which it composes); what’s left smeared across the client is the location + history + link-nav shell. Pull it into @kolu/solid-browser, inject git as a resolver, and back/forward arrives for free. It clears electricity’s hard-volatility bar where “history” didn’t. Phase 2 shipped the history controller + the back/forward feature, and example/docsite is a real in-repo second consumer of it — a different application injecting its own resolve is the closing argument.