Skip to content

Fix docs editor issues and add peer selection highlighting#79

Merged
hackerwins merged 7 commits into
mainfrom
fix/docs-editor-improvements
Mar 24, 2026
Merged

Fix docs editor issues and add peer selection highlighting#79
hackerwins merged 7 commits into
mainfrom
fix/docs-editor-improvements

Conversation

@hackerwins

@hackerwins hackerwins commented Mar 24, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • SidebarInset viewport fix: Add min-w-0 so the header/Share button area shrinks properly when narrowing the viewport in Docs
  • Korean word jump: Remove Hangul syllables from CJK category so Option+Arrow navigates by space-delimited words, not individual characters
  • Arrow Up/Down at boundaries: Move cursor to line start/end when pressing Arrow Up on first line or Arrow Down on last line
  • Remote peer selection highlighting: Display translucent colored rectangles for remote peers' text selections, not just cursor carets

Test plan

  • pnpm verify:fast passes (all 6 commits)
  • Manual test: narrow Docs viewport — Share button area shrinks correctly
  • Manual test: type Korean text, use Option+Arrow — jumps by words
  • Manual test: Arrow Up on first line → cursor moves to line start
  • Manual test: Arrow Down on last line → cursor moves to line end
  • Manual test: open same doc in two tabs — peer selection highlight visible

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Remote peer selections now render visually in the collaborative editor.
  • Bug Fixes

    • Improved Arrow Up/Down cursor behavior across wrapped visual lines.
    • Korean (Hangul) word navigation now treats syllable runs as word units.
    • Sidebar layout adjusted to prevent content shrinking.
  • Documentation

    • Added new task notes for arrow pixel-accuracy and remote cursor work; archive totals updated.
  • Tests

    • Added/updated tests for visual-line and word-boundary behaviors.

@coderabbitai

coderabbitai Bot commented Mar 24, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Adds 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

Cohort / File(s) Summary
Documentation & Tasks
docs/tasks/README.md, docs/tasks/active/20260325-docs-arrow-pixel-accuracy-todo.md, docs/tasks/archive/README.md, docs/tasks/archive/2026/03/20260325-docs-editor-improvements-todo.md
Added two active task entries and a TODO for arrow pixel accuracy; added archived task entries and updated archive totals from 97→99.
Selection computation & API surface
packages/docs/src/view/selection.ts
Extracted selection rectangle logic into exported helpers and added computeSelectionRects(range, paginatedLayout, layout, ctx, canvasWidth) used by Selection.
Canvas rendering & peer selections
packages/docs/src/view/doc-canvas.ts
Added peerColorToSelectionColor and extended DocCanvas.render(...) with optional peerSelections to draw peer highlight rectangles behind local selection.
Editor plumbing & cursor callback
packages/docs/src/view/editor.ts
Changed EditorAPI.onCursorMove signature to pass selection range (or null); compute and pass peerSelections into docCanvas.render; set canvas.style.cursor = 'text'.
Peer cursor model
packages/docs/src/view/peer-cursor.ts
Added optional selection?: DocRange to exported PeerCursor interface.
Visual line detection
packages/docs/src/view/visual-line.ts, packages/docs/test/view/visual-line.test.ts
Added findVisualLine and VisualLineInfo to locate wrapped visual lines; added unit tests covering boundaries and edge cases.
Text editor vertical movement
packages/docs/src/view/text-editor.ts
Use findVisualLine in vertical move logic; detect first/last visual-line boundary and set lineAffinity explicitly when moving across wrapped lines.
Word boundary (CJK/Hangul)
packages/docs/src/view/word-boundary.ts, packages/docs/test/view/word-boundary.test.ts
Refined CJK detection to exclude Hangul syllables (U+AC00–U+D7AF); Hangul now treated as space-delimited words; updated tests accordingly.
Frontend presence & store
packages/frontend/src/app/docs/docs-view.tsx, packages/frontend/src/app/docs/yorkie-doc-store.ts, packages/frontend/src/types/users.ts
Frontend now reads/forwards activeSelection in presence; YorkieDocStore.updateCursorPos accepts optional selection; DocsPresence type gains optional activeSelection.
Sidebar layout tweak
packages/frontend/src/components/ui/sidebar.tsx
Added Tailwind min-w-0 to SidebarInset to allow proper shrinking under flex.

Sequence Diagram

sequenceDiagram
    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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 With twitching whiskers I hop and peep,

peer highlights glow where selections sleep.
Lines wrap neatly, visual bounds align,
Korean words group, not single-char fine.
Together we edit — pixels fall in place.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 61.54% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the two main themes of the changeset: fixing multiple docs editor issues (viewport shrinking, Korean word navigation, Arrow Up/Down boundaries) and adding peer selection highlighting.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/docs-editor-improvements

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.

@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 (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 offset exceeds the block's character count. If findVisualLine should 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 lastStart by iterating through all lines again, but this value equals charsBefore at 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

📥 Commits

Reviewing files that changed from the base of the PR and between c6d9bdc and bcd6fbf.

📒 Files selected for processing (18)
  • docs/tasks/README.md
  • docs/tasks/active/20260325-docs-arrow-pixel-accuracy-todo.md
  • docs/tasks/archive/2026/03/20260324-pr72-review-fixes-todo.md
  • docs/tasks/archive/2026/03/20260325-docs-editor-improvements-todo.md
  • docs/tasks/archive/README.md
  • packages/docs/src/view/doc-canvas.ts
  • packages/docs/src/view/editor.ts
  • packages/docs/src/view/peer-cursor.ts
  • packages/docs/src/view/selection.ts
  • packages/docs/src/view/text-editor.ts
  • packages/docs/src/view/visual-line.ts
  • packages/docs/src/view/word-boundary.ts
  • packages/docs/test/view/visual-line.test.ts
  • packages/docs/test/view/word-boundary.test.ts
  • packages/frontend/src/app/docs/docs-view.tsx
  • packages/frontend/src/app/docs/yorkie-doc-store.ts
  • packages/frontend/src/components/ui/sidebar.tsx
  • packages/frontend/src/types/users.ts

Comment thread docs/tasks/active/20260325-docs-arrow-pixel-accuracy-todo.md

@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/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

📥 Commits

Reviewing files that changed from the base of the PR and between bcd6fbf and 0a718ff.

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

Comment thread packages/docs/src/view/editor.ts
hackerwins and others added 7 commits March 25, 2026 00:52
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]>
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]>
@hackerwins
hackerwins force-pushed the fix/docs-editor-improvements branch from 0a718ff to 612ab9a Compare March 24, 2026 15:53
@github-actions

github-actions Bot commented Mar 24, 2026

Copy link
Copy Markdown
Contributor

Verification: verify:self

Result: ✅ PASS in 103.1s

Lane Status Duration
sheets:build ✅ pass 13.9s
docs:build ✅ pass 5.3s
verify:fast ✅ pass 47.8s
frontend:build ✅ pass 14.4s
verify:frontend:chunks ✅ pass 0.3s
backend:build ✅ pass 4.3s
cli:build ✅ pass 1.8s
verify:entropy ✅ pass 15.3s

Verification: verify:integration

Result: ✅ PASS

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