Skip to content

fix(viewer): one form for every span, pills lead, bars run through threads, and the gutter folds - #34

Merged
folded merged 5 commits into
mainfrom
fix/span-gutter-one-form
Sep 4, 2026
Merged

fix(viewer): one form for every span, pills lead, bars run through threads, and the gutter folds#34
folded merged 5 commits into
mainfrom
fix/span-gutter-one-form

Conversation

@folded

@folded folded commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Four things the reviewer found using the centre gutter on a real run.

1. Smell pills lead the rationale

They rendered at the end of the block. On gcp.py +779..+783 the rationale ran
to row 786 and put its backward-incompatible pill beside an unrelated span's
dot at 787 — read as one thing. Pills now sit at the top of the block, beside
the bar's first row. Smells describe the span; they belong where it begins.

2. Bars run through comment threads

An annotation row inside a span's range had no bar cell, so a comment thread
left a hole (google_group_settings.py +19..+72, thread at 20 — the brief cited
the wrong file). The span pass now gives every row in a span's range a bar
segment at the span's depth, annotation rows included. annotations.ts is
untouched; the cell is a plain extra child its queries never see.

3. One form for every span

Multi-line spans moved to the gutter in 5b; single-line spans didn't follow.
They still used the legacy line_notes form — a full-width note box in the
code column with an arrow from the dot. Real case: config.py +81..+81, a
686-character extra-review note nested inside +76..+81, with the parent in
the gutter and the child pushing every line below it down.

A single-line span is now its dot plus its rationale in the gutter's text
column, joining the waterfall like everything else — here the parent runs from
76, the child collides at 81 and stretches row 80, both readable in the strip.
_attachSpanNote, _buildSpanNoteContent, the .line-note-* / .annot-note
CSS and the arrow are deleted. Every extra-review note is single-line, so all
of them move into the gutter.

Reviewer comments are not spans and are unchanged — threads, editor, replies,
resolved header, all tests pass untouched. The line_note:N legacy check for a
promoted span stays and now hides the dot too, since the comment stands in its
place. One selector in comments.ts follows the DOM change.

4. The gutter folds — g

Collapsed is lineno · bars: text column 0 wide, rationales hidden, bars and
dots still showing where every span is and how they nest. Global, remembered in
localStorage (scr-gutter-fold), default expanded. g toggles; so does clicking
a gutter cell's empty area, with a title hint on every cell. No chevron — the
strip is per-file but built of per-hunk grids, so it has no single top.

Hover on any mark shows the full rationale (native title; Chrome's cap is
~1024 chars, the 686-char case renders whole). Clicking a mark unfolds and
scrolls to the block. Marks are widened to their full 6px column as the hit
target.

Collapsed, no row stretches. The waterfall only stretches to place text, so
the compact view is the undistorted diff — the answer to "blank space beside a
long note" for anyone reading code first. The pass releases its stretches on
collapse and re-applies on expand; takeRecords() still guards it. The .diff
split follows, so both code columns widen equally; the panel follows the same
state; Annotations.reflowAll() is called because a toggle moves the columns
the arrows point into.

Gate

410 vitest (405 → 410), 1051 Python unchanged, pyright 0, ruff clean.
Mutations: pills at the bottom (fails the DOM-order assertion) · annotation rows
skipped by the bar pass · a single-line span rendered code-side (9 fail) ·
collapse skipping the stretch release · toggle not re-running the pass (2).

Decide

"Add as comment" on a span's intent is gone. It only ever existed on the
code-side note form; multi-line spans never had it. Smell pills stay one-click
promotable. If intent promotion is wanted it belongs as a small affordance in
every block's pill row — a follow-up.

The status bar advertised space toggle; no such handler existed. Now reads
keys 1-3 fold · g gutter · ? help.

The pills sat at the end of the rationale, so a long intent put them
rows below the bar they describe — in gcp.py's +779..+783 the
backward-incompatible pill landed at row 786, beside the +787 span's
dot, and read as that span's. Smells describe the span; they go on the
block's first line, beside the bar's start, with the intent below.
A comment thread or fold label row inside a span's rows is inserted
after the bar is drawn on the code rows, and is built without gutter
cells, so the bar broke around it (google_group_settings.py's +19..+72
with the two-comment thread at 20). The placement pass already walks
every row of the half; it now gives each non-code row lying inside a
bar a bars cell with a segment per enclosing span at that span's depth,
and the strip's background pseudo-item covers an old-side thread's
placeholder too. annotations.ts's own layout is untouched.
…r span

A span of one line kept the legacy line-note form — a dot on its row and
a full-width note box in the code column, with an arrow from the dot —
while multi-line spans moved to the centre gutter. config.py's +81 (686
characters, nested in +76..+81) rendered as a code-side box pushing
every line below it down, beside a parent that lived in the gutter.

Every span now takes one form: its mark in the bars column (a bar, or a
dot for one line) and its text block in the text column at its first
row, joining the placement pass — the +81 text collides with its
parent's and stretches row 80, and both read in the strip. Its smell
pills lead the block. The code-side note, its arrow, and the note's
"Add as comment" button are retired with it; a comment already derived
from a span (by its id, or the line_note id a store written before
spans used) still hides it, and comments.ts's post-promotion sweep
addresses the gutter block and marks instead of the note row.

Reviewer comment threads are unchanged: they keep their code-side boxes
and arrows, and annotations.ts is untouched.

Every extra-review note is a single-line span, so this moves all of
them into the gutter.
A global reading preference, like the fold level but not a rung of it:
folded, the gutter's text column is zero wide, every span's block is
hidden and no row is stretched — the compact view is the undistorted
diff — while the bars and dots still show where each span is and how
they nest. The .diff split follows the live text width (a derived
--gutter-text-w, zeroed by html.gutter-collapsed), so both code columns
widen equally, in the diff pane and the explainer's panel alike.

Toggled by g (free beside the 1-3 ladder keys) or by clicking the
strip's empty area; a mark's tooltip is its span's rationale (smells,
then intent — the native title holds the 686-character case whole), and
clicking a mark unfolds the gutter and brings that span's text into
view. Marks widen to their 6px column as the hover and click target,
drawing the 2px stroke as a pseudo-element. Default expanded, remembered
in localStorage (scr-gutter-fold). The placement pass re-runs on every
half at each toggle, releasing its stretches on collapse and re-applying
them on expand; comment arrows are reflowed since the code columns
moved. Rendered mode has no gutter and is unaffected.

The status bar's stale 'space toggle' (no such key) becomes 'g gutter'.
CONTEXT.md: Annotation span describes the one form, the pill position,
the bar through annotation rows and the fold; Fold level notes the fold
is a preference, not a rung.
@folded
folded merged commit b762e9a into main Sep 4, 2026
5 checks passed
@folded
folded deleted the fix/span-gutter-one-form branch September 4, 2026 04:31
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