react-native-richify 1.0.6 → 1.0.7

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.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # react-native-richify
2
2
 
3
- A rich text input for React Native with a normal `TextInput` editing surface, a built-in formatting toolbar, and live Markdown or HTML output.
3
+ A React Native rich text input with a normal `TextInput` editing surface, a horizontally scrollable toolbar, and live Markdown or HTML output below the editor.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/react-native-richify.svg)](https://www.npmjs.com/package/react-native-richify)
6
6
  [![license](https://img.shields.io/npm/l/react-native-richify.svg)](https://github.com/soumya-99/react-native-richify/blob/main/LICENSE)
@@ -8,25 +8,39 @@ A rich text input for React Native with a normal `TextInput` editing surface, a
8
8
  ## Features
9
9
 
10
10
  - Native editing with no WebView
11
- - Rich inline formatting: bold, italic, underline, strikethrough, code
12
- - Line-level formatting: H1, H2, H3, bullet list, ordered list, left/center/right alignment
13
- - Link support on selected text
14
- - Live output panel with Markdown or HTML serialization
15
- - Raw output view or rendered preview view
16
- - Auto-growing input with configurable input and preview heights
17
- - Custom toolbar items, custom toolbar rendering, and theme overrides
18
- - JSON import/export through typed segment data
11
+ - Built-in toolbar uses Lucide React Native icons by default
12
+ - Inline formatting: bold, italic, underline, strikethrough, code
13
+ - Line formatting: H1, H2, H3, bullet list, ordered list, left, center, right
14
+ - Link formatting on selected text
15
+ - Image insertion through the built-in toolbar
16
+ - Output panel can show Markdown or HTML
17
+ - Output panel can show raw serialized text or rendered preview
18
+ - Auto-growing editor with configurable input and preview height limits
19
+ - Custom toolbar items accept text, emoji, or React elements
19
20
  - Headless `useRichText` hook for fully custom editors
21
+ - Typed JSON import and export of editor content
20
22
 
21
23
  ## Installation
22
24
 
25
+ Install the package and `react-native-svg`:
26
+
23
27
  ```bash
24
- npm install react-native-richify
25
- # or
26
- yarn add react-native-richify
28
+ npm install react-native-richify react-native-svg
27
29
  ```
28
30
 
29
- No native modules are required. The package works in Expo and bare React Native apps.
31
+ or:
32
+
33
+ ```bash
34
+ yarn add react-native-richify react-native-svg
35
+ ```
36
+
37
+ `react-native-svg` is required because the built-in toolbar uses Lucide icons. If you want to use Lucide icons in your own custom toolbar items, install that in your app too:
38
+
39
+ ```bash
40
+ npm install lucide-react-native
41
+ ```
42
+
43
+ The package works in Expo and bare React Native apps. In Expo, `react-native-svg` is usually already available.
30
44
 
31
45
  ## Quick Start
32
46
 
@@ -43,10 +57,11 @@ export default function App() {
43
57
  showToolbar
44
58
  showOutputPreview
45
59
  defaultOutputFormat="markdown"
60
+ defaultOutputPreviewMode="rendered"
46
61
  onChangeText={(text) => console.log('Plain text:', text)}
47
- onChangeOutput={(output, format) =>
48
- console.log(`Serialized ${format}:`, output)
49
- }
62
+ onChangeOutput={(output, format) => {
63
+ console.log(format, output);
64
+ }}
50
65
  />
51
66
  </View>
52
67
  );
@@ -56,137 +71,139 @@ export default function App() {
56
71
  This gives you:
57
72
 
58
73
  - a visible auto-growing `TextInput`
59
- - a horizontally scrollable toolbar
60
- - a collapsible output panel that opens when content exists
61
- - Markdown output by default, with HTML and rendered preview toggles in the toolbar
74
+ - the default Lucide-based toolbar
75
+ - live Markdown output by default
76
+ - a preview panel that opens below the input when content exists
62
77
 
63
- ## How Editing Works
78
+ ## How The Editor Works
64
79
 
65
- The editor stores content as `StyledSegment[]`, not HTML strings. The plain text you type is always the source of truth for editing, and formatting metadata is applied to matching text ranges or lines.
80
+ The editing surface is always a normal `TextInput`. The library does not render styled text on top of the input.
81
+
82
+ Internally, content is stored as `StyledSegment[]`. Each segment contains plain text and formatting metadata. That lets the library:
83
+
84
+ - keep typing behavior predictable
85
+ - apply formatting to selected ranges or future input
86
+ - serialize the same content to Markdown or HTML
87
+ - export and re-import structured JSON safely
66
88
 
67
89
  Formatting rules:
68
90
 
69
- - If no text is selected, pressing a style button affects the next characters you type.
70
- - If text is selected, pressing a style button formats the selected range immediately.
71
- - Headings, lists, and alignment are line-level controls.
72
- - Every formatting button is toggleable except the output controls: `MD`, `HTML`, `Raw`, and `View`.
91
+ - If nothing is selected, pressing a format button affects the next characters you type.
92
+ - If text is selected, pressing a format button applies or removes that format immediately.
93
+ - Heading, list, and alignment controls work at line level.
94
+ - Every formatting button is toggleable except `MD`, `HTML`, `Raw`, and `View`.
73
95
 
74
96
  ## Built-in Toolbar
75
97
 
76
- The default toolbar includes these controls:
98
+ The default toolbar is horizontally scrollable and ships with these controls:
77
99
 
78
- | Button | Purpose |
100
+ | Control | Behavior |
79
101
  | --- | --- |
80
- | `B` | Toggle bold |
81
- | `I` | Toggle italic |
82
- | `U` | Toggle underline |
83
- | `S` | Toggle strikethrough |
84
- | `<>` | Toggle inline code |
85
- | `H1`, `H2`, `H3` | Toggle heading level on the current line |
86
- | `•≡` | Toggle bullet list on the current line |
87
- | `1≡` | Toggle ordered list on the current line |
88
- | `🔗` | Apply or clear a hyperlink on the current selection |
89
- | `⇤`, `↔`, `⇥` | Toggle left, center, or right alignment on the current line |
90
- | `MD`, `HTML` | Switch serialized output format |
91
- | `Raw`, `View` | Switch between literal serialized output and rendered preview |
102
+ | Bold, Italic, Underline, Strikethrough, Code | Toggle inline formatting |
103
+ | Heading 1, Heading 2, Heading 3 | Toggle heading level on the current line |
104
+ | Bullet List, Ordered List | Toggle list type on the current line |
105
+ | Link | Apply or clear a hyperlink on the selection |
106
+ | Image | Insert an image through your app's image-picking flow |
107
+ | Align Left, Align Center, Align Right | Toggle paragraph alignment |
108
+ | MD, HTML | Switch serialization format |
109
+ | Raw, View | Switch between literal output and rendered preview |
92
110
 
93
111
  Notes:
94
112
 
95
- - Pressing the active heading again removes that heading.
96
- - Pressing the active list or alignment button again clears it.
97
- - The toolbar is horizontally scrollable by default.
98
- - Image insertion is not part of the built-in toolbar right now.
113
+ - Pressing an active heading button again clears that heading.
114
+ - Pressing an active list or alignment button again clears it.
115
+ - Link clears when the current selection already has a link.
116
+ - Image is a callback-driven action. Your app decides how the URI is chosen.
99
117
 
100
118
  ## Output Panel
101
119
 
102
- The panel below the input can show either:
120
+ The preview panel below the editor is optional and controlled by `showOutputPreview`.
121
+
122
+ It supports two output formats:
103
123
 
104
- - literal serialized output (`Raw`)
105
- - rendered rich output (`View`)
124
+ - `markdown`
125
+ - `html`
106
126
 
107
- It can serialize as either:
127
+ It also supports two display modes:
108
128
 
109
- - Markdown
110
- - HTML
129
+ - `literal`: show the raw Markdown or HTML text
130
+ - `rendered`: render the rich output visually
111
131
 
112
132
  Useful props:
113
133
 
114
- | Prop | Description |
134
+ | Prop | Purpose |
115
135
  | --- | --- |
116
- | `showOutputPreview` | Show or hide the output panel entirely |
117
- | `outputFormat` | Controlled output format |
118
- | `defaultOutputFormat` | Initial output format for uncontrolled usage |
119
- | `outputPreviewMode` | Controlled preview mode: `'literal'` or `'rendered'` |
120
- | `defaultOutputPreviewMode` | Initial preview mode for uncontrolled usage |
121
- | `maxOutputHeight` | Max height of the output panel before it scrolls |
122
- | `onChangeOutput` | Called whenever serialized output changes |
123
- | `onChangeOutputFormat` | Called when toolbar output format changes |
136
+ | `outputFormat` | Controlled Markdown or HTML mode |
137
+ | `defaultOutputFormat` | Initial format for uncontrolled usage |
138
+ | `outputPreviewMode` | Controlled raw or rendered mode |
139
+ | `defaultOutputPreviewMode` | Initial preview mode |
140
+ | `maxOutputHeight` | Max preview height before the panel scrolls |
141
+ | `onChangeOutput` | Receives the serialized output on every change |
142
+ | `onChangeOutputFormat` | Called when toolbar format changes |
124
143
  | `onChangeOutputPreviewMode` | Called when toolbar preview mode changes |
125
144
 
126
- Example:
127
-
128
- ```tsx
129
- import React, { useState } from 'react';
130
- import { RichTextInput, type OutputFormat } from 'react-native-richify';
131
-
132
- export function ControlledOutputEditor() {
133
- const [format, setFormat] = useState<OutputFormat>('html');
134
-
135
- return (
136
- <RichTextInput
137
- outputFormat={format}
138
- onChangeOutputFormat={setFormat}
139
- defaultOutputPreviewMode="rendered"
140
- maxOutputHeight={220}
141
- onChangeOutput={(output) => {
142
- console.log(output);
143
- }}
144
- />
145
- );
146
- }
147
- ```
148
-
149
145
  ## Links
150
146
 
151
147
  Link formatting is selection-based. Select text, then press the link button.
152
148
 
153
- There are two ways to use it:
149
+ You have two common approaches:
154
150
 
155
- 1. Provide `onRequestLink` and show your own prompt, sheet, or modal.
156
- 2. Rely on the built-in fallback, which only auto-links when the selected text already looks like a URL, domain, or email address.
157
-
158
- Custom link flow:
151
+ 1. Provide `onRequestLink` and open your own prompt, modal, or bottom sheet.
152
+ 2. Use the built-in fallback, which auto-links only when the selected text already looks like a URL, domain, or email address.
159
153
 
160
154
  ```tsx
161
155
  <RichTextInput
162
156
  onRequestLink={({ selectedText, currentUrl, applyLink }) => {
163
- console.log('Selected:', selectedText);
164
- console.log('Existing URL:', currentUrl);
165
-
166
- // Replace this with your own modal, bottom sheet, or form.
157
+ console.log(selectedText, currentUrl);
167
158
  applyLink('https://example.com');
168
159
  }}
169
160
  />
170
161
  ```
171
162
 
172
- If the selection already has a link, pressing the link button clears it.
163
+ If the current selection already contains a link, pressing the button clears it.
173
164
 
174
- ## Sizing and TextInput Behavior
165
+ ## Images
175
166
 
176
- `RichTextInput` uses a normal visible `TextInput`.
167
+ The built-in toolbar includes an image button. It does not open the device picker itself. Instead, it calls `onRequestImage`, and your app decides how to produce the final image URI.
177
168
 
178
- - `minHeight` controls the minimum editor height.
179
- - `maxHeight` limits the editor height before the input itself scrolls.
180
- - `maxOutputHeight` limits the output panel height before the preview scrolls.
181
- - `textInputProps` lets you pass through additional native `TextInput` props.
169
+ ```tsx
170
+ <RichTextInput
171
+ onRequestImage={async ({ insertImage }) => {
172
+ const result = await pickImageFromYourApp();
173
+ if (!result?.uri) {
174
+ return;
175
+ }
176
+
177
+ insertImage(result.uri, {
178
+ alt: result.fileName ?? 'Selected image',
179
+ placeholder: '[image]',
180
+ });
181
+ }}
182
+ />
183
+ ```
182
184
 
183
- Example:
185
+ Behavior notes:
186
+
187
+ - The editor inserts a plain text placeholder into the input so typing remains stable.
188
+ - The segment also stores `imageSrc` and optional `imageAlt` metadata.
189
+ - `Raw` mode shows Markdown or HTML image output.
190
+ - `View` mode renders the image in the output panel.
191
+ - If you do not provide `onRequestImage`, the fallback only works when the selected text already looks like an image URL such as `https://site.com/photo.png`.
192
+
193
+ ## Sizing And Input Behavior
194
+
195
+ `RichTextInput` grows with the text by default.
196
+
197
+ - `minHeight` sets the minimum input height.
198
+ - `maxHeight` limits the input height before the `TextInput` itself scrolls.
199
+ - `maxOutputHeight` limits the preview panel height before it scrolls.
200
+ - `textInputProps` passes additional native `TextInput` props through to the editor.
184
201
 
185
202
  ```tsx
186
203
  <RichTextInput
187
204
  minHeight={140}
188
205
  maxHeight={280}
189
- maxOutputHeight={200}
206
+ maxOutputHeight={220}
190
207
  textInputProps={{
191
208
  autoCapitalize: 'sentences',
192
209
  keyboardType: 'default',
@@ -194,89 +211,69 @@ Example:
194
211
  />
195
212
  ```
196
213
 
197
- ## API Overview
198
-
199
- ### `<RichTextInput />`
200
-
201
- Most common props:
202
-
203
- | Prop | Type | Default | Description |
204
- | --- | --- | --- | --- |
205
- | `initialSegments` | `StyledSegment[]` | empty content | Initial document value |
206
- | `onChangeSegments` | `(segments) => void` | - | Called with the rich document model |
207
- | `onChangeText` | `(text) => void` | - | Called with plain text |
208
- | `placeholder` | `string` | `"Start typing..."` | Placeholder text |
209
- | `showToolbar` | `boolean` | `true` | Show the built-in toolbar |
210
- | `toolbarPosition` | `'top' \| 'bottom'` | `'top'` | Place toolbar above or below the editor |
211
- | `toolbarItems` | `ToolbarItem[]` | default items | Replace the built-in toolbar item list |
212
- | `theme` | `RichTextTheme` | default theme | Visual overrides |
213
- | `showOutputPreview` | `boolean` | `true` | Enable the output panel |
214
- | `multiline` | `boolean` | `true` | Standard `TextInput` multiline editing |
215
- | `minHeight` | `number` | `120` | Minimum editor height |
216
- | `maxHeight` | `number` | - | Maximum editor height before scrolling |
217
- | `autoFocus` | `boolean` | `false` | Focus input on mount |
218
- | `textInputProps` | `TextInputProps` subset | - | Additional native input props |
219
- | `renderToolbar` | `(props) => ReactElement` | - | Render a completely custom toolbar |
220
- | `onReady` | `(actions) => void` | - | Access editor actions outside the toolbar |
221
-
222
- ### Actions from `onReady` or `useRichText`
223
-
224
- | Action | Description |
225
- | --- | --- |
226
- | `toggleFormat(format)` | Toggle `bold`, `italic`, `underline`, `strikethrough`, or `code` |
227
- | `setHeading(level)` | Toggle `h1`, `h2`, `h3`, or clear with `none` |
228
- | `setListType(type)` | Toggle `bullet`, `ordered`, or clear with `none` |
229
- | `setTextAlign(align)` | Toggle `left`, `center`, or `right` alignment |
230
- | `setLink(url?)` | Apply or clear hyperlink formatting |
231
- | `setColor(color)` | Set text color |
232
- | `setBackgroundColor(color)` | Set highlight color |
233
- | `setFontSize(size)` | Set font size |
234
- | `handleTextChange(text)` | Sync plain text input changes |
235
- | `handleSelectionChange(selection)` | Sync `TextInput` selection changes |
236
- | `isFormatActive(format)` | Check active state for inline formats |
237
- | `getSelectionStyle()` | Read the common style for the current selection |
238
- | `getOutput(format?)` | Serialize current content as Markdown or HTML |
239
- | `getPlainText()` | Get plain text only |
240
- | `exportJSON()` | Export the current segment array |
241
- | `importJSON(segments)` | Replace content from a saved segment array |
242
- | `clear()` | Reset the editor |
243
-
244
- ## Custom Toolbar
245
-
246
- Use `toolbarItems` when you only want to replace the built-in buttons, or `renderToolbar` when you want full control.
247
-
248
- ### `toolbarItems`
214
+ ## Customizing The Toolbar
249
215
 
250
- Supported built-in item fields:
216
+ Use `toolbarItems` when you want to keep the built-in toolbar behavior but replace some or all buttons.
251
217
 
252
- - `format`
253
- - `heading`
254
- - `listType`
255
- - `textAlign`
256
- - `outputFormat`
257
- - `outputPreviewMode`
258
- - `actionType: 'link'`
259
- - `onPress`
260
- - `renderButton`
218
+ `ToolbarItem.label` accepts any React node, so you can use:
219
+
220
+ - plain text such as `'B'`
221
+ - emoji content
222
+ - a Lucide icon such as `<Bold />`
223
+ - any custom React element
224
+
225
+ When the label is not plain text, set `accessibilityLabel` so screen readers and tests can identify the button correctly.
261
226
 
262
227
  Example:
263
228
 
264
229
  ```tsx
230
+ import React from 'react';
231
+ import { Bold, ImagePlus, List } from 'lucide-react-native';
232
+ import { RichTextInput } from 'react-native-richify';
233
+
265
234
  <RichTextInput
266
235
  toolbarItems={[
267
- { id: 'bold', label: 'B', format: 'bold' },
268
- { id: 'h1', label: 'H1', heading: 'h1' },
269
- { id: 'bullet', label: '•≡', listType: 'bullet' },
270
- { id: 'center', label: '↔', textAlign: 'center' },
236
+ {
237
+ id: 'bold',
238
+ label: <Bold />,
239
+ accessibilityLabel: 'Bold',
240
+ format: 'bold',
241
+ },
242
+ {
243
+ id: 'note',
244
+ label: 'Note',
245
+ onPress: () => console.log('Custom action'),
246
+ },
247
+ {
248
+ id: 'bullet',
249
+ label: <List />,
250
+ accessibilityLabel: 'Bullet list',
251
+ listType: 'bullet',
252
+ },
253
+ {
254
+ id: 'image',
255
+ label: <ImagePlus />,
256
+ accessibilityLabel: 'Insert image',
257
+ actionType: 'image',
258
+ },
271
259
  { id: 'html', label: 'HTML', outputFormat: 'html' },
272
- { id: 'link', label: '🔗', actionType: 'link' },
273
260
  ]}
274
261
  />
275
262
  ```
276
263
 
277
- ### `renderToolbar`
264
+ Supported built-in item fields:
265
+
266
+ - `format`
267
+ - `heading`
268
+ - `listType`
269
+ - `textAlign`
270
+ - `outputFormat`
271
+ - `outputPreviewMode`
272
+ - `actionType: 'link' | 'image'`
273
+ - `onPress`
274
+ - `renderButton`
278
275
 
279
- Use `renderToolbar` if you need a custom layout or your own button components.
276
+ If you need a completely custom layout, use `renderToolbar`:
280
277
 
281
278
  ```tsx
282
279
  <RichTextInput
@@ -286,10 +283,12 @@ Use `renderToolbar` if you need a custom layout or your own button components.
286
283
  outputPreviewMode,
287
284
  onOutputFormatChange,
288
285
  onOutputPreviewModeChange,
286
+ onRequestImage,
289
287
  }) => (
290
288
  <>
291
289
  <MyButton onPress={() => actions.toggleFormat('bold')} label="Bold" />
292
290
  <MyButton onPress={() => actions.setHeading('h2')} label="Heading" />
291
+ <MyButton onPress={onRequestImage} label="Image" />
293
292
  <MyButton
294
293
  onPress={() => onOutputFormatChange('html')}
295
294
  label={outputFormat === 'html' ? 'HTML on' : 'HTML'}
@@ -305,7 +304,7 @@ Use `renderToolbar` if you need a custom layout or your own button components.
305
304
 
306
305
  ## Theming
307
306
 
308
- The theme object lets you style the editor container, input, toolbar, output panel, and button states.
307
+ Use the `theme` prop to override container, toolbar, input, and output styles.
309
308
 
310
309
  ```tsx
311
310
  <RichTextInput
@@ -326,48 +325,42 @@ The theme object lets you style the editor container, input, toolbar, output pan
326
325
  borderColor: '#CBD5E1',
327
326
  },
328
327
  outputContainerStyle: {
329
- marginHorizontal: 12,
330
- marginBottom: 12,
328
+ margin: 12,
331
329
  padding: 12,
332
330
  borderRadius: 12,
333
331
  backgroundColor: '#F8FAFC',
334
332
  },
335
- toolbarButtonActiveStyle: {
336
- backgroundColor: '#CCFBF1',
337
- },
338
333
  }}
339
334
  />
340
335
  ```
341
336
 
342
- Useful theme keys:
337
+ Common theme keys:
343
338
 
344
339
  - `containerStyle`
345
340
  - `inputStyle`
346
341
  - `baseTextStyle`
347
- - `outputContainerStyle`
348
- - `outputLabelStyle`
349
- - `outputTextStyle`
350
- - `renderedOutputStyle`
351
342
  - `toolbarStyle`
352
343
  - `toolbarButtonStyle`
353
344
  - `toolbarButtonActiveStyle`
354
345
  - `toolbarButtonTextStyle`
355
346
  - `toolbarButtonActiveTextStyle`
347
+ - `outputContainerStyle`
348
+ - `outputLabelStyle`
349
+ - `outputTextStyle`
350
+ - `renderedOutputStyle`
356
351
  - `codeStyle`
357
- - `colors.primary`
358
- - `colors.link`
359
352
 
360
- ## Headless Hook
353
+ ## Headless Usage
361
354
 
362
- Use `useRichText` when you want to build your own editor UI.
355
+ Use `useRichText` when you want to build your own editor shell instead of using `<RichTextInput />`.
363
356
 
364
357
  ```tsx
365
358
  import React from 'react';
366
- import { TextInput, View, Button } from 'react-native';
359
+ import { Button, TextInput, View } from 'react-native';
367
360
  import { useRichText } from 'react-native-richify';
368
361
 
369
362
  export function HeadlessEditor() {
370
- const { state, actions } = useRichText({
363
+ const { actions } = useRichText({
371
364
  onChangeText: (text) => console.log(text),
372
365
  });
373
366
 
@@ -389,58 +382,9 @@ export function HeadlessEditor() {
389
382
  }
390
383
  ```
391
384
 
392
- ## Context API
385
+ ## Data Model And Serialization
393
386
 
394
- `RichTextProvider` and `useRichTextContext()` are for custom UIs built on the hook.
395
-
396
- Important: the shipped `<RichTextInput />` manages its own state. Wrapping it in `RichTextProvider` does not automatically bind it to external context state.
397
-
398
- Use context when you want to split a custom editor into multiple components:
399
-
400
- ```tsx
401
- import React from 'react';
402
- import { Button, TextInput, View } from 'react-native';
403
- import {
404
- RichTextProvider,
405
- useRichTextContext,
406
- } from 'react-native-richify';
407
-
408
- function ToolbarRow() {
409
- const { actions } = useRichTextContext();
410
- return <Button title="Italic" onPress={() => actions.toggleFormat('italic')} />;
411
- }
412
-
413
- function EditorField() {
414
- const { actions } = useRichTextContext();
415
-
416
- return (
417
- <TextInput
418
- multiline
419
- value={actions.getPlainText()}
420
- onChangeText={actions.handleTextChange}
421
- onSelectionChange={(event) =>
422
- actions.handleSelectionChange(event.nativeEvent.selection)
423
- }
424
- style={{ minHeight: 120, borderWidth: 1, padding: 12 }}
425
- />
426
- );
427
- }
428
-
429
- export function SplitEditor() {
430
- return (
431
- <RichTextProvider>
432
- <View>
433
- <ToolbarRow />
434
- <EditorField />
435
- </View>
436
- </RichTextProvider>
437
- );
438
- }
439
- ```
440
-
441
- ## Data Model
442
-
443
- Content is stored as segments:
387
+ The editor stores content as typed segments:
444
388
 
445
389
  ```ts
446
390
  type StyledSegment = {
@@ -458,36 +402,23 @@ type StyledSegment = {
458
402
  listType?: 'bullet' | 'ordered' | 'none';
459
403
  textAlign?: 'left' | 'center' | 'right';
460
404
  link?: string;
405
+ imageSrc?: string;
406
+ imageAlt?: string;
461
407
  };
462
408
  };
463
409
  ```
464
410
 
465
- This makes it easy to:
466
-
467
- - store and restore content with `exportJSON()` and `importJSON()`
468
- - inspect formatting state in your own UI
469
- - serialize on demand to Markdown or HTML
470
-
471
- Example:
472
-
473
- ```tsx
474
- const saved = actions.exportJSON();
475
- actions.importJSON(saved);
476
-
477
- const markdown = actions.getOutput('markdown');
478
- const html = actions.getOutput('html');
479
- ```
480
-
481
- ## Current Scope
482
-
483
- Stable built-in features documented above are the recommended way to use the library.
411
+ That makes it straightforward to:
484
412
 
485
- - Rich text formatting, headings, lists, links, alignment, theming, and output serialization are supported.
486
- - The built-in toolbar does not currently expose image insertion.
413
+ - save content with `exportJSON()`
414
+ - restore content with `importJSON()`
415
+ - read plain text with `getPlainText()`
416
+ - generate Markdown with `getOutput('markdown')`
417
+ - generate HTML with `getOutput('html')`
487
418
 
488
419
  ## Contributing
489
420
 
490
- See [AGENTS.md](AGENTS.md) for contributor notes and repository workflow guidance.
421
+ See [AGENTS.md](AGENTS.md) for contributor workflow notes.
491
422
 
492
423
  ## License
493
424
 
@@ -96,6 +96,7 @@ const Toolbar = exports.Toolbar = /*#__PURE__*/_react.default.memo(({
96
96
  contentContainerStyle: styles.scrollContent,
97
97
  children: enrichedItems.map(item => /*#__PURE__*/(0, _jsxRuntime.jsx)(_ToolbarButton.ToolbarButton, {
98
98
  label: item.label,
99
+ accessibilityLabel: item.accessibilityLabel,
99
100
  active: !!item.active,
100
101
  theme: resolvedTheme,
101
102
  renderButton: item.renderButton,
@@ -1 +1 @@
1
- {"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_defaultStyles","_ToolbarButton","_jsxRuntime","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","Toolbar","exports","React","memo","actions","state","items","theme","visible","outputFormat","outputPreviewMode","onOutputFormatChange","onOutputPreviewModeChange","onRequestLink","onRequestImage","renderToolbar","resolvedTheme","DEFAULT_THEME","toolbarItems","DEFAULT_TOOLBAR_ITEMS","enrichedItems","useMemo","selectionStyle","getSelectionStyle","map","item","isActive","format","isFormatActive","heading","listType","textAlign","actionType","link","active","undefined","toolbarStyle","jsx","View","style","children","ScrollView","horizontal","showsHorizontalScrollIndicator","keyboardShouldPersistTaps","contentContainerStyle","styles","scrollContent","ToolbarButton","label","renderButton","onPress","toggleFormat","setHeading","setListType","setTextAlign","id","displayName","StyleSheet","create","flexDirection","alignItems","gap"],"sourceRoot":"../../../src","sources":["components/Toolbar.tsx"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_defaultStyles","_ToolbarButton","_jsxRuntime","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","Toolbar","exports","React","memo","actions","state","items","theme","visible","outputFormat","outputPreviewMode","onOutputFormatChange","onOutputPreviewModeChange","onRequestLink","onRequestImage","renderToolbar","resolvedTheme","DEFAULT_THEME","toolbarItems","DEFAULT_TOOLBAR_ITEMS","enrichedItems","useMemo","selectionStyle","getSelectionStyle","map","item","isActive","format","isFormatActive","heading","listType","textAlign","actionType","link","active","undefined","toolbarStyle","jsx","View","style","children","ScrollView","horizontal","showsHorizontalScrollIndicator","keyboardShouldPersistTaps","contentContainerStyle","styles","scrollContent","ToolbarButton","label","accessibilityLabel","renderButton","onPress","toggleFormat","setHeading","setListType","setTextAlign","id","displayName","StyleSheet","create","flexDirection","alignItems","gap"],"sourceRoot":"../../../src","sources":["components/Toolbar.tsx"],"mappings":"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","ignoreList":[]}