@richscripts/richtexteditor 2.14.1 → 2.16.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 (244) hide show
  1. package/README.md +182 -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 +40 -52
  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 +68 -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 +8816 -2373
  17. package/richtexteditor/all_plugins.min.js +77 -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 +1050 -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 +1056 -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 +1056 -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 +1056 -2
  60. package/richtexteditor/lang/rte-lang-ja.js +1056 -2
  61. package/richtexteditor/lang/rte-lang-kn.js +1 -0
  62. package/richtexteditor/lang/rte-lang-ko.js +1056 -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 +1056 -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 +1056 -2
  78. package/richtexteditor/lang/rte-lang-pt.js +1056 -2
  79. package/richtexteditor/lang/rte-lang-ro.js +1 -0
  80. package/richtexteditor/lang/rte-lang-ru.js +1056 -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 +1050 -2
  101. package/richtexteditor/lang/rte-lang-zh-Hant.js +1050 -2
  102. package/richtexteditor/plugins/a11yenhance.js +4 -1
  103. package/richtexteditor/plugins/a11yenhance.min.js +1 -1
  104. package/richtexteditor/plugins/accessibilitychecker.js +256 -64
  105. package/richtexteditor/plugins/accessibilitychecker.min.js +1 -1
  106. package/richtexteditor/plugins/aitoolkit.js +1057 -386
  107. package/richtexteditor/plugins/aitoolkit.min.js +7 -7
  108. package/richtexteditor/plugins/all_plugins.js +8816 -2373
  109. package/richtexteditor/plugins/all_plugins.min.js +77 -59
  110. package/richtexteditor/plugins/autocorrect.js +66 -31
  111. package/richtexteditor/plugins/autocorrect.min.js +1 -1
  112. package/richtexteditor/plugins/autoembed.js +18 -1
  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 +460 -61
  123. package/richtexteditor/plugins/comments.min.js +4 -3
  124. package/richtexteditor/plugins/contentminimap.js +28 -9
  125. package/richtexteditor/plugins/contentminimap.min.js +1 -1
  126. package/richtexteditor/plugins/crdt-engine.js +27 -1
  127. package/richtexteditor/plugins/crossreference.js +1 -1
  128. package/richtexteditor/plugins/crossreference.min.js +1 -1
  129. package/richtexteditor/plugins/dialoga11y.js +26 -4
  130. package/richtexteditor/plugins/dialoga11y.min.js +1 -1
  131. package/richtexteditor/plugins/dictation.js +1 -1
  132. package/richtexteditor/plugins/documentimport.js +250 -59
  133. package/richtexteditor/plugins/documentimport.min.js +4 -4
  134. package/richtexteditor/plugins/documentoutline.js +47 -8
  135. package/richtexteditor/plugins/documentoutline.min.js +1 -1
  136. package/richtexteditor/plugins/docxexport.js +233 -32
  137. package/richtexteditor/plugins/docxexport.min.js +2 -2
  138. package/richtexteditor/plugins/draghandle.js +267 -3
  139. package/richtexteditor/plugins/draghandle.min.js +1 -1
  140. package/richtexteditor/plugins/emailtoolkit.js +28 -14
  141. package/richtexteditor/plugins/emailtoolkit.min.js +1 -1
  142. package/richtexteditor/plugins/emojiautocomplete.js +111 -57
  143. package/richtexteditor/plugins/emojiautocomplete.min.js +1 -1
  144. package/richtexteditor/plugins/footnotes.js +65 -11
  145. package/richtexteditor/plugins/footnotes.min.js +1 -1
  146. package/richtexteditor/plugins/formatpainter.js +10 -0
  147. package/richtexteditor/plugins/formatpainter.min.js +1 -1
  148. package/richtexteditor/plugins/formattingmarks.js +6 -6
  149. package/richtexteditor/plugins/formattingmarks.min.js +1 -1
  150. package/richtexteditor/plugins/gapcursor.js +60 -0
  151. package/richtexteditor/plugins/gapcursor.min.js +1 -1
  152. package/richtexteditor/plugins/ghostcomplete.js +1 -1
  153. package/richtexteditor/plugins/html2pdf.js +8 -8
  154. package/richtexteditor/plugins/html2pdf.min.js +1 -1
  155. package/richtexteditor/plugins/imageeditor.js +31 -12
  156. package/richtexteditor/plugins/imageeditor.min.js +1 -1
  157. package/richtexteditor/plugins/importstyles.js +1 -1
  158. package/richtexteditor/plugins/inlinecode.js +25 -0
  159. package/richtexteditor/plugins/inlinecode.min.js +1 -1
  160. package/richtexteditor/plugins/insertcode.js +29 -11
  161. package/richtexteditor/plugins/insertcode.min.js +6 -6
  162. package/richtexteditor/plugins/insertemoji.js +113 -16
  163. package/richtexteditor/plugins/insertemoji.min.js +1 -1
  164. package/richtexteditor/plugins/insertgallery.js +46 -40
  165. package/richtexteditor/plugins/insertgallery.min.js +1 -1
  166. package/richtexteditor/plugins/inserttemplate.js +43 -19
  167. package/richtexteditor/plugins/inserttemplate.min.js +1 -1
  168. package/richtexteditor/plugins/keyboarda11y.js +66 -3
  169. package/richtexteditor/plugins/keyboarda11y.min.js +1 -1
  170. package/richtexteditor/plugins/layouttable.js +20 -0
  171. package/richtexteditor/plugins/layouttable.min.js +1 -1
  172. package/richtexteditor/plugins/linkchecker.js +29 -9
  173. package/richtexteditor/plugins/linkchecker.min.js +1 -1
  174. package/richtexteditor/plugins/localdraft.js +51 -7
  175. package/richtexteditor/plugins/localdraft.min.js +1 -1
  176. package/richtexteditor/plugins/markdownexport.js +159 -0
  177. package/richtexteditor/plugins/markdownexport.min.js +2 -2
  178. package/richtexteditor/plugins/mathdialog.js +11 -5
  179. package/richtexteditor/plugins/mathdialog.min.js +1 -1
  180. package/richtexteditor/plugins/mentionplugin.js +90 -27
  181. package/richtexteditor/plugins/mentionplugin.min.js +1 -1
  182. package/richtexteditor/plugins/mermaiddiagram.js +14 -8
  183. package/richtexteditor/plugins/mermaiddiagram.min.js +2 -2
  184. package/richtexteditor/plugins/multilevellist.js +4 -0
  185. package/richtexteditor/plugins/multilevellist.min.js +1 -1
  186. package/richtexteditor/plugins/nodetypes.js +1 -1
  187. package/richtexteditor/plugins/nodetypes.min.js +1 -1
  188. package/richtexteditor/plugins/pagination.js +2 -2
  189. package/richtexteditor/plugins/pagination.min.js +1 -1
  190. package/richtexteditor/plugins/pdfexport.js +1752 -1167
  191. package/richtexteditor/plugins/pdfexport.min.js +28 -13
  192. package/richtexteditor/plugins/permanentpen.js +3 -3
  193. package/richtexteditor/plugins/permanentpen.min.js +1 -1
  194. package/richtexteditor/plugins/readabilitystats.js +25 -11
  195. package/richtexteditor/plugins/readabilitystats.min.js +1 -1
  196. package/richtexteditor/plugins/readaloud.js +30 -16
  197. package/richtexteditor/plugins/readaloud.min.js +1 -1
  198. package/richtexteditor/plugins/restrictedediting.js +166 -0
  199. package/richtexteditor/plugins/restrictedediting.min.js +2 -2
  200. package/richtexteditor/plugins/revisionhistory.js +173 -41
  201. package/richtexteditor/plugins/revisionhistory.min.js +3 -3
  202. package/richtexteditor/plugins/rubyannotation.js +21 -11
  203. package/richtexteditor/plugins/rubyannotation.min.js +1 -1
  204. package/richtexteditor/plugins/sanitizer.js +119 -7
  205. package/richtexteditor/plugins/sanitizer.min.js +1 -1
  206. package/richtexteditor/plugins/slashcommand.js +32 -18
  207. package/richtexteditor/plugins/slashcommand.min.js +2 -2
  208. package/richtexteditor/plugins/smartchips.js +39 -25
  209. package/richtexteditor/plugins/smartchips.min.js +2 -2
  210. package/richtexteditor/plugins/spellcheck.js +20 -11
  211. package/richtexteditor/plugins/spellcheck.min.js +2 -2
  212. package/richtexteditor/plugins/stateinspector.js +21 -11
  213. package/richtexteditor/plugins/stateinspector.min.js +2 -2
  214. package/richtexteditor/plugins/structured-content-bridge.js +90 -32
  215. package/richtexteditor/plugins/structured-content-bridge.min.js +8 -8
  216. package/richtexteditor/plugins/tableofcontents.js +5 -2
  217. package/richtexteditor/plugins/tableofcontents.min.js +1 -1
  218. package/richtexteditor/plugins/tabletools.js +388 -3
  219. package/richtexteditor/plugins/tabletools.min.js +1 -1
  220. package/richtexteditor/plugins/textdirection.js +19 -6
  221. package/richtexteditor/plugins/textdirection.min.js +1 -1
  222. package/richtexteditor/plugins/textpartlanguage.js +161 -32
  223. package/richtexteditor/plugins/textpartlanguage.min.js +1 -1
  224. package/richtexteditor/plugins/todolist.js +90 -6
  225. package/richtexteditor/plugins/todolist.min.js +1 -1
  226. package/richtexteditor/plugins/trackedchanges.js +1812 -25
  227. package/richtexteditor/plugins/trackedchanges.min.js +5 -3
  228. package/richtexteditor/plugins/wordexport.js +2 -2
  229. package/richtexteditor/plugins/yjscollab.js +11 -6
  230. package/richtexteditor/plugins/yjscollab.min.js +1 -1
  231. package/richtexteditor/plugins/zz_richtextbox_dialog_style.js +22 -13
  232. package/richtexteditor/plugins/zz_richtextbox_dialog_style.min.js +1 -1
  233. package/richtexteditor/rte-config.js +144 -86
  234. package/richtexteditor/rte-headless.js +19 -4
  235. package/richtexteditor/rte-watchdog.js +3 -1
  236. package/richtexteditor/rte-webcomponent.js +91 -16
  237. package/richtexteditor/rte.js +146 -88
  238. package/richtexteditor/runtime/colorpicker_more_ns.htm +3 -1
  239. package/scripts/build-angular.mjs +97 -0
  240. package/svelte/index.d.ts +2 -1
  241. package/svelte/index.js +80 -9
  242. package/vue/index.js +130 -34
  243. package/angular/component.ts +0 -297
  244. 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
 
@@ -196,6 +196,15 @@ const validation = validateStructuredContent(documentModel);
196
196
 
197
197
  Use `@richscripts/richtexteditor/server` when you only need conversion and rendering on the server. Use `@richscripts/richtexteditor/integrations/shared` when you also need browser asset loading and editor bootstrapping helpers.
198
198
 
199
+ Queued accessibility repairs are rechecked against the current document before
200
+ they are applied. If that issue code no longer exists at its node path, the
201
+ helper returns a cloned, normalized current document without replacing newer
202
+ heading text, image descriptions or table-header choices. Repeating an already
203
+ completed repair is also a no-op. Re-run `auditAccessibility` after structural
204
+ edits: issue paths are positions, not persistent node identities, so this check
205
+ cannot distinguish a different node that happens to have the same issue at the
206
+ same path. Use `renderHTML(repairedDocument)` to render the returned model.
207
+
199
208
  The same server/shared helpers also expose `getTextStatistics(value)` for consistent word and character counts outside the browser. The React, Vue, and Angular package entry points re-export this same helper family so framework apps can import editor components and document-workflow utilities from one place.
200
209
 
201
210
  ## DOCX import
@@ -214,6 +223,8 @@ Pass `file` to skip the built-in file picker, or use `mode: "insert"` to insert
214
223
  ## React
215
224
 
216
225
  ```jsx
226
+ "use client"; // Required for your own state/callback wrapper in Next.js App Router.
227
+
217
228
  import { useState } from "react";
218
229
  import { RichTextEditorReact } from "@richscripts/richtexteditor/react";
219
230
 
@@ -231,6 +242,18 @@ export default function EditorPage() {
231
242
  }
232
243
  ```
233
244
 
245
+ The `/react` ESM entry declares its own client boundary. A Next.js App Router
246
+ Server Component may render `<RichTextEditorReact value="<p>Hello</p>" />` with
247
+ serializable props. Put `useState`, `onChange`, refs and function-valued editor
248
+ configuration in your own `"use client"` wrapper, as above; do not pass ordinary
249
+ callbacks across the server/client boundary. Copy the runtime to `public/richtexteditor`
250
+ as in the installation steps. Server-side HTML/Markdown conversion belongs in
251
+ `@richscripts/richtexteditor/server`, not in the React client entry.
252
+
253
+ CommonJS `require("@richscripts/richtexteditor/react")` remains available with
254
+ the same named API. No `ssr: false` workaround is needed just to render the wrapper;
255
+ the browser-only editor is created after the component mounts.
256
+
234
257
  ## Vue
235
258
 
236
259
  ```js
@@ -268,7 +291,9 @@ const app = createApp({
268
291
 
269
292
  ## Angular
270
293
 
271
- Angular apps can now use the packaged standalone wrapper component:
294
+ Angular 20.3+ apps can use the packaged standalone wrapper component. The public
295
+ entry point is precompiled partial-Ivy ESM, with generated Angular component
296
+ metadata; consumers do not need to compile the wrapper's TypeScript source:
272
297
 
273
298
  ```ts
274
299
  import { Component } from "@angular/core";
@@ -308,8 +333,57 @@ export class AppComponent {
308
333
 
309
334
  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
335
 
336
+ When asset loading fails, show the `(error)` message and offer an explicit retry:
337
+ `await editorComponent.retry()`. It retries a failed initialization while keeping
338
+ the current bound form value. It does not replace an editor that is already ready,
339
+ start a second concurrent load, or revive a destroyed component. Fix missing asset
340
+ paths/CSP permissions before retrying. Asset autoload waits for theme CSS as well
341
+ as core/plugins; failed script or stylesheet requests can be retried without
342
+ refreshing the whole page. Manually preloaded (`autoload=false`) assets remain
343
+ your application's responsibility.
344
+
311
345
  The repo also includes a full Angular starter in `Demos/rte-ng-ts/` that uses the same wrapper pattern.
312
346
 
347
+ For maintainers: `npm run build:angular` regenerates the ESM entry point,
348
+ declarations and source/output hash manifest. `npm pack` runs this step through
349
+ `prepack`; the build needs the declared Angular/compiler/TypeScript/esbuild dev
350
+ dependencies (or the installed Angular demo toolchain in a repository checkout).
351
+ The normal test suite rejects stale generated files. Angular dependencies are
352
+ optional peers and are not required by React/Vue/server-only consumers.
353
+
354
+ ### Angular forms
355
+
356
+ The wrapper implements `ControlValueAccessor`: import `FormsModule` for
357
+ `[(ngModel)]="body"`, or `ReactiveFormsModule` for `[formControl]="bodyControl"`
358
+ and `formControlName`. These modules come from `@angular/forms`; use matching
359
+ Angular core/forms versions. Angular peers are optional for React/Vue users.
360
+
361
+ ```ts
362
+ import { FormControl, ReactiveFormsModule } from "@angular/forms";
363
+ // Add ReactiveFormsModule and RichTextEditorAngularComponent to your imports.
364
+ bodyControl = new FormControl("<p>Hello</p>", {
365
+ nonNullable: true,
366
+ updateOn: "blur"
367
+ });
368
+ ```
369
+
370
+ ```html
371
+ <rich-text-editor [formControl]="bodyControl"
372
+ [assetBasePath]="'/assets/richtexteditor'" />
373
+ ```
374
+
375
+ HTML is the default; use `valueFormat="json"` for structured document values.
376
+ External writes do not echo as user changes, null resets clear the editor,
377
+ and `disable()` / `enable()` update read-only state. Pending edits are flushed
378
+ before blur so `updateOn: "blur"` receives the final character. The default
379
+ 120 ms change coalescing also applies to form changes; set `[changeDelay]="0"`
380
+ for the shortest quiet period. Do not combine `[value]` with a form binding.
381
+ An explicit `config.readOnly: true` remains read-only after `enable()`.
382
+
383
+ Use a content-aware required validator: `Validators.required` on the HTML
384
+ string alone accepts empty markup such as `<p><br></p>`. Check outstanding image
385
+ uploads before saving; valid form state is not an upload-completion guarantee.
386
+
313
387
  ## Svelte
314
388
 
315
389
  `@richscripts/richtexteditor/svelte` ships a plain Svelte **action** &mdash; no compiler coupling,
@@ -324,10 +398,12 @@ controller, leaving the toolbar markup entirely to you.
324
398
  let state = {}; // active formats, refreshed as the caret moves
325
399
  </script>
326
400
 
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>
401
+ <button disabled={!editor} aria-pressed={!!state.bold} class:is-active={state.bold}
402
+ on:mousedown|preventDefault={() => {}}
403
+ on:click={() => editor.run("bold")}>Bold</button>
404
+ <button disabled={!editor || !state.canUndo}
405
+ on:mousedown|preventDefault={() => {}}
406
+ on:click={() => editor.run("undo")}>Undo</button>
331
407
 
332
408
  <div use:richTextEditor={{
333
409
  value: "<p>Hello</p>",
@@ -336,12 +412,48 @@ controller, leaving the toolbar markup entirely to you.
336
412
  }}></div>
337
413
  ```
338
414
 
339
- Use `on:mousedown|preventDefault` on toolbar buttons so the click never steals the caret. Changing
415
+ Prevent the pointer's default mousedown focus change to keep the caret, but run commands
416
+ from `click` so keyboard activation works too. Changing
340
417
  the `value` you pass in pushes new content into the editor, so a store can own the document, and
341
418
  the action calls `destroy()` for you when the node unmounts. In SvelteKit this is client-only
342
419
  code &mdash; keep it in a component that renders in the browser.
343
420
 
344
- Options: `value`, `config`, `assetBasePath`, `onReady`, `onUpdate`, `onChange`, `onError`.
421
+ Options: `value`, `config`, `assetBasePath`, `signal`, `onReady`, `onUpdate`, `onChange`, `onError`.
422
+
423
+ Updating the action's options replaces callbacks without remounting. Only a
424
+ changed bound `value` replaces the document; an unrelated callback/state update
425
+ does not overwrite typing. Unmounting during asset loading disposes any late
426
+ controller and does not emit ready/error callbacks into a destroyed view.
427
+
428
+ For Svelte 5 runes, use reactive controller/state variables and the modern event syntax:
429
+
430
+ ```svelte
431
+ <script>
432
+ import { richTextEditor } from "@richscripts/richtexteditor/svelte";
433
+ let editor = $state(null);
434
+ let state = $state({});
435
+ let html = $state("<p>Hello</p>");
436
+ </script>
437
+
438
+ <button disabled={!editor} aria-pressed={!!state.bold}
439
+ onmousedown={event => event.preventDefault()}
440
+ onclick={() => editor.run("bold")}>Bold</button>
441
+ <div use:richTextEditor={{
442
+ value: html,
443
+ onReady: controller => { editor = controller; },
444
+ onUpdate: next => { state = next; },
445
+ onChange: function () { html = this.getHTML(); }
446
+ }}></div>
447
+ <textarea aria-label="HTML output" readonly value={html}></textarea>
448
+ ```
449
+
450
+ `onChange` runs with the controller as `this`. On unmount it can synchronously
451
+ deliver a final native edit before the core's deferred change event. Read/save the
452
+ value inside that callback; do not clear your controller reference first. A
453
+ programmatic bound-value update is not emitted again as a final user edit.
454
+ Toolbar controls should have visible names, `aria-pressed` for toggle state and
455
+ disabled state until ready. This is a keyboard-usable starting point, not a
456
+ complete accessibility certification for a custom toolbar.
345
457
 
346
458
  ## Web component
347
459
 
@@ -349,8 +461,12 @@ For pages that use none of the framework bindings above &mdash; Rails, Laravel,
349
461
  htmx, plain HTML.
350
462
 
351
463
  ```html
464
+ <link rel="stylesheet" href="richtexteditor/rte_theme_default.min.css">
465
+ <link rel="stylesheet" href="richtexteditor/rte_mobile.min.css">
352
466
  <script src="richtexteditor/rte-config.js"></script>
353
467
  <script src="richtexteditor/rte.js"></script>
468
+ <!-- Optional plugins for the full toolbar. Omit unused plugins in a modular build. -->
469
+ <script src="richtexteditor/plugins/all_plugins.min.js"></script>
354
470
  <script src="richtexteditor/rte-webcomponent.js"></script>
355
471
 
356
472
  <form method="post">
@@ -359,8 +475,41 @@ htmx, plain HTML.
359
475
  </form>
360
476
  ```
361
477
 
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>`.
478
+ Serve the page and runtime from your local HTTP server, not `file://`. That form
479
+ posts `body=...` with no value-copying glue in browsers supporting form-associated
480
+ custom elements. `required` and `form.elements.body` use `ElementInternals`;
481
+ older-browser hidden-input fallback supports submission, not full native validation.
482
+ Always validate and sanitize the submitted HTML on your server.
483
+
484
+ React and Vue components commit the latest native input on editor blur, even if
485
+ their normal change callback is coalesced or the core change event is still pending.
486
+ This makes an ordinary editor-to-Save-button interaction safer, but it is not an
487
+ upload-completion or persistence guarantee. For programmatic saves, read fresh
488
+ HTML through the editor ref in the save/submit handler and wait for your image
489
+ upload adapter's pending requests first. A `form.submit()` call skips native
490
+ validation and submit listeners; use `requestSubmit()` when you need that workflow.
491
+ React can still batch a parent state update when code forces focus and clicks Save
492
+ inside one synchronous function. Use the editor ref for that same-turn programmatic
493
+ snapshot; a captured value from the previous parent render is not a fresh document.
494
+
495
+ The `value` attribute is the default document; alternatively put initial HTML
496
+ between the tags. `form.reset()` restores that initial document. Set the `.value`
497
+ property for application updates, and await readiness before calling editor APIs:
498
+
499
+ ```js
500
+ const field = document.querySelector("rich-text-editor");
501
+ field.value = "<p>Application content</p>"; // also works before first connection
502
+ await field.ready;
503
+ console.log(field.value); // current HTML, not the original value attribute
504
+ const payload = new FormData(field.form); // name="body" participates automatically
505
+ ```
506
+
507
+ `disabled` (including an enclosing disabled `<fieldset>`) disables editing and
508
+ excludes the value from form submission. `readonly` prevents editing but keeps
509
+ the submitted value. Rich-text emptiness ignores whitespace/entities and blank
510
+ paragraphs; images and other media count as content. These client checks are not
511
+ upload validation. Use the image-handling guide for a persistent upload backend
512
+ and wait for pending uploads before saving a document.
364
513
 
365
514
  Attributes: `value`, `name`, `toolbar`, `height`, `width`, `readonly`, `disabled`, `required`,
366
515
  `shadow`, `config` (JSON). Properties: `.value`, `.editor`, `.ready` (a Promise). Events: `input`,
@@ -395,6 +544,29 @@ Block-level formats go through `run("formatblock", "h1")` rather than `run("h1")
395
544
  from `/richtexteditor` unless you pass `assetBasePath`; pass `autoload: false` if the page already
396
545
  loads them.
397
546
 
547
+ Initialization supports `signal: AbortSignal`. Cancel an abandoned view while
548
+ assets load without cancelling other editors that share the same asset request:
549
+
550
+ ```js
551
+ const pending = new AbortController();
552
+ const initialization = createHeadlessEditor("#host", { signal: pending.signal });
553
+ // If the view closes before initialization settles: pending.abort();
554
+ const controller = await initialization;
555
+ // Once mounted, explicitly call controller.destroy() when the view closes.
556
+ ```
557
+
558
+ Cancellation rejects with the signal's reason (`AbortError` by default). It
559
+ does not destroy an already mounted editor. The React/Vue headless bindings and
560
+ Svelte action wire their own pending-initialization cancellation on unmount.
561
+ React headless bindings use the latest `value` and callbacks. For Vue, pass a
562
+ reactive options object if the value should follow application state:
563
+
564
+ ```js
565
+ const options = reactive({ value: "<p>Initial</p>" }); // import reactive from vue
566
+ const { mountRef, editor, state } = useRichTextEditor(options);
567
+ options.value = "<p>Loaded from the application</p>";
568
+ ```
569
+
398
570
  ## Classic usage
399
571
 
400
572
  ```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": "543fde78fb67291cdc2d338f751dd63af6cd0acf647e84b5bd677bc586ddd2e8",
9
+ "integrations/shared.d.ts": "66e7f7e60f31f4896e902ae562b68e56250ec3bd58ae851027ff77c27e1daf04",
10
+ "index.d.ts": "4f77c6b537eed915f67b61ab268e487fd2ec5890e54a99ae5bcfc3bef3f0ffe8",
11
+ "scripts/build-angular.mjs": "3e087b5d7f297299ff9d729b4e47c888a11b4e5a72f45f39852a1721159d4313"
12
+ },
13
+ "outputs": {
14
+ "angular/index.mjs": {
15
+ "bytes": 124563,
16
+ "sha256": "03bcd629a876f6a68025a1f42f7d7dbcc4518d6f4a822244969f111e3fdadd42"
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";