Skip to content

Complete the deferred-features tails: Review-tab polish, rotated-cell caret, reflow link-open, grid zoom, overflow-menu dismissal, and more#32

Merged
kevincarlson merged 12 commits into
mainfrom
claude/deferred-features-audit-j8riaf
Jul 11, 2026
Merged

Complete the deferred-features tails: Review-tab polish, rotated-cell caret, reflow link-open, grid zoom, overflow-menu dismissal, and more#32
kevincarlson merged 12 commits into
mainfrom
claude/deferred-features-audit-j8riaf

Conversation

@kevincarlson

Copy link
Copy Markdown
Member

Closes the remaining actionable tails from docs/deferred-features-plan-2026-07-04.md (the partially-implemented features' leftover work), plus two stale entries verified as already built. Twelve focused commits:

Editor / Review tab (4a.2 polish — track changes now fully complete)

  • Struck ¶ rendering (TODO(review-para-mark-render)): a tracked paragraph-mark deletion paints a struck, author-coloured end-of-paragraph marker (paint-only items, so caret/hit-test/wrapping are untouched — the constraint that had deferred it). Root-cause fix found while wiring: flatten_paragraph bled the ¶'s revision onto the runs, striking the whole paragraph text; regression-locked.
  • ODF ¶-deletion round-trip: export emits an end-of-paragraph text:change whose deletion region stows the paragraph break (empty text:p, LibreOffice's shape); import maps it back onto the paragraph. DOCX already round-tripped.

Rotated table cells (4b.5 complete)

  • The caret line, selection fills, and handles now paint through the cell's rotation affine (new loki-vello/scene_cursor.rs; unit-tested against CellRotation::local_to_page), so the caret tilts with the text; up/down arrow navigation is rotation-aware via local_to_page + a new visual_y_span.

Hyperlinks (5.11 complete)

  • Reflow-mode Ctrl/Cmd+click now opens links: DocumentView resolves the URL against its continuous layout and reports it via a new on_open_link handler; the browser dependency stays in the app layer.

Apps (4c.5 tails + 4a.1 tail)

  • Spreadsheet grid zoom (TODO(zoom)): the status-bar badge cycles 50–200%, scaling widths/heights/fonts while the document keeps unzoomed pt (resize commits divide the zoom back out).
  • F7a width plumbing: new shared AtViewportWidthSensor; Presentation + Spreadsheet now provide the responsive context and push a measured width, so AtHomeTab's breakpoint tracks the real window.
  • Ribbon More-menu outside-click dismissal (TODO(ribbon)): new window-level backdrop primitives (use_provide_backdrop + AtBackdropHost) hosted in the apps' now-positioned roots; the menu stays anchored, a viewport-spanning transparent backdrop closes it.

Model / formats

  • Loro bridge border colors: v2 border codec (color field last) so Theme/Cmyk border colors survive the CRDT; v1 strings still decode.
  • Inline + cell-level w:sdt (5.9 tail): verified the reader never dropped them (stale deferral note); behaviour regression-locked and both dispatches hardened to skip w:sdtPr chrome wholesale.

Performance

  • Option B y-range filter (6.3, TODO(split-optimise)): split-paragraph fragments carry only the items near their own y-range instead of a full per-page copy; the Option-A clip is kept so rendering is pixel-identical.
  • Real DocumentViewProps::eq (6.7): replaces the hardwired-false comparison (Arc-identity + value fields).

Docs & hygiene

  • Plan/audit/fidelity-status synced; two stale plan entries corrected (typed SaveError already existed; inline/cell sdt were never dropped); file-ceiling baseline ratcheted (scene.rs 727→613, spreadsheet editor_inner.rs 1047→1014, resolve.rs 865→858); CLAUDE.md offender table refreshed; stale TODO(link-click) notes retired.

Verification

  • cargo +1.97 clippy --workspace --all-features -- -D warnings -D clippy::unwrap_used -D clippy::expect_used — clean (the toolchain CI uses).
  • cargo +1.97 fmt --all -- --check — clean.
  • scripts/check-file-ceiling.py — green (baseline only shrank).
  • cargo test --workspace — all suites pass except 3 pre-existing appthere-conformance raster tests that require pdftoppm (poppler-utils is not installable in this sandbox; CI installs it).

Remaining deferrals stay tracked in the plan: upstream-gated Watch-list items, device-gated Spec 06 work, post-MVP Calc/Slides scope, and the larger still-open workstreams (OMML 5.8, Spec 04 M6 touch posture, Spec 03 M4 type-scale, 4a.3 table-style/editing-UI tails, clipboard, memory 6.1/6.2, Phase 7 quality ratchets).

🤖 Generated with Claude Code

https://claude.ai/code/session_017mBbeUqSR6utoozCfLoswr


Generated by Claude Code

claude added 12 commits July 11, 2026 01:48
…rops eq (5.11 tail, 6.7)

Reflow half of feature 5.11: DocumentView resolves the URL against its
continuous layout (reflow_link_at plumbing through RenderLayout and
DocPageSource) and reports it via a new on_open_link handler; loki-text
opens it with webbrowser, keeping the browser dependency in the app layer.
Resolves TODO(link-click-reflow).

Also replaces the hardwired-false DocumentViewProps PartialEq with a real
field comparison (plan 6.7): doc/paginated_layout by Arc identity, scalars
by value; event handlers deliberately omitted (Dioxus EventHandlers always
compare equal).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017mBbeUqSR6utoozCfLoswr
…4b.5 tail)

Resolves TODO(rotated-cell-caret). The caret line, selection fills, and
selection handles now paint through a rotation-aware affine
(scene_cursor::cursor_paint_transform composes the CellRotation the cell's
content is painted with), so the caret tilts with rotated table-cell text
instead of staying upright. Cursor painting moved to a new scene_cursor.rs
(scene.rs is baselined and shrank instead). Up/down arrow navigation maps
the caret position through PageParagraphData::local_to_page and aims at
neighbouring paragraphs via a new visual_y_span (post-rotation bounding
box), replacing the raw rect+origin math.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017mBbeUqSR6utoozCfLoswr
… (4a.2)

Resolves TODO(review-para-mark-render). A tracked paragraph-mark deletion
(direct_char_props.revision of kind Deletion) now paints a struck,
author-coloured marker after the paragraph's last line: two short stems +
a strikethrough segment, emitted as paint-only items in para_underlays so
caret placement, hit-testing, and wrapping are untouched. The colour rides
ResolvedParaProps.para_mark_deleted_color, set by resolve_para_props via
revision_style::para_mark_deletion_color.

Root-cause fix found while wiring: flatten_paragraph merged the paragraph's
direct_char_props into the run base without clearing revision, so a
pilcrow-deleted paragraph rendered its entire text struck in the author
colour. The run base now drops the ¶'s revision. Regression-locked by
tracked_para_mark_deletion_renders_struck_marker_without_striking_text.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017mBbeUqSR6utoozCfLoswr
…tail)

The remaining Review-tab polish item: a tracked pilcrow deletion now
survives save/open through ODF. Export emits an end-of-paragraph
text:change milestone whose deletion region stows only the paragraph break
(an empty text:p — LibreOffice's shape for a deleted paragraph mark);
import maps an empty-deletion change point back onto the paragraph's
CharProps.revision instead of materialising a struck empty run. The
revision-mapping helpers moved to a new inlines_revision.rs sibling module
to hold the 300-line ceiling.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017mBbeUqSR6utoozCfLoswr
Resolves TODO(split-optimise). Each ClippedGroup fragment of a
page-spanning paragraph now carries only the items whose (conservatively
over-estimated) vertical extent intersects the fragment's y-range
(ParagraphLayout::items_in_y_range in para_query.rs), instead of a full
copy of every paragraph item per page. The Option-A clip rect is kept, so
any conservatively-retained extra item is still masked and rendering is
pixel-identical; an item with unknown extent is always kept. Also retires
the stale TODO(link-click) notes (5.11 shipped both halves).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017mBbeUqSR6utoozCfLoswr
Verification first: the paragraph and table-cell dispatch loops never
skipped unknown elements, so inline (w:p/w:sdt) and cell-level (w:tc/w:sdt)
sdtContent was already implicitly unwrapped — the deferral note was stale.
This pins that behaviour with reader regression tests (inline runs +
sdtPr chrome; cell paragraphs before/inside a control) and hardens both
dispatches by skipping w:sdtPr/w:sdtEndPr wholesale, matching the explicit
block-level unwrapper, so control chrome internals can never leak into the
content dispatch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017mBbeUqSR6utoozCfLoswr
Migrates the border codec to a v2 layout (Style:width:spacing:color) whose
color field comes last, so the total color codec's colon-delimited
encodings fit unescaped — non-Rgb border colors no longer collapse to
auto. The decoder accepts both layouts (v2's third field is always a
number, v1's never is), so pre-migration CRDT snapshots keep decoding.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017mBbeUqSR6utoozCfLoswr
…tail)

The zoom badge now cycles 50-200% (appthere_ui::next_zoom) and scales the
rendered grid: column widths, row heights, header sizes, and cell/header
fonts. The document keeps unzoomed pt widths — col_px multiplies the zoom
in, resize commits divide the screen px back out (clamped in screen space),
and auto-fit passes its document-px estimate through the same conversion.
apply_change/sync_undo_redo moved to editor_mutate.rs (their natural home)
to keep editor_inner.rs under its ceiling baseline. Also drops an unused
import left by the border-codec migration.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017mBbeUqSR6utoozCfLoswr
…tail)

New shared appthere_ui::AtViewportWidthSensor: an invisible zero-height,
full-width scroll container that measures its own width into the
responsive context at mount and re-measures on every shell
resync_scroll_geometry tick (blitz re-emits onscroll to scroll containers
after a window resize). Presentation and Spreadsheet now call
use_provide_responsive() and mount the sensor, so AtHomeTab's breakpoint
tracks the real window there instead of falling back to Expanded —
loki-text keeps funnelling its editor scroll-container width as before.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017mBbeUqSR6utoozCfLoswr
…backdrop (4a.1 tail)

Resolves TODO(ribbon). New appthere_ui overlay primitives:
use_provide_backdrop() + AtBackdropHost render a transparent
viewport-spanning click-catcher inside the app's (now position: relative)
root container — the host lives at the window level because position:
fixed collapses to absolute in stylo_taffy, so a backdrop rendered inside
the ribbon could never span the viewport. The overflow menu itself stays
anchored to its More button (no coordinate plumbing); opening it raises
the backdrop (menu z 41 > backdrop z 40), clicking anywhere outside closes
it, and a use_drop guard clears a stale backdrop if the strip unmounts
while open. All three apps wire the context for suite consistency;
degrades to toggle-only dismissal without it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017mBbeUqSR6utoozCfLoswr
- deferred-features plan/audit: mark the tails completed this pass (4a.1
  More-menu dismissal, 4a.2 struck-pilcrow render + ODF pilcrow round-trip,
  4b.5 rotated caret/nav, 4c.5 grid zoom + F7a width plumbing, 5.9 sdt
  verification, 5.11 reflow open, 6.3 Option B filter, 6.7 props eq,
  loro-bridge border colors) and record two stale entries verified as
  already built (typed SaveError; inline/cell w:sdt were never dropped).
- fidelity-status: Hyperlinks, Text Direction (rotated cells), and Track
  Changes rows updated to their completed state.
- file-ceiling baseline ratcheted (scene.rs 727->613, spreadsheet
  editor_inner 1047->1014, resolve.rs 865->858, mapper files); CLAUDE.md
  worst-offenders table refreshed from it.
- clippy 1.97 nits: doc_markdown/doc_lazy_continuation in new docs,
  needless_borrow in loki-vello's visual_conformance test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017mBbeUqSR6utoozCfLoswr
@kevincarlson kevincarlson merged commit 1d1d790 into main Jul 11, 2026
2 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.

2 participants