// ==UserScript== // @name Codeforces Better! // @namespace https://greasyfork.org/users/747162 // @version 1.48 // @description Codeforces界面汉化、题目翻译,markdown视图,一键复制题目,跳转到洛谷 // @author 北极小狐 // @match *://*.codeforces.com/* // @connect www2.deepl.com // @connect m.youdao.com // @connect translate.google.com // @connect openai.api2d.net // @connect api.openai.com // @connect www.luogu.com.cn // @connect greasyfork.org // @connect * // @grant GM_xmlhttpRequest // @grant GM_info // @grant GM_setValue // @grant GM_getValue // @grant GM_addStyle // @grant GM_cookie // @grant GM_setClipboard // @icon https://aowuucdn.oss-cn-beijing.aliyuncs.com/codeforces.png // @require https://cdn.staticfile.org/turndown/7.1.2/turndown.min.js // @require https://cdn.staticfile.org/markdown-it/13.0.1/markdown-it.min.js // @license MIT // @compatible Chrome // @compatible Firefox // @compatible Edge // @downloadURL none // ==/UserScript== // 状态与初始化 const getGMValue = (key, defaultValue) => { const value = GM_getValue(key); if (value === undefined) { GM_setValue(key, defaultValue); return defaultValue; } return value; }; const is_mSite = window.location.hostname.startsWith('m'); const is_acmsguru = window.location.href.includes("acmsguru"); const is_oldLatex = $('.tex-span').length; const bottomZh_CN = getGMValue("bottomZh_CN", true); const showLoading = getGMValue("showLoading", true); const loaded = getGMValue("loaded", false); const translation = getGMValue("translation", "deepl"); const expandFoldingblocks = getGMValue("expandFoldingblocks", true); const enableSegmentedTranslation = getGMValue("enableSegmentedTranslation", false); const showJumpToLuogu = getGMValue("showJumpToLuogu", true); const hoverTargetAreaDisplay = getGMValue("hoverTargetAreaDisplay", false); var x_api2d_no_cache = getGMValue("x_api2d_no_cache", true); var showOpneAiAdvanced = getGMValue("showOpneAiAdvanced", false); // 常量 const helpCircleHTML = '
'; const darkenPageStyle = `body::before { content: ""; display: block; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.4); z-index: 999; }`; // 报错信息捕获 /*let errorMessages = ""; const defaultError = console.error.bind(console); console.error = (message) => { const error = new Error(); const stack = error.stack.split("\n").slice(2).join("\n"); const now = new Date().toLocaleString(); errorMessages += "\n## " + message + "\n### time: " + now +"\n" + stack + "\n"; defaultError(message); }; window.onerror = (message, source, lineno, colno, error) => { const now = new Date().toLocaleString(); errorMessages += "\n## " + message + "\n### time: " + now +"\n" + error.stack + "\n"; defaultError(message); return true; };*/ // 样式 GM_addStyle(` :root { --vp-font-family-base: "Chinese Quotes", "Inter var", "Inter", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Helvetica, Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; } span.mdViewContent { white-space: pre-wrap; } /*翻译区域提示*/ .overlay { pointer-events: none; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: repeating-linear-gradient(135deg, #97e7cacc, #97e7cacc 30px, #e9fbf1cc 0px, #e9fbf1cc 55px); border-radius: 5px; display: flex; align-items: center; justify-content: center; color: #00695C; font-size: 16px; font-weight: bold; text-shadow: 0px 0px 2px #edfcf4; } /*翻译div*/ .translate-problem-statement { display: grid; justify-items: start; white-space: pre-wrap; letter-spacing: 1.8px; color: #059669; background-color: #f9f9fa; border: 1px solid #10b981; border-radius: 0.3rem; padding: 5px; margin: 10px 0px; width: 100%; box-sizing: border-box; font-size: 13px; } .translate-problem-statement.error_translate { color: red; border-color: red; } .translate-problem-statement a { color: #10b981; font-weight: 600; background: 0 0; text-decoration: none; } .translate-problem-statement p { margin: 8px 0 !important; } .translate-problem-statement ol, .translate-problem-statement ul { display: grid; margin-inline-start: 0.8em; margin-block-start: 0em; margin: 0.5em 0 0 3em; } .translate-problem-statement li { display: list-item; height: auto; word-wrap: break-word; } .translate-problem-statement ol li { list-style-type: auto; } .translate-problem-statement ul li { list-style-type: disc; } .translate-problem-statement li p { margin: -20px 0px 0px 0px !important; padding: 0 !important; } .translate-problem-statement img { max-width: 100.0%; max-height: 100.0%; } .ttypography .translate-problem-statement .MathJax { color: #059669!important; } .translate-problem-statement span.math { margin: 0px 2.5px !important; } .translate-problem-statement a:hover { background-color: #800; color: #fff; text-decoration: none; } .html2md-panel { display: flex; justify-content: flex-end; } .html2md-panel a { text-decoration: none; } button.html2mdButton { display: flex; align-items: center; cursor: pointer; background-color: #ffffff; color: #606266; height: 22px; width: auto; font-size: 13px; border-radius: 0.3rem; padding: 1px 5px; margin: 5px; border: 1px solid #dcdfe6; } button.html2mdButton:hover { color: #409eff; border-color: #409eff; background-color: #f1f8ff; } button.html2mdButton.copied { background-color: #f0f9eb; color: #67c23e; border: 1px solid #b3e19d; } button.html2mdButton.mdViewed { background-color: #fdf6ec; color: #e6a23c; border: 1px solid #f3d19e; } button.html2mdButton.error { background-color: #fef0f0; color: #f56c6c; border: 1px solid #fab6b6; } button.translated { background-color: #f0f9eb; color: #67c23e; border: 1px solid #b3e19d; } button.html2mdButton.reTranslation { background-color: #f4f4f5; color: #909399; border: 1px solid #c8c9cc; } .translate-problem-statement table { border: 1px #ccc solid; border-collapse: collapse; margin: 1.3571em 0 0; color: #222; } .translate-problem-statement table td { border-right: 1px solid #ccc; border-top: 1px solid #ccc; padding: 0.7143em 0.5em; } .translate-problem-statement table th { padding: 0.7143em 0.5em; } .translate-problem-statement p:not(:first-child) { margin: 1.5em 0 0; } /*设置面板*/ header .enter-or-register-box, header .languages { position: absolute; right: 170px; } button.html2mdButton.CFBetter_setting { float: right; height: 30px; background: #60a5fa; color: white; margin: 10px; border: 0px; } button.html2mdButton.CFBetter_setting.open { background-color: #e6e6e6; color: #727378; cursor: not-allowed; } #CFBetter_setting_menu { z-index: 9999; box-shadow: 0px 0px 0px 4px #ffffff; display: grid; position: fixed; top: 50%; left: 50%; width: 320px; transform: translate(-50%, -50%); border-radius: 6px; background-color: #edf1ff; border-collapse: collapse; border: 1px solid #ffffff; color: #697e91; font-family: var(--vp-font-family-base); padding: 10px 20px 20px 20px; } #CFBetter_setting_menu h3 { margin-top: 10px; } #CFBetter_setting_menu hr { border: none; height: 1px; background-color: #ccc; margin: 10px 0; } /*设置面板-关闭按钮*/ #CFBetter_setting_menu .tool-box { position: absolute; display: flex; align-items: center; justify-content: center; width: 2.5rem; height: 2.5rem; top: 3px; right: 3px; } #CFBetter_setting_menu .btn-close { display: flex; align-items: center; justify-content: center; text-align: center; padding: 10px !important; width: 1px; height: 1px !important; color: transparent; font-size: 0; cursor: pointer; background-color: #ff000080; border: none; border-radius: 10px; transition: .15s ease all; } #CFBetter_setting_menu .btn-close:hover { width: 20px; height: 20px !important; font-size: 17px; color: #ffffff; background-color: #ff0000cc; box-shadow: 0 5px 5px 0 #00000026; } #CFBetter_setting_menu .btn-close:active { width: .9rem; height: .9rem; font-size: 1px; color: #ffffffde; --shadow-btn-close: 0 3px 3px 0 #00000026; box-shadow: var(--shadow-btn-close); } /*设置面板-checkbox*/ #CFBetter_setting_menu input[type=checkbox]:focus { outline: 0px; } #CFBetter_setting_menu input[type="checkbox"] { margin: 0px; appearance: none; -webkit-appearance: none; width: 40px; height: 20px !important; border: 1.5px solid #D7CCC8; padding: 0px !important; border-radius: 20px; background: #efebe978; position: relative; box-sizing: border-box; } #CFBetter_setting_menu input[type="checkbox"]::before { content: ""; width: 14px; height: 14px; background: #D7CCC8; border: 1.5px solid #BCAAA4; border-radius: 50%; position: absolute; top: 0; left: 0; transform: translate(2%, 2%); transition: all 0.3s ease-in-out; } #CFBetter_setting_menu input[type="checkbox"]::after { content: url("data:image/svg+xml,%3Csvg xmlns='://www.w3.org/2000/svg' width='23' height='23' viewBox='0 0 23 23' fill='none'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M6.55021 5.84315L17.1568 16.4498L16.4497 17.1569L5.84311 6.55026L6.55021 5.84315Z' fill='%23EA0707' fill-opacity='0.89'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M17.1567 6.55021L6.55012 17.1568L5.84302 16.4497L16.4496 5.84311L17.1567 6.55021Z' fill='%23EA0707' fill-opacity='0.89'/%3E%3C/svg%3E"); position: absolute; top: 0; left: 24px; } #CFBetter_setting_menu input[type="checkbox"]:checked { border: 1.5px solid #C5CAE9; background: #E8EAF6; } #CFBetter_setting_menu input[type="checkbox"]:checked::before { background: #C5CAE9; border: 1.5px solid #7986CB; transform: translate(122%, 2%); transition: all 0.3s ease-in-out; } #CFBetter_setting_menu input[type="checkbox"]:checked::after { content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 15 13' fill='none'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M14.8185 0.114533C15.0314 0.290403 15.0614 0.605559 14.8855 0.818454L5.00187 12.5L0.113036 6.81663C-0.0618274 6.60291 -0.0303263 6.2879 0.183396 6.11304C0.397119 5.93817 0.71213 5.96967 0.886994 6.18339L5.00187 11L14.1145 0.181573C14.2904 -0.0313222 14.6056 -0.0613371 14.8185 0.114533Z' fill='%2303A9F4' fill-opacity='0.9'/%3E%3C/svg%3E"); position: absolute; top: 1.5px; left: 4.5px; } #CFBetter_setting_menu label { font-size: 16px; } .CFBetter_setting_list { display: flex; align-items: center; padding: 10px; margin: 5px 0px; background-color: #ffffff; border-bottom: 1px solid #c9c6c696; border-radius: 8px; justify-content: space-between; } /*设置面板-radio*/ #CFBetter_setting_menu>label { display: flex; list-style-type: none; padding-inline-start: 0px; overflow-x: auto; max-width: 100%; margin: 0px; align-items: center; margin: 3px 0px; } .CFBetter_setting_menu_label_text { display: flex; border: 1px dashed #00aeeccc; height: 20px; width: 100%; color: gray; font-weight: 300; font-size: 14px; letter-spacing: 2px; padding: 7px; align-items: center; } input[type="radio"]:checked+.CFBetter_setting_menu_label_text { background: #41e49930; border: 1px solid green; color: green; font-weight: 500; } #CFBetter_setting_menu>label input[type="radio"] { -webkit-appearance: none; appearance: none; list-style: none; padding: 0px !important; margin: 0px; } #CFBetter_setting_menu input[type="text"] { display: block; height: 25px !important; width: 100%; background-color: #ffffff; color: #727378; font-size: 12px; border-radius: 0.3rem; padding: 1px 5px !important; box-sizing: border-box; margin: 5px 0px 5px 0px; border: 1px solid #00aeeccc; box-shadow: 0 0 1px #0000004d; } .CFBetter_setting_menu_input { width: 100%; display: grid; margin-top: 5px; } #CFBetter_setting_menu #save { cursor: pointer; display: inline-flex; padding: 0.5rem 1rem; background-color: #1aa06d; color: #ffffff; font-size: 1rem; line-height: 1.5rem; font-weight: 500; justify-content: center; width: 100%; border-radius: 0.375rem; border: none; box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); } #CFBetter_setting_menu button#debug_button.debug_button { width: 18%; } #CFBetter_setting_menu span.tip { color: #999; font-size: 12px; font-weight: 500; padding: 5px 0px; } /*设置面板-tip*/ .help_tip { margin-right: auto; } .help_tip .tip_text { display: none; position: absolute; color: #697e91; font-weight: 400; letter-spacing: 0px; background-color: #ffffff; padding: 10px; margin: 5px 0px; border-radius: 4px; border: 1px solid #e4e7ed; box-shadow: 0px 0px 12px rgba(0, 0, 0, .12); z-index: 999; } .help_tip .tip_text p { margin-bottom: 5px; } .help_tip .tip_text:before { content: ""; position: absolute; top: -20px; right: -10px; bottom: -10px; left: -10px; z-index: -1; } .help-icon { display: flex; cursor: help; width: 15px; color: rgb(255, 153, 0); margin-left: 5px; } #CFBetter_setting_menu .CFBetter_setting_menu_label_text .help_tip .help-icon { color: #7fbeb2; } .help_tip .help-icon:hover + .tip_text, .help_tip .tip_text:hover { display: block; cursor: help; width: 250px; } /*设置面板-展开*/ #is_showOpneAiAdvanced{ width: 100%; background-color: aliceblue; padding: 8px; box-sizing: border-box; border-radius: 10px; } /*确认弹窗*/ .wordsExceeded { z-index: 9999; box-shadow: 0px 0px 5px 1px rgb(0 0 0 / 10%), 0 10px 10px -5px rgba(0, 0, 0, 0.04); display: grid; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); border-radius: 4px; background-color: #ffffff; border: 1px solid #e4e7ed; color: #697e91; font-family: var(--vp-font-family-base); padding: 10px 20px 20px 20px; } .wordsExceeded button { display: inline-flex; justify-content: center; align-items: center; line-height: 1; white-space: nowrap; cursor: pointer; text-align: center; box-sizing: border-box; outline: none; transition: .1s; user-select: none; vertical-align: middle; -webkit-appearance: none; height: 24px; padding: 5px 11px; font-size: 12px; border-radius: 4px; color: #ffffff; background: #409eff; border-color: #409eff; border: none; margin-right: 12px; } .wordsExceeded button:hover{ background-color:#79bbff; } .wordsExceeded .help-icon { margin: 0px 8px 0px 0px; height: 1em; width: 1em; line-height: 1em; display: inline-flex; justify-content: center; align-items: center; position: relative; fill: currentColor; font-size: inherit; } .wordsExceeded p { margin: 5px 0px; } /*更新检查*/ div#update_panel { z-index: 9999; position: fixed; top: 50%; left: 50%; width: 240px; transform: translate(-50%, -50%); box-shadow: 0px 0px 4px 0px #0000004d; padding: 10px 20px 20px 20px; color: #444242; background-color: #f5f5f5; border: 1px solid #848484; border-radius: 8px; } div#update_panel #updating { cursor: pointer; display: inline-flex; padding: 3px; background-color: #1aa06d; color: #ffffff; font-size: 1rem; line-height: 1.5rem; font-weight: 500; justify-content: center; width: 100%; border-radius: 0.375rem; border: none; box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); } div#update_panel #updating a { text-decoration: none; color: white; display: flex; position: inherit; top: 0; left: 0; width: 100%; height: 22px; font-size: 14px; justify-content: center; align-items: center; } #skip_menu { display: flex; margin-top: 10px; justify-content: flex-end; align-items: center; } #skip_menu .help_tip { margin-right: 5px; margin-left: -5px; } #skip_menu .help-icon { color: #f44336; } `); // 获取cookie function getCookie(name) { const cookies = document.cookie.split(";"); for (let i = 0; i < cookies.length; i++) { const cookie = cookies[i].trim(); const [cookieName, cookieValue] = cookie.split("="); if (cookieName === name) { return decodeURIComponent(cookieValue); } } return ""; } // 更新检查 (function checkScriptVersion() { function compareVersions(version1 = "0", version2 = "0") { const v1Array = String(version1).split("."); const v2Array = String(version2).split("."); const minLength = Math.min(v1Array.length, v2Array.length); let result = 0; for (let i = 0; i < minLength; i++) { const curV1 = Number(v1Array[i]); const curV2 = Number(v2Array[i]); if (curV1 > curV2) { result = 1; break; } else if (curV1 < curV2) { result = -1; break; } } if (result === 0 && v1Array.length !== v2Array.length) { const v1IsBigger = v1Array.length > v2Array.length; const maxLenArray = v1IsBigger ? v1Array : v2Array; for (let i = minLength; i < maxLenArray.length; i++) { const curVersion = Number(maxLenArray[i]); if (curVersion > 0) { v1IsBigger ? result = 1 : result = -1; break; } } } return result; } GM_xmlhttpRequest({ method: "GET", url: "https://greasyfork.org/zh-CN/scripts/465777.json", timeout: 10 * 1e3, onload: function (response) { const scriptData = JSON.parse(response.responseText); const skipUpdate = getCookie("skipUpdate"); if ( scriptData.name === GM_info.script.name && compareVersions(scriptData.version, GM_info.script.version) === 1 && skipUpdate !== "true" ) { const styleElement = GM_addStyle(darkenPageStyle); $("body").append(`