@richscripts/richtexteditor 2.4.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 (184) hide show
  1. package/README.md +360 -0
  2. package/admin/index.d.ts +100 -0
  3. package/admin/index.js +94 -0
  4. package/angular/component.ts +284 -0
  5. package/angular/index.d.ts +80 -0
  6. package/angular/index.ts +24 -0
  7. package/headless/index.d.ts +65 -0
  8. package/headless/index.js +272 -0
  9. package/index.d.ts +1412 -0
  10. package/integrations/shared.d.ts +138 -0
  11. package/integrations/shared.js +5182 -0
  12. package/package.json +130 -0
  13. package/react/index.d.ts +104 -0
  14. package/react/index.js +310 -0
  15. package/richtexteditor/CHANGELOG.md +1876 -0
  16. package/richtexteditor/all_plugins.js +31125 -0
  17. package/richtexteditor/all_plugins.min.js +78 -0
  18. package/richtexteditor/lang/rte-lang-af.js +193 -0
  19. package/richtexteditor/lang/rte-lang-ar.js +193 -0
  20. package/richtexteditor/lang/rte-lang-bg.js +193 -0
  21. package/richtexteditor/lang/rte-lang-bn.js +193 -0
  22. package/richtexteditor/lang/rte-lang-bs.js +193 -0
  23. package/richtexteditor/lang/rte-lang-ca.js +193 -0
  24. package/richtexteditor/lang/rte-lang-cs.js +193 -0
  25. package/richtexteditor/lang/rte-lang-cy.js +193 -0
  26. package/richtexteditor/lang/rte-lang-da.js +193 -0
  27. package/richtexteditor/lang/rte-lang-de.js +193 -0
  28. package/richtexteditor/lang/rte-lang-el.js +193 -0
  29. package/richtexteditor/lang/rte-lang-en.js +6 -0
  30. package/richtexteditor/lang/rte-lang-es.js +193 -0
  31. package/richtexteditor/lang/rte-lang-et.js +193 -0
  32. package/richtexteditor/lang/rte-lang-fa.js +193 -0
  33. package/richtexteditor/lang/rte-lang-fi.js +193 -0
  34. package/richtexteditor/lang/rte-lang-fil.js +193 -0
  35. package/richtexteditor/lang/rte-lang-fj.js +193 -0
  36. package/richtexteditor/lang/rte-lang-fr.js +193 -0
  37. package/richtexteditor/lang/rte-lang-ga.js +193 -0
  38. package/richtexteditor/lang/rte-lang-gu.js +193 -0
  39. package/richtexteditor/lang/rte-lang-he.js +193 -0
  40. package/richtexteditor/lang/rte-lang-hi.js +193 -0
  41. package/richtexteditor/lang/rte-lang-hr.js +193 -0
  42. package/richtexteditor/lang/rte-lang-ht.js +193 -0
  43. package/richtexteditor/lang/rte-lang-hu.js +193 -0
  44. package/richtexteditor/lang/rte-lang-id.js +193 -0
  45. package/richtexteditor/lang/rte-lang-is.js +193 -0
  46. package/richtexteditor/lang/rte-lang-it.js +193 -0
  47. package/richtexteditor/lang/rte-lang-ja.js +193 -0
  48. package/richtexteditor/lang/rte-lang-kn.js +193 -0
  49. package/richtexteditor/lang/rte-lang-ko.js +193 -0
  50. package/richtexteditor/lang/rte-lang-lt.js +193 -0
  51. package/richtexteditor/lang/rte-lang-lv.js +193 -0
  52. package/richtexteditor/lang/rte-lang-mg.js +193 -0
  53. package/richtexteditor/lang/rte-lang-mi.js +193 -0
  54. package/richtexteditor/lang/rte-lang-ml.js +193 -0
  55. package/richtexteditor/lang/rte-lang-mr.js +193 -0
  56. package/richtexteditor/lang/rte-lang-ms.js +193 -0
  57. package/richtexteditor/lang/rte-lang-mt.js +193 -0
  58. package/richtexteditor/lang/rte-lang-mww.js +193 -0
  59. package/richtexteditor/lang/rte-lang-nb.js +193 -0
  60. package/richtexteditor/lang/rte-lang-nl.js +193 -0
  61. package/richtexteditor/lang/rte-lang-otq.js +193 -0
  62. package/richtexteditor/lang/rte-lang-pa.js +193 -0
  63. package/richtexteditor/lang/rte-lang-pl.js +193 -0
  64. package/richtexteditor/lang/rte-lang-pt-pt.js +193 -0
  65. package/richtexteditor/lang/rte-lang-pt.js +193 -0
  66. package/richtexteditor/lang/rte-lang-ro.js +193 -0
  67. package/richtexteditor/lang/rte-lang-ru.js +193 -0
  68. package/richtexteditor/lang/rte-lang-sk.js +193 -0
  69. package/richtexteditor/lang/rte-lang-sl.js +193 -0
  70. package/richtexteditor/lang/rte-lang-sm.js +193 -0
  71. package/richtexteditor/lang/rte-lang-sr-Cyrl.js +193 -0
  72. package/richtexteditor/lang/rte-lang-sr-Latn.js +193 -0
  73. package/richtexteditor/lang/rte-lang-sv.js +193 -0
  74. package/richtexteditor/lang/rte-lang-sw.js +193 -0
  75. package/richtexteditor/lang/rte-lang-ta.js +193 -0
  76. package/richtexteditor/lang/rte-lang-te.js +193 -0
  77. package/richtexteditor/lang/rte-lang-th.js +193 -0
  78. package/richtexteditor/lang/rte-lang-tlh-Latn.js +193 -0
  79. package/richtexteditor/lang/rte-lang-tlh-Piqd.js +193 -0
  80. package/richtexteditor/lang/rte-lang-to.js +193 -0
  81. package/richtexteditor/lang/rte-lang-tr.js +193 -0
  82. package/richtexteditor/lang/rte-lang-ty.js +193 -0
  83. package/richtexteditor/lang/rte-lang-uk.js +193 -0
  84. package/richtexteditor/lang/rte-lang-ur.js +193 -0
  85. package/richtexteditor/lang/rte-lang-vi.js +193 -0
  86. package/richtexteditor/lang/rte-lang-yua.js +193 -0
  87. package/richtexteditor/lang/rte-lang-zh-Hans.js +193 -0
  88. package/richtexteditor/lang/rte-lang-zh-Hant.js +193 -0
  89. package/richtexteditor/lang/rte-lang.js +1 -0
  90. package/richtexteditor/plugins/-combine-plugin.txt +2 -0
  91. package/richtexteditor/plugins/-combine-plugins.bat +20 -0
  92. package/richtexteditor/plugins/a11yenhance.js +90 -0
  93. package/richtexteditor/plugins/accessibilitychecker.js +579 -0
  94. package/richtexteditor/plugins/aitoolkit.css +15830 -0
  95. package/richtexteditor/plugins/aitoolkit.js +19171 -0
  96. package/richtexteditor/plugins/all_plugins.js +31125 -0
  97. package/richtexteditor/plugins/all_plugins.min.js +78 -0
  98. package/richtexteditor/plugins/autoembed.js +174 -0
  99. package/richtexteditor/plugins/blocktypes.js +175 -0
  100. package/richtexteditor/plugins/bookmarkcard.js +226 -0
  101. package/richtexteditor/plugins/charlimit.js +195 -0
  102. package/richtexteditor/plugins/comments.js +672 -0
  103. package/richtexteditor/plugins/contentminimap.js +369 -0
  104. package/richtexteditor/plugins/crdt-engine.js +8905 -0
  105. package/richtexteditor/plugins/crdt-engine.min.js +5 -0
  106. package/richtexteditor/plugins/dialoga11y.js +174 -0
  107. package/richtexteditor/plugins/dictation.js +268 -0
  108. package/richtexteditor/plugins/documentimport.js +369 -0
  109. package/richtexteditor/plugins/documentoutline.js +341 -0
  110. package/richtexteditor/plugins/emailtoolkit.js +165 -0
  111. package/richtexteditor/plugins/emojiautocomplete.js +241 -0
  112. package/richtexteditor/plugins/foldheadings.js +181 -0
  113. package/richtexteditor/plugins/ghostcomplete.js +297 -0
  114. package/richtexteditor/plugins/html2pdf.js +158 -0
  115. package/richtexteditor/plugins/imageeditor.js +159 -0
  116. package/richtexteditor/plugins/insertcode.js +319 -0
  117. package/richtexteditor/plugins/insertemoji.js +229 -0
  118. package/richtexteditor/plugins/insertgallery.js +362 -0
  119. package/richtexteditor/plugins/inserttemplate.js +261 -0
  120. package/richtexteditor/plugins/localdraft.js +213 -0
  121. package/richtexteditor/plugins/markdownexport.js +110 -0
  122. package/richtexteditor/plugins/mathdialog.js +153 -0
  123. package/richtexteditor/plugins/mentionplugin.js +534 -0
  124. package/richtexteditor/plugins/mermaiddiagram.js +294 -0
  125. package/richtexteditor/plugins/readabilitystats.js +148 -0
  126. package/richtexteditor/plugins/readaloud.js +243 -0
  127. package/richtexteditor/plugins/restrictedediting.js +208 -0
  128. package/richtexteditor/plugins/revisionhistory.js +670 -0
  129. package/richtexteditor/plugins/samples/insertcode_v1.js +76 -0
  130. package/richtexteditor/plugins/slashcommand.js +705 -0
  131. package/richtexteditor/plugins/smartchips.js +250 -0
  132. package/richtexteditor/plugins/spellcheck.js +181 -0
  133. package/richtexteditor/plugins/todolist.js +137 -0
  134. package/richtexteditor/plugins/trackedchanges.js +658 -0
  135. package/richtexteditor/plugins/tui.image-editor/fabric.min.js +8 -0
  136. package/richtexteditor/plugins/tui.image-editor/note.txt +5 -0
  137. package/richtexteditor/plugins/tui.image-editor/svg/icon-a.svg +235 -0
  138. package/richtexteditor/plugins/tui.image-editor/svg/icon-b.svg +224 -0
  139. package/richtexteditor/plugins/tui.image-editor/svg/icon-c.svg +224 -0
  140. package/richtexteditor/plugins/tui.image-editor/svg/icon-d.svg +224 -0
  141. package/richtexteditor/plugins/tui.image-editor/tui-code-snippet.min.js +7 -0
  142. package/richtexteditor/plugins/tui.image-editor/tui-color-picker.min.css +6 -0
  143. package/richtexteditor/plugins/tui.image-editor/tui-color-picker.min.js +7 -0
  144. package/richtexteditor/plugins/tui.image-editor/tui-image-editor.min.css +7 -0
  145. package/richtexteditor/plugins/tui.image-editor/tui-image-editor.min.js +15 -0
  146. package/richtexteditor/plugins/typewriter.js +203 -0
  147. package/richtexteditor/plugins/wordexport.js +0 -0
  148. package/richtexteditor/plugins/yjscollab.js +693 -0
  149. package/richtexteditor/plugins/zz_richtextbox_dialog_style.js +335 -0
  150. package/richtexteditor/rte-config.js +933 -0
  151. package/richtexteditor/rte.js +943 -0
  152. package/richtexteditor/rte_mobile.css +234 -0
  153. package/richtexteditor/rte_theme_default.css +4819 -0
  154. package/richtexteditor/runtime/colorpicker_more_ns.htm +2613 -0
  155. package/richtexteditor/runtime/help.htm +162 -0
  156. package/richtexteditor/runtime/images/1x1.gif +0 -0
  157. package/richtexteditor/runtime/images/cpie_Color.cur +0 -0
  158. package/richtexteditor/runtime/images/cpie_GradientPositionDark.gif +0 -0
  159. package/richtexteditor/runtime/images/cpie_GradientPositionLight.gif +0 -0
  160. package/richtexteditor/runtime/images/cpie_VerticalPosition.gif +0 -0
  161. package/richtexteditor/runtime/images/cpie_WebSafe.gif +0 -0
  162. package/richtexteditor/runtime/images/cpie_gradients.png +0 -0
  163. package/richtexteditor/runtime/images/cpns_Color.cur +0 -0
  164. package/richtexteditor/runtime/images/cpns_ColorSpace1.png +0 -0
  165. package/richtexteditor/runtime/images/cpns_ColorSpace2.png +0 -0
  166. package/richtexteditor/runtime/images/cpns_GradientPositionDark.gif +0 -0
  167. package/richtexteditor/runtime/images/cpns_GradientPositionLight.gif +0 -0
  168. package/richtexteditor/runtime/images/cpns_Vertical1.png +0 -0
  169. package/richtexteditor/runtime/images/cpns_Vertical2.png +0 -0
  170. package/richtexteditor/runtime/images/cpns_VerticalPosition.gif +0 -0
  171. package/richtexteditor/runtime/images/cpns_WebSafe.gif +0 -0
  172. package/richtexteditor/runtime/images/cpns_gradients.png +0 -0
  173. package/richtexteditor/runtime/images/cpns_hue2.png +0 -0
  174. package/richtexteditor/runtime/images/formbn.gif +0 -0
  175. package/richtexteditor/runtime/images/multiclavier.gif +0 -0
  176. package/richtexteditor/runtime/richtexteditor_content.css +362 -0
  177. package/richtexteditor/runtime/richtexteditor_preview.css +302 -0
  178. package/richtexteditor/runtime/richtexteditor_preview.js +1 -0
  179. package/server/index.d.ts +21 -0
  180. package/server/index.js +23 -0
  181. package/svelte/index.d.ts +19 -0
  182. package/svelte/index.js +59 -0
  183. package/vue/index.d.ts +94 -0
  184. package/vue/index.js +291 -0
@@ -0,0 +1,294 @@
1
+ if (!window.RTE_DefaultConfig) window.RTE_DefaultConfig = {};
2
+
3
+ // 2026-06-04 Mermaid diagram block. Closes the "diagrams-as-code" gap vs
4
+ // GitHub / GitLab / Notion / Obsidian. Authors write Mermaid text (flowcharts,
5
+ // sequence, gantt, pie, class, state…) and the block renders to SVG.
6
+ //
7
+ // No renderer is bundled (Mermaid is ~2 MB). Three tiers, mirroring the math
8
+ // plugin's "host supplies KaTeX/MathJax" approach:
9
+ // 1. config.diagramResolver(source) -> Promise<{ svg } | { imageUrl }>
10
+ // — server-side render (e.g. Kroki) for hosts without client Mermaid.
11
+ // 2. window.mermaid (loaded by the host page) — client-side render.
12
+ // 3. neither — the source is stored + shown as a <pre> code fallback and will
13
+ // render wherever a renderer is later available (editor.renderDiagrams()).
14
+ // The Mermaid source always round-trips in data-diagram, so the block stays
15
+ // editable and re-renderable regardless of which tier produced the output.
16
+ RTE_DefaultConfig.plugin_mermaiddiagram = RTE_Plugin_MermaidDiagram;
17
+ RTE_DefaultConfig.diagramResolver = RTE_DefaultConfig.diagramResolver || null;
18
+ RTE_DefaultConfig.diagramSamples = RTE_DefaultConfig.diagramSamples || [
19
+ "graph TD\n A[Start] --> B{Decision}\n B -->|Yes| C[Do it]\n B -->|No| D[Skip]",
20
+ "sequenceDiagram\n Alice->>Bob: Hello Bob\n Bob-->>Alice: Hi Alice",
21
+ "pie title Pets\n \"Dogs\" : 386\n \"Cats\" : 85\n \"Birds\" : 24",
22
+ "gantt\n title Schedule\n dateFormat YYYY-MM-DD\n section Phase 1\n Spec :a1, 2026-01-01, 20d\n Build :after a1, 30d"
23
+ ];
24
+
25
+ var RTE_MermaidSeq = 0;
26
+
27
+ function RTE_Plugin_MermaidDiagram() {
28
+ var obj = this;
29
+ var config, editor;
30
+
31
+ obj.PluginName = "MermaidDiagram";
32
+
33
+ obj.InitConfig = function (argconfig) { config = argconfig; };
34
+
35
+ obj.InitEditor = function (argeditor) {
36
+ editor = argeditor;
37
+ // Public API: render every diagram block via the active renderer.
38
+ editor.renderDiagrams = function () { return obj.RenderAll(); };
39
+ editor.attachEvent("exec_command_insertdiagram", function (state) {
40
+ state.returnValue = true;
41
+ obj.OpenDiagramDialog();
42
+ });
43
+ // Edit-in-place: double-clicking a rendered block reopens the editor.
44
+ try {
45
+ var ed = editor.getEditable();
46
+ if (ed) ed.addEventListener("dblclick", function (e) {
47
+ var b = closestDiagram(e.target, ed);
48
+ if (b) { e.preventDefault(); obj.OpenDiagramDialog(b); }
49
+ });
50
+ if (ed) ed.addEventListener("click", function (e) {
51
+ var node = e.target;
52
+ while (node && node !== ed) {
53
+ if (node.nodeType === 1 && node.getAttribute && node.getAttribute("data-rte-mermaid-remove") === "1") {
54
+ var b = closestDiagram(node, ed);
55
+ if (b) {
56
+ e.preventDefault();
57
+ e.stopPropagation();
58
+ removeDiagram(b);
59
+ }
60
+ return;
61
+ }
62
+ node = node.parentNode;
63
+ }
64
+ }, true);
65
+ if (ed) ed.addEventListener("keydown", function (e) {
66
+ if (e.key !== "Delete" && e.key !== "Backspace") return;
67
+ var b = findDiagramAtSelection();
68
+ if (!b) return;
69
+ e.preventDefault();
70
+ removeDiagram(b);
71
+ }, true);
72
+ } catch (e) { /* ignore */ }
73
+ };
74
+
75
+ function append(parent, tag, cssText, className) {
76
+ var el = parent.ownerDocument.createElement(tag);
77
+ if (cssText) el.style.cssText = cssText;
78
+ if (className) el.className = className;
79
+ parent.appendChild(el);
80
+ return el;
81
+ }
82
+ function escAttr(s) {
83
+ return String(s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
84
+ }
85
+ function unescAttr(s) {
86
+ return String(s).replace(/&quot;/g, '"').replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&amp;/g, "&");
87
+ }
88
+ function escText(s) {
89
+ return String(s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
90
+ }
91
+
92
+ function closestDiagram(node, editable) {
93
+ while (node && node !== editable) {
94
+ if (node.nodeType === 1 && node.classList && node.classList.contains("rte-mermaid")) return node;
95
+ node = node.parentNode;
96
+ }
97
+ return null;
98
+ }
99
+ function findDiagramAtSelection() {
100
+ try {
101
+ var sel = editor.getSelection();
102
+ if (!sel || sel.rangeCount === 0) return null;
103
+ return closestDiagram(sel.getRangeAt(0).startContainer, editor.getEditable());
104
+ } catch (e) { return null; }
105
+ }
106
+ function removeDiagram(block) {
107
+ if (!block || !block.parentNode) return;
108
+ block.parentNode.removeChild(block);
109
+ try { if (typeof editor.fireChange === "function") editor.fireChange(); } catch (e) { /* ignore */ }
110
+ try { editor.focus(); } catch (e2) { /* ignore */ }
111
+ }
112
+
113
+ // Inject block CSS at insert/render time (the iframe head isn't ready at
114
+ // InitEditor). head -> documentElement fallback.
115
+ function injectStyles(doc) {
116
+ if (!doc || doc.getElementById("rte-mermaid-styles")) return;
117
+ var css =
118
+ ".rte-mermaid{position:relative;display:block;margin:12px 0;padding:12px;border:1px solid #e2e8f0;border-radius:10px;" +
119
+ "background:#fff;text-align:center;overflow:auto;}" +
120
+ ".rte-mermaid-remove{position:absolute;top:7px;right:7px;width:24px;height:24px;border:1px solid rgba(15,23,42,.14);border-radius:999px;background:rgba(255,255,255,.96);color:#334155;font:700 13px/1 -apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;box-shadow:0 4px 12px rgba(15,23,42,.12);opacity:0;cursor:pointer;transition:opacity 120ms,border-color 120ms,color 120ms;}" +
121
+ ".rte-mermaid:hover .rte-mermaid-remove,.rte-mermaid-remove:focus{opacity:1;}" +
122
+ ".rte-mermaid-remove:hover{border-color:rgba(220,38,38,.38);color:#b91c1c;}" +
123
+ ".rte-mermaid svg{max-width:100%;height:auto;}" +
124
+ ".rte-mermaid .rte-mermaid-src{display:block;text-align:left;white-space:pre;overflow:auto;" +
125
+ "font:12px ui-monospace,Consolas,monospace;color:#334155;background:#f8fafc;border-radius:6px;padding:10px;margin:0;}" +
126
+ ".rte-mermaid .rte-mermaid-err{color:#b91c1c;font:12px ui-monospace,monospace;text-align:left;}";
127
+ var style = doc.createElement("style");
128
+ style.id = "rte-mermaid-styles";
129
+ style.appendChild(doc.createTextNode(css));
130
+ (doc.head || doc.getElementsByTagName("head")[0] || doc.documentElement).appendChild(style);
131
+ }
132
+
133
+ function getMermaid(win) {
134
+ try { return win.mermaid || (win.parent && win.parent.mermaid) || null; } catch (e) { return null; }
135
+ }
136
+
137
+ // Normalize mermaid.render across v8/v9 (string return) and v10+ (promise).
138
+ function mermaidRender(mermaid, id, src) {
139
+ return new Promise(function (resolve, reject) {
140
+ try {
141
+ var r = mermaid.render(id, src);
142
+ if (r && typeof r.then === "function") r.then(function (o) { resolve(o && o.svg ? o.svg : o); }, reject);
143
+ else if (typeof r === "string") resolve(r);
144
+ else reject(new Error("Unsupported Mermaid API"));
145
+ } catch (e) { reject(e); }
146
+ });
147
+ }
148
+
149
+ // Render `source` into `target` (a .rte-mermaid-render container) using the
150
+ // best available tier. Returns a Promise that always resolves.
151
+ function renderInto(target, source) {
152
+ var doc = target.ownerDocument;
153
+ var win = doc.defaultView || window;
154
+ function fallback(msg) {
155
+ target.innerHTML = "";
156
+ var pre = doc.createElement("pre");
157
+ pre.className = "rte-mermaid-src";
158
+ pre.textContent = source;
159
+ target.appendChild(pre);
160
+ if (msg) { var e = doc.createElement("div"); e.className = "rte-mermaid-err"; e.textContent = msg; target.appendChild(e); }
161
+ }
162
+ if (typeof config.diagramResolver === "function") {
163
+ return Promise.resolve().then(function () { return config.diagramResolver(source); }).then(function (res) {
164
+ if (res && res.svg) { target.innerHTML = res.svg; }
165
+ else if (res && res.imageUrl) {
166
+ target.innerHTML = "";
167
+ var img = doc.createElement("img"); img.src = res.imageUrl; img.alt = "diagram"; img.style.maxWidth = "100%";
168
+ target.appendChild(img);
169
+ } else { fallback(""); }
170
+ }).catch(function (e) { fallback(e && e.message ? e.message : "resolver error"); });
171
+ }
172
+ var mermaid = getMermaid(win);
173
+ if (mermaid && typeof mermaid.render === "function") {
174
+ var id = "rtemmd" + (++RTE_MermaidSeq);
175
+ return mermaidRender(mermaid, id, source).then(function (svg) {
176
+ target.innerHTML = svg;
177
+ }).catch(function (e) { fallback(e && e.message ? e.message : "render error"); });
178
+ }
179
+ fallback("");
180
+ return Promise.resolve();
181
+ }
182
+
183
+ // Render every diagram block in the editable. Returns the count.
184
+ obj.RenderAll = function () {
185
+ var editable = editor.getEditable();
186
+ if (!editable) return 0;
187
+ injectStyles(editor.getDocument());
188
+ var blocks = editable.querySelectorAll(".rte-mermaid[data-diagram]");
189
+ for (var i = 0; i < blocks.length; i++) {
190
+ var b = blocks[i];
191
+ var target = b.querySelector(".rte-mermaid-render");
192
+ if (!target) { target = b.ownerDocument.createElement("div"); target.className = "rte-mermaid-render"; b.appendChild(target); }
193
+ renderInto(target, unescAttr(b.getAttribute("data-diagram") || ""));
194
+ }
195
+ return blocks.length;
196
+ };
197
+
198
+ function buildBlockHTML(source, pendingId) {
199
+ var enc = escAttr(source);
200
+ return '<div class="rte-mermaid" data-rte-diagram="1" data-diagram="' + enc + '" contenteditable="false"' +
201
+ (pendingId ? ' id="' + pendingId + '"' : '') + '>' +
202
+ '<button type="button" class="rte-mermaid-remove" data-rte-mermaid-remove="1" aria-label="Remove diagram" title="Remove diagram">x</button>' +
203
+ '<div class="rte-mermaid-render"><pre class="rte-mermaid-src">' + escText(source) + '</pre></div>' +
204
+ '</div>';
205
+ }
206
+
207
+ obj.OpenDiagramDialog = function (existing) {
208
+ if (!existing) existing = findDiagramAtSelection();
209
+ var initial = existing ? unescAttr(existing.getAttribute("data-diagram") || "") : "";
210
+
211
+ var dlg = editor.createDialog(
212
+ (editor.getLangText && editor.getLangText("insertdiagramtitle")) || "Insert / edit diagram",
213
+ "rte-dialog-diagram"
214
+ );
215
+ var close = typeof dlg.close === "function" ? function () { dlg.close(); } : function () { editor.closeCurrentPopup(); };
216
+ var win = dlg.ownerDocument.defaultView || window;
217
+
218
+ var wrap = append(dlg, "div", "padding:14px;min-width:480px;font:13px -apple-system,Segoe UI,sans-serif");
219
+ append(wrap, "div", "font-weight:600;margin-bottom:4px").innerText =
220
+ (editor.getLangText && editor.getLangText("insertdiagramlabel")) || "Mermaid";
221
+ var ta = append(wrap, "textarea",
222
+ "width:100%;box-sizing:border-box;min-height:120px;padding:8px 10px;border:1px solid #cbd5e1;border-radius:8px;font:12px ui-monospace,Consolas,monospace;resize:vertical");
223
+ ta.value = initial;
224
+ ta.placeholder = "graph TD\n A --> B";
225
+
226
+ var samples = append(wrap, "div", "display:flex;flex-wrap:wrap;gap:6px;margin:8px 0");
227
+ (config.diagramSamples || []).forEach(function (s) {
228
+ var chip = append(samples, "button", "font:11px ui-monospace,monospace;padding:3px 8px;border:1px solid #e2e8f0;border-radius:6px;background:#f8fafc;cursor:pointer");
229
+ chip.type = "button";
230
+ chip.textContent = (s.split("\n")[0] || "sample").slice(0, 22);
231
+ chip.title = s;
232
+ chip.onclick = function () { ta.value = s; updatePreview(); ta.focus(); };
233
+ });
234
+
235
+ append(wrap, "div", "font-weight:600;margin:10px 0 4px").innerText =
236
+ (editor.getLangText && editor.getLangText("insertdiagrampreview")) || "Preview";
237
+ var preview = append(wrap, "div",
238
+ "min-height:80px;padding:10px 12px;border:1px solid #e2e8f0;border-radius:8px;background:#fff;overflow:auto;text-align:center");
239
+ var note = append(wrap, "div", "font-size:11px;color:#94a3b8;margin-top:4px");
240
+ if (typeof config.diagramResolver !== "function" && !getMermaid(win)) {
241
+ note.innerText = "No Mermaid renderer detected on the page — the diagram is stored as source and will render wherever Mermaid (or config.diagramResolver) is available.";
242
+ }
243
+
244
+ var previewTimer = null;
245
+ function updatePreview() {
246
+ var src = ta.value.trim();
247
+ if (!src) { preview.textContent = ""; return; }
248
+ if (previewTimer) clearTimeout(previewTimer);
249
+ previewTimer = setTimeout(function () {
250
+ var holder = preview.ownerDocument.createElement("div");
251
+ holder.className = "rte-mermaid-render";
252
+ preview.innerHTML = "";
253
+ preview.appendChild(holder);
254
+ renderInto(holder, src);
255
+ }, 250);
256
+ }
257
+ ta.oninput = updatePreview;
258
+ setTimeout(function () { ta.focus(); updatePreview(); }, 0);
259
+
260
+ var footer = append(wrap, "div", "display:flex;justify-content:flex-end;gap:8px;margin-top:14px");
261
+ var cancel = append(footer, "button", "padding:6px 14px;border:1px solid #cbd5e1;border-radius:8px;background:#fff;cursor:pointer");
262
+ cancel.type = "button"; cancel.textContent = "Cancel"; cancel.onclick = close;
263
+ var ok = append(footer, "button", "padding:6px 14px;border:1px solid #1d67ba;border-radius:8px;background:#1d67ba;color:#fff;cursor:pointer");
264
+ ok.type = "button"; ok.textContent = existing ? "Update" : "Insert";
265
+
266
+ ok.onclick = function () {
267
+ var src = ta.value.trim();
268
+ if (!src) { close(); return; }
269
+ injectStyles(editor.getDocument());
270
+ if (existing) {
271
+ existing.setAttribute("data-diagram", escAttr(src));
272
+ var target = existing.querySelector(".rte-mermaid-render");
273
+ if (!target) { target = existing.ownerDocument.createElement("div"); target.className = "rte-mermaid-render"; existing.appendChild(target); }
274
+ renderInto(target, src);
275
+ close();
276
+ editor.focus();
277
+ return;
278
+ }
279
+ var pendingId = "rtemmd-pending-" + (++RTE_MermaidSeq);
280
+ editor.insertHTML(buildBlockHTML(src, pendingId));
281
+ close();
282
+ try {
283
+ var doc = editor.getDocument();
284
+ var block = doc.getElementById(pendingId);
285
+ if (block) {
286
+ block.removeAttribute("id");
287
+ var target = block.querySelector(".rte-mermaid-render");
288
+ renderInto(target, src);
289
+ }
290
+ } catch (e) { /* ignore */ }
291
+ editor.focus();
292
+ };
293
+ };
294
+ }
@@ -0,0 +1,148 @@
1
+ if (!window.RTE_DefaultConfig) window.RTE_DefaultConfig = {};
2
+
3
+ // 2026-06-09 Readability statistics. An instant, LOCAL, no-API writing-assistant
4
+ // readout (Hemingway / Yoast style) that complements the built-in word/char
5
+ // count: Flesch Reading Ease, Flesch-Kincaid Grade Level, reading time, and the
6
+ // sentence/word/syllable breakdown. Pure computation — works offline, no key,
7
+ // no library. Read-only (never mutates the document).
8
+ //
9
+ // API:
10
+ // editor.getReadabilityStats(text?) -> { words, sentences, syllables, characters,
11
+ // avgWordsPerSentence, avgSyllablesPerWord, fleschReadingEase, fleschKincaidGrade,
12
+ // readingTimeMinutes, readingTimeText, easeLabel }
13
+ // editor.countSyllables(word) -> number (heuristic)
14
+ // Command: exec_command "readability" opens a stats dialog. Slash: "/readability".
15
+ // Config:
16
+ // config.readabilityStats = false // disable
17
+ // config.readabilityWordsPerMinute = 200 // reading-speed for the time estimate
18
+ RTE_DefaultConfig.plugin_readabilitystats = RTE_Plugin_ReadabilityStats;
19
+ if (typeof RTE_DefaultConfig.readabilityStats === "undefined") RTE_DefaultConfig.readabilityStats = true;
20
+
21
+ function RTE_Plugin_ReadabilityStats() {
22
+ var obj = this;
23
+ var config, editor;
24
+
25
+ obj.PluginName = "ReadabilityStats";
26
+
27
+ obj.InitConfig = function (argconfig) { config = argconfig; };
28
+
29
+ obj.InitEditor = function (argeditor) {
30
+ editor = argeditor;
31
+ if (config.readabilityStats === false) return;
32
+
33
+ editor.countSyllables = function (w) { return countSyllables(w); };
34
+ editor.getReadabilityStats = function (text) { return compute(text != null ? String(text) : currentText()); };
35
+
36
+ editor.attachEvent("exec_command_readability", function (state) {
37
+ state.returnValue = true;
38
+ state.stopBubble = true;
39
+ openDialog();
40
+ });
41
+
42
+ if (editor.slashCommands && typeof editor.slashCommands.register === "function") {
43
+ try {
44
+ editor.slashCommands.register({
45
+ id: "readability-stats",
46
+ title: "Readability statistics",
47
+ description: "Flesch reading ease, grade level, and reading time",
48
+ keywords: ["readability", "flesch", "grade", "reading", "stats", "score"],
49
+ action: function () { openDialog(); }
50
+ });
51
+ } catch (e) {}
52
+ }
53
+ };
54
+
55
+ function currentText() {
56
+ try {
57
+ var ed = editor.getEditable && editor.getEditable();
58
+ if (ed) return ed.innerText || ed.textContent || "";
59
+ } catch (e) {}
60
+ return "";
61
+ }
62
+
63
+ // Heuristic English syllable counter (good enough for Flesch metrics).
64
+ function countSyllables(word) {
65
+ word = String(word || "").toLowerCase().replace(/[^a-z]/g, "");
66
+ if (!word) return 0;
67
+ if (word.length <= 3) return 1;
68
+ word = word.replace(/(?:[^laeiouy]es|ed|[^laeiouy]e)$/, "").replace(/^y/, "");
69
+ var m = word.match(/[aeiouy]{1,2}/g);
70
+ return m ? m.length : 1;
71
+ }
72
+
73
+ function compute(text) {
74
+ text = String(text || "").replace(/\s+/g, " ").trim();
75
+ var wordTokens = text ? text.split(/\s+/) : [];
76
+ var words = wordTokens.length;
77
+ // Sentences: terminal . ! ? (collapsed runs). At least 1 if there is text.
78
+ var sentenceMatches = text.match(/[^.!?]+[.!?]+/g);
79
+ var sentences = sentenceMatches ? sentenceMatches.length : (words ? 1 : 0);
80
+ if (words && sentences === 0) sentences = 1;
81
+ var syllables = 0;
82
+ for (var i = 0; i < wordTokens.length; i++) syllables += countSyllables(wordTokens[i]);
83
+ var characters = text.replace(/\s/g, "").length;
84
+
85
+ var wps = sentences ? words / sentences : 0;
86
+ var spw = words ? syllables / words : 0;
87
+ var ease = words ? (206.835 - 1.015 * wps - 84.6 * spw) : 0;
88
+ var grade = words ? (0.39 * wps + 11.8 * spw - 15.59) : 0;
89
+ ease = Math.round(ease * 10) / 10;
90
+ grade = Math.round(grade * 10) / 10;
91
+
92
+ var wpm = parseInt(config.readabilityWordsPerMinute, 10) || 200;
93
+ var minutes = words / wpm;
94
+ var readingTimeText = minutes < 1 ? (Math.max(1, Math.round(minutes * 60)) + " sec") : (Math.round(minutes) + " min");
95
+
96
+ return {
97
+ words: words, sentences: sentences, syllables: syllables, characters: characters,
98
+ avgWordsPerSentence: Math.round(wps * 10) / 10,
99
+ avgSyllablesPerWord: Math.round(spw * 100) / 100,
100
+ fleschReadingEase: ease,
101
+ fleschKincaidGrade: Math.max(0, grade),
102
+ readingTimeMinutes: Math.round(minutes * 10) / 10,
103
+ readingTimeText: readingTimeText,
104
+ easeLabel: easeLabel(ease)
105
+ };
106
+ }
107
+
108
+ function easeLabel(ease) {
109
+ if (ease >= 90) return "Very easy (5th grade)";
110
+ if (ease >= 80) return "Easy (6th grade)";
111
+ if (ease >= 70) return "Fairly easy (7th grade)";
112
+ if (ease >= 60) return "Standard (8–9th grade)";
113
+ if (ease >= 50) return "Fairly difficult (10–12th grade)";
114
+ if (ease >= 30) return "Difficult (college)";
115
+ return "Very difficult (college graduate)";
116
+ }
117
+
118
+ function openDialog() {
119
+ var s = compute(currentText());
120
+ try {
121
+ var d = editor.createDialog((editor.getLangText && editor.getLangText("readabilitytitle")) || "Readability statistics", "rte-dialog-readability");
122
+ var doc = d.ownerDocument;
123
+ var wrap = doc.createElement("div");
124
+ wrap.style.cssText = "padding:14px 18px;min-width:340px;font:13px -apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Arial,sans-serif;color:#0f172a";
125
+
126
+ var rows = [
127
+ ["Flesch Reading Ease", s.fleschReadingEase + " — " + s.easeLabel, true],
128
+ ["Flesch–Kincaid Grade", String(s.fleschKincaidGrade), false],
129
+ ["Reading time", s.readingTimeText, false],
130
+ ["Words", String(s.words), false],
131
+ ["Sentences", String(s.sentences), false],
132
+ ["Avg words / sentence", String(s.avgWordsPerSentence), false],
133
+ ["Avg syllables / word", String(s.avgSyllablesPerWord), false],
134
+ ["Characters (no spaces)", String(s.characters), false]
135
+ ];
136
+ for (var i = 0; i < rows.length; i++) {
137
+ var r = doc.createElement("div");
138
+ r.style.cssText = "display:flex;justify-content:space-between;gap:24px;padding:6px 0;border-bottom:1px solid #eef2f7" + (rows[i][2] ? ";font-weight:600;font-size:14px" : "");
139
+ var k = doc.createElement("span"); k.textContent = rows[i][0]; k.style.color = "#475569";
140
+ var v = doc.createElement("span"); v.textContent = rows[i][1]; v.style.textAlign = "right";
141
+ r.appendChild(k); r.appendChild(v); wrap.appendChild(r);
142
+ }
143
+ d.appendChild(wrap);
144
+ } catch (e) {
145
+ if (window.console) console.warn("readabilitystats:", s);
146
+ }
147
+ }
148
+ }