Skip to content

Add word-processor keyboard and mouse actions to docs editor#64

Merged
hackerwins merged 6 commits into
mainfrom
add-docs-keyboard-mouse-actions
Mar 22, 2026
Merged

Add word-processor keyboard and mouse actions to docs editor#64
hackerwins merged 6 commits into
mainfrom
add-docs-keyboard-mouse-actions

Conversation

@hackerwins

@hackerwins hackerwins commented Mar 22, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Add word boundary utility with CJK support for word-level navigation
  • Implement standard word-processor keyboard shortcuts: word-by-word movement (Opt/Ctrl+Arrow), word/line deletion (Opt/Ctrl+Backspace/Delete, Cmd+Backspace), document start/end navigation (Cmd+Up/Down, Ctrl+Home/End)
  • Add mouse interactions: double-click word select, triple-click paragraph select, Shift+Click selection extension
  • Add clipboard support: copy (Cmd+C), paste (Cmd+V), cut (Cmd+X) with multi-line paste
  • Add strikethrough toggle (Cmd+Shift+X) and clear formatting (Cmd+)

Test plan

  • pnpm verify:fast passes (all 1271 tests)
  • 21 new word-boundary unit tests
  • Manual: double-click selects word, triple-click selects paragraph
  • Manual: Opt+Arrow moves word-by-word, Cmd+Arrow moves to line/doc boundaries
  • Manual: copy/paste/cut works with multi-block selections
  • Manual: strikethrough renders correctly on canvas

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Strikethrough formatting and a clear-format command; visual rendering updated and trailing-space trimming improved.
    • Enhanced keyboard navigation and word-level editing (word-aware movement/deletion, line/document jumps).
    • Multi-click selection (double-click word, triple-click paragraph), refined cursor behavior on wrapped lines, and custom clipboard copy/cut/paste handling.
    • Word-boundary utilities powering more accurate cursor/selection behavior.
  • Documentation

    • Added keyboard & mouse interaction guide and implementation checklist.
  • Tests

    • Added unit tests for word-boundary navigation and selection.

Canvas-based document editor now supports standard text editing
shortcuts: word-by-word navigation (Opt/Ctrl+Arrow), word/line
deletion, double/triple click selection, Shift+Click, clipboard
(copy/paste/cut), document start/end navigation, strikethrough,
and clear formatting.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@coderabbitai

coderabbitai Bot commented Mar 22, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@hackerwins has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 16 minutes and 30 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bd6e18c6-d4b7-40af-b117-a9759a820090

📥 Commits

Reviewing files that changed from the base of the PR and between b16e8ba and 8fd99fe.

📒 Files selected for processing (4)
  • packages/docs/src/view/doc-canvas.ts
  • packages/docs/src/view/text-editor.ts
  • packages/docs/src/view/word-boundary.ts
  • packages/docs/test/view/word-boundary.test.ts
📝 Walkthrough

Walkthrough

Adds keyboard and mouse interaction features (word/line/document navigation, multi‑click selection, clipboard copy/cut/paste), word‑boundary utilities with CJK support, strikethrough style support and rendering, tests, and two new documentation notes.

Changes

Cohort / File(s) Summary
Documentation
docs/tasks/active/20260322-docs-keyboard-mouse-lessons.md, docs/tasks/active/20260322-docs-keyboard-mouse-todo.md
Added lessons-learned and implementation TODO docs describing keyboard/mouse behaviors and pending items.
Type System
packages/docs/src/model/types.ts
Added optional strikethrough?: boolean to InlineStyle and updated inlineStylesEqual() to compare it.
Rendering
packages/docs/src/view/doc-canvas.ts
Render signature updated to accept layout; renderRun() gained optional trimTrailingSpaces and now draws a 1px horizontal strikethrough when style.strikethrough is true.
Editor View
packages/docs/src/view/editor.ts
Updated paint() callsite to pass layout into docCanvas.render(...) (argument position shift).
Text Editor Core
packages/docs/src/view/text-editor.ts
Large additions: custom clipboard handlers (copy/cut/paste), click-count selection (double/triple click + shift-extend), modifier-aware navigation/deletion (word/mod/line/doc handlers), visual-line aware cursor movement and trimming, clear-formatting and strikethrough toggle, and many movement/delete helpers.
Word Boundary Utilities
packages/docs/src/view/word-boundary.ts
New module exporting findNextWordBoundary, findPrevWordBoundary, and getWordRange with classification for whitespace, word, punctuation, and CJK handling.
Tests
packages/docs/test/view/word-boundary.test.ts
New tests covering next/prev word boundaries and word-range behavior across Latin, punctuation, whitespace, and CJK scenarios.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant TextEditor
    participant WordBoundary
    participant Model
    participant Canvas

    User->>TextEditor: Alt+Right / Alt+Left (word navigation)
    TextEditor->>WordBoundary: findNextWordBoundary(text, offset) / findPrevWordBoundary(text, offset)
    WordBoundary-->>TextEditor: boundary offset
    TextEditor->>Model: update cursor/selection
    TextEditor->>Canvas: request render(layout, paginatedLayout, ...)
    Canvas-->>User: updated view
Loading
sequenceDiagram
    actor User
    participant TextEditor
    participant Clipboard
    participant Model
    participant Canvas

    User->>TextEditor: Cmd+C / Cmd+X (copy/cut)
    TextEditor->>Model: read selection plaintext
    Model-->>TextEditor: plaintext
    TextEditor->>Clipboard: write text/plain
    TextEditor->>Model: (on cut) snapshot + delete selection
    TextEditor->>Canvas: render updated document

    User->>TextEditor: Cmd+V (paste)
    TextEditor->>Clipboard: read text/plain
    Clipboard-->>TextEditor: plaintext
    TextEditor->>Model: delete selection, insert text (split into blocks if newline)
    TextEditor->>Canvas: render updated document
    Canvas-->>User: display pasted content
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Poem

🐰 I hopped through lines and leapt each word,
Double-clicked carrots, triple-clicked a herd,
A tiny strike through a letter so sly,
I copied the crumbs, then pasted the sky—
Hoppy edits from your rabbit ally!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title clearly and specifically summarizes the main change: adding word-processor keyboard and mouse actions to the docs editor, which aligns with all the file changes introducing keyboard shortcuts, mouse interactions, and formatting features.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch add-docs-keyboard-mouse-actions

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Mar 22, 2026

Copy link
Copy Markdown
Contributor

Verification: verify:self

Result: ✅ PASS in 88.2s

Lane Status Duration
sheets:build ✅ pass 12.3s
verify:fast ✅ pass 43.8s
frontend:build ✅ pass 11.9s
verify:frontend:chunks ✅ pass 0.3s
backend:build ✅ pass 4.0s
cli:build ✅ pass 1.6s
verify:entropy ✅ pass 14.3s

Verification: verify:integration

Result: ✅ PASS

Home/End and Cmd+Left/Right now move to the start/end of the
visual (wrapped) line instead of the paragraph. This matches
standard word processor behavior where long paragraphs wrap
into multiple visual lines. Cmd+Backspace also deletes to the
visual line start.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/docs/src/view/text-editor.ts (1)

700-723: ⚠️ Potential issue | 🟠 Major

Use rendered-line boundaries here, not block boundaries.

These helpers always target offset 0 / getBlockTextLength(block), so in a wrapped paragraph Cmd+Left/Right/Home/End and Cmd+Backspace jump or delete to the paragraph bounds instead of the current visual line. You already resolve page/line layout elsewhere in this file; reuse that to compute the active line’s start/end offsets.

Also applies to: 755-768

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/docs/src/view/text-editor.ts` around lines 700 - 723, handleHome and
handleEnd currently use block boundaries (offset 0 and getBlockTextLength) which
jumps to paragraph bounds; instead, use the editor's rendered-line/layout
helpers already used elsewhere in this file to compute the active visual line's
start and end offsets for the current cursor position and use those offsets when
constructing newPos, updating selection, moving the cursor, and requesting
render. Apply the same change to the other command-key handlers that use block
boundaries (the Cmd+Left/Cmd+Right/Cmd+Backspace handlers around the 755-768
region) so all command-line navigation/deletion uses rendered-line boundaries
rather than whole-block offsets.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/docs/src/view/text-editor.ts`:
- Around line 239-242: The issue is that mod and wordMod are both derived
incorrectly (mod = ctrlKey || metaKey; wordMod = altKey) causing Ctrl on
Windows/Linux to be treated like Meta on Mac; fix by separating platform-aware
modifiers: compute isMac (e.g. via navigator.platform/userAgent) and set mod =
isMac ? metaKey : ctrlKey and set wordMod = isMac ? altKey : ctrlKey so
Ctrl+Left/Right/Backspace/Delete on Windows/Linux uses word navigation correctly
while Mac keeps Option as the word modifier; update any uses of mod and wordMod
in the key handling code (the key/ctrlKey/metaKey/altKey references and the
Backspace/Arrow branches) to rely on these new values.
- Around line 239-243: The keyboard handler uses the raw KeyboardEvent.key so
Shift+letter yields uppercase and breaks single-letter shortcut matching; create
a normalized key variable (e.g., lowercase single-character keys) before the
switch and use that in all comparisons (replace uses of key in the switch with
the normalized variable), ensuring you handle non-single-character keys
unchanged; apply the same normalization at the other handler occurrence (the
block referenced around lines 352-356) and keep existing modifiers (mod,
wordMod) logic intact.

In `@packages/docs/src/view/word-boundary.ts`:
- Around line 35-40: categorize currently uses the ASCII-only /\w/ check which
misclassifies non-ASCII letters; update categorize (the function that returns
CharCategory and uses isCJK) to detect Unicode letters and digits by using
Unicode property escapes (match Unicode Letter, Mark and Number categories with
the 'u' flag) instead of /\w/, and return CharCategory.Word for those matches so
accented letters, combining marks and non-Latin scripts are treated as words.

---

Outside diff comments:
In `@packages/docs/src/view/text-editor.ts`:
- Around line 700-723: handleHome and handleEnd currently use block boundaries
(offset 0 and getBlockTextLength) which jumps to paragraph bounds; instead, use
the editor's rendered-line/layout helpers already used elsewhere in this file to
compute the active visual line's start and end offsets for the current cursor
position and use those offsets when constructing newPos, updating selection,
moving the cursor, and requesting render. Apply the same change to the other
command-key handlers that use block boundaries (the
Cmd+Left/Cmd+Right/Cmd+Backspace handlers around the 755-768 region) so all
command-line navigation/deletion uses rendered-line boundaries rather than
whole-block offsets.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e243630b-8a22-4676-958a-21ce8af01eb9

📥 Commits

Reviewing files that changed from the base of the PR and between fe1cc3e and a9d0da5.

📒 Files selected for processing (7)
  • docs/tasks/active/20260322-docs-keyboard-mouse-lessons.md
  • docs/tasks/active/20260322-docs-keyboard-mouse-todo.md
  • packages/docs/src/model/types.ts
  • packages/docs/src/view/doc-canvas.ts
  • packages/docs/src/view/text-editor.ts
  • packages/docs/src/view/word-boundary.ts
  • packages/docs/test/view/word-boundary.test.ts

Comment thread packages/docs/src/view/text-editor.ts Outdated
Comment thread packages/docs/src/view/text-editor.ts Outdated
Comment thread packages/docs/src/view/word-boundary.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (2)
packages/docs/src/view/text-editor.ts (2)

239-239: ⚠️ Potential issue | 🟠 Major

Normalize printable keys before the switch.

Line 239 uses the raw KeyboardEvent.key, so shifted letters arrive as uppercase. Mod+Shift+X will never match case 'x', and the same root cause affects any other shifted letter shortcut.

Minimal fix
-    const { key, ctrlKey, metaKey, shiftKey, altKey } = e;
+    const { ctrlKey, metaKey, shiftKey, altKey } = e;
+    const key = e.key.length === 1 ? e.key.toLowerCase() : e.key;
Does the DOM `KeyboardEvent.key` value for printable letter keys become uppercase when Shift is held, while non-printable keys such as `ArrowLeft` keep their standard names?

Also applies to: 352-356

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/docs/src/view/text-editor.ts` at line 239, The code reads the raw
KeyboardEvent.key into the local variable key (const { key, ctrlKey, metaKey,
shiftKey, altKey } = e) and then switches on it, which fails for shifted
printable letters; normalize printable single-character letter keys to a
canonical form before the switch (e.g., add a small helper normalizeKey(rawKey)
that returns rawKey.toLowerCase() when /^[A-Za-z]$/.test(rawKey) and otherwise
returns rawKey), then replace the destructured key or use const normKey =
normalizeKey(key) and switch on normKey; apply the same change to the other
shortcut-handling block that also switches on key (the region mentioned around
the later cases 352-356).

239-242: ⚠️ Potential issue | 🟠 Major

Split command shortcuts from navigation modifiers.

Line 240 still uses ctrlKey || metaKey for every special branch, while Line 242 hard-codes word navigation to altKey. On Windows/Linux that makes Ctrl+Left/Right and Ctrl+Backspace take the Cmd/line path, Ctrl+Delete fall back to plain delete, and Ctrl+Up/Down jump to the document ends. Please break this into separate shortcut vs. word/line/document modifiers instead of reusing mod here.

Also applies to: 253-327

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/docs/src/view/text-editor.ts` around lines 239 - 242, The handler
currently conflates shortcut modifier and navigation modifiers by using mod
(ctrlKey || metaKey) for shortcuts and hard-coding wordMod = altKey, causing
wrong behavior on Windows/Linux; change to three distinct booleans: shortcutMod
(ctrlKey || metaKey) for shortcut/command actions, wordMod (on Mac use altKey,
otherwise use ctrlKey) for word-level navigation, and line/document modifiers as
needed (e.g., shortcutMod or a dedicated lineMod) so Up/Down and
Delete/Backspace branches use the correct modifier. Update the variables
replacing mod and wordMod (the existing mod and wordMod in the key handler) and
apply the same split to the subsequent key-branch logic referenced in the block
covering lines 253-327 so each branch checks the appropriate modifier boolean.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/docs/src/view/text-editor.ts`:
- Around line 614-618: The current handleLineBackspace path calls
this.doc.deleteBackward(pos) unconditionally which assumes a previous block
exists and can dereference blocks[-1]; add a guard that detects document-start
(e.g., pos.offset === 0 && pos.blockIndex === 0) and no-ops instead of calling
this.doc.deleteBackward(), so only call invalidateLayout() and deleteBackward
when there is a previous block to merge; apply the same guard to the other
similar site around lines 769-771, or alternatively harden Doc.deleteBackward()
to safely handle offsets at the very start of the document — reference
handleLineBackspace, this.doc.deleteBackward, this.cursor.moveTo, and
invalidateLayout when making the fix.
- Around line 795-803: The clearFormatting() command mutates the document via
this.doc.applyInlineStyle(range, ...) without recording an undo snapshot; call
the editor snapshot method (e.g., this.saveSnapshot()) immediately after
verifying the selection (after checking this.selection.hasSelection() and
this.selection.range but before calling this.doc.applyInlineStyle) so the change
is pushed to the undo stack; update clearFormatting() to invoke saveSnapshot()
prior to applyInlineStyle() and then proceed with the existing style clears.
- Around line 955-965: The caret-at-wrap bug is caused by treating pos.offset ==
lineEnd as belonging to the previous visual line; in the loop over lb.lines and
line.runs (using charsBefore, lineChars, lineStart, lineEnd, pos.offset) change
the boundary check to treat the end offset as belonging to the next visual line
— e.g. replace the current condition (pos.offset >= lineStart && pos.offset <=
lineEnd) with a boundary-safe comparison that uses strict less-than for the end
(pos.offset >= lineStart && pos.offset < lineEnd) or explicitly skip when
pos.offset === lineEnd so Home/End/Cmd+Backspace operate on the next wrapped
line.

---

Duplicate comments:
In `@packages/docs/src/view/text-editor.ts`:
- Line 239: The code reads the raw KeyboardEvent.key into the local variable key
(const { key, ctrlKey, metaKey, shiftKey, altKey } = e) and then switches on it,
which fails for shifted printable letters; normalize printable single-character
letter keys to a canonical form before the switch (e.g., add a small helper
normalizeKey(rawKey) that returns rawKey.toLowerCase() when
/^[A-Za-z]$/.test(rawKey) and otherwise returns rawKey), then replace the
destructured key or use const normKey = normalizeKey(key) and switch on normKey;
apply the same change to the other shortcut-handling block that also switches on
key (the region mentioned around the later cases 352-356).
- Around line 239-242: The handler currently conflates shortcut modifier and
navigation modifiers by using mod (ctrlKey || metaKey) for shortcuts and
hard-coding wordMod = altKey, causing wrong behavior on Windows/Linux; change to
three distinct booleans: shortcutMod (ctrlKey || metaKey) for shortcut/command
actions, wordMod (on Mac use altKey, otherwise use ctrlKey) for word-level
navigation, and line/document modifiers as needed (e.g., shortcutMod or a
dedicated lineMod) so Up/Down and Delete/Backspace branches use the correct
modifier. Update the variables replacing mod and wordMod (the existing mod and
wordMod in the key handler) and apply the same split to the subsequent
key-branch logic referenced in the block covering lines 253-327 so each branch
checks the appropriate modifier boolean.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4ec3e966-8ea6-45ec-a74c-4281f66c540d

📥 Commits

Reviewing files that changed from the base of the PR and between a9d0da5 and be4d216.

📒 Files selected for processing (1)
  • packages/docs/src/view/text-editor.ts

Comment thread packages/docs/src/view/text-editor.ts
Comment thread packages/docs/src/view/text-editor.ts
Comment thread packages/docs/src/view/text-editor.ts
Cursor now skips trailing whitespace at line wrap boundaries,
matching Google Docs behavior. Moving right past the last visible
character on a wrapped line jumps to the next line start. Moving
left from a line start skips back over trailing spaces. End key
also stops at the last non-space character on wrapped lines.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/docs/src/view/text-editor.ts`:
- Around line 962-991: The skipLineWrapSpaces and skipLineWrapSpacesBackward
functions read line boundaries from this.getLayout(), which can be stale right
after edits; update the layout for the specific block synchronously before using
it (e.g., call the existing synchronous relayout/recompute method for a single
block such as this.relayoutBlock(blockId) or
this.recomputeLayoutForBlock(blockId) — whichever exists in the codebase — and
fall back to the current getLayout() only after that) so the functions use
up-to-date line boundaries; apply the same change in both skipLineWrapSpaces and
skipLineWrapSpacesBackward and reference getLayout, skipLineWrapSpaces,
skipLineWrapSpacesBackward, and the blockId parameter to locate where to inject
the synchronous relayout call.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b4daa78c-8e40-4743-bc9b-1577c3bd933b

📥 Commits

Reviewing files that changed from the base of the PR and between be4d216 and 9aed65b.

📒 Files selected for processing (1)
  • packages/docs/src/view/text-editor.ts

Comment thread packages/docs/src/view/text-editor.ts Outdated
hackerwins and others added 3 commits March 22, 2026 16:20
Trailing whitespace at the end of wrapped lines is no longer
rendered, matching Google Docs behavior. The cursor also renders
at the next line start when positioned at a trailing space offset
on a wrapped line.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
The trailing space skip and visual trimming logic made cursor
behavior worse. Removing it to re-implement properly in a
follow-up PR.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
- Normalize key to lowercase for Shift+letter shortcuts
- Add platform-aware modifier detection (Mac vs Win/Linux)
- Guard document-start before calling deleteBackward()
- Add undo snapshot to clearFormatting()
- Fix wrap-boundary offset in getVisualLineRange
- Use Unicode property escapes for word boundary

Co-Authored-By: Claude Opus 4.6 <[email protected]>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/docs/src/view/text-editor.ts (1)

410-418: Consider calling invalidateLayout() once before the loop.

Currently invalidateLayout() is called inside the loop for each new line, but a single call before the loop would suffice since it just clears the cache.

♻️ Optional optimization
+    if (lines.length > 1) {
+      this.invalidateLayout();
+    }
     for (let i = 0; i < lines.length; i++) {
       if (i > 0) {
         // Create a new block for each line after the first
-        this.invalidateLayout();
         const newBlockId = this.doc.splitBlock(
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/docs/src/view/text-editor.ts` around lines 410 - 418, Move the call
to invalidateLayout() out of the per-line loop and invoke it once before
iterating lines (e.g., if lines.length > 1) to avoid repeated cache clears;
remove the invalidateLayout() call that currently sits inside the for loop that
creates new blocks via this.doc.splitBlock and moves the cursor with
this.cursor.moveTo so behavior remains the same but with a single cache
invalidation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/docs/src/view/text-editor.ts`:
- Around line 390-398: The handleCut implementation may delete selection even
when e.clipboardData is null; update handleCut to first attempt to write the
selected text to the clipboard (use e.clipboardData when present, otherwise fall
back to navigator.clipboard.writeText), and only proceed to saveSnapshot(),
deleteSelection(), and requestRender() after confirming the clipboard write
succeeded (handle promise rejections and abort deletion on failure); reference
handleCut, this.selection.getSelectedText, e.clipboardData,
navigator.clipboard.writeText, saveSnapshot, deleteSelection, and requestRender
to locate and protect the critical path.

---

Nitpick comments:
In `@packages/docs/src/view/text-editor.ts`:
- Around line 410-418: Move the call to invalidateLayout() out of the per-line
loop and invoke it once before iterating lines (e.g., if lines.length > 1) to
avoid repeated cache clears; remove the invalidateLayout() call that currently
sits inside the for loop that creates new blocks via this.doc.splitBlock and
moves the cursor with this.cursor.moveTo so behavior remains the same but with a
single cache invalidation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b52cae0b-8a13-40ce-b253-ddd523c7ed61

📥 Commits

Reviewing files that changed from the base of the PR and between 9aed65b and b16e8ba.

📒 Files selected for processing (3)
  • packages/docs/src/view/doc-canvas.ts
  • packages/docs/src/view/editor.ts
  • packages/docs/src/view/text-editor.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/docs/src/view/doc-canvas.ts

Comment on lines +390 to +398
private handleCut = (e: ClipboardEvent): void => {
if (!this.selection.hasSelection()) return;
e.preventDefault();
const text = this.selection.getSelectedText(this.getLayout());
e.clipboardData?.setData('text/plain', text);
this.saveSnapshot();
this.deleteSelection();
this.requestRender();
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

handleCut may silently lose text if clipboard write fails.

If e.clipboardData is null (rare but possible in certain browser contexts), the optional chaining silently skips the clipboard write, but the subsequent lines still delete the selection. The user would lose their text without it being copied.

🛡️ Proposed fix to guard against clipboard failure
 private handleCut = (e: ClipboardEvent): void => {
   if (!this.selection.hasSelection()) return;
   e.preventDefault();
+  if (!e.clipboardData) return;
   const text = this.selection.getSelectedText(this.getLayout());
-  e.clipboardData?.setData('text/plain', text);
+  e.clipboardData.setData('text/plain', text);
   this.saveSnapshot();
   this.deleteSelection();
   this.requestRender();
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private handleCut = (e: ClipboardEvent): void => {
if (!this.selection.hasSelection()) return;
e.preventDefault();
const text = this.selection.getSelectedText(this.getLayout());
e.clipboardData?.setData('text/plain', text);
this.saveSnapshot();
this.deleteSelection();
this.requestRender();
};
private handleCut = (e: ClipboardEvent): void => {
if (!this.selection.hasSelection()) return;
e.preventDefault();
if (!e.clipboardData) return;
const text = this.selection.getSelectedText(this.getLayout());
e.clipboardData.setData('text/plain', text);
this.saveSnapshot();
this.deleteSelection();
this.requestRender();
};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/docs/src/view/text-editor.ts` around lines 390 - 398, The handleCut
implementation may delete selection even when e.clipboardData is null; update
handleCut to first attempt to write the selected text to the clipboard (use
e.clipboardData when present, otherwise fall back to
navigator.clipboard.writeText), and only proceed to saveSnapshot(),
deleteSelection(), and requestRender() after confirming the clipboard write
succeeded (handle promise rejections and abort deletion on failure); reference
handleCut, this.selection.getSelectedText, e.clipboardData,
navigator.clipboard.writeText, saveSnapshot, deleteSelection, and requestRender
to locate and protect the critical path.

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.

1 participant