@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.
- package/README.md +360 -0
- package/admin/index.d.ts +100 -0
- package/admin/index.js +94 -0
- package/angular/component.ts +284 -0
- package/angular/index.d.ts +80 -0
- package/angular/index.ts +24 -0
- package/headless/index.d.ts +65 -0
- package/headless/index.js +272 -0
- package/index.d.ts +1412 -0
- package/integrations/shared.d.ts +138 -0
- package/integrations/shared.js +5182 -0
- package/package.json +130 -0
- package/react/index.d.ts +104 -0
- package/react/index.js +310 -0
- package/richtexteditor/CHANGELOG.md +1876 -0
- package/richtexteditor/all_plugins.js +31125 -0
- package/richtexteditor/all_plugins.min.js +78 -0
- package/richtexteditor/lang/rte-lang-af.js +193 -0
- package/richtexteditor/lang/rte-lang-ar.js +193 -0
- package/richtexteditor/lang/rte-lang-bg.js +193 -0
- package/richtexteditor/lang/rte-lang-bn.js +193 -0
- package/richtexteditor/lang/rte-lang-bs.js +193 -0
- package/richtexteditor/lang/rte-lang-ca.js +193 -0
- package/richtexteditor/lang/rte-lang-cs.js +193 -0
- package/richtexteditor/lang/rte-lang-cy.js +193 -0
- package/richtexteditor/lang/rte-lang-da.js +193 -0
- package/richtexteditor/lang/rte-lang-de.js +193 -0
- package/richtexteditor/lang/rte-lang-el.js +193 -0
- package/richtexteditor/lang/rte-lang-en.js +6 -0
- package/richtexteditor/lang/rte-lang-es.js +193 -0
- package/richtexteditor/lang/rte-lang-et.js +193 -0
- package/richtexteditor/lang/rte-lang-fa.js +193 -0
- package/richtexteditor/lang/rte-lang-fi.js +193 -0
- package/richtexteditor/lang/rte-lang-fil.js +193 -0
- package/richtexteditor/lang/rte-lang-fj.js +193 -0
- package/richtexteditor/lang/rte-lang-fr.js +193 -0
- package/richtexteditor/lang/rte-lang-ga.js +193 -0
- package/richtexteditor/lang/rte-lang-gu.js +193 -0
- package/richtexteditor/lang/rte-lang-he.js +193 -0
- package/richtexteditor/lang/rte-lang-hi.js +193 -0
- package/richtexteditor/lang/rte-lang-hr.js +193 -0
- package/richtexteditor/lang/rte-lang-ht.js +193 -0
- package/richtexteditor/lang/rte-lang-hu.js +193 -0
- package/richtexteditor/lang/rte-lang-id.js +193 -0
- package/richtexteditor/lang/rte-lang-is.js +193 -0
- package/richtexteditor/lang/rte-lang-it.js +193 -0
- package/richtexteditor/lang/rte-lang-ja.js +193 -0
- package/richtexteditor/lang/rte-lang-kn.js +193 -0
- package/richtexteditor/lang/rte-lang-ko.js +193 -0
- package/richtexteditor/lang/rte-lang-lt.js +193 -0
- package/richtexteditor/lang/rte-lang-lv.js +193 -0
- package/richtexteditor/lang/rte-lang-mg.js +193 -0
- package/richtexteditor/lang/rte-lang-mi.js +193 -0
- package/richtexteditor/lang/rte-lang-ml.js +193 -0
- package/richtexteditor/lang/rte-lang-mr.js +193 -0
- package/richtexteditor/lang/rte-lang-ms.js +193 -0
- package/richtexteditor/lang/rte-lang-mt.js +193 -0
- package/richtexteditor/lang/rte-lang-mww.js +193 -0
- package/richtexteditor/lang/rte-lang-nb.js +193 -0
- package/richtexteditor/lang/rte-lang-nl.js +193 -0
- package/richtexteditor/lang/rte-lang-otq.js +193 -0
- package/richtexteditor/lang/rte-lang-pa.js +193 -0
- package/richtexteditor/lang/rte-lang-pl.js +193 -0
- package/richtexteditor/lang/rte-lang-pt-pt.js +193 -0
- package/richtexteditor/lang/rte-lang-pt.js +193 -0
- package/richtexteditor/lang/rte-lang-ro.js +193 -0
- package/richtexteditor/lang/rte-lang-ru.js +193 -0
- package/richtexteditor/lang/rte-lang-sk.js +193 -0
- package/richtexteditor/lang/rte-lang-sl.js +193 -0
- package/richtexteditor/lang/rte-lang-sm.js +193 -0
- package/richtexteditor/lang/rte-lang-sr-Cyrl.js +193 -0
- package/richtexteditor/lang/rte-lang-sr-Latn.js +193 -0
- package/richtexteditor/lang/rte-lang-sv.js +193 -0
- package/richtexteditor/lang/rte-lang-sw.js +193 -0
- package/richtexteditor/lang/rte-lang-ta.js +193 -0
- package/richtexteditor/lang/rte-lang-te.js +193 -0
- package/richtexteditor/lang/rte-lang-th.js +193 -0
- package/richtexteditor/lang/rte-lang-tlh-Latn.js +193 -0
- package/richtexteditor/lang/rte-lang-tlh-Piqd.js +193 -0
- package/richtexteditor/lang/rte-lang-to.js +193 -0
- package/richtexteditor/lang/rte-lang-tr.js +193 -0
- package/richtexteditor/lang/rte-lang-ty.js +193 -0
- package/richtexteditor/lang/rte-lang-uk.js +193 -0
- package/richtexteditor/lang/rte-lang-ur.js +193 -0
- package/richtexteditor/lang/rte-lang-vi.js +193 -0
- package/richtexteditor/lang/rte-lang-yua.js +193 -0
- package/richtexteditor/lang/rte-lang-zh-Hans.js +193 -0
- package/richtexteditor/lang/rte-lang-zh-Hant.js +193 -0
- package/richtexteditor/lang/rte-lang.js +1 -0
- package/richtexteditor/plugins/-combine-plugin.txt +2 -0
- package/richtexteditor/plugins/-combine-plugins.bat +20 -0
- package/richtexteditor/plugins/a11yenhance.js +90 -0
- package/richtexteditor/plugins/accessibilitychecker.js +579 -0
- package/richtexteditor/plugins/aitoolkit.css +15830 -0
- package/richtexteditor/plugins/aitoolkit.js +19171 -0
- package/richtexteditor/plugins/all_plugins.js +31125 -0
- package/richtexteditor/plugins/all_plugins.min.js +78 -0
- package/richtexteditor/plugins/autoembed.js +174 -0
- package/richtexteditor/plugins/blocktypes.js +175 -0
- package/richtexteditor/plugins/bookmarkcard.js +226 -0
- package/richtexteditor/plugins/charlimit.js +195 -0
- package/richtexteditor/plugins/comments.js +672 -0
- package/richtexteditor/plugins/contentminimap.js +369 -0
- package/richtexteditor/plugins/crdt-engine.js +8905 -0
- package/richtexteditor/plugins/crdt-engine.min.js +5 -0
- package/richtexteditor/plugins/dialoga11y.js +174 -0
- package/richtexteditor/plugins/dictation.js +268 -0
- package/richtexteditor/plugins/documentimport.js +369 -0
- package/richtexteditor/plugins/documentoutline.js +341 -0
- package/richtexteditor/plugins/emailtoolkit.js +165 -0
- package/richtexteditor/plugins/emojiautocomplete.js +241 -0
- package/richtexteditor/plugins/foldheadings.js +181 -0
- package/richtexteditor/plugins/ghostcomplete.js +297 -0
- package/richtexteditor/plugins/html2pdf.js +158 -0
- package/richtexteditor/plugins/imageeditor.js +159 -0
- package/richtexteditor/plugins/insertcode.js +319 -0
- package/richtexteditor/plugins/insertemoji.js +229 -0
- package/richtexteditor/plugins/insertgallery.js +362 -0
- package/richtexteditor/plugins/inserttemplate.js +261 -0
- package/richtexteditor/plugins/localdraft.js +213 -0
- package/richtexteditor/plugins/markdownexport.js +110 -0
- package/richtexteditor/plugins/mathdialog.js +153 -0
- package/richtexteditor/plugins/mentionplugin.js +534 -0
- package/richtexteditor/plugins/mermaiddiagram.js +294 -0
- package/richtexteditor/plugins/readabilitystats.js +148 -0
- package/richtexteditor/plugins/readaloud.js +243 -0
- package/richtexteditor/plugins/restrictedediting.js +208 -0
- package/richtexteditor/plugins/revisionhistory.js +670 -0
- package/richtexteditor/plugins/samples/insertcode_v1.js +76 -0
- package/richtexteditor/plugins/slashcommand.js +705 -0
- package/richtexteditor/plugins/smartchips.js +250 -0
- package/richtexteditor/plugins/spellcheck.js +181 -0
- package/richtexteditor/plugins/todolist.js +137 -0
- package/richtexteditor/plugins/trackedchanges.js +658 -0
- package/richtexteditor/plugins/tui.image-editor/fabric.min.js +8 -0
- package/richtexteditor/plugins/tui.image-editor/note.txt +5 -0
- package/richtexteditor/plugins/tui.image-editor/svg/icon-a.svg +235 -0
- package/richtexteditor/plugins/tui.image-editor/svg/icon-b.svg +224 -0
- package/richtexteditor/plugins/tui.image-editor/svg/icon-c.svg +224 -0
- package/richtexteditor/plugins/tui.image-editor/svg/icon-d.svg +224 -0
- package/richtexteditor/plugins/tui.image-editor/tui-code-snippet.min.js +7 -0
- package/richtexteditor/plugins/tui.image-editor/tui-color-picker.min.css +6 -0
- package/richtexteditor/plugins/tui.image-editor/tui-color-picker.min.js +7 -0
- package/richtexteditor/plugins/tui.image-editor/tui-image-editor.min.css +7 -0
- package/richtexteditor/plugins/tui.image-editor/tui-image-editor.min.js +15 -0
- package/richtexteditor/plugins/typewriter.js +203 -0
- package/richtexteditor/plugins/wordexport.js +0 -0
- package/richtexteditor/plugins/yjscollab.js +693 -0
- package/richtexteditor/plugins/zz_richtextbox_dialog_style.js +335 -0
- package/richtexteditor/rte-config.js +933 -0
- package/richtexteditor/rte.js +943 -0
- package/richtexteditor/rte_mobile.css +234 -0
- package/richtexteditor/rte_theme_default.css +4819 -0
- package/richtexteditor/runtime/colorpicker_more_ns.htm +2613 -0
- package/richtexteditor/runtime/help.htm +162 -0
- package/richtexteditor/runtime/images/1x1.gif +0 -0
- package/richtexteditor/runtime/images/cpie_Color.cur +0 -0
- package/richtexteditor/runtime/images/cpie_GradientPositionDark.gif +0 -0
- package/richtexteditor/runtime/images/cpie_GradientPositionLight.gif +0 -0
- package/richtexteditor/runtime/images/cpie_VerticalPosition.gif +0 -0
- package/richtexteditor/runtime/images/cpie_WebSafe.gif +0 -0
- package/richtexteditor/runtime/images/cpie_gradients.png +0 -0
- package/richtexteditor/runtime/images/cpns_Color.cur +0 -0
- package/richtexteditor/runtime/images/cpns_ColorSpace1.png +0 -0
- package/richtexteditor/runtime/images/cpns_ColorSpace2.png +0 -0
- package/richtexteditor/runtime/images/cpns_GradientPositionDark.gif +0 -0
- package/richtexteditor/runtime/images/cpns_GradientPositionLight.gif +0 -0
- package/richtexteditor/runtime/images/cpns_Vertical1.png +0 -0
- package/richtexteditor/runtime/images/cpns_Vertical2.png +0 -0
- package/richtexteditor/runtime/images/cpns_VerticalPosition.gif +0 -0
- package/richtexteditor/runtime/images/cpns_WebSafe.gif +0 -0
- package/richtexteditor/runtime/images/cpns_gradients.png +0 -0
- package/richtexteditor/runtime/images/cpns_hue2.png +0 -0
- package/richtexteditor/runtime/images/formbn.gif +0 -0
- package/richtexteditor/runtime/images/multiclavier.gif +0 -0
- package/richtexteditor/runtime/richtexteditor_content.css +362 -0
- package/richtexteditor/runtime/richtexteditor_preview.css +302 -0
- package/richtexteditor/runtime/richtexteditor_preview.js +1 -0
- package/server/index.d.ts +21 -0
- package/server/index.js +23 -0
- package/svelte/index.d.ts +19 -0
- package/svelte/index.js +59 -0
- package/vue/index.d.ts +94 -0
- package/vue/index.js +291 -0
|
@@ -0,0 +1,672 @@
|
|
|
1
|
+
if (!window.RTE_DefaultConfig) window.RTE_DefaultConfig = {};
|
|
2
|
+
|
|
3
|
+
if (!RTE_DefaultConfig.svgCode_commentadd) {
|
|
4
|
+
RTE_DefaultConfig.svgCode_commentadd = '<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="M4 5h14a2 2 0 012 2v8a2 2 0 01-2 2h-7l-4 4v-4H4a2 2 0 01-2-2V7a2 2 0 012-2z"/><path d="M8 9h8"/><path d="M8 12h5"/></svg>';
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
RTE_DefaultConfig.plugin_comments = RTE_Plugin_Comments;
|
|
8
|
+
|
|
9
|
+
function RTE_Plugin_Comments() {
|
|
10
|
+
var obj = this;
|
|
11
|
+
var config;
|
|
12
|
+
var editor;
|
|
13
|
+
var sidebar = null;
|
|
14
|
+
var composer = null;
|
|
15
|
+
|
|
16
|
+
obj.PluginName = "Comments";
|
|
17
|
+
|
|
18
|
+
obj.InitConfig = function (argconfig) {
|
|
19
|
+
config = argconfig;
|
|
20
|
+
if (config.commentsEnabled === false) return;
|
|
21
|
+
|
|
22
|
+
config.text_commentadd = config.text_commentadd || "Comment";
|
|
23
|
+
config.commentHighlightBg = config.commentHighlightBg || "#fff9c4";
|
|
24
|
+
config.commentHighlightBorder = config.commentHighlightBorder || "#f9a825";
|
|
25
|
+
|
|
26
|
+
appendToolbarCommand("toolbar_default", "#{commentadd}");
|
|
27
|
+
appendToolbarCommand("toolbar_full", "#{commentadd}");
|
|
28
|
+
appendToolbarCommand("toolbar_mobile", "#{commentadd}");
|
|
29
|
+
if ((config.controltoolbar_TEXT || "").indexOf("commentadd") === -1) {
|
|
30
|
+
config.controltoolbar_TEXT = (config.controltoolbar_TEXT || "") + "|{commentadd}";
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
obj.InitEditor = function (argeditor) {
|
|
35
|
+
editor = argeditor;
|
|
36
|
+
if (config.commentsEnabled === false) return;
|
|
37
|
+
|
|
38
|
+
editor.comments = {
|
|
39
|
+
add: function (options) { return addComment(options); },
|
|
40
|
+
reply: function (commentId, text) { return replyToComment(commentId, text); },
|
|
41
|
+
resolve: function (commentId) { return resolveComment(commentId); },
|
|
42
|
+
delete: function (commentId) { return deleteComment(commentId); },
|
|
43
|
+
list: function (filter) { return listComments(filter); },
|
|
44
|
+
openSidebar: function () { openSidebar(); },
|
|
45
|
+
closeSidebar: function () { closeSidebar(); },
|
|
46
|
+
toggleSidebar: function () { if (sidebar && sidebar.isConnected) closeSidebar(); else openSidebar(); },
|
|
47
|
+
focusComment: function (id) { focusComment(id); }
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
injectStyles();
|
|
51
|
+
|
|
52
|
+
editor.toolbarFactoryMap = editor.toolbarFactoryMap || {};
|
|
53
|
+
editor.toolbarFactoryMap["commentadd"] = function (cmd) {
|
|
54
|
+
return editor.createToolbarButton(cmd);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
editor.attachEvent("exec_command_commentadd", function (state) {
|
|
58
|
+
state.returnValue = true;
|
|
59
|
+
state.stopBubble = true;
|
|
60
|
+
openComposerForSelection();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
editor.getEditable().addEventListener("click", function (e) {
|
|
64
|
+
var span = e.target && e.target.closest ? e.target.closest(".rte-comment") : null;
|
|
65
|
+
if (!span) return;
|
|
66
|
+
var id = span.getAttribute("data-comment-id");
|
|
67
|
+
if (id) focusComment(id);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Restore existing comments on load so their spans re-hydrate from the ledger.
|
|
71
|
+
// (Span markup is persisted in the HTML itself, but we also make sure no
|
|
72
|
+
// orphan ledger entries point at missing spans.)
|
|
73
|
+
pruneOrphanComments();
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
function appendToolbarCommand(toolbar, item) {
|
|
77
|
+
if (!config[toolbar]) return;
|
|
78
|
+
if (config[toolbar].indexOf(item) !== -1) return;
|
|
79
|
+
config[toolbar] = config[toolbar] + item;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function getCurrentUser() {
|
|
83
|
+
if (config.currentUser && config.currentUser.id) return config.currentUser;
|
|
84
|
+
return { id: "user", name: "User", color: "#2563eb" };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// The sidebar/composer are body-appended, outside the editor container, so
|
|
88
|
+
// the container's rte-dark class can't reach them via CSS. Mirror forced
|
|
89
|
+
// dark mode by adding rte-comment-dark; automatic dark uses a media query.
|
|
90
|
+
function isEditorDark() {
|
|
91
|
+
try {
|
|
92
|
+
var host = editor.container || (editor.getEditable && editor.getEditable().closest && editor.getEditable().closest(".richtexteditor"));
|
|
93
|
+
return !!(host && host.classList && host.classList.contains("rte-dark"));
|
|
94
|
+
} catch (e) { return false; }
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function listComments(filter) {
|
|
98
|
+
if (!editor.reviewLedger) return [];
|
|
99
|
+
return editor.reviewLedger.list().filter(function (e) {
|
|
100
|
+
if (e.changeType !== "comment") return false;
|
|
101
|
+
if (filter && filter.status && e.status !== filter.status) return false;
|
|
102
|
+
if (filter && filter.author && e.author.id !== filter.author) return false;
|
|
103
|
+
return true;
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function pruneOrphanComments() {
|
|
108
|
+
var editable = editor.getEditable();
|
|
109
|
+
var entries = listComments();
|
|
110
|
+
for (var i = 0; i < entries.length; i++) {
|
|
111
|
+
var id = entries[i].id;
|
|
112
|
+
if (!editable.querySelector('[data-comment-id="' + cssEscape(id) + '"]')) {
|
|
113
|
+
// Don't actually delete — just mark stale.
|
|
114
|
+
if (editor.reviewLedger && entries[i].status === "pending") {
|
|
115
|
+
editor.reviewLedger.update(id, { status: "stale" });
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// --- creating comments ---
|
|
122
|
+
|
|
123
|
+
function openComposerForSelection() {
|
|
124
|
+
var sel = editor.getSelection();
|
|
125
|
+
if (!sel || sel.rangeCount === 0 || sel.isCollapsed) {
|
|
126
|
+
// No selection — open composer anchored to caret, with empty snapshot.
|
|
127
|
+
var rangeInfo = sel && sel.rangeCount ? snapshotRange(sel.getRangeAt(0)) : null;
|
|
128
|
+
showComposer({ anchor: rangeInfo, allowEmpty: true });
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
var range = sel.getRangeAt(0);
|
|
132
|
+
showComposer({ anchor: snapshotRange(range), range: range });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function snapshotRange(range) {
|
|
136
|
+
if (!range) return null;
|
|
137
|
+
return {
|
|
138
|
+
startContainer: range.startContainer,
|
|
139
|
+
startOffset: range.startOffset,
|
|
140
|
+
endContainer: range.endContainer,
|
|
141
|
+
endOffset: range.endOffset,
|
|
142
|
+
text: range.toString()
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function showComposer(options) {
|
|
147
|
+
closeComposer();
|
|
148
|
+
options = options || {};
|
|
149
|
+
var host = editor.iframe.ownerDocument;
|
|
150
|
+
composer = host.createElement("div");
|
|
151
|
+
composer.className = "rte-comment-composer" + (isEditorDark() ? " rte-comment-dark" : "");
|
|
152
|
+
composer.setAttribute("role", "dialog");
|
|
153
|
+
composer.setAttribute("aria-label", "Add comment");
|
|
154
|
+
|
|
155
|
+
var header = host.createElement("div");
|
|
156
|
+
header.className = "rte-comment-composer-header";
|
|
157
|
+
header.textContent = "Add comment";
|
|
158
|
+
composer.appendChild(header);
|
|
159
|
+
|
|
160
|
+
var textarea = host.createElement("textarea");
|
|
161
|
+
textarea.className = "rte-comment-composer-textarea";
|
|
162
|
+
textarea.placeholder = "Type your comment...";
|
|
163
|
+
textarea.rows = 3;
|
|
164
|
+
composer.appendChild(textarea);
|
|
165
|
+
|
|
166
|
+
var actions = host.createElement("div");
|
|
167
|
+
actions.className = "rte-comment-composer-actions";
|
|
168
|
+
var cancel = host.createElement("button");
|
|
169
|
+
cancel.type = "button";
|
|
170
|
+
cancel.className = "rte-comment-btn rte-comment-btn-ghost";
|
|
171
|
+
cancel.textContent = "Cancel";
|
|
172
|
+
cancel.addEventListener("mousedown", function (e) { e.preventDefault(); closeComposer(); });
|
|
173
|
+
var submit = host.createElement("button");
|
|
174
|
+
submit.type = "button";
|
|
175
|
+
submit.className = "rte-comment-btn rte-comment-btn-primary";
|
|
176
|
+
submit.textContent = "Comment";
|
|
177
|
+
submit.addEventListener("mousedown", function (e) {
|
|
178
|
+
e.preventDefault();
|
|
179
|
+
var text = textarea.value.replace(/^\s+|\s+$/g, "");
|
|
180
|
+
if (!text) return;
|
|
181
|
+
addComment({ anchor: options.anchor, text: text });
|
|
182
|
+
closeComposer();
|
|
183
|
+
});
|
|
184
|
+
actions.appendChild(cancel);
|
|
185
|
+
actions.appendChild(submit);
|
|
186
|
+
composer.appendChild(actions);
|
|
187
|
+
|
|
188
|
+
host.body.appendChild(composer);
|
|
189
|
+
positionComposer(options.anchor);
|
|
190
|
+
setTimeout(function () { textarea.focus(); }, 0);
|
|
191
|
+
|
|
192
|
+
var escListener = function (e) {
|
|
193
|
+
if (e.key === "Escape" || e.key === "Esc") closeComposer();
|
|
194
|
+
};
|
|
195
|
+
composer.addEventListener("keydown", escListener);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function positionComposer(anchor) {
|
|
199
|
+
if (!composer) return;
|
|
200
|
+
composer.style.position = "absolute";
|
|
201
|
+
var iframe = editor.iframe;
|
|
202
|
+
var ir = iframe.getBoundingClientRect();
|
|
203
|
+
var left = ir.left + window.pageXOffset + 12;
|
|
204
|
+
var top = ir.top + window.pageYOffset + 12;
|
|
205
|
+
if (anchor && anchor.startContainer) {
|
|
206
|
+
try {
|
|
207
|
+
var r = editor.getDocument().createRange();
|
|
208
|
+
r.setStart(anchor.startContainer, anchor.startOffset);
|
|
209
|
+
r.setEnd(anchor.endContainer, anchor.endOffset);
|
|
210
|
+
var rects = r.getClientRects();
|
|
211
|
+
var rect = rects && rects.length ? rects[rects.length - 1] : r.getBoundingClientRect();
|
|
212
|
+
left = ir.left + (rect.left || 0) + window.pageXOffset;
|
|
213
|
+
top = ir.top + (rect.bottom || rect.top || 0) + window.pageYOffset + 6;
|
|
214
|
+
} catch (ignore) { }
|
|
215
|
+
}
|
|
216
|
+
composer.style.left = left + "px";
|
|
217
|
+
composer.style.top = top + "px";
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function closeComposer() {
|
|
221
|
+
if (composer && composer.parentNode) composer.parentNode.removeChild(composer);
|
|
222
|
+
composer = null;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function addComment(options) {
|
|
226
|
+
options = options || {};
|
|
227
|
+
if (!editor.reviewLedger) return null;
|
|
228
|
+
var text = (options.text || "").replace(/^\s+|\s+$/g, "");
|
|
229
|
+
if (!text) return null;
|
|
230
|
+
|
|
231
|
+
var id = "cmt-" + Date.now() + "-" + Math.floor(Math.random() * 10000);
|
|
232
|
+
var user = getCurrentUser();
|
|
233
|
+
var anchor = options.anchor;
|
|
234
|
+
var wrappedText = "";
|
|
235
|
+
|
|
236
|
+
if (anchor && anchor.startContainer && anchor.endContainer && anchor.text) {
|
|
237
|
+
var editdoc = editor.getDocument();
|
|
238
|
+
try {
|
|
239
|
+
var wrapRange = editdoc.createRange();
|
|
240
|
+
wrapRange.setStart(anchor.startContainer, anchor.startOffset);
|
|
241
|
+
wrapRange.setEnd(anchor.endContainer, anchor.endOffset);
|
|
242
|
+
var span = editdoc.createElement("span");
|
|
243
|
+
span.className = "rte-comment";
|
|
244
|
+
span.setAttribute("data-comment-id", id);
|
|
245
|
+
span.setAttribute("data-comment-author", user.id);
|
|
246
|
+
var fragment = wrapRange.extractContents();
|
|
247
|
+
span.appendChild(fragment);
|
|
248
|
+
wrapRange.insertNode(span);
|
|
249
|
+
wrappedText = span.textContent;
|
|
250
|
+
// Collapse caret after the span.
|
|
251
|
+
var after = editdoc.createRange();
|
|
252
|
+
after.setStartAfter(span);
|
|
253
|
+
after.collapse(true);
|
|
254
|
+
var sel = editor.getSelection();
|
|
255
|
+
sel.removeAllRanges();
|
|
256
|
+
sel.addRange(after);
|
|
257
|
+
} catch (err) {
|
|
258
|
+
console.warn("comments: range wrap failed:", err);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
editor.reviewLedger.add({
|
|
263
|
+
id: id,
|
|
264
|
+
changeType: "comment",
|
|
265
|
+
author: user,
|
|
266
|
+
text: text,
|
|
267
|
+
originalText: wrappedText || (anchor && anchor.text) || "",
|
|
268
|
+
status: "pending",
|
|
269
|
+
sourceLabel: "Comment",
|
|
270
|
+
createdAt: Date.now(),
|
|
271
|
+
replies: []
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
if (sidebar && sidebar.isConnected) renderSidebar();
|
|
275
|
+
else openSidebar();
|
|
276
|
+
focusComment(id);
|
|
277
|
+
try { if (typeof editor.fireEvent === "function") editor.fireEvent("comment_added", { id: id, text: text, author: user }); } catch (e) { }
|
|
278
|
+
return id;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function replyToComment(commentId, text) {
|
|
282
|
+
if (!editor.reviewLedger) return false;
|
|
283
|
+
var entry = editor.reviewLedger.get(commentId);
|
|
284
|
+
if (!entry) return false;
|
|
285
|
+
var replies = Array.isArray(entry.replies) ? entry.replies.slice() : [];
|
|
286
|
+
replies.push({
|
|
287
|
+
id: "rpl-" + Date.now() + "-" + Math.floor(Math.random() * 10000),
|
|
288
|
+
author: getCurrentUser(),
|
|
289
|
+
text: text,
|
|
290
|
+
createdAt: Date.now()
|
|
291
|
+
});
|
|
292
|
+
editor.reviewLedger.update(commentId, { replies: replies });
|
|
293
|
+
if (sidebar && sidebar.isConnected) renderSidebar();
|
|
294
|
+
return true;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function resolveComment(commentId) {
|
|
298
|
+
if (!editor.reviewLedger) return false;
|
|
299
|
+
var span = editor.getEditable().querySelector('[data-comment-id="' + cssEscape(commentId) + '"]');
|
|
300
|
+
if (span) unwrapKeepChildren(span);
|
|
301
|
+
editor.reviewLedger.update(commentId, { status: "resolved", decidedAt: Date.now() });
|
|
302
|
+
if (sidebar && sidebar.isConnected) renderSidebar();
|
|
303
|
+
try { if (typeof editor.fireEvent === "function") editor.fireEvent("comment_resolved", { id: commentId }); } catch (e) { }
|
|
304
|
+
return true;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function deleteComment(commentId) {
|
|
308
|
+
if (!editor.reviewLedger) return false;
|
|
309
|
+
var span = editor.getEditable().querySelector('[data-comment-id="' + cssEscape(commentId) + '"]');
|
|
310
|
+
if (span) unwrapKeepChildren(span);
|
|
311
|
+
editor.reviewLedger.remove(commentId);
|
|
312
|
+
if (sidebar && sidebar.isConnected) renderSidebar();
|
|
313
|
+
return true;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function focusComment(commentId) {
|
|
317
|
+
if (!sidebar || !sidebar.isConnected) openSidebar();
|
|
318
|
+
setTimeout(function () {
|
|
319
|
+
if (!sidebar) return;
|
|
320
|
+
var item = sidebar.querySelector('[data-comment-ref="' + cssEscape(commentId) + '"]');
|
|
321
|
+
if (item) {
|
|
322
|
+
item.scrollIntoView({ block: "nearest" });
|
|
323
|
+
item.classList.add("rte-comment-item-active");
|
|
324
|
+
setTimeout(function () { item.classList.remove("rte-comment-item-active"); }, 1400);
|
|
325
|
+
}
|
|
326
|
+
}, 30);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function unwrapKeepChildren(el) {
|
|
330
|
+
var parent = el.parentNode;
|
|
331
|
+
if (!parent) return;
|
|
332
|
+
while (el.firstChild) parent.insertBefore(el.firstChild, el);
|
|
333
|
+
parent.removeChild(el);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// 2026-05-28 Prefer native CSS.escape when available — handles every code
|
|
337
|
+
// point that would break an attribute selector (], ', U+0000, U+0001..0x1F,
|
|
338
|
+
// leading digits, etc.). Comment IDs are auto-generated today so the legacy
|
|
339
|
+
// single-char shim worked, but defense-in-depth pays off if a customer
|
|
340
|
+
// ever wires reviewLedger.add() with custom IDs containing punctuation.
|
|
341
|
+
function cssEscape(s) {
|
|
342
|
+
var str = String(s);
|
|
343
|
+
if (typeof CSS !== "undefined" && typeof CSS.escape === "function") {
|
|
344
|
+
try { return CSS.escape(str); } catch (e) { /* fall through */ }
|
|
345
|
+
}
|
|
346
|
+
// Fallback: escape every char that could break an attribute selector.
|
|
347
|
+
return str.replace(/["\\\]\[\(\)\{\}'#:;,<>=`~!@$%^&*+/?\s\x00-\x1F]/g, function (c) {
|
|
348
|
+
return "\\" + c;
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// --- sidebar ---
|
|
353
|
+
|
|
354
|
+
function openSidebar() {
|
|
355
|
+
if (sidebar && sidebar.isConnected) { renderSidebar(); return; }
|
|
356
|
+
var host = editor.iframe.ownerDocument;
|
|
357
|
+
sidebar = host.createElement("div");
|
|
358
|
+
sidebar.className = "rte-comment-sidebar" + (isEditorDark() ? " rte-comment-dark" : "");
|
|
359
|
+
sidebar.setAttribute("role", "complementary");
|
|
360
|
+
sidebar.setAttribute("aria-label", "Comments");
|
|
361
|
+
var shell = getEditorShell();
|
|
362
|
+
if (shell) {
|
|
363
|
+
shell.parentNode.insertBefore(sidebar, shell.nextSibling);
|
|
364
|
+
shell.classList.add("rte-comment-sidebar-host");
|
|
365
|
+
} else {
|
|
366
|
+
host.body.appendChild(sidebar);
|
|
367
|
+
}
|
|
368
|
+
renderSidebar();
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function closeSidebar() {
|
|
372
|
+
if (sidebar && sidebar.parentNode) sidebar.parentNode.removeChild(sidebar);
|
|
373
|
+
sidebar = null;
|
|
374
|
+
var shell = getEditorShell();
|
|
375
|
+
if (shell) shell.classList.remove("rte-comment-sidebar-host");
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function getEditorShell() {
|
|
379
|
+
var el = editor.iframe;
|
|
380
|
+
while (el && el !== document.body) {
|
|
381
|
+
if (el.classList && el.classList.contains("richtexteditor")) return el;
|
|
382
|
+
el = el.parentNode;
|
|
383
|
+
}
|
|
384
|
+
return null;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function renderSidebar() {
|
|
388
|
+
if (!sidebar) return;
|
|
389
|
+
var host = sidebar.ownerDocument;
|
|
390
|
+
sidebar.innerHTML = "";
|
|
391
|
+
|
|
392
|
+
var header = host.createElement("div");
|
|
393
|
+
header.className = "rte-comment-sidebar-header";
|
|
394
|
+
var title = host.createElement("div");
|
|
395
|
+
title.className = "rte-comment-sidebar-title";
|
|
396
|
+
title.textContent = "Comments";
|
|
397
|
+
header.appendChild(title);
|
|
398
|
+
var close = host.createElement("button");
|
|
399
|
+
close.type = "button";
|
|
400
|
+
close.className = "rte-comment-sidebar-close";
|
|
401
|
+
close.setAttribute("aria-label", "Close");
|
|
402
|
+
close.innerHTML = '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 6l12 12"/><path d="M18 6L6 18"/></svg>';
|
|
403
|
+
close.addEventListener("mousedown", function (e) { e.preventDefault(); closeSidebar(); });
|
|
404
|
+
header.appendChild(close);
|
|
405
|
+
sidebar.appendChild(header);
|
|
406
|
+
|
|
407
|
+
var list = host.createElement("div");
|
|
408
|
+
list.className = "rte-comment-list";
|
|
409
|
+
sidebar.appendChild(list);
|
|
410
|
+
|
|
411
|
+
var entries = listComments({ status: "pending" });
|
|
412
|
+
if (!entries.length) {
|
|
413
|
+
var empty = host.createElement("div");
|
|
414
|
+
empty.className = "rte-comment-empty";
|
|
415
|
+
empty.textContent = "No open comments. Select text and click the Comment button to start.";
|
|
416
|
+
list.appendChild(empty);
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
entries.sort(function (a, b) { return (b.createdAt || 0) - (a.createdAt || 0); });
|
|
421
|
+
|
|
422
|
+
for (var i = 0; i < entries.length; i++) {
|
|
423
|
+
list.appendChild(renderEntry(entries[i], host));
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
function renderEntry(entry, host) {
|
|
428
|
+
var wrap = host.createElement("div");
|
|
429
|
+
wrap.className = "rte-comment-item";
|
|
430
|
+
wrap.setAttribute("data-comment-ref", entry.id);
|
|
431
|
+
wrap.setAttribute("role", "article");
|
|
432
|
+
|
|
433
|
+
var topRow = host.createElement("div");
|
|
434
|
+
topRow.className = "rte-comment-item-top";
|
|
435
|
+
var avatar = host.createElement("span");
|
|
436
|
+
avatar.className = "rte-comment-avatar";
|
|
437
|
+
avatar.style.background = entry.author.color || "#2563eb";
|
|
438
|
+
avatar.textContent = initialsOf(entry.author.name || entry.author.id || "?");
|
|
439
|
+
var nameWrap = host.createElement("div");
|
|
440
|
+
nameWrap.className = "rte-comment-namewrap";
|
|
441
|
+
var name = host.createElement("div");
|
|
442
|
+
name.className = "rte-comment-name";
|
|
443
|
+
name.textContent = entry.author.name || entry.author.id || "";
|
|
444
|
+
var when = host.createElement("div");
|
|
445
|
+
when.className = "rte-comment-when";
|
|
446
|
+
when.textContent = relativeTime(entry.createdAt);
|
|
447
|
+
nameWrap.appendChild(name);
|
|
448
|
+
nameWrap.appendChild(when);
|
|
449
|
+
topRow.appendChild(avatar);
|
|
450
|
+
topRow.appendChild(nameWrap);
|
|
451
|
+
|
|
452
|
+
var actions = host.createElement("div");
|
|
453
|
+
actions.className = "rte-comment-actions";
|
|
454
|
+
var resolveBtn = host.createElement("button");
|
|
455
|
+
resolveBtn.type = "button";
|
|
456
|
+
resolveBtn.className = "rte-comment-action rte-comment-resolve";
|
|
457
|
+
resolveBtn.textContent = "Resolve";
|
|
458
|
+
resolveBtn.setAttribute("aria-label", "Resolve comment");
|
|
459
|
+
resolveBtn.addEventListener("mousedown", function (e) { e.preventDefault(); resolveComment(entry.id); });
|
|
460
|
+
var delBtn = host.createElement("button");
|
|
461
|
+
delBtn.type = "button";
|
|
462
|
+
delBtn.className = "rte-comment-action rte-comment-delete";
|
|
463
|
+
delBtn.textContent = "Delete";
|
|
464
|
+
delBtn.setAttribute("aria-label", "Delete comment");
|
|
465
|
+
delBtn.addEventListener("mousedown", function (e) { e.preventDefault(); deleteComment(entry.id); });
|
|
466
|
+
actions.appendChild(resolveBtn);
|
|
467
|
+
actions.appendChild(delBtn);
|
|
468
|
+
topRow.appendChild(actions);
|
|
469
|
+
|
|
470
|
+
wrap.appendChild(topRow);
|
|
471
|
+
|
|
472
|
+
if (entry.originalText) {
|
|
473
|
+
var quote = host.createElement("div");
|
|
474
|
+
quote.className = "rte-comment-quote";
|
|
475
|
+
quote.textContent = truncate(entry.originalText, 140);
|
|
476
|
+
quote.addEventListener("click", function () { scrollToSpan(entry.id); });
|
|
477
|
+
wrap.appendChild(quote);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
var body = host.createElement("div");
|
|
481
|
+
body.className = "rte-comment-body";
|
|
482
|
+
body.textContent = entry.text;
|
|
483
|
+
wrap.appendChild(body);
|
|
484
|
+
|
|
485
|
+
var replies = Array.isArray(entry.replies) ? entry.replies : [];
|
|
486
|
+
for (var i = 0; i < replies.length; i++) {
|
|
487
|
+
var rep = replies[i];
|
|
488
|
+
var repEl = host.createElement("div");
|
|
489
|
+
repEl.className = "rte-comment-reply";
|
|
490
|
+
var repAv = host.createElement("span");
|
|
491
|
+
repAv.className = "rte-comment-reply-avatar";
|
|
492
|
+
repAv.style.background = (rep.author && rep.author.color) || "#64748b";
|
|
493
|
+
repAv.textContent = initialsOf((rep.author && rep.author.name) || "?");
|
|
494
|
+
var repBody = host.createElement("div");
|
|
495
|
+
repBody.className = "rte-comment-reply-body";
|
|
496
|
+
var repName = host.createElement("div");
|
|
497
|
+
repName.className = "rte-comment-reply-name";
|
|
498
|
+
repName.textContent = ((rep.author && rep.author.name) || "User") + " \u00B7 " + relativeTime(rep.createdAt);
|
|
499
|
+
var repText = host.createElement("div");
|
|
500
|
+
repText.className = "rte-comment-reply-text";
|
|
501
|
+
repText.textContent = rep.text;
|
|
502
|
+
repBody.appendChild(repName);
|
|
503
|
+
repBody.appendChild(repText);
|
|
504
|
+
repEl.appendChild(repAv);
|
|
505
|
+
repEl.appendChild(repBody);
|
|
506
|
+
wrap.appendChild(repEl);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// Reply composer
|
|
510
|
+
var replyRow = host.createElement("div");
|
|
511
|
+
replyRow.className = "rte-comment-reply-composer";
|
|
512
|
+
var replyInput = host.createElement("input");
|
|
513
|
+
replyInput.type = "text";
|
|
514
|
+
replyInput.className = "rte-comment-reply-input";
|
|
515
|
+
replyInput.placeholder = "Reply...";
|
|
516
|
+
replyInput.setAttribute("aria-label", "Reply to comment");
|
|
517
|
+
replyInput.addEventListener("keydown", function (e) {
|
|
518
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
519
|
+
e.preventDefault();
|
|
520
|
+
var t = replyInput.value.replace(/^\s+|\s+$/g, "");
|
|
521
|
+
if (t) replyToComment(entry.id, t);
|
|
522
|
+
replyInput.value = "";
|
|
523
|
+
}
|
|
524
|
+
});
|
|
525
|
+
replyRow.appendChild(replyInput);
|
|
526
|
+
wrap.appendChild(replyRow);
|
|
527
|
+
|
|
528
|
+
return wrap;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
function scrollToSpan(commentId) {
|
|
532
|
+
var span = editor.getEditable().querySelector('[data-comment-id="' + cssEscape(commentId) + '"]');
|
|
533
|
+
if (!span) return;
|
|
534
|
+
span.scrollIntoView({ block: "center", behavior: "smooth" });
|
|
535
|
+
span.classList.add("rte-comment-flash");
|
|
536
|
+
setTimeout(function () { span.classList.remove("rte-comment-flash"); }, 1200);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
function initialsOf(label) {
|
|
540
|
+
var parts = String(label).trim().split(/\s+/);
|
|
541
|
+
if (!parts[0]) return "?";
|
|
542
|
+
if (parts.length === 1) return parts[0].charAt(0).toUpperCase();
|
|
543
|
+
return (parts[0].charAt(0) + parts[parts.length - 1].charAt(0)).toUpperCase();
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
function relativeTime(ts) {
|
|
547
|
+
if (!ts) return "";
|
|
548
|
+
var diff = Math.floor((Date.now() - ts) / 1000);
|
|
549
|
+
if (diff < 60) return "just now";
|
|
550
|
+
if (diff < 3600) return Math.floor(diff / 60) + "m ago";
|
|
551
|
+
if (diff < 86400) return Math.floor(diff / 3600) + "h ago";
|
|
552
|
+
return Math.floor(diff / 86400) + "d ago";
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
function truncate(s, n) {
|
|
556
|
+
if (s.length <= n) return s;
|
|
557
|
+
return s.slice(0, n - 1) + "\u2026";
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
function injectStyles() {
|
|
561
|
+
var host = (editor && editor.iframe && editor.iframe.ownerDocument) || document;
|
|
562
|
+
if (!host.querySelector("style[data-rte-comments]")) {
|
|
563
|
+
var style = host.createElement("style");
|
|
564
|
+
style.setAttribute("data-rte-comments", "1");
|
|
565
|
+
style.textContent = [
|
|
566
|
+
".rte-comment-sidebar-host{display:flex!important;align-items:stretch}",
|
|
567
|
+
".rte-comment-sidebar{width:310px;min-width:270px;max-width:min(360px,32vw);margin-left:12px;padding:0;background:linear-gradient(180deg,rgba(255,255,255,.98),rgba(248,251,255,.96));border:1px solid rgba(148,163,184,.22);border-radius:16px;font-family:Aptos,'Segoe UI',sans-serif;font-size:13px;color:#172033;display:flex;flex-direction:column;max-height:calc(100vh - 120px);overflow:hidden;box-shadow:0 18px 42px rgba(29,78,216,.12),0 2px 8px rgba(15,23,42,.06)}",
|
|
568
|
+
".rte-comment-sidebar-header{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:10px 10px 9px 14px;border-bottom:1px solid rgba(148,163,184,.16);font-weight:750;background:rgba(255,255,255,.72)}",
|
|
569
|
+
".rte-comment-sidebar-title{font-size:13px;font-weight:850;letter-spacing:.02em}",
|
|
570
|
+
".rte-comment-sidebar-close{width:28px;height:28px;border:1px solid rgba(100,116,139,.18);background:#fff;cursor:pointer;color:#64748b;padding:0;border-radius:999px;display:inline-flex;align-items:center;justify-content:center}",
|
|
571
|
+
".rte-comment-sidebar-close:hover,.rte-comment-sidebar-close:focus-visible{background:#eef4ff;color:#0f3f9f;border-color:rgba(37,99,235,.28)}",
|
|
572
|
+
".rte-comment-list{flex:1;min-height:0;overflow-y:auto;padding:6px;scrollbar-width:thin}",
|
|
573
|
+
".rte-comment-empty{margin:6px;padding:14px;color:#52657e;font-size:12px;font-weight:700;line-height:1.45;text-align:center;background:#fff;border:1px dashed rgba(148,163,184,.28);border-radius:13px}",
|
|
574
|
+
".rte-comment-item{padding:10px;border:1px solid rgba(148,163,184,.14);border-radius:14px;background:rgba(255,255,255,.82);box-shadow:0 8px 18px rgba(15,23,42,.04);transition:background 200ms ease,box-shadow 200ms ease,transform 200ms ease}",
|
|
575
|
+
".rte-comment-item+.rte-comment-item{margin-top:7px}",
|
|
576
|
+
".rte-comment-item-active{background:#eef4ff;box-shadow:0 12px 28px rgba(37,99,235,.14);transform:translateY(-1px)}",
|
|
577
|
+
".rte-comment-item-top{display:grid;grid-template-columns:30px minmax(0,1fr) auto;align-items:center;gap:8px}",
|
|
578
|
+
".rte-comment-avatar{width:30px;height:30px;border-radius:11px;display:inline-flex;align-items:center;justify-content:center;color:#fff;font-size:11px;font-weight:800;flex:0 0 30px;box-shadow:inset 0 0 0 1px rgba(255,255,255,.28)}",
|
|
579
|
+
".rte-comment-namewrap{flex:1;min-width:0}",
|
|
580
|
+
".rte-comment-name{font-weight:800;font-size:12px;line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}",
|
|
581
|
+
".rte-comment-when{font-size:11px;color:#64748b}",
|
|
582
|
+
".rte-comment-actions{display:flex;gap:4px;align-items:center}",
|
|
583
|
+
".rte-comment-action{font-size:11px;font-weight:750;padding:4px 8px;border-radius:999px;border:1px solid rgba(100,116,139,.16);background:#fff;cursor:pointer}",
|
|
584
|
+
".rte-comment-action:hover,.rte-comment-action:focus-visible{transform:translateY(-1px)}",
|
|
585
|
+
".rte-comment-resolve{color:#0f3f9f;border-color:rgba(37,99,235,.2);background:#eef4ff}",
|
|
586
|
+
".rte-comment-delete{color:#9f1239;border-color:rgba(159,18,57,.18);background:#fff1f2}",
|
|
587
|
+
".rte-comment-quote{margin:8px 0 0;padding:7px 9px;background:#fff8e8;border:1px solid rgba(245,158,11,.18);border-left:3px solid #f59e0b;border-radius:11px;font-size:11px;color:#67430a;cursor:pointer;line-height:1.35}",
|
|
588
|
+
".rte-comment-quote:hover{background:#fff3cf}",
|
|
589
|
+
".rte-comment-body{margin-top:8px;font-size:13px;line-height:1.45;color:#172033;white-space:pre-wrap}",
|
|
590
|
+
".rte-comment-reply{display:flex;gap:8px;margin-top:10px;padding-top:9px;border-top:1px dashed rgba(148,163,184,.22)}",
|
|
591
|
+
".rte-comment-reply-avatar{width:24px;height:24px;border-radius:9px;display:inline-flex;align-items:center;justify-content:center;color:#fff;font-size:10px;font-weight:800;flex:0 0 24px}",
|
|
592
|
+
".rte-comment-reply-body{flex:1;min-width:0}",
|
|
593
|
+
".rte-comment-reply-name{font-size:11px;color:#64748b}",
|
|
594
|
+
".rte-comment-reply-text{font-size:12px;color:#172033;line-height:1.45;white-space:pre-wrap;margin-top:2px}",
|
|
595
|
+
".rte-comment-reply-composer{margin-top:8px;display:flex}",
|
|
596
|
+
".rte-comment-reply-input{flex:1;min-width:0;padding:7px 10px;border-radius:999px;border:1px solid rgba(148,163,184,.24);font:inherit;font-size:12px;background:#fff}",
|
|
597
|
+
".rte-comment-reply-input:focus,.rte-comment-composer-textarea:focus{outline:2px solid rgba(37,99,235,.18);border-color:rgba(37,99,235,.38)}",
|
|
598
|
+
".rte-comment-composer{position:absolute;z-index:2147483000;width:min(320px,calc(100vw - 24px));background:linear-gradient(180deg,#fff,#f8fbff);border:1px solid rgba(148,163,184,.22);box-shadow:0 18px 42px rgba(29,78,216,.16),0 2px 8px rgba(15,23,42,.08);border-radius:16px;padding:12px;font-family:Aptos,'Segoe UI',sans-serif;color:#172033}",
|
|
599
|
+
".rte-comment-composer-header{font-weight:850;font-size:13px;margin-bottom:8px;color:#172033}",
|
|
600
|
+
".rte-comment-composer-textarea{width:100%;box-sizing:border-box;padding:9px 10px;border-radius:12px;border:1px solid rgba(148,163,184,.24);font:inherit;font-size:13px;resize:vertical;min-height:70px;background:#fff}",
|
|
601
|
+
".rte-comment-composer-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:9px}",
|
|
602
|
+
".rte-comment-btn{min-height:30px;padding:0 13px;border-radius:999px;border:1px solid rgba(100,116,139,.18);font:750 12px/1 Aptos,'Segoe UI',sans-serif;cursor:pointer}",
|
|
603
|
+
".rte-comment-btn-ghost{background:#fff;color:#52657e}",
|
|
604
|
+
".rte-comment-btn-primary{background:#1d67ba;color:#fff;border-color:#1d67ba;box-shadow:0 8px 16px rgba(29,103,186,.18)}",
|
|
605
|
+
// Dark mode — the sidebar/composer are body-appended (outside the
|
|
606
|
+
// editor container) so the .rte-dark class can't reach them. Apply
|
|
607
|
+
// automatically via prefers-color-scheme, and via an explicit
|
|
608
|
+
// .rte-comment-dark class set when the editor is forced dark.
|
|
609
|
+
"@media (prefers-color-scheme:dark){",
|
|
610
|
+
" .rte-comment-sidebar,.rte-comment-composer{background:linear-gradient(180deg,#1e293b,#0f172a);border-color:#334155;color:#e2e8f0}",
|
|
611
|
+
" .rte-comment-sidebar-header{background:rgba(30,41,59,.72);border-color:#334155}",
|
|
612
|
+
" .rte-comment-item{background:rgba(30,41,59,.85);border-color:#334155}",
|
|
613
|
+
" .rte-comment-item-active{background:#1e3a8a}",
|
|
614
|
+
" .rte-comment-empty{background:#0f172a;color:#94a3b8;border-color:#334155}",
|
|
615
|
+
" .rte-comment-when,.rte-comment-reply-name{color:#94a3b8}",
|
|
616
|
+
" .rte-comment-body,.rte-comment-reply-text,.rte-comment-composer-header{color:#e2e8f0}",
|
|
617
|
+
" .rte-comment-reply-input,.rte-comment-composer-textarea{background:#0f172a;color:#e2e8f0;border-color:#334155}",
|
|
618
|
+
" .rte-comment-action,.rte-comment-btn-ghost,.rte-comment-sidebar-close{background:#0f172a;color:#cbd5e1;border-color:#334155}",
|
|
619
|
+
" .rte-comment-quote{background:rgba(245,158,11,.12);color:#fcd34d}",
|
|
620
|
+
"}",
|
|
621
|
+
".rte-comment-dark.rte-comment-sidebar,.rte-comment-dark.rte-comment-composer{background:linear-gradient(180deg,#1e293b,#0f172a);border-color:#334155;color:#e2e8f0}",
|
|
622
|
+
".rte-comment-dark .rte-comment-sidebar-header{background:rgba(30,41,59,.72);border-color:#334155}",
|
|
623
|
+
".rte-comment-dark .rte-comment-item{background:rgba(30,41,59,.85);border-color:#334155}",
|
|
624
|
+
".rte-comment-dark .rte-comment-item-active{background:#1e3a8a}",
|
|
625
|
+
".rte-comment-dark .rte-comment-empty{background:#0f172a;color:#94a3b8;border-color:#334155}",
|
|
626
|
+
".rte-comment-dark .rte-comment-when,.rte-comment-dark .rte-comment-reply-name{color:#94a3b8}",
|
|
627
|
+
".rte-comment-dark .rte-comment-body,.rte-comment-dark .rte-comment-reply-text,.rte-comment-dark .rte-comment-composer-header{color:#e2e8f0}",
|
|
628
|
+
".rte-comment-dark .rte-comment-reply-input,.rte-comment-dark .rte-comment-composer-textarea{background:#0f172a;color:#e2e8f0;border-color:#334155}",
|
|
629
|
+
".rte-comment-dark .rte-comment-action,.rte-comment-dark .rte-comment-btn-ghost,.rte-comment-dark .rte-comment-sidebar-close{background:#0f172a;color:#cbd5e1;border-color:#334155}",
|
|
630
|
+
".rte-comment-dark .rte-comment-quote{background:rgba(245,158,11,.12);color:#fcd34d}"
|
|
631
|
+
].join("\n");
|
|
632
|
+
host.head.appendChild(style);
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
var editdoc = editor.getDocument();
|
|
636
|
+
if (editdoc && editdoc.head && !editdoc.querySelector("style[data-rte-comments-inline]")) {
|
|
637
|
+
var iStyle = editdoc.createElement("style");
|
|
638
|
+
iStyle.setAttribute("data-rte-comments-inline", "1");
|
|
639
|
+
iStyle.textContent = [
|
|
640
|
+
".rte-comment{background:" + config.commentHighlightBg + ";border-bottom:2px solid " + config.commentHighlightBorder + ";cursor:pointer;padding:0 1px;border-radius:2px}",
|
|
641
|
+
".rte-comment:hover{background:rgba(253,230,138,.6)}",
|
|
642
|
+
".rte-comment-flash{animation:rte-comment-flash 1.2s ease}",
|
|
643
|
+
"@keyframes rte-comment-flash{0%{background:rgba(251,191,36,.9)}100%{background:" + config.commentHighlightBg + "}}"
|
|
644
|
+
].join("\n");
|
|
645
|
+
editdoc.head.appendChild(iStyle);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// Block-anchored comments: img, table, figure, video, audio, iframe, embed, object, hr, svg.
|
|
650
|
+
function resolveCommentTarget(range, editdoc) {
|
|
651
|
+
if (!range) return null;
|
|
652
|
+
var container = range.commonAncestorContainer;
|
|
653
|
+
var el = container && container.nodeType === 1 ? container : (container && container.parentNode);
|
|
654
|
+
if (!el) return null;
|
|
655
|
+
// If selection collapses on/inside an embedded block, anchor to that element instead of a text range.
|
|
656
|
+
var block = el.closest && el.closest("img,table,figure,video,audio,iframe,embed,object,hr,svg");
|
|
657
|
+
if (!block && el.querySelector) {
|
|
658
|
+
block = el.querySelector("img,table,figure,video,audio,iframe,embed,object,hr,svg");
|
|
659
|
+
}
|
|
660
|
+
if (block) {
|
|
661
|
+
block.setAttribute("data-rte-comment-anchor", "element");
|
|
662
|
+
return { kind: "element", element: block };
|
|
663
|
+
}
|
|
664
|
+
return { kind: "range", range: range };
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
function isElementAnchoredComment(el) {
|
|
668
|
+
if (!el || el.nodeType !== 1 || !el.getAttribute) return false;
|
|
669
|
+
if (el.getAttribute("data-rte-comment-anchor") === "element") return true;
|
|
670
|
+
return false;
|
|
671
|
+
}
|
|
672
|
+
}
|