@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,670 @@
1
+ if (!window.RTE_DefaultConfig) window.RTE_DefaultConfig = {};
2
+
3
+ if (!RTE_DefaultConfig.svgCode_revisionhistory) {
4
+ RTE_DefaultConfig.svgCode_revisionhistory = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 12a9 9 0 1 0 3-6.7"/><path d="M3 4v5h5"/><path d="M12 8v4l3 2"/></svg>';
5
+ }
6
+
7
+ RTE_DefaultConfig.plugin_revisionhistory = RTE_Plugin_RevisionHistory;
8
+
9
+ function RTE_Plugin_RevisionHistory() {
10
+ var obj = this;
11
+ var config;
12
+ var editor;
13
+ var dialog = null;
14
+ var autoSnapshotTimer = null;
15
+ var lastSnapshotHtml = "";
16
+
17
+ obj.PluginName = "RevisionHistory";
18
+
19
+ obj.InitConfig = function (argconfig) {
20
+ config = argconfig;
21
+ if (config.revisionHistoryEnabled === false) return;
22
+
23
+ config.revisionHistoryMaxEntries = config.revisionHistoryMaxEntries || 50;
24
+ config.revisionHistoryAutoSnapshotMs = config.revisionHistoryAutoSnapshotMs || 0;
25
+ config.revisionHistoryUrl = config.revisionHistoryUrl || "";
26
+ config.text_revisionhistory = config.text_revisionhistory || "Revision history";
27
+
28
+ appendToolbarCommand("toolbar_default", "#{revisionhistory}");
29
+ appendToolbarCommand("toolbar_full", "#{revisionhistory}");
30
+ appendToolbarCommand("toolbar_mobile", "#{revisionhistory}");
31
+ if ((config.controltoolbar_TEXT || "").indexOf("revisionhistory") === -1) {
32
+ config.controltoolbar_TEXT = (config.controltoolbar_TEXT || "") + "|{revisionhistory}";
33
+ }
34
+ };
35
+
36
+ obj.InitEditor = function (argeditor) {
37
+ editor = argeditor;
38
+ if (config.revisionHistoryEnabled === false) return;
39
+
40
+ editor.revisionHistory = {
41
+ snapshot: function (label, metadata) { return snapshot(label, metadata); },
42
+ promptAndSnapshot: function (defaultLabel) { return promptAndSnapshot(defaultLabel); },
43
+ rename: function (id, label) { return rename(id, label); },
44
+ list: function () { return getStore().slice().sort(function (a, b) { return (b.createdAt || 0) - (a.createdAt || 0); }); },
45
+ listNamed: function () {
46
+ return getStore().slice()
47
+ .filter(function (e) { return e.isNamed === true; })
48
+ .sort(function (a, b) { return (b.createdAt || 0) - (a.createdAt || 0); });
49
+ },
50
+ get: function (id) { return findById(id); },
51
+ restore: function (id, opts) { return restore(id, opts || {}); },
52
+ delete: function (id) { return deleteEntry(id); },
53
+ clear: function () { return clearAll(); },
54
+ diff: function (idA, idB) { return diffBetween(idA, idB); },
55
+ openBrowser: function () { openDialog(); },
56
+ closeBrowser: function () { closeDialog(); }
57
+ };
58
+
59
+ editor.toolbarFactoryMap = editor.toolbarFactoryMap || {};
60
+ editor.toolbarFactoryMap["revisionhistory"] = function (cmd) {
61
+ return editor.createToolbarButton(cmd);
62
+ };
63
+
64
+ editor.attachEvent("exec_command_revisionhistory", function (state) {
65
+ state.returnValue = true;
66
+ state.stopBubble = true;
67
+ openDialog();
68
+ });
69
+
70
+ injectStyles();
71
+ restoreStore();
72
+ lastSnapshotHtml = editor.getHTML ? editor.getHTML() : "";
73
+
74
+ // Auto-snapshot on idle typing (opt-in).
75
+ if (config.revisionHistoryAutoSnapshotMs > 0) {
76
+ editor.getDocument().addEventListener("input", function () {
77
+ if (autoSnapshotTimer) clearTimeout(autoSnapshotTimer);
78
+ autoSnapshotTimer = setTimeout(function () {
79
+ var currentHtml = editor.getHTML();
80
+ if (currentHtml !== lastSnapshotHtml) {
81
+ snapshot("Auto", { auto: true });
82
+ }
83
+ }, config.revisionHistoryAutoSnapshotMs);
84
+ }, true);
85
+ }
86
+
87
+ // Keyboard shortcut: Ctrl/Cmd + Shift + H opens the browser.
88
+ editor.getDocument().addEventListener("keydown", function (e) {
89
+ if ((e.ctrlKey || e.metaKey) && e.shiftKey && (e.key === "H" || e.key === "h")) {
90
+ e.preventDefault();
91
+ openDialog();
92
+ }
93
+ }, true);
94
+ };
95
+
96
+ function appendToolbarCommand(toolbar, item) {
97
+ if (!config[toolbar]) return;
98
+ if (config[toolbar].indexOf(item) !== -1) return;
99
+ config[toolbar] = config[toolbar] + item;
100
+ }
101
+
102
+ function getCurrentUser() {
103
+ if (config.currentUser && config.currentUser.id) return config.currentUser;
104
+ return { id: "user", name: "User", color: "#64748b" };
105
+ }
106
+
107
+ function getStoreKey() {
108
+ var key = config.aiToolkitPersistenceKey || "default";
109
+ return "RTE.Revisions." + key;
110
+ }
111
+
112
+ function getStore() {
113
+ if (!editor.__revisions) editor.__revisions = [];
114
+ return editor.__revisions;
115
+ }
116
+
117
+ function findById(id) {
118
+ var store = getStore();
119
+ for (var i = 0; i < store.length; i++) if (store[i].id === id) return store[i];
120
+ return null;
121
+ }
122
+
123
+ function persistStore() {
124
+ var store = getStore();
125
+ // Trim oldest beyond max.
126
+ while (store.length > config.revisionHistoryMaxEntries) {
127
+ store.shift();
128
+ }
129
+ try {
130
+ if (window.localStorage) {
131
+ window.localStorage.setItem(getStoreKey(), JSON.stringify(store));
132
+ }
133
+ } catch (err) {
134
+ console.warn("revisionhistory: localStorage quota or access issue:", err && err.message);
135
+ }
136
+ if (config.revisionHistoryUrl) {
137
+ sendRemote(store);
138
+ }
139
+ }
140
+
141
+ function sendRemote(store) {
142
+ try {
143
+ var xhr = new XMLHttpRequest();
144
+ xhr.open("POST", config.revisionHistoryUrl, true);
145
+ xhr.setRequestHeader("Content-Type", "application/json");
146
+ xhr.send(JSON.stringify({
147
+ documentKey: config.aiToolkitPersistenceKey || "",
148
+ savedAt: Date.now(),
149
+ revisions: store
150
+ }));
151
+ } catch (err) {
152
+ console.warn("revisionhistory: remote POST failed:", err);
153
+ }
154
+ }
155
+
156
+ function restoreStore() {
157
+ try {
158
+ if (!window.localStorage) return;
159
+ var raw = window.localStorage.getItem(getStoreKey());
160
+ if (!raw) return;
161
+ var parsed = JSON.parse(raw);
162
+ if (Array.isArray(parsed)) editor.__revisions = parsed;
163
+ } catch (ignore) { }
164
+ }
165
+
166
+ function snapshot(label, metadata) {
167
+ var html = editor.getHTML ? editor.getHTML() : (editor.getHTMLCode ? editor.getHTMLCode() : "");
168
+ var user = getCurrentUser();
169
+ var trimmedLabel = (label || "").toString().slice(0, 120);
170
+ var isNamed = trimmedLabel.length > 0 && (!metadata || metadata.auto !== true);
171
+ var entry = {
172
+ id: "rev-" + Date.now() + "-" + Math.floor(Math.random() * 10000),
173
+ html: html,
174
+ text: editor.getText ? editor.getText() : stripHtml(html),
175
+ label: trimmedLabel || autoLabel(),
176
+ isNamed: isNamed,
177
+ author: { id: user.id, name: user.name, color: user.color },
178
+ createdAt: Date.now(),
179
+ metadata: metadata || {}
180
+ };
181
+ getStore().push(entry);
182
+ lastSnapshotHtml = html;
183
+ persistStore();
184
+ if (dialog && dialog.isConnected) renderDialog();
185
+ try { if (typeof editor.fireEvent === "function") editor.fireEvent("revision_snapshot", { id: entry.id, label: entry.label, isNamed: entry.isNamed, createdAt: entry.createdAt }); } catch (e) { }
186
+ return entry;
187
+ }
188
+
189
+ // Prompt the user for a snapshot name via native window.prompt, then take a snapshot.
190
+ // Returns the entry (or null if the user cancelled).
191
+ function promptAndSnapshot(defaultLabel) {
192
+ var iframeDoc = editor.iframe ? editor.iframe.ownerDocument : document;
193
+ var win = iframeDoc.defaultView || window;
194
+ var value = win.prompt("Name this version (e.g. \"Draft sent to legal\"):", defaultLabel || "");
195
+ if (value === null) return null;
196
+ var trimmed = (value || "").trim();
197
+ if (!trimmed) return null;
198
+ return snapshot(trimmed, { user: true });
199
+ }
200
+
201
+ // Update the label of an existing entry. Flips `isNamed` to true so the
202
+ // "only named versions" filter picks it up.
203
+ function rename(id, label) {
204
+ var entry = findById(id);
205
+ if (!entry) return false;
206
+ var trimmed = (label || "").toString().trim().slice(0, 120);
207
+ if (!trimmed) return false;
208
+ entry.label = trimmed;
209
+ entry.isNamed = true;
210
+ persistStore();
211
+ if (dialog && dialog.isConnected) renderDialog();
212
+ return true;
213
+ }
214
+
215
+ // Return { oldText, newText, lines } — useful for callers building their own
216
+ // side-by-side diff UI on top of the plugin's data.
217
+ function diffBetween(idA, idB) {
218
+ var a = findById(idA);
219
+ var b = findById(idB);
220
+ if (!a || !b) return null;
221
+ var oldText = a.text || stripHtml(a.html);
222
+ var newText = b.text || stripHtml(b.html);
223
+ return {
224
+ oldEntry: a,
225
+ newEntry: b,
226
+ oldText: oldText,
227
+ newText: newText,
228
+ lines: diffLines(oldText, newText)
229
+ };
230
+ }
231
+
232
+ function autoLabel() {
233
+ var d = new Date();
234
+ function pad(n) { return n < 10 ? "0" + n : "" + n; }
235
+ return pad(d.getHours()) + ":" + pad(d.getMinutes()) + ":" + pad(d.getSeconds());
236
+ }
237
+
238
+ function restore(id, opts) {
239
+ var entry = findById(id);
240
+ if (!entry) return false;
241
+ if (!opts.skipConfirm) {
242
+ var doc = editor.iframe ? editor.iframe.ownerDocument : document;
243
+ if (!doc.defaultView.confirm("Restore this version? Your current content will be replaced (this itself is not auto-snapshotted).")) {
244
+ return false;
245
+ }
246
+ }
247
+ if (editor.setHTMLCode) editor.setHTMLCode(entry.html);
248
+ else if (editor.setHTML) editor.setHTML(entry.html);
249
+ lastSnapshotHtml = entry.html;
250
+ if (dialog && dialog.isConnected) closeDialog();
251
+ return true;
252
+ }
253
+
254
+ function deleteEntry(id) {
255
+ var store = getStore();
256
+ for (var i = 0; i < store.length; i++) {
257
+ if (store[i].id === id) {
258
+ store.splice(i, 1);
259
+ persistStore();
260
+ if (dialog && dialog.isConnected) renderDialog();
261
+ return true;
262
+ }
263
+ }
264
+ return false;
265
+ }
266
+
267
+ function clearAll() {
268
+ editor.__revisions = [];
269
+ persistStore();
270
+ if (dialog && dialog.isConnected) renderDialog();
271
+ }
272
+
273
+ // --- Simple line-based diff (Hunt–McIlroy style LCS) ---
274
+
275
+ function diffLines(oldText, newText) {
276
+ var a = String(oldText || "").split(/\r?\n/);
277
+ var b = String(newText || "").split(/\r?\n/);
278
+ var out = [];
279
+ var lcs = computeLcs(a, b);
280
+ var i = 0, j = 0, k = 0;
281
+ while (i < a.length && j < b.length) {
282
+ if (k < lcs.length && a[i] === lcs[k] && b[j] === lcs[k]) {
283
+ out.push({ type: "eq", text: a[i] });
284
+ i++; j++; k++;
285
+ } else if (k < lcs.length && a[i] !== lcs[k]) {
286
+ out.push({ type: "del", text: a[i] });
287
+ i++;
288
+ } else if (k < lcs.length && b[j] !== lcs[k]) {
289
+ out.push({ type: "add", text: b[j] });
290
+ j++;
291
+ } else {
292
+ // past the LCS — drain the rest
293
+ if (i < a.length) { out.push({ type: "del", text: a[i] }); i++; }
294
+ else if (j < b.length) { out.push({ type: "add", text: b[j] }); j++; }
295
+ }
296
+ }
297
+ while (i < a.length) { out.push({ type: "del", text: a[i] }); i++; }
298
+ while (j < b.length) { out.push({ type: "add", text: b[j] }); j++; }
299
+ return out;
300
+ }
301
+
302
+ function computeLcs(a, b) {
303
+ // O(n*m) DP. Fine for a few hundred lines; acceptable for snapshot sizes in v1.
304
+ var m = a.length, n = b.length;
305
+ var row = new Array(n + 1).fill(0);
306
+ var prev = new Array(n + 1).fill(0);
307
+ // Backpointers compacted: store length matrix fully, then walk it back.
308
+ var len = [];
309
+ for (var i = 0; i <= m; i++) len.push(new Array(n + 1).fill(0));
310
+ for (var i = 1; i <= m; i++) {
311
+ for (var j = 1; j <= n; j++) {
312
+ if (a[i - 1] === b[j - 1]) len[i][j] = len[i - 1][j - 1] + 1;
313
+ else len[i][j] = Math.max(len[i - 1][j], len[i][j - 1]);
314
+ }
315
+ }
316
+ var out = [];
317
+ var i = m, j = n;
318
+ while (i > 0 && j > 0) {
319
+ if (a[i - 1] === b[j - 1]) { out.unshift(a[i - 1]); i--; j--; }
320
+ else if (len[i - 1][j] >= len[i][j - 1]) i--;
321
+ else j--;
322
+ }
323
+ return out;
324
+ }
325
+
326
+ function stripHtml(html) {
327
+ var tmp = document.createElement("div");
328
+ tmp.innerHTML = html || "";
329
+ return tmp.innerText || tmp.textContent || "";
330
+ }
331
+
332
+ // --- Dialog UI ---
333
+
334
+ function openDialog() {
335
+ if (dialog && dialog.isConnected) return;
336
+ // 2026-05-28 Defense: editor.iframe may be null when revisionhistory
337
+ // is invoked via a keyboard shortcut before InitEditor finishes wiring
338
+ // up the iframe (e.g. customer auto-fires the shortcut on page-load).
339
+ // Bail out cleanly rather than throw "Cannot read property
340
+ // 'ownerDocument' of null" — the next user keypress will retry.
341
+ if (!editor || !editor.iframe || !editor.iframe.ownerDocument) return;
342
+ // Also: if a previous renderDialog() crashed and left `dialog` set
343
+ // but detached, openDialog would short-circuit on isConnected=false
344
+ // line above (correct), so explicitly null it before re-creating.
345
+ if (dialog && !dialog.isConnected) dialog = null;
346
+ var host = editor.iframe.ownerDocument;
347
+ if (!host || !host.body) return;
348
+ dialog = host.createElement("div");
349
+ // Body-appended modal — mirror the editor's forced dark mode (automatic
350
+ // dark is handled by a prefers-color-scheme media query in the styles).
351
+ var __revDark = false;
352
+ try {
353
+ var __h = editor.container || (editor.getEditable && editor.getEditable().closest && editor.getEditable().closest(".richtexteditor"));
354
+ __revDark = !!(__h && __h.classList && __h.classList.contains("rte-dark"));
355
+ } catch (e) {}
356
+ dialog.className = "rte-rev-dialog" + (__revDark ? " rte-rev-dark" : "");
357
+ dialog.setAttribute("role", "dialog");
358
+ dialog.setAttribute("aria-modal", "true");
359
+ dialog.setAttribute("aria-labelledby", "rte-rev-dialog-title");
360
+
361
+ var backdrop = host.createElement("div");
362
+ backdrop.className = "rte-rev-backdrop";
363
+ backdrop.addEventListener("mousedown", function (e) {
364
+ if (e.target === backdrop) closeDialog();
365
+ });
366
+
367
+ var panel = host.createElement("div");
368
+ panel.className = "rte-rev-panel";
369
+ backdrop.appendChild(panel);
370
+ dialog.appendChild(backdrop);
371
+ host.body.appendChild(dialog);
372
+ renderDialog();
373
+ }
374
+
375
+ function closeDialog() {
376
+ if (dialog && dialog.parentNode) dialog.parentNode.removeChild(dialog);
377
+ dialog = null;
378
+ }
379
+
380
+ var dialogSelectedId = null;
381
+ var dialogView = "preview"; // "preview" | "diff"
382
+
383
+ function renderDialog() {
384
+ if (!dialog) return;
385
+ var host = dialog.ownerDocument;
386
+ var panel = dialog.querySelector(".rte-rev-panel");
387
+ // 2026-05-28 Defense: if the dialog DOM was tampered with externally
388
+ // (or a previous render partially failed) `.rte-rev-panel` may be
389
+ // missing. Bail out instead of dereferencing null on innerHTML.
390
+ if (!panel) { closeDialog(); return; }
391
+ panel.innerHTML = "";
392
+
393
+ var header = host.createElement("div");
394
+ header.className = "rte-rev-header";
395
+ var title = host.createElement("div");
396
+ title.id = "rte-rev-dialog-title";
397
+ title.className = "rte-rev-title";
398
+ title.textContent = "Revision history";
399
+ header.appendChild(title);
400
+ var actions = host.createElement("div");
401
+ actions.className = "rte-rev-header-actions";
402
+ var snapBtn = host.createElement("button");
403
+ snapBtn.type = "button";
404
+ snapBtn.className = "rte-rev-btn rte-rev-btn-ghost";
405
+ snapBtn.textContent = "Save version now";
406
+ snapBtn.addEventListener("mousedown", function (e) {
407
+ e.preventDefault();
408
+ var label = host.defaultView.prompt("Label this version (optional):", "");
409
+ if (label === null) return; // cancelled
410
+ snapshot(label || "");
411
+ });
412
+ actions.appendChild(snapBtn);
413
+ var closeBtn = host.createElement("button");
414
+ closeBtn.type = "button";
415
+ closeBtn.className = "rte-rev-btn rte-rev-btn-ghost";
416
+ closeBtn.textContent = "Close";
417
+ closeBtn.setAttribute("aria-label", "Close revision history");
418
+ closeBtn.addEventListener("mousedown", function (e) { e.preventDefault(); closeDialog(); });
419
+ actions.appendChild(closeBtn);
420
+ header.appendChild(actions);
421
+ panel.appendChild(header);
422
+
423
+ var body = host.createElement("div");
424
+ body.className = "rte-rev-body";
425
+
426
+ // Left: list
427
+ var list = host.createElement("div");
428
+ list.className = "rte-rev-list";
429
+ list.setAttribute("role", "listbox");
430
+ list.setAttribute("aria-label", "Saved revisions");
431
+ var entries = editor.revisionHistory.list();
432
+ if (!entries.length) {
433
+ var empty = host.createElement("div");
434
+ empty.className = "rte-rev-empty";
435
+ empty.textContent = "No revisions yet. Click \"Save version now\" to capture the current document.";
436
+ list.appendChild(empty);
437
+ } else {
438
+ if (!dialogSelectedId || !findById(dialogSelectedId)) {
439
+ dialogSelectedId = entries[0].id;
440
+ }
441
+ for (var i = 0; i < entries.length; i++) {
442
+ (function (e) {
443
+ var row = host.createElement("button");
444
+ row.type = "button";
445
+ row.className = "rte-rev-row" + (e.id === dialogSelectedId ? " rte-rev-row-active" : "");
446
+ row.setAttribute("role", "option");
447
+ row.setAttribute("aria-selected", e.id === dialogSelectedId ? "true" : "false");
448
+ var dot = host.createElement("span");
449
+ dot.className = "rte-rev-dot";
450
+ dot.style.background = (e.author && e.author.color) || "#64748b";
451
+ var who = host.createElement("div");
452
+ who.className = "rte-rev-row-who";
453
+ who.textContent = (e.author && e.author.name) || "User";
454
+ var when = host.createElement("div");
455
+ when.className = "rte-rev-row-when";
456
+ when.textContent = formatDate(e.createdAt) + " \u00B7 " + (e.label || "");
457
+ var col = host.createElement("div");
458
+ col.className = "rte-rev-row-col";
459
+ col.appendChild(who);
460
+ col.appendChild(when);
461
+ row.appendChild(dot);
462
+ row.appendChild(col);
463
+ row.addEventListener("mousedown", function (ev) {
464
+ ev.preventDefault();
465
+ dialogSelectedId = e.id;
466
+ renderDialog();
467
+ });
468
+ list.appendChild(row);
469
+ })(entries[i]);
470
+ }
471
+ }
472
+ body.appendChild(list);
473
+
474
+ // Right: preview + diff tabs
475
+ var pane = host.createElement("div");
476
+ pane.className = "rte-rev-pane";
477
+ if (entries.length) {
478
+ var selected = findById(dialogSelectedId) || entries[0];
479
+
480
+ var tabs = host.createElement("div");
481
+ tabs.className = "rte-rev-tabs";
482
+ tabs.setAttribute("role", "tablist");
483
+ tabs.setAttribute("aria-label", "Revision view");
484
+ var tabPreview = host.createElement("button");
485
+ tabPreview.type = "button";
486
+ tabPreview.className = "rte-rev-tab" + (dialogView === "preview" ? " rte-rev-tab-active" : "");
487
+ tabPreview.setAttribute("role", "tab");
488
+ tabPreview.setAttribute("aria-selected", dialogView === "preview" ? "true" : "false");
489
+ tabPreview.textContent = "Preview";
490
+ tabPreview.addEventListener("mousedown", function (e) { e.preventDefault(); dialogView = "preview"; renderDialog(); });
491
+ var tabDiff = host.createElement("button");
492
+ tabDiff.type = "button";
493
+ tabDiff.className = "rte-rev-tab" + (dialogView === "diff" ? " rte-rev-tab-active" : "");
494
+ tabDiff.setAttribute("role", "tab");
495
+ tabDiff.setAttribute("aria-selected", dialogView === "diff" ? "true" : "false");
496
+ tabDiff.textContent = "Diff vs current";
497
+ tabDiff.addEventListener("mousedown", function (e) { e.preventDefault(); dialogView = "diff"; renderDialog(); });
498
+ tabs.appendChild(tabPreview);
499
+ tabs.appendChild(tabDiff);
500
+ pane.appendChild(tabs);
501
+
502
+ var content = host.createElement("div");
503
+ content.className = "rte-rev-content";
504
+ if (dialogView === "preview") {
505
+ var iframe = host.createElement("iframe");
506
+ iframe.className = "rte-rev-preview-frame";
507
+ iframe.setAttribute("title", "Revision preview");
508
+ iframe.setAttribute("sandbox", "allow-same-origin");
509
+ content.appendChild(iframe);
510
+ // Write after appending so document is available.
511
+ setTimeout(function () {
512
+ try {
513
+ iframe.contentDocument.open();
514
+ iframe.contentDocument.write(
515
+ '<!DOCTYPE html><html><head><meta charset="utf-8"><style>body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;padding:16px;color:#0f172a;line-height:1.6}img{max-width:100%}</style></head><body>' +
516
+ (selected.html || "") +
517
+ "</body></html>"
518
+ );
519
+ iframe.contentDocument.close();
520
+ } catch (err) { }
521
+ }, 0);
522
+ } else {
523
+ var currentText = editor.getText ? editor.getText() : stripHtml(editor.getHTML());
524
+ var diff = diffLines(selected.text || stripHtml(selected.html), currentText);
525
+ var diffBox = host.createElement("pre");
526
+ diffBox.className = "rte-rev-diff";
527
+ for (var d = 0; d < diff.length; d++) {
528
+ var line = host.createElement("span");
529
+ line.className = "rte-rev-diff-line rte-rev-diff-" + diff[d].type;
530
+ line.textContent = (diff[d].type === "add" ? "+ " : diff[d].type === "del" ? "- " : " ") + diff[d].text + "\n";
531
+ diffBox.appendChild(line);
532
+ }
533
+ content.appendChild(diffBox);
534
+ }
535
+ pane.appendChild(content);
536
+
537
+ var footer = host.createElement("div");
538
+ footer.className = "rte-rev-footer";
539
+ var labelInfo = host.createElement("div");
540
+ labelInfo.className = "rte-rev-footer-info";
541
+ labelInfo.textContent = selected.label ? "Label: " + selected.label : "";
542
+ var footerBtns = host.createElement("div");
543
+ footerBtns.className = "rte-rev-footer-btns";
544
+ var delBtn = host.createElement("button");
545
+ delBtn.type = "button";
546
+ delBtn.className = "rte-rev-btn rte-rev-btn-danger";
547
+ delBtn.textContent = "Delete";
548
+ delBtn.addEventListener("mousedown", function (e) { e.preventDefault(); deleteEntry(selected.id); });
549
+ var restoreBtn = host.createElement("button");
550
+ restoreBtn.type = "button";
551
+ restoreBtn.className = "rte-rev-btn rte-rev-btn-primary";
552
+ restoreBtn.textContent = "Restore this version";
553
+ restoreBtn.addEventListener("mousedown", function (e) { e.preventDefault(); });
554
+ restoreBtn.addEventListener("click", function (e) {
555
+ e.preventDefault();
556
+ restore(selected.id);
557
+ });
558
+ footerBtns.appendChild(delBtn);
559
+ footerBtns.appendChild(restoreBtn);
560
+ footer.appendChild(labelInfo);
561
+ footer.appendChild(footerBtns);
562
+ pane.appendChild(footer);
563
+ }
564
+ body.appendChild(pane);
565
+ panel.appendChild(body);
566
+ }
567
+
568
+ function formatDate(ts) {
569
+ if (!ts) return "";
570
+ var d = new Date(ts);
571
+ var now = new Date();
572
+ function pad(n) { return n < 10 ? "0" + n : "" + n; }
573
+ var time = pad(d.getHours()) + ":" + pad(d.getMinutes());
574
+ if (d.toDateString() === now.toDateString()) return "Today " + time;
575
+ return d.getFullYear() + "-" + pad(d.getMonth() + 1) + "-" + pad(d.getDate()) + " " + time;
576
+ }
577
+
578
+ // --- styles ---
579
+
580
+ function injectStyles() {
581
+ var host = (editor && editor.iframe && editor.iframe.ownerDocument) || document;
582
+ if (host.querySelector("style[data-rte-revisionhistory]")) return;
583
+ var style = host.createElement("style");
584
+ style.setAttribute("data-rte-revisionhistory", "1");
585
+ style.textContent = [
586
+ ".rte-rev-dialog{position:fixed;inset:0;z-index:2147483600;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;font-size:13px;color:#172033}",
587
+ ".rte-rev-backdrop{position:absolute;inset:0;background:linear-gradient(135deg,rgba(12,18,32,.44),rgba(40,54,77,.26));display:flex;align-items:center;justify-content:center;padding:18px}",
588
+ ".rte-rev-panel{background:linear-gradient(180deg,#ffffff 0%,#f8fbff 100%);border:1px solid rgba(117,137,163,.28);border-radius:20px;width:min(960px,calc(100vw - 28px));max-height:min(82vh,740px);display:flex;flex-direction:column;box-shadow:0 24px 60px rgba(35,48,72,.26);overflow:hidden}",
589
+ ".rte-rev-header{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 14px;border-bottom:1px solid rgba(117,137,163,.18);background:rgba(255,255,255,.72)}",
590
+ ".rte-rev-title{font-size:16px;font-weight:760;letter-spacing:-.01em;color:#172033}",
591
+ ".rte-rev-header-actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap}",
592
+ ".rte-rev-btn{padding:8px 12px;border-radius:999px;border:1px solid rgba(116,135,162,.25);font:inherit;font-size:12px;font-weight:680;line-height:1;cursor:pointer;background:#fff;color:#223652;box-shadow:0 1px 2px rgba(32,45,66,.06);transition:background .14s ease,border-color .14s ease,box-shadow .14s ease,transform .14s ease}",
593
+ ".rte-rev-btn:hover{background:#f5f8fc;border-color:rgba(81,111,151,.42);box-shadow:0 5px 14px rgba(32,45,66,.09);transform:translateY(-1px)}",
594
+ ".rte-rev-btn:focus-visible{outline:2px solid rgba(56,122,255,.34);outline-offset:2px}",
595
+ ".rte-rev-btn-ghost{background:#fff;color:#34506f}",
596
+ ".rte-rev-btn-primary{background:linear-gradient(135deg,#2563eb,#1d67ba);color:#fff;border-color:rgba(29,103,186,.85);box-shadow:0 8px 18px rgba(37,99,235,.22)}",
597
+ ".rte-rev-btn-primary:hover{background:linear-gradient(135deg,#1d4ed8,#195fad);color:#fff;border-color:rgba(29,78,216,.9);box-shadow:0 9px 20px rgba(37,99,235,.28)}",
598
+ ".rte-rev-btn-danger{background:#fff5f5;color:#9f1d1d;border-color:rgba(190,45,45,.22)}",
599
+ ".rte-rev-body{display:flex;flex:1;min-height:0;background:linear-gradient(90deg,#f8fbff 0%,#fff 42%)}",
600
+ ".rte-rev-list{width:258px;flex:0 0 258px;border-right:1px solid rgba(117,137,163,.18);overflow-y:auto;background:rgba(244,248,253,.78);padding:10px}",
601
+ ".rte-rev-empty{padding:16px;border:1px dashed rgba(99,118,145,.28);border-radius:14px;background:#fff;color:#5b6c81;font-size:12px;line-height:1.5}",
602
+ ".rte-rev-row{display:flex;align-items:center;gap:10px;width:100%;padding:10px;border:1px solid transparent;border-radius:14px;background:transparent;text-align:left;cursor:pointer;font:inherit;color:inherit}",
603
+ ".rte-rev-row:hover{background:#fff;border-color:rgba(93,122,161,.24);box-shadow:0 8px 18px rgba(33,48,70,.08)}",
604
+ ".rte-rev-row-active{background:#eef6ff;border-color:rgba(37,99,235,.28);box-shadow:0 8px 22px rgba(37,99,235,.12)}",
605
+ ".rte-rev-dot{width:9px;height:9px;border-radius:50%;flex:0 0 9px;box-shadow:0 0 0 3px rgba(255,255,255,.95),0 0 0 4px rgba(96,116,140,.18)}",
606
+ ".rte-rev-row-col{flex:1;min-width:0}",
607
+ ".rte-rev-row-who{font-weight:720;font-size:12px;line-height:1.25;color:#172033}",
608
+ ".rte-rev-row-when{margin-top:2px;font-size:11px;color:#637487;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}",
609
+ ".rte-rev-pane{flex:1;display:flex;flex-direction:column;min-width:0;padding:10px}",
610
+ ".rte-rev-tabs{display:flex;align-self:flex-start;gap:3px;padding:3px;margin:0 0 10px;border:1px solid rgba(117,137,163,.2);border-radius:999px;background:#eef3f8}",
611
+ ".rte-rev-tab{padding:7px 12px;border:0;border-radius:999px;background:transparent;font:inherit;font-size:12px;font-weight:680;color:#637487;cursor:pointer}",
612
+ ".rte-rev-tab:hover{color:#1d3557;background:rgba(255,255,255,.58)}",
613
+ ".rte-rev-tab:focus-visible{outline:2px solid rgba(56,122,255,.34);outline-offset:2px}",
614
+ ".rte-rev-tab-active{color:#163966;background:#fff;box-shadow:0 2px 7px rgba(35,48,72,.08)}",
615
+ ".rte-rev-content{flex:1;overflow:auto;padding:0;border:1px solid rgba(117,137,163,.18);border-radius:16px;background:#fff;min-height:300px}",
616
+ ".rte-rev-preview-frame{width:100%;height:100%;min-height:330px;border:0;display:block;background:#fff}",
617
+ ".rte-rev-diff{margin:0;padding:14px 16px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:1.55;white-space:pre-wrap;color:#172033;background:#fbfdff;min-height:100%}",
618
+ ".rte-rev-diff-line{display:block}",
619
+ ".rte-rev-diff-add{background:#e8f8ef;color:#17633f}",
620
+ ".rte-rev-diff-del{background:#fff0f0;color:#9f1d1d}",
621
+ ".rte-rev-diff-eq{color:#556575}",
622
+ ".rte-rev-footer{display:flex;justify-content:space-between;align-items:center;gap:12px;padding:10px 0 0;background:transparent}",
623
+ ".rte-rev-footer-info{font-size:12px;color:#637487;min-height:18px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
624
+ ".rte-rev-footer-btns{display:flex;gap:8px;flex-wrap:wrap;justify-content:flex-end}",
625
+ "@media(max-width:720px){.rte-rev-backdrop{padding:10px;align-items:flex-start}.rte-rev-panel{width:calc(100vw - 20px);max-height:calc(100vh - 20px);border-radius:16px}.rte-rev-header{align-items:flex-start;flex-direction:column}.rte-rev-body{flex-direction:column}.rte-rev-list{width:auto;flex:0 0 auto;max-height:178px;border-right:0;border-bottom:1px solid rgba(117,137,163,.18)}.rte-rev-pane{min-height:360px}.rte-rev-footer{align-items:stretch;flex-direction:column}.rte-rev-footer-info{white-space:normal}.rte-rev-footer-btns{justify-content:stretch}.rte-rev-footer-btns .rte-rev-btn{flex:1}}",
626
+ // Dark mode — modal is body-appended; auto via prefers-color-scheme +
627
+ // explicit .rte-rev-dark class when the editor is forced dark. The
628
+ // preview iframe stays white (it renders the actual document).
629
+ "@media (prefers-color-scheme:dark){",
630
+ " .rte-rev-dialog{color:#e2e8f0}",
631
+ " .rte-rev-panel{background:linear-gradient(180deg,#1e293b,#0f172a);border-color:#334155}",
632
+ " .rte-rev-header{background:rgba(30,41,59,.72);border-color:#334155}",
633
+ " .rte-rev-title,.rte-rev-row-who{color:#e2e8f0}",
634
+ " .rte-rev-btn,.rte-rev-btn-ghost{background:#0f172a;color:#cbd5e1;border-color:#334155}",
635
+ " .rte-rev-btn:hover{background:#334155}",
636
+ " .rte-rev-btn-primary,.rte-rev-btn-primary:hover{background:linear-gradient(135deg,#2563eb,#1d4ed8);color:#fff;border-color:#3b82f6}",
637
+ " .rte-rev-body{background:#0f172a}",
638
+ " .rte-rev-list{background:rgba(15,23,42,.6);border-color:#334155}",
639
+ " .rte-rev-empty{background:#1e293b;color:#94a3b8;border-color:#334155}",
640
+ " .rte-rev-row:hover{background:#1e293b;border-color:#334155}",
641
+ " .rte-rev-row-active{background:#1e3a8a;border-color:#3b82f6}",
642
+ " .rte-rev-row-when,.rte-rev-footer-info{color:#94a3b8}",
643
+ " .rte-rev-tabs{background:#1e293b;border-color:#334155}",
644
+ " .rte-rev-tab{color:#94a3b8}.rte-rev-tab-active{color:#fff;background:#1d4ed8}",
645
+ " .rte-rev-diff{background:#0f172a;color:#cbd5e1}.rte-rev-diff-eq{color:#94a3b8}",
646
+ " .rte-rev-diff-add{background:rgba(16,185,129,.16);color:#6ee7b7}",
647
+ " .rte-rev-diff-del{background:rgba(244,63,94,.16);color:#fda4af}",
648
+ "}",
649
+ ".rte-rev-dark{color:#e2e8f0}",
650
+ ".rte-rev-dark .rte-rev-panel{background:linear-gradient(180deg,#1e293b,#0f172a);border-color:#334155}",
651
+ ".rte-rev-dark .rte-rev-header{background:rgba(30,41,59,.72);border-color:#334155}",
652
+ ".rte-rev-dark .rte-rev-title,.rte-rev-dark .rte-rev-row-who{color:#e2e8f0}",
653
+ ".rte-rev-dark .rte-rev-btn,.rte-rev-dark .rte-rev-btn-ghost{background:#0f172a;color:#cbd5e1;border-color:#334155}",
654
+ ".rte-rev-dark .rte-rev-btn:hover{background:#334155}",
655
+ ".rte-rev-dark .rte-rev-btn-primary,.rte-rev-dark .rte-rev-btn-primary:hover{background:linear-gradient(135deg,#2563eb,#1d4ed8);color:#fff;border-color:#3b82f6}",
656
+ ".rte-rev-dark .rte-rev-body{background:#0f172a}",
657
+ ".rte-rev-dark .rte-rev-list{background:rgba(15,23,42,.6);border-color:#334155}",
658
+ ".rte-rev-dark .rte-rev-empty{background:#1e293b;color:#94a3b8;border-color:#334155}",
659
+ ".rte-rev-dark .rte-rev-row:hover{background:#1e293b;border-color:#334155}",
660
+ ".rte-rev-dark .rte-rev-row-active{background:#1e3a8a;border-color:#3b82f6}",
661
+ ".rte-rev-dark .rte-rev-row-when,.rte-rev-dark .rte-rev-footer-info{color:#94a3b8}",
662
+ ".rte-rev-dark .rte-rev-tabs{background:#1e293b;border-color:#334155}",
663
+ ".rte-rev-dark .rte-rev-tab{color:#94a3b8}.rte-rev-dark .rte-rev-tab-active{color:#fff;background:#1d4ed8}",
664
+ ".rte-rev-dark .rte-rev-diff{background:#0f172a;color:#cbd5e1}.rte-rev-dark .rte-rev-diff-eq{color:#94a3b8}",
665
+ ".rte-rev-dark .rte-rev-diff-add{background:rgba(16,185,129,.16);color:#6ee7b7}",
666
+ ".rte-rev-dark .rte-rev-diff-del{background:rgba(244,63,94,.16);color:#fda4af}"
667
+ ].join("\n");
668
+ host.head.appendChild(style);
669
+ }
670
+ }