From aad1400be9675c433918d3f11be0934fe789325d Mon Sep 17 00:00:00 2001 From: zsTree <233546082+def-WA2025@users.noreply.github.com> Date: Sat, 30 May 2026 23:13:50 +0800 Subject: [PATCH 01/13] Use Monaco Editor and save code in submitpage.php when edit the content. --- XMOJ.user.js | 221 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 194 insertions(+), 27 deletions(-) diff --git a/XMOJ.user.js b/XMOJ.user.js index 8b13e45a..56474dfc 100644 --- a/XMOJ.user.js +++ b/XMOJ.user.js @@ -240,6 +240,162 @@ let GetUserBadge = async (Username) => { } } }; +async function ensureMonaco() { + if (typeof monaco !== 'undefined') return; + const loaderUrl = 'https://cdn.jsdelivr.net/npm/monaco-editor@0.55.1/min/vs/loader.js'; + if (typeof require === 'undefined' || typeof require.config === 'undefined') { + await new Promise((resolve, reject) => { + const s = document.createElement('script'); + s.src = loaderUrl; + s.onload = () => { + try { require.config({ paths: { vs: 'https://cdn.jsdelivr.net/npm/monaco-editor@0.55.1/min/vs' } }); } catch (e) {} + resolve(); + }; + s.onerror = reject; + document.head.appendChild(s); + }); + } else { + try { require.config({ paths: { vs: 'https://cdn.jsdelivr.net/npm/monaco-editor@0.55.1/min/vs' } }); } catch (e) {} + } + await new Promise((resolve) => { + try { + require(['vs/editor/editor.main'], function() { resolve(); }); + } catch (e) { + const check = setInterval(() => { if (typeof monaco !== 'undefined') { clearInterval(check); resolve(); } }, 50); + } + }); + if (!document.getElementById('monaco-custom-style')) { + const style = document.createElement('style'); + style.id = 'monaco-custom-style'; + style.textContent = ` +/* rounded corners + find/replace/goto UI tweaks */ +.monaco-editor, .monaco-editor .margin, .monaco-editor .overflow-guard, .monaco-editor .monaco-editor-background { + border-radius: 8px !important; +} +.monaco-editor .find-widget, .monaco-editor .replace-widget, .monaco-editor .gotoLine-widget { + border-radius: 8px !important; + overflow: hidden; +} +.monaco-editor .monaco-scrollable-element .monaco-editor-background { + border-radius: 8px !important; +} + `; + document.head.appendChild(style); + } +} + +async function createMonacoEditor(containerOrId, options = {}) { + await ensureMonaco(); + let container = null; + if (typeof containerOrId === 'string') container = document.getElementById(containerOrId); + else container = containerOrId; + if (!container) throw new Error('Monaco container not found'); + if (!container.id) container.id = 'monaco-' + Math.random().toString(36).slice(2,9); + const key = options.localStorageKey || ('XMOJ-Monaco-' + location.pathname + ':' + container.id); + const theme = options.theme || (typeof UtilityEnabled === 'function' && UtilityEnabled("DarkMode") ? 'vs-dark' : 'vs'); + const readOnly = !!options.readOnly; + const language = options.language || (options.mode === 'text/x-c++src' ? 'cpp' : (options.mode || 'cpp')); + const editor = monaco.editor.create(container, { + value: options.value || '', + language: language, + automaticLayout: !!options.automaticLayout, + theme: theme, + minimap: (typeof options.minimap !== 'undefined' ? options.minimap : { enabled: false }), + readOnly: readOnly, + lineNumbers: typeof options.lineNumbers !== 'undefined' ? options.lineNumbers : 'on', + tabSize: options.tabSize || 4 + }); + try { + if (options.restoreOnLoad !== false) { + const saved = localStorage.getItem(key); + if (saved !== null && saved !== 'null') editor.setValue(saved); + } + } catch (e) {} + let saveTimer = null; + const doSave = () => { try { localStorage.setItem(key, editor.getValue()); } catch (e) {} }; + editor.onDidChangeModelContent(() => { if (saveTimer) clearTimeout(saveTimer); saveTimer = setTimeout(doSave, options.saveDebounce || 500); }); + const adapter = { + getValue: () => editor.getValue(), + setValue: (v) => { editor.setValue(v); }, + setSize: (w, h) => { const el = container; if (w) el.style.width = w; if (h) { if (h === 'auto') { try { const lines = editor.getModel().getLineCount(); el.style.height = Math.max(80, Math.min(1200, lines * 18)) + 'px'; } catch (e) { el.style.height = typeof h === 'number' ? h + 'px' : h; } } else el.style.height = h; } try { editor.layout(); } catch (e) {} }, + getWrapperElement: () => container, + focus: () => { try { editor.focus(); } catch (e) {} }, + _monacoEditor: editor, + showFind: () => { try { editor.trigger('', 'editor.action.startFindReplaceAction'); } catch (e) {} }, + goToLine: (line) => { try { editor.setPosition({ lineNumber: parseInt(line) || 1, column: 1 }); editor.revealPositionInCenter({ lineNumber: parseInt(line) || 1, column: 1 }); editor.focus(); } catch (e) {} }, + selectRange: (sLine, sCol, eLine, eCol) => { try { editor.setSelection({ startLineNumber: sLine, startColumn: sCol, endLineNumber: eLine, endColumn: eCol }); editor.revealRangeInCenter({ startLineNumber: sLine, startColumn: sCol, endLineNumber: eLine, endColumn: eCol }); } catch (e) {} }, + saveToLocal: doSave, + localStorageKey: key + }; + return adapter; +} + +(function() { + const shim = function(containerOrTextArea, options) { + let container = containerOrTextArea; + let initialValue = ''; + if (container && container.tagName && container.tagName.toLowerCase() === 'textarea') { + initialValue = container.value || container.textContent || ''; + const div = document.createElement('div'); + div.className = 'codemirror-shim-host'; + container.parentNode.replaceChild(div, container); + container = div; + } else if (typeof containerOrTextArea === 'string') { + container = document.querySelector(containerOrTextArea) || document.getElementById(containerOrTextArea); + } + if (!container) { container = document.createElement('div'); document.body.appendChild(container); } + container._cmValue = (options && options.value) ? options.value : initialValue; + const placeholderAdapter = { + getValue: () => container._cmValue || '', + setValue: (v) => { container._cmValue = v; if (container._cmEditor) try { container._cmEditor.setValue(v); } catch (e) {} }, + setSize: (w, h) => { if (w) container.style.width = w; if (h) { if (h === 'auto') container.style.height = 'auto'; else container.style.height = h; } if (container._cmEditor) try { container._cmEditor.layout(); } catch (e) {} }, + getWrapperElement: () => container, + focus: () => { if (container._cmEditor) try { container._cmEditor.focus(); } catch (e) {} }, + _monacoEditor: null + }; + (async () => { + try { + const opts = options || {}; + await ensureMonaco(); + const lang = opts.mode === 'text/x-c++src' || (opts.mode && opts.mode.indexOf('c++') !== -1) ? 'cpp' : (opts.language || 'cpp'); + const monacoAdapter = await createMonacoEditor(container, Object.assign({ language: lang, value: container._cmValue || '', readOnly: !!opts.readOnly, theme: (typeof UtilityEnabled === 'function' && UtilityEnabled("DarkMode") ? 'vs-dark' : 'vs') }, opts)); + container._cmEditor = monacoAdapter._monacoEditor; + placeholderAdapter.getValue = monacoAdapter.getValue; + placeholderAdapter.setValue = monacoAdapter.setValue; + placeholderAdapter.setSize = monacoAdapter.setSize; + placeholderAdapter.getWrapperElement = monacoAdapter.getWrapperElement; + placeholderAdapter.focus = monacoAdapter.focus; + placeholderAdapter._monacoEditor = monacoAdapter._monacoEditor; + } catch (e) { console.error(e); } + })(); + return placeholderAdapter; + }; + shim.fromTextArea = function(textarea, options) { return shim(textarea, options); }; + shim.MergeView = function(container, options) { + let el = container; + if (typeof container === 'string') el = document.getElementById(container) || document.querySelector(container); + if (!el) { el = document.createElement('div'); document.body.appendChild(el); } + const wrapper = { ignoreWhitespace: !!(options && options.ignoreWhitespace), _diffEditor: null, _originalModel: null, _modifiedModel: null }; + (async () => { + try { + await ensureMonaco(); + const diffEditor = monaco.editor.createDiffEditor(el, { readOnly: !!(options && options.readOnly), theme: (typeof UtilityEnabled === 'function' && UtilityEnabled("DarkMode") ? 'vs-dark' : 'vs'), minimap: { enabled: false }, automaticLayout: true }); + const orig = options && options.value ? options.value : ''; + const mod = options && options.orig ? options.orig : ''; + const origVal = wrapper.ignoreWhitespace ? orig.replace(/\s+/g,' ') : orig; + const modVal = wrapper.ignoreWhitespace ? mod.replace(/\s+/g,' ') : mod; + const originalModel = monaco.editor.createModel(origVal, 'cpp'); + const modifiedModel = monaco.editor.createModel(modVal, 'cpp'); + diffEditor.setModel({ original: originalModel, modified: modifiedModel }); + wrapper._diffEditor = diffEditor; + wrapper._originalModel = originalModel; + wrapper._modifiedModel = modifiedModel; + } catch (e) { console.error(e); } + })(); + return wrapper; + }; + window.CodeMirror = shim; +})(); /** * Sets the HTML content of an element to display a username with optional additional information. * @param {HTMLElement} Element - The element to set the HTML content. @@ -3475,7 +3631,8 @@ async function main() { } else if (location.pathname == "/submitpage.php") { document.title = "提交代码: " + (SearchParams.get("id") != null ? "题目" + Number(SearchParams.get("id")) : "比赛" + Number(SearchParams.get("cid"))); document.querySelector("body > div > div.mt-3").innerHTML = `
` + `

提交代码

` + (SearchParams.get("id") != null ? `题目${Number(SearchParams.get("id"))}` : `比赛${Number(SearchParams.get("cid")) + ` 题目` + String.fromCharCode(65 + parseInt(SearchParams.get("pid")))}`) + `
- +
+

@@ -3489,24 +3646,21 @@ async function main() { document.querySelector("#enable_O2").checked = true; } let CodeMirrorElement; - (() => { - CodeMirrorElement = CodeMirror.fromTextArea(document.querySelector("#CodeInput"), { - lineNumbers: true, - matchBrackets: true, - mode: "text/x-c++src", - indentUnit: 4, - indentWithTabs: true, - enterMode: "keep", - tabMode: "shift", - theme: (UtilityEnabled("DarkMode") ? "darcula" : "default"), - extraKeys: { - "Ctrl-Space": "autocomplete", "Ctrl-Enter": function (instance) { - Submit.click(); - } - } - }) - })(); - CodeMirrorElement.setSize("100%", "auto"); + CodeMirrorElement = await createMonacoEditor('MonacoEditor', { + language: 'cpp', + value: '', + automaticLayout: true, + theme: (UtilityEnabled("DarkMode") ? 'vs-dark' : 'vs'), + minimap: { enabled: false }, + lineNumbers: 'on', + tabSize: 4, + localStorageKey: (SearchParams.get("id") != null ? ('XMOJ-Submit-id-' + SearchParams.get("id")) : ('XMOJ-Submit-cid-' + SearchParams.get("cid") + '-pid-' + SearchParams.get("pid"))) + }); + try { + CodeMirrorElement._monacoEditor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter, function() { Submit.click(); }); + CodeMirrorElement._monacoEditor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.Space, function() { CodeMirrorElement._monacoEditor.trigger('keyboard', 'editor.action.triggerSuggest', {}); }); + } catch (e) {} + CodeMirrorElement.setSize("100%", "400px"); CodeMirrorElement.getWrapperElement().style.border = UtilityEnabled("MonochromeUI") ? "2px solid var(--mono-black)" : "1px solid #ddd"; if (SearchParams.get("sid") !== null) { @@ -3641,14 +3795,27 @@ async function main() { }, 1500); }); document.getElementById('ErrorMessage').appendChild(copyFreopenButton); - let freopenCodeField = CodeMirror(document.getElementById('ErrorMessage'), { - value: 'freopen("' + IOFilename + '.in", "r", stdin);\nfreopen("' + IOFilename + '.out", "w", stdout);', - mode: 'text/x-c++src', - theme: (UtilityEnabled("DarkMode") ? "darcula" : "default"), - readOnly: true, - lineNumbers: true - }); - freopenCodeField.setSize("100%", "auto"); + // create a small read-only Monaco editor or fallback text for the freopen snippet + let codeHost = document.createElement('div'); + codeHost.style.width = '100%'; + codeHost.style.height = '80px'; + codeHost.style.marginTop = '10px'; + document.getElementById('ErrorMessage').appendChild(codeHost); + if (typeof monaco !== 'undefined') { + monaco.editor.create(codeHost, { + value: 'freopen("' + IOFilename + '.in", "r", stdin);\nfreopen("' + IOFilename + '.out", "w", stdout);', + language: 'cpp', + readOnly: true, + theme: (UtilityEnabled("DarkMode") ? 'vs-dark' : 'vs'), + automaticLayout: true, + minimap: { enabled: false }, + lineNumbers: 'on' + }); + } else { + const pre = document.createElement('pre'); + pre.textContent = 'freopen("' + IOFilename + '.in", "r", stdin);\nfreopen("' + IOFilename + '.out", "w", stdout);'; + document.getElementById('ErrorMessage').appendChild(pre); + } document.querySelector("#Submit").disabled = false; document.querySelector("#Submit").value = "提交"; return false; From 9a6993160fa36c9ee858e5113dd1a64ed863cab8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 30 May 2026 15:17:17 +0000 Subject: [PATCH 02/13] 3.5.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9538b928..b1f5181a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "xmoj-script", - "version": "3.5.1", + "version": "3.5.2", "description": "an improvement script for xmoj.tech", "main": "AddonScript.js", "scripts": { From 6f7cc75c9013efae091c31a1407822af22e4ef1a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 30 May 2026 15:17:24 +0000 Subject: [PATCH 03/13] Update version info to 3.5.2 --- Update.json | 11 +++++++++++ XMOJ.user.js | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Update.json b/Update.json index 4eebcd48..bd6c62ea 100644 --- a/Update.json +++ b/Update.json @@ -3627,6 +3627,17 @@ } ], "Notes": "No release notes were provided for this release." + }, + "3.5.2": { + "UpdateDate": 1780154238096, + "Prerelease": true, + "UpdateContents": [ + { + "PR": 989, + "Description": "Use Monaco Editor and save code in submitpage.php when edit the content." + } + ], + "Notes": "Use Monaco Editor and save code in submitpage.php when edit the content." } } } \ No newline at end of file diff --git a/XMOJ.user.js b/XMOJ.user.js index 56474dfc..0b12cd3c 100644 --- a/XMOJ.user.js +++ b/XMOJ.user.js @@ -1,6 +1,6 @@ // ==UserScript== // @name XMOJ -// @version 3.5.1 +// @version 3.5.2 // @description XMOJ增强脚本 // @author @XMOJ-Script-dev, @langningchen and the community // @namespace https://github/langningchen From 2623c04432507520923c7911ee2928a970840a4d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 31 May 2026 08:17:53 +0000 Subject: [PATCH 04/13] Fix AI-detected bugs: Monaco timeout, MergeView language, XSS, fallback editor, package.json version --- XMOJ.user.js | 80 +++++++++++++++++++++++++++++++++++++++------------- package.json | 2 +- 2 files changed, 62 insertions(+), 20 deletions(-) diff --git a/XMOJ.user.js b/XMOJ.user.js index 0b12cd3c..2ee4c03f 100644 --- a/XMOJ.user.js +++ b/XMOJ.user.js @@ -257,11 +257,12 @@ async function ensureMonaco() { } else { try { require.config({ paths: { vs: 'https://cdn.jsdelivr.net/npm/monaco-editor@0.55.1/min/vs' } }); } catch (e) {} } - await new Promise((resolve) => { + await new Promise((resolve, reject) => { try { require(['vs/editor/editor.main'], function() { resolve(); }); } catch (e) { const check = setInterval(() => { if (typeof monaco !== 'undefined') { clearInterval(check); resolve(); } }, 50); + setTimeout(() => { clearInterval(check); reject(new Error('Monaco load timeout')); }, 30000); } }); if (!document.getElementById('monaco-custom-style')) { @@ -384,8 +385,9 @@ async function createMonacoEditor(containerOrId, options = {}) { const mod = options && options.orig ? options.orig : ''; const origVal = wrapper.ignoreWhitespace ? orig.replace(/\s+/g,' ') : orig; const modVal = wrapper.ignoreWhitespace ? mod.replace(/\s+/g,' ') : mod; - const originalModel = monaco.editor.createModel(origVal, 'cpp'); - const modifiedModel = monaco.editor.createModel(modVal, 'cpp'); + const lang = (options && options.mode === 'text/x-c++src') || (options && options.mode && options.mode.indexOf('c++') !== -1) ? 'cpp' : (options && options.language || 'cpp'); + const originalModel = monaco.editor.createModel(origVal, lang); + const modifiedModel = monaco.editor.createModel(modVal, lang); diffEditor.setModel({ original: originalModel, modified: modifiedModel }); wrapper._diffEditor = diffEditor; wrapper._originalModel = originalModel; @@ -3630,7 +3632,7 @@ async function main() { } } else if (location.pathname == "/submitpage.php") { document.title = "提交代码: " + (SearchParams.get("id") != null ? "题目" + Number(SearchParams.get("id")) : "比赛" + Number(SearchParams.get("cid"))); - document.querySelector("body > div > div.mt-3").innerHTML = `
` + `

提交代码

` + (SearchParams.get("id") != null ? `题目${Number(SearchParams.get("id"))}` : `比赛${Number(SearchParams.get("cid")) + ` 题目` + String.fromCharCode(65 + parseInt(SearchParams.get("pid")))}`) + `
+ document.querySelector("body > div > div.mt-3").innerHTML = `
@@ -3642,26 +3644,66 @@ async function main() {
`; + (function() { + const _header = document.getElementById('_submitPageHeader'); + const _h3 = document.createElement('h3'); + _h3.textContent = '提交代码'; + _header.appendChild(_h3); + if (SearchParams.get("id") != null) { + _header.appendChild(document.createTextNode('题目')); + const _idSpan = document.createElement('span'); + _idSpan.className = 'blue'; + _idSpan.textContent = String(Number(SearchParams.get("id"))); + _header.appendChild(_idSpan); + } else { + _header.appendChild(document.createTextNode('比赛')); + const _cidSpan = document.createElement('span'); + _cidSpan.className = 'blue'; + _cidSpan.textContent = String(Number(SearchParams.get("cid"))); + _header.appendChild(_cidSpan); + _header.appendChild(document.createTextNode('\u2003题目')); + const _pidSpan = document.createElement('span'); + _pidSpan.className = 'blue'; + _pidSpan.textContent = String.fromCharCode(65 + parseInt(SearchParams.get("pid"))); + _header.appendChild(_pidSpan); + } + })(); if (UtilityEnabled("AutoO2")) { document.querySelector("#enable_O2").checked = true; } let CodeMirrorElement; - CodeMirrorElement = await createMonacoEditor('MonacoEditor', { - language: 'cpp', - value: '', - automaticLayout: true, - theme: (UtilityEnabled("DarkMode") ? 'vs-dark' : 'vs'), - minimap: { enabled: false }, - lineNumbers: 'on', - tabSize: 4, - localStorageKey: (SearchParams.get("id") != null ? ('XMOJ-Submit-id-' + SearchParams.get("id")) : ('XMOJ-Submit-cid-' + SearchParams.get("cid") + '-pid-' + SearchParams.get("pid"))) - }); try { - CodeMirrorElement._monacoEditor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter, function() { Submit.click(); }); - CodeMirrorElement._monacoEditor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.Space, function() { CodeMirrorElement._monacoEditor.trigger('keyboard', 'editor.action.triggerSuggest', {}); }); - } catch (e) {} - CodeMirrorElement.setSize("100%", "400px"); - CodeMirrorElement.getWrapperElement().style.border = UtilityEnabled("MonochromeUI") ? "2px solid var(--mono-black)" : "1px solid #ddd"; + CodeMirrorElement = await createMonacoEditor('MonacoEditor', { + language: 'cpp', + value: '', + automaticLayout: true, + theme: (UtilityEnabled("DarkMode") ? 'vs-dark' : 'vs'), + minimap: { enabled: false }, + lineNumbers: 'on', + tabSize: 4, + localStorageKey: (SearchParams.get("id") != null ? ('XMOJ-Submit-id-' + SearchParams.get("id")) : ('XMOJ-Submit-cid-' + SearchParams.get("cid") + '-pid-' + SearchParams.get("pid"))) + }); + try { + CodeMirrorElement._monacoEditor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter, function() { Submit.click(); }); + CodeMirrorElement._monacoEditor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.Space, function() { CodeMirrorElement._monacoEditor.trigger('keyboard', 'editor.action.triggerSuggest', {}); }); + } catch (e) {} + CodeMirrorElement.setSize("100%", "400px"); + CodeMirrorElement.getWrapperElement().style.border = UtilityEnabled("MonochromeUI") ? "2px solid var(--mono-black)" : "1px solid #ddd"; + } catch (e) { + const _fallbackTA = document.getElementById('CodeInput'); + document.getElementById('MonacoEditor').style.display = 'none'; + _fallbackTA.style.display = ''; + _fallbackTA.style.width = '100%'; + _fallbackTA.style.height = '400px'; + CodeMirrorElement = { + getValue: () => _fallbackTA.value, + setValue: (v) => { _fallbackTA.value = v; }, + setSize: (w, h) => { if (w) _fallbackTA.style.width = w; if (h) _fallbackTA.style.height = h; }, + getWrapperElement: () => _fallbackTA, + focus: () => _fallbackTA.focus(), + _monacoEditor: null + }; + } if (SearchParams.get("sid") !== null) { await fetch("https://www.xmoj.tech/getsource.php?id=" + SearchParams.get("sid")) diff --git a/package.json b/package.json index b1f5181a..9538b928 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "xmoj-script", - "version": "3.5.2", + "version": "3.5.1", "description": "an improvement script for xmoj.tech", "main": "AddonScript.js", "scripts": { From a400ee2008f13e15edfceee299430ba46ae4b8f8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 31 May 2026 08:19:11 +0000 Subject: [PATCH 05/13] Fix code review issues: MergeView language expression, em space comment, fallback interface stubs --- XMOJ.user.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/XMOJ.user.js b/XMOJ.user.js index 2ee4c03f..0d4ec838 100644 --- a/XMOJ.user.js +++ b/XMOJ.user.js @@ -385,7 +385,8 @@ async function createMonacoEditor(containerOrId, options = {}) { const mod = options && options.orig ? options.orig : ''; const origVal = wrapper.ignoreWhitespace ? orig.replace(/\s+/g,' ') : orig; const modVal = wrapper.ignoreWhitespace ? mod.replace(/\s+/g,' ') : mod; - const lang = (options && options.mode === 'text/x-c++src') || (options && options.mode && options.mode.indexOf('c++') !== -1) ? 'cpp' : (options && options.language || 'cpp'); + const isCpp = (options && options.mode === 'text/x-c++src') || (options && options.mode && options.mode.indexOf('c++') !== -1); + const lang = isCpp ? 'cpp' : (options && options.language || 'cpp'); const originalModel = monaco.editor.createModel(origVal, lang); const modifiedModel = monaco.editor.createModel(modVal, lang); diffEditor.setModel({ original: originalModel, modified: modifiedModel }); @@ -3661,7 +3662,7 @@ async function main() { _cidSpan.className = 'blue'; _cidSpan.textContent = String(Number(SearchParams.get("cid"))); _header.appendChild(_cidSpan); - _header.appendChild(document.createTextNode('\u2003题目')); + _header.appendChild(document.createTextNode('\u2003题目')); // \u2003 = em space const _pidSpan = document.createElement('span'); _pidSpan.className = 'blue'; _pidSpan.textContent = String.fromCharCode(65 + parseInt(SearchParams.get("pid"))); @@ -3701,6 +3702,11 @@ async function main() { setSize: (w, h) => { if (w) _fallbackTA.style.width = w; if (h) _fallbackTA.style.height = h; }, getWrapperElement: () => _fallbackTA, focus: () => _fallbackTA.focus(), + showFind: () => {}, + goToLine: () => {}, + selectRange: () => {}, + saveToLocal: () => {}, + localStorageKey: null, _monacoEditor: null }; } From 99a34a6b150d016ebf36a39335db3c1cac74f908 Mon Sep 17 00:00:00 2001 From: Shan Wenxiao Date: Sun, 31 May 2026 16:20:34 +0800 Subject: [PATCH 06/13] Bump version from 3.5.1 to 3.5.2 Signed-off-by: Shan Wenxiao --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9538b928..b1f5181a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "xmoj-script", - "version": "3.5.1", + "version": "3.5.2", "description": "an improvement script for xmoj.tech", "main": "AddonScript.js", "scripts": { From 61f2fd25737966f0f51f28efbcafb9648616861e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 31 May 2026 08:20:59 +0000 Subject: [PATCH 07/13] Update time and description of 3.5.2 --- Update.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Update.json b/Update.json index bd6c62ea..65a29c87 100644 --- a/Update.json +++ b/Update.json @@ -3629,7 +3629,7 @@ "Notes": "No release notes were provided for this release." }, "3.5.2": { - "UpdateDate": 1780154238096, + "UpdateDate": 1780215653791, "Prerelease": true, "UpdateContents": [ { From 07c6694c717cb3a6cbfa9969a3ef0ca40708191b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 31 May 2026 08:23:44 +0000 Subject: [PATCH 08/13] Fix constant condition: replace dead typeof check with fallback '80px' in setSize --- XMOJ.user.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/XMOJ.user.js b/XMOJ.user.js index 0d4ec838..45dd7140 100644 --- a/XMOJ.user.js +++ b/XMOJ.user.js @@ -318,7 +318,7 @@ async function createMonacoEditor(containerOrId, options = {}) { const adapter = { getValue: () => editor.getValue(), setValue: (v) => { editor.setValue(v); }, - setSize: (w, h) => { const el = container; if (w) el.style.width = w; if (h) { if (h === 'auto') { try { const lines = editor.getModel().getLineCount(); el.style.height = Math.max(80, Math.min(1200, lines * 18)) + 'px'; } catch (e) { el.style.height = typeof h === 'number' ? h + 'px' : h; } } else el.style.height = h; } try { editor.layout(); } catch (e) {} }, + setSize: (w, h) => { const el = container; if (w) el.style.width = w; if (h) { if (h === 'auto') { try { const lines = editor.getModel().getLineCount(); el.style.height = Math.max(80, Math.min(1200, lines * 18)) + 'px'; } catch (e) { el.style.height = '80px'; } } else el.style.height = h; } try { editor.layout(); } catch (e) {} }, getWrapperElement: () => container, focus: () => { try { editor.focus(); } catch (e) {} }, _monacoEditor: editor, From ed9cda4fc7dd24bf85ece21a11fc71b243a8aec1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 31 May 2026 08:25:49 +0000 Subject: [PATCH 09/13] Update time and description of 3.5.2 --- Update.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Update.json b/Update.json index 65a29c87..14c73eb8 100644 --- a/Update.json +++ b/Update.json @@ -3629,12 +3629,12 @@ "Notes": "No release notes were provided for this release." }, "3.5.2": { - "UpdateDate": 1780215653791, + "UpdateDate": 1780215944157, "Prerelease": true, "UpdateContents": [ { "PR": 989, - "Description": "Use Monaco Editor and save code in submitpage.php when edit the content." + "Description": "Use Monaco Editor and save code in submitpage.php when edit the content" } ], "Notes": "Use Monaco Editor and save code in submitpage.php when edit the content." From 92ddf2b1f184ae569351b107789387adc3993bdd Mon Sep 17 00:00:00 2001 From: Shan Wenxiao Date: Sun, 31 May 2026 16:30:07 +0800 Subject: [PATCH 10/13] Update XMOJ.user.js Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> Signed-off-by: Shan Wenxiao --- XMOJ.user.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/XMOJ.user.js b/XMOJ.user.js index 45dd7140..2ac9f7e8 100644 --- a/XMOJ.user.js +++ b/XMOJ.user.js @@ -258,11 +258,13 @@ async function ensureMonaco() { try { require.config({ paths: { vs: 'https://cdn.jsdelivr.net/npm/monaco-editor@0.55.1/min/vs' } }); } catch (e) {} } await new Promise((resolve, reject) => { + let check = null; + const done = () => { if (check) clearInterval(check); clearTimeout(timeout); resolve(); }; + const timeout = setTimeout(() => { if (check) clearInterval(check); reject(new Error('Monaco load timeout')); }, 30000); try { - require(['vs/editor/editor.main'], function() { resolve(); }); + require(['vs/editor/editor.main'], function() { done(); }); } catch (e) { - const check = setInterval(() => { if (typeof monaco !== 'undefined') { clearInterval(check); resolve(); } }, 50); - setTimeout(() => { clearInterval(check); reject(new Error('Monaco load timeout')); }, 30000); + check = setInterval(() => { if (typeof monaco !== 'undefined') done(); }, 50); } }); if (!document.getElementById('monaco-custom-style')) { From f3dfa90d90cbaef0bbb523b763efd3042608bf4d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 31 May 2026 08:30:34 +0000 Subject: [PATCH 11/13] Update time and description of 3.5.2 --- Update.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Update.json b/Update.json index 14c73eb8..f6661c30 100644 --- a/Update.json +++ b/Update.json @@ -3629,7 +3629,7 @@ "Notes": "No release notes were provided for this release." }, "3.5.2": { - "UpdateDate": 1780215944157, + "UpdateDate": 1780216228797, "Prerelease": true, "UpdateContents": [ { From 680c4a888d04de205bd32a65788416b739cebf8c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 31 May 2026 08:34:17 +0000 Subject: [PATCH 12/13] Fix 3 AI review bugs: ignoreWhitespace newlines, setSize replay, fallback persistence --- XMOJ.user.js | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/XMOJ.user.js b/XMOJ.user.js index 2ac9f7e8..d5bf7ebd 100644 --- a/XMOJ.user.js +++ b/XMOJ.user.js @@ -348,10 +348,11 @@ async function createMonacoEditor(containerOrId, options = {}) { } if (!container) { container = document.createElement('div'); document.body.appendChild(container); } container._cmValue = (options && options.value) ? options.value : initialValue; + let _lastSetSizeArgs = null; const placeholderAdapter = { getValue: () => container._cmValue || '', setValue: (v) => { container._cmValue = v; if (container._cmEditor) try { container._cmEditor.setValue(v); } catch (e) {} }, - setSize: (w, h) => { if (w) container.style.width = w; if (h) { if (h === 'auto') container.style.height = 'auto'; else container.style.height = h; } if (container._cmEditor) try { container._cmEditor.layout(); } catch (e) {} }, + setSize: (w, h) => { _lastSetSizeArgs = [w, h]; if (w) container.style.width = w; if (h) { if (h === 'auto') container.style.height = 'auto'; else container.style.height = h; } if (container._cmEditor) try { container._cmEditor.layout(); } catch (e) {} }, getWrapperElement: () => container, focus: () => { if (container._cmEditor) try { container._cmEditor.focus(); } catch (e) {} }, _monacoEditor: null @@ -369,6 +370,7 @@ async function createMonacoEditor(containerOrId, options = {}) { placeholderAdapter.getWrapperElement = monacoAdapter.getWrapperElement; placeholderAdapter.focus = monacoAdapter.focus; placeholderAdapter._monacoEditor = monacoAdapter._monacoEditor; + if (_lastSetSizeArgs) monacoAdapter.setSize(_lastSetSizeArgs[0], _lastSetSizeArgs[1]); } catch (e) { console.error(e); } })(); return placeholderAdapter; @@ -382,15 +384,13 @@ async function createMonacoEditor(containerOrId, options = {}) { (async () => { try { await ensureMonaco(); - const diffEditor = monaco.editor.createDiffEditor(el, { readOnly: !!(options && options.readOnly), theme: (typeof UtilityEnabled === 'function' && UtilityEnabled("DarkMode") ? 'vs-dark' : 'vs'), minimap: { enabled: false }, automaticLayout: true }); + const diffEditor = monaco.editor.createDiffEditor(el, { readOnly: !!(options && options.readOnly), theme: (typeof UtilityEnabled === 'function' && UtilityEnabled("DarkMode") ? 'vs-dark' : 'vs'), minimap: { enabled: false }, automaticLayout: true, ignoreTrimWhitespace: !!wrapper.ignoreWhitespace }); const orig = options && options.value ? options.value : ''; const mod = options && options.orig ? options.orig : ''; - const origVal = wrapper.ignoreWhitespace ? orig.replace(/\s+/g,' ') : orig; - const modVal = wrapper.ignoreWhitespace ? mod.replace(/\s+/g,' ') : mod; const isCpp = (options && options.mode === 'text/x-c++src') || (options && options.mode && options.mode.indexOf('c++') !== -1); const lang = isCpp ? 'cpp' : (options && options.language || 'cpp'); - const originalModel = monaco.editor.createModel(origVal, lang); - const modifiedModel = monaco.editor.createModel(modVal, lang); + const originalModel = monaco.editor.createModel(orig, lang); + const modifiedModel = monaco.editor.createModel(mod, lang); diffEditor.setModel({ original: originalModel, modified: modifiedModel }); wrapper._diffEditor = diffEditor; wrapper._originalModel = originalModel; @@ -3698,6 +3698,11 @@ async function main() { _fallbackTA.style.display = ''; _fallbackTA.style.width = '100%'; _fallbackTA.style.height = '400px'; + const _fallbackKey = SearchParams.get("id") != null ? ('XMOJ-Submit-id-' + SearchParams.get("id")) : ('XMOJ-Submit-cid-' + SearchParams.get("cid") + '-pid-' + SearchParams.get("pid")); + try { const _saved = localStorage.getItem(_fallbackKey); if (_saved !== null && _saved !== 'null') _fallbackTA.value = _saved; } catch (_e) {} + let _fallbackTimer = null; + const _fallbackSave = () => { try { localStorage.setItem(_fallbackKey, _fallbackTA.value); } catch (_e) {} }; + _fallbackTA.addEventListener('input', () => { if (_fallbackTimer) clearTimeout(_fallbackTimer); _fallbackTimer = setTimeout(_fallbackSave, 500); }); CodeMirrorElement = { getValue: () => _fallbackTA.value, setValue: (v) => { _fallbackTA.value = v; }, @@ -3707,8 +3712,8 @@ async function main() { showFind: () => {}, goToLine: () => {}, selectRange: () => {}, - saveToLocal: () => {}, - localStorageKey: null, + saveToLocal: _fallbackSave, + localStorageKey: _fallbackKey, _monacoEditor: null }; } From ca1cdbedbfba6a0702f758e954a80d2146cc344c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 31 May 2026 08:58:52 +0000 Subject: [PATCH 13/13] Update time and description of 3.5.2 --- Update.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Update.json b/Update.json index f6661c30..ee7882f6 100644 --- a/Update.json +++ b/Update.json @@ -3629,7 +3629,7 @@ "Notes": "No release notes were provided for this release." }, "3.5.2": { - "UpdateDate": 1780216228797, + "UpdateDate": 1780217926990, "Prerelease": true, "UpdateContents": [ {