@richscripts/richtexteditor 2.14.0 → 2.15.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.
Files changed (238) hide show
  1. package/README.md +173 -10
  2. package/angular/build-manifest.json +23 -0
  3. package/angular/index.d.ts +71 -76
  4. package/angular/index.mjs +2 -0
  5. package/angular/package.json +4 -0
  6. package/headless/index.d.ts +3 -1
  7. package/headless/index.js +72 -16
  8. package/index.d.ts +33 -54
  9. package/integrations/initialization-scope.js +27 -0
  10. package/integrations/native-mount.js +30 -0
  11. package/integrations/shared.d.ts +23 -1
  12. package/integrations/shared.js +58 -22
  13. package/package.json +25 -3
  14. package/react/index.js +118 -37
  15. package/react/index.mjs +29 -0
  16. package/richtexteditor/all_plugins.js +7113 -1111
  17. package/richtexteditor/all_plugins.min.js +62 -59
  18. package/richtexteditor/lang/emoji/LICENSE-CLDR.txt +41 -0
  19. package/richtexteditor/lang/emoji/rte-emoji-de.js +3 -0
  20. package/richtexteditor/lang/emoji/rte-emoji-es.js +3 -0
  21. package/richtexteditor/lang/emoji/rte-emoji-fr.js +3 -0
  22. package/richtexteditor/lang/emoji/rte-emoji-it.js +3 -0
  23. package/richtexteditor/lang/emoji/rte-emoji-ja.js +3 -0
  24. package/richtexteditor/lang/emoji/rte-emoji-ko.js +3 -0
  25. package/richtexteditor/lang/emoji/rte-emoji-nl.js +3 -0
  26. package/richtexteditor/lang/emoji/rte-emoji-pt-pt.js +3 -0
  27. package/richtexteditor/lang/emoji/rte-emoji-pt.js +3 -0
  28. package/richtexteditor/lang/emoji/rte-emoji-ru.js +3 -0
  29. package/richtexteditor/lang/emoji/rte-emoji-zh-Hans.js +3 -0
  30. package/richtexteditor/lang/emoji/rte-emoji-zh-Hant.js +3 -0
  31. package/richtexteditor/lang/rte-lang-af.js +1 -0
  32. package/richtexteditor/lang/rte-lang-ar.js +1 -0
  33. package/richtexteditor/lang/rte-lang-bg.js +1 -0
  34. package/richtexteditor/lang/rte-lang-bn.js +1 -0
  35. package/richtexteditor/lang/rte-lang-bs.js +1 -0
  36. package/richtexteditor/lang/rte-lang-ca.js +1 -0
  37. package/richtexteditor/lang/rte-lang-cs.js +1 -0
  38. package/richtexteditor/lang/rte-lang-cy.js +1 -0
  39. package/richtexteditor/lang/rte-lang-da.js +1 -0
  40. package/richtexteditor/lang/rte-lang-de.js +1017 -2
  41. package/richtexteditor/lang/rte-lang-el.js +1 -0
  42. package/richtexteditor/lang/rte-lang-en.js +1 -0
  43. package/richtexteditor/lang/rte-lang-es.js +1023 -2
  44. package/richtexteditor/lang/rte-lang-et.js +1 -0
  45. package/richtexteditor/lang/rte-lang-fa.js +1 -0
  46. package/richtexteditor/lang/rte-lang-fi.js +1 -0
  47. package/richtexteditor/lang/rte-lang-fil.js +1 -0
  48. package/richtexteditor/lang/rte-lang-fj.js +1 -0
  49. package/richtexteditor/lang/rte-lang-fr.js +1023 -2
  50. package/richtexteditor/lang/rte-lang-ga.js +1 -0
  51. package/richtexteditor/lang/rte-lang-gu.js +1 -0
  52. package/richtexteditor/lang/rte-lang-he.js +1 -0
  53. package/richtexteditor/lang/rte-lang-hi.js +1 -0
  54. package/richtexteditor/lang/rte-lang-hr.js +1 -0
  55. package/richtexteditor/lang/rte-lang-ht.js +1 -0
  56. package/richtexteditor/lang/rte-lang-hu.js +1 -0
  57. package/richtexteditor/lang/rte-lang-id.js +1 -0
  58. package/richtexteditor/lang/rte-lang-is.js +1 -0
  59. package/richtexteditor/lang/rte-lang-it.js +1023 -2
  60. package/richtexteditor/lang/rte-lang-ja.js +1023 -2
  61. package/richtexteditor/lang/rte-lang-kn.js +1 -0
  62. package/richtexteditor/lang/rte-lang-ko.js +1023 -2
  63. package/richtexteditor/lang/rte-lang-lt.js +1 -0
  64. package/richtexteditor/lang/rte-lang-lv.js +1 -0
  65. package/richtexteditor/lang/rte-lang-mg.js +1 -0
  66. package/richtexteditor/lang/rte-lang-mi.js +1 -0
  67. package/richtexteditor/lang/rte-lang-ml.js +1 -0
  68. package/richtexteditor/lang/rte-lang-mr.js +1 -0
  69. package/richtexteditor/lang/rte-lang-ms.js +1 -0
  70. package/richtexteditor/lang/rte-lang-mt.js +1 -0
  71. package/richtexteditor/lang/rte-lang-mww.js +1 -0
  72. package/richtexteditor/lang/rte-lang-nb.js +1 -0
  73. package/richtexteditor/lang/rte-lang-nl.js +1023 -2
  74. package/richtexteditor/lang/rte-lang-otq.js +1 -0
  75. package/richtexteditor/lang/rte-lang-pa.js +1 -0
  76. package/richtexteditor/lang/rte-lang-pl.js +1 -0
  77. package/richtexteditor/lang/rte-lang-pt-pt.js +1023 -2
  78. package/richtexteditor/lang/rte-lang-pt.js +1023 -2
  79. package/richtexteditor/lang/rte-lang-ro.js +1 -0
  80. package/richtexteditor/lang/rte-lang-ru.js +1023 -2
  81. package/richtexteditor/lang/rte-lang-sk.js +1 -0
  82. package/richtexteditor/lang/rte-lang-sl.js +1 -0
  83. package/richtexteditor/lang/rte-lang-sm.js +1 -0
  84. package/richtexteditor/lang/rte-lang-sr-Cyrl.js +1 -0
  85. package/richtexteditor/lang/rte-lang-sr-Latn.js +1 -0
  86. package/richtexteditor/lang/rte-lang-sv.js +1 -0
  87. package/richtexteditor/lang/rte-lang-sw.js +1 -0
  88. package/richtexteditor/lang/rte-lang-ta.js +1 -0
  89. package/richtexteditor/lang/rte-lang-te.js +1 -0
  90. package/richtexteditor/lang/rte-lang-th.js +1 -0
  91. package/richtexteditor/lang/rte-lang-tlh-Latn.js +1 -0
  92. package/richtexteditor/lang/rte-lang-tlh-Piqd.js +1 -0
  93. package/richtexteditor/lang/rte-lang-to.js +1 -0
  94. package/richtexteditor/lang/rte-lang-tr.js +1 -0
  95. package/richtexteditor/lang/rte-lang-ty.js +1 -0
  96. package/richtexteditor/lang/rte-lang-uk.js +1 -0
  97. package/richtexteditor/lang/rte-lang-ur.js +1 -0
  98. package/richtexteditor/lang/rte-lang-vi.js +1 -0
  99. package/richtexteditor/lang/rte-lang-yua.js +1 -0
  100. package/richtexteditor/lang/rte-lang-zh-Hans.js +1017 -2
  101. package/richtexteditor/lang/rte-lang-zh-Hant.js +1017 -2
  102. package/richtexteditor/plugins/a11yenhance.js +2 -1
  103. package/richtexteditor/plugins/a11yenhance.min.js +1 -1
  104. package/richtexteditor/plugins/accessibilitychecker.js +252 -60
  105. package/richtexteditor/plugins/accessibilitychecker.min.js +1 -1
  106. package/richtexteditor/plugins/aitoolkit.js +966 -303
  107. package/richtexteditor/plugins/aitoolkit.min.js +7 -7
  108. package/richtexteditor/plugins/all_plugins.js +7113 -1111
  109. package/richtexteditor/plugins/all_plugins.min.js +62 -59
  110. package/richtexteditor/plugins/autocorrect.js +66 -31
  111. package/richtexteditor/plugins/autocorrect.min.js +1 -1
  112. package/richtexteditor/plugins/autoembed.js +17 -0
  113. package/richtexteditor/plugins/autoembed.min.js +1 -1
  114. package/richtexteditor/plugins/blocktypes.js +18 -9
  115. package/richtexteditor/plugins/blocktypes.min.js +1 -1
  116. package/richtexteditor/plugins/bookmarkcard.js +12 -5
  117. package/richtexteditor/plugins/bookmarkcard.min.js +2 -2
  118. package/richtexteditor/plugins/changecase.js +72 -19
  119. package/richtexteditor/plugins/changecase.min.js +1 -1
  120. package/richtexteditor/plugins/charlimit.js +62 -2
  121. package/richtexteditor/plugins/charlimit.min.js +2 -2
  122. package/richtexteditor/plugins/comments.js +458 -59
  123. package/richtexteditor/plugins/comments.min.js +4 -3
  124. package/richtexteditor/plugins/contentminimap.js +25 -6
  125. package/richtexteditor/plugins/contentminimap.min.js +1 -1
  126. package/richtexteditor/plugins/crossreference.js +1 -1
  127. package/richtexteditor/plugins/crossreference.min.js +1 -1
  128. package/richtexteditor/plugins/dialoga11y.js +26 -4
  129. package/richtexteditor/plugins/dialoga11y.min.js +1 -1
  130. package/richtexteditor/plugins/dictation.js +1 -1
  131. package/richtexteditor/plugins/documentimport.js +220 -51
  132. package/richtexteditor/plugins/documentimport.min.js +4 -4
  133. package/richtexteditor/plugins/documentoutline.js +88 -38
  134. package/richtexteditor/plugins/documentoutline.min.js +1 -1
  135. package/richtexteditor/plugins/docxexport.js +204 -32
  136. package/richtexteditor/plugins/docxexport.min.js +2 -2
  137. package/richtexteditor/plugins/draghandle.js +267 -3
  138. package/richtexteditor/plugins/draghandle.min.js +1 -1
  139. package/richtexteditor/plugins/emailtoolkit.js +28 -14
  140. package/richtexteditor/plugins/emailtoolkit.min.js +1 -1
  141. package/richtexteditor/plugins/emojiautocomplete.js +110 -56
  142. package/richtexteditor/plugins/emojiautocomplete.min.js +1 -1
  143. package/richtexteditor/plugins/footnotes.js +65 -11
  144. package/richtexteditor/plugins/footnotes.min.js +1 -1
  145. package/richtexteditor/plugins/formatpainter.js +10 -0
  146. package/richtexteditor/plugins/formatpainter.min.js +1 -1
  147. package/richtexteditor/plugins/formattingmarks.js +53 -8
  148. package/richtexteditor/plugins/formattingmarks.min.js +1 -1
  149. package/richtexteditor/plugins/gapcursor.js +60 -0
  150. package/richtexteditor/plugins/gapcursor.min.js +1 -1
  151. package/richtexteditor/plugins/ghostcomplete.js +1 -1
  152. package/richtexteditor/plugins/html2pdf.js +1 -1
  153. package/richtexteditor/plugins/html2pdf.min.js +1 -1
  154. package/richtexteditor/plugins/imageeditor.js +12 -6
  155. package/richtexteditor/plugins/imageeditor.min.js +1 -1
  156. package/richtexteditor/plugins/importstyles.js +1 -1
  157. package/richtexteditor/plugins/inlinecode.js +25 -0
  158. package/richtexteditor/plugins/inlinecode.min.js +1 -1
  159. package/richtexteditor/plugins/insertcode.js +21 -3
  160. package/richtexteditor/plugins/insertcode.min.js +4 -4
  161. package/richtexteditor/plugins/insertemoji.js +112 -15
  162. package/richtexteditor/plugins/insertemoji.min.js +1 -1
  163. package/richtexteditor/plugins/insertgallery.js +42 -36
  164. package/richtexteditor/plugins/insertgallery.min.js +1 -1
  165. package/richtexteditor/plugins/inserttemplate.js +33 -17
  166. package/richtexteditor/plugins/inserttemplate.min.js +1 -1
  167. package/richtexteditor/plugins/keyboarda11y.js +66 -3
  168. package/richtexteditor/plugins/keyboarda11y.min.js +1 -1
  169. package/richtexteditor/plugins/layouttable.js +20 -0
  170. package/richtexteditor/plugins/layouttable.min.js +1 -1
  171. package/richtexteditor/plugins/linkchecker.js +29 -9
  172. package/richtexteditor/plugins/linkchecker.min.js +1 -1
  173. package/richtexteditor/plugins/localdraft.js +51 -7
  174. package/richtexteditor/plugins/localdraft.min.js +1 -1
  175. package/richtexteditor/plugins/markdownexport.js +159 -0
  176. package/richtexteditor/plugins/markdownexport.min.js +2 -2
  177. package/richtexteditor/plugins/mathdialog.js +11 -5
  178. package/richtexteditor/plugins/mathdialog.min.js +1 -1
  179. package/richtexteditor/plugins/mentionplugin.js +88 -25
  180. package/richtexteditor/plugins/mentionplugin.min.js +1 -1
  181. package/richtexteditor/plugins/mermaiddiagram.js +9 -3
  182. package/richtexteditor/plugins/mermaiddiagram.min.js +2 -2
  183. package/richtexteditor/plugins/multilevellist.js +4 -0
  184. package/richtexteditor/plugins/multilevellist.min.js +1 -1
  185. package/richtexteditor/plugins/pdfexport.js +235 -35
  186. package/richtexteditor/plugins/pdfexport.min.js +12 -12
  187. package/richtexteditor/plugins/permanentpen.js +3 -3
  188. package/richtexteditor/plugins/permanentpen.min.js +1 -1
  189. package/richtexteditor/plugins/readabilitystats.js +25 -11
  190. package/richtexteditor/plugins/readabilitystats.min.js +1 -1
  191. package/richtexteditor/plugins/readaloud.js +29 -15
  192. package/richtexteditor/plugins/readaloud.min.js +1 -1
  193. package/richtexteditor/plugins/restrictedediting.js +166 -0
  194. package/richtexteditor/plugins/restrictedediting.min.js +2 -2
  195. package/richtexteditor/plugins/revisionhistory.js +169 -37
  196. package/richtexteditor/plugins/revisionhistory.min.js +3 -3
  197. package/richtexteditor/plugins/rubyannotation.js +19 -9
  198. package/richtexteditor/plugins/rubyannotation.min.js +1 -1
  199. package/richtexteditor/plugins/sanitizer.js +86 -4
  200. package/richtexteditor/plugins/sanitizer.min.js +1 -1
  201. package/richtexteditor/plugins/slashcommand.js +30 -16
  202. package/richtexteditor/plugins/slashcommand.min.js +2 -2
  203. package/richtexteditor/plugins/smartchips.js +36 -22
  204. package/richtexteditor/plugins/smartchips.min.js +2 -2
  205. package/richtexteditor/plugins/spellcheck.js +17 -8
  206. package/richtexteditor/plugins/spellcheck.min.js +2 -2
  207. package/richtexteditor/plugins/stateinspector.js +19 -9
  208. package/richtexteditor/plugins/stateinspector.min.js +2 -2
  209. package/richtexteditor/plugins/structured-content-bridge.js +56 -24
  210. package/richtexteditor/plugins/structured-content-bridge.min.js +7 -7
  211. package/richtexteditor/plugins/tableofcontents.js +5 -2
  212. package/richtexteditor/plugins/tableofcontents.min.js +1 -1
  213. package/richtexteditor/plugins/tabletools.js +388 -3
  214. package/richtexteditor/plugins/tabletools.min.js +1 -1
  215. package/richtexteditor/plugins/textdirection.js +18 -5
  216. package/richtexteditor/plugins/textdirection.min.js +1 -1
  217. package/richtexteditor/plugins/textpartlanguage.js +161 -32
  218. package/richtexteditor/plugins/textpartlanguage.min.js +1 -1
  219. package/richtexteditor/plugins/todolist.js +90 -6
  220. package/richtexteditor/plugins/todolist.min.js +1 -1
  221. package/richtexteditor/plugins/trackedchanges.js +1812 -25
  222. package/richtexteditor/plugins/trackedchanges.min.js +5 -3
  223. package/richtexteditor/plugins/wordexport.js +2 -2
  224. package/richtexteditor/plugins/yjscollab.js +9 -4
  225. package/richtexteditor/plugins/yjscollab.min.js +1 -1
  226. package/richtexteditor/plugins/zz_richtextbox_dialog_style.js +22 -13
  227. package/richtexteditor/plugins/zz_richtextbox_dialog_style.min.js +1 -1
  228. package/richtexteditor/rte-config.js +131 -50
  229. package/richtexteditor/rte-headless.js +19 -4
  230. package/richtexteditor/rte-webcomponent.js +89 -16
  231. package/richtexteditor/rte.js +133 -52
  232. package/richtexteditor/runtime/colorpicker_more_ns.htm +93 -16
  233. package/scripts/build-angular.mjs +97 -0
  234. package/svelte/index.d.ts +2 -1
  235. package/svelte/index.js +80 -9
  236. package/vue/index.js +130 -34
  237. package/angular/component.ts +0 -297
  238. package/angular/index.ts +0 -24
package/README.md CHANGED
@@ -60,7 +60,7 @@ A full collaborative-document workspace, not just a textarea replacement:
60
60
  - **Smart link paste** *(2.2.4)* &mdash; pasting a URL or email while text is selected wraps the selection in `<a href>` instead of replacing it. Matches Google Docs / Notion / TipTap / ProseMirror behavior. Opt-out via `smartLinkPaste: false`.
61
61
  - **Mobile toolbar mode**, **classic toolbar customization**, **plugins**, **localization**, **image editing**, **PDF export**, **paste-from-Word fidelity**, **find & replace**, **autosave**, **draft recovery**.
62
62
  - **Configurable empty-editor placeholders** &mdash; set `placeholder`, `placeholderShowOnlyWhenEditable`, and `placeholderColor` to match the guided empty-state UX offered by current Tiptap and TinyMCE setups.
63
- - **TypeScript declarations** &mdash; 487-line `index.d.ts` with typed signatures for all 31 documented editor instance methods, the `RichTextEditorCommand` string-literal union (50+ built-in commands), event-handler signatures, and the `RichTextEditorConfig` interface.
63
+ - **TypeScript declarations** &mdash; typed editor methods, commands, event handlers, plugin namespaces, and configuration interfaces, with strict integration compilation tests.
64
64
 
65
65
  The structured content bridge now returns a semantic document model while still carrying compatibility HTML and plain text:
66
66
 
@@ -214,6 +214,8 @@ Pass `file` to skip the built-in file picker, or use `mode: "insert"` to insert
214
214
  ## React
215
215
 
216
216
  ```jsx
217
+ "use client"; // Required for your own state/callback wrapper in Next.js App Router.
218
+
217
219
  import { useState } from "react";
218
220
  import { RichTextEditorReact } from "@richscripts/richtexteditor/react";
219
221
 
@@ -231,6 +233,18 @@ export default function EditorPage() {
231
233
  }
232
234
  ```
233
235
 
236
+ The `/react` ESM entry declares its own client boundary. A Next.js App Router
237
+ Server Component may render `<RichTextEditorReact value="<p>Hello</p>" />` with
238
+ serializable props. Put `useState`, `onChange`, refs and function-valued editor
239
+ configuration in your own `"use client"` wrapper, as above; do not pass ordinary
240
+ callbacks across the server/client boundary. Copy the runtime to `public/richtexteditor`
241
+ as in the installation steps. Server-side HTML/Markdown conversion belongs in
242
+ `@richscripts/richtexteditor/server`, not in the React client entry.
243
+
244
+ CommonJS `require("@richscripts/richtexteditor/react")` remains available with
245
+ the same named API. No `ssr: false` workaround is needed just to render the wrapper;
246
+ the browser-only editor is created after the component mounts.
247
+
234
248
  ## Vue
235
249
 
236
250
  ```js
@@ -268,7 +282,9 @@ const app = createApp({
268
282
 
269
283
  ## Angular
270
284
 
271
- Angular apps can now use the packaged standalone wrapper component:
285
+ Angular 20.3+ apps can use the packaged standalone wrapper component. The public
286
+ entry point is precompiled partial-Ivy ESM, with generated Angular component
287
+ metadata; consumers do not need to compile the wrapper's TypeScript source:
272
288
 
273
289
  ```ts
274
290
  import { Component } from "@angular/core";
@@ -308,8 +324,57 @@ export class AppComponent {
308
324
 
309
325
  The Angular wrapper exposes the same JSON bridge and convenience instance methods as the React and Vue wrappers: `focus()`, `getEditor()`, `getHTMLCode()`, `getJSON()`, `setHTMLCode()`, `setJSON()`, and plugin accessors such as `getAiToolkit()` / `getCollab()` / `getComments()`.
310
326
 
327
+ When asset loading fails, show the `(error)` message and offer an explicit retry:
328
+ `await editorComponent.retry()`. It retries a failed initialization while keeping
329
+ the current bound form value. It does not replace an editor that is already ready,
330
+ start a second concurrent load, or revive a destroyed component. Fix missing asset
331
+ paths/CSP permissions before retrying. Asset autoload waits for theme CSS as well
332
+ as core/plugins; failed script or stylesheet requests can be retried without
333
+ refreshing the whole page. Manually preloaded (`autoload=false`) assets remain
334
+ your application's responsibility.
335
+
311
336
  The repo also includes a full Angular starter in `Demos/rte-ng-ts/` that uses the same wrapper pattern.
312
337
 
338
+ For maintainers: `npm run build:angular` regenerates the ESM entry point,
339
+ declarations and source/output hash manifest. `npm pack` runs this step through
340
+ `prepack`; the build needs the declared Angular/compiler/TypeScript/esbuild dev
341
+ dependencies (or the installed Angular demo toolchain in a repository checkout).
342
+ The normal test suite rejects stale generated files. Angular dependencies are
343
+ optional peers and are not required by React/Vue/server-only consumers.
344
+
345
+ ### Angular forms
346
+
347
+ The wrapper implements `ControlValueAccessor`: import `FormsModule` for
348
+ `[(ngModel)]="body"`, or `ReactiveFormsModule` for `[formControl]="bodyControl"`
349
+ and `formControlName`. These modules come from `@angular/forms`; use matching
350
+ Angular core/forms versions. Angular peers are optional for React/Vue users.
351
+
352
+ ```ts
353
+ import { FormControl, ReactiveFormsModule } from "@angular/forms";
354
+ // Add ReactiveFormsModule and RichTextEditorAngularComponent to your imports.
355
+ bodyControl = new FormControl("<p>Hello</p>", {
356
+ nonNullable: true,
357
+ updateOn: "blur"
358
+ });
359
+ ```
360
+
361
+ ```html
362
+ <rich-text-editor [formControl]="bodyControl"
363
+ [assetBasePath]="'/assets/richtexteditor'" />
364
+ ```
365
+
366
+ HTML is the default; use `valueFormat="json"` for structured document values.
367
+ External writes do not echo as user changes, null resets clear the editor,
368
+ and `disable()` / `enable()` update read-only state. Pending edits are flushed
369
+ before blur so `updateOn: "blur"` receives the final character. The default
370
+ 120 ms change coalescing also applies to form changes; set `[changeDelay]="0"`
371
+ for the shortest quiet period. Do not combine `[value]` with a form binding.
372
+ An explicit `config.readOnly: true` remains read-only after `enable()`.
373
+
374
+ Use a content-aware required validator: `Validators.required` on the HTML
375
+ string alone accepts empty markup such as `<p><br></p>`. Check outstanding image
376
+ uploads before saving; valid form state is not an upload-completion guarantee.
377
+
313
378
  ## Svelte
314
379
 
315
380
  `@richscripts/richtexteditor/svelte` ships a plain Svelte **action** &mdash; no compiler coupling,
@@ -324,10 +389,12 @@ controller, leaving the toolbar markup entirely to you.
324
389
  let state = {}; // active formats, refreshed as the caret moves
325
390
  </script>
326
391
 
327
- <button class:is-active={state.bold}
328
- on:mousedown|preventDefault={() => editor.run("bold")}>B</button>
329
- <button disabled={!state.canUndo}
330
- on:mousedown|preventDefault={() => editor.run("undo")}>Undo</button>
392
+ <button disabled={!editor} aria-pressed={!!state.bold} class:is-active={state.bold}
393
+ on:mousedown|preventDefault={() => {}}
394
+ on:click={() => editor.run("bold")}>Bold</button>
395
+ <button disabled={!editor || !state.canUndo}
396
+ on:mousedown|preventDefault={() => {}}
397
+ on:click={() => editor.run("undo")}>Undo</button>
331
398
 
332
399
  <div use:richTextEditor={{
333
400
  value: "<p>Hello</p>",
@@ -336,12 +403,48 @@ controller, leaving the toolbar markup entirely to you.
336
403
  }}></div>
337
404
  ```
338
405
 
339
- Use `on:mousedown|preventDefault` on toolbar buttons so the click never steals the caret. Changing
406
+ Prevent the pointer's default mousedown focus change to keep the caret, but run commands
407
+ from `click` so keyboard activation works too. Changing
340
408
  the `value` you pass in pushes new content into the editor, so a store can own the document, and
341
409
  the action calls `destroy()` for you when the node unmounts. In SvelteKit this is client-only
342
410
  code &mdash; keep it in a component that renders in the browser.
343
411
 
344
- Options: `value`, `config`, `assetBasePath`, `onReady`, `onUpdate`, `onChange`, `onError`.
412
+ Options: `value`, `config`, `assetBasePath`, `signal`, `onReady`, `onUpdate`, `onChange`, `onError`.
413
+
414
+ Updating the action's options replaces callbacks without remounting. Only a
415
+ changed bound `value` replaces the document; an unrelated callback/state update
416
+ does not overwrite typing. Unmounting during asset loading disposes any late
417
+ controller and does not emit ready/error callbacks into a destroyed view.
418
+
419
+ For Svelte 5 runes, use reactive controller/state variables and the modern event syntax:
420
+
421
+ ```svelte
422
+ <script>
423
+ import { richTextEditor } from "@richscripts/richtexteditor/svelte";
424
+ let editor = $state(null);
425
+ let state = $state({});
426
+ let html = $state("<p>Hello</p>");
427
+ </script>
428
+
429
+ <button disabled={!editor} aria-pressed={!!state.bold}
430
+ onmousedown={event => event.preventDefault()}
431
+ onclick={() => editor.run("bold")}>Bold</button>
432
+ <div use:richTextEditor={{
433
+ value: html,
434
+ onReady: controller => { editor = controller; },
435
+ onUpdate: next => { state = next; },
436
+ onChange: function () { html = this.getHTML(); }
437
+ }}></div>
438
+ <textarea aria-label="HTML output" readonly value={html}></textarea>
439
+ ```
440
+
441
+ `onChange` runs with the controller as `this`. On unmount it can synchronously
442
+ deliver a final native edit before the core's deferred change event. Read/save the
443
+ value inside that callback; do not clear your controller reference first. A
444
+ programmatic bound-value update is not emitted again as a final user edit.
445
+ Toolbar controls should have visible names, `aria-pressed` for toggle state and
446
+ disabled state until ready. This is a keyboard-usable starting point, not a
447
+ complete accessibility certification for a custom toolbar.
345
448
 
346
449
  ## Web component
347
450
 
@@ -349,8 +452,12 @@ For pages that use none of the framework bindings above &mdash; Rails, Laravel,
349
452
  htmx, plain HTML.
350
453
 
351
454
  ```html
455
+ <link rel="stylesheet" href="richtexteditor/rte_theme_default.min.css">
456
+ <link rel="stylesheet" href="richtexteditor/rte_mobile.min.css">
352
457
  <script src="richtexteditor/rte-config.js"></script>
353
458
  <script src="richtexteditor/rte.js"></script>
459
+ <!-- Optional plugins for the full toolbar. Omit unused plugins in a modular build. -->
460
+ <script src="richtexteditor/plugins/all_plugins.min.js"></script>
354
461
  <script src="richtexteditor/rte-webcomponent.js"></script>
355
462
 
356
463
  <form method="post">
@@ -359,8 +466,41 @@ htmx, plain HTML.
359
466
  </form>
360
467
  ```
361
468
 
362
- That form posts `body=...` with no glue code. The element is form-associated, so `form.reset()`,
363
- `required` and `form.elements.body` behave the way they do for a `<textarea>`.
469
+ Serve the page and runtime from your local HTTP server, not `file://`. That form
470
+ posts `body=...` with no value-copying glue in browsers supporting form-associated
471
+ custom elements. `required` and `form.elements.body` use `ElementInternals`;
472
+ older-browser hidden-input fallback supports submission, not full native validation.
473
+ Always validate and sanitize the submitted HTML on your server.
474
+
475
+ React and Vue components commit the latest native input on editor blur, even if
476
+ their normal change callback is coalesced or the core change event is still pending.
477
+ This makes an ordinary editor-to-Save-button interaction safer, but it is not an
478
+ upload-completion or persistence guarantee. For programmatic saves, read fresh
479
+ HTML through the editor ref in the save/submit handler and wait for your image
480
+ upload adapter's pending requests first. A `form.submit()` call skips native
481
+ validation and submit listeners; use `requestSubmit()` when you need that workflow.
482
+ React can still batch a parent state update when code forces focus and clicks Save
483
+ inside one synchronous function. Use the editor ref for that same-turn programmatic
484
+ snapshot; a captured value from the previous parent render is not a fresh document.
485
+
486
+ The `value` attribute is the default document; alternatively put initial HTML
487
+ between the tags. `form.reset()` restores that initial document. Set the `.value`
488
+ property for application updates, and await readiness before calling editor APIs:
489
+
490
+ ```js
491
+ const field = document.querySelector("rich-text-editor");
492
+ field.value = "<p>Application content</p>"; // also works before first connection
493
+ await field.ready;
494
+ console.log(field.value); // current HTML, not the original value attribute
495
+ const payload = new FormData(field.form); // name="body" participates automatically
496
+ ```
497
+
498
+ `disabled` (including an enclosing disabled `<fieldset>`) disables editing and
499
+ excludes the value from form submission. `readonly` prevents editing but keeps
500
+ the submitted value. Rich-text emptiness ignores whitespace/entities and blank
501
+ paragraphs; images and other media count as content. These client checks are not
502
+ upload validation. Use the image-handling guide for a persistent upload backend
503
+ and wait for pending uploads before saving a document.
364
504
 
365
505
  Attributes: `value`, `name`, `toolbar`, `height`, `width`, `readonly`, `disabled`, `required`,
366
506
  `shadow`, `config` (JSON). Properties: `.value`, `.editor`, `.ready` (a Promise). Events: `input`,
@@ -395,6 +535,29 @@ Block-level formats go through `run("formatblock", "h1")` rather than `run("h1")
395
535
  from `/richtexteditor` unless you pass `assetBasePath`; pass `autoload: false` if the page already
396
536
  loads them.
397
537
 
538
+ Initialization supports `signal: AbortSignal`. Cancel an abandoned view while
539
+ assets load without cancelling other editors that share the same asset request:
540
+
541
+ ```js
542
+ const pending = new AbortController();
543
+ const initialization = createHeadlessEditor("#host", { signal: pending.signal });
544
+ // If the view closes before initialization settles: pending.abort();
545
+ const controller = await initialization;
546
+ // Once mounted, explicitly call controller.destroy() when the view closes.
547
+ ```
548
+
549
+ Cancellation rejects with the signal's reason (`AbortError` by default). It
550
+ does not destroy an already mounted editor. The React/Vue headless bindings and
551
+ Svelte action wire their own pending-initialization cancellation on unmount.
552
+ React headless bindings use the latest `value` and callbacks. For Vue, pass a
553
+ reactive options object if the value should follow application state:
554
+
555
+ ```js
556
+ const options = reactive({ value: "<p>Initial</p>" }); // import reactive from vue
557
+ const { mountRef, editor, state } = useRichTextEditor(options);
558
+ options.value = "<p>Loaded from the application</p>";
559
+ ```
560
+
398
561
  ## Classic usage
399
562
 
400
563
  ```html
@@ -0,0 +1,23 @@
1
+ {
2
+ "angularCompiler": "20.3.31",
3
+ "typescript": "5.9.3",
4
+ "esbuild": "0.28.1",
5
+ "sources": {
6
+ "angular/component.ts": "c20f6c003689c8b931b55fd08232d4da56ca7ab95257bfe82edd2a42b34829f8",
7
+ "angular/index.ts": "e20427ea679cdac2f9103663089d60b59cc6739d973c3c9d2983b3b9be07a6f5",
8
+ "integrations/shared.js": "df288497a07fd2f5652fccaf33299ba2d5c6468af7453d801f56013451875f11",
9
+ "integrations/shared.d.ts": "66e7f7e60f31f4896e902ae562b68e56250ec3bd58ae851027ff77c27e1daf04",
10
+ "index.d.ts": "87ede19a371082213cda51db0dfc99621f83480f215776f99cf0a9d8619cd503",
11
+ "scripts/build-angular.mjs": "3e087b5d7f297299ff9d729b4e47c888a11b4e5a72f45f39852a1721159d4313"
12
+ },
13
+ "outputs": {
14
+ "angular/index.mjs": {
15
+ "bytes": 124323,
16
+ "sha256": "75093e8ff2410a07cb5a7cb43a20274cd06eada4f7d404a3526f2369fa7ec925"
17
+ },
18
+ "angular/index.d.ts": {
19
+ "bytes": 5009,
20
+ "sha256": "f533d2ac09f202326cfe3cc256489f0b345c1daba5f4c8ab6e05558f1056a90a"
21
+ }
22
+ }
23
+ }
@@ -1,80 +1,75 @@
1
- import type {
2
- RichTextEditorAccessibilityResult,
3
- RichTextEditorAiToolkit,
4
- RichTextEditorCollab,
5
- RichTextEditorComments,
6
- RichTextEditorDictation,
7
- RichTextEditorDocumentMetrics,
8
- RichTextEditorDocumentOutlineItem,
9
- RichTextEditorFootnoteItem,
10
- RichTextEditorInstance,
11
- RichTextEditorMentions,
12
- RichTextEditorPageSetup,
13
- RichTextEditorReviewLedger,
14
- RichTextEditorRevisionHistory,
15
- RichTextEditorSlashCommands,
16
- RichTextEditorStructuredDocument,
17
- RichTextEditorStructuredInput,
18
- RichTextEditorTableOfContentsItem,
19
- RichTextEditorTableOfContentsOptions,
20
- RichTextEditorTextStatistics,
21
- RichTextEditorValueFormat
22
- } from "../index";
23
-
1
+ import { AfterViewInit, ElementRef, EventEmitter, OnChanges, OnDestroy, SimpleChanges } from "@angular/core";
2
+ import { type ControlValueAccessor } from "@angular/forms";
3
+ import type { RichTextEditorAccessibilityResult, RichTextEditorAiToolkit, RichTextEditorCollab, RichTextEditorComments, RichTextEditorDictation, RichTextEditorDocumentMetrics, RichTextEditorDocumentOutlineItem, RichTextEditorFootnoteItem, RichTextEditorInstance, RichTextEditorMentions, RichTextEditorPageSetup, RichTextEditorReviewLedger, RichTextEditorRevisionHistory, RichTextEditorSlashCommands, RichTextEditorStructuredDocument, RichTextEditorStructuredInput, RichTextEditorTableOfContentsItem, RichTextEditorTableOfContentsOptions, RichTextEditorTextStatistics, RichTextEditorValueFormat } from "../index";
4
+ import * as i0 from "@angular/core";
24
5
  export interface RichTextEditorAngularChangeEvent {
25
- editor: RichTextEditorInstance;
26
- value: string | RichTextEditorStructuredDocument;
6
+ editor: RichTextEditorInstance;
7
+ value: string | RichTextEditorStructuredDocument;
27
8
  }
28
-
29
- export declare class RichTextEditorAngularComponent {
30
- assetBasePath: string;
31
- autoload: boolean;
32
- config: Record<string, unknown>;
33
- value?: RichTextEditorStructuredInput;
34
- valueFormat: RichTextEditorValueFormat;
35
-
36
- focus(): void;
37
- getEditor(): RichTextEditorInstance | null;
38
- getHTMLCode(): string;
39
- setHTMLCode(value: string): void;
40
- getJSON(): RichTextEditorStructuredDocument | null;
41
- setJSON(value: RichTextEditorStructuredInput): void;
42
- getTextStatistics(): RichTextEditorTextStatistics | null;
43
- getSelectionStatistics(): RichTextEditorTextStatistics | null;
44
- getDocumentMetrics(): RichTextEditorDocumentMetrics | null;
45
- getDocumentPageSetup(): RichTextEditorPageSetup | null;
46
- setDocumentPageSetup(pageSetup: RichTextEditorPageSetup | null): RichTextEditorAngularComponent | null;
47
- getDocumentOutline(): RichTextEditorDocumentOutlineItem[];
48
- getFootnotes(): RichTextEditorFootnoteItem[];
49
- getTableOfContents(options?: RichTextEditorTableOfContentsOptions): RichTextEditorTableOfContentsItem[];
50
- syncTableOfContents(options?: RichTextEditorTableOfContentsOptions): RichTextEditorStructuredDocument | null;
51
- auditAccessibility(): RichTextEditorAccessibilityResult | null;
52
-
53
- getAiToolkit(): RichTextEditorAiToolkit | null | undefined;
54
- getDictation(): RichTextEditorDictation | null | undefined;
55
- getRevisionHistory(): RichTextEditorRevisionHistory | null | undefined;
56
- getCollab(): RichTextEditorCollab | null | undefined;
57
- getSlashCommands(): RichTextEditorSlashCommands | null | undefined;
58
- getMentions(): RichTextEditorMentions | null | undefined;
59
- getComments(): RichTextEditorComments | null | undefined;
60
- getReviewLedger(): RichTextEditorReviewLedger | null | undefined;
9
+ export declare class RichTextEditorAngularComponent implements AfterViewInit, OnChanges, OnDestroy, ControlValueAccessor {
10
+ assetBasePath: string;
11
+ autoload: boolean;
12
+ config: Record<string, unknown>;
13
+ value?: RichTextEditorStructuredInput;
14
+ valueFormat: RichTextEditorValueFormat;
15
+ changeDelay: number;
16
+ disabled: boolean;
17
+ change: EventEmitter<RichTextEditorAngularChangeEvent>;
18
+ error: EventEmitter<Error>;
19
+ ready: EventEmitter<RichTextEditorInstance>;
20
+ hostRef: ElementRef<HTMLDivElement>;
21
+ private changeHandler;
22
+ private emitChange;
23
+ private lastEmitted;
24
+ private editor;
25
+ private destroyed;
26
+ private initializing;
27
+ private writingValue;
28
+ private lastKnownHtml;
29
+ private blurHandler;
30
+ private onModelChange;
31
+ private onModelTouched;
32
+ ngAfterViewInit(): Promise<void>;
33
+ /** Retry a failed initial load; never replace a ready or destroyed instance. */
34
+ retry(): Promise<void>;
35
+ ngOnChanges(changes: SimpleChanges): void;
36
+ writeValue(value: RichTextEditorStructuredInput | null | undefined): void;
37
+ registerOnChange(fn: (value: string | RichTextEditorStructuredDocument) => void): void;
38
+ registerOnTouched(fn: () => void): void;
39
+ setDisabledState(disabled: boolean): void;
40
+ private applyDisabledState;
41
+ private emitCurrentValue;
42
+ ngOnDestroy(): void;
43
+ private releaseEditor;
44
+ focus(): void;
45
+ getEditor(): RichTextEditorInstance | null;
46
+ getHTMLCode(): string;
47
+ setHTMLCode(value: string): void;
48
+ getJSON(): RichTextEditorStructuredDocument | null;
49
+ setJSON(value: RichTextEditorStructuredInput): void;
50
+ getTextStatistics(): RichTextEditorTextStatistics | null;
51
+ getSelectionStatistics(): RichTextEditorTextStatistics | null;
52
+ getDocumentMetrics(): RichTextEditorDocumentMetrics | null;
53
+ getDocumentPageSetup(): RichTextEditorPageSetup | null;
54
+ setDocumentPageSetup(pageSetup: RichTextEditorPageSetup | null): RichTextEditorAngularComponent | null;
55
+ getDocumentOutline(): RichTextEditorDocumentOutlineItem[];
56
+ getFootnotes(): RichTextEditorFootnoteItem[];
57
+ getTableOfContents(options?: RichTextEditorTableOfContentsOptions): RichTextEditorTableOfContentsItem[];
58
+ syncTableOfContents(options?: RichTextEditorTableOfContentsOptions): RichTextEditorStructuredDocument | null;
59
+ auditAccessibility(): RichTextEditorAccessibilityResult | null;
60
+ getAiToolkit(): RichTextEditorAiToolkit | null | undefined;
61
+ getDictation(): RichTextEditorDictation | null | undefined;
62
+ getRevisionHistory(): RichTextEditorRevisionHistory | null | undefined;
63
+ getCollab(): RichTextEditorCollab | null | undefined;
64
+ getSlashCommands(): RichTextEditorSlashCommands | null | undefined;
65
+ getMentions(): RichTextEditorMentions | null | undefined;
66
+ getComments(): RichTextEditorComments | null | undefined;
67
+ getReviewLedger(): RichTextEditorReviewLedger | null | undefined;
68
+ private getPluginNamespace;
69
+ private syncEditor;
70
+ static ɵfac: i0.ɵɵFactoryDeclaration<RichTextEditorAngularComponent, never>;
71
+ static ɵcmp: i0.ɵɵComponentDeclaration<RichTextEditorAngularComponent, "rich-text-editor", never, { "assetBasePath": { "alias": "assetBasePath"; "required": false; }; "autoload": { "alias": "autoload"; "required": false; }; "config": { "alias": "config"; "required": false; }; "value": { "alias": "value"; "required": false; }; "valueFormat": { "alias": "valueFormat"; "required": false; }; "changeDelay": { "alias": "changeDelay"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; }, { "change": "change"; "error": "error"; "ready": "ready"; }, never, never, true, never>;
61
72
  }
62
73
 
63
- export {
64
- auditAccessibility,
65
- createStructuredContent,
66
- createStructuredDocument,
67
- fromMarkdown,
68
- getDocumentPageSetup,
69
- getDocumentMetrics,
70
- getDocumentOutline,
71
- getFootnotes,
72
- getTableOfContents,
73
- getTextStatistics,
74
- loadRichTextEditorAssets,
75
- renderHTML,
76
- setDocumentPageSetup,
77
- syncTableOfContents,
78
- toMarkdown,
79
- validateStructuredContent
80
- } from "../integrations/shared";
74
+
75
+ export { auditAccessibility, auditLinks, createStructuredContent, createStructuredDocument, fromMarkdown, getDocumentPageSetup, getDocumentMetrics, getDocumentOutline, getFootnotes, getTableOfContents, getTextStatistics, loadRichTextEditorAssets, renderHTML, setDocumentPageSetup, syncTableOfContents, toMarkdown, validateStructuredContent } from "../integrations/shared";