Skip to content

fix(chat): preserve pasted user message newlines - #353

Open
AlphaCatMeow wants to merge 3 commits into
Stack-Cairn:mainfrom
AlphaCatMeow:codex/fix-paste-newline-serialization
Open

fix(chat): preserve pasted user message newlines#353
AlphaCatMeow wants to merge 3 commits into
Stack-Cairn:mainfrom
AlphaCatMeow:codex/fix-paste-newline-serialization

Conversation

@AlphaCatMeow

@AlphaCatMeow AlphaCatMeow commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Linked issue

Closes #352

Depends-On: none
Stack-Root: #353

Summary

  • Normalize CRLF/CR to LF without trimming or moving logical newlines.
  • Insert pasted plaintext as one safely escaped, undoable contenteditable operation and serialize DIV/P/placeholder BR nodes without double-counting blank lines.
  • Preserve leading/trailing newlines through draft, outbound payload, uploads metadata, history/replay, and user-message display.
  • Add a user-only trailing-LF visual anchor so Chromium allocates the final line box without changing DOM text or assistant/tool Markdown semantics.
  • Keep desktop GUI and Gateway WebUI behavior mirrored.

Change scope

  • Modules: agent-gui React UI, Gateway WebUI, mirror verification, frontend tests
  • Key paths:
    • crates/agent-gui/src/components/chat/MentionComposer.tsx
    • crates/agent-gui/src/lib/chat/composerText.ts
    • crates/agent-gui/src/lib/chat/messages/userMessageContent.tsx
    • crates/agent-gateway/web/src/components/chat/MentionComposer.tsx
    • crates/agent-gateway/web/src/lib/chat/composerText.ts
    • crates/agent-gateway/web/src/lib/chat/userMessageContent.tsx
    • mirrored Node and real-browser pipeline fixtures/tests

No Rust, Go, protocol, database, dependency, lockfile, or generated-code changes.

Rebase and diff audit

  • Rebased without conflicts onto latest upstream/main@00a2c6fc on 2026-08-06.
  • Final topology is exactly three PR commits on that baseline (upstream/main...HEAD = 0 behind / 3 ahead).
  • Original and rebased implementation/docs patches are range-diff equivalent; the style commit only adds the post-rebase worklog/acceptance record beyond its original formatting patch.
  • Original and rebased product scope remains 23 files. GatewayApp.tsx, useSendChatTurn.ts, and scripts/mirror-manifest.json were explicitly audited to preserve current-main behavior while adding only newline fidelity support.
  • The accepted product tree at bb060885 and final PR head differ only in docs/worklog/paste-newline-serialization.md.

Screenshots / preview

Production-module runtime preview for alpha\n\nbeta:

Stage GUI Gateway WebUI
Clipboard logical newlines 2 2
Composer logical newlines 2 2
Outbound logical newlines 2 2
History/replay logical newlines 2 2
User bubble DOM logical newlines 2 2
User bubble visual lines 3 3
white-space pre-wrap pre-wrap

The attached PNG below uses the actual production composer and UserMessageContent modules and shows the five-stage escaped text/counts, sent bubble, and history/reconnect replay without user session data.

Verification

  • GUI production build/typecheck: passed
  • Gateway WebUI production build/typecheck: passed
  • GUI focused paste-pipeline + user-bubble tests: 19/19 passed
  • Gateway WebUI focused paste-pipeline tests: 5/5 passed
  • Gateway WebUI full tests: 498/498 passed
  • GUI full tests: 1404/1409 passed; all 5 failures reproduced exactly on a fresh non-Worktree snapshot of upstream/main@00a2c6fc:
    • 2 mention composer selection source-extractor CRLF assumptions
    • 2 mention refetch source-extractor CRLF assumptions
    • 1 existing Rust/TypeScript provider usage preset byte comparison
  • Full lint baseline comparison on the same Windows CRLF checkout:
    • GUI current: 421 errors / 358 warnings / 9 infos; latest-main baseline: 424 / 358 / 9
    • WebUI current: 290 errors / 310 warnings / 10 infos; latest-main baseline: 292 / 310 / 10
    • changed production files pass targeted lint with no errors; normalized LF Git blobs pass Biome checks with exit 0
  • Actual Chromium production-module matrix: passed for LF/CRLF/CR; zero/single/multiple blank lines; leading/trailing newlines; pure whitespace; rich + plaintext clipboard; Markdown; Unicode/emoji; 40,002-character input; undo/redo; repeat send; cancellation; reload/history replay; and GUI ↔ WebUI replay
  • Same-HEAD Tauri desktop runtime: Rust dev build 747/747, current-workspace binary launched and responsive; user explicitly accepted the complete manual matrix on 2026-08-06
  • node scripts/check-mirror.mjs: 116 files passed
  • git diff --check upstream/main..HEAD: passed
  • No requested validation was skipped.
2026-08-01_10-20-53

Open PR overlap

Pre-submit checklist

  • A requirement issue is linked.
  • Rebased onto the latest target baseline with no merge conflicts.
  • The change is focused, with no unrelated modifications.
  • No secrets, tokens, personal data, generated assets, or local runtime files are included.
  • Worklog and behavior tests are updated.
  • Same-HEAD manual acceptance is complete.

@AlphaCatMeow
AlphaCatMeow marked this pull request as ready for review August 1, 2026 02:13
@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

PR governance checks passed. Awaiting human review.

@AlphaCatMeow
AlphaCatMeow marked this pull request as draft August 1, 2026 02:14
Normalize logical line endings once across paste, draft, send, history, and user bubble rendering. Add mirrored GUI/WebUI pipeline coverage for DOM serialization, replay, cross-client parity, and visual blank-line spacing.
@AlphaCatMeow
AlphaCatMeow force-pushed the codex/fix-paste-newline-serialization branch from bcbd4a5 to 8d74b7c Compare August 5, 2026 17:08
@AlphaCatMeow
AlphaCatMeow marked this pull request as ready for review August 5, 2026 17:10
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.

[Bug] Preserve logical newlines across paste, serialization, and user bubbles

1 participant