feat(chat): add inline paste token composer - #8310
Conversation
998b4c4 to
3aed02a
Compare
First Principles Review (Fable 5.1, fork) — 🟡 CONCERNSPremise-level review of Confirmed: no prior Lexical or First-Principles-Verdict: CONCERNS Honestly-framed incremental migration, but the whole slice ships behind What this change shipsIntent: fix the textarea/mirror paste-highlight detach by representing pastes as real inline editor nodes — an ADDITION (author explicitly declines
Watch
Subtractions
[FIRST-PRINCIPLES-REVIEWED] 64b5b67 |
Design Review (Fable 5.1, fork) — 🟡 CONCERNSDesign-level review of Verified two things before judging: Design assessment: this is a well-structured incremental migration — opt-in off by default, textarea kept as the declared rollback target with a lazy-load error boundary and recovery notice, paste policy and IME latch factored into shared modules, and an engine-neutral Design-Verdict: CONCERNS Sound, reversible migration-slice design; the real risk is two full composer implementations that must stay behavior-identical for an open-ended window with no parity harness pinning them. Watch
Suggestions
[DESIGN-REVIEWED] 64b5b67 |
UX Review (Fable 5.1, fork) — 🟡 CONCERNSUX-level review of This is the fork lane: PR is from owner Reconcile — user-visible surface this diff adds (all gated behind the default-off
None appears in a materialized screenshot; no first-time reader has seen any of them. The default textarea path (the production path) is behaviorally unchanged by this diff. Checks against the read-off BLOCK exits: no blind read → the primary-control exit cannot fire. The textarea↔Lexical substitution is gated on an opt-in flag / async lazy-load failure (lens-4 lifecycle), not a runtime flip of a persistent element the user identified — not a lens-13 hard swap. The fallback notice asserts what happened and the recovered state (draft unchanged) — it neither hedges nor is content-free. So no BLOCK; verdict is capped at CONCERNS by the evidence gaps. UX-Verdict: CONCERNS Every new user-visible control ships unseen — fork lane has no screenshots and no blind read, so comprehension of the paste-token chip and fallback notice is unverified. Watch
Evidence gaps
[UX-REVIEWED] 64b5b67 |
Opus 4.8 Review (fork) — ✅ no blocking findingsReviewed |
GPT 5.6 Review (fork) — ✅ no blocking findingsReviewed Review detailsNo findings. |
3aed02a to
bd8225a
Compare
Fix: |
bd8225a to
d803aad
Compare
Fix: the plugin now uses the repository's shared IME latch ( |
d803aad to
65ef15d
Compare
Fix: the boundary now logs the error and component stack via the repository's boundary convention ( |
65ef15d to
a025deb
Compare
Fix: |
a025deb to
6e1ebc7
Compare
bolichen97
left a comment
There was a problem hiding this comment.
Tech Lead review: not approving.
Security and i18n are clean (no innerHTML/dangerouslySetInnerHTML, paste-derived label is numeric-only, i18n gate green). Three substantive issues:
Fixes #8309would auto-close a still-open bug: zero production callers currently setlexicalComposer, so this PR does not actually exercise the fixed path in production yet. Please remove the auto-close linkage or confirm a production caller.openTriggernow inserts@/$at the caret instead of appending, for every current user on the DEFAULT textarea path — this contradicts the PR's own stated claim that the textarea path is an untouched rollback target. This is an undeclared behavior change.PasteTokenNodereuses theexpand_pasteaction string for what is actually a preview-only action; please use a distinct, accurately-named string.
Also structurally blocked: this touches 5 non-exempt website/src/** files, which requires a UX-ruleset (team) approval on top of a general approval — neither exists yet.
6e1ebc7 to
2568209
Compare
Add a default-off Lexical composer path with atomic inline paste nodes, editor-neutral selection and focus controls, shared clipboard policy, lazy-load rollback, and focused regression coverage while preserving the textarea fallback.
2568209 to
64b5b67
Compare
|
@bolichen97 Thank you for the review — all three substantive issues are addressed in
On the structural gate: understood on the UX-ruleset approval — the branch is ready for that review whenever the team can take it. On the flag-enablement question raised here and by the First Principles lane, we're deciding between enabling |
Problem / Motivation
Collapsed paste markers are rendered in a native textarea while their background and hover hit area come from a separate mirrored div. Browser wrapping, resize, and scroll behavior can make those two layouts diverge, detaching the highlight and preview from the marker text.
Why it matters
A detached paste token is visually misleading and difficult to target. Additional mirror synchronization cannot guarantee parity because textarea layout is browser-owned.
What changed (motivation → approach → change)
This PR adds a default-off Lexical composer path that represents collapsed pastes as real atomic inline editor nodes. The existing serialized
[ Paste #N · M lines ]value andPasteBlock[]sidecar remain canonical, so textarea rollback and stored drafts remain compatible.The slice includes:
PasteTokenNodemarkComposerResize), so the transcript's bounce fix from perf(chat): reachable archived history and stable phone scrolling #7916 holds when the editor grows under typingNo production caller enables
lexicalComposer; the textarea remains the default and rollback path while broader rollout evidence is gathered.Tests
namespaceconfig identifier is exempted ineslint.i18n.config.jswith a measured rationale (single occurrence, no baseline handed back)Manual verification
A real Chromium harness verified that inline nodes remain inside the editor during wrapping, narrowing, and internal scrolling, previews anchor to the actual node rectangle, large pastes create new atomic nodes, no mirror is rendered, and no browser Lexical errors are emitted.
Screenshots / video
Before — the textarea/mirror path with the highlight detached from the marker text (identifying regions redacted):
After — the paste token as a real inline Lexical node laid out with the surrounding text:
After — the preview anchored to the token's own DOM element:
Related Issues
Related to #8309 — deliberately NOT
Fixes: this slice ships default-off with no production caller, so the defect remains user-visible until a rollout PR enableslexicalComposer; auto-closing the bug here would be dishonest.Pattern harvest
Rule candidate: review-prompt
Pattern: Do not style native textarea substrings through a separately laid-out mirror when alignment must survive wrapping and scrolling; use one editor-owned inline node.
Checklist
feat|fix|docs|refactor|perf|test|chore|ci|build|revert: ...)