Fix docs editor issues and add peer selection highlighting#79
Conversation
📝 WalkthroughWalkthroughAdds peer selection propagation and rendering, extracts selection-rect computation into reusable helpers, introduces visual-line detection for wrapped lines, adjusts vertical cursor movement and Hangul word-boundary behavior, and updates editor/store/frontend types and callbacks to include selection ranges alongside cursor positions. Changes
Sequence DiagramsequenceDiagram
participant User as Local User
participant Editor as Editor
participant Store as YorkieDocStore
participant Presence as Presence/Broadcast
participant Frontend as DocsView
participant Canvas as DocCanvas
User->>Editor: Move cursor / change selection
Editor->>Editor: computeSelectionRects(range)
Editor->>Editor: findVisualLine(...) (vertical logic)
Editor->>Store: updateCursorPos(pos, selection)
Store->>Presence: set activeCursorPos & activeSelection
Presence-->>Frontend: broadcast peer presence
Frontend->>Editor: receive peer cursor+selection
Editor->>Editor: build peerSelections list
Editor->>Canvas: render(..., peerSelections, localSelection)
Canvas->>Canvas: draw peer selections (behind)
Canvas->>Canvas: draw local selection (on top)
Canvas-->>User: visual update
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
packages/docs/test/view/visual-line.test.ts (1)
52-55: Consider adding an out-of-bounds offset test.The current tests don't verify behavior when
offsetexceeds the block's character count. IffindVisualLineshould handle this gracefully (e.g., clamp to last line), a test would document that behavior.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/docs/test/view/visual-line.test.ts` around lines 52 - 55, Add a test in visual-line.test.ts that calls findVisualLine with an offset greater than the block's length (e.g., offset beyond 25) to assert graceful handling: expect it to clamp to the last visual line (use the same lb and blockId: 'b1') and return lineIndex equal to the last line, totalLines unchanged, lineStart at the last line's start and lineEnd equal to the block's length; this documents and verifies findVisualLine's out-of-bounds behavior.packages/docs/src/view/visual-line.ts (2)
36-48: Fallback code recomputes values already available in the loop.The fallback block recalculates
lastStartby iterating through all lines again, but this value equalscharsBeforeat loop exit. Consider capturing it before the loop ends or returning early to avoid this redundancy.♻️ Proposed simplification
let charsBefore = 0; + let lastLineStart = 0; + let lastLineEnd = 0; for (let i = 0; i < lb.lines.length; i++) { let lineChars = 0; for (const run of lb.lines[i].runs) { lineChars += run.charEnd - run.charStart; } const lineStart = charsBefore; const lineEnd = charsBefore + lineChars; + lastLineStart = lineStart; + lastLineEnd = lineEnd; const isLastLine = i === lb.lines.length - 1; if (pos.offset >= lineStart && (pos.offset < lineEnd || (isLastLine && pos.offset <= lineEnd))) { return { lineIndex: i, totalLines: lb.lines.length, lineStart, lineEnd }; } charsBefore = lineEnd; } // Fallback: last line - const lastLine = lb.lines.length - 1; - let lastStart = 0; - for (let i = 0; i < lastLine; i++) { - for (const run of lb.lines[i].runs) { - lastStart += run.charEnd - run.charStart; - } - } - let lastChars = 0; - for (const run of lb.lines[lastLine].runs) { - lastChars += run.charEnd - run.charStart; - } - return { lineIndex: lastLine, totalLines: lb.lines.length, lineStart: lastStart, lineEnd: lastStart + lastChars }; + return { lineIndex: lb.lines.length - 1, totalLines: lb.lines.length, lineStart: lastLineStart, lineEnd: lastLineEnd }; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/docs/src/view/visual-line.ts` around lines 36 - 48, The fallback recomputes lastStart by iterating lines again; instead use the accumulated value already tracked (charsBefore) when the loop exits or return early from the loop where you detect the last line. Update the logic in the visual line calculation to set lastStart = charsBefore (or return { lineIndex: lastLine, totalLines: lb.lines.length, lineStart: charsBefore, lineEnd: charsBefore + lastChars }) instead of recomputing over lb.lines, referencing the variables lb.lines, lastLine, lastChars, lastStart and the existing charsBefore accumulator in the surrounding function.
30-34: Consider simplifying the boundary condition.The condition on line 30 is correct but complex. A brief inline comment explaining the inclusive end check for the last line would improve readability.
const isLastLine = i === lb.lines.length - 1; + // Last line uses inclusive end (<=) since cursor can be at text end if (pos.offset >= lineStart && (pos.offset < lineEnd || (isLastLine && pos.offset <= lineEnd))) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/docs/src/view/visual-line.ts` around lines 30 - 34, The if-condition comparing pos.offset against lineStart/lineEnd is correct but complex; simplify it by making the end check uniformly use < lineEnd for normal lines and <= lineEnd only when isLastLine (e.g., compute a local maxOffset = isLastLine ? lineEnd : lineEnd - 1 or use a boolean endInclusive = isLastLine and then test pos.offset <= maxOffset), and add a short inline comment by the check explaining that the last line uses an inclusive end check to allow offsets equal to lineEnd; target the existing condition that uses pos.offset, lineStart, lineEnd, and isLastLine in this file to apply the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/tasks/active/20260325-docs-arrow-pixel-accuracy-todo.md`:
- Around line 1-44: Create a paired lessons file named
docs/tasks/active/20260325-docs-arrow-pixel-accuracy-lessons.md and update the
active tasks index to replace the "-" in the Lessons column with a link to that
file; in the lessons file summarize the bug (cursor misaligned due to
paginatedPixelToPosition using uniform charWidth while getPixelForPosition uses
ctx.measureText), state the chosen approach (prefer Option 2: add charOffsets:
number[] to LayoutRun and use it in paginatedPixelToPosition with binary
search), list implementation steps (add charOffsets during layout build, update
pagination.test.ts with proportional-width tests, and verify Arrow Up/Down and
mouse click positioning), and note any trade-offs and testing notes for
reviewers.
---
Nitpick comments:
In `@packages/docs/src/view/visual-line.ts`:
- Around line 36-48: The fallback recomputes lastStart by iterating lines again;
instead use the accumulated value already tracked (charsBefore) when the loop
exits or return early from the loop where you detect the last line. Update the
logic in the visual line calculation to set lastStart = charsBefore (or return {
lineIndex: lastLine, totalLines: lb.lines.length, lineStart: charsBefore,
lineEnd: charsBefore + lastChars }) instead of recomputing over lb.lines,
referencing the variables lb.lines, lastLine, lastChars, lastStart and the
existing charsBefore accumulator in the surrounding function.
- Around line 30-34: The if-condition comparing pos.offset against
lineStart/lineEnd is correct but complex; simplify it by making the end check
uniformly use < lineEnd for normal lines and <= lineEnd only when isLastLine
(e.g., compute a local maxOffset = isLastLine ? lineEnd : lineEnd - 1 or use a
boolean endInclusive = isLastLine and then test pos.offset <= maxOffset), and
add a short inline comment by the check explaining that the last line uses an
inclusive end check to allow offsets equal to lineEnd; target the existing
condition that uses pos.offset, lineStart, lineEnd, and isLastLine in this file
to apply the change.
In `@packages/docs/test/view/visual-line.test.ts`:
- Around line 52-55: Add a test in visual-line.test.ts that calls findVisualLine
with an offset greater than the block's length (e.g., offset beyond 25) to
assert graceful handling: expect it to clamp to the last visual line (use the
same lb and blockId: 'b1') and return lineIndex equal to the last line,
totalLines unchanged, lineStart at the last line's start and lineEnd equal to
the block's length; this documents and verifies findVisualLine's out-of-bounds
behavior.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 762d9114-77f2-4823-9692-472ca34f47ca
📒 Files selected for processing (18)
docs/tasks/README.mddocs/tasks/active/20260325-docs-arrow-pixel-accuracy-todo.mddocs/tasks/archive/2026/03/20260324-pr72-review-fixes-todo.mddocs/tasks/archive/2026/03/20260325-docs-editor-improvements-todo.mddocs/tasks/archive/README.mdpackages/docs/src/view/doc-canvas.tspackages/docs/src/view/editor.tspackages/docs/src/view/peer-cursor.tspackages/docs/src/view/selection.tspackages/docs/src/view/text-editor.tspackages/docs/src/view/visual-line.tspackages/docs/src/view/word-boundary.tspackages/docs/test/view/visual-line.test.tspackages/docs/test/view/word-boundary.test.tspackages/frontend/src/app/docs/docs-view.tsxpackages/frontend/src/app/docs/yorkie-doc-store.tspackages/frontend/src/components/ui/sidebar.tsxpackages/frontend/src/types/users.ts
There was a problem hiding this comment.
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/editor.ts`:
- Line 41: The onCursorMove callback currently narrows position objects to
{blockId, offset} and loses lineAffinity; update the onCursorMove signature to
accept and forward the full position/selection shape (including lineAffinity)
for both the single-pos argument and the optional selection {anchor, focus} so
downstream code can rebroadcast accurate visual-line-aware carets; change any
related helper invocations (selection/text-editor helpers that consume
onCursorMove payloads) to expect and propagate position objects with
lineAffinity rather than the reduced shape so wrap-boundary offsets resolve to
the correct visual line.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: ceded2a8-f4f5-46b2-bfe1-f8a9e42dc5a9
📒 Files selected for processing (1)
packages/docs/src/view/editor.ts
The Docs editor canvas has a fixed 816px width (Letter paper size), which prevented the SidebarInset flex item from shrinking when the viewport narrowed. Adding min-w-0 allows the flex item to shrink below its min-content size, matching the Sheets behavior. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Hangul syllables (U+AC00-U+D7AF) were classified as CJK ideographs,
causing word jump to stop at every character. Removed Hangul from the
CJK range so Korean text is categorized as Word via Unicode \p{L},
enabling space-delimited word navigation matching user expectations.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Pressing Arrow Up on the first line or Arrow Down on the last line now moves the cursor to the start or end of that line respectively, matching standard editor behavior. Extracted findVisualLine() into a standalone module for testability with unit tests. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Show translucent colored rectangles for remote peers' text selections, not just their cursor carets. Extracted computeSelectionRects() as a free function from the Selection class, added activeSelection to presence, and render peer selections behind local selection. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Show the text input cursor (I-beam) when hovering over the document content area, indicating that the area is editable. Co-Authored-By: Claude Opus 4.6 <[email protected]>
0a718ff to
612ab9a
Compare
Verification: verify:selfResult: ✅ PASS in 103.1s
Verification: verify:integrationResult: ✅ PASS |
Summary
min-w-0so the header/Share button area shrinks properly when narrowing the viewport in DocsTest plan
pnpm verify:fastpasses (all 6 commits)🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes
Documentation
Tests