Lexical Editor Integration#
The agenta web app uses Lexical (v0.30.x) as its primary rich text editor framework. The editor lives at web/oss/src/components/Editor/ and exposes two public surfaces: a self-contained Editor default export and an EditorProvider for cases where you need to embed the editor inside a pre-existing Lexical context.
Component Architecture#
Editor (default export, memo-wrapped)
└── EditorProvider — wraps LexicalComposer with config from useEditorConfig
└── EditorInner — registers commands, calls EditorPlugins
└── EditorPlugins — mounts all Lexical plugins
EditorProvider calls useEditorConfig to lazily build the LexicalComposer initialConfig, then renders <LexicalComposer initialConfig={config}>.
EditorInner is a forwardRef component that handles content hydration and change propagation. It registers the ON_HYDRATE_FROM_REMOTE_CONTENT command to load initial markdown into the editor state.
Markdown ↔ Editor State#
All markdown conversion uses the stock TRANSFORMERS array from @lexical/markdown — no custom transformers are defined.
-
Markdown → editor state (hydration):
$convertFromMarkdownString(content, TRANSFORMERS)is called inside theON_HYDRATE_FROM_REMOTE_CONTENTcommand handler. The command fires on mount withinitialValueand can be re-dispatched by external consumers to reload content. -
Editor state → markdown (onChange):
$convertToMarkdownString(TRANSFORMERS)runs inside theOnChangePlugincallback. The resulting string has backslash escapes stripped (replaceAll(/\\(.)/g, "$1")) before being passed to theonChangeprop.
In codeOnly mode, markdown conversion is skipped entirely; content is read as a plain string via $getEditorCodeAsString.
Node Registration (useEditorConfig)#
useEditorConfig lazily imports and registers all node types on first render. Nodes registered for rich-text mode include:
| Package | Nodes |
|---|---|
@lexical/rich-text | HeadingNode, QuoteNode |
@lexical/list | ListNode, ListItemNode |
@lexical/code | CodeNode, CodeHighlightNode |
@lexical/table | TableNode, TableCellNode, TableRowNode |
@lexical/hashtag | HashtagNode |
@lexical/link | AutoLinkNode, LinkNode |
@lexical/overflow | OverflowNode |
@lexical/react | HorizontalRuleNode |
@lexical/mark | MarkNode |
When enableTokens is true, custom TokenNode and TokenInputNode are added to this list. When codeOnly is true, custom code block nodes (CodeBlockNode, CodeHighlightNode, CodeLineNode, CodeBlockErrorIndicatorNode) replace the standard Lexical code nodes.
Note on link handling:
AutoLinkNodeandLinkNodeare registered as node types, but noLinkPluginorAutoLinkPluginfrom@lexical/reactis mounted. This means link nodes can exist in the editor state (e.g., parsed from markdown) but there is no interactive link-creation UI.
Plugins#
All plugins are lazy-loaded via React.lazy and wrapped in a single <Suspense> boundary with an Ant Design Skeleton fallback. Active plugins and their conditions:
| Plugin | Condition |
|---|---|
RichTextPlugin | Always on |
HistoryPlugin | Always on |
OnChangePlugin | Always on |
AutoFocusPlugin | autoFocus === true |
ToolbarPlugin | showToolbar && !singleLine && !codeOnly |
TokenPlugin | enableTokens === true |
SingleLinePlugin | singleLine === true |
CodeEditorPlugin + TabIndentationPlugin | codeOnly === true |
DebugPlugin | debug === true |
The ToolbarPlugin provides bold, italic, underline, inline-code formatting, and left/center/right alignment via Lexical's FORMAT_TEXT_COMMAND / FORMAT_ELEMENT_COMMAND.
Theme#
The Lexical theme at assets/theme.ts maps syntax-highlight token types (keyword, string, function, etc.) to CSS class names (editor-tokenAttr, editor-tokenFunction, …). No rich-text styling classes (headings, lists, links) are defined here — those are handled by global CSS outside the component.
Key EditorProps#
Defined in types.d.ts:
| Prop | Default | Purpose |
|---|---|---|
initialValue | "" | Markdown string to hydrate on mount |
onChange | — | Receives { textContent, tokens } on every edit |
singleLine | false | Restricts input to one line |
codeOnly | false | Switches to code editor mode |
language | — | Syntax highlighting language for code mode |
enableTokens | false | Enables token nodes |
showToolbar | true | Shows the formatting toolbar |
noProvider | false | Skip EditorProvider (use when already inside a LexicalComposer) |
Key Files#
| File | Purpose |
|---|---|
Editor.tsx | Main component, markdown conversion logic |
hooks/useEditorConfig/index.ts | Builds LexicalComposer config + node list |
plugins/index.tsx | Plugin orchestration |
plugins/toolbar/ToolbarPlugin.tsx | Formatting toolbar |
assets/theme.ts | Lexical theme (syntax highlight classes) |
types.d.ts | TypeScript prop types |