Skip to content

Conversation

@malore350
Copy link
Contributor

@malore350 malore350 commented Nov 1, 2025

Description

Fix cursor positioning, viewport motion based on cursor movement, horizontal scrollbar activity and overall scrollbar design.

  • Improve layout accuracy by calculating line content width based on rendered text and handling tabs and variable-width characters.

  • Normalize line endings on file read/write to prevent inconsistencies.

  • Optimize scrollbar appearance and behavior with theme-aware styling and consistent behavior across browsers.

  • Fix cursor location on the last position of any row by removing scrollbar gutter settings.

  • Ensure horizontal scrollbar functionality works correctly.

Related Issues

Fixes #382

Summary by CodeRabbit

Release Notes

  • New Features

    • Scrollbars are now visible and themed with colors matching your editor theme, including improved touch scrolling support
  • Improvements

    • Line ending formats are automatically preserved when saving files
    • Zoom functionality now handles edge cases reliably
    • Enhanced horizontal scrolling and content layout calculation
    • Improved dynamic font handling and text rendering accuracy

- Calculates line content width based on rendered text, improving layout accuracy.
- Ensures proper handling of tabs and variable-width characters.
- Normalizes line endings on file read/write to prevent inconsistencies.
- Recalculates metrics when font settings change to avoid layout issues.
Enhances the editor's scrollbar appearance and behavior by:

- Implementing theme-aware scrollbar styling using CSS variables for better customization.
- Ensuring consistent scrollbar behavior across different browsers.
- Optimizing scrollbar size calculation based on zoom level.
- Maintaining editor alignment and rendering quality.
This change fixes the cursor location on the last position of any row
@coderabbitai
Copy link

coderabbitai bot commented Nov 1, 2025

Walkthrough

This PR addresses cursor synchronization and layout precision issues by implementing line-ending normalization, precise character width measurement infrastructure with off-DOM measurement contexts, dynamic viewport width calculations based on font metrics, and theme-aware scrollbar styling. It also improves zoom level boundary handling.

Changes

Cohort / File(s) Summary
Line Ending Handling
src/utils/line-endings.ts, src/stores/app-store.ts, src/features/editor/stores/buffer-store.ts
New utility module exports LineEnding type and functions (detectLineEnding, normalizeLineEndings, applyLineEnding). app-store applies line-ending transformation before persistence. buffer-store extends Buffer with lineEnding field and normalizes content on creation, update, and reload from disk.
Character Width & Measurement
src/features/editor/utils/position.ts
Removes rounding from character width calculation; introduces MeasurementContext for off-DOM element reuse. Adds getCharWidthCached, getAccurateCursorX (full substring width), and getLineRenderWidth (entire line width). Introduces scrollbar sizing utilities (computeScrollbarSize, getScrollbarSize) and cache management (clearCharWidthCache, clearScrollbarSizeCache).
Layout & Viewport Rendering
src/features/editor/components/rendering/viewport.tsx, src/features/editor/hooks/use-layout.ts
viewport computes padded content width using line widths and font settings; adds zoom state and scrollbar size calculations. use-layout adds font family handling, clears character width cache on font/size changes, monitors document.fonts loading events, and recalculates layout metrics dynamically.
Scrollbar Styling
src/features/editor/components/stylesheet.tsx
Replaces hidden scrollbars with theme-aware visible scrollbars. Adds CSS variables for scrollbar colors, WebKit dimensions, hover states, and touch scrolling support.
Zoom State Handling
src/stores/zoom-store.ts
Fixes undefined zoom index by capping to last level before computing next/previous index; removes debug console.log.
Configuration Files
.clinerules, .cursorrules, .windsurfrules, CLAUDE.md, GEMINI.md
Configuration files containing .rules reference for AI tool integration.

Sequence Diagram(s)

sequenceDiagram
    participant Editor as Editor Component
    participant Layout as useEditorLayout Hook
    participant Fonts as document.fonts
    participant Position as position.ts Utils
    participant Cache as Measurement Cache

    Editor->>Layout: Render with fontSize/fontFamily
    activate Layout
    
    rect rgb(200, 220, 255)
    Note over Layout,Fonts: Font Loading Detection
    Layout->>Fonts: Monitor ready/loadingdone events
    Fonts-->>Layout: Font loaded
    Layout->>Cache: clearCharWidthCache()
    Cache-->>Layout: Cache cleared
    end
    
    Layout->>Position: useEffectDependency: fontFamily changed
    activate Position
    Position->>Cache: Create MeasurementContext
    Cache-->>Position: Context ready (keyed by font+size+tabSize)
    Position->>Position: computeCharacterWidths (full precision)
    Position-->>Layout: Metrics updated
    deactivate Position
    
    Layout->>Layout: Compute effectiveFontFamily
    Layout->>Position: useMemo(includeFont dependencies)
    activate Position
    Position->>Cache: getCharWidthCached(char, fontSize, fontFamily)
    Cache-->>Position: Exact width (no rounding)
    Position-->>Layout: Layout metrics
    deactivate Position
    
    deactivate Layout
    Editor->>Editor: Render with precise layout
Loading
sequenceDiagram
    participant Content as File Content
    participant Buffer as Buffer Store
    participant AppStore as App Store
    participant Disk as Disk/Remote

    Content->>Buffer: Load/Update Content
    activate Buffer
    Buffer->>Buffer: normalizeLineEndings(content)
    Note over Buffer: Detect & store lineEnding
    Buffer-->>AppStore: Buffer updated (content, lineEnding)
    deactivate Buffer
    
    AppStore->>AppStore: Autosave/Save triggered
    activate AppStore
    AppStore->>AppStore: applyLineEnding(content, lineEnding)
    Note over AppStore: Transform LF back to original format
    AppStore->>Disk: Write transformed content
    Disk-->>AppStore: ✓ Saved
    deactivate AppStore
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • src/features/editor/utils/position.ts — Dense logic introducing measurement infrastructure, off-DOM contexts, and full-precision calculations across multiple new functions. Requires careful validation of caching strategy, context lifecycle, and edge cases.
  • src/features/editor/components/rendering/viewport.tsx — Multiple interacting layout computations (line width, padding, scrollbar size) and integration with new measurement utilities from position.ts. Verify zoom state integration and content width propagation.
  • src/features/editor/hooks/use-layout.ts — Font loading event handling, cache invalidation, and dependency chain updates. Ensure proper cleanup and avoid memory leaks from document.fonts listeners.
  • Interaction across files — position.ts, viewport.tsx, and use-layout.ts form a chain; changes must be validated together for correctness and performance.
  • src/features/editor/stores/buffer-store.ts & src/stores/app-store.ts — Line-ending normalization logic and persistence flow; verify round-trip correctness (normalize → save → reload → same ending).

Poem

🐰✨ Characters dance with pixel-perfect grace,
No more jittering cursors losing their place!
Fonts load, widths measure, scrollbars now shine,
Line endings preserved—the editor's divine!
Zoom steady, content true, a tale of precision,
Synced at last! Thump-thump 🐇

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning The PR includes five configuration files (.clinerules, .cursorrules, .windsurfrules, CLAUDE.md, GEMINI.md) that were added as part of this changeset. These files contain only the text ".rules" and do not directly relate to the stated PR objectives of fixing cursor positioning, viewport motion, or scrollbar functionality. While these files introduce no executable code and minimal maintenance burden, they fall outside the scope of the primary technical improvements and should be considered out-of-scope additions. Consider removing the out-of-scope configuration files (.clinerules, .cursorrules, .windsurfrules, CLAUDE.md, GEMINI.md) from this PR and creating a separate PR or commit dedicated to adding AI assistant configuration files. This maintains a clear separation of concerns and makes the change history easier to understand when reviewing what actually fixes the cursor positioning issue.
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title directly references the primary focus areas of the changeset: cursor positioning accuracy, viewport motion based on cursor movement, horizontal scrollbar functionality, and overall scrollbar design. These elements are clearly present in the substantial changes to position utilities (getAccurateCursorX, getLineRenderWidth), viewport rendering (width calculations, zoom integration), and stylesheet updates (theme-aware scrollbar styling). The title is specific and descriptive enough that a reviewer scanning history would understand the core intent of these changes without relying on vague terminology.
Linked Issues Check ✅ Passed The changes directly address the issue #382 objective of restoring correct, synchronized cursor positioning during typing. The primary implementation includes full-precision character width measurement in position.ts (via getCharWidthCached and getAccurateCursorX), MeasurementContext for accurate width calculations, improved tab handling, and enhanced layout metrics recalculation when fonts load or font settings change. These improvements are supported by line-ending normalization throughout the store and persistence layers to prevent platform-specific inconsistencies, and viewport rendering updates to ensure accurate horizontal space calculations and proper cursor tracking during typing and movement.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 644dfaa and 9b70940.

📒 Files selected for processing (16)
  • .clinerules (0 hunks)
  • .clinerules (1 hunks)
  • .cursorrules (0 hunks)
  • .cursorrules (1 hunks)
  • .windsurfrules (0 hunks)
  • .windsurfrules (1 hunks)
  • CLAUDE.md (0 hunks)
  • CLAUDE.md (1 hunks)
  • GEMINI.md (0 hunks)
  • GEMINI.md (1 hunks)
  • src/features/editor/components/rendering/viewport.tsx (5 hunks)
  • src/features/editor/components/stylesheet.tsx (1 hunks)
  • src/features/editor/hooks/use-layout.ts (2 hunks)
  • src/features/editor/stores/buffer-store.ts (4 hunks)
  • src/features/editor/utils/position.ts (3 hunks)
  • src/stores/app-store.ts (4 hunks)
✅ Files skipped from review due to trivial changes (10)
  • .windsurfrules
  • GEMINI.md
  • .cursorrules
  • CLAUDE.md
  • .cursorrules
  • .windsurfrules
  • GEMINI.md
  • CLAUDE.md
  • .clinerules
  • .clinerules
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/stores/app-store.ts
🧰 Additional context used
🧬 Code graph analysis (1)
src/features/editor/components/rendering/viewport.tsx (1)
src/stores/zoom-store.ts (1)
  • useZoomStore (27-90)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: check format and cargo check

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

Copy link

@coderabbitai coderabbitai bot left a comment

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)
src/components/editor/rendering/editor-viewport.tsx (1)

67-82: Avoid recalculating every line’s width on each change.

maxLineContentWidth now iterates over every entry in lines and runs getLineRenderWidth per render. On large files this is O(total characters) per keystroke and will hammer responsiveness. Please cache or incrementally track the widest line instead of recomputing the full file on every update (e.g., maintain the max in a store and update only when affected lines change).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d298164 and 644dfaa.

📒 Files selected for processing (8)
  • src/components/editor/editor-stylesheet.tsx (1 hunks)
  • src/components/editor/rendering/editor-viewport.tsx (5 hunks)
  • src/hooks/use-editor-layout.ts (2 hunks)
  • src/stores/app-store.ts (4 hunks)
  • src/stores/buffer-store.ts (5 hunks)
  • src/stores/zoom-store.ts (2 hunks)
  • src/utils/editor-position.ts (3 hunks)
  • src/utils/line-endings.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (5)
src/stores/app-store.ts (2)
src/utils/line-endings.ts (1)
  • applyLineEnding (44-51)
src/file-system/controllers/platform.ts (1)
  • writeFile (37-45)
src/hooks/use-editor-layout.ts (3)
src/stores/editor-settings-store.ts (1)
  • useEditorSettingsStore (29-52)
src/stores/editor-view-store.ts (1)
  • useEditorViewStore (127-155)
src/utils/editor-position.ts (3)
  • clearCharWidthCache (289-295)
  • getLineHeight (60-63)
  • getCharWidth (69-93)
src/stores/buffer-store.ts (2)
src/utils/line-endings.ts (2)
  • LineEnding (1-1)
  • normalizeLineEndings (22-39)
src/types/buffer.ts (1)
  • Buffer (1-14)
src/components/editor/rendering/editor-viewport.tsx (6)
src/stores/editor-settings-store.ts (1)
  • useEditorSettingsStore (29-52)
src/hooks/use-editor-layout.ts (1)
  • useEditorLayout (7-62)
src/stores/editor-view-store.ts (1)
  • useEditorViewStore (127-155)
src/stores/zoom-store.ts (1)
  • useZoomStore (27-90)
src/utils/editor-position.ts (2)
  • getLineRenderWidth (250-284)
  • getScrollbarSize (328-339)
src/constants/editor-constants.ts (1)
  • EDITOR_CONSTANTS (2-45)
src/utils/editor-position.ts (1)
src/constants/editor-constants.ts (1)
  • EDITOR_CONSTANTS (2-45)

Comment on lines +133 to 135
overflowX: "hidden",
overflowY: "auto",
height: `${viewportHeight}px`,
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Horizontal scrolling regressed by overflowX: hidden.

Setting the viewport container to overflowX: "hidden" removes the horizontal scroll area entirely, so scrollbars never appear and scrollLeft stays pinned at zero. This blocks the fix we need for horizontal scrolling—the code content becomes inaccessible once it exceeds the viewport width. Please restore horizontal overflow (e.g., switch back to auto) so the editor can expose horizontal scroll again.

-        overflowX: "hidden",
-        overflowY: "auto",
+        overflowX: "auto",
+        overflowY: "auto",
📝 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
overflowX: "hidden",
overflowY: "auto",
height: `${viewportHeight}px`,
overflowX: "auto",
overflowY: "auto",
height: `${viewportHeight}px`,
🤖 Prompt for AI Agents
In src/components/editor/rendering/editor-viewport.tsx around lines 133 to 135,
the style sets overflowX: "hidden" which removes the horizontal scrollbar and
pins scrollLeft to zero; change overflowX back to "auto" (or remove the explicit
overflowX so it inherits/uses overflow: auto) so horizontal overflow is allowed
and the editor can scroll horizontally when content exceeds the viewport width.

Updated the import statement for `getLineRenderWidth` and `getScrollbarSize` to reflect the new file structure under the features directory.
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.

Off sync typing on code editor

1 participant