Repository navigation
Mock Element Positioning System for Precise Cursor Handling #33
Copy link
Copy link
Closed
Labels
enhancementNew feature or requestNew feature or request
Description
Implement character-level coordinate mapping system for precise cursor positioning during click-to-edit transitions, especially with different font sizes between display and edit modes.
Dependencies
None - This is a self-contained positioning system that can be implemented independently.
Related Issues
- Hybrid Markdown Rendering System - Live Preview with Syntax Visibility #26 (Hybrid markdown rendering will benefit from accurate positioning)
- Hover previews for node references #34 (Rich decorations require precise coordinate mapping)
Self-Contained Scope
- Complete mock element positioning system using hidden character mapping
- Coordinate utilities for click-to-character position conversion
- Integration with existing BaseNode textarea architecture
- Mock data patterns for independent development and testing
- Comprehensive testing with various font sizes and content types
- Documentation and usage examples for positioning accuracy
Implementation Approach
- Mock Element Component: Hidden div that mirrors textarea with character-level spans
- Coordinate Mapping: Utilities using getBoundingClientRect for precise measurements
- BaseNode Integration: Replace current binary search with mock element positioning
- Testing Strategy: Accuracy tests across font sizes and content scenarios
- Demo/Documentation: Working examples showing positioning precision
Requirements
- Character-Level Precision: Click coordinates map to exact character positions
- Font Size Awareness: Accurate positioning across H1, H2, H3, normal text
- Multi-line Support: Correct positioning for line breaks and wrapped content
- Performance: Position calculation < 50ms for smooth user experience
- Unicode Support: Handle emojis, accented characters, and grapheme clusters
Technical Implementation Notes
Step 1: Mock Element Component
<\!-- MockTextElement.svelte -->
<script>
export let content = '';
export let fontFamily = '';
export let fontSize = '';
export let fontWeight = '';
export let lineHeight = '';
export let width = '';
$: characters = content.split('').map((char, idx) => ({ char, idx }));
</script>
<div
class="mock-text-element"
style="
font-family: {fontFamily};
font-size: {fontSize};
font-weight: {fontWeight};
line-height: {lineHeight};
width: {width}px;
position: absolute;
visibility: hidden;
white-space: pre-wrap;
"
>
{#each characters as {char, idx}}
<span id="mock-char-{idx}" data-position="{idx}">
{char === '\n' ? '' : char}{#if char === '\n'}<br/>{/if}
</span>
{/each}
</div>Step 2: Positioning Utilities
// CursorPositioning.js
export function findCharacterFromClick(mockElement, clickX, clickY, textareaRect) {
const relativeX = clickX - textareaRect.left;
const relativeY = clickY - textareaRect.top;
let bestMatch = { index: 0, distance: Infinity };
const allSpans = mockElement.querySelectorAll('[data-position]');
for (const span of allSpans) {
const rect = span.getBoundingClientRect();
const mockRect = mockElement.getBoundingClientRect();
const spanX = rect.left - mockRect.left;
const spanY = rect.top - mockRect.top;
const distance = Math.sqrt(
Math.pow(spanX - relativeX, 2) +
Math.pow(spanY - relativeY, 2)
);
if (distance < bestMatch.distance) {
bestMatch = { index: parseInt(span.dataset.position), distance };
}
}
return bestMatch.index;
}Step 3: BaseNode Integration
<\!-- BaseNode.svelte -->
<script>
import MockTextElement from './MockTextElement.svelte';
import { findCharacterFromClick } from './CursorPositioning.js';
let mockElementRef;
async function positionCursorFromClick(clickEvent) {
if (\!textareaElement || \!mockElementRef) return;
const textareaRect = textareaElement.getBoundingClientRect();
const characterIndex = findCharacterFromClick(
mockElementRef,
clickEvent.clientX,
clickEvent.clientY,
textareaRect
);
textareaElement.setSelectionRange(characterIndex, characterIndex);
}
</script>
<\!-- Add mock element alongside textarea -->
<MockTextElement
bind:this={mockElementRef}
{content}
fontFamily={getComputedStyle(textareaElement).fontFamily}
fontSize={getComputedStyle(textareaElement).fontSize}
fontWeight={getComputedStyle(textareaElement).fontWeight}
lineHeight={getComputedStyle(textareaElement).lineHeight}
width={textareaElement.offsetWidth}
/>File Modifications Required:
- Create:
MockTextElement.sveltecomponent - Create:
CursorPositioning.jsutilities - Modify:
BaseNode.svelteto integrate mock element positioning - Replace: Current binary search logic with mock element approach
Patterns to Follow:
- Follow existing BaseNode component structure and lifecycle
- Use current content binding and event handling patterns
- Maintain compatibility with existing TextNode integration
- Follow project Svelte component conventions and TypeScript patterns
Acceptance Criteria
- Click at start of text → cursor positions at character 0
- Click between characters → cursor lands within 1-2 characters of click
- Click at end of text → cursor positions at final character
- Accurate positioning across different font sizes (H1=2rem, H2=1.5rem, normal)
- Multi-line content positioning works correctly
- Unicode characters and emojis handled properly
- Performance < 50ms for position calculation
- No memory leaks or cleanup issues
- Integration with existing BaseNode API maintained
- Cross-browser compatibility (Chrome, Firefox, Safari)
Resources & References
- Logseq Implementation: Character mapping research from
/Users/malibio/Zed Projects/logseq - Current Implementation: BaseNode positioning logic in
/src/lib/design/components/BaseNode.svelte - Component Patterns: Existing Svelte components for structure and lifecycle
- Testing Utilities: Current testing patterns in project
Definition of Done
- MockTextElement component created and functional
- CursorPositioning utilities implemented and tested
- BaseNode integration completed with mock element positioning
- All acceptance criteria met and verified through testing
- Unit tests >90% coverage for positioning utilities
- Manual testing across various content and font scenarios
- Performance benchmarks meet requirements (< 50ms)
- Code follows project standards (ESLint, Prettier, TypeScript)
- Documentation includes usage examples and API reference
- Ready for integration with hybrid markdown and decoration systems
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or request
Activity
Update architecture documentation with advanced UI/UX capabilities
Implement Mock Element Positioning System for Precise Cursor Handling (…
Fix linting issues from Issue #33 implementation
🚨 CRITICAL: Fix development process to prevent linting failures
Update architecture documentation with advanced UI/UX capabilities
Implement Mock Element Positioning System for Precise Cursor Handling (…
Fix linting issues from Issue #33 implementation
🚨 CRITICAL: Fix development process to prevent linting failures
Update architecture documentation with advanced UI/UX capabilities
Implement Mock Element Positioning System for Precise Cursor Handling (…
Fix linting issues from Issue #33 implementation
🚨 CRITICAL: Fix development process to prevent linting failures