githubnext/chopinPublic

Let's compose rich plans together

Stop typing on a selected decision card from landing at the document start #387

MergedMaggieAppletonmerged 1 commit intomainfromrefs/pull/387/head
  1. MA

    ## Why

    `e2e/decision-marker.e2e.ts` "typing with a visible decision card selected never lands at the document start" has been failing on and off (`zThe rollout…`). The flake comes from a real bug that users can hit: typed text can land at the top of the document.

    Root cause, confirmed by tracing Lexical selection, DOM selection and Lexical's `_inputState` on every keydown, beforeinput and selectionchange:

    1. Lexical's `pointerdown` handler sets `isSelectionChangeFromMouseDown = true`. Only the next `selectionchange` clears it. 2. ArrowDown onto a visible card gives a `NodeSelection`. Lexical removes every DOM range, and Chrome parks its caret at the start of the root. 3. That caret move fires a `selectionchange`. If the flag is still set, Lexical treats it as a click back onto text and builds a `RangeSelection` from the DOM caret: document start. 4. From then on `BEFORE_INPUT_COMMAND` no longer sees a node selection, so the characters go in at the top. This gives `xyz…`, `yz…` or `z…`, depending on when the selectionchange arrives.

    The flag goes stale whenever no `selectionchange` follows the click: - **Users:** clicking where the caret already is fires no `selectionchange`. Click the end of a paragraph twice, wait, press ArrowDown onto a card, then type: before this fix, about 1 in 3 human-paced runs put the text at the document start. - **CI:** Playwright presses ArrowDown in the same frame as the click, ahead of the click's queued `selectionchange`.

    ## What changed

    - `DecoratorSelectionPlugin` registers a `KEY_DOWN_COMMAND` handler at critical priority. It clears `isSelectionChangeFromMouseDown`, because once a key is pressed the next selection change belongs to the keyboard, not the earlier pointer press. It returns `false`, so nothing else changes. - Unit test: a key press clears the flag. - New E2E stress test: the human-paced repeat-click path, run 5 times in one test with frame settles between steps.

    ## Selection flow

    ![Selection change bug and keydown fix](https://github.com/user-attachments/assets/a03faa69-2cc2-444f-ba17-cf8c2931ad39)

    ## Testing

    Run on my own server (port 8896, own database), using the Playwright suite with a local config:

    | Run | Before fix | After fix | | --- | --- | --- | | existing test, `--repeat-each 40`, 4 workers | **25/40 failed** | — | | existing test, `--repeat-each 10` / `30` | **8/10 failed** | **0/30 failed** | | new repeat-click test, `--repeat-each 10` / `30` | **10/10 failed** | **0/30 failed** | | instrumented same-frame script, 12 / 15 runs | 4/12 failed | 0/15 failed | | instrumented human-paced script, 12 / 15 runs | 4/12 failed | 0/15 failed | | same-frame script with `Emulation.setCPUThrottlingRate 4`, 20 runs | — | 0/20 failed |

    - `bun test packages/editor`: 504 pass - `bun run ci`, `bun run types`: pass - Local `bun run e2e` is suspended for this audit (shared ports are occupied), so CI runs the full browser suite.

    Note: `_inputState` is a typed but underscore-prefixed Lexical field. The comment in `decorator-selection.tsx` explains why we touch it.

    🤖 Generated with [Claude Code](https://claude.com/claude-code)

  2. MaggieAppletonhead-ref-force-pushed 37e04cb67e3dlast month on GitHub

  3. MaggieAppletonmerged 6caaa51886dblast month on GitHub

  4. MaggieAppletonclosedlast month on GitHub

  5. MaggieAppletonhead-ref-deletedlast month on GitHub