UNPKG

on-codemerge

Version:

A WYSIWYG editor for on-codemerge is a user-friendly interface that allows users to edit and view their code in real time, exactly as it will appear in the final product

199 lines (198 loc) 9.38 kB
var m = Object.defineProperty; var g = (i, e, t) => e in i ? m(i, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : i[e] = t; var o = (i, e, t) => g(i, typeof e != "symbol" ? e + "" : e, t); import { createToolbarButton as k } from "../ToolbarPlugin/utils.mjs"; import C from "../../icons/spell-check.svg.mjs"; import x from "../../../node_modules/.pnpm/spellchecker-wasm@0.3.3/node_modules/spellchecker-wasm/lib/spellchecker-wasm.wasm.mjs"; const S = /* @__PURE__ */ Object.assign({ "./dictionaries/ar.txt": () => import("./dictionaries/ar.txt.mjs").then((i) => i.default), "./dictionaries/cs.txt": () => import("./dictionaries/cs.txt.mjs").then((i) => i.default), "./dictionaries/de.txt": () => import("./dictionaries/de.txt.mjs").then((i) => i.default), "./dictionaries/en.txt": () => import("./dictionaries/en.txt.mjs").then((i) => i.default), "./dictionaries/es.txt": () => import("./dictionaries/es.txt.mjs").then((i) => i.default), "./dictionaries/fr.txt": () => import("./dictionaries/fr.txt.mjs").then((i) => i.default), "./dictionaries/hi.txt": () => import("./dictionaries/hi.txt.mjs").then((i) => i.default), "./dictionaries/id.txt": () => import("./dictionaries/id.txt.mjs").then((i) => i.default), "./dictionaries/it.txt": () => import("./dictionaries/it.txt.mjs").then((i) => i.default), "./dictionaries/ja.txt": () => import("./dictionaries/ja.txt.mjs").then((i) => i.default), "./dictionaries/ko.txt": () => import("./dictionaries/ko.txt.mjs").then((i) => i.default), "./dictionaries/nl.txt": () => import("./dictionaries/nl.txt.mjs").then((i) => i.default), "./dictionaries/pl.txt": () => import("./dictionaries/pl.txt.mjs").then((i) => i.default), "./dictionaries/pt.txt": () => import("./dictionaries/pt.txt.mjs").then((i) => i.default), "./dictionaries/ru.txt": () => import("./dictionaries/ru.txt.mjs").then((i) => i.default), "./dictionaries/th.txt": () => import("./dictionaries/th.txt.mjs").then((i) => i.default), "./dictionaries/tr.txt": () => import("./dictionaries/tr.txt.mjs").then((i) => i.default), "./dictionaries/vi.txt": () => import("./dictionaries/vi.txt.mjs").then((i) => i.default), "./dictionaries/zh.txt": () => import("./dictionaries/zh.txt.mjs").then((i) => i.default) }); class v { constructor() { o(this, "name", "spellchecker"); o(this, "hotkeys", [ { keys: "Ctrl+Shift+L", description: "Run spell checker", command: "spellchecker", icon: "🔍✅" } ]); o(this, "editor", null); o(this, "toolbarButton", null); o(this, "isSpellCheckEnabled", !1); o(this, "spellChecker", null); o(this, "misspelledWords", /* @__PURE__ */ new Map()); o(this, "lastResults", []); } async initialize(e) { this.editor = e, this.addToolbarButton(), this.enableSpellCheck(this.isSpellCheckEnabled), this.editor.on("spellchecker", async () => { var t, l; this.isSpellCheckEnabled = !this.isSpellCheckEnabled, this.isSpellCheckEnabled && await this.loadDictionary(((t = this.editor) == null ? void 0 : t.getLocale()) ?? "en"), this.enableSpellCheck(this.isSpellCheckEnabled), (l = this.toolbarButton) == null || l.classList.toggle("active", this.isSpellCheckEnabled); }); } async loadDictionary(e) { try { const t = await fetch(x), l = S[`./dictionaries/${e}.txt`]; if (!l) throw new Error(`Dictionary files for locale ${e} not found`); const s = await l(), n = await fetch(s); if (!t.ok || !n.ok) throw new Error("Failed to load dictionary files"); const { SpellcheckerWasm: a } = await import("../../../_virtual/index.mjs").then((r) => r.i); this.spellChecker = new a((r) => { this.lastResults = r; }), await this.spellChecker.prepareSpellchecker(t, n); } catch (t) { console.error("Failed to load dictionary:", t), this.spellChecker = null; } } addToolbarButton() { var t; const e = document.querySelector(".editor-toolbar"); e && (this.toolbarButton = k({ icon: C, title: (t = this.editor) == null ? void 0 : t.t("Spell Check"), onClick: async () => { var l, s; this.isSpellCheckEnabled = !this.isSpellCheckEnabled, this.isSpellCheckEnabled && await this.loadDictionary(((l = this.editor) == null ? void 0 : l.getLocale()) ?? "en"), this.enableSpellCheck(this.isSpellCheckEnabled), (s = this.toolbarButton) == null || s.classList.toggle("active", this.isSpellCheckEnabled); } }), e.appendChild(this.toolbarButton)); } enableSpellCheck(e) { var l; ((l = this.editor) == null ? void 0 : l.getContainer()) && (e ? (this.addSpellCheckListeners(), this.checkAllText()) : (this.removeSpellCheckListeners(), this.clearHighlights())); } addSpellCheckListeners() { var t; const e = (t = this.editor) == null ? void 0 : t.getContainer(); e && (e.addEventListener("input", this.handleInput.bind(this)), e.addEventListener("mouseup", this.handleMouseUp.bind(this)), e.addEventListener("contextmenu", this.handleContextMenu.bind(this))); } removeSpellCheckListeners() { var t; const e = (t = this.editor) == null ? void 0 : t.getContainer(); e && (e.removeEventListener("input", this.handleInput.bind(this)), e.removeEventListener("mouseup", this.handleMouseUp.bind(this)), e.removeEventListener("contextmenu", this.handleContextMenu.bind(this))); } handleInput(e) { this.checkAllText(); } handleMouseUp(e) { const t = window.getSelection(); if (t && t.rangeCount > 0) { const l = t.getRangeAt(0); l.toString() && this.checkSpelling(l); } } handleContextMenu(e) { const t = window.getSelection(); if (t && t.rangeCount > 0) { const l = t.getRangeAt(0), s = l.toString(); if (s && this.isMisspelled(s)) { e.preventDefault(); const n = this.getSpellingSuggestions(s); n.length > 0 && this.showContextMenu(e, n, l); } } } checkAllText() { var s; const e = (s = this.editor) == null ? void 0 : s.getContainer(); if (!e) return; this.clearHighlights(); const t = document.createTreeWalker(e, NodeFilter.SHOW_TEXT); let l; for (; l = t.nextNode(); ) { const n = l, a = n.textContent; if (a) { const r = a.split(/\s+/); let d = 0; r.forEach((h) => { if (this.isMisspelled(h)) { const c = document.createRange(); c.setStart(n, d), c.setEnd(n, d + h.length), this.highlightMisspelledWord(n, c); } d += h.length + 1; }); } } } checkSpelling(e) { const t = e.toString(); if (this.isMisspelled(t)) { const l = e.startContainer; this.highlightMisspelledWord(l, e); } } isMisspelled(e) { return this.spellChecker ? (this.lastResults = [], this.spellChecker.checkSpelling(e, { includeUnknown: !0, // Включаем слова, которых нет в словаре maxEditDistance: 2, // Максимальное расстояние редактирования verbosity: 1, // Уровень детализации предложений includeSelf: !1 // Не включать само слово в результаты }), this.lastResults.length > 0) : !1; } getSpellingSuggestions(e) { return this.spellChecker ? (this.lastResults = [], this.spellChecker.checkSpelling(e, { includeUnknown: !0, maxEditDistance: 2, verbosity: 2, // Возвращаем все предложения includeSelf: !1 }), this.lastResults.map((t) => t.term)) : []; } highlightMisspelledWord(e, t) { var l; (l = e.parentElement) == null || l.classList.add("misspelled-word"), this.misspelledWords.set(e, t); } clearHighlights() { this.misspelledWords.forEach((e, t) => { var l; (l = t.parentElement) == null || l.classList.remove("misspelled-word"); }), this.misspelledWords.clear(); } showContextMenu(e, t, l) { var u; const s = document.createElement("div"); s.style.position = "fixed", s.style.backgroundColor = "#fff", s.style.border = "1px solid #ccc", s.style.boxShadow = "0 2px 5px rgba(0, 0, 0, 0.2)", s.style.zIndex = "1000", s.style.maxHeight = "200px", s.style.overflowY = "auto", s.style.padding = "4px 0"; const n = 200, a = Math.min(t.length * 32, 200), r = window.innerWidth, d = window.innerHeight; let h = e.clientY, c = e.clientX; h + a > d && (h = d - a), c + n > r && (c = r - n), s.style.top = `${h}px`, s.style.left = `${c}px`, t.forEach((f) => { const p = document.createElement("div"); p.style.padding = "8px 16px", p.style.cursor = "pointer", p.textContent = f, p.addEventListener("click", () => { this.replaceMisspelledWord(l, f), s.remove(); }), s.appendChild(p); }), (u = this.editor) == null || u.getInnerContainer().appendChild(s), document.addEventListener("click", () => s.remove(), { once: !0 }); } replaceMisspelledWord(e, t) { e.deleteContents(), e.insertNode(document.createTextNode(t)), this.checkAllText(); } destroy() { var e; this.enableSpellCheck(!1), (e = this.toolbarButton) == null || e.remove(), this.toolbarButton = null, this.editor = null, this.spellChecker = null; } } export { v as SpellCheckerPlugin };