Add opt-in wrap_rows: wrap long task rows in list and archive views - #94
Open
sambatia wants to merge 7 commits into
Open
Add opt-in wrap_rows: wrap long task rows in list and archive views#94sambatia wants to merge 7 commits into
sambatia wants to merge 7 commits into
Conversation
Parsed, serialized, and persisted like the other display toggles; absent from existing config files it defaults to today's truncating behavior.
Rebindable as toggle_wrap in keybinds.toml, listed in the command palette, help overlay, and settings overlay. Persists via the same save-prefs path as the other layout toggles. Rendering lands in the next commit; the toggle currently only flips and saves the pref.
Rows wrap at word boundaries to the pane width, continuation lines indent to the body column, and cursor/selection styling carries across every wrapped line. Words wider than a whole line (URLs) hard-break at character boundaries; widths are measured through ratatui's Span::width so wrap math matches cell clipping for wide (CJK/emoji) content. keep_cursor_visible now tracks the cursor row as a (start, height) span so scrolling keeps the whole wrapped row in view; single-line spans behave exactly as before. With wrap_rows off the render path is unchanged.
Unit tests cover word-boundary wrapping, unbroken-word hard breaks, separator preservation, CJK display-width accounting, exact-boundary fits, hide_keys interaction, cursor styling on continuation lines, gutter-width indentation, and single-line equivalence at wide widths. Snapshot scenes exercise wrap-on in the list and archive views, and a buffer-equality test proves wrap-on is a strict no-op while every row fits the pane.
The overlay height was a hand-maintained constant; adding the wrap keybinding row pushed the keybinding columns one row taller and silently clipped the FORMAT section's last line (@context / x DATE BODY) out of the popup. Derive the height from the section tables via help::required_height so a new row can never clip the bottom again, and add a test pinning the computed height to what render actually lays out.
…back Three fixes to the wrap engine, found in review: - Hard breaks now split at grapheme-cluster boundaries (via unicode-segmentation, already in the tree through ratatui) with each cluster measured string-level. Char-by-char accounting undercounted VS16 emoji sequences — lines rendered wider than the pane and the overflow was clipped invisibly — and tore ZWJ sequences apart. - A word wider than a whole line no longer fuses onto the previous word when the separator lands exactly at the pane edge; the separator is kept when it fits and the line breaks first when it doesn't. - Panes too narrow to wrap usefully (width < gutter + 4) fall back to the classic clipped single line instead of emitting one invisible column per line. Structural cleanup from the same review: build_line_parts is now the single source of truth for the gutter/body span boundary (build_lines previously re-derived the span count from RowOpts at a distance); wrap state lives in a LineAcc accumulator whose cur_w drives both fit checks and line-break bookkeeping; the wrap-or-not decision moved into task_row::build_lines(wrap_width: Option<u16>) so the list and archive views share one call shape; rows that fit take an allocation-free fast path; and keep_cursor_visible takes the cursor row as a line Range. New regression tests cover the fusion case, VS16 measurement, the degenerate-pane fallback, and a wrap-off snapshot of overflowing rows.
This was referenced Jul 11, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Addresses the "truncate long task rows" part of #24 (item 3).
Long rows currently clip at the pane edge with no indication — the only way to read a long task is the detail sidebar. This adds an opt-in, display-only wrap mode:
wrap_rows(default off — the dense single-line layout is untouched out of the box, per the preference expressed in [Feature request] batch of UX improvements: grouping, collapsible sections, input editing #24).toggle_wraponw, wired through keybinds.toml, the command palette, the help overlay, and the settings overlay, persisted like the other layout toggles (hot-reload works too).hide_keysis applied before the wrap width is measured. Scrolling keeps the whole wrapped cursor row visible, rows that fit take a fast path, and panes too narrow to wrap usefully fall back to today's clipped single line.The only dependency additions are
unicode-segmentationandunicode-widthas direct deps — both already in the tree transitively via ratatui, so nothing new compiles.Default rendering is unchanged: the existing snapshot suite passes byte-identically, plus a new wrap-on/off buffer-equality test and a wrap-off snapshot of overflowing rows. Adds 15 unit tests on the wrap machinery, 3 scroll-span tests, a help-overlay height test, and 3 new snapshot scenes.
cargo fmt --all --check,cargo clippy --all-targets --locked -- -D warnings, andcargo test --lockedall pass.One commit also fixes a help-overlay sizing trap this feature would otherwise have tripped: the overlay height was a hand-maintained constant, and any new keybinding row silently clipped the FORMAT section's last line; it is now derived from the section tables (with a test pinning it to what
renderlays out).Deliberately not included, to keep the default pixel-identical: the
…truncation indicator sketched in #24 (that changes default rendering — separate decision), and the add/edit input-box overflow handling (adjacent to #92). One known limitation worth flagging: OSC 8 hyperlink targets are derived from the visible underlined run, so a hard-broken URL yields fragment links — the same class of breakage that already exists for URLs clipped at the pane edge today; the clean fix belongs inhyperlinks::collectand I'm happy to follow up there.Happy to split this differently or adjust naming (
wrap_rows/toggle_wrap) to taste.CI note: the upstream workflow run is awaiting first-contributor approval; the identical workflow (fmt + clippy + test) is green on the fork: https://github.com/sambatia/tuxedo/actions/runs/29166937663
🤖 Generated with Claude Code