Skip to content

Cditor-inspired: WYSIWYG polish, tables, windowed rendering, block reorder#52

Merged
packetThrower merged 51 commits into
mainfrom
feat/cditor-inspired
Jul 18, 2026
Merged

Cditor-inspired: WYSIWYG polish, tables, windowed rendering, block reorder#52
packetThrower merged 51 commits into
mainfrom
feat/cditor-inspired

Conversation

@packetThrower

@packetThrower packetThrower commented Jul 18, 2026

Copy link
Copy Markdown
Owner

The full port of what we liked from JYChen-8866's Cditor (issue #16) into zorite's flat-markdown engines, plus the follow-on audit fixes. 41 commits:

Features

  • Notion-style checkboxes, visible scrollbars, code-card chrome (Copy + language picker), slash palette with category flyout, context-menu restyle with a selection format bar (B/I/U/S/<>)
  • Tables: Cditor-style menu, hover outlines + split-pill add/remove affordances, drag-to-resize columns persisted as <!-- table:STYLE cols=… -->, and wide tables scroll horizontally in place (natural column widths, masked paint, slim thumb) instead of scaling down
  • Full inline-marker hiding: formatting markers never reveal (caret moves by visible position; Cditor-style delete collapses emptied pairs)
  • "Turn into" block conversion flyout in the right-click menu
  • Block drag-reorder via a hover gutter grip (whole regions move as units; window-level mouse pipeline since the grip lives outside the element)
  • Whiteboard: adopted the ding-board fork wholesale (mindmaps, flowcharts, bound connectors, alignment guides, culling)

Performance (from the Cditor audit)

  • Measure→prepaint shape memo, content-generation scan cache, cross-frame line-run cache, table column/wrap-row caches, resumable IME UTF-16 mapping
  • Windowed rendering: offscreen plain lines skip shaping (exact cached heights, no estimates), paint culls offscreen lines
  • Scroll anchoring for async raster loads — compensator gated to the real final width (intrinsic-width measures used to yank the feed offset) with a once-per-paint latch
  • Reader-mode journal: offscreen days render as fixed-height spacers

Fixes

  • Escaped \| handling through cell split/rewrite, CRLF paste normalization, double-backtick spans, nested inline emphasis, ragged-table grid widening with row spans, selection-highlight bands (full-height rows, no phantom stripes), table style-marker reveal leaks, unwrapped hidden sources for table/widget rows

🤖 Generated with Claude Code

Credits

Huge thanks to @JYChen-8866 (jychen), who wrote Cditor specifically to contribute its editor to Zorite (#16). This PR ports its best ideas into Zorite's flat-markdown engines — the table interactions, marker-hiding behavior, menus, code cards, and drag-reorder are all his designs — and the whiteboard improvements are his ding-board fork adopted wholesale. The multi-window virtual-rendering architecture in Cditor also directly informed the windowed-rendering work here.

…gh text

Cditor-inspired (issue #16 probe): the task box grows to 0.9em, a done box
fills with the accent color under a white check, and a checked item's text
renders struck through + muted — in BOTH views (reader draws the same box
the WYSIWYG paints). Reveal-on-caret still shows plain source.
Cditor-inspired: gpui-component's Scrollbar overlaid on the feed and
page scroll containers (full-inset overlay, its own helper's pattern —
a right-anchored zero-width box gives the thumb no bounds). Fades in on
scroll/hover per the theme's show mode.
…ews)

Cditor-inspired: every fenced block gets top-right chrome. Reader: a
hover-revealed language tag + Copy (writes the raw code). WYSIWYG: the
same chip painted on the card — Copy writes the block body via the
clipboard writer; the language tag opens a scrollable picker of the
host's grammars (set_code_languages, new pub API) and selecting one
rewrites the opening fence as one undo step. Zorite passes its compiled
tree-sitter set plus text/mermaid (highlight::LANGUAGES).
Pre-existing since 1e0ffe6: `(a < b).then_some(a - start..)` evaluates
its argument eagerly, so a token wholly before the current line
underflows `b - start` (panic with overflow checks on — debug builds).
First reachable on a multi-line highlighted block; surfaced by the new
language picker. `then` (lazy) instead.
…iet gutter

Follow-ups to the code-card chrome from user testing:
- chips are hover-revealed (full-card hover tracking, the heading-chevron
  pattern), on an opaque popover-surface pill, at 13px
- a fence reveals only with the caret/selection on its own line — editing
  inside the block no longer shows the fence markers (their language now
  lives in the chip); dead code_regions removed
- picking a language parks the caret on the body's first line, not the
  fence (which would reveal it)
- vertical caret movement skips zero-height (collapsed) rows in the
  travel direction, and end-of-document clamps to the last visible line —
  arrows can't land on (and reveal) either fence
- the line-number gutter paints nothing for zero-height rows (the hidden
  fence got a number inside the card's pad gap)
Cditor-inspired restyle of the `/` menu (issue #16):
- rows show a boxed glyph + title + one-line description; capped at 7
  tall rows. The other completions (`[[`, `#`, `\`, `{{`) stay compact.
- categories (Markdown, Templates) open a submenu that flies out beside
  the main panel instead of replacing the list; hover / arrow / Enter to
  focus it, Esc steps back, hover the main list to return.

Positioning polish (the hard part):
- the flyout is an ABSOLUTE child of the main panel, so it never inflates
  the anchored element — gpui always snaps an overflowing anchored box
  into the window, which was yanking the whole menu up as you hovered.
  With only the stable main panel anchored, it sits cleanly below the
  caret and doesn't move when the flyout shows/hides.
- the flyout doesn't auto-open on a fresh `/` (a `flyout_shown` flag,
  set only on deliberate hover / arrow / Enter).
- the flyout shifts up when it would overflow the window bottom (the
  caller replicates where snap lands the main panel, then offsets).
- a transparent backdrop under the menu dismisses it on an outside click
  (on_mouse_down_out fires in capture phase against only the main
  panel's bounds, so it would have closed on flyout-row clicks).
The rest of the menu-family restyle (issue #16):
- the shared page menu (sidebar rows, All Pages, search, backlinks) and
  the tab menu get Lucide icons per row and a RED "Delete page" (label +
  trash icon in the theme's danger color); the sidebar's "New page" and
  the formula/Edit right-click menu get icons too
- selection right-click grows an inline-format bar across its top:
  B / I / U / S / <> toggle bold, italic, underline, strikethrough, and
  inline code on the selection (the editor's toggle-wrap, so a second
  press un-wraps); spell suggestions + clipboard verbs sit below
- NEW: underline (cmd-u) via the <u> tag — markdown has none — honored
  in BOTH views like <mark>: WYSIWYG hides the tags and underlines the
  body (toggle_wrap generalized to open/close pairs), the reader
  underlines the wrapped runs; "Underline" joins the slash palette; NEW:
  strikethrough action (cmd-shift-x)
- every menu (app + all five in-editor popups) carries the same drop
  shadow, so the family reads as one system
- six Lucide faces newly embedded for release builds: trash-2,
  app-window, file-text, file-down, align-center, align-right
…eletes

The table-interaction pass (issue #16): the right-click table menu
becomes grouped Cditor-style rows (glyph column, checkmarks, shadow)
and grows real new verbs:

- Duplicate row (duplicate_table_row — header duplicates as the first
  body row; caret lands in the copy, same cell + offset)
- table styles in the menu (set_table_style rewrites the
  <!-- table:STYLE --> marker as one undo step, caret stays put) with
  the current style checked — previously pickable only at creation
- Copy as Markdown (copy_table — grid + style marker via the clipboard
  writer)
- alignment rows check the cell's current alignment
- the delete group (row / column / table) renders red via the new
  SyntaxStyle::popover_danger (host-themed; zorite passes a Radix red)

API.md documents the three new methods + field.
…r pills

The hovered row/column now reads by BORDER, not fill (issue #16,
Cditor's look): a thin accent outline around it, plus a small accent
capsule sitting ON the table border — "+" / "−" halves with a hairline
divider — replacing the old outside strips and gutter handles. Row pill
on the left border (insert below / delete row); column pill on the top
border (insert right / delete column) — column inserts now land right
of the HOVERED column, not the last. Hover the last row/column to
append. The caret's cell carries a quieter accent outline, and the
column highlight yields while the pointer is on the row's pill.
Cditor's column resizing, zorite-style (issue #16): each column's right
border becomes a grab band (resize cursor, accent line when hovered) and
dragging resizes the column LIVE; release persists every column's width
into the table's marker comment — <!-- table:grid cols=142,96,210 --> —
so it's plain markdown both views honor and other viewers ignore. One
undo step per drag; the style menu and resize preserve each other's
marker attributes.

Cells word-wrap once a drag narrows a column under its content: the row
grows to the tallest cell's wrap rows, the caret seats on the correct
wrap row, and clicks hit-test in 2-D inside the cell (alignment applies
only to unwrapped cells). While the pointer is on a resize band — or a
drag is live — the row/column outlines + pills yield to it.

Recognition consolidates in gpui-markdown's syntax module (the
sanctioned direction): the shared TableStyle replaces gpui-editor's
duplicate, and table_col_widths / table_marker_text parse + write the
cols= attribute, with tests.
…IWYG

Bold/italic (* and _), strike, inline code, <mark>, and <u> markers now
NEVER show in WYSIWYG — not under the caret (the old per-construct
reveal) and not under a selection (the old whole-line raw reveal). The
styling is the feedback; the toggles (⌘B/I/U/⇧X/E, the selection format
bar) edit it — the model Cditor gets by converting typed markers to
styled spans. Structural constructs (links, wiki links, headings, math)
keep reveal-on-caret: their raw text is the only way to edit them. An
unclosed pair still shows while you type it.

Caret movement is by VISIBLE position now: left/right (and shift-select)
extend their grapheme step while the display column is unchanged, so the
zero-width marker bytes never cost extra keypresses. Selected lines keep
the hidden view with correctly mapped highlight geometry.

New always_hide span flag + fmt_marker() in the scanner; tests updated
to the new contract plus a never-reveal matrix.
Backspace / forward delete now remove the adjacent VISIBLE character —
at a construct's edge the invisible marker bytes are skipped, so a
hidden ** / ~~ / ` / <u> / <mark> is never half-eaten into revealed
junk. Deleting a construct's last character collapses the construct:
the now-empty marker pair goes with it as one edit (one undo step),
matching deleting the last char of a bold run in Cditor.

fmt_marker_pairs (new, tested) returns the opening/closing marker pair
per construct — pairing is per-construct so a delete between two
adjacent constructs can't fuse one's closer with the other's opener —
and fmt_delete_range plans the edit (skip → visible grapheme → pair
collapse), bowing out inside code fences and across line joins.
…ectors

JYChen-8866 forked gpui-whiteboard as Cditor's `ding-board` and built on
it; this brings his work home (issue #16). New with this adoption:

- mind-map tool: seeded root+branches with per-node metadata, auto
  layout, sides/direction, straight/bezier/orthogonal connectors, and
  "+" add-node buttons on the selection
- flowchart tool: seeded Start/Process/Decision/Yes/No/End scaffold
- shape-bound connectors: line/arrow endpoints snap to shapes' connector
  points and FOLLOW them through move/resize/rotate (SegmentAnchor;
  detach on moving the segment); dashed segment style
- alignment guides + snapping while dragging elements
- perf: viewport culling (render_perf's WorldViewport) and cached
  text/label layouts — his fork's answer to our "defer until measured"
- read-only mode, BoardEmbedView (embedded board + host-driven Edit
  button), BoardThumbnailView + LocalThumbnailSnapshot (static previews)
- font: system CJK fallback face behind the bundled JetBrains Mono

All new scene fields are serde-defaulted, so existing boards load
unchanged. Zorite-side: importers/tests gain the new fields; UI strings
localized from Chinese; his additions brought up to the clippy gate.
README adopted; API.md documents the new surface. His tests (a superset
of ours) ride along.
table_cells / table_cell_ranges split blindly on '|', so a cell holding
an escaped pipe miscounted against the reader's GFM parse (grid, caret,
and click hit-tests disagreed with the rendered table) — and
rewrite_table_columns' split+rejoin then CORRUPTED the row on any column
insert/delete (the cell became two, shifting every later column).

Splitting now skips pipes preceded by an odd backslash run; cells stay
VERBATIM source (the escape visible in the grid), so ranges/caret math
keep their exact source mapping and rewrites round-trip. Found auditing
against Cditor's table.rs, which unescapes+re-escapes.
Two paste bugs from the Cditor audit:

- a Windows/browser clipboard carries \r\n (or bare \r); pasting put
  literal \r bytes into the buffer — garbled rendering and desynced
  \n-based row/column math. Normalize to \n on paste.
- pasting text with newlines/pipes inside a table cell split the
  `| … |` row markup, though Enter is carefully guarded there. Flatten
  newlines to spaces and escape pipes (unescape-first so an existing \|
  doesn't double), keeping the paste one cell's content — consistent
  with the new escape-aware cell splitting.
Two reader/editor divergences from the Cditor audit:

- inline code now matches backtick RUNS: an opening run of N backticks
  closes at the next run of exactly N, so ``a`b`` keeps its inner
  backtick as body; a run with no matching closer is skipped whole
  (one token, CommonMark) instead of re-matching a shorter span inside
  itself.
- backslash escapes are honored for * (bold + italic), ~~, the backtick
  opener, and every [ construct (links, wiki links, footnotes): \*text\*
  now stays literal in WYSIWYG exactly as the reader shows it, and an
  escaped closer no longer closes. Shared is_backslash_escaped (from the
  \| table fix) + _unescaped find variants; `_` already checked.
A backslash escape's `\` now renders as a hidden marker (reveal-on-
caret), so `\*text\*` displays *text* and `\**bold**` displays exactly
what the reader renders (literal star, italic body, literal star)
instead of leaving the backslashes visible. The escaped character stays
literal content — no construct can open on it — and `\\` shows a single
backslash. Follow-up to the escape-honoring scanner fix, verified
against the reader's GFM parse.
…vely

The last reader/editor divergence from the Cditor audit's bug list:
scan_line consumed a construct wholesale, so **bold *italic* bold**
rendered its inner markers as literal text with only the outer style.

The inline loop is now scan_inline, recursive: a formatting construct's
body (bold/italic both forms, strike, <mark>, <u>) re-scans with the
construct's style layered as the base (Style::over — booleans OR,
options prefer the inner construct's), the gaps between inner constructs
fill with the body style, and depth caps at 3. Inline code, links, wiki
links, tags, and bare URLs inherit the enclosing style without
recursing (code bodies stay literal per CommonMark).

Marker pairing moves from adjacency to shared pair_ids stamped on each
construct's opener+closer, so fmt_marker_pairs — and the Cditor-style
delete collapse built on it — survives nested constructs between a
pair's markers.
Three audit items in one pass:

- the measure pass memoizes its full shape_document output (ShapeMemo,
  RefCell — the closure only holds a read borrow) and prepaint consumes
  it when the wrap width / caret / selection / font size match — the
  whole document was being shaped TWICE per frame with identical inputs.
  Taken-once semantics: the memo can never go stale across frames, and
  a key mismatch just falls back to shaping.
- UTF-8↔UTF-16 conversions resume from an anchor of the last converted
  offset (valid per content generation) instead of scanning from byte 0
  — IME composition fires these many times per keystroke, so CJK
  composition latency no longer grows with note size. The new
  content_gen counter (bumped in record_edit + the three bypasses:
  set_text, undo/redo restore, IME replace_and_mark) is also the
  staleness key a future cross-frame shape cache needs.
- bounds_for_range spans the whole marked range when it sits on one
  wrap row, so the IME candidate window anchors under the composing
  text instead of a zero-width bar at its start.
shape_document re-derived every structural scan on each call — table
regions, ordered-list numbering, mermaid blocks, math regions, property
runs, foldable callouts — all O(document) walks that ran on every frame
(twice, before the shape memo). They now live in ScanData, cached on
EditorState keyed by content_gen, rebuilt only when the content changes.

The same scan feeds the caret-driven table ops (align/menu/duplicate/
style/resize used to call table_regions on every click) and the fenced-
code parity guard: apply_auto_replace re-scanned every line above the
caret on each boundary keystroke, and fmt_delete_range on each delete —
both now read the scan's per-line fence_odd.
…'t jump

Cditor's anchor-restore, adapted (its AnchorFrame::restore_once): a
math/mermaid/image raster arriving collapses raw source lines into a
rendered block, shifting every line below — including whatever the user
was reading. The measure pass now detects it (same content_gen as the
last paint but different line tops ⇒ the height change was async, not
an edit), and when the first changed row sits above the window viewport
it hands the delta to a host hook (set_scroll_compensator, new pub API)
BEFORE the scroll container places its children — the feed/page scroll
offset absorbs the shift in the same frame. Edits never trigger it, and
a width change (resize reflow) is excluded. Both hosts wired; API.md
documents the hook.
The audit's cross-frame item, right-sized: gpui's own LineLayoutCache
already retains glyph shaping across frames, so the residual per-frame
cost was rebuilding every markdown line's display string + run list
(hidden_runs) on every repaint — caret blinks, hovers, and any edit in
ANOTHER feed editor re-derived every visible day's every line.

Each line's built (display, runs, map) is now cached across frames,
keyed by a hash of the line bytes, its reveal state (caret column,
prefix reveal/hide, selection reveal), its diagnostics, and a font+
palette epoch — with the source line verified on hit, so collisions
can't render the wrong text. Edits re-key only the lines they change;
theme/font switches miss wholesale via the epoch. Capacity-capped so
retired keys from typing can't accumulate.

(A full WrappedLine-level cache needs Clone that gpui's WrappedLine
doesn't have — this captures the same win without the ownership
refactor.)
packetThrower and others added 21 commits July 17, 2026 18:13
…ames

table_column_widths shaped every cell of every table per shape_document
call, and table_row_wrap_rows re-shaped each drag-narrowed row's cells per
frame. Bundle the editor's shaping caches into one ShapeCaches struct
(line runs + a keyed region_cols entry + a per-row wrap-count memo);
rebuilt only when the tables' source, the wrap width, the font epoch, or
a live column drag changes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…menu

Cditor-style: hovering a new "Turn into" row opens a kind-list flyout
(Text / H1-H3 / bulleted / numbered / to-do / quote / callout / code /
math) with the caret block's current kind checked. Flat-markdown
conversions — a line-prefix rewrite in one undo step; quote runs and
fenced code/math blocks convert whole, and fenced targets seat the
caret inside the body so the markers stay hidden. The flyout floats as
an absolute sibling of the clipped menu box so it can't inflate the
anchored bounds (the slash-flyout lesson).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ws span

A body row with more cells than the header used to hide the excess (and
a caret seated there could panic on a col_widths slice). Reader parity
instead: the grid's column count is the max cell count across the
region's rows, and a short row's last cell spans the remaining columns
with no divider — paint, caret math, click hit-testing, and the
caret-cell outline all follow the span via cell_span_width.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
In reader mode every loaded day rebuilt its full MarkdownView element
tree per frame, making feed scrolling slow. The feed column now tracks
each day section's bounds (a second ScrollHandle that never scrolls),
and days more than a viewport-height offscreen render as fixed-height
spacers using their last measured height, so the scroll geometry is
preserved. Fully rendered days refresh their recorded height each
frame; a width change clears the cache. WYSIWYG days (one cached editor
element each) and feed-find sessions (which need every day's blocks
painted) always render fully.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…aint

The document-scale half of the Cditor audit's L item, without the
estimated-heights machinery (heights stay exact, so no convergence
jumps; the honest ceiling is that a document's FIRST shape is still
O(doc), cached thereafter):

- Geometry reads a new per-line wrap-row count (ShapedLines + committed
  EditorState/PrepaintState vecs) instead of wrap_boundaries(), so a
  line's layout no longer needs its shaped text.
- Paint culls lines outside the window viewport (nesting-guide
  bookkeeping still walks offscreen ancestors' marks).
- shape_document takes a quantized viewport band (published one frame
  stale from prepaint so measure + prepaint always agree and the
  ShapeMemo keeps hitting): plain markdown lines wholly outside it skip
  run-building and shaping, reusing exactly-cached (row height, wrap
  rows). Caret/selection lines, code, tables, math, images, embeds, and
  folds always shape fully.

Also two scroll-anchoring fixes this surfaced (sandbox-reproduced: the
"stuck feed at a narrow window" bug): the compensator now only reacts
to measures at the real final width — taffy's intrinsic-width passes
produced multi-thousand-px bogus deltas that yanked the feed offset and
fought the user's scrolling (and broke drag-and-drop mid-yank) — and a
once-per-paint latch stops one async raster arrival from compensating
once per measure call. ZORITE_WINDOW_DEBUG=1 logs compensator events.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Cditor/Notion-style: hovering a line reveals a six-dot grip in the left
margin (left of the line-number rail when it's up — the host pads for
it via set_grip_inset); dragging it moves the line's whole block, with
an accent bar marking the drop boundary. Blocks travel as units: fenced
code, tables + their style marker, math/mermaid regions, property
panels, quote/callout runs, and list items with their deeper-indented
children. Drops snap out of rendered-region interiors, and the move is
ONE undoable splice of the span between block and target.

The grip lives OUTSIDE the editor div's bounds, so the press, the drag
tracking, the drop, and hover-row repaints all run through window-level
mouse listeners gated on painted rects — the div's own listeners never
see gutter events (and its hitboxes report unhovered out there).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A viewport-sized hitbox inserted above the editor's own keeps the grab
cursor while the pointer crosses text (which would flip it to I-beam).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Natural content-fit columns always (the scale-to-fit pass is gone); a
table wider than the viewport scrolls in place — horizontal wheel/
trackpad over it moves it (vertical still scrolls the page), a slim
thumb under the last row shows the position, and rows paint shifted
under a per-row content mask (the outer Grid border gets its own
full-height mask). All x hit-testing — cell clicks, hover pills,
resize bands, the caret and selection — applies the same offset.
Table source lines now shape unwrapped: their raw `| … |` text is
hidden, and a wrapped source would multiply the row's height. Matches
the reader, which already scrolls over-wide tables in their row.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two caret leaks flashed the raw `<!-- table:… -->` marker: the drag's
commit re-seated a caret that wasn't in the table into a fabricated
"cell" on row 0 (the marker line) — it now stays where it was, shifted
by the edit's byte delta — and a band press right after a document
loads focused the editor with the caret still parked at offset 0 on
the marker line — it now seats into the header cell before the drag.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Table selection quads now paint one full-height band per row: wrapped
cells highlight every wrap row, and vacant trailing cells highlight to
the table's edge (endpoints inside a cell keep the exact caret x; an
empty cell the shaper can't position falls back to the edge). Collapsed
rows (the |---| separator, hidden marker/fence lines, folds) paint no
quad — they used to smear a base-height band over their neighbors —
and a wide scrolled table's bands clamp to its visible area.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Same bug tables had: a long `![](path){width=N}` source wrapping at a
narrow window multiplied the image row's height by its wrap count.
Widget lines now shape unwrapped; revealed-on-caret lines still wrap.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two ultrareview findings on PR #52:
- The gutter grip's window-level press listener gated on a bare rect, so
  a drag could start THROUGH a dialog/menu/popover covering the gutter.
  It now gates on the grip hitbox's is_hovered, which respects gpui
  occlusion (BlockMouse hitboxes above it win).
- Turn-into's hand-rolled prefix grammar diverged from the renderer's:
  `* [ ]`/`+ [ ]` tasks classified as bullets, `1)` lists and indented
  items inconsistently, any `[!garbage` counted as a callout, and the
  strip/detect/render grammars could triple-drift. block_kind_at,
  strip_block_prefix, and the quote-run walks now compose the renderer's
  own recognizers (task_prefix/list_prefix/heading_level/
  blockquote_prefix/alert_kind) — one grammar, three consumers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The invariant, enforced once: set_text — the only PASSIVE caret-parking
path — steps a caret landing on a `<!-- table/math:… -->` marker line
to the table's header (or past the math block), so every
focus-without-caret-move affordance (pills, delete handles, code Copy,
fold chevrons, image grips, resize bands) is safe by construction.
Deliberate navigation onto a marker still reveals it for editing. The
resize-band point patch this replaces is deleted.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A persisted light/dark choice still wins; unknown persisted values now
fall back to Auto too.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CI caught what the local gate missed: running `cargo check -p` before
`cargo clippy` can reuse the check fingerprint and skip lints, so the
gate printed clean while CI's fresh clippy failed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Five of the six ultrareview dedup findings:
- table_left(): THE single source for a (possibly scrolled) table's
  content-left, replacing six hand-built `bounds.left + GUTTER - sx`
  sites including paint's inline clamp fork (the cloned sx map is gone).
- fence_block_rows() + quote_run_rows(): one fence walk and one quote
  run walk, replacing three copies of each across turn_into,
  drag_block_rows, snap_drop_boundary, and block_kind_at.
- grip_left(): the grip x formula shared by prepaint and the event-time
  hover mirror, so the two can't drift.
- is_escaped deleted — is_backslash_escaped is the one escape predicate.
- menu_turn_into moved into DiagMenu, so the flyout flag dies with the
  menu instead of leaking across opens.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Named per-line channels instead of positional .0-.8 access, and ONE
push_placeholder method replaces the seven hand-replicated
empty-placeholder blobs (mermaid/math/embed/fold/props arms + the
windowed skip path) — the parallel-vec lockstep invariant now lives in
the type. Last of the six ultrareview dedup findings.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Hover listener: none for raw editors, O(1) x/y bail via a read-only
  check before any entity update (was: N editor updates + full line
  scans per pointer move across the feed).
- on_scroll_wheel bails on dx == 0 before the O(lines) row walk.
- line_runs cache payloads are SharedString/Rc — a hit is three
  refcount bumps, not three deep clones per visible line per frame;
  the maps channel shares the same allocation, inline math/image calls
  are span-gated, and the display string feeds shape_runs directly.
- region_cols keys on the scan generation (no per-frame rehash of all
  table text) and stores Rc (hit = pointer bump).
- Per-row content-key memo: steady-state frames hash three u64s per
  line instead of every line's bytes for the run/height cache keys;
  diagnostics changes invalidate explicitly.
- Slash flyout rows cached per (level, title) instead of rebuilt every
  frame and twice per arrow key.

ScanData carries its generation; ShapedDoc.maps is Rc-shared.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A selection with both ends inside the same wrapped cell now paints one
band per wrap row (start x → cell edge, full-width middles, cell edge →
end x) instead of a single band that covered only part of the text.
Cross-cell and full-row selections keep the full-height row band.

Also the type_complexity lints CI caught: RowKeys/RegionCols/FlyoutCache
aliases. Root cause of the recurring local-pass/CI-fail gap found: the
local gate ran clippy WITHOUT `-- -D warnings`, so default-warn lints
printed as warnings the error grep ignored — the gate now mirrors CI
exactly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@packetThrower
packetThrower merged commit 013ff53 into main Jul 18, 2026
5 checks passed
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