nbugs commited on
Commit
da066c6
·
verified ·
1 Parent(s): 85c4208

Create editor.js

Browse files
Files changed (1) hide show
  1. editor.js +237 -0
editor.js ADDED
@@ -0,0 +1,237 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (function () {
2
+ "use strict";
3
+ const MARKDOWN_IT_CDN =
4
+ "https://cdn.jsdelivr.net/npm/[email protected]/dist/markdown-it.min.js";
5
+ const HIGHLIGHT_JS_CDN =
6
+ "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/highlight.min.js";
7
+ const HIGHLIGHT_LANG_GO_CDN =
8
+ "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/languages/go.min.js";
9
+ const HIGHLIGHT_LANG_RUST_CDN =
10
+ "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/languages/rust.min.js";
11
+ const HIGHLIGHT_LANG_TS_CDN =
12
+ "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/languages/typescript.min.js";
13
+ const EDITOR_BUTTON_ID = "custom-markdown-edit-btn";
14
+ const MODAL_ID = "markdown-editor-modal";
15
+ const CHAT_INPUT_SELECTOR = "#chat-input";
16
+ const VOICE_BUTTON_SELECTOR = "#voice-input-button";
17
+ let md;
18
+ let hljs;
19
+ let editorModal = null;
20
+ let editorInput = null;
21
+ let markdownPreview = null;
22
+ let mainChatInput = null;
23
+ let mouseDownTarget = null;
24
+ function loadScript(src) {
25
+ return new Promise((resolve, reject) => {
26
+ if (document.querySelector(`script[src="${src}"]`)) {
27
+ resolve();
28
+ return;
29
+ }
30
+ const script = document.createElement("script");
31
+ script.src = src;
32
+ script.onload = resolve;
33
+ script.onerror = reject;
34
+ document.head.appendChild(script);
35
+ });
36
+ }
37
+ async function ensureDependencies() {
38
+ try {
39
+ if (typeof window.markdownit === "undefined") {
40
+ await loadScript(MARKDOWN_IT_CDN);
41
+ }
42
+ if (typeof window.hljs === "undefined") {
43
+ await loadScript(HIGHLIGHT_JS_CDN);
44
+ await Promise.all([
45
+ loadScript(HIGHLIGHT_LANG_GO_CDN),
46
+ loadScript(HIGHLIGHT_LANG_RUST_CDN),
47
+ loadScript(HIGHLIGHT_LANG_TS_CDN),
48
+ ]).catch((e) =>
49
+ console.warn("Could not load optional highlight.js languages:", e)
50
+ );
51
+ }
52
+ md = window.markdownit({
53
+ html: true,
54
+ linkify: true,
55
+ typographer: true,
56
+ highlight: function (str, lang) {
57
+ if (window.hljs) {
58
+ hljs = window.hljs;
59
+ if (lang && hljs.getLanguage(lang)) {
60
+ try {
61
+ return (
62
+ '<pre class="hljs"><code>' +
63
+ hljs.highlight(str, { language: lang, ignoreIllegals: true })
64
+ .value +
65
+ "</code></pre>"
66
+ );
67
+ } catch (__) {}
68
+ }
69
+ try {
70
+ return (
71
+ '<pre class="hljs"><code>' +
72
+ hljs.highlightAuto(str).value +
73
+ "</code></pre>"
74
+ );
75
+ } catch (__) {}
76
+ }
77
+ return (
78
+ '<pre class="hljs"><code>' +
79
+ md.utils.escapeHtml(str) +
80
+ "</code></pre>"
81
+ );
82
+ },
83
+ });
84
+ hljs = window.hljs;
85
+ console.log("Markdown Editor Dependencies loaded.");
86
+ } catch (error) {
87
+ console.error("Failed to load Markdown editor dependencies:", error);
88
+ }
89
+ }
90
+ function createModal() {
91
+ if (document.getElementById(MODAL_ID)) {
92
+ return document.getElementById(MODAL_ID);
93
+ }
94
+ const modalHTML = `<div class="editor-container"><div class="editor-header"><div class="editor-title">Markdown编辑器</div><button class="close-btn"title="关闭 (Esc)">&times;</button></div><div class="editor-body"><textarea class="editor-input"placeholder="在这里输入 Markdown 内容..."></textarea><div class="preview-pane"><div class="markdown-preview"></div></div></div><div class="editor-footer"><button class="apply-btn"title="应用更改 (Ctrl+Enter)">应用</button></div></div>`;
95
+ const modalDiv = document.createElement("div");
96
+ modalDiv.id = MODAL_ID;
97
+ modalDiv.innerHTML = modalHTML;
98
+ document.body.appendChild(modalDiv);
99
+ editorModal = modalDiv;
100
+ editorInput = modalDiv.querySelector(".editor-input");
101
+ markdownPreview = modalDiv.querySelector(".markdown-preview");
102
+ const closeBtn = modalDiv.querySelector(".close-btn");
103
+ const applyBtn = modalDiv.querySelector(".apply-btn");
104
+ closeBtn.addEventListener("click", closeModal);
105
+ applyBtn.addEventListener("click", applyChanges);
106
+ editorInput.addEventListener("input", updatePreview);
107
+ modalDiv.addEventListener("mousedown", (e) => {
108
+ if (e.target === modalDiv) {
109
+ mouseDownTarget = e.target;
110
+ } else {
111
+ mouseDownTarget = null;
112
+ }
113
+ });
114
+ modalDiv.addEventListener("mouseup", (e) => {
115
+ if (e.target === modalDiv && mouseDownTarget === modalDiv) {
116
+ closeModal();
117
+ }
118
+ mouseDownTarget = null;
119
+ });
120
+ return modalDiv;
121
+ }
122
+ function openModal() {
123
+ if (!md || !hljs) {
124
+ console.error("Markdown editor dependencies not ready.");
125
+ alert("编辑器依赖未能加载,请检查网络连接或控制台错误。");
126
+ return;
127
+ }
128
+ if (!mainChatInput) {
129
+ mainChatInput = document.querySelector(CHAT_INPUT_SELECTOR);
130
+ if (!mainChatInput) {
131
+ console.error("Chat input element not found when opening modal.");
132
+ return;
133
+ }
134
+ }
135
+ editorModal = createModal();
136
+ const currentText = (mainChatInput.innerText || "").trim();
137
+ editorInput.value = currentText;
138
+ updatePreview();
139
+ editorModal.classList.add("active");
140
+ editorInput.focus();
141
+ }
142
+ function closeModal() {
143
+ if (editorModal) {
144
+ editorModal.classList.remove("active");
145
+ }
146
+ }
147
+ function updatePreview() {
148
+ if (!md || !editorInput || !markdownPreview) return;
149
+ const content = editorInput.value;
150
+ markdownPreview.innerHTML = md.render(content);
151
+ }
152
+ function applyChanges() {
153
+ if (!mainChatInput || !editorInput) return;
154
+ const newText = editorInput.value;
155
+ mainChatInput.innerText = newText;
156
+ const placeholderP = mainChatInput.querySelector("p.is-empty");
157
+ if (newText.trim() === "") {
158
+ if (!placeholderP) {
159
+ }
160
+ } else {
161
+ if (placeholderP) {
162
+ }
163
+ mainChatInput.classList.remove("is-editor-empty");
164
+ const parentPlaceholder = mainChatInput.querySelector("p.is-empty");
165
+ if (parentPlaceholder) parentPlaceholder.classList.remove("is-empty");
166
+ }
167
+ mainChatInput.dispatchEvent(
168
+ new Event("input", { bubbles: true, cancelable: true })
169
+ );
170
+ closeModal();
171
+ mainChatInput.focus();
172
+ }
173
+ function injectEditorButton() {
174
+ if (document.getElementById(EDITOR_BUTTON_ID)) {
175
+ return;
176
+ }
177
+ mainChatInput = document.querySelector(CHAT_INPUT_SELECTOR);
178
+ const voiceButton = document.querySelector(VOICE_BUTTON_SELECTOR);
179
+ if (!mainChatInput || !voiceButton) {
180
+ return;
181
+ }
182
+ const voiceButtonContainer = voiceButton.closest(".flex");
183
+ if (!voiceButtonContainer || !voiceButtonContainer.parentElement) {
184
+ console.warn("Could not find suitable container for Markdown button.");
185
+ return;
186
+ }
187
+ const editButton = document.createElement("button");
188
+ editButton.id = EDITOR_BUTTON_ID;
189
+ editButton.className = "markdown-edit-btn";
190
+ editButton.type = "button";
191
+ editButton.title = "Markdown 编辑 (打开/关闭)";
192
+ editButton.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg"width="20"height="20"viewBox="0 0 24 24"fill="none"stroke="currentColor"stroke-width="2"stroke-linecap="round"stroke-linejoin="round"><path d="M12 20h9"></path><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"></path></svg>`;
193
+ editButton.addEventListener("click", (e) => {
194
+ e.preventDefault();
195
+ if (editorModal && editorModal.classList.contains("active")) {
196
+ closeModal();
197
+ } else {
198
+ openModal();
199
+ }
200
+ });
201
+ voiceButtonContainer.parentElement.insertBefore(
202
+ editButton,
203
+ voiceButtonContainer
204
+ );
205
+ console.log("Markdown Editor button injected.");
206
+ }
207
+ function handleKeyDown(e) {
208
+ if (editorModal && editorModal.classList.contains("active")) {
209
+ if (e.key === "Escape") {
210
+ e.preventDefault();
211
+ closeModal();
212
+ }
213
+ if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) {
214
+ e.preventDefault();
215
+ applyChanges();
216
+ }
217
+ }
218
+ }
219
+ ensureDependencies()
220
+ .then(() => {
221
+ injectEditorButton();
222
+ const observer = new MutationObserver((mutationsList, observer) => {
223
+ if (
224
+ document.querySelector(CHAT_INPUT_SELECTOR) &&
225
+ !document.getElementById(EDITOR_BUTTON_ID)
226
+ ) {
227
+ injectEditorButton();
228
+ }
229
+ });
230
+ observer.observe(document.body, { childList: true, subtree: true });
231
+ document.addEventListener("keydown", handleKeyDown);
232
+ console.log("Markdown Editor script initialized and observing DOM.");
233
+ })
234
+ .catch((error) => {
235
+ console.error("Initialization failed:", error);
236
+ });
237
+ })();