您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
将 ZZULIOJ 界面美化为类 iOS 扁平圆角半透明风格,支持暗黑模式与快速切换
// ==UserScript== // @name ZZULIOJ iOS Style Modernizer // @namespace https://acm.zzuli.edu.cn/ // @version 0.1.6 // @description 将 ZZULIOJ 界面美化为类 iOS 扁平圆角半透明风格,支持暗黑模式与快速切换 // @author ShiYi // @match *://acm.zzuli.edu.cn/* // @match *://acm.zzuli.edu.cn/*?* // @run-at document-end // @grant none // @license MIT // ==/UserScript== (function () { 'use strict'; const LS_KEY_ENABLE = 'zzuli_ios_theme_enabled'; const LS_KEY_DARK = 'zzuli_ios_theme_dark'; const LS_KEY_AUTO = 'zzuli_ios_theme_auto'; const STATE = { enabled: localStorage.getItem(LS_KEY_ENABLE) !== 'false', dark: localStorage.getItem(LS_KEY_DARK) === 'true', auto: localStorage.getItem(LS_KEY_AUTO) !== 'false', }; // 如果跟随系统,则用 matchMedia const mqDark = window.matchMedia('(prefers-color-scheme: dark)'); if (STATE.auto) { STATE.dark = mqDark.matches; } function saveState() { localStorage.setItem(LS_KEY_ENABLE, STATE.enabled); localStorage.setItem(LS_KEY_DARK, STATE.dark); localStorage.setItem(LS_KEY_AUTO, STATE.auto); applyTheme(); } function toggleEnabled() { STATE.enabled = !STATE.enabled; saveState(); } function toggleDark() { STATE.auto = false; STATE.dark = !STATE.dark; saveState(); } function toggleAuto() { STATE.auto = !STATE.auto; if (STATE.auto) { STATE.dark = mqDark.matches; } saveState(); } mqDark.addEventListener('change', () => { if (STATE.auto) { STATE.dark = mqDark.matches; saveState(); } }); // 创建样式 const styleTag = document.createElement('style'); styleTag.id = 'zzuli-ios-theme-style'; document.documentElement.appendChild(styleTag); const baseCSS = ` :root { --ios-font-stack: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", "PingFang SC", "Helvetica Neue", Arial, sans-serif; --ios-radius-s: 6px; --ios-radius: 12px; --ios-radius-l: 18px; --ios-border-separator-light: rgba(0,0,0,0.08); --ios-border-separator-dark: rgba(255,255,255,0.15); --ios-accent: #0A84FF; --ios-accent-rgb: 10,132,255; --ios-danger: #FF3B30; --ios-bg-light: #f5f6f7; --ios-bg-dark: #1c1c1e; --ios-card-light: rgba(255,255,255,0.72); --ios-card-dark: rgba(44,44,46,0.72); --ios-text-light: #1c1c1e; --ios-text-dark: #f5f5f7; --ios-muted-light: #6e6e73; --ios-muted-dark: #9d9da1; --ios-code-bg-light: #f2f2f7; --ios-code-bg-dark: #2c2c2e; --ios-shadow-light: 0 2px 6px rgba(0,0,0,0.08), 0 8px 24px -8px rgba(0,0,0,0.12); --ios-shadow-dark: 0 2px 6px rgba(0,0,0,0.6), 0 8px 24px -8px rgba(0,0,0,0.5); --ios-transition: 150ms cubic-bezier(.4,.2,.2,1); } html.zzuli-ios-theme { font-family: var(--ios-font-stack); -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; } html.zzuli-ios-theme body { background: var(--zzuli-bg) !important; color: var(--zzuli-text) !important; transition: background-color var(--ios-transition), color var(--ios-transition); } html.zzuli-ios-theme.zzuli-dark { --zzuli-bg: var(--ios-bg-dark); --zzuli-text: var(--ios-text-dark); --zzuli-muted: var(--ios-muted-dark); --zzuli-card: var(--ios-card-dark); --zzuli-separator: var(--ios-border-separator-dark); --zzuli-code-bg: var(--ios-code-bg-dark); --zzuli-shadow: var(--ios-shadow-dark); } html.zzuli-ios-theme:not(.zzuli-dark) { --zzuli-bg: var(--ios-bg-light); --zzuli-text: var(--ios-text-light); --zzuli-muted: var(--ios-muted-light); --zzuli-card: var(--ios-card-light); --zzuli-separator: var(--ios-border-separator-light); --zzuli-code-bg: var(--ios-code-bg-light); --zzuli-shadow: var(--ios-shadow-light); } html.zzuli-ios-theme ::selection { background: rgba(var(--ios-accent-rgb),0.25); } /* 顶部导航 */ html.zzuli-ios-theme .navbar.navbar-default { background: var(--zzuli-card) !important; backdrop-filter: saturate(180%) blur(18px); -webkit-backdrop-filter: saturate(180%) blur(18px); border: none !important; box-shadow: var(--zzuli-shadow); margin-bottom: 20px; border-radius: var(--ios-radius-l); padding: 4px 20px; } html.zzuli-ios-theme .navbar.navbar-default .navbar-brand { font-weight: 600; font-size: 18px; color: var(--zzuli-text) !important; } html.zzuli-ios-theme .navbar-nav > li > a { color: var(--zzuli-text) !important; border-radius: 20px; padding: 8px 14px; margin: 4px 4px; font-weight: 500; line-height: 20px; position: relative; transition: background-color var(--ios-transition), color var(--ios-transition); } html.zzuli-ios-theme .navbar-nav > li.active > a, html.zzuli-ios-theme .navbar-nav > li > a:hover, html.zzuli-ios-theme .navbar-nav > li > a:focus { background: rgba(var(--ios-accent-rgb),0.12) !important; color: var(--ios-accent) !important; } html.zzuli-ios-theme .navbar-toggle { border: none; background: rgba(var(--ios-accent-rgb),0.12); border-radius: 10px; } html.zzuli-ios-theme .navbar-toggle .icon-bar { background: var(--ios-accent); } /* 下拉菜单 */ html.zzuli-ios-theme .dropdown-menu { background: var(--zzuli-card); backdrop-filter: blur(18px); border: 1px solid var(--zzuli-separator); border-radius: var(--ios-radius); box-shadow: var(--zzuli-shadow); overflow: hidden; } html.zzuli-ios-theme .dropdown-menu > li > a { padding: 10px 16px; color: var(--zzuli-text) !important; transition: background-color var(--ios-transition), color var(--ios-transition); } html.zzuli-ios-theme .dropdown-menu > li > a:hover { background: rgba(var(--ios-accent-rgb),0.15); color: var(--ios-accent) !important; } /* 主体块 / jumbotron => 卡片 */ html.zzuli-ios-theme .jumbotron { background: var(--zzuli-card) !important; border-radius: var(--ios-radius-l); padding: 30px 36px; box-shadow: var(--zzuli-shadow); border: 1px solid var(--zzuli-separator); } /* 表格 */ html.zzuli-ios-theme table { width: 100%; border-collapse: collapse !important; background: var(--zzuli-card); border: 1px solid var(--zzuli-separator); border-radius: var(--ios-radius); overflow: hidden; } html.zzuli-ios-theme table[border], html.zzuli-ios-theme table td, html.zzuli-ios-theme table th { border: none !important; } html.zzuli-ios-theme table th { background: linear-gradient(to bottom, rgba(var(--ios-accent-rgb),0.10), rgba(var(--ios-accent-rgb),0.05)); font-weight: 600; color: var(--zzuli-text); padding: 10px 12px; } html.zzuli-ios-theme table td { padding: 8px 12px; border-top: 1px solid var(--zzuli-separator); } html.zzuli-ios-theme table tr:hover td { background: rgba(var(--ios-accent-rgb),0.07); } /* 深色模式表格条纹与背景修复 */ html.zzuli-ios-theme.zzuli-dark .table-striped > tbody > tr:nth-of-type(odd) { background: rgba(255,255,255,0.05) !important; } html.zzuli-ios-theme.zzuli-dark .table-striped > tbody > tr:nth-of-type(even) { background: transparent !important; } html.zzuli-ios-theme.zzuli-dark table tr, html.zzuli-ios-theme.zzuli-dark table tbody, html.zzuli-ios-theme.zzuli-dark table thead { background: transparent !important; } /* 分页组件 */ html.zzuli-ios-theme .pagination > li > a, html.zzuli-ios-theme .pagination > li > span { background: var(--zzuli-card) !important; border: 1px solid var(--zzuli-separator) !important; color: var(--zzuli-text) !important; transition: background-color var(--ios-transition), color var(--ios-transition), border-color var(--ios-transition); } html.zzuli-ios-theme .pagination > li > a:hover, html.zzuli-ios-theme .pagination > li > span:hover { background: rgba(var(--ios-accent-rgb),0.18) !important; color: var(--ios-accent) !important; } html.zzuli-ios-theme .pagination > .active > a, html.zzuli-ios-theme .pagination > .active > span, html.zzuli-ios-theme .pagination > .active > a:focus, html.zzuli-ios-theme .pagination > .active > span:focus, html.zzuli-ios-theme .pagination > .active > a:hover, html.zzuli-ios-theme .pagination > .active > span:hover { background: var(--ios-accent) !important; border-color: var(--ios-accent) !important; color: #fff !important; } html.zzuli-ios-theme.zzuli-dark .pagination > li > a, html.zzuli-ios-theme.zzuli-dark .pagination > li > span { background: rgba(60,60,62,0.65) !important; } /* Panel / 列表块 修复默认白底 */ html.zzuli-ios-theme .panel, html.zzuli-ios-theme .panel-default, html.zzuli-ios-theme .panel-heading, html.zzuli-ios-theme .panel-body, html.zzuli-ios-theme .list-group-item { background: var(--zzuli-card) !important; border-color: var(--zzuli-separator) !important; color: var(--zzuli-text) !important; } html.zzuli-ios-theme .list-group-item { border: 1px solid var(--zzuli-separator) !important; } html.zzuli-ios-theme .list-group-item:hover { background: rgba(var(--ios-accent-rgb),0.08) !important; } /* 按钮 */ html.zzuli-ios-theme .btn, html.zzuli-ios-theme input[type=button], html.zzuli-ios-theme input[type=submit], html.zzuli-ios-theme button { border-radius: 22px !important; border: 1px solid rgba(var(--ios-accent-rgb),0.35) !important; background: linear-gradient(to bottom right, rgba(var(--ios-accent-rgb),0.15), rgba(var(--ios-accent-rgb),0.05)) !important; color: var(--ios-accent) !important; font-weight: 500; padding: 6px 18px; line-height: 20px; box-shadow: none !important; text-shadow: none !important; transition: background-color var(--ios-transition), border-color var(--ios-transition), color var(--ios-transition); } html.zzuli-ios-theme .btn:hover { background: rgba(var(--ios-accent-rgb),0.25) !important; } html.zzuli-ios-theme .btn-primary, html.zzuli-ios-theme .btn-success { background: var(--ios-accent) !important; color: #fff !important; border: 1px solid var(--ios-accent) !important; } html.zzuli-ios-theme .btn-primary:hover { background: rgba(var(--ios-accent-rgb),0.85) !important; } html.zzuli-ios-theme .btn-danger { background: #FF3B30 !important; border-color: #FF3B30 !important; color: #fff !important; } /* 链接 */ html.zzuli-ios-theme a { color: var(--ios-accent); transition: color var(--ios-transition), background-color var(--ios-transition); } html.zzuli-ios-theme a:hover { color: rgba(var(--ios-accent-rgb),0.8); text-decoration: none; } /* 代码块 */ html.zzuli-ios-theme pre, html.zzuli-ios-theme code, html.zzuli-ios-theme pre code { background: var(--zzuli-code-bg) !important; border: 1px solid var(--zzuli-separator) !important; color: var(--zzuli-text) !important; border-radius: var(--ios-radius); font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace; padding: 10px 14px; overflow-x: auto; } html.zzuli-ios-theme pre { box-shadow: inset 0 0 0 1px rgba(0,0,0,0.03); } /* 代码编辑器 (提交代码页) 行号栏与暗色优化 */ /* 通用容器与边框 */ html.zzuli-ios-theme .CodeMirror, html.zzuli-ios-theme .ace_editor { background: var(--zzuli-code-bg) !important; border: 1px solid var(--zzuli-separator) !important; border-radius: var(--ios-radius); } /* CodeMirror 行号与 gutter */ html.zzuli-ios-theme .CodeMirror-gutters, html.zzuli-ios-theme .CodeMirror-gutter { /* 兼容不同版本 */ background: var(--zzuli-card) !important; border-right: 1px solid var(--zzuli-separator) !important; } html.zzuli-ios-theme .CodeMirror-linenumber { color: var(--zzuli-muted) !important; } html.zzuli-ios-theme .CodeMirror-activeline-background { background: rgba(var(--ios-accent-rgb),0.08) !important; } html.zzuli-ios-theme.zzuli-dark .CodeMirror-activeline-background { background: rgba(var(--ios-accent-rgb),0.18) !important; } html.zzuli-ios-theme.zzuli-dark .CodeMirror, html.zzuli-ios-theme.zzuli-dark .CodeMirror-gutters, html.zzuli-ios-theme.zzuli-dark .CodeMirror-gutter, html.zzuli-ios-theme.zzuli-dark .CodeMirror-linenumber { background: var(--zzuli-card) !important; color: var(--zzuli-muted) !important; } /* Ace Editor Gutter */ html.zzuli-ios-theme .ace_gutter, html.zzuli-ios-theme .ace_gutter-layer, html.zzuli-ios-theme .ace_gutter-active-line, html.zzuli-ios-theme .ace_gutter-cell { background: var(--zzuli-card) !important; color: var(--zzuli-muted) !important; } html.zzuli-ios-theme .ace_gutter-active-line { background: rgba(var(--ios-accent-rgb),0.10) !important; } html.zzuli-ios-theme.zzuli-dark .ace_gutter-active-line { background: rgba(var(--ios-accent-rgb),0.22) !important; } html.zzuli-ios-theme .ace_marker-layer .ace_active-line { background: rgba(var(--ios-accent-rgb),0.08) !important; } html.zzuli-ios-theme.zzuli-dark .ace_marker-layer .ace_active-line { background: rgba(var(--ios-accent-rgb),0.16) !important; } html.zzuli-ios-theme.zzuli-dark .ace_gutter, html.zzuli-ios-theme.zzuli-dark .ace_gutter-layer, html.zzuli-ios-theme.zzuli-dark .ace_gutter-cell { background: var(--zzuli-card) !important; color: var(--zzuli-muted) !important; } /* 通用/备用类名(可能的自定义行号容器) */ html.zzuli-ios-theme .line-numbers, html.zzuli-ios-theme .line-numbers-rows, html.zzuli-ios-theme .linenums, html.zzuli-ios-theme .gutter, html.zzuli-ios-theme.zzuli-dark .line-numbers, html.zzuli-ios-theme.zzuli-dark .line-numbers-rows, html.zzuli-ios-theme.zzuli-dark .linenums, html.zzuli-ios-theme.zzuli-dark .gutter { background: var(--zzuli-card) !important; color: var(--zzuli-muted) !important; } html.zzuli-ios-theme .line-numbers-rows > span:before { color: var(--zzuli-muted); } html.zzuli-ios-theme.zzuli-dark .line-numbers-rows > span:before { color: var(--zzuli-muted); } /* 代码编辑器语法高亮与光标优化(深色模式) */ html.zzuli-ios-theme.zzuli-dark .CodeMirror { color: #e7e9ef !important; } html.zzuli-ios-theme.zzuli-dark .CodeMirror-cursor { border-left: 2px solid #fff !important; } html.zzuli-ios-theme.zzuli-dark .CodeMirror-selected { background: rgba(var(--ios-accent-rgb),0.35) !important; } html.zzuli-ios-theme .CodeMirror-focused .CodeMirror-selected { background: rgba(var(--ios-accent-rgb),0.40) !important; } /* CodeMirror 5 token colors */ html.zzuli-ios-theme.zzuli-dark .cm-keyword { color: #82AAFF !important; } html.zzuli-ios-theme.zzuli-dark .cm-operator { color: #89DDFF !important; } html.zzuli-ios-theme.zzuli-dark .cm-variable { color: #ECEFF4 !important; } html.zzuli-ios-theme.zzuli-dark .cm-variable-2 { color: #C5E4FF !important; } html.zzuli-ios-theme.zzuli-dark .cm-variable-3 { color: #F6C177 !important; } html.zzuli-ios-theme.zzuli-dark .cm-def { color: #B3E5FC !important; } html.zzuli-ios-theme.zzuli-dark .cm-string { color: #C3E88D !important; } html.zzuli-ios-theme.zzuli-dark .cm-number { color: #F78C6C !important; } html.zzuli-ios-theme.zzuli-dark .cm-comment { color: #6b7480 !important; font-style: italic; } html.zzuli-ios-theme.zzuli-dark .cm-meta { color: #FFCB6B !important; } html.zzuli-ios-theme.zzuli-dark .cm-builtin { color: #FF9CAC !important; } html.zzuli-ios-theme.zzuli-dark .cm-tag { color: #7FDBCA !important; } html.zzuli-ios-theme.zzuli-dark .cm-attribute { color: #F6C177 !important; } html.zzuli-ios-theme.zzuli-dark .cm-atom { color: #FFCB6B !important; } html.zzuli-ios-theme.zzuli-dark .cm-error { background: #B00020 !important; color: #fff !important; } html.zzuli-ios-theme.zzuli-dark .CodeMirror-activeline-background { background: rgba(255,255,255,0.06) !important; } /* CodeMirror 6 (如果未来升级) */ html.zzuli-ios-theme.zzuli-dark .cm-editor { color: #e7e9ef !important; } html.zzuli-ios-theme.zzuli-dark .cm-editor .cm-content ::selection { background: rgba(var(--ios-accent-rgb),0.35); } html.zzuli-ios-theme.zzuli-dark .cm-activeLine { background: rgba(255,255,255,0.06) !important; } html.zzuli-ios-theme.zzuli-dark .cm-gutters { background: var(--zzuli-card) !important; color: #6b7480 !important; border-right: 1px solid var(--zzuli-separator) !important; } /* Ace Editor dark overrides */ html.zzuli-ios-theme.zzuli-dark .ace_editor { color: #E7E9EF !important; } html.zzuli-ios-theme.zzuli-dark .ace_cursor { color: #FFFFFF !important; } html.zzuli-ios-theme.zzuli-dark .ace_marker-layer .ace_selection { background: rgba(var(--ios-accent-rgb),0.38) !important; } html.zzuli-ios-theme.zzuli-dark .ace_marker-layer .ace_active-line { background: rgba(255,255,255,0.06) !important; } html.zzuli-ios-theme.zzuli-dark .ace_gutter { color: #6b7480 !important; } html.zzuli-ios-theme.zzuli-dark .ace_keyword, html.zzuli-ios-theme.zzuli-dark .ace_storage { color: #82AAFF !important; } html.zzuli-ios-theme.zzuli-dark .ace_operator { color: #89DDFF !important; } html.zzuli-ios-theme.zzuli-dark .ace_identifier, html.zzuli-ios-theme.zzuli-dark .ace_variable { color: #ECEFF4 !important; } html.zzuli-ios-theme.zzuli-dark .ace_variable.ace_parameter { color: #C5E4FF !important; } html.zzuli-ios-theme.zzuli-dark .ace_support.ace_function, html.zzuli-ios-theme.zzuli-dark .ace_entity.ace_name.ace_function { color: #B3E5FC !important; } html.zzuli-ios-theme.zzuli-dark .ace_string { color: #C3E88D !important; } html.zzuli-ios-theme.zzuli-dark .ace_numeric, html.zzuli-ios-theme.zzuli-dark .ace_constant.ace_numeric { color: #F78C6C !important; } html.zzuli-ios-theme.zzuli-dark .ace_comment { color: #6b7480 !important; font-style: italic; } html.zzuli-ios-theme.zzuli-dark .ace_constant.ace_language, html.zzuli-ios-theme.zzuli-dark .ace_constant.ace_character { color: #FFCB6B !important; } html.zzuli-ios-theme.zzuli-dark .ace_constant.ace_other { color: #FF9CAC !important; } html.zzuli-ios-theme.zzuli-dark .ace_invalid { background: #B00020 !important; color: #fff !important; } html.zzuli-ios-theme.zzuli-dark .ace_markup.ace_heading, html.zzuli-ios-theme.zzuli-dark .ace_entity.ace_other.ace_attribute-name { color: #F6C177 !important; } html.zzuli-ios-theme.zzuli-dark .ace_entity.ace_name.ace_tag { color: #7FDBCA !important; } /* 分割线 */ html.zzuli-ios-theme hr { border: none; height: 1px; background: var(--zzuli-separator); margin: 24px 0; } /* 图片适配圆角 */ html.zzuli-ios-theme img { border-radius: 8px; } /* 表单输入 */ html.zzuli-ios-theme input[type=text], html.zzuli-ios-theme input[type=password], html.zzuli-ios-theme select, html.zzuli-ios-theme textarea { background: var(--zzuli-card); border: 1px solid var(--zzuli-separator); border-radius: 10px; padding: 6px 10px; box-shadow: none; color: var(--zzuli-text); transition: border-color var(--ios-transition), background-color var(--ios-transition); } html.zzuli-ios-theme input:focus, html.zzuli-ios-theme select:focus, html.zzuli-ios-theme textarea:focus { outline: none; border-color: var(--ios-accent); } /* 页脚 / 版权 */ html.zzuli-ios-theme table[width="100%"][border="0"] { background: transparent !important; border: none !important; box-shadow: none !important; } /* 适配较窄屏幕 */ @media (max-width: 768px) { html.zzuli-ios-theme .navbar.navbar-default { border-radius: 0 0 20px 20px; margin: 0 0 16px 0; } html.zzuli-ios-theme .jumbotron { padding: 22px 20px; } } /* 控制面板 */ #zzuli-ios-toggle-panel { position: fixed; right: 16px; bottom: 16px; z-index: 99999; display: flex; flex-direction: column; gap: 8px; font-family: var(--ios-font-stack); } #zzuli-ios-toggle-panel button { cursor: pointer; min-width: 140px; font-size: 13px; } #zzuli-ios-toggle-panel .zzuli-chip { background: var(--zzuli-card); border: 1px solid var(--zzuli-separator); border-radius: 14px; padding: 6px 12px; font-size: 12px; color: var(--zzuli-muted); text-align: center; backdrop-filter: blur(18px); } html:not(.zzuli-ios-theme) #zzuli-ios-toggle-panel .state-indicator { color: #999; } /* 关闭时隐藏效果 */ html:not(.zzuli-ios-theme) .zzuli-ios-wrapped { all: unset; } /* ===== 追加:暗色模式下白块/条纹/底栏修复 ===== */ /* 底部题目页面提交条 panel-footer 白底修复 */ html.zzuli-ios-theme .panel-footer { background: var(--zzuli-card) !important; border-top: 1px solid var(--zzuli-separator) !important; color: var(--zzuli-text) !important; box-shadow: none !important; } html.zzuli-ios-theme.zzuli-dark .panel-footer a { color: var(--ios-accent) !important; } /* 题目详情页 center 容器可能残留白色 */ html.zzuli-ios-theme.zzuli-dark center, html.zzuli-ios-theme.zzuli-dark center > div { background: transparent !important; } /* 自定义 evenrow / oddrow 条纹在暗色模式下统一风格 */ html.zzuli-ios-theme.zzuli-dark tr.evenrow, html.zzuli-ios-theme.zzuli-dark tr.oddrow { background: transparent !important; } html.zzuli-ios-theme.zzuli-dark tr.evenrow td { background: rgba(255,255,255,0.04) !important; } html.zzuli-ios-theme.zzuli-dark tr.oddrow td { background: rgba(255,255,255,0.02) !important; } html.zzuli-ios-theme.zzuli-dark tr.evenrow:hover td, html.zzuli-ios-theme.zzuli-dark tr.oddrow:hover td { background: rgba(var(--ios-accent-rgb),0.18) !important; } /* 兼容可能被写死的 header 背景 */ html.zzuli-ios-theme.zzuli-dark th, html.zzuli-ios-theme.zzuli-dark thead td { background: rgba(255,255,255,0.05) !important; } /* select/option 在暗色模式下下拉白底修复 */ html.zzuli-ios-theme.zzuli-dark select, html.zzuli-ios-theme.zzuli-dark option, html.zzuli-ios-theme.zzuli-dark select option { background: var(--zzuli-card) !important; color: var(--zzuli-text) !important; } /* 捕获仍然用内联 #fff/#FFF 的背景块(尽量温和) */ html.zzuli-ios-theme.zzuli-dark [style*="background-color:#FFF"], html.zzuli-ios-theme.zzuli-dark [style*="background-color:#fff"], html.zzuli-ios-theme.zzuli-dark [style*="background:#FFF"], html.zzuli-ios-theme.zzuli-dark [style*="background:#fff"] { background: var(--zzuli-card) !important; } /* 某些内联 style 设为 white 的表格/td 修复 */ html.zzuli-ios-theme.zzuli-dark td[bgcolor="white"], html.zzuli-ios-theme.zzuli-dark tr[bgcolor="white"], html.zzuli-ios-theme.zzuli-dark table[bgcolor="white"] { background: transparent !important; } /* Pie 图所在单元格白底强制透明 */ html.zzuli-ios-theme.zzuli-dark #pie[bgcolor="white"], html.zzuli-ios-theme.zzuli-dark #pie { background: transparent !important; } /* 避免分页条在暗色模式出现突兀白底 */ html.zzuli-ios-theme.zzuli-dark .pagination > li > a, html.zzuli-ios-theme.zzuli-dark .pagination > li > span { background: rgba(60,60,62,0.65) !important; } /* ===== 追加结束 ===== */ /* ===== 题目详情页专属增强 ===== */ html.zzuli-ios-theme .zzuli-problem-header { background: var(--zzuli-card); border: 1px solid var(--zzuli-separator); box-shadow: var(--zzuli-shadow); border-radius: 26px; padding: 28px 32px 22px 32px; margin-bottom: 26px; position: relative; overflow: hidden; } html.zzuli-ios-theme .zzuli-problem-header::before { content: ''; position: absolute; inset:0; background: radial-gradient(circle at 85% 15%, rgba(var(--ios-accent-rgb),0.18), transparent 55%); pointer-events:none; } html.zzuli-ios-theme.zzuli-dark .zzuli-problem-header::before { background: radial-gradient(circle at 78% 18%, rgba(var(--ios-accent-rgb),0.28), transparent 60%); } html.zzuli-ios-theme .zzuli-problem-title { font-size: 28px; font-weight: 700; margin:0 0 14px 0; letter-spacing: .5px; background: linear-gradient(92deg, var(--ios-accent), rgba(var(--ios-accent-rgb),0.55)); -webkit-background-clip: text; color: transparent; } html.zzuli-ios-theme .zzuli-problem-meta { display:flex; flex-wrap:wrap; gap:10px 14px; margin-bottom:4px; } html.zzuli-ios-theme .zzuli-chip-badge { font-size:12px; padding:4px 10px 5px; line-height:1.2; border-radius: 18px; background: rgba(var(--ios-accent-rgb),0.12); color: var(--ios-accent); font-weight:500; backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); border:1px solid rgba(var(--ios-accent-rgb),0.35); } html.zzuli-ios-theme.zzuli-dark .zzuli-chip-badge { background: rgba(var(--ios-accent-rgb),0.22); } html.zzuli-ios-theme .zzuli-chip-badge[data-kind="limit"] { background: rgba(var(--ios-accent-rgb),0.08); } html.zzuli-ios-theme.zzuli-dark .zzuli-chip-badge[data-kind="limit"] { background: rgba(var(--ios-accent-rgb),0.30); } html.zzuli-ios-theme .zzuli-problem-actions-bar { display:flex; gap:14px; margin-top:12px; } html.zzuli-ios-theme .zzuli-problem-actions-bar .zzuli-action-btn { cursor:pointer; border:none; border-radius: 22px; padding:10px 20px; font-size:14px; font-weight:600; background: var(--ios-accent); color:#fff !important; box-shadow: 0 4px 14px -4px rgba(var(--ios-accent-rgb),0.55); transition: transform var(--ios-transition), box-shadow var(--ios-transition), background var(--ios-transition); text-decoration:none !important; display:inline-flex; align-items:center; gap:6px; } html.zzuli-ios-theme .zzuli-problem-actions-bar .zzuli-action-btn:hover { transform: translateY(-2px); box-shadow:0 6px 18px -6px rgba(var(--ios-accent-rgb),0.65); } html.zzuli-ios-theme .zzuli-problem-actions-bar .zzuli-action-btn.secondary { background: linear-gradient(to bottom right, rgba(var(--ios-accent-rgb),0.20), rgba(var(--ios-accent-rgb),0.08)); color: var(--ios-accent) !important; box-shadow:none; border:1px solid rgba(var(--ios-accent-rgb),0.35); } html.zzuli-ios-theme .zzuli-problem-actions-bar .zzuli-action-btn.secondary:hover { background: rgba(var(--ios-accent-rgb),0.30); } /* 小节 Panel 视觉强化 */ html.zzuli-ios-theme .panel.zzuli-problem-section { position:relative; border-radius:22px; overflow:hidden; } html.zzuli-ios-theme .panel.zzuli-problem-section::before { content:''; position:absolute; left:0; top:0; bottom:0; width:4px; background: linear-gradient(var(--ios-accent), rgba(var(--ios-accent-rgb),0.4)); } html.zzuli-ios-theme.zzuli-dark .panel.zzuli-problem-section::before { background: linear-gradient(rgba(var(--ios-accent-rgb),0.85), rgba(var(--ios-accent-rgb),0.35)); } html.zzuli-ios-theme .panel.zzuli-problem-section .panel-heading { font-weight:600 !important; font-size:16px !important; background: linear-gradient(to right, rgba(var(--ios-accent-rgb),0.16), rgba(var(--ios-accent-rgb),0.06)) !important; border-bottom:1px solid var(--zzuli-separator) !important; } html.zzuli-ios-theme.zzuli-dark .panel.zzuli-problem-section .panel-heading { background: linear-gradient(to right, rgba(var(--ios-accent-rgb),0.28), rgba(var(--ios-accent-rgb),0.10)) !important; } /* 样例区代码块微调 */ html.zzuli-ios-theme pre.content { font-size:13.5px; line-height:1.5; } html.zzuli-ios-theme .sampledata { font-family: ui-monospace, Menlo, Consolas, monospace; } /* 底部 footer 中的旧方括号隐藏(仅在我们生成新按钮后附加类时生效) */ html.zzuli-ios-theme .panel-footer.zzuli-clean-brackets { font-size:0; } html.zzuli-ios-theme .panel-footer.zzuli-clean-brackets a { font-size:14px; } /* 让原 footer 转为透明容器(我们另建按钮栏) */ html.zzuli-ios-theme .panel-footer.zzuli-problem-footer { background:transparent !important; border:none !important; box-shadow:none !important; padding:0 !important; margin-top:12px; } /* ===== 题目详情页专属增强结束 ===== */ /* ===== 提交代码页增强样式 ===== */ html.zzuli-ios-theme .zzuli-submit-wrapper { background: var(--zzuli-card); border:1px solid var(--zzuli-separator); border-radius:28px; padding:32px 34px 28px; box-shadow: var(--zzuli-shadow); max-width:1200px; margin:0 auto 42px; position:relative; } html.zzuli-ios-theme .zzuli-submit-wrapper.zzuli-fullscreen { position:fixed; inset:16px; z-index:99990; overflow:auto; backdrop-filter:blur(22px) saturate(180%); -webkit-backdrop-filter:blur(22px) saturate(180%); animation:zzuliFadeIn .18s ease; } @keyframes zzuliFadeIn { from { opacity:0; transform:scale(.985);} to { opacity:1; transform:scale(1);} } html.zzuli-ios-theme .zzuli-submit-header { display:flex; flex-wrap:wrap; gap:18px; align-items:center; margin-bottom:18px; } html.zzuli-ios-theme .zzuli-submit-title { font-size:24px; font-weight:700; background:linear-gradient(92deg, var(--ios-accent), rgba(var(--ios-accent-rgb),0.55)); -webkit-background-clip:text; color:transparent; margin-right:auto; } html.zzuli-ios-theme .zzuli-lang-segment { display:inline-flex; background:rgba(var(--ios-accent-rgb),0.12); border:1px solid rgba(var(--ios-accent-rgb),0.35); border-radius:20px; padding:4px; gap:4px; } html.zzuli-ios-theme.zzuli-dark .zzuli-lang-segment { background:rgba(var(--ios-accent-rgb),0.25); } html.zzuli-ios-theme .zzuli-lang-segment button { border:none; background:transparent; padding:6px 14px; font-size:13px; font-weight:500; border-radius:14px; cursor:pointer; color:var(--ios-accent); transition: background var(--ios-transition), color var(--ios-transition); } html.zzuli-ios-theme .zzuli-lang-segment button.zzuli-active { background:var(--ios-accent); color:#fff; box-shadow:0 2px 6px -1px rgba(var(--ios-accent-rgb),0.55); } html.zzuli-ios-theme .zzuli-submit-toolbar { display:flex; flex-wrap:wrap; gap:10px; align-items:center; margin-bottom:10px; } html.zzuli-ios-theme .zzuli-chip-small { font-size:12px; padding:4px 10px; border-radius:14px; background:rgba(var(--ios-accent-rgb),0.10); color:var(--ios-accent); font-weight:500; border:1px solid rgba(var(--ios-accent-rgb),0.25); } html.zzuli-ios-theme.zzuli-dark .zzuli-chip-small { background:rgba(var(--ios-accent-rgb),0.22); } html.zzuli-ios-theme .zzuli-submit-actions { margin-left:auto; display:flex; gap:10px; } html.zzuli-ios-theme .zzuli-submit-fullscreen-btn { background:linear-gradient(to bottom right, rgba(var(--ios-accent-rgb),0.18), rgba(var(--ios-accent-rgb),0.05)); border:1px solid rgba(var(--ios-accent-rgb),0.4); color:var(--ios-accent); padding:6px 14px; font-size:12px; line-height:1; border-radius:18px; cursor:pointer; } html.zzuli-ios-theme .zzuli-submit-fullscreen-btn:hover { background:rgba(var(--ios-accent-rgb),0.30); color:#fff; } html.zzuli-ios-theme .zzuli-editor-container { position:relative; border-radius:18px; overflow:hidden; } html.zzuli-ios-theme .zzuli-editor-container .CodeMirror, html.zzuli-ios-theme .zzuli-editor-container .ace_editor, html.zzuli-ios-theme .zzuli-editor-container textarea { min-height:380px; font-size:13.5px; } html.zzuli-ios-theme .zzuli-code-overlay { position:absolute; right:10px; bottom:8px; font-size:11px; background:rgba(0,0,0,0.35); color:#fff; padding:4px 10px 5px; border-radius:14px; display:flex; gap:8px; align-items:center; backdrop-filter:blur(6px); -webkit-backdrop-filter:blur(6px); pointer-events:none; } html.zzuli-ios-theme.zzuli-dark .zzuli-code-overlay { background:rgba(255,255,255,0.15); } html.zzuli-ios-theme .zzuli-auto-saved { font-size:11px; color:var(--zzuli-muted); margin-left:4px; min-height:14px; } html.zzuli-ios-theme .zzuli-submit-footer { display:flex; justify-content:space-between; align-items:center; margin-top:18px; gap:12px; flex-wrap:wrap; } html.zzuli-ios-theme .zzuli-submit-footer .btn-primary { font-weight:600; padding:8px 26px; } html.zzuli-ios-theme .zzuli-warning-unsaved { color:#d35400; font-size:12px; font-weight:500; } html.zzuli-ios-theme.zzuli-dark .zzuli-warning-unsaved { color:#ffb347; } html.zzuli-ios-theme .zzuli-hidden { display:none !important; } html.zzuli-ios-theme .zzuli-lang-select-original { display:none !important; } @media (max-width: 768px){ html.zzuli-ios-theme .zzuli-submit-wrapper { padding:24px 20px 26px; border-radius:22px; } html.zzuli-ios-theme .zzuli-submit-title { font-size:20px; } html.zzuli-ios-theme .zzuli-submit-fullscreen-btn { padding:6px 12px; } html.zzuli-ios-theme .zzuli-code-overlay { font-size:10px; right:6px; bottom:6px; } } /* ===== 提交代码页增强样式结束 ===== */ /* ===== 代码补全 / 导入提示 iOS 风格 ===== */ /* CodeMirror 5 show-hint */ html.zzuli-ios-theme .CodeMirror-hints { position:absolute; z-index:999999 !important; padding:6px 0; margin:4px 0 0; list-style:none; background:var(--zzuli-card); backdrop-filter:blur(22px) saturate(180%); -webkit-backdrop-filter:blur(22px) saturate(180%); border:1px solid var(--zzuli-separator); border-radius:16px; font-size:13px; box-shadow:0 8px 28px -6px rgba(0,0,0,0.18), 0 2px 6px rgba(0,0,0,0.08); max-height:320px; overflow-y:auto; scrollbar-width:thin; } html.zzuli-ios-theme.zzuli-dark .CodeMirror-hints { box-shadow:0 8px 28px -8px rgba(0,0,0,0.6), 0 2px 6px rgba(0,0,0,0.5); } html.zzuli-ios-theme .CodeMirror-hint { padding:6px 14px 6px 14px; cursor:pointer; white-space:nowrap; color:var(--zzuli-text); display:flex; align-items:center; gap:10px; position:relative; font-family:var(--ios-font-stack); } html.zzuli-ios-theme .CodeMirror-hint:hover { background:rgba(var(--ios-accent-rgb),0.10); } html.zzuli-ios-theme .CodeMirror-hint-active { background:linear-gradient(92deg, var(--ios-accent), rgba(var(--ios-accent-rgb),0.7)); color:#fff; border-radius:10px; } html.zzuli-ios-theme .CodeMirror-hint span.zzuli-hint-meta { margin-left:auto; font-size:11px; color:var(--zzuli-muted); font-weight:500; letter-spacing:.5px; } html.zzuli-ios-theme .CodeMirror-hint-active span.zzuli-hint-meta { color:rgba(255,255,255,0.9); } /* 右侧导入 / 来源 / 类型提示通用元信息容器(若存在) */ html.zzuli-ios-theme .CodeMirror-hint .zzuli-hint-meta { margin-left:auto; font-size:11px; color:var(--zzuli-muted); font-weight:500; letter-spacing:.5px; } /* CodeMirror 6 自动补全 */ html.zzuli-ios-theme .cm-tooltip.cm-tooltip-autocomplete { border:1px solid var(--zzuli-separator); background:var(--zzuli-card); backdrop-filter:blur(22px) saturate(180%); -webkit-backdrop-filter:blur(22px) saturate(180%); border-radius:18px; padding:6px 0; box-shadow:0 8px 28px -6px rgba(0,0,0,0.18), 0 2px 6px rgba(0,0,0,0.08); } html.zzuli-ios-theme.zzuli-dark .cm-tooltip.cm-tooltip-autocomplete { box-shadow:0 8px 28px -8px rgba(0,0,0,0.6), 0 2px 6px rgba(0,0,0,0.5); } html.zzuli-ios-theme .cm-tooltip-autocomplete ul { padding:0; margin:0; } html.zzuli-ios-theme .cm-tooltip-autocomplete li { padding:6px 14px; font-size:13px; display:flex; align-items:center; gap:8px; } html.zzuli-ios-theme .cm-tooltip-autocomplete li[aria-selected="true"] { background:linear-gradient(92deg, var(--ios-accent), rgba(var(--ios-accent-rgb),0.7)); color:#fff; } html.zzuli-ios-theme .cm-completionDetail { margin-left:auto; font-size:11px; opacity:.75; } html.zzuli-ios-theme .cm-completionMatchedText { font-weight:600; color:var(--ios-accent); } html.zzuli-ios-theme.zzuli-dark .cm-tooltip-autocomplete li[aria-selected="true"] .cm-completionMatchedText { color:#fff; text-decoration:underline; } /* Ace Editor 自动补全 */ html.zzuli-ios-theme .ace_autocomplete { background:var(--zzuli-card) !important; color:var(--zzuli-text) !important; border:1px solid var(--zzuli-separator) !important; border-radius:18px !important; box-shadow:0 8px 26px -6px rgba(0,0,0,0.18),0 2px 6px rgba(0,0,0,0.08) !important; padding:4px 0 6px !important; backdrop-filter:blur(20px) saturate(180%); -webkit-backdrop-filter:blur(20px) saturate(180%); } html.zzuli-ios-theme.zzuli-dark .ace_autocomplete { box-shadow:0 8px 28px -8px rgba(0,0,0,0.6),0 2px 6px rgba(0,0,0,0.5) !important; } html.zzuli-ios-theme .ace_autocomplete .ace_completion { padding:4px 14px 4px 14px !important; position:relative; } html.zzuli-ios-theme .ace_autocomplete .ace_completion:hover { background:rgba(var(--ios-accent-rgb),0.10) !important; } html.zzuli-ios-theme .ace_autocomplete .ace_selected { background:linear-gradient(92deg, var(--ios-accent), rgba(var(--ios-accent-rgb),0.75)) !important; color:#fff !important; border-radius:10px; } html.zzuli-ios-theme .ace_autocomplete .ace_completion.ace_selected .ace_rightAlignedText { color:rgba(255,255,255,0.85) !important; } html.zzuli-ios-theme .ace_autocomplete .ace_rightAlignedText { font-size:11px; color:var(--zzuli-muted) !important; font-weight:500; } html.zzuli-ios-theme .ace_doc-tooltip { background:var(--zzuli-card) !important; border:1px solid var(--zzuli-separator) !important; color:var(--zzuli-text) !important; border-radius:16px !important; box-shadow:0 10px 30px -8px rgba(0,0,0,0.25),0 2px 6px rgba(0,0,0,0.08) !important; max-width:420px; line-height:1.5; padding:10px 14px !important; font-size:12.5px; } html.zzuli-ios-theme.zzuli-dark .ace_doc-tooltip { box-shadow:0 10px 34px -10px rgba(0,0,0,0.65),0 2px 6px rgba(0,0,0,0.5) !important; } html.zzuli-ios-theme .ace_doc-tooltip code, html.zzuli-ios-theme .ace_doc-tooltip pre { background:rgba(var(--ios-accent-rgb),0.10); padding:2px 6px; border-radius:8px; } /* 滚动条微调 */ html.zzuli-ios-theme .CodeMirror-hints::-webkit-scrollbar, html.zzuli-ios-theme .ace_autocomplete::-webkit-scrollbar { width:8px; } html.zzuli-ios-theme .CodeMirror-hints::-webkit-scrollbar-track, html.zzuli-ios-theme .ace_autocomplete::-webkit-scrollbar-track { background:transparent; } html.zzuli-ios-theme .CodeMirror-hints::-webkit-scrollbar-thumb, html.zzuli-ios-theme .ace_autocomplete::-webkit-scrollbar-thumb { background:rgba(var(--ios-accent-rgb),0.35); border-radius:20px; } html.zzuli-ios-theme.zzuli-dark .CodeMirror-hints::-webkit-scrollbar-thumb, html.zzuli-ios-theme.zzuli-dark .ace_autocomplete::-webkit-scrollbar-thumb { background:rgba(var(--ios-accent-rgb),0.55); } /* ===== 补全弹窗结束 ===== */ `; styleTag.textContent = baseCSS; function applyTheme() { const html = document.documentElement; if (STATE.enabled) { html.classList.add('zzuli-ios-theme'); if (STATE.dark) { html.classList.add('zzuli-dark'); } else { html.classList.remove('zzuli-dark'); } } else { html.classList.remove('zzuli-ios-theme'); html.classList.remove('zzuli-dark'); } updatePanelText(); } // 控制面板 const panel = document.createElement('div'); panel.id = 'zzuli-ios-toggle-panel'; panel.innerHTML = ` <div class="zzuli-chip state-indicator">iOS主题: <span id="zzuli-ios-state"></span></div> <button id="zzuli-ios-toggle" class="btn btn-sm">切换 iOS 主题</button> <button id="zzuli-ios-dark" class="btn btn-sm">切换深色模式</button> <button id="zzuli-ios-auto" class="btn btn-sm">跟随系统: <span id="zzuli-ios-auto-state"></span></button> <div class="zzuli-chip">快捷键: Ctrl/⌘ + Alt/⌥ + I</div> `; document.body.appendChild(panel); function updatePanelText() { const state = document.getElementById('zzuli-ios-state'); const autoState = document.getElementById('zzuli-ios-auto-state'); if (state) { state.textContent = STATE.enabled ? (STATE.dark ? '开启 (深色)' : '开启 (浅色)') : '关闭'; } if (autoState) { autoState.textContent = STATE.auto ? '开' : '关'; } } panel.querySelector('#zzuli-ios-toggle').addEventListener('click', toggleEnabled); panel.querySelector('#zzuli-ios-dark').addEventListener('click', toggleDark); panel.querySelector('#zzuli-ios-auto').addEventListener('click', toggleAuto); // 快捷键 window.addEventListener('keydown', (e) => { const meta = navigator.platform.includes('Mac'); const combo = meta ? e.metaKey && e.altKey : e.ctrlKey && e.altKey; if (combo && (e.key === 'i' || e.key === 'I')) { e.preventDefault(); toggleEnabled(); } }); // 对已有元素做一次必要轻量包装(这里主要依赖纯 CSS 覆盖,不侵入 DOM) function initialAdjust() { // 移除可能影响的内联多余背景色 (扩展匹配 #FFF/#fff) document.querySelectorAll('[style*="background-color:#FFF"],[style*="background-color:#fff"],[style*="background:#FFF"],[style*="background:#fff"]').forEach(el => { if (!el.dataset._origInlineBg) { el.dataset._origInlineBg = el.getAttribute('style'); } try { el.style.backgroundColor = 'transparent'; } catch(e) {} }); } /* 题目详情页增强 */ function enhanceProblemPage() { if (!document.documentElement.classList.contains('zzuli-ios-theme')) return; // URL 或特征匹配 const isProblem = /problem\.php/i.test(location.href) || document.querySelector('center h3')?.textContent?.match(/^\d+:/); if (!isProblem) return; if (document.body.dataset.zzuliProblemEnhanced) return; // 避免重复 document.body.dataset.zzuliProblemEnhanced = '1'; const centerNode = document.querySelector('center h3')?.parentElement?.parentElement; // 依据结构往上两层 if (centerNode) { // 构建新头部容器 const header = document.createElement('div'); header.className = 'zzuli-problem-header'; // 题目标题 const h3 = centerNode.querySelector('h3'); if (h3) { const title = document.createElement('div'); title.className = 'zzuli-problem-title'; title.textContent = h3.textContent.trim(); header.appendChild(title); } // Meta 信息 const metaWrap = document.createElement('div'); metaWrap.className = 'zzuli-problem-meta'; // 时间限制 const timeSpan = centerNode.querySelector('span[fd="time_limit"]'); if (timeSpan) { const chip = document.createElement('span'); chip.className = 'zzuli-chip-badge'; chip.dataset.kind = 'limit'; chip.textContent = '时间 ' + timeSpan.textContent.trim() + 's'; metaWrap.appendChild(chip); } // 内存限制:直接用全文检索相邻含“内存限制”文本 const memoryNode = Array.from(centerNode.childNodes).find(n => n.textContent && n.textContent.includes('内存限制')); if (memoryNode) { const match = memoryNode.textContent.match(/内存限制:\s*([^\s<]+)/); if (match) { const chip = document.createElement('span'); chip.className = 'zzuli-chip-badge'; chip.dataset.kind = 'limit'; chip.textContent = '内存 ' + match[1]; metaWrap.appendChild(chip); } } // 提交 / 解决 const statsText = centerNode.textContent; const submitMatch = statsText.match(/提交:\s*(\d+)/); const solveMatch = statsText.match(/解决:\s*(\d+)/); if (submitMatch) { const chip = document.createElement('span'); chip.className = 'zzuli-chip-badge'; chip.textContent = '提交 ' + submitMatch[1]; metaWrap.appendChild(chip); } if (solveMatch) { const chip = document.createElement('span'); chip.className = 'zzuli-chip-badge'; chip.textContent = '解决 ' + solveMatch[1]; metaWrap.appendChild(chip); } // 命题人 const creator = centerNode.querySelector('#creator a'); if (creator) { const chip = document.createElement('span'); chip.className = 'zzuli-chip-badge'; chip.textContent = '命题人 ' + creator.textContent.trim(); metaWrap.appendChild(chip); } header.appendChild(metaWrap); // 动作按钮栏(提交 / 状态) const actionsBar = document.createElement('div'); actionsBar.className = 'zzuli-problem-actions-bar'; const submitLink = centerNode.querySelector('a[href*="submitpage.php"]'); const statusLink = centerNode.querySelector('a[href*="problemstatus.php"]'); if (submitLink) { const a = document.createElement('a'); a.href = submitLink.href; a.className = 'zzuli-action-btn'; a.textContent = '提交代码'; actionsBar.appendChild(a); } if (statusLink) { const a2 = document.createElement('a'); a2.href = statusLink.href; a2.className = 'zzuli-action-btn secondary'; a2.textContent = '查看状态'; actionsBar.appendChild(a2); } if (actionsBar.children.length) header.appendChild(actionsBar); // 插入到原 centerNode 前面 centerNode.parentElement.insertBefore(header, centerNode); } // 面板强化 document.querySelectorAll('.panel.panel-default').forEach(p => { p.classList.add('zzuli-problem-section'); }); // 底部 panel-footer 改造(如果存在) const footer = document.querySelector('center .panel-footer'); if (footer) { // 移除旧括号并隐藏原文字结构 footer.innerHTML = footer.innerHTML.replace(/[\[\]]/g, ''); footer.classList.add('zzuli-problem-footer'); // 如果我们已经有 header 中的动作按钮,就让 footer 更简洁;否则也复制按钮 if (!document.querySelector('.zzuli-problem-actions-bar')) { const actionsBar = document.createElement('div'); actionsBar.className = 'zzuli-problem-actions-bar'; footer.querySelectorAll('a').forEach((a, idx) => { a.classList.add('zzuli-action-btn'); if (idx === 1) a.classList.add('secondary'); a.textContent = a.textContent === '提交' ? '提交代码' : (a.textContent === '状态' ? '查看状态' : a.textContent); actionsBar.appendChild(a); }); footer.textContent = ''; footer.appendChild(actionsBar); } else { // 已在上方生成;隐藏 footer footer.style.display = 'none'; } } } // 提交代码页增强 function enhanceSubmitPage(){ if(!document.documentElement.classList.contains('zzuli-ios-theme')) return; if(!/submitpage\.php/i.test(location.href)) return; if(document.body.dataset.zzuliSubmitEnhanced) return; document.body.dataset.zzuliSubmitEnhanced = '1'; const form = document.querySelector('form textarea, form .CodeMirror, form .ace_editor')?.closest('form'); if(!form) return; const params = new URLSearchParams(location.search); const pid = params.get('id') || params.get('pid') || params.get('problem_id') || '未知'; const langSelect = form.querySelector('select'); // 保留原生下拉框 const wrapper = document.createElement('div'); wrapper.className = 'zzuli-submit-wrapper'; form.parentElement.insertBefore(wrapper, form); wrapper.appendChild(form); const header = document.createElement('div'); header.className = 'zzuli-submit-header'; const title = document.createElement('div'); title.className = 'zzuli-submit-title'; title.textContent = `提交代码 #${pid}`; header.appendChild(title); // 不再构建分段语言控件,保留原 select wrapper.insertBefore(header, form); const toolbar = document.createElement('div'); toolbar.className = 'zzuli-submit-toolbar'; const statsChip = document.createElement('div'); statsChip.className='zzuli-chip-small'; statsChip.textContent='长度: 0'; toolbar.appendChild(statsChip); const autoSaved = document.createElement('div'); autoSaved.className='zzuli-auto-saved'; toolbar.appendChild(autoSaved); wrapper.insertBefore(toolbar, form); const submitBtn = form.querySelector('input[type=submit],button[type=submit],input[name=submit]'); const footer = document.createElement('div'); footer.className='zzuli-submit-footer'; const leftInfo = document.createElement('div'); leftInfo.className='zzuli-warning-unsaved zzuli-hidden'; leftInfo.textContent='有未保存的更改'; const rightActions = document.createElement('div'); rightActions.style.display='flex'; rightActions.style.gap='10px'; if(submitBtn){ submitBtn.classList.add('btn','btn-primary'); rightActions.appendChild(submitBtn); } footer.appendChild(leftInfo); footer.appendChild(rightActions); form.appendChild(footer); const codeNode = form.querySelector('.CodeMirror, .ace_editor, textarea'); if(codeNode){ const container = document.createElement('div'); container.className='zzuli-editor-container'; codeNode.parentElement.insertBefore(container, codeNode); container.appendChild(codeNode); const overlay = document.createElement('div'); overlay.className='zzuli-code-overlay'; overlay.textContent=''; container.appendChild(overlay); overlay.dataset.role='overlayStats'; } const textarea = form.querySelector('textarea'); function getCode(){ const cmEl = form.querySelector('.CodeMirror'); if(cmEl && cmEl.CodeMirror) return cmEl.CodeMirror.getValue(); const aceEl = form.querySelector('.ace_editor'); if(window.ace && aceEl && aceEl.env && aceEl.env.editor) return aceEl.env.editor.getValue(); return textarea ? textarea.value : ''; } function setCode(v){ const cmEl = form.querySelector('.CodeMirror'); if(cmEl && cmEl.CodeMirror){ cmEl.CodeMirror.setValue(v); return; } const aceEl = form.querySelector('.ace_editor'); if(window.ace && aceEl && aceEl.env && aceEl.env.editor){ aceEl.env.editor.setValue(v,-1); return; } if(textarea) textarea.value = v; } function draftKey(){ const langIdx = langSelect ? langSelect.selectedIndex : 0; return `zzuli_draft_${pid}_lang${langIdx}`; } function loadDraftForLang(){ const saved = localStorage.getItem(draftKey()); if(saved!=null){ setCode(saved); lastSavedValue=saved; updateStats(); autoSaved.textContent='已载入草稿'; setTimeout(()=>{ if(autoSaved.textContent==='已载入草稿') autoSaved.textContent='';},1500);} else { updateStats(); } } let lastSavedValue = getCode(); let debounceTimer = null; function scheduleAutosave(){ if(debounceTimer) clearTimeout(debounceTimer); debounceTimer = setTimeout(()=>{ if(getCode()!==lastSavedValue) performAutosave(); },1500); } function performAutosave(){ const code = getCode(); localStorage.setItem(draftKey(), code); lastSavedValue=code; autoSaved.textContent='已自动保存'; setTimeout(()=>{ if(autoSaved.textContent==='已自动保存') autoSaved.textContent=''; },1800); updateStats(); } function updateStats(){ const code = getCode(); const lines = code.length? code.split(/\n/).length : 0; statsChip.textContent = `长度: ${code.length} | 行: ${lines}`; leftInfo.classList.toggle('zzuli-hidden', code===lastSavedValue); const overlay = form.querySelector('.zzuli-code-overlay'); if(overlay){ let lineInfo=''; const cmEl = form.querySelector('.CodeMirror'); if(cmEl && cmEl.CodeMirror){ const pos = cmEl.CodeMirror.getCursor(); lineInfo = `Ln ${pos.line+1}, Col ${pos.ch+1}`; } else { const aceEl = form.querySelector('.ace_editor'); if(window.ace && aceEl && aceEl.env && aceEl.env.editor){ const pos = aceEl.env.editor.getCursorPosition(); lineInfo = `Ln ${pos.row+1}, Col ${pos.column+1}`; } } overlay.textContent = `${lineInfo}${lineInfo? ' | ' : ''}Len ${code.length} Lns ${lines}`; } } if(textarea) textarea.addEventListener('input', ()=>{ updateStats(); scheduleAutosave(); }); const cmEl = form.querySelector('.CodeMirror'); if(cmEl && cmEl.CodeMirror){ cmEl.CodeMirror.on('change', ()=>{ updateStats(); scheduleAutosave(); }); cmEl.CodeMirror.on('cursorActivity', updateStats); } const aceEl = form.querySelector('.ace_editor'); if(window.ace && aceEl && aceEl.env && aceEl.env.editor){ aceEl.env.editor.on('change', ()=>{ updateStats(); scheduleAutosave(); }); aceEl.env.editor.selection.on('changeCursor', updateStats); } if(langSelect){ langSelect.addEventListener('change', ()=>{ loadDraftForLang(); }); } if(submitBtn){ submitBtn.addEventListener('click', ()=>{ const k=draftKey(); setTimeout(()=>localStorage.removeItem(k),200); lastSavedValue=getCode(); updateStats(); }); } window.addEventListener('beforeunload', (e)=>{ if(getCode()!==lastSavedValue){ e.preventDefault(); e.returnValue=''; }}); loadDraftForLang(); updateStats(); } // 监听后续变化(若有异步加载) const observer = new MutationObserver(() => { if (!STATE.enabled) return; // 保留位置,后续可添加对动态节点的增强 }); observer.observe(document.documentElement, { childList: true, subtree: true, }); // 初始化 initialAdjust(); applyTheme(); updatePanelText(); // 延迟执行题目增强与提交页增强 setTimeout(()=>{ enhanceProblemPage(); enhanceSubmitPage(); },0); // ===== 补全提示后处理(拆分导入 / 来源 meta) ===== const hintObserver = new MutationObserver((muts)=>{ muts.forEach(m=>{ if(!(m.addedNodes||m.removedNodes)) return; document.querySelectorAll('.CodeMirror-hints').forEach(ul=>{ ul.querySelectorAll('li.CodeMirror-hint').forEach(li=>{ if(li.dataset.zzuliHintProcessed) return; const txt = li.textContent || ''; // 匹配常见格式:identifier — source / identifier - source / identifier · source / identifier (source) let match, namePart, metaPart; if((match = txt.match(/^(.+?)\s+[—\-·]\s+(.+?)$/))){ namePart = match[1]; metaPart = match[2]; } else if((match = txt.match(/^(.+?)\s+\(([^()]+)\)$/))){ namePart = match[1]; metaPart = match[2]; } if(namePart && metaPart){ li.textContent = ''; const spanLeft = document.createElement('span'); spanLeft.textContent = namePart.trim(); const spanMeta = document.createElement('span'); spanMeta.textContent = metaPart.trim(); spanMeta.className='zzuli-hint-meta'; li.appendChild(spanLeft); li.appendChild(spanMeta); li.dataset.zzuliHintProcessed = '1'; } else { li.dataset.zzuliHintProcessed = '1'; } }); }); }); }); hintObserver.observe(document.body,{ childList:true, subtree:true }); })();