UNPKG

vue-use-monaco

Version:

A Vue library for integrating Monaco Editor with Shiki syntax highlighting, supporting real-time updates.

1,359 lines (1,345 loc) 59.4 kB
import * as monaco$3 from "monaco-editor"; import * as monaco$2 from "monaco-editor"; import * as monaco$1 from "monaco-editor"; import * as monaco from "monaco-editor"; import { computed, onUnmounted, watch } from "vue"; import { useDark } from "@vueuse/core"; import { shikiToMonaco } from "@shikijs/monaco"; import { createHighlighter } from "shiki"; //#region src/code.detect.ts /** * Language detection definitions */ const languages = [ [ "bash", [/#!(\/usr)?\/bin\/bash/g, 500], [/\b(if|elif|then|fi|echo)\b|\$/g, 10] ], [ "html", [/<\/?[a-z-][^\n>]*>/g, 10], [/^\s+<!DOCTYPE\s+html/g, 500] ], ["http", [/^(GET|HEAD|POST|PUT|DELETE|PATCH|HTTP)\b/g, 500]], ["js", [/\b(console|await|async|function|export|import|this|class|for|let|const|map|join|require)\b/g, 10]], ["ts", [/\b(console|await|async|function|export|import|this|class|for|let|const|map|join|require|implements|interface|namespace)\b/g, 10]], ["py", [/\b(def|print|class|and|or|lambda)\b/g, 10]], ["sql", [/\b(SELECT|INSERT|FROM)\b/g, 50]], [ "pl", [/#!(\/usr)?\/bin\/perl/g, 500], [/\b(use|print)\b|\$/g, 10] ], ["lua", [/#!(\/usr)?\/bin\/lua/g, 500]], ["make", [/\b(ifneq|endif|if|elif|then|fi|echo|.PHONY|^[a-z]+ ?:$)\b|\$/gm, 10]], ["uri", [/https?:|mailto:|tel:|ftp:/g, 30]], ["css", [/^(@import|@page|@media|(\.|#)[a-z]+)/gm, 20]], [ "diff", [/^[+><-]/gm, 10], [/^@@[-+,0-9 ]+@@/gm, 25] ], [ "md", [/^(>|\t\*|\t\d+.|#{1,6} |-\s+|\*\s+)/gm, 25], [/\[.*\](.*)/g, 10] ], ["docker", [/^(FROM|ENTRYPOINT|RUN)/gm, 500]], [ "xml", [/<\/?[a-z-][^\n>]*>/g, 10], [/^<\?xml/g, 500] ], ["c", [/#include\b|\bprintf\s+\(/g, 100]], ["rs", [/^\s+(use|fn|mut|match)\b/gm, 100]], ["go", [/\b(func|fmt|package)\b/g, 100]], ["java", [/^import\s+java/gm, 500]], ["asm", [/^(section|global main|extern|\t(call|mov|ret))/gm, 100]], ["css", [/^(@import|@page|@media|(\.|#)[a-z]+)/gm, 20]], ["json", [/\b(true|false|null|\{\})\b|"[^"]+":/g, 10]], ["yaml", [/^(\s+)?[a-z][a-z0-9]*:/gim, 10]], [ "toml", [/^\s*\[.*\]\s*$/gm, 100], [/^\s*[\w-]+ *= */gm, 20] ], [ "mermaid", [/^(graph|flowchart|sequenceDiagram|classDiagram|stateDiagram|erDiagram|gantt|pie|mindmap)/gm, 500], [/\b(-->|--o|--x|=>|\[\]|[{}])\b/g, 10] ] ]; /** * Try to find the language the given code belongs to * * @param {string} code The code to analyze * @param {LanguageDefinition[]} [additionalLanguages] Additional language definitions to supplement the built-in ones * @returns {CodeLanguage} The detected language of the code */ function detectLanguage(code, additionalLanguages) { var _allLanguages$map$fil; const allLanguages = additionalLanguages ? [...languages, ...additionalLanguages] : languages; return ((_allLanguages$map$fil = allLanguages.map(([lang, ...features]) => [lang, features.reduce((acc, [match, score]) => acc + [...code.matchAll(match)].length * score, 0)]).filter(([, score]) => score > 20).sort((a, b) => b[1] - a[1])[0]) === null || _allLanguages$map$fil === void 0 ? void 0 : _allLanguages$map$fil[0]) || "plain"; } function processedLanguage(language) { if (/^(?:shellscript|bash|sh|shell|zsh)/i.test(language)) return "shell"; if (/^(?:powershell|ps1?)/i.test(language)) return "powershell"; return language.split(":")[0]; } /** * 使用示例: * * // 基本用法 * const language1 = detectLanguage('console.log("hello")') // 'js' * * // 使用自定义语言检测规则 * const customLanguages: LanguageDefinition[] = [ * ['vue', [/<template>/g, 100], [/<script>/g, 50], [/<style>/g, 50]], * ['kotlin', [/\b(fun|class|val|var)\b/g, 20]] * ] * * const language2 = detectLanguage(` * <template> * <div>Hello Vue</div> * </template> * `, customLanguages) // 'vue' * * const language3 = detectLanguage(` * fun main() { * val name = "Kotlin" * println("Hello $name") * } * `, customLanguages) // 'kotlin' */ //#endregion //#region src/constant.ts const defaultLanguages = [ "jsx", "tsx", "vue", "csharp", "python", "java", "c", "cpp", "rust", "go", "powershell", "sql", "json", "html", "javascript", "typescript", "css", "markdown", "xml", "yaml", "toml", "dockerfile", "kotlin", "objective-c", "objective-cpp", "php", "ruby", "scala", "svelte", "swift", "erlang", "angular-html", "angular-ts", "dart", "lua", "mermaid", "cmake", "nginx" ]; const defaultThemes = ["vitesse-dark", "vitesse-light"]; const defaultScrollbar = { verticalScrollbarSize: 8, horizontalScrollbarSize: 8, handleMouseWheel: true, alwaysConsumeMouseWheel: false }; const padding = 16; //#endregion //#region src/minimalEdit.ts /** * Compute the minimal middle replacement between two UTF-16 strings. * Returns null when prev === next (no edits required). */ function computeMinimalEdit(prev, next) { if (prev === next) return null; let start = 0; const minLen = Math.min(prev.length, next.length); while (start < minLen && prev.charCodeAt(start) === next.charCodeAt(start)) start++; let endPrev = prev.length - 1; let endNext = next.length - 1; while (endPrev >= start && endNext >= start && prev.charCodeAt(endPrev) === next.charCodeAt(endNext)) { endPrev--; endNext--; } return { start, endPrevIncl: endPrev, endNextIncl: endNext, replaceText: next.slice(start, endNext + 1) }; } //#endregion //#region src/utils/height.ts function createHeightManager(container, computeNext) { let raf = null; let lastApplied = -1; let suppressed = false; function apply() { const next = computeNext(); if (next === lastApplied) return; suppressed = true; container.style.height = `${next}px`; lastApplied = next; queueMicrotask(() => { suppressed = false; }); } function update() { if (raf != null) return; raf = requestAnimationFrame(() => { raf = null; apply(); }); } function dispose() { if (raf != null) { try { cancelAnimationFrame(raf); } catch {} raf = null; } } function isSuppressed() { return suppressed; } function getLastApplied() { return lastApplied; } return { update, dispose, isSuppressed, getLastApplied }; } //#endregion //#region src/utils/raf.ts function createRafScheduler(timeSource) { const ts = timeSource ?? { requestAnimationFrame: (cb) => requestAnimationFrame(cb), cancelAnimationFrame: (id) => cancelAnimationFrame(id) }; const ids = {}; function schedule(kind, cb) { const existing = ids[kind]; if (existing != null) try { ts.cancelAnimationFrame(existing); } catch {} ids[kind] = ts.requestAnimationFrame((t) => { ids[kind] = null; cb(t); }); } function cancel(kind) { const id = ids[kind]; if (id != null) { try { ts.cancelAnimationFrame(id); } catch {} ids[kind] = null; } } return { schedule, cancel }; } //#endregion //#region src/utils/scroll.ts function createScrollWatcherForEditor(ed, opts) { try { var _ed$getScrollTop, _ed$onDidScrollChange; const initial = ((_ed$getScrollTop = ed.getScrollTop) === null || _ed$getScrollTop === void 0 ? void 0 : _ed$getScrollTop.call(ed)) ?? 0; opts.setLast(initial); const disp = ((_ed$onDidScrollChange = ed.onDidScrollChange) === null || _ed$onDidScrollChange === void 0 ? void 0 : _ed$onDidScrollChange.call(ed, (e) => { var _ed$getScrollTop2; const currentTop = e && typeof e.scrollTop === "number" ? e.scrollTop : ((_ed$getScrollTop2 = ed.getScrollTop) === null || _ed$getScrollTop2 === void 0 ? void 0 : _ed$getScrollTop2.call(ed)) ?? 0; const delta = currentTop - opts.getLast(); opts.setLast(currentTop); if (delta < 0) { opts.onPause(); return; } opts.onMaybeResume(); })) ?? null; return disp; } catch { return null; } } //#endregion //#region src/core/DiffEditorManager.ts var DiffEditorManager = class { diffEditorView = null; originalModel = null; modifiedModel = null; lastContainer = null; lastKnownOriginalCode = null; lastKnownModifiedCode = null; pendingDiffUpdate = null; shouldAutoScrollDiff = true; diffScrollWatcher = null; lastScrollTopDiff = 0; _hasScrollBar = false; cachedScrollHeightDiff = null; cachedLineHeightDiff = null; cachedComputedHeightDiff = null; appendBufferDiff = []; appendBufferDiffScheduled = false; rafScheduler = createRafScheduler(); diffHeightManager = null; constructor(options, maxHeightValue, maxHeightCSS, autoScrollOnUpdate, autoScrollInitial, autoScrollThresholdPx, autoScrollThresholdLines, diffAutoScroll) { this.options = options; this.maxHeightValue = maxHeightValue; this.maxHeightCSS = maxHeightCSS; this.autoScrollOnUpdate = autoScrollOnUpdate; this.autoScrollInitial = autoScrollInitial; this.autoScrollThresholdPx = autoScrollThresholdPx; this.autoScrollThresholdLines = autoScrollThresholdLines; this.diffAutoScroll = diffAutoScroll; } computedHeight() { var _originalEditor$getMo, _modifiedEditor$getMo; if (!this.diffEditorView) return Math.min(1 * 18 + padding, this.maxHeightValue); const modifiedEditor = this.diffEditorView.getModifiedEditor(); const originalEditor = this.diffEditorView.getOriginalEditor(); const lineHeight = modifiedEditor.getOption(monaco$3.editor.EditorOption.lineHeight); const oCount = ((_originalEditor$getMo = originalEditor.getModel()) === null || _originalEditor$getMo === void 0 ? void 0 : _originalEditor$getMo.getLineCount()) ?? 1; const mCount = ((_modifiedEditor$getMo = modifiedEditor.getModel()) === null || _modifiedEditor$getMo === void 0 ? void 0 : _modifiedEditor$getMo.getLineCount()) ?? 1; const lineCount = Math.max(oCount, mCount); return Math.min(lineCount * lineHeight + padding, this.maxHeightValue); } hasVerticalScrollbarModified() { try { if (!this.diffEditorView) return false; if (this._hasScrollBar) return true; const me = this.diffEditorView.getModifiedEditor(); const ch = this.cachedComputedHeightDiff ?? this.computedHeight(); return this._hasScrollBar = me.getScrollHeight() > ch + padding / 2; } catch { return false; } } userIsNearBottomDiff() { try { var _me$getLayoutInfo, _me$getScrollTop, _me$getScrollHeight; if (!this.diffEditorView) return true; const me = this.diffEditorView.getModifiedEditor(); const li = (_me$getLayoutInfo = me.getLayoutInfo) === null || _me$getLayoutInfo === void 0 ? void 0 : _me$getLayoutInfo.call(me); if (!li) return true; const lineHeight = this.cachedLineHeightDiff ?? me.getOption(monaco$3.editor.EditorOption.lineHeight); const lineThreshold = (this.autoScrollThresholdLines ?? 0) * lineHeight; const threshold = Math.max(lineThreshold || 0, this.autoScrollThresholdPx || 0); const st = ((_me$getScrollTop = me.getScrollTop) === null || _me$getScrollTop === void 0 ? void 0 : _me$getScrollTop.call(me)) ?? 0; const sh = this.cachedScrollHeightDiff ?? ((_me$getScrollHeight = me.getScrollHeight) === null || _me$getScrollHeight === void 0 ? void 0 : _me$getScrollHeight.call(me)) ?? li.height; const distance = sh - (st + li.height); return distance <= threshold; } catch { return true; } } maybeScrollDiffToBottom(targetLine) { if (!this.diffEditorView) return; if (this.diffAutoScroll && this.autoScrollOnUpdate && this.shouldAutoScrollDiff && this.hasVerticalScrollbarModified()) { const me = this.diffEditorView.getModifiedEditor(); const model = me.getModel(); const line = targetLine ?? (model === null || model === void 0 ? void 0 : model.getLineCount()) ?? 1; this.rafScheduler.schedule("revealDiff", () => { try { me.revealLine(line); } catch {} }); } } async createDiffEditor(container, originalCode, modifiedCode, language, currentTheme) { var _oEditor$onDidContent, _mEditor$onDidContent; this.cleanup(); this.lastContainer = container; container.style.overflow = "auto"; container.style.maxHeight = this.maxHeightCSS; const lang = processedLanguage(language) || language; this.originalModel = monaco$3.editor.createModel(originalCode, lang); this.modifiedModel = monaco$3.editor.createModel(modifiedCode, lang); this.diffEditorView = monaco$3.editor.createDiffEditor(container, { automaticLayout: true, scrollBeyondLastLine: false, renderSideBySide: true, originalEditable: false, readOnly: this.options.readOnly ?? true, minimap: { enabled: false }, theme: currentTheme, contextmenu: false, scrollbar: { ...defaultScrollbar, ...this.options.scrollbar || {} }, ...this.options }); this.diffEditorView.setModel({ original: this.originalModel, modified: this.modifiedModel }); this.lastKnownOriginalCode = originalCode; this.lastKnownModifiedCode = modifiedCode; this.shouldAutoScrollDiff = !!(this.autoScrollInitial && this.diffAutoScroll); if (this.diffScrollWatcher) { try { this.diffScrollWatcher.dispose(); } catch {} this.diffScrollWatcher = null; } if (this.diffAutoScroll) { const me = this.diffEditorView.getModifiedEditor(); this.diffScrollWatcher = createScrollWatcherForEditor(me, { onPause: () => { this.shouldAutoScrollDiff = false; }, onMaybeResume: () => { this.shouldAutoScrollDiff = this.userIsNearBottomDiff(); }, getLast: () => this.lastScrollTopDiff, setLast: (v) => { this.lastScrollTopDiff = v; } }); } this.maybeScrollDiffToBottom(this.modifiedModel.getLineCount()); if (this.diffHeightManager) { try { this.diffHeightManager.dispose(); } catch {} this.diffHeightManager = null; } this.diffHeightManager = createHeightManager(container, () => this.computedHeight()); this.diffHeightManager.update(); try { var _me$getScrollHeight2, _me$getOption; const me = this.diffEditorView.getModifiedEditor(); this.cachedScrollHeightDiff = ((_me$getScrollHeight2 = me.getScrollHeight) === null || _me$getScrollHeight2 === void 0 ? void 0 : _me$getScrollHeight2.call(me)) ?? null; this.cachedLineHeightDiff = ((_me$getOption = me.getOption) === null || _me$getOption === void 0 ? void 0 : _me$getOption.call(me, monaco$3.editor.EditorOption.lineHeight)) ?? null; this.cachedComputedHeightDiff = this.computedHeight(); } catch {} const oEditor = this.diffEditorView.getOriginalEditor(); const mEditor = this.diffEditorView.getModifiedEditor(); (_oEditor$onDidContent = oEditor.onDidContentSizeChange) === null || _oEditor$onDidContent === void 0 || _oEditor$onDidContent.call(oEditor, () => { var _this$diffHeightManag, _this$diffHeightManag2; this._hasScrollBar = false; try { var _oEditor$getScrollHei, _oEditor$getOption; this.cachedScrollHeightDiff = ((_oEditor$getScrollHei = oEditor.getScrollHeight) === null || _oEditor$getScrollHei === void 0 ? void 0 : _oEditor$getScrollHei.call(oEditor)) ?? this.cachedScrollHeightDiff; this.cachedLineHeightDiff = ((_oEditor$getOption = oEditor.getOption) === null || _oEditor$getOption === void 0 ? void 0 : _oEditor$getOption.call(oEditor, monaco$3.editor.EditorOption.lineHeight)) ?? this.cachedLineHeightDiff; this.cachedComputedHeightDiff = this.computedHeight(); } catch {} if ((_this$diffHeightManag = this.diffHeightManager) === null || _this$diffHeightManag === void 0 ? void 0 : _this$diffHeightManag.isSuppressed()) return; (_this$diffHeightManag2 = this.diffHeightManager) === null || _this$diffHeightManag2 === void 0 || _this$diffHeightManag2.update(); }); (_mEditor$onDidContent = mEditor.onDidContentSizeChange) === null || _mEditor$onDidContent === void 0 || _mEditor$onDidContent.call(mEditor, () => { var _this$diffHeightManag3, _this$diffHeightManag4; this._hasScrollBar = false; try { var _mEditor$getScrollHei, _mEditor$getOption; this.cachedScrollHeightDiff = ((_mEditor$getScrollHei = mEditor.getScrollHeight) === null || _mEditor$getScrollHei === void 0 ? void 0 : _mEditor$getScrollHei.call(mEditor)) ?? this.cachedScrollHeightDiff; this.cachedLineHeightDiff = ((_mEditor$getOption = mEditor.getOption) === null || _mEditor$getOption === void 0 ? void 0 : _mEditor$getOption.call(mEditor, monaco$3.editor.EditorOption.lineHeight)) ?? this.cachedLineHeightDiff; this.cachedComputedHeightDiff = this.computedHeight(); } catch {} if ((_this$diffHeightManag3 = this.diffHeightManager) === null || _this$diffHeightManag3 === void 0 ? void 0 : _this$diffHeightManag3.isSuppressed()) return; (_this$diffHeightManag4 = this.diffHeightManager) === null || _this$diffHeightManag4 === void 0 || _this$diffHeightManag4.update(); }); return this.diffEditorView; } updateDiff(originalCode, modifiedCode, codeLanguage) { if (!this.diffEditorView || !this.originalModel || !this.modifiedModel) return; const plang = codeLanguage ? processedLanguage(codeLanguage) : void 0; if (plang && (this.originalModel.getLanguageId() !== plang || this.modifiedModel.getLanguageId() !== plang)) { this.pendingDiffUpdate = { original: originalCode, modified: modifiedCode, lang: codeLanguage }; this.rafScheduler.schedule("diff", () => this.flushPendingDiffUpdate()); return; } if (this.lastKnownOriginalCode == null) this.lastKnownOriginalCode = this.originalModel.getValue(); if (this.lastKnownModifiedCode == null) this.lastKnownModifiedCode = this.modifiedModel.getValue(); const prevO = this.lastKnownOriginalCode; const prevM = this.lastKnownModifiedCode; let didImmediate = false; if (originalCode !== prevO && originalCode.startsWith(prevO)) { this.appendToModel(this.originalModel, originalCode.slice(prevO.length)); this.lastKnownOriginalCode = originalCode; didImmediate = true; } if (modifiedCode !== prevM && modifiedCode.startsWith(prevM)) { this.appendToModel(this.modifiedModel, modifiedCode.slice(prevM.length)); this.lastKnownModifiedCode = modifiedCode; didImmediate = true; this.maybeScrollDiffToBottom(this.modifiedModel.getLineCount()); } if (originalCode !== this.lastKnownOriginalCode || modifiedCode !== this.lastKnownModifiedCode) { this.pendingDiffUpdate = { original: originalCode, modified: modifiedCode }; this.rafScheduler.schedule("diff", () => this.flushPendingDiffUpdate()); } else if (didImmediate) {} } updateOriginal(newCode, codeLanguage) { if (!this.diffEditorView || !this.originalModel) return; if (codeLanguage) { const lang = processedLanguage(codeLanguage); if (lang && this.originalModel.getLanguageId() !== lang) monaco$3.editor.setModelLanguage(this.originalModel, lang); } const prev = this.lastKnownOriginalCode ?? this.originalModel.getValue(); if (prev === newCode) return; if (newCode.startsWith(prev) && prev.length < newCode.length) this.appendToModel(this.originalModel, newCode.slice(prev.length)); else this.applyMinimalEditToModel(this.originalModel, prev, newCode); this.lastKnownOriginalCode = newCode; } updateModified(newCode, codeLanguage) { if (!this.diffEditorView || !this.modifiedModel) return; if (codeLanguage) { const lang = processedLanguage(codeLanguage); if (lang && this.modifiedModel.getLanguageId() !== lang) monaco$3.editor.setModelLanguage(this.modifiedModel, lang); } const prev = this.lastKnownModifiedCode ?? this.modifiedModel.getValue(); if (prev === newCode) return; if (newCode.startsWith(prev) && prev.length < newCode.length) { this.appendToModel(this.modifiedModel, newCode.slice(prev.length)); this.maybeScrollDiffToBottom(this.modifiedModel.getLineCount()); } else this.applyMinimalEditToModel(this.modifiedModel, prev, newCode); this.lastKnownModifiedCode = newCode; } appendOriginal(appendText, codeLanguage) { if (!this.diffEditorView || !this.originalModel || !appendText) return; if (codeLanguage) { const lang = processedLanguage(codeLanguage); if (lang && this.originalModel.getLanguageId() !== lang) monaco$3.editor.setModelLanguage(this.originalModel, lang); } this.appendToModel(this.originalModel, appendText); try { this.lastKnownOriginalCode = this.originalModel.getValue(); } catch {} } appendModified(appendText, codeLanguage) { if (!this.diffEditorView || !this.modifiedModel || !appendText) return; if (codeLanguage) { const lang = processedLanguage(codeLanguage); if (lang && this.modifiedModel.getLanguageId() !== lang) monaco$3.editor.setModelLanguage(this.modifiedModel, lang); } this.appendToModel(this.modifiedModel, appendText); try { this.lastKnownModifiedCode = this.modifiedModel.getValue(); } catch {} this.appendBufferDiff.push(appendText); if (!this.appendBufferDiffScheduled) { this.appendBufferDiffScheduled = true; this.rafScheduler.schedule("appendDiff", () => this.flushAppendBufferDiff()); } } setLanguage(language, languages$1) { if (!languages$1.includes(language)) { console.warn(`Language "${language}" is not registered. Available languages: ${languages$1.join(", ")}`); return; } if (this.originalModel && this.originalModel.getLanguageId() !== language) monaco$3.editor.setModelLanguage(this.originalModel, language); if (this.modifiedModel && this.modifiedModel.getLanguageId() !== language) monaco$3.editor.setModelLanguage(this.modifiedModel, language); } getDiffEditorView() { return this.diffEditorView; } getDiffModels() { return { original: this.originalModel, modified: this.modifiedModel }; } cleanup() { this.rafScheduler.cancel("diff"); this.pendingDiffUpdate = null; this.rafScheduler.cancel("appendDiff"); this.appendBufferDiffScheduled = false; this.appendBufferDiff.length = 0; if (this.diffScrollWatcher) { try { this.diffScrollWatcher.dispose(); } catch {} this.diffScrollWatcher = null; } if (this.diffHeightManager) { try { this.diffHeightManager.dispose(); } catch {} this.diffHeightManager = null; } if (this.diffEditorView) { try { this.diffEditorView.dispose(); } catch {} this.diffEditorView = null; } if (this.originalModel) { try { this.originalModel.dispose(); } catch {} this.originalModel = null; } if (this.modifiedModel) { try { this.modifiedModel.dispose(); } catch {} this.modifiedModel = null; } this.lastKnownOriginalCode = null; this.lastKnownModifiedCode = null; if (this.lastContainer) { this.lastContainer.innerHTML = ""; this.lastContainer = null; } } safeClean() { this.rafScheduler.cancel("diff"); this.pendingDiffUpdate = null; if (this.diffScrollWatcher) { try { this.diffScrollWatcher.dispose(); } catch {} this.diffScrollWatcher = null; } this._hasScrollBar = false; this.shouldAutoScrollDiff = !!(this.autoScrollInitial && this.diffAutoScroll); this.lastScrollTopDiff = 0; if (this.diffHeightManager) { try { this.diffHeightManager.dispose(); } catch {} this.diffHeightManager = null; } } flushPendingDiffUpdate() { if (!this.pendingDiffUpdate || !this.diffEditorView) return; const o = this.originalModel; const m = this.modifiedModel; if (!o || !m) { this.pendingDiffUpdate = null; return; } const { original, modified, lang } = this.pendingDiffUpdate; this.pendingDiffUpdate = null; if (lang) { const plang = processedLanguage(lang); if (plang) { if (o.getLanguageId() !== plang) { monaco$3.editor.setModelLanguage(o, plang); monaco$3.editor.setModelLanguage(m, plang); } } } if (this.lastKnownOriginalCode == null) this.lastKnownOriginalCode = o.getValue(); if (this.lastKnownModifiedCode == null) this.lastKnownModifiedCode = m.getValue(); const prevO = this.lastKnownOriginalCode; if (prevO !== original) { if (original.startsWith(prevO) && prevO.length < original.length) this.appendToModel(o, original.slice(prevO.length)); else this.applyMinimalEditToModel(o, prevO, original); this.lastKnownOriginalCode = original; } const prevM = this.lastKnownModifiedCode; const prevMLineCount = m.getLineCount(); if (prevM !== modified) { if (modified.startsWith(prevM) && prevM.length < modified.length) this.appendToModel(m, modified.slice(prevM.length)); else this.applyMinimalEditToModel(m, prevM, modified); this.lastKnownModifiedCode = modified; const newMLineCount = m.getLineCount(); if (newMLineCount !== prevMLineCount) this.maybeScrollDiffToBottom(newMLineCount); } } flushAppendBufferDiff() { if (!this.diffEditorView) return; if (this.appendBufferDiff.length === 0) return; this.appendBufferDiffScheduled = false; const me = this.diffEditorView.getModifiedEditor(); const model = me.getModel(); if (!model) { this.appendBufferDiff.length = 0; return; } const text = this.appendBufferDiff.join(""); this.appendBufferDiff.length = 0; try { const lastLine = model.getLineCount(); const lastColumn = model.getLineMaxColumn(lastLine); const range = new monaco$3.Range(lastLine, lastColumn, lastLine, lastColumn); model.applyEdits([{ range, text, forceMoveMarkers: true }]); if (this.lastKnownModifiedCode != null) this.lastKnownModifiedCode = this.lastKnownModifiedCode + text; try { this.maybeScrollDiffToBottom(model.getLineCount()); } catch {} } catch {} } applyMinimalEditToModel(model, prev, next) { const res = computeMinimalEdit(prev, next); if (!res) return; const { start, endPrevIncl, replaceText } = res; const rangeStart = model.getPositionAt(start); const rangeEnd = model.getPositionAt(endPrevIncl + 1); const range = new monaco$3.Range(rangeStart.lineNumber, rangeStart.column, rangeEnd.lineNumber, rangeEnd.column); model.applyEdits([{ range, text: replaceText, forceMoveMarkers: true }]); } appendToModel(model, appendText) { if (!appendText) return; const lastLine = model.getLineCount(); const lastColumn = model.getLineMaxColumn(lastLine); const range = new monaco$3.Range(lastLine, lastColumn, lastLine, lastColumn); model.applyEdits([{ range, text: appendText, forceMoveMarkers: true }]); } }; //#endregion //#region src/core/EditorManager.ts var EditorManager = class { editorView = null; lastContainer = null; lastKnownCode = null; pendingUpdate = null; _hasScrollBar = false; shouldAutoScroll = true; scrollWatcher = null; lastScrollTop = 0; cachedScrollHeight = null; cachedLineHeight = null; cachedComputedHeight = null; appendBuffer = []; appendBufferScheduled = false; rafScheduler = createRafScheduler(); editorHeightManager = null; constructor(options, maxHeightValue, maxHeightCSS, autoScrollOnUpdate, autoScrollInitial, autoScrollThresholdPx, autoScrollThresholdLines) { this.options = options; this.maxHeightValue = maxHeightValue; this.maxHeightCSS = maxHeightCSS; this.autoScrollOnUpdate = autoScrollOnUpdate; this.autoScrollInitial = autoScrollInitial; this.autoScrollThresholdPx = autoScrollThresholdPx; this.autoScrollThresholdLines = autoScrollThresholdLines; } hasVerticalScrollbar() { try { if (!this.editorView) return false; if (this._hasScrollBar) return true; const ch = this.cachedComputedHeight ?? this.computedHeight(this.editorView); return this._hasScrollBar = this.editorView.getScrollHeight() > ch + padding / 2; } catch { return false; } } userIsNearBottom() { try { var _this$editorView$getL, _this$editorView, _this$editorView$getS, _this$editorView2, _this$editorView$getS2, _this$editorView3; if (!this.editorView) return true; const li = (_this$editorView$getL = (_this$editorView = this.editorView).getLayoutInfo) === null || _this$editorView$getL === void 0 ? void 0 : _this$editorView$getL.call(_this$editorView); if (!li) return true; const lineHeight = this.cachedLineHeight ?? this.editorView.getOption(monaco$2.editor.EditorOption.lineHeight); const lineThreshold = (this.autoScrollThresholdLines ?? 0) * lineHeight; const threshold = Math.max(lineThreshold || 0, this.autoScrollThresholdPx || 0); const st = ((_this$editorView$getS = (_this$editorView2 = this.editorView).getScrollTop) === null || _this$editorView$getS === void 0 ? void 0 : _this$editorView$getS.call(_this$editorView2)) ?? 0; const sh = this.cachedScrollHeight ?? ((_this$editorView$getS2 = (_this$editorView3 = this.editorView).getScrollHeight) === null || _this$editorView$getS2 === void 0 ? void 0 : _this$editorView$getS2.call(_this$editorView3)) ?? li.height; const distance = sh - (st + li.height); return distance <= threshold; } catch { return true; } } computedHeight(editorView) { var _editorView$getModel; const lineCount = ((_editorView$getModel = editorView.getModel()) === null || _editorView$getModel === void 0 ? void 0 : _editorView$getModel.getLineCount()) ?? 1; const lineHeight = editorView.getOption(monaco$2.editor.EditorOption.lineHeight); const height = Math.min(lineCount * lineHeight + padding, this.maxHeightValue); return height; } maybeScrollToBottom(targetLine) { if (this.autoScrollOnUpdate && this.shouldAutoScroll && this.hasVerticalScrollbar()) { const model = this.editorView.getModel(); const line = targetLine ?? (model === null || model === void 0 ? void 0 : model.getLineCount()) ?? 1; this.rafScheduler.schedule("reveal", () => { try { this.editorView.revealLine(line); } catch {} }); } } async createEditor(container, code, language, currentTheme) { var _this$editorView$onDi, _this$editorView6; this.cleanup(); this.lastContainer = container; container.style.overflow = "auto"; container.style.maxHeight = this.maxHeightCSS; this.editorView = monaco$2.editor.create(container, { value: code, language: processedLanguage(language) || language, theme: currentTheme, scrollBeyondLastLine: false, minimap: { enabled: false }, automaticLayout: true, readOnly: this.options.readOnly ?? true, contextmenu: false, scrollbar: { ...defaultScrollbar, ...this.options.scrollbar || {} }, ...this.options }); this.lastKnownCode = this.editorView.getValue(); if (this.editorHeightManager) { try { this.editorHeightManager.dispose(); } catch {} this.editorHeightManager = null; } this.editorHeightManager = createHeightManager(container, () => this.computedHeight(this.editorView)); this.editorHeightManager.update(); try { var _this$editorView$getS3, _this$editorView4, _this$editorView$getO, _this$editorView5; this.cachedScrollHeight = ((_this$editorView$getS3 = (_this$editorView4 = this.editorView).getScrollHeight) === null || _this$editorView$getS3 === void 0 ? void 0 : _this$editorView$getS3.call(_this$editorView4)) ?? null; this.cachedLineHeight = ((_this$editorView$getO = (_this$editorView5 = this.editorView).getOption) === null || _this$editorView$getO === void 0 ? void 0 : _this$editorView$getO.call(_this$editorView5, monaco$2.editor.EditorOption.lineHeight)) ?? null; this.cachedComputedHeight = this.computedHeight(this.editorView); } catch {} (_this$editorView$onDi = (_this$editorView6 = this.editorView).onDidContentSizeChange) === null || _this$editorView$onDi === void 0 || _this$editorView$onDi.call(_this$editorView6, () => { var _this$editorHeightMan, _this$editorHeightMan2; this._hasScrollBar = false; try { var _getScrollHeight, _ref, _getOption, _ref2; this.cachedScrollHeight = ((_getScrollHeight = (_ref = this.editorView).getScrollHeight) === null || _getScrollHeight === void 0 ? void 0 : _getScrollHeight.call(_ref)) ?? null; this.cachedLineHeight = ((_getOption = (_ref2 = this.editorView).getOption) === null || _getOption === void 0 ? void 0 : _getOption.call(_ref2, monaco$2.editor.EditorOption.lineHeight)) ?? null; this.cachedComputedHeight = this.computedHeight(this.editorView); } catch {} if ((_this$editorHeightMan = this.editorHeightManager) === null || _this$editorHeightMan === void 0 ? void 0 : _this$editorHeightMan.isSuppressed()) return; (_this$editorHeightMan2 = this.editorHeightManager) === null || _this$editorHeightMan2 === void 0 || _this$editorHeightMan2.update(); }); this.editorView.onDidChangeModelContent(() => { try { this.lastKnownCode = this.editorView.getValue(); } catch {} }); this.shouldAutoScroll = !!this.autoScrollInitial; if (this.scrollWatcher) { try { this.scrollWatcher.dispose(); } catch {} this.scrollWatcher = null; } this.scrollWatcher = createScrollWatcherForEditor(this.editorView, { onPause: () => { this.shouldAutoScroll = false; }, onMaybeResume: () => { this.shouldAutoScroll = this.userIsNearBottom(); }, getLast: () => this.lastScrollTop, setLast: (v) => { this.lastScrollTop = v; } }); this.maybeScrollToBottom(); return this.editorView; } updateCode(newCode, codeLanguage) { this.pendingUpdate = { code: newCode, lang: codeLanguage }; this.rafScheduler.schedule("update", () => this.flushPendingUpdate()); } flushPendingUpdate() { if (!this.pendingUpdate || !this.editorView) return; const model = this.editorView.getModel(); if (!model) return; const { code: newCode, lang: codeLanguage } = this.pendingUpdate; this.pendingUpdate = null; const processedCodeLanguage = processedLanguage(codeLanguage); const languageId = model.getLanguageId(); if (languageId !== processedCodeLanguage) { if (processedCodeLanguage) monaco$2.editor.setModelLanguage(model, processedCodeLanguage); const prevLineCount$1 = model.getLineCount(); model.setValue(newCode); this.lastKnownCode = newCode; const newLineCount$1 = model.getLineCount(); if (newLineCount$1 !== prevLineCount$1) this.maybeScrollToBottom(newLineCount$1); return; } const prevCode = this.lastKnownCode ?? this.editorView.getValue(); if (prevCode === newCode) return; if (newCode.startsWith(prevCode) && prevCode.length < newCode.length) { const suffix = newCode.slice(prevCode.length); if (suffix) this.appendCode(suffix, codeLanguage); this.lastKnownCode = newCode; return; } const prevLineCount = model.getLineCount(); this.applyMinimalEdit(prevCode, newCode); this.lastKnownCode = newCode; const newLineCount = model.getLineCount(); if (newLineCount !== prevLineCount) this.maybeScrollToBottom(newLineCount); } appendCode(appendText, codeLanguage) { if (!this.editorView) return; const model = this.editorView.getModel(); if (!model) return; const processedCodeLanguage = codeLanguage ? processedLanguage(codeLanguage) : model.getLanguageId(); if (processedCodeLanguage && model.getLanguageId() !== processedCodeLanguage) monaco$2.editor.setModelLanguage(model, processedCodeLanguage); const lastLine = model.getLineCount(); const lastColumn = model.getLineMaxColumn(lastLine); const range = new monaco$2.Range(lastLine, lastColumn, lastLine, lastColumn); const isReadOnly = this.editorView.getOption(monaco$2.editor.EditorOption.readOnly); if (isReadOnly) model.applyEdits([{ range, text: appendText, forceMoveMarkers: true }]); else this.editorView.executeEdits("append", [{ range, text: appendText, forceMoveMarkers: true }]); try { this.lastKnownCode = model.getValue(); } catch {} if (appendText) { this.appendBuffer.push(appendText); if (!this.appendBufferScheduled) { this.appendBufferScheduled = true; this.rafScheduler.schedule("append", () => this.flushAppendBuffer()); } } } applyMinimalEdit(prev, next) { if (!this.editorView) return; const model = this.editorView.getModel(); if (!model) return; const res = computeMinimalEdit(prev, next); if (!res) return; const { start, endPrevIncl, replaceText } = res; const rangeStart = model.getPositionAt(start); const rangeEnd = model.getPositionAt(endPrevIncl + 1); const range = new monaco$2.Range(rangeStart.lineNumber, rangeStart.column, rangeEnd.lineNumber, rangeEnd.column); const isReadOnly = this.editorView.getOption(monaco$2.editor.EditorOption.readOnly); const edit = [{ range, text: replaceText, forceMoveMarkers: true }]; if (isReadOnly) model.applyEdits(edit); else this.editorView.executeEdits("minimal-replace", edit); } flushAppendBuffer() { if (!this.editorView) return; if (this.appendBuffer.length === 0) return; this.appendBufferScheduled = false; const model = this.editorView.getModel(); if (!model) { this.appendBuffer.length = 0; return; } const text = this.appendBuffer.join(""); this.appendBuffer.length = 0; try { const lastLine = model.getLineCount(); const lastColumn = model.getLineMaxColumn(lastLine); const range = new monaco$2.Range(lastLine, lastColumn, lastLine, lastColumn); const isReadOnly = this.editorView.getOption(monaco$2.editor.EditorOption.readOnly); if (isReadOnly) model.applyEdits([{ range, text, forceMoveMarkers: true }]); else this.editorView.executeEdits("append", [{ range, text, forceMoveMarkers: true }]); if (this.lastKnownCode != null) this.lastKnownCode = this.lastKnownCode + text; try { this.maybeScrollToBottom(model.getLineCount()); } catch {} } catch {} } setLanguage(language, languages$1) { if (languages$1.includes(language)) { if (this.editorView) { const model = this.editorView.getModel(); if (model && model.getLanguageId() !== language) monaco$2.editor.setModelLanguage(model, language); } } else console.warn(`Language "${language}" is not registered. Available languages: ${languages$1.join(", ")}`); } getEditorView() { return this.editorView; } cleanup() { this.rafScheduler.cancel("update"); this.pendingUpdate = null; this.rafScheduler.cancel("append"); this.appendBufferScheduled = false; this.appendBuffer.length = 0; if (this.editorView) { this.editorView.dispose(); this.editorView = null; } this.lastKnownCode = null; if (this.lastContainer) { this.lastContainer.innerHTML = ""; this.lastContainer = null; } if (this.scrollWatcher) { try { this.scrollWatcher.dispose(); } catch {} this.scrollWatcher = null; } if (this.editorHeightManager) { try { this.editorHeightManager.dispose(); } catch {} this.editorHeightManager = null; } } safeClean() { this.rafScheduler.cancel("update"); this.pendingUpdate = null; if (this.scrollWatcher) { try { this.scrollWatcher.dispose(); } catch {} this.scrollWatcher = null; } this._hasScrollBar = false; this.shouldAutoScroll = !!this.autoScrollInitial; this.lastScrollTop = 0; if (this.editorHeightManager) { try { this.editorHeightManager.dispose(); } catch {} this.editorHeightManager = null; } } }; //#endregion //#region src/isDark.ts const isDark = useDark(); //#endregion //#region src/preloadMonacoWorkers.ts async function preloadMonacoWorkers(options) { if (typeof window === "undefined" || typeof document === "undefined") return; const workerUrlJson = new URL("monaco-editor/esm/vs/language/json/json.worker.js", import.meta.url); const workerUrlCss = new URL("monaco-editor/esm/vs/language/css/css.worker.js", import.meta.url); const workerUrlHtml = new URL("monaco-editor/esm/vs/language/html/html.worker.js", import.meta.url); const workerUrlTs = new URL("monaco-editor/esm/vs/language/typescript/ts.worker.js", import.meta.url); const workerUrlEditor = new URL("monaco-editor/esm/vs/editor/editor.worker.js", import.meta.url); const unique = Array.from(new Set([ String(workerUrlJson), String(workerUrlCss), String(workerUrlHtml), String(workerUrlTs), String(workerUrlEditor) ])); const workerUrlByLabel = { json: workerUrlJson, css: workerUrlCss, scss: workerUrlCss, less: workerUrlCss, html: workerUrlHtml, handlebars: workerUrlHtml, razor: workerUrlHtml, typescript: workerUrlTs, javascript: workerUrlTs }; try { for (const href of unique) if (!document.querySelector(`link[rel="modulepreload"][href="${href}"]`)) { const link = document.createElement("link"); link.rel = "modulepreload"; link.href = href; document.head.appendChild(link); } if (options === null || options === void 0 ? void 0 : options.fetch) await Promise.all(unique.map((u) => fetch(u, { method: "GET", cache: "force-cache" }).catch(() => void 0))); self.MonacoEnvironment = { getWorker(_, label) { const url = workerUrlByLabel[label] ?? workerUrlEditor; return new Worker(url, { type: "module" }); } }; } catch {} } //#endregion //#region src/utils/arraysEqual.ts function arraysEqual(a, b) { if (a === b) return true; if (!a || !b) return false; if (a.length !== b.length) return false; for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false; return true; } //#endregion //#region src/utils/registerMonacoThemes.ts let themesRegistered = false; let languagesRegistered = false; let currentThemes = []; let currentLanguages = []; let themeRegisterPromise = null; function setThemeRegisterPromise(p) { return themeRegisterPromise = p; } async function registerMonacoThemes(themes, languages$1) { registerMonacoLanguages(languages$1); if (themesRegistered && arraysEqual(themes, currentThemes) && arraysEqual(languages$1, currentLanguages)) return; try { const highlighter = await createHighlighter({ themes, langs: languages$1 }); shikiToMonaco(highlighter, monaco$1); themesRegistered = true; currentThemes = themes; currentLanguages = languages$1; } catch (e) { themeRegisterPromise = null; throw e; } } function registerMonacoLanguages(languages$1) { if (languagesRegistered && arraysEqual(languages$1, currentLanguages)) return; const existing = new Set(monaco$1.languages.getLanguages().map((l) => l.id)); for (const lang of languages$1) if (!existing.has(lang)) try { monaco$1.languages.register({ id: lang }); } catch {} languagesRegistered = true; currentLanguages = languages$1; } //#endregion //#region src/index.ts preloadMonacoWorkers(); const disposals = []; /** * useMonaco 组合式函数 * * 提供 Monaco 编辑器的创建、销毁、内容/主题/语言更新等能力。 * 支持主题自动切换、语言高亮、代码更新等功能。 * * @param {MonacoOptions} [monacoOptions] - 编辑器初始化配置,支持 Monaco 原生配置及扩展项 * @param {number | string} [monacoOptions.MAX_HEIGHT] - 编辑器最大高度,可以是数字(像素)或 CSS 字符串(如 '100%', 'calc(100vh - 100px)') * @param {boolean} [monacoOptions.readOnly] - 是否为只读模式 * @param {MonacoTheme[]} [monacoOptions.themes] - 主题数组,至少包含两个主题:[暗色主题, 亮色主题] * @param {MonacoLanguage[]} [monacoOptions.languages] - 支持的编程语言数组 * @param {string} [monacoOptions.theme] - 初始主题名称 * @param {boolean} [monacoOptions.isCleanOnBeforeCreate] - 是否在创建前清理之前注册的资源, 默认为 true * @param {(monaco: typeof import('monaco-editor')) => monaco.IDisposable[]} [monacoOptions.onBeforeCreate] - 编辑器创建前的钩子函数 * * @returns {{ * createEditor: (container: HTMLElement, code: string, language: string) => Promise<monaco.editor.IStandaloneCodeEditor>, * createDiffEditor: ( * container: HTMLElement, * originalCode: string, * modifiedCode: string, * language: string, * ) => Promise<monaco.editor.IStandaloneDiffEditor>, * cleanupEditor: () => void, * updateCode: (newCode: string, codeLanguage: string) => void, * appendCode: (appendText: string, codeLanguage?: string) => void, * updateDiff: ( * originalCode: string, * modifiedCode: string, * codeLanguage?: string, * ) => void, * updateOriginal: (newCode: string, codeLanguage?: string) => void, * updateModified: (newCode: string, codeLanguage?: string) => void, * appendOriginal: (appendText: string, codeLanguage?: string) => void, * appendModified: (appendText: string, codeLanguage?: string) => void, * setTheme: (theme: MonacoTheme) => void, * setLanguage: (language: MonacoLanguage) => void, * getCurrentTheme: () => string, * getEditor: () => typeof monaco.editor, * getEditorView: () => monaco.editor.IStandaloneCodeEditor | null, * getDiffEditorView: () => monaco.editor.IStandaloneDiffEditor | null, * getDiffModels: () => { original: monaco.editor.ITextModel | null, modified: monaco.editor.ITextModel | null }, * }} 返回对象包含以下方法和属性: * * @property {Function} createEditor - 创建并挂载 Monaco 编辑器到指定容器 * @property {Function} cleanupEditor - 销毁编辑器并清理容器 * @property {Function} updateCode - 更新编辑器内容和语言,必要时滚动到底部 * @property {Function} appendCode - 在编辑器末尾追加文本,必要时滚动到底部 * @property {Function} createDiffEditor - 创建并挂载 Diff 编辑器 * @property {Function} updateDiff - 更新 Diff 编辑器的 original/modified 内容(RAF 合并、增量更新) * @property {Function} updateOriginal - 仅更新 Diff 的 original 内容(增量更新) * @property {Function} updateModified - 仅更新 Diff 的 modified 内容(增量更新) * @property {Function} appendOriginal - 在 Diff 的 original 末尾追加(显式流式场景) * @property {Function} appendModified - 在 Diff 的 modified 末尾追加(显式流式场景) * @property {Function} setTheme - 切换编辑器主题 * @property {Function} setLanguage - 切换编辑器语言 * @property {Function} getCurrentTheme - 获取当前主题名称 * @property {Function} getEditor - 获取 Monaco 的静态 editor 对象(用于静态方法调用) * @property {Function} getEditorView - 获取当前编辑器实例 * @property {Function} getDiffEditorView - 获取当前 Diff 编辑器实例 * @property {Function} getDiffModels - 获取 Diff 的 original/modified 两个模型 * * @throws {Error} 当主题数组不是数组或长度小于2时抛出错误 * * @example * ```typescript * import { useMonaco } from 'vue-use-monaco' * * const { createEditor, updateCode, setTheme } = useMonaco({ * themes: ['vitesse-dark', 'vitesse-light'], * languages: ['javascript', 'typescript'], * readOnly: false * }) * * // 创建编辑器 * const editor = await createEditor(containerRef.value, 'console.log("hello")', 'javascript') * * // 更新代码 * updateCode('console.log("world")', 'javascript') * * // 切换主题 * setTheme('vitesse-light') * ``` */ function useMonaco(monacoOptions = {}) { if (monacoOptions.isCleanOnBeforeCreate ?? true) disposals.forEach((d) => d.dispose()); if (monacoOptions.isCleanOnBeforeCreate ?? true) disposals.length = 0; let editorView = null; let editorMgr = null; let diffEditorView = null; let diffMgr = null; let originalModel = null; let modifiedModel = null; let _hasScrollBar = false; const themes = monacoOptions.themes ?? defaultThemes; if (!Array.isArray(themes) || themes.length < 2) throw new Error("Monaco themes must be an array with at least two themes: [darkTheme, lightTheme]"); const languages$1 = monacoOptions.languages ?? defaultLanguages; const MAX_HEIGHT = monacoOptions.MAX_HEIGHT ?? 500; const autoScrollOnUpdate = monacoOptions.autoScrollOnUpdate ?? true; const autoScrollInitial = monacoOptions.autoScrollInitial ?? true; const autoScrollThresholdPx = monacoOptions.autoScrollThresholdPx ?? 32; const autoScrollThresholdLines = monacoOptions.autoScrollThresholdLines ?? 2; const diffAutoScroll = monacoOptions.diffAutoScroll ?? true; const getMaxHeightValue = () => { if (typeof MAX_HEIGHT === "number") return MAX_HEIGHT; const match = MAX_HEIGHT.match(/^(\d+(?:\.\d+)?)/); return match ? Number.parseFloat(match[1]) : 500; }; const getMaxHeightCSS = () => { if (typeof MAX_HEIGHT === "number") return `${MAX_HEIGHT}px`; return MAX_HEIGHT; }; const maxHeightValue = getMaxHeightValue(); const maxHeightCSS = getMaxHeightCSS(); let lastContainer = null; let lastKnownCode = null; let pendingUpdate = null; let shouldAutoScroll = true; let scrollWatcher = null; const cachedComputedHeight = null; const appendBuffer = []; let appendBufferScheduled = false; let lastAppliedTheme = null; const currentTheme = computed(() => isDark.value ? typeof themes[0] === "string" ? themes[0] : themes[0].name : typeof themes[1] === "string" ? themes[1] : themes[1].name); let themeWatcher = null; const rafScheduler = createRafScheduler(); function hasVerticalScrollbar() { try { if (!editorView) return false; if (_hasScrollBar) return true; const ch = cachedComputedHeight ?? computedHeight(editorView); return _hasScrollBar = editorView.getScrollHeight() > ch + padding / 2; } catch { return false; } } function maybeScrollToBottom(targetLine) { if (autoScrollOnUpdate && shouldAutoScroll && hasVerticalScrollbar()) { const model = editorView.getModel(); const line = targetLine ?? (model === null || model === void 0 ? void 0 : model.getLineCount()) ?? 1; rafScheduler.schedule("reveal", () => { try { editorView.revealLine(line); } catch {} }); } } async function createEditor(container, code, language) { cleanupEditor(); lastContainer = container; if (monacoOptions.isCleanOnBeforeCreate ?? true) { disposals.forEach((d) => d.dispose()); disposals.length = 0; } if (monacoOptions.onBeforeCreate) { const ds = monacoOptions.onBeforeCreate(monaco); if (ds) disposals.push(...ds); } if (themeWatcher) { themeWatcher(); themeWatcher = null; } await setThemeRegisterPromise(registerMonacoThemes(themes, languages$1)); editorMgr = new EditorManager(monacoOptions, maxHeightValue, maxHeightCSS, autoScrollOnUpdate, autoScrollInitial, autoScrollThresholdPx, autoScrollThresholdLines); editorView = await editorMgr.createEditor(container, code, language, currentTheme.value); themeWatcher = watch(() => isDark.value, () => { if (currentTheme.value !== lastAppliedTheme) { monaco.editor.setTheme(currentTheme.value); lastAppliedTheme = currentTheme.value; } }, { flush: "post", immediate: true }); try { if (editorView) lastKnownCode = editorView.getValue(); } catch {} return editorView; } function computedHeight(editorView$1) { var _getModel; const lineCount = ((_getModel = editorView$1.getModel()) === null || _getModel === void 0 ? void 0 : _getModel.getLineCount()) ?? 1; const lineHeight = editorView$1.getOption(monaco.editor.EditorOption.lineHeight); const height = Math.min(lineCount * lineHeight + padding, maxHeightValue); return height; } async function createDiffEditor(container, originalCode, modifiedCode, language) { cleanupEditor(); lastContainer = container; if (monacoOptions.isCleanOnBeforeCreate ?? true) { disposals.forEach((d) => d.dispose()); disposals.length = 0; } if (monacoOptions.onBeforeCreate) { const ds = monacoOptions.onBeforeCreate(monaco); if (ds) disposals.push(...ds); } if (themeWatcher) { themeWatcher();