# Changelog

## 0.12.1 — 2026-09-11

- **The map no longer paints over the settings dialog (#3).** The map lives
  in the host's frame-wide overlay layer (`z-index: 20`), while the settings
  dialog renders in place inside the sidebar. Once anything opens a stacking
  context on that column (a theme, another plugin), the dialog's
  `z-index: 1000` is trapped below the layer, and with the right-hand panel
  open, which puts the rail inside the dialog, the rail, the dots and the
  clue wall were drawn on top of it. No z-index can get a layer entry under
  such a dialog, so the map and the wall now step out (`visibility: hidden`,
  layout untouched) while any `aria-modal` dialog is up, and come back when
  it closes. Reproduced and verified on an isolated DSH 0.1.5-rc.1.

## 0.12.0 — 2026-09-08

Adapted to DSH 0.1.2-rc.1 (the current `latest`), verified on a live 0.1.2
instance and an isolated 0.1.1-rc.2 — both run the same replay suite green.

- **Labels through the host's locale service.** The built-in zh/en table is
  registered as the `dsh-strata` namespace and every label is translated
  through `ctx.locale.bind`, so a language switch in Settings → General
  relabels the rail, the dots, the switcher and the wall in place — no
  reload, no remount — and a language pack can translate the plugin into a
  third-party language (new in 0.1.2). A host without the service (or one
  that has not provided it yet) gets the built-in table keyed on the
  document language, as before.
- **The load chain survives 0.1.2's pager lifecycle.** The host unmounts the
  "load older" control while a page is in flight and re-creates it after the
  rows land, in a later commit; a measure taken between the two saw rows and
  no pager, and a 全 or unloaded-card jump ended "incomplete" part-way with
  the pager back on screen. A vanished pager now gets 600ms to return before
  the chain calls the history exhausted. Progress is measured by anchored
  rows landed (with the topmost key as tie-breaker): the key alone missed a
  page that filled in a turn's older steps BELOW that turn's folding control.
- **The dot column owns its pointer.** 0.1.2's content-width handle (40px,
  col-resize, hover bar) sits under the dot column at laptop widths; with
  the strip transparent to the pointer, a gap between two dots showed the
  resize cursor and a drag there resized the column.
- Manifest: `dsh.client.inject` names `@deepseek-ai/dsh-client-ui-chat`, the
  package the rows come from since 0.1.2 (a soft ordering hint — a host
  without the package skips it).
- README: a compatibility table and what each 0.1.2 change means for the map.

## 0.11.0 — 2026-09-08

Correctness and performance pass over the whole map, measured on a live
1,879-row session (0.1.1-rc.2) and a live 0.1.2-rc.1 with turn folding on.

Fixed:

- **Every tool call was painted twice.** The tool renderer nests an anchor of
  its own (`call:<id>`) inside the flow row, and the map took both for rows —
  a phantom `unknown` band over every tool tick, and a duplicate error dot on
  every failed one (633 of 1,877 bands on the long session were phantoms).
  Only the outermost anchored element is a row now; a nested anchor and any
  error mark inside it belong to that row.
- **Turn folding (DSH 0.1.2, Compact mode).** A folded turn hides its rows in
  place (`hidden`, no mutation the map watched), so a collapse left the folded
  rows as zero-height bands still painted at their floor height, with their
  error dots, until something else forced a measure. A row hiding or showing
  itself is structure now, hidden rows stay under size observation so a turn
  opening reports itself, and a band that collapses to nothing triggers the
  measure instead of a phantom.
- **A page that folds the transcript no longer reads as a stall.** The load
  chain measured progress by scroll height; the page that completes the
  history switches folding on and the transcript ends up SHORTER, so an
  unloaded-card jump was reported amber "incomplete" and never landed.
  Progress is the topmost row's identity now — new exactly when a page
  prepended, whatever it did to the height.
- **The host's turn rail is not the pager.** With everything loaded, the
  positional fallback for the "load older" control picked the first button
  above the first row — on 0.1.2 that is a mark of the native turn navigator
  (a `<nav>` in a sticky slot at the top): the ⌃ stayed on and an auto-page
  near the top would have clicked a turn mark. Navigation chrome is excluded,
  and position alone is trusted only when it is unambiguous.
- **近/中 painted sixty frames a second, idle, after any history load.** The
  rescale morph a landed page starts is only displayed in 全, and the zoomed
  early return sat before its expiry — so it stayed armed and kept the frame
  loop scheduling itself until the next 全. It expires on the clock in every
  mode now (1 paint per 3s idle, as designed).
- **A chain that ends at once no longer leaves a phantom "active" record**
  (goal already met, or pager already gone before the first click): it
  blocked auto-paging and turned every later growth into a rescale morph.
- A zoom chain whose scrollport went away mid-flight could throw out of its
  goal and leave the rail pulsing "loading" forever; it ends amber instead,
  and losing the scrollport cancels the chain and forgets the pager.
- A retired export request now cancels its inflate reader instead of
  finishing the whole log for nobody.
- Attribute churn outside any row (disclosure state on the pager, the turn
  controls) no longer forces a full measure; a row re-kinding itself does.
- A stood-down map leaves the accessibility tree (no tab stop on an invisible
  scrollbar, no dots from the previous session read out).
- README: the rail preview card removed in 0.7.3 was still documented.

Performance:

- **Streaming while scrolling no longer measures the whole transcript per
  growth.** The row observer runs after layout, so a frame scheduled by a
  scroll or a store emit saw a row's growth one step before its observer did
  and fell back to a full measure — 27 of 27 growth bursts on the long
  session. The map now gives the observer that frame first (0 of 27).
- **Hover repaints one band, not the session.** The strata are cached as an
  offscreen base layer and composited; the hovered band is painted over it.
- The 3s safety tick no longer repaints the canvas: the rail box and DPR are
  in the bitmap key already (was a full repaint of every band every 3s).
- One error scan per measure instead of a subtree query per row; the pager
  search stops at the first row; a canvas is resized only when its size
  changed; dots, lens, shades and wall strings skip identical writes.

Development: the mounted map exposes a read-only seam (`root.__dshStrata`)
for the headless replay; the replay's top-of-wall assertion reads the
smallest on-screen index (cards are created on demand, so DOM order is not
index order) and accepts a session that is already fully loaded.

## 0.10.0 — 2026-09-08

Added:

- **The host's own turn rail steps aside for the map.** DSH 0.1.2-rc.1 ships a
  fixed-pitch turn navigator down the right edge of the transcript — the strip
  this map already took over, so the two overlapped and the host's rail covered
  the anchor dots (its frame spans the dot column exactly). It is now suppressed
  for exactly as long as the map is up, on the same contract the scrollbar
  takeover keeps: stand down (Trajectory, no session, a transcript that does not
  scroll) and it is handed straight back; uninstall and it never moved.
  Suppression is a stylesheet rule, not a written-on element, so it survives the
  rail's React remounts, and the selector is read from the component's own
  stylesheet by the `--turn-rail-band` property it declares — a rebuild that
  reshuffles the CSS-module hash still resolves.
  Set `localStorage['dsh-strata.native-turn-rail'] = 'keep'` to keep both.
  (reported in [#2](https://github.com/jsdvjx/dsh-strata/issues/2))

Compatibility:

- Verified against DSH 0.1.2-rc.1, where chat rendering moved to
  `@deepseek-ai/dsh-client-ui-chat`: the anchors the map reads
  (`data-conversation-scroll`, `data-chat-anchor-key`, `data-chat-flow-kind`,
  `data-composer-seat`) all still publish, and the map, the dots, the clue wall
  and the load chain behave as they do on 0.1.1-rc.2.
- DSH releases without that rail (0.1.1-rc.2 and earlier) resolve nothing and
  are untouched; the probe is throttled so looking for a rail that is not there
  costs one CSSOM walk every 2s, not one per frame.

## 0.9.0 — 2026-08-26

Correctness and performance pass driven by the 2026-08-26 audit
(`AUDIT_REPORT.md`), plus the first automated tests.

Fixed:

- 近/中: the canvas cache key now includes the zoom mode, the window offset,
  the scale and the device pixel ratio, and one display mapping is computed
  per frame and shared by the canvas, the dots, the lens and the hit test —
  scrolling in a zoomed scale no longer leaves the strata behind the anchors.
- 近/中: dragging the rail maps against the window frozen at pointerdown, so
  a pixel of pointer travel is always the same scroll distance; a 3px slop
  separates a band click from a drag.
- Clue wall: the full-log cache is validated by the LAST loaded user row's
  anchor key, not by a count. Loading older history keeps it; a message you
  send is grafted from the transcript instead of re-downloading the export;
  anything else refetches. Only `surfaceOp: "append"` user messages count,
  matching the host's projection.
- Clue wall: export requests carry a generation and an AbortController; a
  session switch retires them, and a late response from the previous session
  can neither overwrite the cache nor render into the open wall.
- History loading has one owner (`createHistoryLoader`): auto-paging, 中/全
  and unloaded-card jumps run through it. A page with no progress stops the
  chain at once, the 60-page cap and a vanished pager are reported (amber
  bar and ⌃) instead of shown as 100%, a mode switch cancels the old chain,
  and 全 clicked during an auto page supersedes it instead of doing nothing.
- Clue wall pagers keep their rows while hidden (visibility, not display),
  so the card area measured before packing is the area the cards get.
- The native scrollbar's thumb variables are restored to their previous
  inline values and priorities, per element, not merely removed.
- Rail wheel honours `deltaMode` (line and page deltas scale by the
  scroller's line height / viewport).
- Keyboard and AT: cards are `role=button`, focusable, Enter/Space jump;
  Enter on the rail opens the wall on the prompt you are at and focuses its
  card; Esc returns focus to the rail; zoom buttons carry labels and
  `aria-pressed`.

Performance:

- Streaming output no longer re-measures the transcript: rows are watched by
  a ResizeObserver whose border-box sizes update ONE band and shift the rest
  arithmetically; the MutationObserver drops `characterData` and only treats
  rows appearing/leaving/failing as structural.
- Canvas repaints and hit tests visit only the bands inside the display
  window (binary search on the sorted bands); anchor sync starts from the
  window too.
- The clue wall is virtualised: only the visible window plus a small buffer
  exists as DOM, heights are cached per column width and measured in
  batches, and connector paths persist across frames with card rects re-read
  only while a layout settles.
- The export is parsed as a stream of lines while it inflates; failed
  exports back off (2s doubling to 30s) instead of retrying on every hover.
- The idle safety-net tick runs every 3s (idle while the tab is hidden)
  instead of 800ms; session switches come from the session store, composer
  growth from its own ResizeObserver.

Tests: `npm test` runs `node --test` over the DOM-free internals (window
mapping, cache key, scrub mapping, visible range, wheel units, thumb
restore, prompt parsing, prompt store, history loader). `test/replay/`
holds the headless-Chrome replay used against a live `dsh web`.

## 0.8.2 — 2026-08-19

- 中 responds from a fresh session. Its extent was derived from the loaded
  content, which equals the initial extent before any history loads — so the
  button did nothing exactly where users first try it. 中 now means TWICE
  the initial view, chain-loading the difference on demand with the rail
  progress bar (capped by the session's actual length).

## 0.8.1 — 2026-08-19

Scale switcher polish.

- 近 is the default scale.
- 中 is now halfway between the initial extent and the full session — the
  old formula collapsed onto 近 whenever the initial window exceeded half,
  which made the button feel dead.
- Switching scales glides over 450ms instead of snapping, and the load
  progress bar moves on a slow 0.9s ease with asymptotic advancement, so
  chunked loads read as one continuous pull.
- The switcher stacks vertically under the rail.

## 0.8.0 — 2026-08-19

Map scale states, switched from a widget under the rail.

- 近 / 中 / 全 segmented pill below the rail (shown while it is awake):
  initial restores the session-open extent, medium spans half, full maps
  everything — chain-loading any missing history first with the rail
  progress bar. The choice persists per browser.
- Zoomed scales are pinned and slide with the reading position: prepends
  shift the window, never the scale, so no rescale morph applies there.
  Bands and anchor dots outside the window are culled.

## 0.7.7 — 2026-08-19

- Paging slides the window instead of shrinking it. Paging up used to keep
  only the newly revealed earlier cards and hide everything else; the
  leftover room is now backfilled from the other side, so the window moves
  like a viewport — whatever no longer fits lands in the opposite pager.

## 0.7.6 — 2026-08-19

- Clicking a pager no longer closes the wall. The pagers (and the title
  pill) sit inside the wall, so their clicks bubbled into the
  click-in-a-gap-closes handler — paging and closing at once. Controls are
  now excluded from the gap test.

## 0.7.5 — 2026-08-19

The unloaded-jump sequence is re-staged: close, load, glide.

- Clicking an unloaded card retires the wall IMMEDIATELY; the loading then
  plays on the rail — the ⌃ pulses in the accent blue and a thin bar above
  the rail fills with real progress (chunks landed over chunks needed)
  while the map morphs — and only then the glide-and-flash jump runs.
- The in-wall loading choreography (card shimmer, dimmed collage, marching
  string) is removed with the stage it played on; the wall cannot reopen
  from hover while the load is in flight.

## 0.7.4 — 2026-08-19

Jumping to an unloaded prompt no longer feels stuck — the wait is staged.

- The clicked card pulses in the accent blue with a shimmer sweep, over a
  REAL progress bar: chunks of history landed over chunks needed, advancing
  as each load lands.
- Its string turns into marching dashes flowing toward the rail — the
  history is visibly being pulled in — while every other card dims to
  background.
- The bar completes and rests a beat before the wall retires and the jump
  carries on; the grace-close is suspended while loading so the wall cannot
  vanish mid-wait. All of it stands down under prefers-reduced-motion.

## 0.7.3 — 2026-08-19

- The lightweight preview card is gone — the wall is the single hover
  surface (non-user anchors now highlight only).
- The hovered anchor's card always packs into the slot NEAREST its dot:
  rightmost column, vertically centred on the anchor, with the rest of the
  collage flowing around the reservation. Position changes glide as swap
  animations on a spring.
- Discipline split by gesture: anchor-side hovers relocate the spotlight;
  wall-side hovers only move the highlight and the solid string — the card
  under the pointer never slides away.

## 0.7.2 — 2026-08-19

- The board is gone: cards float straight over the transcript, which stays
  readable through the gaps; the title is a compact pill and a click in a
  gap closes the wall.
- Hovering a user BAND on the rail now opens the wall too, not just the dot.
- String rule inverted to match the spotlight: the hovered message hangs by
  the ONLY solid string; every other card's string is dashed and faint.
- Fixed the masonry containing block: cards anchored to the outer wall
  instead of the card area, overlaying the header.

## 0.7.1 — 2026-08-19

The wall packs like an evidence board.

- Cards take exactly the size their content needs (up to 10 lines) instead
  of a uniform clamp, and flow into masonry columns — the whole session fits
  on one board whenever it can, and the window is computed by real measured
  fit, not a fixed page size. Pagers appear only on genuine overflow and
  page by whatever actually fits.
- Connector strings ride above the collage at low opacity, each from its
  card's right edge to its anchor dot, the spotlight line emphasized.

## 0.7.0 — 2026-08-19

The clue wall becomes a floating hover panel.

- Hovering a user dot floats the wall over the right half of the screen —
  one chronological column of every prompt (export-fed, unloaded included),
  each card tied to its anchor dot by a live-tracking bezier (dashed for
  unloaded, emphasized on the spotlight). The transcript stays visible.
- When the column cannot fit, ↑/↓ pager buttons at the top and bottom page
  through the rest — no internal scrolling.
- The wall lives while the pointer is on it or the rail (400ms grace on the
  crossings, the same discipline as the rail itself); Esc closes instantly.
  Clicking a dot jumps directly; clicking a card jumps (chain-loading
  unloaded history first) and closes.

## 0.6.0 — 2026-08-19

The prompt browser is redesigned as the clue wall.

- The hover deck tried to fit "all visible, all selectable, all stable" into
  a narrow strip beside the rail — geometrically incompatible goals that
  produced one interaction flaw after another. The wall dissolves the
  constraint: clicking a user dot opens a full-screen pinboard grid of every
  prompt in the session (export-fed, unloaded ones included, dashed and
  tagged), spotlighted on the message you came from.
- Cards are large, static and scannable; clicking one jumps (chain-loading
  first when unloaded) and closes the wall; Esc, the backdrop or ✕ close it
  explicitly — a full-screen surface opened and closed by clicks has no
  hover traps by construction. Long sessions scroll naturally.
- Hovering a dot is back to the lightweight single-message preview; every
  rail behavior (band click-jump, scrub, wheel, keyboard) is unchanged.
- The fisheye/dock layout engine, spacing regimes and connector fans are
  all retired.

## 0.5.4 — 2026-08-19

- The default layout is airy again. 0.5.3 made the guarantee-bubble spacing
  unconditional, huddling the whole deck around the focus. The two regimes
  now layer correctly: strips spread EVENLY across each side while the even
  share stays above the 16px selectable floor, and only below it does the
  side switch to the bubble (16px nearest, geometric compression beyond,
  rail-edge overflow). The regimes meet seamlessly at 16px.

## 0.5.3 — 2026-08-19

Climbing to the ends of the deck can no longer strand the pointer.

- Even distribution divided the shrinking headroom among the remaining
  strips, so approaching the top squeezed them into an unselectable pile.
  Spacing is now itself a gradient with a GUARANTEED zone: the three strips
  nearest the focus always expose 16px — the bubble travels with each
  climb, so the next card is always hittable — while farther strips
  compress geometrically (9 → 5 → 4px floor) and re-expand as the focus
  approaches them.
- The stack may overflow the rail by 44px at either end (it is an overlay),
  so the ceiling is no longer a hard wall; whatever still does not fit
  folds into the count chips, and the wheel remains the precise fallback.

## 0.5.2 — 2026-08-19

- The focus card hugs its anchor again: hovering a dot centres the card on
  that dot's y (clamped at the rail edges; verified Δ ≤ 1px mid-rail). An
  in-deck refocus instead keeps the hovered card's own centre, so it stays
  under the pointer, and the other cards spread EVENLY over the space above
  and below — a fresh distribution each time, nothing accumulates.
- Longer gradient range: width recedes 4%/step down to a 60% floor and
  height 1px/step down to the one-line floor, so the falloff spans ten
  cards instead of saturating after three.

## 0.5.1 — 2026-08-19

Deck gradient tuning.

- Card height never dips below one full line of content (22px floor; the
  12px minimum crushed the strips).
- WIDTH now carries the depth gradient: the focus spans the full deck, and
  neighbours recede 94% → 66% with distance, right edges staying with the
  rail — a much steeper, clearer falloff than height alone allowed.
- The ordinal is its own element: bold, accent-blue, instantly scannable.
- The spring transition covers width as well.

## 0.5.0 — 2026-08-19

The deck becomes a macOS-dock fisheye.

- The card stack now spans the full rail height on a fixed index grid — the
  deck area matches the scrollbar it annotates.
- The hovered card magnifies in place around its own slot; neighbours shrink
  and part with distance (size and shove both taper), far cards rest small at
  their grid positions. Movement settles on a springy overshoot curve
  (disabled under prefers-reduced-motion).
- The stable grid replaces the whole pinned/frozen-frame machinery: nothing
  can drift, cascade, or leave holes by construction, because the focus
  always expands centred on the slot the pointer is already inside.

## 0.4.7 — 2026-08-19

- Hovering a user dot always opens the deck again after history loads in.
  A fully loaded rail packs the anchors densely, and a neighbouring error
  dot's invisible hit halo — a later DOM sibling — could sit on top of a
  user dot's centre: the hover then read as the error dot and the deck
  closed instead of opening. Hit priority is now semantic (user dots above
  error dots above compaction marks) and the halos are tighter. Verified by
  sweeping every user dot on the fully loaded rail.

## 0.4.6 — 2026-08-19

- No more hole in the middle of a pinned stack. The pinned frame used to be a
  raw snapshot of the anchored layout, which reserved the entry focus's full
  expanded height — refocusing away collapsed the card and left a ~70px gap
  in its place, forever. Entering the deck now builds a COMPACT frame (every
  card a uniform strip slot; the expanded card is only an overlay), so a
  collapsing focus lands exactly in its slot.
- Fresh prompt lists merge into the OPEN deck dynamically: when the export
  fetch lands (or a reopen finds new messages), the focused card keeps its
  position to the pixel — matched by seq, else by tail distance — and a
  pinned session gets a new compact frame anchored at the old focus top,
  with the new cards joining above.

## 0.4.5 — 2026-08-19

Pinned-mode layout rebuilt around a frozen frame — climbing the deck no
longer pushes it away from under the pointer.

- Expand-in-place accounting was structurally biased: the focus card is ~70px
  taller than a strip, and pinning its top pushed that surplus downward on
  every upward refocus, drifting the stack until cards fell off both ends.
  Entering the deck now freezes EVERY strip position for the whole pinned
  session; the focus expands as an overlay on its own strip.
- The overlay opens toward where the pointer came from, covering only visited
  strips — and the direction is never flipped: when the room on that side
  runs out, the card shrinks to fit instead of burying the strips ahead
  (the old bounds-flip could hide the entire upper half behind one tall card).
- Verified by a strip-by-strip climb across the whole deck: pointer lands on
  every aimed card, zero drifted strips, all cards reachable.

## 0.4.4 — 2026-08-19

- The deck no longer vanishes while the pointer travels to it. Crossing the
  10px gap between the rail and the cards starts the grace-collapse timer,
  but the cancel listened on the rail alone — landing on a card lost the
  race and the deck closed under the pointer. Re-entering ANY part of the
  surface (rail, dots, deck) now cancels the pending collapse, and the grace
  window is 300ms.

## 0.4.3 — 2026-08-19

Deck stability and performance.

- Two layout modes, keyed to where the pointer is. Anchored (pointer on the
  rail): the stack follows the hovered anchor as before. Pinned (pointer on
  the deck): the base freezes — refocusing keeps the hovered card's top
  exactly where the pointer found it and expands it in place, so a card can
  never slide out from under the cursor and trigger the hover cascade that
  made the stack jump around.
- The deck DOM is now persistent: cards, connector paths and chips are built
  once per prompt list and reused — a refocus flips data-focus, moves tops,
  and rewrites path data in place instead of rebuilding everything, and top
  shifts glide on a 140ms transition instead of snapping.

## 0.4.2 — 2026-08-19

- Every deck card now draws its own bezier back to its anchor dot, fanning
  out across the gap: the focus line strong, the rest faint, and unloaded
  prompts dashed and converging on the top-right corner where their history
  extends past the map.

## 0.4.1 — 2026-08-19

Deck layout redesigned around the anchor:

- The focused card now sits beside its anchor dot (clamped to the rail), and
  shows the message in full when it fits (up to 14 lines).
- The other cards shingle away from the focus like a library card file —
  absolutely stacked, each showing only its top strip with index, time and an
  inline snippet; capacity overflow folds into ↑/↓ count chips.
- A bezier connector ties the focused card to its anchor dot; an unloaded
  focus points at the top of the rail where its history would extend.

## 0.4.0 — 2026-08-19

The prompt deck: hover a user band or its anchor dot and every user message
in the session stacks up as cards — including the ones above the loaded
window, read from the session's own export log (`/api/session.export`,
unzipped in the browser; the loaded window is a contiguous log suffix, so
the log tail aligns 1:1 with the loaded bands). The hovered card expands,
the rest compress to one-line slivers with timestamps; unloaded prompts are
marked with a hollow dot. Wheel steps focus, click jumps — an unloaded
prompt chain-loads the missing history first, then lands on the message.
Falls back to loaded-only cards when the export endpoint is unavailable.

## 0.3.8 — 2026-08-19

- Clicks made right after a session opens no longer die. The chat view pins
  itself to the floor and re-pins on every column resize while ownership is
  pinned; a smooth glide eases out of the floor so slowly that its first
  frames stay inside the 24px pinned zone — and a freshly opened session
  resizes constantly (markdown, images, highlighting settling), so the glide
  was routinely yanked back and killed. Upward jumps and keyboard moves now
  hop out of the pinned zone instantly first (which the view's scroll ledger
  attributes to the reader, releasing the pin), then glide. Verified against
  a forced 60ms resize storm: the glide survives start to finish.

## 0.3.7 — 2026-08-19

- Hover feedback is now purely photometric: the band's geometry never
  changes. Full alpha, the same-color glow, and a brightness lift painted
  onto the identical path — verified by pixel scan (span unchanged,
  brightness up) — replace the earlier widen, whose size flick read as
  jitter at band scale.

## 0.3.6 — 2026-08-19

- The hover stroke is gone: even inset and clamped it read as visual noise on
  the small bands. Hover keeps the widen plus the same-color glow.

## 0.3.5 — 2026-08-19

- The hover stroke is now a true inner stroke: the path is inset by half the
  line width and clamped to the canvas, and thin bands get a proportionally
  thinner line. A canvas stroke rides the path, so half of the previous one
  landed outside the band — swallowing thin bands and bleeding toward
  neighbours and past the canvas edge.

## 0.3.4 — 2026-08-19

- Hovering a band now highlights it visibly regardless of its resting
  opacity. The old treatment only raised alpha to 1 — a no-op on the user
  blue and error red, which already sit at 1. A hovered band now widens,
  glows in its own color, and carries a bright inner stroke; full-width
  bands (where the widening clips at the canvas edge) still read through
  the stroke and glow.

## 0.3.3 — 2026-08-19

- The lens gains an accent-blue edge glow — an outer halo spilling past the
  rail plus a faint inner wash — so the reading position registers even on
  the 12px resting rail.

## 0.3.2 — 2026-08-19

- A click-jump near the top no longer stutter-hops through the load chain:
  auto-load defers until the glide lands (hold scales with distance), and the
  clicked row is pinned at the reading line across every prepend — corrected
  only on real drift (>48px), released by any manual scroll (wheel, drag,
  scrub, keyboard).
- The viewport now stands out on the rail: everything outside the lens dims
  toward the page background and the lens carries an accent-blue edge — the
  classic minimap focus treatment.

## 0.3.1 — 2026-08-19

Auto-load tuning and a rescale animation.

- Loading a chunk of history now morphs the map instead of snapping it: the
  existing strata glide to their compressed positions while the new history
  slides in from the top, lens and anchor dots riding the same eased mapping
  (420ms, starts on the exact frame the growth lands, skipped under
  prefers-reduced-motion).
- Thresholds are live ratios of the current scroll range, re-read on every
  check so a grown scale never dilutes them: loading triggers inside the top
  10%, then chains until 30% of headroom stands above the reading position —
  one chunk barely moves the needle on a long session, and without the
  headroom a reader tops out again two wheel-ticks later.

## 0.3.0 — 2026-08-19

Discovering older history no longer requires aiming at a tiny button.

- Scrolling (or dragging the lens, or jumping, or pressing Home) to the top
  of the transcript now auto-loads older history; the map rescales as it
  arrives, and holding the lens at the very top chain-loads. One load in
  flight at a time, and DSH's own anchored prepend keeps the reading position.
- The `⌃` above the rail is now a pure indicator: visible whenever unloaded
  history remains (no hover needed), gone when everything is loaded, and no
  longer clickable — there is nothing left to click for.

## 0.2.2 — 2026-08-19

The ⌃ load-older cap was practically unclickable: it sat 13px above the rail
with a 1px dead gap in between, so the slow, aimed pointer travel it demands
sampled the gap, collapsed the rail, and hid the cap mid-approach.

- Collapse now runs on a 260ms grace timer instead of the boundary event;
  re-entering (the cap and the anchor dots included) cancels it.
- The cap's bottom edge is flush with the rail and it carries a generous
  invisible hit halo; slightly larger glyph.
- A press on the cap no longer falls through to the rail's scrub handler,
  which used to yank the view to the top before the load even started.

## 0.2.1 — 2026-08-19

Fixes two ways an anchor-dot click could die, both worst at the topmost dot:

- Anchor dots are now updated in place instead of being rebuilt wholesale.
  A streaming turn rescales the map on every delta; replacing the buttons
  between a press and its release made the browser retarget the click at the
  container, silently dropping it. The top dot invited exactly that — its
  position barely moves while everything below it reflows. A position-based
  fallback also recovers any click whose target dot vanished mid-press.
- The rail now collapses on leaving the whole root, not the rail: collapsing
  shifts the dot column 14px, so the old behavior slid the aimed-at dot out
  from under the pointer while crossing from the rail toward it.

## 0.2.0 — 2026-08-19

- The rail now sits in the transcript's own scrollbar gutter and suppresses the
  native thumb through the theme's `--dsh-scrollbar-thumb` seam while visible;
  the scrollbar is handed back the moment the map stands down.
- Anchor dot column beside the rail: blue for every user message, red for every
  failed tool call or command, click to jump, reading position stays enlarged.
- Hovering a dot lights the band it maps to; wheel over the dots scrolls.
- Scrollbar semantics (`role=scrollbar`, live `aria-valuenow`) and keyboard
  control on the rail: arrows nudge, PageUp/Down page, Home/End jump.
- Failed rows are detected through `data-state="error"` (the convention the
  tool and command renderers actually use); `data-error` kept as fallback.
- Load-older cap matches the pager by its label before falling back to position.

## 0.1.0 — 2026-08-18

- First release: to-scale minimap of the loaded conversation in the Web GUI,
  bands colored by Chat Node kind, user messages emphasised, hover preview
  card, click-to-jump with landing flash, proportional drag scrub, wheel
  scroll, double-click pin, load-older cap, theme-token palette, and
  stand-down on non-chat views.
