fix(composer): scope multiline block formatting - #3246
Conversation
Block-format toolbar buttons (bullet list, ordered list, quote, code block) reformatted the entire draft when clicked with a collapsed caret, because Shift+Enter lines are hardBreak nodes inside one paragraph and ProseMirror block toggles operate on whole textblocks. Extend isolateSelectionForBlockFormatting so a collapsed caret isolates its hard-break-delimited line — replacing the bordering hard breaks with block splits — before the toggle runs, keeping isolation and toggle one undoable transaction. Route the quote button through the same isolation path it previously skipped. Explicit selections keep their existing behavior: format only the selected lines. Adds unit coverage for the caret-line isolation and E2E coverage for caret-only formatting (new line, middle line, serialized Markdown) plus quote in the selection matrix. Co-authored-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Split selected hard-break lines before list wrapping so each visual line becomes its own item. Merge selected textblocks into one code block while preserving visual line breaks, unselected list neighbors, and caret-only behavior. Co-authored-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
wpfleger96
left a comment
There was a problem hiding this comment.
🤖 Thanks for the fix — the caret-line isolation is nicely done (first/middle/last/empty trailing lines all covered, backward selections restored, single undo unit) and the serialized-Markdown e2e assertions are the right end-to-end check.
That said, we found two selection shapes where unselected content gets structurally reformatted, plus a branch that I think defeats the toolbar's own WKWebView selection workaround, so requesting changes. Details inline on the relevant lines.
A couple of non-blocking notes while you're in here:
selectionBlockFormatting.test.mjsonly coversisolateSelectionForBlockFormatting—splitSelectedLinesForListFormattingandmergeSelectedTextblocksIntoCodeBlockare e2e-only. They're pure transaction logic so unit cases should be cheap to add.textblockTextForCode()drops any leaf node that doesn't implementleafText, so mentions/custom emoji could silently disappear when converted to code. Probably fine as a contract, but worth a unit case or a comment saying it's intentional.
Normalize exact textblock boundary selections inward before isolation and collect selected textblocks from the exact range so visually unselected endpoint blocks are not reformatted. Co-authored-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
|
@wpfleger96 We also closed out both non-blocking notes from your review:
Validated with 3,731 desktop unit tests, typecheck/check, and focused composer Playwright 24/24. — AI-generated response on behalf of Taylor Ho |
* origin/main: chore(compose): remove stale typesense env vars (block#3332) feat(desktop): refine agent catalog sharing (block#2439) fix(desktop): keep drafts out of the Inbox All view (block#3217) docs: restructure DCO guidance into scannable subsection (block#3337) Unify mobile loading spinners (block#3314) fix(desktop): restore the inbox icon in the sidebar (block#3341) fix(desktop): gate codex-acp on a minimum supported version (block#3254) feat(cli): add users set-status command for NIP-38 profile status (block#3253) fix(composer): scope multiline block formatting (block#3246) feat(chart): add relay pod extension points (block#3322) Refine mobile attachment picking (block#3313) Polish mobile message and search layouts (block#3121) Add mobile message image galleries (block#3312) Signed-off-by: LordMelkor <kray@squareup.com>
* origin/main: fix(desktop): paint community rail full height (#3382) fix(acp): disable goose cron scheduler in managed agent children (#3144) feat(desktop): add custom harness inline from agent dialogs (#3252) chore(compose): remove stale typesense env vars (#3332) feat(desktop): refine agent catalog sharing (#2439) fix(desktop): keep drafts out of the Inbox All view (#3217) docs: restructure DCO guidance into scannable subsection (#3337) Unify mobile loading spinners (#3314) fix(desktop): restore the inbox icon in the sidebar (#3341) fix(desktop): gate codex-acp on a minimum supported version (#3254) feat(cli): add users set-status command for NIP-38 profile status (#3253) fix(composer): scope multiline block formatting (#3246) feat(chart): add relay pod extension points (#3322) Refine mobile attachment picking (#3313) Polish mobile message and search layouts (#3121) Add mobile message image galleries (#3312) Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz> # Conflicts: # desktop/src-tauri/src/commands/personas/snapshot/import.rs # desktop/src-tauri/src/managed_agents/agent_snapshot.rs # desktop/src/features/agents/ui/AgentSnapshotImportDialog.tsx
Pull in nostr 0.44.6 (patched NIP-44 v2 DoS advisory) to clear the Security/cargo-deny failure. No conflicts. Co-authored-by: Phuoc (Phu) Do <91568955+dophsquare@users.noreply.github.com> Signed-off-by: Phuoc (Phu) Do <91568955+dophsquare@users.noreply.github.com> * origin/main: (74 commits) chore(ci): bump desktop smoke E2E timeout to 30 minutes (block#3409) release(chart): publish 0.1.7 (block#3393) feat(acp): steer claude-code and codex agents via _session/steering (block#3007) feat(desktop): apply WebKit rendering workarounds at startup on Linux (block#3271) fix(desktop): stabilize flaky DM expansion E2E ordering assertions (block#2004) docs(contributing): document the Linux system libraries just ci requires (block#3396) fix(desktop): paint community rail full height (block#3382) fix(acp): disable goose cron scheduler in managed agent children (block#3144) feat(desktop): add custom harness inline from agent dialogs (block#3252) chore(compose): remove stale typesense env vars (block#3332) feat(desktop): refine agent catalog sharing (block#2439) fix(desktop): keep drafts out of the Inbox All view (block#3217) docs: restructure DCO guidance into scannable subsection (block#3337) Unify mobile loading spinners (block#3314) fix(desktop): restore the inbox icon in the sidebar (block#3341) fix(desktop): gate codex-acp on a minimum supported version (block#3254) feat(cli): add users set-status command for NIP-38 profile status (block#3253) fix(composer): scope multiline block formatting (block#3246) feat(chart): add relay pod extension points (block#3322) Refine mobile attachment picking (block#3313) ...
Co-authored-by: Phuoc (Phu) Do <91568955+dophsquare@users.noreply.github.com> Signed-off-by: Phuoc (Phu) Do <91568955+dophsquare@users.noreply.github.com> * origin/main: chore(ci): bump desktop smoke E2E timeout to 30 minutes (block#3409) release(chart): publish 0.1.7 (block#3393) feat(acp): steer claude-code and codex agents via _session/steering (block#3007) feat(desktop): apply WebKit rendering workarounds at startup on Linux (block#3271) fix(desktop): stabilize flaky DM expansion E2E ordering assertions (block#2004) docs(contributing): document the Linux system libraries just ci requires (block#3396) fix(desktop): paint community rail full height (block#3382) fix(acp): disable goose cron scheduler in managed agent children (block#3144) feat(desktop): add custom harness inline from agent dialogs (block#3252) chore(compose): remove stale typesense env vars (block#3332) feat(desktop): refine agent catalog sharing (block#2439) fix(desktop): keep drafts out of the Inbox All view (block#3217) docs: restructure DCO guidance into scannable subsection (block#3337) Unify mobile loading spinners (block#3314) fix(desktop): restore the inbox icon in the sidebar (block#3341) fix(desktop): gate codex-acp on a minimum supported version (block#3254) feat(cli): add users set-status command for NIP-38 profile status (block#3253) fix(composer): scope multiline block formatting (block#3246) feat(chart): add relay pod extension points (block#3322) Refine mobile attachment picking (block#3313)
Co-authored-by: Phuoc (Phu) Do <91568955+dophsquare@users.noreply.github.com> Signed-off-by: Phuoc (Phu) Do <91568955+dophsquare@users.noreply.github.com> * origin/main: chore(ci): bump desktop smoke E2E timeout to 30 minutes (block#3409) release(chart): publish 0.1.7 (block#3393) feat(acp): steer claude-code and codex agents via _session/steering (block#3007) feat(desktop): apply WebKit rendering workarounds at startup on Linux (block#3271) fix(desktop): stabilize flaky DM expansion E2E ordering assertions (block#2004) docs(contributing): document the Linux system libraries just ci requires (block#3396) fix(desktop): paint community rail full height (block#3382) fix(acp): disable goose cron scheduler in managed agent children (block#3144) feat(desktop): add custom harness inline from agent dialogs (block#3252) chore(compose): remove stale typesense env vars (block#3332) feat(desktop): refine agent catalog sharing (block#2439) fix(desktop): keep drafts out of the Inbox All view (block#3217) docs: restructure DCO guidance into scannable subsection (block#3337) Unify mobile loading spinners (block#3314) fix(desktop): restore the inbox icon in the sidebar (block#3341) fix(desktop): gate codex-acp on a minimum supported version (block#3254) feat(cli): add users set-status command for NIP-38 profile status (block#3253) fix(composer): scope multiline block formatting (block#3246)
Co-authored-by: Phuoc (Phu) Do <91568955+dophsquare@users.noreply.github.com> Signed-off-by: Phuoc (Phu) Do <91568955+dophsquare@users.noreply.github.com> * origin/main: (111 commits) chore(ci): bump desktop smoke E2E timeout to 30 minutes (block#3409) release(chart): publish 0.1.7 (block#3393) feat(acp): steer claude-code and codex agents via _session/steering (block#3007) feat(desktop): apply WebKit rendering workarounds at startup on Linux (block#3271) fix(desktop): stabilize flaky DM expansion E2E ordering assertions (block#2004) docs(contributing): document the Linux system libraries just ci requires (block#3396) fix(desktop): paint community rail full height (block#3382) fix(acp): disable goose cron scheduler in managed agent children (block#3144) feat(desktop): add custom harness inline from agent dialogs (block#3252) chore(compose): remove stale typesense env vars (block#3332) feat(desktop): refine agent catalog sharing (block#2439) fix(desktop): keep drafts out of the Inbox All view (block#3217) docs: restructure DCO guidance into scannable subsection (block#3337) Unify mobile loading spinners (block#3314) fix(desktop): restore the inbox icon in the sidebar (block#3341) fix(desktop): gate codex-acp on a minimum supported version (block#3254) feat(cli): add users set-status command for NIP-38 profile status (block#3253) fix(composer): scope multiline block formatting (block#3246) feat(chart): add relay pod extension points (block#3322) Refine mobile attachment picking (block#3313) ...
Co-authored-by: Phuoc (Phu) Do <91568955+dophsquare@users.noreply.github.com> Signed-off-by: Phuoc (Phu) Do <91568955+dophsquare@users.noreply.github.com> * origin/main: (84 commits) chore(ci): bump desktop smoke E2E timeout to 30 minutes (block#3409) release(chart): publish 0.1.7 (block#3393) feat(acp): steer claude-code and codex agents via _session/steering (block#3007) feat(desktop): apply WebKit rendering workarounds at startup on Linux (block#3271) fix(desktop): stabilize flaky DM expansion E2E ordering assertions (block#2004) docs(contributing): document the Linux system libraries just ci requires (block#3396) fix(desktop): paint community rail full height (block#3382) fix(acp): disable goose cron scheduler in managed agent children (block#3144) feat(desktop): add custom harness inline from agent dialogs (block#3252) chore(compose): remove stale typesense env vars (block#3332) feat(desktop): refine agent catalog sharing (block#2439) fix(desktop): keep drafts out of the Inbox All view (block#3217) docs: restructure DCO guidance into scannable subsection (block#3337) Unify mobile loading spinners (block#3314) fix(desktop): restore the inbox icon in the sidebar (block#3341) fix(desktop): gate codex-acp on a minimum supported version (block#3254) feat(cli): add users set-status command for NIP-38 profile status (block#3253) fix(composer): scope multiline block formatting (block#3246) feat(chart): add relay pod extension points (block#3322) Refine mobile attachment picking (block#3313) ...
Co-authored-by: Phuoc (Phu) Do <91568955+dophsquare@users.noreply.github.com> Signed-off-by: Phuoc (Phu) Do <91568955+dophsquare@users.noreply.github.com> * origin/main: (111 commits) chore(ci): bump desktop smoke E2E timeout to 30 minutes (block#3409) release(chart): publish 0.1.7 (block#3393) feat(acp): steer claude-code and codex agents via _session/steering (block#3007) feat(desktop): apply WebKit rendering workarounds at startup on Linux (block#3271) fix(desktop): stabilize flaky DM expansion E2E ordering assertions (block#2004) docs(contributing): document the Linux system libraries just ci requires (block#3396) fix(desktop): paint community rail full height (block#3382) fix(acp): disable goose cron scheduler in managed agent children (block#3144) feat(desktop): add custom harness inline from agent dialogs (block#3252) chore(compose): remove stale typesense env vars (block#3332) feat(desktop): refine agent catalog sharing (block#2439) fix(desktop): keep drafts out of the Inbox All view (block#3217) docs: restructure DCO guidance into scannable subsection (block#3337) Unify mobile loading spinners (block#3314) fix(desktop): restore the inbox icon in the sidebar (block#3341) fix(desktop): gate codex-acp on a minimum supported version (block#3254) feat(cli): add users set-status command for NIP-38 profile status (block#3253) fix(composer): scope multiline block formatting (block#3246) feat(chart): add relay pod extension points (block#3322) Refine mobile attachment picking (block#3313) ...
Co-authored-by: Phuoc (Phu) Do <91568955+dophsquare@users.noreply.github.com> Signed-off-by: Phuoc (Phu) Do <91568955+dophsquare@users.noreply.github.com> * origin/main: (126 commits) chore(ci): bump desktop smoke E2E timeout to 30 minutes (block#3409) release(chart): publish 0.1.7 (block#3393) feat(acp): steer claude-code and codex agents via _session/steering (block#3007) feat(desktop): apply WebKit rendering workarounds at startup on Linux (block#3271) fix(desktop): stabilize flaky DM expansion E2E ordering assertions (block#2004) docs(contributing): document the Linux system libraries just ci requires (block#3396) fix(desktop): paint community rail full height (block#3382) fix(acp): disable goose cron scheduler in managed agent children (block#3144) feat(desktop): add custom harness inline from agent dialogs (block#3252) chore(compose): remove stale typesense env vars (block#3332) feat(desktop): refine agent catalog sharing (block#2439) fix(desktop): keep drafts out of the Inbox All view (block#3217) docs: restructure DCO guidance into scannable subsection (block#3337) Unify mobile loading spinners (block#3314) fix(desktop): restore the inbox icon in the sidebar (block#3341) fix(desktop): gate codex-acp on a minimum supported version (block#3254) feat(cli): add users set-status command for NIP-38 profile status (block#3253) fix(composer): scope multiline block formatting (block#3246) feat(chart): add relay pod extension points (block#3322) Refine mobile attachment picking (block#3313) ...
Category: fix
User Impact: Composer block formatting now applies to the intended line or selection without collapsing multiline content.
Problem: Block formatting from a Shift+Enter line could convert the entire draft, selected visual lines could collapse into one list item, and code conversion could lose line breaks. Solution: Scope caret formatting to its hard-break-delimited line and normalize explicit selections for the destination block type while preserving neighboring content and visual line boundaries.
File changes
desktop/src/features/messages/lib/selectionBlockFormatting.ts
Scopes collapsed-caret block actions to the active visual line and normalizes multiline selections for lists and code blocks.
desktop/src/features/messages/lib/selectionBlockFormatting.test.mjs
Adds unit coverage for caret-line isolation across line positions and selection directions.
desktop/src/features/messages/ui/FormattingToolbar.tsx
Routes list, quote, and code-block actions through the selection-aware formatting transaction.
desktop/tests/e2e/composer-selection-formatting.spec.ts
Covers caret-only formatting, multiline list conversion, list-to-code conversion, preserved hard breaks, Markdown output, and backward selections.
Reproduction steps
Screenshots/Demos
Expected multiline code-block result: https://buzz.block.builderlab.xyz/media/d2e2668093af3b67d896a32e9799daccd236da9fc9e24ec56ddb4ebf7d01dd96.png