@octane-xplat/richtext
v0.18.0
Published
Native rich-text editing leaf for Octane xplat — Android Aztec and AppKit WKWebView StarterKit
Maintainers
Readme
@octane-xplat/richtext
Native rich-text editing for Octane Xplat apps: WordPress Aztec's AztecText
on Android — a Spannable-backed EditText, not a WebView — and
AztecEditor-iOS (Aztec.TextView) on iOS through the XplatAztecEditorView
Swift facade in platforms/ios. Web and Windows return supported: false;
the @octane-xplat/tiptap facade covers web.
import { useState } from 'octane'
import { RichTextEditor, supported } from '@octane-xplat/richtext'
import { Text } from '@octane-xplat/ui'
export function Notes() {
const [html, setHtml] = useState('<p>Trip notes</p>')
return supported ? (
<RichTextEditor value={html} onChange={setHtml} />
) : (
<Text>Editing is unavailable on this target</Text>
)
}Content in/out is document HTML via Aztec fromHtml/toPlainHtml. The
ref callback returns an imperative handle (getHTML/setHTML,
apply(format), linkTo/removeLink, isActive, undo/redo,
focus/blur, native) once the editor exists, and onSelectionChange
reports the active RichTextFormat set at the caret.
Structural commands exist on the Aztec surface: split() breaks the block
at the caret, join() merges a block into the previous one from a
collapsed caret at its start, and indent()/outdent() demote or promote
the selected blocks — a real nested-list restructure on <ul>/<ol>, a
\t indent on plain blocks. canIndent()/canOutdent() report whether
the current selection can take the command. All six return false when
the command cannot apply; markup Aztec does not understand (for example
<div data-*> wrappers) is opaque to them. On AppKit the commands return
false — the engine transport cannot report a synchronous result.
import { useRef } from 'octane'
import { RichTextEditor, type RichTextEditorHandle } from '@octane-xplat/richtext'
import { Button } from '@octane-xplat/ui'
export function Formatting() {
const editor = useRef<RichTextEditorHandle | null>(null)
return (
<>
<RichTextEditor
ref={(handle) => {
editor.current = handle
}}
onSelectionChange={({ active }) => console.log(active)}
/>
<Button clickAction={() => editor.current?.apply('bold')}>Bold</Button>
<Button
clickAction={() => {
const handle = editor.current
if (!handle) return
handle.setHTML('<p>New note</p>')
console.log(handle.getHTML(), handle.isActive('bold'))
handle.linkTo('https://example.com')
handle.removeLink()
handle.undo()
handle.redo()
handle.focus()
handle.blur()
}}
>
Try editor commands
</Button>
</>
)
}Native dependencies
The Android Aztec dependency (org.wordpress:aztec:v2.1.7) arrives through
the leaf's platforms/android/include.gradle — no app-level gradle work.
On iOS the leaf declares AztecEditor-iOS as a pinned-revision Swift Package
in its plugin-level nativescript.config.ts and compiles
platforms/ios/src/XplatAztecEditor.swift into the app — NativeScript
plugin discovery applies both when the app depends on
@octane-xplat/richtext. iOS has no task-list or text-alignment surface:
apply('taskList') and the align* formats report a warning and leave the
document unchanged, and isActive returns false for them.
See the framework guide for the full contract and the demo:
docs/app/rich-text.md and
packages/demos/src/RichTextEditorDemo.tsrx.
macOS AppKit
The macOS export mounts a bundled local editor document in WKWebView.
RichText uses StarterKit; Tiptap and Lexical use their existing web facades.
Wait for onReady; synchronous getters return the latest received snapshot
and commands cross WebKit asynchronously. The native handle is the Swift
host transport. See AppKit setup and engine limits.
