Replace native textarea with contentEditable for inline autocomplete#394
Draft
vimzh wants to merge 3 commits intoaymericzip:mainfrom
Draft
Replace native textarea with contentEditable for inline autocomplete#394vimzh wants to merge 3 commits intoaymericzip:mainfrom
vimzh wants to merge 3 commits intoaymericzip:mainfrom
Conversation
… autocomplete Swap the native <textarea> in AutocompleteTextArea with a contentEditable <div> that renders each line as a <span>. This enables pixel-accurate inline ghost text for autocomplete on any line, without the positioning hacks required by absolute-positioned overlays. New ContentEditableTextArea component with: - Per-line span rendering with editable + ghost segments - useContentEditable hook for DOM <-> state sync and caret management - Memoized Line component to skip unchanged line re-renders - Auto-sizing, LTR/RTL, disabled state, placeholder - Full ARIA: role=textbox, aria-multiline, aria-autocomplete=inline Closes aymericzip#197
aymericzip
reviewed
Mar 14, 2026
| ); | ||
| } catch (err) { | ||
| console.error('Autocomplete error:', err); | ||
| const _cursorToLinePos = useCallback((cursor: number, src: string) => { |
| ? text.split('\n').length - 1 | ||
| : ghostPos?.line; | ||
| const activeOffset = suggestionProp | ||
| ? (text.split('\n').at(-1)?.length ?? 0) |
Owner
There was a problem hiding this comment.
Property 'at' does not exist on type 'string[]'
|
The preview deployment for website failed. 🔴 Last updated at: 2026-03-14 21:57:12 CET |
Owner
|
the logic is broken Screen.Recording.2026-03-14.at.4.09.37.PM.mov |
|
The preview deployment for design-system is ready. 🟢 Open Preview | Open Build Logs Last updated at: 2026-03-15 21:33:55 CET |
…dback Address all maintainer review comments on PR aymericzip#394: - Remove unused _cursorToLinePos function and dead autocomplete state - Replace .at(-1) with bracket access for ES2020 compatibility - Fix broken input logic by intercepting all keyboard/clipboard events via React state instead of allowing browser DOM mutations - Add cursor preservation across re-renders via pendingCaretRef - Handle text selections for Backspace/Delete/Enter/Cut/Paste - Support modifier keys: Option+Backspace (word), Cmd+Backspace (line) - Block Cmd+Z/Redo to prevent React state desync - Add IME composition guards for CJK input - Add grapheme-safe deletion for emoji and surrogate pairs - Block drag-and-drop to prevent uncontrolled DOM mutations - Handle spell-check replacements via insertReplacementText - Fix {..rest} spread ordering to prevent handler override - Fix placeholder alignment to match inputVariants padding - Forward aria-invalid and aria-describedby to contentEditable div - Strip zero-width spaces from text extraction - Add 24 unit tests for hook and component behavior
Author
|
Hey @aymericzip, thanks for the review and for recording the video, I've pushed a fix addressing all the feedback |
aymericzip
approved these changes
Mar 15, 2026
Owner
aymericzip
left a comment
There was a problem hiding this comment.
good to me. just before merging, few point regarding the codebase convention
- remove the useMemo / useCallback. We use the react compiler
- prefer import of
import {ChangeEvent} from 'react'overimport * as react from 'react' - Remove react 18 related syntax (e.g.
Line.displayName = 'Line';)
…review React compiler handles memoization, so useCallback and memo wrappers are unnecessary. Also removes Line.displayName (React 18 pattern).
Author
|
Hey @aymericzip, thanks for the review! I've pushed the changes:
Let me know if there's anything else! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replaces the native
<textarea>inAutoCompleteTextareawith acontentEditablediv (ContentEditableTextArea) to enable inline ghost text rendering for autocomplete suggestions.Review feedback addressed
_cursorToLinePosfunction and all dead autocomplete state (ghostPos,cursorAtFetch,isTyped,debouncedText).at(-1)with bracket access for ES2020 compatibilityWhat changed
ContentEditableTextArea (new component):
pendingCaretRefIntl.SegmenterinsertReplacementText){...rest}spread before explicit handlers to prevent overrideinputVariantspaddingAutocompleteTextArea (simplified):
useAutocomplete,useConfiguration)isActiveprop properly gates ghost text displayaria-invalidandaria-describedbynow forwardedtext.split('\n')(was called 3x per render)Tests:
Test plan
npx tsc --noEmit)anytypes