Skip to content

feat(site): replace Agent chat textarea with Lexical editor#22449

Merged
kylecarbs merged 4 commits intomainfrom
lexical-agent-chat-input
Mar 1, 2026
Merged

feat(site): replace Agent chat textarea with Lexical editor#22449
kylecarbs merged 4 commits intomainfrom
lexical-agent-chat-input

Conversation

@kylecarbs
Copy link
Copy Markdown
Member

Summary

Replaces the plain <TextareaAutosize> in the Agent chat input (AgentChatInput) with a Lexical-based editor component, matching the pattern used in coder/blink.

What changed

New component: ChatMessageInput

site/src/components/ChatMessageInput/ChatMessageInput.tsx

A Lexical-powered text input that behaves as a plain-text editor with:

  • Enter submits, Shift+Enter inserts newline
  • Rich-text formatting disabled (Cmd+B/I/U blocked)
  • Paste sanitization (strips formatting, inserts plain text)
  • Undo/redo via HistoryPlugin
  • Imperative ref API: insertText(), clear(), focus(), getValue()

Updated components

  • AgentChatInput.tsx — Swapped <TextareaAutosize> for <ChatMessageInput>. Moved from controlled value/onChange to ref-based pattern with initialValue/onContentChange.
  • AgentDetail.tsx — Updated to use useRef for input value tracking and editorInitialValue state for editor resets (edit/cancel flows).
  • AgentsPage.tsx — Updated to use useRef + initialValue pattern.
  • AgentChatInput.stories.tsx — Updated prop names.

Why Lexical?

This lays the groundwork for features that a native <textarea> can't support:

  • Ghost text / inline autocomplete suggestions
  • @-mentions and slash commands
  • Programmatic text insertion (e.g. from speech-to-text)
  • Custom inline decorators (chips, pills, badges)
  • Syntax-highlighted code blocks

No adornments are added in this PR — it's a drop-in replacement that matches existing behavior.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 1, 2026


Thank you for your submission, we really appreciate it. Like many open-source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution. You can sign the CLA by just posting a Pull Request Comment same as the below format.


I have read the CLA Document and I hereby sign the CLA


Coder seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You can retrigger this bot by commenting recheck in this Pull Request. Posted by the CLA Assistant Lite bot.

@kylecarbs kylecarbs force-pushed the lexical-agent-chat-input branch 2 times, most recently from 27840ed to 8b6d88b Compare March 1, 2026 02:26
Replace the plain <TextareaAutosize> in the Agent chat input with a
Lexical-based editor component, matching the pattern used in
coder/blink. This gives us a content-editable surface with proper
keyboard command handling, paste sanitization, and an imperative ref
API (insertText, clear, focus, getValue).

The new ChatMessageInput component:
- Uses Lexical as the editor engine (plain-text mode, formatting disabled)
- Enter submits, Shift+Enter inserts newline
- Strips rich-text formatting on paste
- Supports undo/redo via HistoryPlugin
- Exposes imperative ref for programmatic text manipulation

This lays the groundwork for future enhancements like ghost text
autocomplete, @-mentions, slash commands, and inline decorators.

Parent components (AgentDetail, AgentsPage) updated from controlled
value/onChange to the ref-based pattern.
@kylecarbs kylecarbs force-pushed the lexical-agent-chat-input branch from 8b6d88b to 8a85794 Compare March 1, 2026 02:33
Coder added 3 commits March 1, 2026 02:48
ValueSyncPlugin seeded content in a useEffect, making it async.
In Chromatic, the Send button click could fire before the editor
had flushed the update, so getValue() returned empty string.

Move initial value seeding into initialConfig.editorState which
runs synchronously during LexicalComposer mount.
…in story

The editor.focus() callback runs via Lexical's $onUpdate() deferred
mechanism, which executes outside of editor.update()/read() context.
Calling $getRoot() there threw 'Unable to find an active editor state'.

Fix: wrap the focus callback body in editor.update().

Also restore ValueSyncPlugin for initial value seeding (editorState
config approach had the same issue), and update the story to wait for
the editor text to render before clicking Send.
@kylecarbs kylecarbs merged commit 897f178 into main Mar 1, 2026
25 of 26 checks passed
@kylecarbs kylecarbs deleted the lexical-agent-chat-input branch March 1, 2026 03:18
@github-actions github-actions bot locked and limited conversation to collaborators Mar 1, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant