@octane-xplat/richtext 0.0.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,282 @@
1
+ /** @jsxImportSource @nativescript-community/octane */
2
+ import { useImperativeHandle, useCallback, useEffect, useRef } from 'octane'
3
+ import { View } from '@nativescript/core'
4
+ import { registerElement } from '@nativescript-community/octane'
5
+ import type { Attributes } from '@nativescript-community/octane/intrinsics'
6
+ import type {
7
+ RichTextEditorHandle,
8
+ RichTextEditorProps,
9
+ RichTextFormat,
10
+ } from './types'
11
+
12
+ /** `org.wordpress.aztec` arrives through the leaf's aar — there are no
13
+ * generated typings for it, so every touch goes through `as any` at one
14
+ * boundary instead of `declare const org` per call site. */
15
+ const aztec = (globalThis as any).org?.wordpress?.aztec
16
+ const androidText = (globalThis as any).android?.text
17
+ const androidView = (globalThis as any).android?.view
18
+
19
+ const FORMATS: Record<Exclude<RichTextFormat, 'link'>, () => any> = {
20
+ bold: () => aztec.AztecTextFormat.FORMAT_BOLD,
21
+ italic: () => aztec.AztecTextFormat.FORMAT_ITALIC,
22
+ underline: () => aztec.AztecTextFormat.FORMAT_UNDERLINE,
23
+ strikethrough: () => aztec.AztecTextFormat.FORMAT_STRIKETHROUGH,
24
+ code: () => aztec.AztecTextFormat.FORMAT_CODE,
25
+ blockquote: () => aztec.AztecTextFormat.FORMAT_QUOTE,
26
+ bulletList: () => aztec.AztecTextFormat.FORMAT_UNORDERED_LIST,
27
+ orderedList: () => aztec.AztecTextFormat.FORMAT_ORDERED_LIST,
28
+ taskList: () => aztec.AztecTextFormat.FORMAT_TASK_LIST,
29
+ codeBlock: () => aztec.AztecTextFormat.FORMAT_PREFORMAT,
30
+ horizontalRule: () => aztec.AztecTextFormat.FORMAT_HORIZONTAL_RULE,
31
+ highlight: () => aztec.AztecTextFormat.FORMAT_MARK,
32
+ subscript: () => aztec.AztecTextFormat.FORMAT_SUBSCRIPT,
33
+ superscript: () => aztec.AztecTextFormat.FORMAT_SUPERSCRIPT,
34
+ paragraph: () => aztec.AztecTextFormat.FORMAT_PARAGRAPH,
35
+ heading1: () => aztec.AztecTextFormat.FORMAT_HEADING_1,
36
+ heading2: () => aztec.AztecTextFormat.FORMAT_HEADING_2,
37
+ heading3: () => aztec.AztecTextFormat.FORMAT_HEADING_3,
38
+ heading4: () => aztec.AztecTextFormat.FORMAT_HEADING_4,
39
+ heading5: () => aztec.AztecTextFormat.FORMAT_HEADING_5,
40
+ heading6: () => aztec.AztecTextFormat.FORMAT_HEADING_6,
41
+ alignLeft: () => aztec.AztecTextFormat.FORMAT_ALIGN_LEFT,
42
+ alignCenter: () => aztec.AztecTextFormat.FORMAT_ALIGN_CENTER,
43
+ alignRight: () => aztec.AztecTextFormat.FORMAT_ALIGN_RIGHT,
44
+ }
45
+
46
+ const AZTEC_OF: [RichTextFormat, () => any][] = Object.entries(FORMATS) as any
47
+
48
+ const formatOf = (format: RichTextFormat) =>
49
+ format === 'link' ? aztec.AztecTextFormat.FORMAT_LINK : FORMATS[format]()
50
+
51
+ class AztecEditor extends View {
52
+ createNativeView() {
53
+ return new aztec.AztecText(this._context)
54
+ }
55
+ }
56
+
57
+ declare module '@nativescript-community/octane/intrinsics' {
58
+ interface NativeScriptElements {
59
+ xplatrichtext: Attributes<typeof AztecEditor>
60
+ }
61
+ }
62
+
63
+ registerElement('xplatrichtext', AztecEditor)
64
+
65
+ export const supported = true
66
+
67
+ /** Active formats at the selection, mapped back to facade spellings. */
68
+ const activeFormats = (editor: any): RichTextFormat[] => {
69
+ const styles = editor.getAppliedStyles(
70
+ editor.getSelectionStart(),
71
+ editor.getSelectionEnd(),
72
+ )
73
+
74
+ const count = styles?.size?.() ?? 0
75
+ const active = new Set<any>()
76
+ for (let i = 0; i < count; i++) active.add(styles.get(i))
77
+
78
+ const out: RichTextFormat[] = []
79
+ for (const [name, get] of AZTEC_OF) {
80
+ if (active.has(get())) out.push(name)
81
+ }
82
+
83
+ if (active.has(aztec.AztecTextFormat.FORMAT_LINK)) out.push('link')
84
+ return out
85
+ }
86
+
87
+ /** WordPress Aztec surface on Android — a true Spannable-backed editor, not
88
+ * a WebView. Content in/out is HTML (`fromHtml`/`toPlainHtml`); JSON
89
+ * interchange lives one layer up in @octane-xplat/tiptap. */
90
+ export function RichTextEditor(props: RichTextEditorProps) @{
91
+ const host = useRef<any>(null)
92
+ const lastEmitted = useRef<string | undefined>(undefined)
93
+ const applyingValue = useRef(false)
94
+ const keyListener = useRef<any>(null)
95
+ const emitTimer = useRef<any>(null)
96
+
97
+ // Stable identity — an inline closure re-fires ref(null)/ref(v) per
98
+ // render, which would bounce host.current around the effects below.
99
+ useImperativeHandle(props.ref, () => handle(editor()), []);
100
+
101
+ const bindHost = useCallback((v: any) => {
102
+ host.current = v
103
+ }, [])
104
+
105
+ const editor = () => host.current?.android
106
+
107
+ const setEditable = (next: boolean) => {
108
+ const e = editor()
109
+ if (!e) return
110
+ if (keyListener.current == null) keyListener.current = e.getKeyListener()
111
+ if (next) {
112
+ e.setKeyListener(keyListener.current)
113
+ e.setFocusable(true)
114
+ e.setCursorVisible(true)
115
+ } else {
116
+ e.setKeyListener(null)
117
+ e.setFocusable(false)
118
+ e.setCursorVisible(false)
119
+ }
120
+ }
121
+
122
+ const showKeyboard = (e: any) => {
123
+ try {
124
+ const imm = e.getContext().getSystemService(
125
+ (globalThis as any).android.content.Context.INPUT_METHOD_SERVICE,
126
+ )
127
+
128
+ imm?.showSoftInput(e, androidView.inputmethod.InputMethodManager.SHOW_IMPLICIT)
129
+ } catch {}
130
+ }
131
+
132
+ const emitChange = () => {
133
+ const e = editor()
134
+ if (!e || applyingValue.current) return
135
+ clearTimeout(emitTimer.current)
136
+ emitTimer.current = setTimeout(() => {
137
+ const current = editor()
138
+ if (!current || applyingValue.current) return
139
+ const html = current.toPlainHtml(false)
140
+ lastEmitted.current = html
141
+ props.onChange?.(html)
142
+ }, 150)
143
+ }
144
+
145
+ const handle = (e: any): RichTextEditorHandle => ({
146
+ getHTML: () => e.toPlainHtml(false),
147
+ setHTML: (html: string) => {
148
+ applyingValue.current = true
149
+ try {
150
+ e.fromHtml(html, true)
151
+ lastEmitted.current = html
152
+ } finally {
153
+ applyingValue.current = false
154
+ }
155
+ },
156
+ apply: (format: RichTextFormat) => {
157
+ if (format === 'link') return
158
+ e.toggleFormatting(formatOf(format))
159
+ emitChange()
160
+ },
161
+ linkTo: (url: string, anchor?: string) => {
162
+ e.link(url, anchor ?? url, false)
163
+ emitChange()
164
+ },
165
+ removeLink: () => {
166
+ e.removeLink()
167
+ emitChange()
168
+ },
169
+ isActive: (format: RichTextFormat) => {
170
+ const applied = e.getAppliedStyles(e.getSelectionStart(), e.getSelectionEnd())
171
+ const count = applied?.size?.() ?? 0
172
+ const target = formatOf(format)
173
+ for (let i = 0; i < count; i++) {
174
+ if (applied.get(i) === target) return true
175
+ }
176
+
177
+ return false
178
+ },
179
+ undo: () => e.undo(),
180
+ redo: () => e.redo(),
181
+ focus: () => {
182
+ e.requestFocus()
183
+ showKeyboard(e)
184
+ },
185
+ blur: () => e.clearFocus(),
186
+ isFocused: () => e.isFocused(),
187
+ native: e,
188
+ })
189
+
190
+ useEffect(() => {
191
+ const e = editor()
192
+ if (!e || !aztec) return
193
+
194
+ e.setOnSelectionChangedListener(
195
+ new aztec.AztecText.OnSelectionChangedListener({
196
+ onSelectionChanged: (start: number, end: number) =>
197
+ props.onSelectionChange?.({
198
+ start,
199
+ end,
200
+ active: activeFormats(e),
201
+ }),
202
+ }),
203
+ )
204
+
205
+ e.addTextChangedListener(
206
+ new androidText.TextWatcher({
207
+ beforeTextChanged: () => {},
208
+ onTextChanged: () => {},
209
+ afterTextChanged: () => emitChange(),
210
+ }),
211
+ )
212
+
213
+ e.setOnFocusChangeListener(
214
+ new androidView.View.OnFocusChangeListener({
215
+ onFocusChange: (_v: any, focused: boolean) =>
216
+ focused ? props.onFocus?.() : props.onBlur?.(),
217
+ }),
218
+ )
219
+
220
+ if (props.placeholder !== undefined) e.setHint(props.placeholder)
221
+ setEditable(props.editable !== false)
222
+
223
+ if (props.value !== undefined) {
224
+ applyingValue.current = true
225
+ try {
226
+ e.fromHtml(props.value, true)
227
+ } finally {
228
+ applyingValue.current = false
229
+ }
230
+ }
231
+
232
+ if (props.autofocus) {
233
+ e.requestFocus()
234
+ showKeyboard(e)
235
+ }
236
+
237
+ if (props.android) Object.assign(e, props.android)
238
+ props.onReady?.()
239
+ }, [])
240
+
241
+ // `value` lands like a controlled prop, minus the loop: text the editor
242
+ // emitted through onChange is never pushed back; any other change is an
243
+ // external reset (and resets Aztec's undo stack, like PM setContent).
244
+ useEffect(() => {
245
+ const e = editor()
246
+ if (!e || props.value === undefined) return
247
+ if (props.value === lastEmitted.current) return
248
+ applyingValue.current = true
249
+ try {
250
+ e.fromHtml(props.value, true)
251
+ } finally {
252
+ applyingValue.current = false
253
+ }
254
+ }, [props.value])
255
+
256
+ useEffect(() => {
257
+ setEditable(props.editable !== false)
258
+ }, [props.editable])
259
+
260
+ useEffect(() => {
261
+ const e = editor()
262
+ if (e && props.placeholder !== undefined) e.setHint(props.placeholder)
263
+ }, [props.placeholder])
264
+
265
+ <xplatrichtext
266
+ ref={bindHost}
267
+ id={props.id}
268
+ className={props.className}
269
+ style={props.style}
270
+ row={props.row}
271
+ col={props.col}
272
+ rowSpan={props.rowSpan}
273
+ colSpan={props.colSpan}
274
+ dock={props.dock}
275
+ left={props.left}
276
+ top={props.top}
277
+ flexGrow={props.flexGrow}
278
+ flexShrink={props.flexShrink}
279
+ alignSelf={props.alignSelf}
280
+ order={props.order}
281
+ />;
282
+ }
@@ -0,0 +1,15 @@
1
+ /** @jsxImportSource @nativescript-community/octane */
2
+ import type { RichTextEditorProps } from './types'
3
+
4
+ export const supported = false
5
+
6
+ /** iOS bring-up is deferred — the Swift facade over Aztec-iOS lands in a
7
+ * follow-up. Renders the placeholder so a shared screen still mounts. */
8
+ export function RichTextEditor(props: RichTextEditorProps) @{
9
+ <label
10
+ id={props.id}
11
+ className={props.className}
12
+ style={props.style}
13
+ text="Rich text editing is not supported on iOS yet."
14
+ />;
15
+ }
@@ -0,0 +1,15 @@
1
+ /** @jsxImportSource @nativescript-community/octane */
2
+ import type { RichTextEditorProps } from './types'
3
+
4
+ export const supported = false
5
+
6
+ /** Windows bring-up is deferred — renders the placeholder so a shared
7
+ * screen still mounts. */
8
+ export function RichTextEditor(props: RichTextEditorProps) @{
9
+ <label
10
+ id={props.id}
11
+ className={props.className}
12
+ style={props.style}
13
+ text="Rich text editing is not supported on Windows yet."
14
+ />;
15
+ }
@@ -0,0 +1,119 @@
1
+ const formats = [
2
+ 'bold',
3
+ 'italic',
4
+ 'underline',
5
+ 'strikethrough',
6
+ 'code',
7
+ 'blockquote',
8
+ 'bulletList',
9
+ 'orderedList',
10
+ 'taskList',
11
+ 'codeBlock',
12
+ 'horizontalRule',
13
+ 'highlight',
14
+ 'subscript',
15
+ 'superscript',
16
+ 'paragraph',
17
+ 'heading1',
18
+ 'heading2',
19
+ 'heading3',
20
+ 'heading4',
21
+ 'heading5',
22
+ 'heading6',
23
+ 'alignLeft',
24
+ 'alignCenter',
25
+ 'alignRight',
26
+ 'link',
27
+ ]
28
+
29
+ /** Internal document transport. Engine handles never cross the WebKit boundary. */
30
+ export function installEditorDocument(render: (props: any) => void) {
31
+ let handle: any = null
32
+ const pending: { method: string; args: any[] }[] = []
33
+ let selection = { start: 0, end: 0, active: [] as string[] }
34
+ const post = (event: string, extra: object = {}) => {
35
+ const packet = {
36
+ event,
37
+ html: handle?.getHTML() ?? '',
38
+ json: handle?.getJSON?.() ?? null,
39
+ focused: handle?.isFocused() ?? false,
40
+ selection,
41
+ ...extra,
42
+ }
43
+
44
+ ;(window as any).webkit.messageHandlers.editor.postMessage(JSON.stringify(packet))
45
+ }
46
+
47
+ const snapshot = (event: string) => {
48
+ setTimeout(() => {
49
+ if (!handle) {
50
+ return
51
+ }
52
+
53
+ selection = { ...selection, active: formats.filter((f) => handle.isActive(f)) }
54
+ post(event)
55
+ }, 0)
56
+ }
57
+
58
+ const command = (packet: { method: string; args: any[] }) => {
59
+ if (packet.method === 'props') {
60
+ render({ ...packet.args[0], ...callbacks })
61
+ if (handle) {
62
+ snapshot('snapshot')
63
+ }
64
+ } else if (!handle) {
65
+ pending.push(packet)
66
+ } else if (
67
+ [
68
+ 'setHTML',
69
+ 'setJSON',
70
+ 'apply',
71
+ 'linkTo',
72
+ 'removeLink',
73
+ 'undo',
74
+ 'redo',
75
+ 'focus',
76
+ 'blur',
77
+ ].includes(packet.method)
78
+ ) {
79
+ handle[packet.method]?.(...packet.args)
80
+ snapshot('snapshot')
81
+ }
82
+ }
83
+
84
+ const callbacks = {
85
+ ref(next: any) {
86
+ if (!next || next === handle) {
87
+ return
88
+ }
89
+
90
+ handle = next
91
+ snapshot('ready')
92
+ for (const packet of pending.splice(0)) {
93
+ command(packet)
94
+ }
95
+ },
96
+ onChange: () => snapshot('change'),
97
+ onSelectionChange(next: typeof selection) {
98
+ selection = next
99
+ snapshot('selection')
100
+ },
101
+ onFocus: () => snapshot('focus'),
102
+ onBlur: () => snapshot('blur'),
103
+ }
104
+
105
+ ;(window as any).__xplatEditorReceive = (serialized: string) => {
106
+ try {
107
+ command(JSON.parse(serialized))
108
+ } catch (error) {
109
+ post('error', { message: String(error) })
110
+ }
111
+ }
112
+
113
+ window.addEventListener('error', (event) => post('error', { message: event.message }))
114
+ window.addEventListener('unhandledrejection', (event) =>
115
+ post('error', { message: String(event.reason) }),
116
+ )
117
+
118
+ post('boot')
119
+ }
@@ -0,0 +1,133 @@
1
+ import type { RichTextEditorHandle, RichTextEditorProps, RichTextFormat } from './types'
2
+
3
+ export interface EditorPacket {
4
+ event: string
5
+ html?: string
6
+ json?: any
7
+ selection?: { start: number; end: number; active: RichTextFormat[] }
8
+ focused?: boolean
9
+ message?: string
10
+ }
11
+
12
+ /** Internal transport adapter: synchronous getters read the last WebKit snapshot. */
13
+ export function createEditorSession(
14
+ send: (packet: string) => void,
15
+ native: any,
16
+ readProps: () => RichTextEditorProps & {
17
+ onJSONChange?: (doc: any) => void
18
+ onJSONReady?: (ok: boolean) => void
19
+ },
20
+ ) {
21
+ let disposed = false
22
+ let ready = false
23
+ let html = ''
24
+ let json: any = null
25
+ let active: RichTextFormat[] = []
26
+ let focused = false
27
+ let pending: string[] = []
28
+ const command = (method: string, ...args: any[]) => {
29
+ if (disposed) {
30
+ return
31
+ }
32
+
33
+ const packet = JSON.stringify({ method, args })
34
+ if (ready) {
35
+ send(packet)
36
+ } else {
37
+ pending.push(packet)
38
+ }
39
+ }
40
+
41
+ const handle: RichTextEditorHandle & { getJSON(): any; setJSON(doc: any): void } = {
42
+ getHTML: () => html,
43
+ setHTML: (value) => command('setHTML', value),
44
+ getJSON: () => json,
45
+ setJSON: (doc) => command('setJSON', doc),
46
+ apply: (format) => command('apply', format),
47
+ linkTo: (url, anchor) => command('linkTo', url, anchor),
48
+ removeLink: () => command('removeLink'),
49
+ isActive: (format) => active.includes(format),
50
+ undo: () => command('undo'),
51
+ redo: () => command('redo'),
52
+ focus: () => command('focus'),
53
+ blur: () => command('blur'),
54
+ isFocused: () => focused,
55
+ native,
56
+ }
57
+
58
+ return {
59
+ handle,
60
+ update(props: object) {
61
+ command('props', props)
62
+ },
63
+ receive(serialized: string) {
64
+ if (disposed) {
65
+ return
66
+ }
67
+
68
+ const packet: EditorPacket = JSON.parse(serialized)
69
+ const props = readProps()
70
+ if (packet.html !== undefined) {
71
+ html = packet.html
72
+ }
73
+
74
+ if ('json' in packet) {
75
+ json = packet.json
76
+ }
77
+
78
+ if (packet.selection) {
79
+ active = packet.selection.active
80
+ }
81
+
82
+ if (packet.focused !== undefined) {
83
+ focused = packet.focused
84
+ }
85
+
86
+ switch (packet.event) {
87
+ case 'boot':
88
+ ready = true
89
+ for (const packet of pending) {
90
+ send(packet)
91
+ }
92
+
93
+ pending = []
94
+ break
95
+ case 'ready':
96
+ props.onJSONReady?.(true)
97
+ props.onReady?.()
98
+ break
99
+ case 'change':
100
+ props.onChange?.(html)
101
+ if (json) {
102
+ props.onJSONChange?.(json)
103
+ }
104
+
105
+ break
106
+ case 'selection':
107
+ if (packet.selection) {
108
+ props.onSelectionChange?.(packet.selection)
109
+ }
110
+
111
+ break
112
+ case 'focus':
113
+ props.onFocus?.()
114
+ break
115
+ case 'blur':
116
+ props.onBlur?.()
117
+ break
118
+ case 'error':
119
+ console.error('[macos-editor] ' + packet.message)
120
+ break
121
+ }
122
+ },
123
+ dispose() {
124
+ if (disposed) {
125
+ return
126
+ }
127
+
128
+ disposed = true
129
+ pending = []
130
+ native.dispose()
131
+ },
132
+ }
133
+ }
@@ -0,0 +1,4 @@
1
+ export { RichTextEditor } from './Editor.macos'
2
+ /** The AppKit/WKWebView backend is implemented. */
3
+ export const supported = true
4
+ export type * from './types'
package/src/index.ts ADDED
@@ -0,0 +1,5 @@
1
+ // Keep this relative import extensionless so the NativeScript resolver picks
2
+ // the Android (Aztec) or iOS (stub until the Swift facade lands) component
3
+ // for the current build target.
4
+ export { RichTextEditor, supported } from './RichTextEditor'
5
+ export type * from './types'
@@ -0,0 +1,11 @@
1
+ import type { RichTextEditorProps } from './types'
2
+
3
+ export const supported = false
4
+
5
+ /** Web routes through @octane-xplat/tiptap — this leaf only exists for the
6
+ * native (Aztec) surface. */
7
+ export function RichTextEditor(_props: RichTextEditorProps): null {
8
+ return null
9
+ }
10
+
11
+ export type * from './types'
package/src/types.ts ADDED
@@ -0,0 +1,100 @@
1
+ import type { Octane } from 'octane/jsx-runtime'
2
+
3
+ /** Android uses WordPress Aztec; AppKit uses StarterKit in WKWebView.
4
+ * iOS is stubbed. Web/Windows use the tiptap facade instead of this leaf. */
5
+ export type RichTextFormat =
6
+ | 'bold'
7
+ | 'italic'
8
+ | 'underline'
9
+ | 'strikethrough'
10
+ | 'code'
11
+ | 'blockquote'
12
+ | 'bulletList'
13
+ | 'orderedList'
14
+ | 'taskList'
15
+ | 'codeBlock'
16
+ | 'horizontalRule'
17
+ | 'highlight'
18
+ | 'subscript'
19
+ | 'superscript'
20
+ | 'paragraph'
21
+ | 'heading1'
22
+ | 'heading2'
23
+ | 'heading3'
24
+ | 'heading4'
25
+ | 'heading5'
26
+ | 'heading6'
27
+ | 'alignLeft'
28
+ | 'alignCenter'
29
+ | 'alignRight'
30
+ | 'link'
31
+
32
+ export interface RichTextEditorHandle {
33
+ /** Serialized document HTML (Aztec's "plain" HTML — no contenteditable
34
+ * markup). */
35
+ /** AppKit: returns the latest WebKit snapshot; commands are asynchronous. */
36
+ getHTML(): string
37
+ /** Replace the document. Android resets Aztec history; AppKit uses engine history. */
38
+ setHTML(html: string): void
39
+ /** Toggle a format at the current selection. `link` needs
40
+ * `linkTo(url, anchor)` instead. */
41
+ apply(format: RichTextFormat): void
42
+ /** Insert or re-target a link over the current selection. */
43
+ linkTo(url: string, anchor?: string): void
44
+ removeLink(): void
45
+ /** Formats active at the selection — powers toolbar highlight state. */
46
+ isActive(format: RichTextFormat): boolean
47
+ /** Android: Aztec's history batches keyboard input only — format
48
+ * toggles and programmatic edits do not register. */
49
+ undo(): void
50
+ redo(): void
51
+ focus(): void
52
+ blur(): void
53
+ isFocused(): boolean
54
+ /** The platform surface — `AztecText` on Android. */
55
+ /** AppKit: the XplatEditorHost transport, not the browser engine instance. */
56
+ native: any
57
+ }
58
+
59
+ export interface RichTextSelectionChange {
60
+ start: number
61
+ end: number
62
+ /** Formats active at the caret/range, in facade spellings. */
63
+ active: RichTextFormat[]
64
+ }
65
+
66
+ export interface RichTextEditorProps {
67
+ id?: string
68
+ className?: string
69
+ style?: any
70
+ /** Initial document HTML. Re-applied only when the string changes
71
+ * externally — text the editor emitted back through `onChange` is never
72
+ * re-pushed, so typing doesn't reparse the document. */
73
+ value?: string
74
+ placeholder?: string
75
+ editable?: boolean
76
+ autofocus?: boolean
77
+ onReady?: () => void
78
+ onChange?: (html: string) => void
79
+ onSelectionChange?: (event: RichTextSelectionChange) => void
80
+ onFocus?: () => void
81
+ onBlur?: () => void
82
+ /** Imperative handle — fires once when the native editor exists. */
83
+ ref?: Octane.Ref<RichTextEditorHandle>
84
+ /** Per-platform escape bag — properties are assigned onto the native
85
+ * `AztecText` after the leaf's own props. */
86
+ android?: Record<string, any>
87
+ ios?: Record<string, any>
88
+ // NativeScript layout-child attributes the driver reads on this element.
89
+ row?: number | string
90
+ col?: number | string
91
+ rowSpan?: number
92
+ colSpan?: number
93
+ dock?: 'left' | 'top' | 'right' | 'bottom'
94
+ left?: number
95
+ top?: number
96
+ flexGrow?: number
97
+ flexShrink?: number
98
+ alignSelf?: 'auto' | 'flex-start' | 'flex-end' | 'center' | 'stretch' | 'baseline'
99
+ order?: number
100
+ }
@@ -0,0 +1,2 @@
1
+ /** Internal WebKit document transport installer. */
2
+ export declare function installEditorDocument(render: (props: any) => void): void
@@ -0,0 +1,6 @@
1
+ import type { UniversalComponent } from 'octane/universal'
2
+ import type { RichTextEditorProps } from './types'
3
+
4
+ export declare const RichTextEditor: UniversalComponent<RichTextEditorProps>
5
+ export declare const supported: boolean
6
+ export type * from './types'