/* ========================================================================== CyberStrikeAI — Mobile UI Layer -------------------------------------------------------------------------- 目标:在不改动 style.css(1.1MB)与既有桌面布局的前提下,用一层独立的 响应式样式把平台改造成"手机优先可用"的形态。 设计语言参考 QuantumNous/new-api 的移动端实现: - 断点 768px(useIsMobile: innerWidth < 768) - 底部滑出式面板(rounded sheet + backdrop-blur 遮罩 + spring 动画) - 100svh/dvh 视口单位、--app-header-height 变量驱动的布局高度 - 抽屉式导航 + 卡片化列表 本文件只在 max-width: 767.98px(以及平板/横屏区间)生效, 桌面端与 ≥768px 的既有表现完全不受影响。 ========================================================================== */ /* -------------------------------------------------------------------------- 1. 设计令牌 -------------------------------------------------------------------------- */ :root { --m-appbar-h: 52px; --m-tabbar-h: 58px; --m-safe-top: env(safe-area-inset-top, 0px); --m-safe-bottom: env(safe-area-inset-bottom, 0px); --m-safe-left: env(safe-area-inset-left, 0px); --m-safe-right: env(safe-area-inset-right, 0px); --m-drawer-w: min(84vw, 320px); --m-touch: 44px; --m-radius-sheet: 18px; --m-radius-card: 14px; --m-gutter: 14px; /* 24 节「按钮 / 控件视觉体系」的度量:全站按钮只有这一组尺寸口径。 改造前 style.css 里没有 .btn 基类,圆角 11 种、字号 9 种、字重 7 档、 同一个 .btn-secondary 出现 12 种签名(_mbtn.mjs 实测 645 个控件)。 */ --m-r-ctl: 12px; /* 图标按钮一律正圆、文字按钮一律胶囊:24 节那批 !important 规则读的就是这两个 令牌,改令牌一处生效全站,不要再往下补选择器或跟 !important 对轰 */ --m-r-icon: 50%; --m-r-btn: 999px; --m-r-pill: 999px; --m-ctl-h: var(--m-touch); --m-ctl-pad-x: 16px; --m-ctl-pad-x-sm: 12px; --m-ctl-fs: 14px; --m-ctl-fs-sm: 13px; --m-ctl-fw: 500; --m-ctl-fw-strong: 600; --m-ctl-gap: 8px; --m-ease: cubic-bezier(0.32, 0.72, 0, 1); --m-scrim: rgba(15, 23, 42, 0.5); --m-z-tabbar: 800; --m-z-scrim: 1200; --m-z-drawer: 1201; --m-z-sheet: 1202; --m-z-toast: 1300; /* 运行时由 mobile-ui.js 写入,作为 100dvh 不可用时的兜底 */ --m-app-height: 100vh; /* 贴底固定件(底栏 / 面板)的抬升量:iOS Safari 工具栏展开时,可见视口比 100dvh 矮一截,bottom:0 的固定件会被 Safari 底栏压住。JS 每次 resize 都会 重写 --m-app-height,所以这里能实时算出那一截是多少。默认 0,桌面与 不支持 dvh 的环境都不动。 */ --m-bar-bottom: 0px; } /* -------------------------------------------------------------------------- 2. 视口与滚动基础 -------------------------------------------------------------------------- */ @media (max-width: 767.98px) { html { height: 100%; overflow: hidden; } body { height: 100vh; height: 100dvh; height: var(--m-app-height, 100dvh); overflow: hidden; overscroll-behavior: none; -webkit-text-size-adjust: 100%; text-size-adjust: 100%; } .container { height: 100%; max-height: 100%; border-radius: 0; box-shadow: none; overflow: hidden; } /* 触摸设备:去掉 300ms 点击延迟与高亮块 */ a, button, .nav-item-content, .nav-submenu-item, [onclick], [role="button"] { -webkit-tap-highlight-color: transparent; touch-action: manipulation; } /* 双击缩放整体抑制(仍保留双指缩放),替代原先的全局 JS touchend 拦截 */ body { touch-action: manipulation; } /* iOS 输入聚焦自动放大:所有表单控件至少 16px */ input, select, textarea { font-size: 16px; } /* 滚动容器统一惯性滚动与边界收敛 */ .page-content, .main-sidebar-nav, .chat-messages, .conversation-list, .notification-list, .m-sheet-body { -webkit-overflow-scrolling: touch; overscroll-behavior: contain; } /* 触控目标下限 */ button, .btn-primary, .btn-secondary, .btn-danger, .btn-small, .nav-item-content, .nav-submenu-item, .user-menu-item { min-height: var(--m-touch); } button, .btn-primary, .btn-secondary, .btn-danger { display: inline-flex; align-items: center; justify-content: center; gap: 6px; } /* 桌面专属元素在手机上隐藏 */ .m-desktop-only { display: none !important; } } /* 手机专属元素默认隐藏,避免桌面端闪现 */ .m-mobile-only { display: none; } @media (max-width: 767.98px) { .m-mobile-only { display: block; } .m-mobile-only.m-flex { display: flex; } .m-mobile-only.m-inline-flex { display: inline-flex; } } /* -------------------------------------------------------------------------- 3. 顶部应用栏 -------------------------------------------------------------------------- */ @media (max-width: 767.98px) { /* Only the application bar has a fixed height and overlays page content. Nested headers (e.g. project details/settings) must grow with their content. */ .container > header { height: var(--m-appbar-h); min-height: var(--m-appbar-h); padding: 0 var(--m-gutter); padding-top: var(--m-safe-top); height: calc(var(--m-appbar-h) + var(--m-safe-top)); min-height: calc(var(--m-appbar-h) + var(--m-safe-top)); display: flex; align-items: center; flex-shrink: 0; position: relative; z-index: 20; background: var(--bg-primary); border-bottom: 1px solid var(--border-color); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06); } .header-content { display: flex; align-items: center; gap: 8px; width: 100%; height: 100%; } .header-logo-link { flex: 1; min-width: 0; gap: 8px; display: flex; align-items: center; } .header-logo-link .brand-logo { width: 28px; height: 28px; flex-shrink: 0; } .header-logo-link .brand-wordmark--lg { font-size: 1.05rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .version-badge { display: none; } .header-right { gap: 4px; flex-shrink: 0; } .header-actions { gap: 4px; align-items: center; } /* 桌面端的次要操作(API 文档 / GitHub / 主题 / 语言)收进"更多"面板 */ .header-actions > .openapi-doc-btn, .header-actions > .lang-switcher, .header-actions > .theme-toggle, .header-actions > #theme-toggle-btn, .header-actions > .theme-toggle-btn { display: none !important; } .header-actions > .notification-menu-container, .header-actions > .user-menu-container { display: flex; } /* 汉堡按钮 / 关闭按钮 */ .m-menu-btn, .m-more-btn, .m-close-btn { width: var(--m-touch); height: var(--m-touch); min-height: var(--m-touch); padding: 0; border: none; border-radius: 10px; background: transparent; color: var(--text-primary); flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; cursor: pointer; -webkit-tap-highlight-color: transparent; } .m-menu-btn:active, .m-more-btn:active, .m-close-btn:active { background: var(--bg-tertiary); } .m-menu-btn svg, .m-more-btn svg, .m-close-btn svg { width: 22px; height: 22px; } /* 通知与用户菜单是本层唯一漏掉 44px 下限的常驻控件:38px 在手指下是 「看得见但点不准」,与上面 --m-touch 的口径必须一致 */ .notification-btn, .user-avatar-btn { width: var(--m-touch); height: var(--m-touch); min-height: var(--m-touch); padding: 0; display: inline-flex; align-items: center; justify-content: center; } } /* -------------------------------------------------------------------------- 4. 侧边栏 → 抽屉导航 -------------------------------------------------------------------------- */ @media (max-width: 767.98px) { .main-layout { position: relative; overflow: hidden; min-height: 0; flex: 1; } .main-sidebar { position: fixed; top: 0; bottom: 0; left: 0; width: var(--m-drawer-w) !important; max-width: 86vw; height: 100%; height: 100dvh; height: var(--m-app-height, 100dvh); z-index: var(--m-z-drawer); transform: translateX(-102%); visibility: hidden; transition: transform 0.3s var(--m-ease), visibility 0.3s linear; box-shadow: none; border-right: 1px solid var(--border-color); border-radius: 0 var(--m-radius-sheet) var(--m-radius-sheet) 0; display: flex; flex-direction: column; overflow: hidden; } .main-sidebar.m-open { transform: translateX(0); visibility: visible; box-shadow: 0 18px 48px rgba(0, 0, 0, 0.45); } /* 折叠态在手机上无意义,强制展开 */ .main-sidebar.collapsed { width: var(--m-drawer-w) !important; } .main-sidebar.collapsed .nav-item-content span, .main-sidebar.collapsed .nav-submenu-item { display: revert; } /* 桌面折叠按钮由抽屉头部关闭按钮取代 */ .sidebar-collapse-btn { display: none !important; } /* 抽屉头部(由 mobile-ui.js 注入) */ .m-drawer-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 10px 10px 10px 14px; padding-top: calc(10px + var(--m-safe-top)); border-bottom: 1px solid var(--border-color); flex-shrink: 0; } .m-drawer-brand { display: flex; align-items: center; gap: 8px; min-width: 0; } .m-drawer-brand img { width: 26px; height: 26px; flex-shrink: 0; } .m-drawer-brand .brand-wordmark { font-size: 1.02rem; white-space: nowrap; } .m-drawer-search { padding: 8px 12px; border-bottom: 1px solid var(--border-color); flex-shrink: 0; } .m-drawer-search input { width: 100%; box-sizing: border-box; height: 38px; padding: 0 12px; border-radius: 10px; border: 1px solid var(--border-color); background: var(--input-bg); color: var(--text-primary); font-size: 15px; outline: none; } .m-drawer-search input:focus { border-color: var(--accent-color); } .m-nav-empty { padding: 10px 4px 2px; font-size: 13px; color: var(--text-muted); text-align: center; } .m-nav-empty[hidden] { display: none; } /* 抽屉内的导航项:更大、更好点 */ .main-sidebar-nav { flex: 1; overflow-y: auto; padding: 6px 8px calc(12px + var(--m-safe-bottom)); } .nav-section-label { padding: 12px 10px 6px; font-size: 0.7rem; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-muted); } .nav-item-content { padding: 11px 12px; min-height: 46px; font-size: 0.95rem; border-radius: 10px; gap: 12px; } .nav-item-content svg { width: 20px; height: 20px; flex-shrink: 0; } .nav-submenu-item { padding: 10px 12px 10px 44px; /* 原来是 42px——本层自己定的 --m-touch(44px) 下限被自己漏掉了一处 */ min-height: var(--m-touch); font-size: 0.9rem; border-radius: 8px; } .nav-item.active > .nav-item-content, .nav-item-content.active { background: color-mix(in srgb, var(--accent-color) 14%, transparent); color: var(--accent-color); } .nav-submenu-item.active { background: color-mix(in srgb, var(--accent-color) 12%, transparent); color: var(--accent-color); } /* 菜单搜索命中的项 */ .m-nav-hit > .nav-item-content, .nav-submenu-item.m-nav-hit { outline: 1px solid color-mix(in srgb, var(--accent-color) 45%, transparent); } .m-nav-hidden { display: none !important; } /* 遮罩 */ .m-scrim { position: fixed; inset: 0; z-index: var(--m-z-scrim); /* 全屏 backdrop-filter 在无 GPU 加速环境代价极高,用不透明度替代 */ background: var(--m-scrim); opacity: 0; visibility: hidden; transition: opacity 0.24s ease, visibility 0.24s linear; } .m-scrim.m-show { opacity: 1; visibility: visible; } } /* -------------------------------------------------------------------------- 5. 底部标签栏 -------------------------------------------------------------------------- */ @media (max-width: 767.98px) { .content-area { padding-bottom: calc(var(--m-tabbar-h) + var(--m-safe-bottom)); background: var(--bg-secondary); } #m-tabbar { position: fixed; left: 0; right: 0; bottom: var(--m-bar-bottom); z-index: var(--m-z-tabbar); display: flex; align-items: stretch; height: calc(var(--m-tabbar-h) + var(--m-safe-bottom)); padding-bottom: var(--m-safe-bottom); padding-left: var(--m-safe-left); padding-right: var(--m-safe-right); /* 固定底栏带 backdrop-filter 时,软件渲染每帧都要重算模糊,是移动端掉帧主因 */ background: var(--bg-primary); border-top: 1px solid var(--border-color); box-shadow: 0 -2px 14px rgba(0, 0, 0, 0.06); transition: transform 0.24s var(--m-ease); } #m-tabbar.m-hidden { transform: translateY(110%); } .m-tab { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px; padding: 6px 2px 4px; border: none; background: transparent; color: var(--text-muted); /* 原 0.66rem = 10.56px,是本层自己漏掉的"小字号下限 12px"(见 18.2), 底部标签栏是主导航,字号必须达标 */ font-size: 12px; line-height: 1.15; position: relative; border-radius: var(--m-r-ctl); transition: color 0.16s ease, transform 0.16s ease; -webkit-tap-highlight-color: transparent; touch-action: manipulation; } .m-tab svg { width: 23px; height: 23px; flex-shrink: 0; } .m-tab span { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .m-tab.m-active { color: var(--accent-color); } .m-tab.m-active svg { transform: translateY(-1px) scale(1.06); } .m-tab:active { transform: scale(0.94); } .m-tab-badge { position: absolute; top: 3px; left: 50%; margin-left: 6px; min-width: 18px; height: 18px; padding: 0 5px; border-radius: 9px; background: var(--error-color, #ef4444); color: #fff; font-size: 11px; line-height: 18px; text-align: center; font-weight: 600; box-sizing: border-box; } .m-tab-dot { position: absolute; top: 6px; left: 50%; margin-left: 8px; width: 8px; height: 8px; border-radius: 50%; background: var(--error-color, #ef4444); } /* 键盘弹出时让出空间 */ body.m-keyboard #m-tabbar { transform: translateY(110%); } body.m-keyboard .content-area { padding-bottom: 0; } } /* -------------------------------------------------------------------------- 6. 底部滑出面板(更多 / 快捷操作) -------------------------------------------------------------------------- */ @media (max-width: 767.98px) { #m-sheet { position: fixed; left: 0; right: 0; bottom: 0; z-index: var(--m-z-sheet); width: 95%; margin: 0 auto; bottom: calc(8px + var(--m-safe-bottom) + var(--m-bar-bottom)); background: var(--bg-primary); border: 1px solid var(--border-color); border-radius: var(--m-radius-sheet); box-shadow: 0 -8px 40px rgba(0, 0, 0, 0.32); transform: translateY(calc(100% + 24px)); opacity: 0; visibility: hidden; transition: transform 0.32s var(--m-ease), opacity 0.2s ease, visibility 0.32s linear; max-height: 78vh; max-height: 78dvh; display: flex; flex-direction: column; overflow: hidden; } #m-sheet.m-show { transform: translateY(0); opacity: 1; visibility: visible; } .m-sheet-grip { width: 38px; height: 4px; border-radius: 2px; background: var(--border-color); margin: 8px auto 2px; flex-shrink: 0; } .m-sheet-head { display: flex; align-items: center; justify-content: space-between; padding: 6px 16px 10px; flex-shrink: 0; } .m-sheet-title { font-size: 0.95rem; font-weight: 600; color: var(--text-primary); } .m-sheet-body { overflow-y: auto; padding: 0 10px 12px; display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px; } .m-sheet-item { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; padding: 12px 4px; border: none; background: transparent; border-radius: 12px; color: var(--text-secondary); font-size: 0.72rem; text-align: center; line-height: 1.2; min-height: 74px; } .m-sheet-item:active { background: var(--bg-tertiary); } .m-sheet-item svg { width: 22px; height: 22px; } .m-sheet-item.m-on { color: var(--accent-color); background: color-mix(in srgb, var(--accent-color) 10%, transparent); } .m-sheet-sep { grid-column: 1 / -1; height: 1px; background: var(--border-color); margin: 8px 6px; } } /* -------------------------------------------------------------------------- 7. 页面头 / 内容区 -------------------------------------------------------------------------- */ @media (max-width: 767.98px) { .page-header { padding: 10px var(--m-gutter); flex-direction: column; flex-wrap: nowrap; gap: 8px; align-items: stretch; border-radius: 0; } /* 标题独占一行,操作按钮区满宽换到下一行(避免 space-between 把按钮挤窄、 主按钮右端出屏被截)。按钮区内部仍可横向滚动,但占满整行、左对齐平齐。 */ .page-header h1, .page-header h2, .page-header h3 { font-size: 1.05rem; line-height: 1.3; width: 100%; } .page-header-actions { display: flex; align-items: center; gap: 6px; width: 100%; padding-bottom: 2px; flex-wrap: wrap; } .page-header-actions > * { flex: 0 0 auto; white-space: nowrap; } .page-content { padding: var(--m-gutter) var(--m-gutter) calc(var(--m-gutter) + 8px); overflow-x: hidden; } /* 页面切换的轻量过渡 */ .page.active { animation: m-page-in 0.22s var(--m-ease); } @keyframes m-page-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } } /* 卡片化:常见容器统一圆角与内边距 */ .card, .stat-card, .metric-card, .dashboard-card, .panel, .settings-section, .info-card { border-radius: var(--m-radius-card); } } /* -------------------------------------------------------------------------- 8. 表格 → 横向滚动 + 边缘渐隐 -------------------------------------------------------------------------- */ @media (max-width: 767.98px) { .m-table-scroll { overflow-x: auto; overflow-y: hidden; -webkit-overflow-scrolling: touch; overscroll-behavior-x: contain; margin-left: calc(-1 * var(--m-gutter)); margin-right: calc(-1 * var(--m-gutter)); padding: 0 var(--m-gutter); /* 左右渐隐阴影(Lea Verou 滚动阴影) */ background: linear-gradient(to right, var(--bg-primary) 30%, rgba(255, 255, 255, 0)) left / 32px 100% no-repeat local, linear-gradient(to left, var(--bg-primary) 30%, rgba(255, 255, 255, 0)) right / 32px 100% no-repeat local, radial-gradient(farthest-side at 0 50%, rgba(15, 23, 42, 0.16), rgba(15, 23, 42, 0)) left / 12px 100% no-repeat scroll, radial-gradient(farthest-side at 100% 50%, rgba(15, 23, 42, 0.16), rgba(15, 23, 42, 0)) right / 12px 100% no-repeat scroll; } .m-table-scroll > table { min-width: 560px; } .m-table-scroll.m-tight > table { min-width: 0; } table, .data-table { font-size: 0.82rem; } table th, table td, .data-table thead th, .data-table tbody td { padding: 9px 10px; white-space: nowrap; } /* 表格内的按钮别撑破行 */ table .btn-primary, table .btn-secondary, table .btn-danger, .data-table .btn-primary, .data-table .btn-secondary, .data-table .btn-danger { min-height: 34px; padding: 4px 10px; font-size: 0.78rem; } /* 单元格里的长文本允许换行 */ .data-table td .m-wrap, td.m-wrap { white-space: normal; min-width: 160px; } } /* -------------------------------------------------------------------------- 9. 表单与按钮 -------------------------------------------------------------------------- */ @media (max-width: 767.98px) { .form-group, .form-row { margin-bottom: 12px; } .form-group label { font-size: 0.85rem; margin-bottom: 6px; } input[type="text"], input[type="search"], input[type="password"], input[type="number"], input[type="email"], input[type="url"], input[type="date"], input[type="datetime-local"], select, textarea { width: 100%; box-sizing: border-box; min-height: var(--m-touch); padding: 10px 12px; border-radius: 10px; } /* Preserve the fact search icon gutter after the generic mobile input padding. */ .projects-fact-filter-field--search input { padding-left: 34px; } .projects-fact-search-icon { bottom: calc((var(--m-touch) - 16px) / 2); } textarea { min-height: 96px; resize: vertical; } /* 一行多列的表单在手机上堆叠 */ .form-grid, .form-row.multi, .settings-grid { grid-template-columns: 1fr !important; display: grid; gap: 12px; } .form-actions, .page-header-actions.m-stack { display: flex; flex-wrap: wrap; gap: 8px; } .form-actions > button, .modal-footer > button { flex: 1 1 auto; } } /* -------------------------------------------------------------------------- 10. 弹窗 / 下拉 / 浮层 -------------------------------------------------------------------------- */ @media (max-width: 767.98px) { .modal { padding: 0; } .modal-content { width: 100%; max-width: 100%; height: 100%; max-height: 100%; margin: 0; border-radius: 0; border: none; animation: m-sheet-up 0.26s var(--m-ease); padding-top: var(--m-safe-top); padding-bottom: var(--m-safe-bottom); box-sizing: border-box; } @keyframes m-sheet-up { from { transform: translateY(16px); opacity: 0.4; } to { transform: none; opacity: 1; } } .modal-header { padding: 12px var(--m-gutter); flex-shrink: 0; position: sticky; top: 0; background: var(--bg-primary); z-index: 2; border-bottom: 1px solid var(--border-color); } .modal-header h2, .modal-header h3 { font-size: 1.02rem; } .modal-body { padding: var(--m-gutter); flex: 1; overflow-y: auto; -webkit-overflow-scrolling: touch; } .modal-footer { padding: 10px var(--m-gutter); padding-bottom: calc(10px + var(--m-safe-bottom)); flex-shrink: 0; position: sticky; bottom: 0; background: var(--bg-primary); border-top: 1px solid var(--border-color); display: flex; gap: 8px; flex-wrap: wrap; } .modal-close, .modal .close { width: var(--m-touch); height: var(--m-touch); min-height: var(--m-touch); } /* 头部下拉:贴边全宽 */ .notification-dropdown, .user-menu-dropdown, .lang-dropdown { position: fixed !important; left: 8px !important; right: 8px !important; top: calc(var(--m-appbar-h) + var(--m-safe-top) - 4px) !important; width: auto !important; max-width: none !important; max-height: 70vh; overflow-y: auto; -webkit-overflow-scrolling: touch; border-radius: var(--m-radius-card); z-index: 1210; } /* 角色 / 项目 / 对话模式选择面板在手机上统一为底部抽屉,但**不盖输入框**: YG 反馈「输入框和这三个功能点重叠」,实测面板 bottom:66px 恰好盖住输入条 (.chat-input-container 占据视口底部 186px)。现在 bottom 用 JS 测得的 --m-panel-offset(输入条顶部到视口底 + 8px 间距),面板始终弹在输入条正上方, 微信输入面板同款行为:输入条全程可见可点,点输入条即收起面板。 对话模式面板原先漏在这一组之外,仍按桌面的 position:absolute + left:0 定位, 在 320–412px 视口下以按钮包裹层为基准取全屏宽度,右边缘实测越界 198px。 */ .role-selection-panel, .chat-project-panel, .agent-mode-panel { position: fixed !important; left: 8px !important; right: 8px !important; bottom: var(--m-panel-offset, calc(var(--m-tabbar-h) + var(--m-safe-bottom) + 8px)) !important; top: auto !important; width: auto !important; max-width: none !important; max-height: var(--m-panel-maxh, 62vh); border-radius: var(--m-radius-sheet); z-index: 1210; /* 面板与背景同色,上向投影把抽屉与聊天区明确分开 */ box-shadow: 0 -10px 30px rgba(0, 0, 0, 0.16), 0 -1px 0 rgba(0, 0, 0, 0.06); } /* 轻提示 */ #m-toast { position: fixed; left: 50%; /* 底栏按 --m-bar-bottom 抬升时提示条要一起抬,否则会压在底栏上 */ bottom: calc(var(--m-tabbar-h) + var(--m-safe-bottom) + 16px + var(--m-bar-bottom)); transform: translate(-50%, 12px); z-index: var(--m-z-toast); max-width: 88vw; padding: 10px 16px; border-radius: 999px; background: rgba(15, 23, 42, 0.92); color: #fff; font-size: 0.82rem; line-height: 1.35; text-align: center; opacity: 0; visibility: hidden; transition: opacity 0.2s ease, transform 0.24s var(--m-ease), visibility 0.24s; pointer-events: none; } #m-toast.m-show { opacity: 1; visibility: visible; transform: translate(-50%, 0); } } /* -------------------------------------------------------------------------- 11. 对话页 -------------------------------------------------------------------------- */ @media (max-width: 767.98px) { .chat-page-layout { flex-direction: row; } /* 会话列表 → 左侧抽屉 */ .conversation-sidebar { position: fixed; top: 0; bottom: 0; left: 0; width: var(--m-drawer-w) !important; max-width: 86vw; height: 100%; height: 100dvh; height: var(--m-app-height, 100dvh); z-index: var(--m-z-drawer); transform: translateX(-102%); visibility: hidden; transition: transform 0.3s var(--m-ease), visibility 0.3s linear; border-radius: 0 var(--m-radius-sheet) var(--m-radius-sheet) 0; box-shadow: none; padding-top: var(--m-safe-top); } .conversation-sidebar.m-open { transform: translateX(0); visibility: visible; box-shadow: 0 18px 48px rgba(0, 0, 0, 0.45); } .conversation-sidebar.collapsed { width: var(--m-drawer-w) !important; } /* 桌面语义的「折叠」在手机上只会加一个无效 class,隐藏掉,让位给关闭按钮 */ .conversation-sidebar-collapse-btn { display: none !important; } /* 抽屉只盖住左侧 320px,右侧露出的工具条按钮会误触,打开时整体隐藏 */ body.m-chat-drawer-open .m-chat-bar { visibility: hidden; } .m-chat-drawer-close { margin-left: auto; flex-shrink: 0; } .m-chat-drawer-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 8px 10px 8px var(--m-gutter); border-bottom: 1px solid var(--border-color); background: var(--bg-primary); flex-shrink: 0; } .m-chat-drawer-title { font-size: 0.9375rem; font-weight: 600; color: var(--text-primary); } .conversation-header { padding: 6px var(--m-gutter); } .chat-messages { padding: 12px 12px 12px 12px; } /* 消息气泡占满宽度 */ .chat-messages .message, .chat-messages .message-row, .chat-messages .conversation-message { max-width: 100%; } /* 轮次导航条在窄屏上太挤,直接收起 */ #chat-turn-rail { display: none !important; } #chat-return-latest { right: 10px; bottom: 12px; } .chat-input-container { padding: 8px 10px; gap: 6px; border-top: 1px solid var(--border-color); } .chat-input-primary-row { gap: 6px; } .chat-input-leading { gap: 6px; } /* 输入区里的次要按钮收成图标 */ .chat-input-container .role-selector-text { max-width: 88px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } /* 三个选择面板的关闭按钮桌面是 32×32,手机端放大到 44×44 满足触控 */ .role-selection-panel-close { width: var(--m-touch) !important; height: var(--m-touch) !important; min-width: var(--m-touch) !important; min-height: var(--m-touch) !important; } /* 发送按钮桌面是 40×40 圆形,手机端补足到 44×44(触控最小命中区) */ .chat-input-container .send-btn, #chat-send-btn { width: var(--m-touch) !important; min-width: var(--m-touch) !important; height: var(--m-touch) !important; min-height: var(--m-touch) !important; } .chat-input-container .send-btn svg { width: 20px; height: 20px; } /* ------------------------------------------------------------------ 19.1 触摸命中区统一补齐到 44×44(WCAG 2.5.8 / iOS HIG 最小可点区) 依据 _mtouch.mjs 实测:顶部栏图标 38×38、通用按钮高 40、 .btn-small 高 38、设置页导航项高 37、对话工具条高 40 —— 均低于 44。 只提升命中区高度/宽度,不改字号与配色。 ------------------------------------------------------------------ */ .header-logo-link, #notification-bell-btn, #user-avatar-btn, .m-chat-bar-btn, .chat-upload-btn, .btn-secondary, .btn-primary, .btn-small, .btn-danger, .settings-nav-item, .settings-custom-select-trigger, .page-header-actions button, .page-header-actions a { min-height: var(--m-touch) !important; } #notification-bell-btn, #user-avatar-btn, .chat-upload-btn { width: var(--m-touch) !important; min-width: var(--m-touch) !important; } /* 设置页导航 chip「C2」是最短项,实测只有 41px 宽 */ .settings-nav-item { min-width: var(--m-touch) !important; } /* 复选框/单选框的尺寸见 24.11(已并入按钮体系,避免两处各说一套) */ .chat-input-field textarea, .chat-input-field .chat-textarea { max-height: 132px; font-size: 16px; } #chat-hitl-approval-dock { border-radius: var(--m-radius-card); } /* 附件列表横向滚动 */ .chat-file-list { overflow-x: auto; flex-wrap: nowrap; -webkit-overflow-scrolling: touch; } /* 对话页移动端工具条(由 mobile-ui.js 注入到 .chat-container 首位) */ #m-chat-bar { display: flex; align-items: center; gap: 8px; flex-shrink: 0; padding: 6px 10px; background: var(--bg-primary); border-bottom: 1px solid var(--border-color); } .m-chat-bar-spacer { flex: 1; } .m-chat-bar-btn { display: inline-flex; align-items: center; gap: 6px; min-height: 36px; padding: 0 13px; border-radius: 999px; border: 1px solid var(--border-color); background: var(--bg-tertiary); color: var(--text-primary); font-size: 0.8125rem; font-weight: 500; line-height: 1; cursor: pointer; -webkit-tap-highlight-color: transparent; transition: transform 0.12s var(--m-ease), background 0.15s; } .m-chat-bar-btn svg { width: 16px; height: 16px; flex-shrink: 0; } .m-chat-bar-btn:active { transform: scale(0.95); } .m-chat-bar-btn.m-primary { background: var(--accent-color); border-color: var(--accent-color); color: #ffffff; } /* _mchat12.mjs 实测:会话抽屉条目 38px、会话设置折叠头 39px、 HITL 审批人开关仅 30px——都藏在抽屉/折叠区,之前全站扫描扫不到。 这些是可点交互控件,统一提到 44px。 */ #conversation-sidebar .conversation-item, #conversation-sidebar .project-conversation-item, #conversation-reasoning-toggle, .hitl-reviewer-toggle-btn, .hitl-config-select, .hitl-config-field .hitl-config-label { min-height: var(--m-touch) !important; } .hitl-reviewer-toggle-btn { display: inline-flex; align-items: center; } } /* -------------------------------------------------------------------------- 12. 终端 / WebShell / C2 -------------------------------------------------------------------------- */ @media (max-width: 767.98px) { .webshell-layout, .c2-layout, .terminal-layout { flex-direction: column !important; } .webshell-sidebar, .c2-sidebar, .terminal-sidebar { width: 100% !important; max-width: none !important; max-height: 40vh; border-right: none; border-bottom: 1px solid var(--border-color); } .terminal-container, .xterm { font-size: 12px; } .xterm-viewport { -webkit-overflow-scrolling: touch; overscroll-behavior: contain; } /* 终端工具条横向滚动 */ .terminal-toolbar, .webshell-toolbar, .c2-toolbar { overflow-x: auto; flex-wrap: nowrap; scrollbar-width: none; -webkit-overflow-scrolling: touch; } .terminal-toolbar::-webkit-scrollbar, .webshell-toolbar::-webkit-scrollbar, .c2-toolbar::-webkit-scrollbar { display: none; } } /* -------------------------------------------------------------------------- 13. 登录页 -------------------------------------------------------------------------- */ @media (max-width: 767.98px) { .login-overlay { padding: 16px; padding-top: calc(16px + var(--m-safe-top)); padding-bottom: calc(16px + var(--m-safe-bottom)); align-items: center; overflow-y: auto; -webkit-overflow-scrolling: touch; } .login-card { width: 100%; max-width: 420px; padding: 22px 18px; border-radius: var(--m-radius-sheet); margin: auto; } .login-title { font-size: 1.25rem; } .login-submit { min-height: 48px; font-size: 1rem; width: 100%; } } /* -------------------------------------------------------------------------- 14. 网格 / 统计卡 / 图表 -------------------------------------------------------------------------- */ @media (max-width: 767.98px) { .stats-grid, .kpi-grid, .cards-grid, .metrics-grid, .dashboard-grid, .overview-grid { grid-template-columns: 1fr !important; gap: 10px; } .stats-grid.m-two, .kpi-grid.m-two { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; } /* 图表容器限高,避免 canvas 撑出屏幕 */ canvas { max-width: 100% !important; } .chart-container, .graph-container, #fact-graph, #asset-graph { min-height: 260px; max-height: 62vh; } /* 标签 / 徽章不换行溢出 */ .badge, .tag, .status-badge, .pill { white-space: nowrap; } } /* -------------------------------------------------------------------------- 15. 平板区间(768–1023.98px):保留桌面结构,仅做收敛 -------------------------------------------------------------------------- */ @media (min-width: 768px) and (max-width: 1023.98px) { .main-sidebar { width: 190px; } .page-content { padding: 14px 16px; } .chat-messages { padding: 16px 16px 16px 24px; } .conversation-sidebar { width: 264px; } } /* -------------------------------------------------------------------------- 16. 横屏手机:把底部栏压薄,让出纵向空间 -------------------------------------------------------------------------- */ @media (max-width: 900px) and (orientation: landscape) and (max-height: 500px) { :root { --m-tabbar-h: 46px; --m-appbar-h: 44px; } #m-tabbar .m-tab span { display: none; } #m-tabbar .m-tab svg { width: 21px; height: 21px; } .main-sidebar { width: min(70vw, 300px) !important; } } /* -------------------------------------------------------------------------- 17. 无障碍与性能 -------------------------------------------------------------------------- */ @media (max-width: 767.98px) { @media (prefers-reduced-motion: reduce) { .main-sidebar, .conversation-sidebar, #m-tabbar, #m-sheet, .m-scrim, #m-toast { transition-duration: 0.01ms !important; } .page.active { animation: none !important; } } /* 焦点可见性(键盘 / 辅助设备) */ a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible, [tabindex]:focus-visible { outline: 2px solid var(--accent-color); outline-offset: 2px; } } /* 抽屉/面板打开时锁定背景滚动 */ body.m-locked { overflow: hidden !important; } /* -------------------------------------------------------------------------- 17.6 选择面板层级修正(关键) -------------------------------------------------------------------------- .chat-input-container 带 container-type: inline-size,会创建独立的层叠 上下文,导致内部 position:fixed 的 .chat-project-panel / .role-selection-panel / .agent-mode-panel 的 z-index:1210 只在容器内部比较,无法与 body 级 #m-scrim(z-index:1200) 竞争 —— 实测 elementFromPoint 命中 .m-scrim, 面板收不到任何触摸事件,下滑关闭失效。 面板打开期间把容器提升到 1215(高于 scrim 1200 / drawer 1201 / sheet 1202, 低于 toast 1300),容器本身无 position,故需显式 position: relative。 -------------------------------------------------------------------------- */ body.m-panel-open .chat-input-container { position: relative; z-index: 1215; } /* -------------------------------------------------------------------------- 18. 可读性 / 对比度 / 溢出修复 -------------------------------------------------------------------------- 依据 _mvisual.mjs 实测:light 主题 --text-muted(#adb5bd) 在白底上只有 2.07:1,--text-secondary(#6c757d) 4.69:1,均不满足 WCAG AA(4.5:1); 11px 标签在手机上不可读;WebShell 工作区比视口宽 5px;设置页下拉选中值 与 AI 通道 chip 被 flex 压缩到 6~22px 宽。 以下仅在移动端生效,桌面端保持原样。 -------------------------------------------------------------------------- */ @media (max-width: 767.98px) { /* 18.1 文本色令牌提到 AA 级 */ html:not([data-theme="dark"]) { --text-secondary: #525d6c; /* 白底 6.6:1,灰底(≥#f8fafc)仍 ≥6.2:1 */ --text-muted: #5b6472; /* 白底 6.0:1,浅灰底(#f1f3f5) 5.5:1 —— 原 #adb5bd 仅 2.07:1 */ /* 原 #3b82f6 作正文色在白底仅 3.68:1,深一档后 5.2:1 */ --accent-color: #2563eb; } html[data-theme="dark"] { --text-muted: #9ca3af; /* #111827 底 7.2:1 */ } /* 18.2 小字号下限 12px */ .dashboard-severity-insight-score-label, .dashboard-severity-insight-urgent-label, .dashboard-severity-center-label, .dashboard-severity-status-label, .mcp-stats-kpi__label, .mcp-stats-kpi__chip, .pagination-info, .pagination-page, .hitl-tab-badge, .version-badge, /* 补漏(_mprobe.mjs 实测仍为 11px):projects 页的状态胶囊、统计 chip、 事实筛选标签,以及分页的「每页」标签 */ .projects-status-pill, .projects-stat-chip, .projects-fact-filter-label, .pagination-page-size { font-size: 12px !important; } /* 18.2b 第九轮(审计覆盖从假 14 页修成真实 29 页后)新暴露的小字。 asset-overview 的资产类型统计标签 11px、协议表头 9px; agents-management 的角色徽章 11px;platform-rbac 的状态胶囊 11px。 图标字符(如 .tool-expand-icon 的 ▶ 10px)不属于文本,不在此列。 */ .asset-type-item span, .asset-protocol-compact__header p, .asset-panel-meta, .asset-protocol-columns span, .agent-role-badge, .rbac-pill { font-size: 12px !important; } /* 18.3 语义色徽章/数值加粗,补偿配色本身的低对比 */ .dashboard-severity-insight-risk-badge, .dashboard-severity-status-value, .dashboard-severity-insight-score-value { font-weight: 700; } /* 18.4 WebShell / C2 / 终端工作区横向溢出 */ .webshell-main, .webshell-workspace, .c2-main, .terminal-main { width: 100% !important; max-width: 100% !important; min-width: 0 !important; box-sizing: border-box; overflow-x: hidden; } /* 18.5 设置页自定义下拉:选中值不再被压扁 */ *:has(> .settings-custom-select-value) { min-width: 0; } .settings-custom-select-value { flex: 1 1 max-content !important; min-width: 0 !important; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } /* 18.6 AI 通道 chip:容器占满整行并允许换行,chip 不再被压到不可读 */ *:has(> .ai-channel-editor-chip) { flex: 1 1 100% !important; flex-wrap: wrap !important; width: 100%; min-width: 0; row-gap: 6px; } .ai-channel-editor-chip { flex: 0 0 auto !important; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } /* 18.7 对话页人工审批标签:原思路是"缩小 1px 就能完整显示",实测不成立 —— 见 24.14 的真实根因(max-width:142px 才是瓶颈,缩字号只是把裁切推迟)。 */ /* 18.8 dashboard 残留 11px 标签 */ .dashboard-batch-side-stat-label, .dashboard-severity-progress-legend-item span, .dashboard-severity-status-text span:not([id]) { font-size: 12px !important; } /* 18.9 硬编码浅灰文本(不随令牌走)逐个纠正 */ #c2-sessions-count, #page-knowledge-management .empty-state p, #page-knowledge-management .empty-state div { color: var(--text-secondary) !important; } /* 18.10 语义色徽章 / 状态数值:加深到 AA 级 */ .role-card-badge.enabled { color: #157347; background: rgba(40, 167, 69, 0.16); } #dashboard-status-fp { color: #64748b !important; } /* 18.10b 第七轮补:语义色**文字**再次加深(原 2.09~2.88:1) 重算方式不是拿白底硬套,而是用 `_mcontrast.mjs` 逐层合成真实有效背景 (徽章自己还叠了 rgba(34,197,94,0.12)+0.04 两层底 → #dcf6e6),再对 "保持色相与饱和度、只压亮度"做二分求解,目标 5.0:1(比 AA 的 4.5 留一点 余量,避免不同背景合成算法得出不同结论)。 最要紧的是 dashboard 的「安全」徽章与四个状态计数——它们是页面主数据, 2.09:1 在手机户外基本读不出来。原则:**只动亮度,不动色相**,所以 「红=高危 / 绿=已修复 / 琥珀=待确认 / 灰=已忽略」的语义完整保留, 纠正的只是「把填充色直接当文字色用」这个反模式。 深色主题不受影响(同 18.14 的做法,见 23.4 的对称处理)。 */ html:not([data-theme="dark"]) #dashboard-severity-risk-level { color: #107636 !important; } html:not([data-theme="dark"]) #dashboard-status-open { color: #d81212 !important; } html:not([data-theme="dark"]) #dashboard-status-confirmed { color: #986206 !important; } html:not([data-theme="dark"]) #dashboard-status-fixed { color: #167e3c !important; } html:not([data-theme="dark"]) #dashboard-status-ignored { color: #5a6d88 !important; } html:not([data-theme="dark"]) .dashboard-recommend-arrow { color: #67707f !important; } html:not([data-theme="dark"]) .projects-fact-filter-label { color: #5d718c !important; } /* 18.10c 必填项星号:样式表里有 4 种写法(`.required-mark` / `.form-required` / `.required` / 内联 `style="color: red;"`),同一种语义 4 套颜色。 其中内联那种是纯 #ff0000,在白底上只有 4.0:1(星号也是文字,归 AA)。 统一到 18.11 已经在用的 #b02a37(≈6.0:1),一个语义一个颜色。 内联样式优先级高于普通规则,必须 !important 才压得住。 */ html:not([data-theme="dark"]) label > span[style*="color: red"], html:not([data-theme="dark"]) label > span[style*="color:red"], html:not([data-theme="dark"]) label > span.form-required, html:not([data-theme="dark"]) label > span.required, html:not([data-theme="dark"]) label > span.required-mark { color: #b02a37 !important; } /* 18.11 白底红字按钮:原 #dc3545 仅 4.0:1 */ .btn-secondary.btn-danger, .btn-secondary.btn-delete { color: #b02a37 !important; } /* 18.12 C2 工具栏文字型按钮(无类名,继承 accent 色); 排除带内联背景的按钮,避免文字色与底色撞车 */ #page-c2-sessions button:not(.btn-primary):not(.btn-secondary):not(.btn-danger):not([style*="background"]) { color: #1d4ed8 !important; } /* 18.13 dashboard / HITL / 知识库残留的低对比文本(原 3.3~4.5:1) */ #dashboard-fix-rate, .dashboard-feed-tab { color: var(--text-secondary) !important; } .dashboard-feed-tab.active { color: var(--accent-color) !important; } .dashboard-empty-action, #page-knowledge-management .empty-state button, #page-knowledge-management .empty-state .btn, #hitl-pending-count { background-color: #2563eb !important; border-color: #2563eb !important; color: #ffffff !important; font-weight: 600; } /* 18.14 硬编码灰/蓝文本再深一档(实测 4.0~4.34:1) 注意:这个深灰只在亮色主题下成立,深色主题下由区块 23.4 覆写。 */ html:not([data-theme="dark"]) #chat-hitl-shortcut-text, html:not([data-theme="dark"]) #page-hitl .hitl-reviewer-toggle-btn, html:not([data-theme="dark"]) #page-hitl .hitl-reviewer-toggle-btn span { color: #4a5462 !important; } #projects-list-count { color: #0052cc !important; } #page-settings .required-mark { color: #b02a37 !important; } } /* ========================================================================== 19. 设置页两栏布局在移动端塌陷(实测正文区仅剩 98px 宽) -------------------------------------------------------------------------- 根因:.page-content.settings-layout 是 flex-direction:row, .settings-sidebar 固定 200px。390px 视口下 .settings-content 只剩 162px, 再扣 32px×2 内边距 → 正文 98px,下拉选中值与 AI 通道 chip 被压到 6~22px。 方案:移动端改单列,左侧导航变成顶部横向滚动的胶囊标签条。 ========================================================================== */ @media (max-width: 767.98px) { #page-settings .page-content.settings-layout { flex-direction: column !important; padding: 0 0 16px !important; } #page-settings .settings-sidebar { width: 100% !important; max-width: 100% !important; height: auto !important; flex: 0 0 auto !important; border-right: 0 !important; border-bottom: 1px solid var(--border-color); background: var(--bg-primary); position: sticky; top: 0; z-index: 5; } #page-settings .settings-nav { display: flex !important; flex-direction: row !important; width: 100% !important; height: auto !important; min-height: 0 !important; overflow-x: auto !important; overflow-y: hidden !important; gap: 6px; padding: 8px 10px; box-sizing: border-box; scrollbar-width: none; -webkit-overflow-scrolling: touch; } #page-settings .settings-nav::-webkit-scrollbar { display: none; } #page-settings .settings-nav-item { flex: 0 0 auto !important; width: auto !important; max-width: none !important; white-space: nowrap; padding: 8px 13px; border-radius: 999px; font-size: 13px; border-left: 0 !important; border-bottom: 0 !important; } #page-settings .settings-nav-item.active { background: var(--accent-color); color: #ffffff; } #page-settings .settings-content { width: 100% !important; max-width: 100% !important; min-width: 0 !important; flex: 1 1 auto !important; padding: 14px 12px 24px !important; box-sizing: border-box; overflow-x: hidden; } /* 19.1 设置页内所有可伸缩容器放开宽度,禁止再被压缩 */ #page-settings .settings-section-content, #page-settings .settings-subsection, #page-settings .ai-channel-manager, #page-settings .ai-channel-manager-body, #page-settings .ai-channel-editor, #page-settings .ai-channel-editor-head, #page-settings .ai-channel-editor-meta, #page-settings .ai-channel-switcher, #page-settings .ai-channel-switcher-field, #page-settings .settings-custom-select, #page-settings .settings-custom-select-trigger { width: 100% !important; max-width: 100% !important; min-width: 0 !important; box-sizing: border-box; } /* 19.2 通道选择区原为 grid,移动端改为单列堆叠 */ #page-settings .ai-channel-switcher-field { display: block !important; } /* 19.3 设置页宽表(审计日志 / 存储明细)自带 min-width:560px, 在 390px 视口直接撑破页面 → 改为容器内横向滚动 */ #page-settings *:has(> table.audit-log-table) { overflow-x: auto !important; max-width: 100% !important; box-sizing: border-box; -webkit-overflow-scrolling: touch; } #page-settings table.audit-log-table { max-width: none; } /* 19.4 审计筛选区:.audit-field 固定 min-width:150px, 320px 视口下第二个字段被挤出屏幕 → 允许压缩 + 整行换行 */ #page-settings .audit-field { min-width: 0 !important; flex: 1 1 140px !important; } #page-settings .audit-datetime-field, #page-settings .audit-datetime-btn { min-width: 0 !important; } #page-settings *:has(> .audit-field) { flex-wrap: wrap !important; } } /* -------------------------------------------------------------------------- 20. 页面头功能按钮布局 / 工具条触控 / 底部安全区 -------------------------------------------------------------------------- */ @media (max-width: 767.98px) { /* 20.1 页面头功能按钮位置 根因:style.css:19132 的 @media (max-width: 768px) 把 .page-header-actions 设为 flex-direction: column; align-items: stretch;。本文件此前只覆盖了 align-items / flex-wrap / width,没有覆盖 flex-direction,于是 「刷新 / 批量导入 / 新增资产」这类按钮在手机上各占一行、各自居中。 改为横向换行自适应排列:能一行就一行(宽度 ≥444 左右),放不下自动换行, 任何设备宽度下按钮都完整可见、无需横向滑动。 注意:不要改回 nowrap + overflow-x —— 那会让主按钮(保存/新建)被推到 视口外需横滑才能看到,违背"自适应"诉求。 */ .page-header-actions, .page-header > .page-header-actions, .platform-rbac-page .page-header-actions, .mcp-monitor-page .page-header-actions { flex-direction: row !important; flex-wrap: wrap !important; align-items: center !important; justify-content: flex-start !important; gap: 8px !important; width: 100%; padding-bottom: 0; } /* 20.1b「已与服务端同步」状态块(tool-guard)84px 太占宽,会把主按钮挤出 一行。允许它在窄容器里收缩省略,留出空间给按钮。 */ .page-header-actions > .tool-guard-save-state { flex: 0 1 auto; min-width: 0; max-width: 96px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 11px; } .page-header-actions > * { flex: 0 0 auto; white-space: nowrap; } /* 头部文字按钮统一到可点尺寸并留出横向内边距 */ .page-header-actions > button, .page-header-actions > a.btn, .page-header-actions > .btn-primary, .page-header-actions > .btn-secondary, .page-header-actions > .btn-danger { min-height: var(--m-touch) !important; min-width: 64px; padding-left: 14px !important; padding-right: 14px !important; } /* 20.2 图标型按钮(分页 ‹ › 等)原本只有 20~32px 宽,点不中 */ .btn-icon-pagination, .btn-icon, .table-icon-btn, .page-size-select { min-width: var(--m-touch) !important; min-height: var(--m-touch) !important; } /* 20.3 工作流画布工具条:5 个按钮挤在 32px 宽里,文字被截断 */ .workflow-toolbar, .workflow-toolbar-actions, .workflow-canvas-toolbar, .workflow-toolbar-group { flex-wrap: wrap !important; gap: 6px !important; } .workflow-toolbar-button, .workflow-save-button { min-width: 72px !important; min-height: var(--m-touch) !important; padding: 0 10px !important; font-size: 12px; } /* 20.4 小号按钮保底触控高度(原 .btn-small 只有 30~36px 高) */ .btn-small { min-height: var(--m-touch) !important; padding: 0 12px !important; } /* 20.5 底部安全区:在标签栏之上再留 10px, 避免滚到底后最后一行控件仍贴住标签栏 */ .content-area { padding-bottom: calc(var(--m-tabbar-h) + var(--m-safe-bottom) + 10px); } /* 20.6 页面内自定义下拉触发器高度不足 44px */ .settings-custom-select-trigger, .monitor-filter-select-trigger, .projects-filter-select-trigger { min-height: var(--m-touch) !important; } /* 20.7 对话页顶部工具条按钮原为 36px 高 */ .m-chat-bar-btn { min-height: var(--m-touch) !important; padding: 0 12px !important; } /* 20.8 上传等纯图标按钮宽高都不足 */ .chat-upload-btn { min-width: var(--m-touch) !important; min-height: var(--m-touch) !important; } } /* ========================================================================== 21. 内容区滚动模型修复(手机端局部滚动容器塌陷) -------------------------------------------------------------------------- 桌面端这些页面用「固定高度容器 + 内部 overflow:hidden」把滚动锁在局部, 例如 .asset-list-card 自己滚分页。390x664 的手机视口下父级 flex 空间不足, 这些局部滚动容器被压到 16~48px 高,内部控件被 overflow:hidden 裁掉, 而且无论怎么滑动都露不出来。改成「内容区整页滚动 + 内部容器按内容展开」。 ========================================================================== */ @media (max-width: 767.98px) { /* 21.1 资产库:.asset-list-card 实测仅 48px 高,里面的分页控件 183px 全被裁 */ .page-content.asset-content { display: block !important; overflow-y: auto !important; overflow-x: hidden !important; -webkit-overflow-scrolling: touch; } .asset-list-card { display: block !important; flex: none !important; height: auto !important; max-height: none !important; min-height: 0 !important; overflow: visible !important; } #asset-pagination, .pagination-container.pagination-fixed { position: static !important; height: auto !important; min-height: 0 !important; } /* 21.2 技能管理:#skills-list 实测仅 16px 高,5483px 内容挤成一条缝 */ .page-content.page-content-with-pagination { display: block !important; overflow-y: auto !important; overflow-x: hidden !important; -webkit-overflow-scrolling: touch; } #skills-list, .skills-grid { flex: none !important; height: auto !important; max-height: none !important; min-height: 0 !important; overflow: visible !important; } #skills-pagination { position: static !important; height: auto !important; } /* 21.3 平台 RBAC:.rbac-panel-detail 实测 2px、#rbac-tab-roles 28px */ .platform-rbac-page .page-content { display: block !important; overflow-y: auto !important; overflow-x: hidden !important; -webkit-overflow-scrolling: touch; } #rbac-view-users, .rbac-layout { display: block !important; height: auto !important; min-height: 0 !important; overflow: visible !important; } .rbac-panel, .rbac-tab-panel, #rbac-tab-roles, #rbac-tab-permissions, #rbac-tab-resources, #rbac-tab-audit { flex: none !important; height: auto !important; max-height: none !important; min-height: 0 !important; overflow: visible !important; } /* 21.4 工作流:侧栏节点列表与画布工具条被 overflow:hidden 裁掉 */ .page-content.workflow-page-content { display: block !important; min-height: 0 !important; overflow-y: auto !important; overflow-x: hidden !important; -webkit-overflow-scrolling: touch; } .workflow-sidebar { height: auto !important; max-height: none !important; overflow: visible !important; } /* 画布本身需要一块固定高度,不能被内容压成 0 */ .workflow-main { height: auto !important; min-height: 380px !important; max-height: none !important; overflow: visible !important; } /* Projects: one vertical scroller must fit between the page heading and bottom navigation. Desktop viewport minimums and nested flex scrollers otherwise put the final rows outside the clipped mobile page. */ #page-projects .page-content.projects-page-layout { display: block; min-height: 0; overflow-y: auto; } #page-projects .projects-sidebar-card { height: auto; min-height: 0; max-height: none; margin-bottom: 12px; } #page-projects .projects-list { max-height: 220px; } #page-projects .projects-detail, #page-projects .projects-detail-inner, #page-projects .projects-panel, #page-projects .projects-table-wrap, #page-projects .projects-settings-layout { flex: none; height: auto; min-height: 0; max-height: none; } #page-projects .projects-panel, #page-projects .projects-settings-layout { overflow: visible; } #page-projects .projects-table-wrap { overflow-x: auto; overscroll-behavior-y: auto; } /* The project wrapper already owns scrolling; the generic mobile wrapper must not add a second scrollbar or negative gutters inside the card. */ #page-projects .projects-table-wrap > .m-table-scroll { overflow: visible; margin: 0; padding: 0; background: none; } /* Eight fact columns need readable widths. Keep one horizontal scroll area and let actions scroll with their row instead of covering other cells. */ #page-projects #project-panel-facts .data-table--projects { width: 1440px; min-width: 1440px; } #page-projects #project-panel-facts th:nth-child(1) { width: 160px; } #page-projects #project-panel-facts th:nth-child(2) { width: 120px; } #page-projects #project-panel-facts th:nth-child(3) { width: 280px; } #page-projects #project-panel-facts th:nth-child(4) { width: 100px; } #page-projects #project-panel-facts th:nth-child(5) { width: 120px; } #page-projects #project-panel-facts th:nth-child(6) { width: 140px; } #page-projects #project-panel-facts th:nth-child(7) { width: 160px; } #page-projects #project-panel-facts th:nth-child(8) { width: 360px; } #page-projects #project-panel-facts .data-table--projects .col-actions { position: static; max-width: none; box-shadow: none; } #page-projects #project-panel-facts .col-actions .projects-table-actions { flex-wrap: wrap; } #page-projects #project-panel-facts :is(.projects-category, .projects-fact-badge, .projects-confidence) { max-width: 100%; box-sizing: border-box; white-space: normal; overflow-wrap: anywhere; } #page-projects #project-panel-assets .data-table--projects { height: auto; } #page-projects #project-panel-assets .data-table--projects tbody { flex: none; overflow: visible; } #page-projects .project-fact-graph-layout { flex: none; height: 360px; max-height: none; } /* 21.5 C2 会话「重置」按钮实测 24x44,横向点不中 */ #page-c2-sessions button, .c2-sessions-page button { min-width: 44px !important; } } /* -------------------------------------------------------------------------- 22. 平板竖屏(768–1023.98px):对话页三个选择面板同样贴底 桌面规则 style.css:29402 的 @media (max-width:768px) 把 .role-selection-panel / .agent-mode-panel 改成 width:calc(100vw - 16px) + left:-8px,但仍是 position:absolute,定位基准是各自在 100–200px 处的按钮包裹层;实测 768x1024 下两者右边缘分别越界 563px 与 654px,面板完全落在屏幕之外。 -------------------------------------------------------------------------- */ @media (min-width: 768px) and (max-width: 1023.98px) { .role-selection-panel, .chat-project-panel, .agent-mode-panel { position: fixed !important; left: 16px !important; right: 16px !important; bottom: var(--m-panel-offset, 16px) !important; top: auto !important; width: auto !important; max-width: none !important; max-height: var(--m-panel-maxh, 62vh); border-radius: var(--m-radius-sheet); z-index: 1210; box-shadow: 0 -10px 30px rgba(0, 0, 0, 0.16), 0 -1px 0 rgba(0, 0, 0, 0.06); } } /* -------------------------------------------------------------------------- 22.2 触摸型横屏平板(1024–1439.98px + pointer:coarse):模式面板仍会越界 _mchat11.mjs 实测 1024x768 下 .agent-mode-panel 右边缘 1035 > 1024(offR=11)。 桌面鼠标保持原样(_mdesktop.mjs 已证明 1024x768 的桌面溢出为既有问题,不归本层管)。 -------------------------------------------------------------------------- */ @media (min-width: 1024px) and (max-width: 1439.98px) and (pointer: coarse) { .role-selection-panel, .chat-project-panel, .agent-mode-panel { position: fixed !important; left: 16px !important; right: 16px !important; bottom: var(--m-panel-offset, 16px) !important; top: auto !important; width: auto !important; max-width: none !important; max-height: var(--m-panel-maxh, 62vh); border-radius: var(--m-radius-sheet); z-index: 1210; box-shadow: 0 -10px 30px rgba(0, 0, 0, 0.16), 0 -1px 0 rgba(0, 0, 0, 0.06); } } /* -------------------------------------------------------------------------- 23. 深色主题对比度修正(_mdark.mjs 全站实测出 4 类违规) 深色令牌里 --accent-color 是 #60a5fa(浅蓝),配白字只有 2.54:1; 语义绿 #15803d 在 #111f29 底上 3.33:1;危险红 #b02a37 在 #111827 底上 2.73:1。 统一只改文字色、不动背景,保证亮色主题与桌面端完全不受影响。 -------------------------------------------------------------------------- */ @media (max-width: 767.98px) { /* 23.1 浅蓝底(#60a5fa)上的白字 → 深墨蓝 #0b1120,实测 9.4:1 */ html[data-theme="dark"] .btn-primary, html[data-theme="dark"] .btn-primary:hover, html[data-theme="dark"] .btn-primary:focus, html[data-theme="dark"] .m-chat-bar-btn.m-primary, html[data-theme="dark"] .settings-nav-item.active, html[data-theme="dark"] .chat-input-container .send-btn, html[data-theme="dark"] .ai-channel-save-btn, html[data-theme="dark"] #hitl-pending-count, html[data-theme="dark"] .dashboard-empty-action, html[data-theme="dark"] #page-knowledge-management .empty-state button, html[data-theme="dark"] #page-knowledge-management .empty-state .btn { color: #0b1120 !important; } html[data-theme="dark"] .btn-primary svg, html[data-theme="dark"] .m-chat-bar-btn.m-primary svg, html[data-theme="dark"] .chat-input-container .send-btn svg, html[data-theme="dark"] .settings-nav-item.active svg { color: #0b1120 !important; fill: currentColor; stroke: currentColor; } /* 23.2 语义绿在深底上提亮:#15803d → #4ade80,实测 8.9:1 */ html[data-theme="dark"] .is-success, html[data-theme="dark"] .dashboard-kpi-sub-text.is-success, html[data-theme="dark"] .mcp-stats-kpi__value--rate.is-success, html[data-theme="dark"] .mcp-stats-kpi__status.is-success, html[data-theme="dark"] .mcp-stats-tool-item__rate.is-success, html[data-theme="dark"] .mcp-stats-rate.is-success { color: #4ade80 !important; } /* 刷新闪烁态「上次更新」徽章同样是硬编码 #15803d */ html[data-theme="dark"] .dashboard-last-updated.is-flash { color: #4ade80 !important; background: rgba(74, 222, 128, 0.14); } /* 23.3 危险红在深底上提亮:#b02a37 → #f87171,实测 6.4:1 */ html[data-theme="dark"] { --danger-color: #f87171; --success-color: #4ade80; } html[data-theme="dark"] .required-mark, html[data-theme="dark"] .btn-danger, html[data-theme="dark"] .btn-secondary.btn-small.danger, html[data-theme="dark"] .text-danger, html[data-theme="dark"] .is-danger { color: #f87171 !important; } /* 23.4 18.14 的硬编码深灰只对亮色成立,深色下交回 --text-muted(#9ca3af) */ html[data-theme="dark"] #chat-hitl-shortcut-text, html[data-theme="dark"] #page-hitl .hitl-reviewer-toggle-btn, html[data-theme="dark"] #page-hitl .hitl-reviewer-toggle-btn span { color: var(--text-muted) !important; } } /* ========================================================================== 24. 按钮 / 控件视觉体系统一 -------------------------------------------------------------------------- _mbtn.mjs 在 27 页上实测 645 个可点控件,改造前的客观问题: · style.css 里**没有 .btn 基类**(`^\s*\.btn\s*\{` 零命中),因此没有尺寸 基准:.btn-danger 是唯一 10px 20px / 15px 的"大一号"按钮,.btn-search 与 .pagination-btn 固定 height:32px,.btn-icon-pagination 只有 20x20, .settings-custom-select-trigger 同一页存在 32/38/42/48px 四种高度。 · 类语义被打破:.btn-secondary 一个类出现 **12 种签名**(205 个实例)—— 高度 44/52/71、圆角 6px/8px、字号 13px/14px、横内边距 12/14/16/18px; .btn-primary 6 种、.btn-small 4 种。 · 度量发散:圆角 11 种(0/4/5/6/7/8/10/12/14/999px/50%)、字号 9 种 (10.56~16px)、字重 7 档(400/500/520/560/600/650/700)。 · 同一行内尺寸互不相同(39 处容器):mcp-management 分页「上一页/下一页」 被 style.css:11867 的 `flex:1 + max-width:120px` 压到 53px 宽、中文换行 撑高成 53x71,而同行「首页/末页」是 44x52;资产库工具条三个按钮的横内边距 是 16/16/6;工作流工具条的圆角是 7px 与 0px。 · 手机主导航(底部标签栏)字号 10.56px、角标 9.6px,低于本层 18.2 自己 定的 12px 下限。 本节只做"收敛",不改任何布局结构与配色(配色仍由 18/23 节负责): 按钮高度 -> 44px(触控下限,也是唯一档) 圆角 -> 矩形控件 10px / 方形图标按钮 12px / 标签胶囊 999px 字号 -> 标准 14px、小号与胶囊 13px 字重 -> 常规 500、主要行动与选中态 600 横内边距 -> 标准 16px、小号 12px 分组间距 -> 8px 两条刻意不碰:`.btn-compact`(style.css:32833 显式 `min-height:0`,它是侧栏 分页的紧凑按钮,抬到 44px 会把侧栏撑破)与 `.chat-files-dropdown-item` (是左对齐的菜单行,不是按钮,单独定义)。 ========================================================================== */ @media (max-width: 767.98px) { /* 24.1 基底:所有"按钮化控件"共用同一套度量,取代原先散落在 20.1 / 20.2 / 20.4 / 20.6 / 20.7 / 20.8 的逐类补丁。 只设左右内边距,不动上下——保持 style.css 里各族的垂直视觉重心。 */ :is(.btn-primary, .btn-secondary, .btn-danger, .btn-ghost, .btn-small, .btn-sm, .btn-filter, .btn-search, .mcp-detail-btn, .btn-delete, .btn-loading, .page-header-actions > button, .page-header-actions > a.btn, .tools-actions button, .attack-chain-action-btn, .attack-chain-filters button, .robot-manager-toolbar button, .robot-command-entry button, .asset-batch-menu button, .workflow-toolbar-button, .workflow-save-button, .m-chat-bar-btn, .chat-session-shortcut, .role-selector-btn, .new-chat-btn, .ai-channel-save-btn, .dashboard-empty-action, .dashboard-severity-status-filter-btn, .empty-state button, .workflow-node-palette button, .c2-sessions-quick-links button, .syntax-guide-toggle) { min-height: var(--m-ctl-h) !important; padding-left: var(--m-ctl-pad-x) !important; padding-right: var(--m-ctl-pad-x) !important; font-size: var(--m-ctl-fs) !important; font-weight: var(--m-ctl-fw) !important; border-radius: var(--m-r-btn) !important; line-height: 1.25 !important; box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center; } /* 24.2 小号档:横内边距收到 12px,字号 13px(高度仍是 44px 触控下限) */ :is(.btn-small, .btn-sm, .btn-filter, .btn-search, .btn-icon-pagination, .mcp-detail-btn, .tools-actions button, .workflow-toolbar-button, .workflow-save-button, .m-chat-bar-btn, .chat-session-shortcut, .workflow-node-palette button, .c2-sessions-quick-links button, .syntax-guide-toggle, .robot-command-entry button) { padding-left: var(--m-ctl-pad-x-sm) !important; padding-right: var(--m-ctl-pad-x-sm) !important; font-size: var(--m-ctl-fs-sm) !important; } /* 24.3 图标档:纯图标按钮一律 44x44 正方形,圆角略大于文字按钮, 让"图标块"与"文字条"在一起时不显单薄。 (原来 .chat-upload-btn 36x36/圆角 10、.btn-icon-pagination 20x20、 顶部图标 38x38,三种口径并存) */ :is(.btn-icon, .btn-icon-pagination, .chat-upload-btn, .m-menu-btn, .m-more-btn, .m-close-btn, .projects-list-item-menu, #notification-bell-btn, #user-avatar-btn, .header-actions .btn-icon) { width: var(--m-ctl-h) !important; min-width: var(--m-ctl-h) !important; height: var(--m-ctl-h) !important; min-height: var(--m-ctl-h) !important; padding: 0 !important; border-radius: var(--m-r-icon) !important; font-size: var(--m-ctl-fs-sm) !important; font-weight: var(--m-ctl-fw) !important; } :is(.btn-icon, .chat-upload-btn, .m-menu-btn, .m-more-btn, .m-close-btn, #notification-bell-btn, #user-avatar-btn) svg { width: 20px; height: 20px; } /* 24.4 行内文字按钮(.btn-link):原来只有 32px 宽、圆角 0、横内边距 6px, 和旁边 6px 圆角按钮排在一行时基线都不齐。给它按钮的位置感,但保持无底无框。 */ .btn-link { min-width: var(--m-touch) !important; min-height: var(--m-ctl-h) !important; padding-left: var(--m-ctl-pad-x-sm) !important; padding-right: var(--m-ctl-pad-x-sm) !important; font-size: var(--m-ctl-fs-sm) !important; font-weight: var(--m-ctl-fw) !important; border-radius: var(--m-r-btn) !important; background: transparent !important; box-shadow: none !important; } /* 24.5 标签 / 胶囊档:凡是"一组里选一个"的控件统一胶囊形, 原来 .rbac-tab 是 0 圆角 + 1px 内边距、.projects-tab 是 8px、 .hitl-page-tab 是 0 圆角、.asset-period-switch 是 11px 字号、 .c2-tasks-time-preset-btn 是 12px 字号。 */ :is(.rbac-tab, .projects-tab, .dashboard-feed-tab, .hitl-page-tab, .rbac-workspace-tab, .settings-nav-item, .preset-chip, .asset-period-switch button, .c2-tasks-time-preset-btn, .hitl-reviewer-toggle-btn) { min-height: var(--m-ctl-h) !important; padding-left: 14px !important; padding-right: 14px !important; font-size: var(--m-ctl-fs-sm) !important; font-weight: var(--m-ctl-fw) !important; border-radius: var(--m-r-pill) !important; line-height: 1.25 !important; white-space: nowrap; box-sizing: border-box; } :is(.rbac-tab, .projects-tab, .dashboard-feed-tab, .hitl-page-tab, .rbac-workspace-tab, .settings-nav-item, .preset-chip, .asset-period-switch button, .c2-tasks-time-preset-btn, .hitl-reviewer-toggle-btn).is-active, :is(.rbac-tab, .projects-tab, .dashboard-feed-tab, .hitl-page-tab, .rbac-workspace-tab, .settings-nav-item, .preset-chip, .asset-period-switch button, .c2-tasks-time-preset-btn, .hitl-reviewer-toggle-btn).active { font-weight: var(--m-ctl-fw-strong) !important; } /* 分段控件(周期切换)容器跟着胶囊化,3px 内衬保留 */ .asset-period-switch { border-radius: var(--m-r-pill) !important; padding: 3px !important; gap: 2px !important; display: inline-flex !important; align-items: center; } /* 24.5b rbac-summary-count 是"查看明细 70"的**虚线文字按钮**,style.css 设计为 border-radius:0 + 虚线底边 + color:secondary。上面把它误纳入胶囊档,被强制 成全圆角、无背景的怪圈(实测 42×44、border-radius:999px)。排除它并还原 虚线文字按钮本义,仅给移动端 44px 命中高。 */ .rbac-summary-count--interactive { border-radius: 0 !important; background: none !important; min-height: var(--m-touch) !important; padding: 0 2px 1px !important; display: inline-flex !important; align-items: flex-end; } /* 24.6 下拉触发器:与按钮同高同圆角,但文字左对齐、右端留给箭头。 原来同一类在资产库有 32/38/42/48px 四种高度、在内距 9/11/12 之间跳动。 */ :is(.settings-custom-select-trigger, .monitor-filter-select-trigger, .projects-filter-select-trigger, .vuln-filter-select-trigger, .tasks-filter-select-trigger, .chat-files-filter-select-trigger, .c2-form-select-trigger, .asset-custom-select .settings-custom-select-trigger) { min-height: var(--m-ctl-h) !important; padding-left: var(--m-ctl-pad-x-sm) !important; padding-right: var(--m-ctl-pad-x-sm) !important; font-size: var(--m-ctl-fs) !important; font-weight: var(--m-ctl-fw) !important; border-radius: var(--m-r-ctl) !important; line-height: 1.25 !important; box-sizing: border-box; display: flex; align-items: center; justify-content: space-between; gap: 8px; } /* 24.7 分页结构修复 实测:mcp-management 分页里「上一页/下一页」53x71(中文被挤成两行), 同行「首页/末页」44x52 —— style.css:11867 在 ≤768px 给 `.pagination-fixed .pagination-controls .btn-secondary` 设了 `flex:1; min-width:60px; max-width:120px`,按钮被压窄后文字换行撑高。 这里取消伸缩、禁止换行、允许整行折行,四个按钮回到同一高度。 */ :is(.pagination-controls, .pagination-fixed .pagination-controls, .pagination) { display: flex !important; flex-wrap: wrap !important; align-items: center !important; gap: 6px !important; } :is(.pagination-controls, .pagination-fixed .pagination-controls) > :is(button, .btn-secondary, .btn-primary) { flex: 0 0 auto !important; max-width: none !important; min-width: var(--m-touch) !important; min-height: var(--m-ctl-h) !important; padding-left: var(--m-ctl-pad-x-sm) !important; padding-right: var(--m-ctl-pad-x-sm) !important; font-size: var(--m-ctl-fs-sm) !important; border-radius: var(--m-r-ctl) !important; white-space: nowrap !important; } /* 24.8 分组间距统一:控件成组出现时一律 8px,原来 6/8/10/12/16 混用 */ :is(.page-header-actions, .tools-actions, .rbac-tabs, .projects-tabs, .dashboard-feed-tabs, .attack-chain-filters, .asset-toolbar-main, .workflow-toolbar, .workflow-toolbar-group, .workflow-node-palette, .chat-composer-context, .chat-composer-footer-leading, .c2-tasks-time-presets, .c2-sessions-quick-links, .settings-nav, .asset-saved-views, .header-actions, .hitl-reviewer-toggle, .syntax-guide-actions, .rbac-summary-permissions, .empty-state) { gap: var(--m-ctl-gap) !important; } /* 24.9 可点卡片 / 行的圆角归一到卡片档(原为 0/4/8/10/12 五种混用) */ :is(.dashboard-resource-item, .dashboard-severity-legend-item, .dashboard-severity-status-cell, .dashboard-access-stat, .dashboard-recommend-item, .tool-guard-rule-summary, .rbac-user-row, .stat-card, .dashboard-kpi-card) { border-radius: var(--m-radius-card) !important; } /* 24.10 层级:主要行动与选中态用 600,其余 500。 原来同一个界面里并存 400/500/520/560/600/650 六档字重。 */ :is(.btn-primary, .new-chat-btn, .ai-channel-save-btn, .rbac-summary-count, .empty-state button, .role-selector-btn.m-primary) { font-weight: var(--m-ctl-fw-strong) !important; } .chat-input-container .send-btn { font-weight: var(--m-ctl-fw-strong) !important; } /* 24.11 表单勾选类控件:视觉 24px,命中面 44x44(由整行 label 提供) 实测结论(_mchk2.mjs,运行时注入对照实验):Chrome 对 input[type=checkbox]/radio 这类替换元素**不采纳作者的 padding**——连运行时 注入 `input[type=checkbox]{padding:10px !important}` 都不生效(computed padding 恒为 0、外框恒为 24x24)。按 CSS 层叠规范,!important 的优先级是 反的:UA 的 !important 高于作者的 !important,而 Blink 的 UA 样式表对勾选框 强制了内衬。所以"把本体撑到 44px"这条路在本平台上不成立(真撑大了视觉上也 是个巨大方框)。正确做法是:本体保持 24px 视觉,把 44px 命中面交给整行 label —— 实测该行 68x44,点行内任意位置都能切换。 */ input[type="checkbox"], input[type="radio"] { width: 30px !important; height: 30px !important; min-width: 30px !important; min-height: 30px !important; /* 仍需 !important:style.css 的 `.auto-refresh-toggle input[type=checkbox]` (18871)等后代选择器特异性更高,会把它按回 18px */ } label:has(> input[type="checkbox"]), label:has(> input[type="radio"]) { min-height: var(--m-touch); display: inline-flex; align-items: center; gap: 8px; } /* 24.12 胶囊标签条:横向滚动,不换行、不裁切 _mprobe.mjs 实测 `.hitl-page-tabs` 在 320px 视口 clientW=292 / scrollW=371, 且 overflow-x 是 visible —— 第 4 个页签右边缘 385 > 视口 320,被祖先 `.page-content{overflow-x:hidden}` 直接切掉,**手指也滑不出来**:一个点不到 的页签。390px 下也已溢出 9px。这里沿用文件里既有的"横向滚动 + nowrap"惯例 (见 6.x 终端工具条、19.3 宽表),把溢出交还给标签条自己滚动。 只列真正单行排布的胶囊条,工作流工具条这类靠换行展示全部的容器不动。 */ :is(.hitl-page-tabs, .projects-tabs, .dashboard-feed-tabs, .rbac-tabs, .rbac-workspace-tabs, .c2-tasks-time-presets) { flex-wrap: nowrap !important; overflow-x: auto !important; overflow-y: hidden !important; scrollbar-width: none; -webkit-overflow-scrolling: touch; overscroll-behavior-x: contain; scroll-snap-type: x proximity; } :is(.hitl-page-tabs, .projects-tabs, .dashboard-feed-tabs, .rbac-tabs, .rbac-workspace-tabs, .c2-tasks-time-presets)::-webkit-scrollbar { display: none; } /* 条目不许被压扁(否则"横向滚动"会退化成"每个页签都被挤成省略号") */ :is(.hitl-page-tabs, .projects-tabs, .dashboard-feed-tabs, .rbac-tabs, .rbac-workspace-tabs, .c2-tasks-time-presets) > * { flex: 0 0 auto !important; scroll-snap-align: start; } /* 24.13 projects 页残留小字号已并入 18.2 的下限清单(保持单一出处) */ /* 24.14 对话页「人工审批:关闭」按钮:文字不再被截断 实测根因(_mprobe.mjs):按钮被 style.css 的 max-width:142px 钉死,而内衬 16px 在图标+间距之外只剩 69px 给标签,标签实际需要 84px → 恒显省略号 (iPhone SE 上只剩 49px)。原 18.7 用"字号缩 1px"绕,属于治标。 底部这一行总宽 368px,上传键 44 + 发送键 44 之后仍有富余,放宽即可完整显示。 */ #chat-hitl-shortcut { max-width: none !important; padding-left: var(--m-ctl-pad-x-sm) !important; padding-right: var(--m-ctl-pad-x-sm) !important; font-size: var(--m-ctl-fs-sm) !important; overflow: hidden !important; } /* 24.9 会话快捷按钮的文本由 JS 动态写入(如「Agent 审查:关闭 · OpenAI 协议模型 · qwen3-max」), 实测比按钮宽 30px+。原来强制 nowrap + visible 会被按钮容器裁掉尾部; 改为在按钮内收缩,超出显示省略号(回到 style.css:44875 的初衷)。 */ #chat-hitl-shortcut-text { flex: 1 1 auto !important; min-width: 0 !important; font-size: var(--m-ctl-fs-sm) !important; overflow: hidden !important; text-overflow: ellipsis !important; white-space: nowrap !important; text-align: left; } /* 24.15 自制开关:**不得**套用 24.11 的原生勾选框尺寸 (必须排在 24.11 之后,靠层叠接管) tool-guard 页「调用拦截」把复选框做成了开关:tool-guard.css:68 定义 30×18 胶囊轨道 + 12px 圆形滑块(::after,left:2,选中 translateX(12)); tool-guard.js:205-218 同时还给这个 input 挂了 `.theme-checkbox` 类。 24.11 是 `input[type=checkbox]{width/height:24px !important}`——特异性 (0,1,1) 低于 `.tool-guard-page .tool-guard-switch input[type=checkbox]` (0,2,1),但一加 !important 就反超,于是: · 轨道被压成 24×24 方块(本该是 30×18 的胶囊) · 滑块仍是 left2 + 12 + 位移12 = 右缘 26,**越出轨道** · 未选中态:24 宽轨道里左侧一个 12px 圆点、右侧空 10px → 坏掉的控件 这里按原设计还原轨道与滑块,并用更高特异性接管两态位移。 */ .tool-guard-page .tool-guard-switch input[type="checkbox"] { width: 30px !important; height: 18px !important; min-width: 30px !important; min-height: 18px !important; border-radius: 999px !important; padding: 0 !important; position: relative !important; } /* 未选中态:滑块归位。transform 必须显式声明,否则会从 24.16 的命中区 规则里继承 `translate(-50%,-50%)`,把滑块挪到轨道外 */ .tool-guard-page .tool-guard-switch input[type="checkbox"]::after { content: '' !important; position: absolute !important; top: 2px !important; left: 2px !important; width: 12px !important; height: 12px !important; border-radius: 50% !important; background: #fff !important; transform: none !important; } /* 选中态:left2 + 滑块12 + 位移12 = 右缘 26,轨道内宽 28(30 减 1px 边框), 右侧留 2px —— 与未选中态左侧留 2px 对称 */ .tool-guard-page .tool-guard-switch input[type="checkbox"]:checked::after { transform: translateX(12px) !important; } /* 裸开关:label 内只有 input、没有文字时宽度只有 24px,给它 44 命中面。 `justify-content` 在 auto 宽度下无副作用(内容即容器),所以对带文字的 「启用调用拦截」那行(104×44)不产生影响。 */ .tool-guard-page .tool-guard-switch { min-height: var(--m-touch); min-width: var(--m-touch); justify-content: center; } /* 24.16 勾选框(.theme-checkbox):等比圆角 + 44px 命中面 (a) 等比圆角:24.11 把本体放大到 24px,但 `.theme-checkbox` 自己的 border-radius 是 4px(为默认 16px 设计),24px 下显得方。按钮体系是 10px/44px ≈ 23%,这里取 6px/24px = 25%——**按尺寸等比**才叫协调。 (b) 44px 命中面:24.11 的注释写着"命中面交给整行 label",但全站有 22 个 勾选框并没有可点的 label(mcp-management 工具列表 20 个、 asset-library 表头全选、info-collect 全选),本体 24×24 在手机上 很难点中。`.theme-checkbox` 是 appearance:none 的,此时 input 会渲染 伪元素、**且伪元素参与命中测试**;原生外观下则被 UA 样式表锁死—— 这正是 24.11 里"padding 撑不开"的原因,两者是同一个机制的两面。 所以用一个透明 44×44 ::after 撑开命中面:不占布局、不改视觉。 */ .theme-checkbox { position: relative !important; border-radius: 6px !important; } .theme-checkbox::after { content: '' !important; position: absolute !important; top: 50% !important; left: 50% !important; width: 44px !important; height: 44px !important; transform: translate(-50%, -50%) !important; background: transparent !important; } /* 24.17 开关型隐形 input 必须排除在 24.11 之外 项目里其实有**三套**勾选/开关机制,24.11 只能套第一套: 1. `.theme-checkbox`(style.css:43032)——真·勾选框,appearance:none、 可见可点,24.16 已给它等比圆角与 44px 命中面; 2. `.modern-checkbox` + `.checkbox-custom`(style.css:11011)——开关, input 本体是 `position:absolute; opacity:0; width:0; height:0; pointer-events:none`,**根本不接收点击**,点击由外层 `.checkbox-label` 代收,视觉交给兄弟元素 `.checkbox-custom`(44×24 胶囊 + 16px 滑块, 选中 translateX(20):2+16+20 = 38 ≤ 40,左右各留 2px,设计本身正确); 3. `.tool-guard-page .tool-guard-switch input`——30×18 开关,见 24.15。 24.11 给第 2 套的 input 强推 24×24,等于在页面上多出一层 24×24 的隐形盒子 (因为 pointer-events:none 所以不拦点击,但没有任何存在理由,也会让 审计脚本把它误判成"点不中的勾选框")。这里恢复它的 0×0。 */ input.modern-checkbox { width: 0 !important; height: 0 !important; min-width: 0 !important; min-height: 0 !important; } /* 第 2 套开关的真实点击接收者是 `.checkbox-label`(实测 150×40,差 4px)。 这才是"点不中"的正确修复点——修 label,而不是修那个不可见的 input。 必须 !important:info-collect 有一条 `#info-collect-full-option .checkbox-label{min-height:40px}`(特异性 1,2,0) 压着这里的 (0,1,0);基础规则那边的 40px 则是 `padding:8px 0` + 24px 内容得来的。 */ .checkbox-label { min-height: var(--m-touch) !important; } } /* ========================================================================== 区块 25:审计覆盖被修正后(假 14 页 → 真实 29 页)新暴露的问题收口 -------------------------------------------------------------------------- 起因:`_mtouch.mjs` / `_mdark.mjs` 的页表里有 6 个并不存在的页面 id,而 `switchPage()` 对无效 id 是静默 no-op —— 那 6 页一直在重复测上一页, 于是「14 页全 0」是假覆盖。修好页表(共用 `_pages.mjs` 的 29 个真实 id, 切换后回读 `.page.active` 断言)再测,下面这些问题才第一次被看见。 ========================================================================== */ @media (max-width: 767.98px) { /* 25.1 info-collect 表单在手机上仍是 4 列栅格。 基础规则(style.css:30644)的 grid-template-columns: minmax(160px,.7fr) 同 ×2 + minmax(150px,.45fr) + minmax(280px,1fr) 最小合计 750px,390px 屏上第 3/4 列被直接推出视口;而 `@media (max-width:980px)` 里改为单列的那条(style.css:30627)被写在 **它之后**且特异性同为 (0,1,0) —— 顺序错误让这个覆盖永久失效。 实测「完整模式」选项 left=390 right=540(vw=390),整块不在屏内。 */ .info-collect-form-row { grid-template-columns: 1fr !important; } /* 25.2 mcp-management 统计条用 flex-wrap:nowrap, 「📌 常驻: 49」right=409、「🔗 已连接: 0」right=468,都越过屏幕右缘。 */ #tools-stats, .external-mcp-stats { flex-wrap: wrap !important; } /* 25.3 语义色文字:沿用 18.10b 的同一套体系(一个语义一个色,只压亮度、 冻结色相与饱和度,目标 ≥4.5:1),限定亮色主题。注释里是修复前的实测值。 */ /* 中性灰 —— 与 .projects-fact-filter-label 取同值,全站灰标签一色到底 */ .c2-tasks-stat-label, /* 2.56 on #fff */ .c2-events-stat-label, .c2-tasks-time-preset-btn, /* 2.45 on #f8fafc */ .c2-events-time-preset-btn { color: #5d718c !important; } /* 信息蓝 */ #c2-events-stat-info, /* 3.68 */ .c2-tasks-time-preset-btn.is-active, /* 3.36 on #eff5fe */ .c2-events-time-preset-btn.is-active, .agent-role-badge--orchestrator, /* 4.36 on #e0edff */ #rbac-metric-users, /* 4.34 on #e5ecf9,strong 是本体的 id */ .rbac-metric-users, .rbac-metric-users span { color: #1557e7 !important; } /* 待处理琥珀 */ #c2-tasks-stat-pending, /* 2.15 */ #c2-events-stat-warn { color: #986206 !important; } /* 失败 / 严重 红 */ #c2-tasks-stat-failed, /* 3.76 */ #c2-events-stat-critical { color: #d81212 !important; } /* 成功绿(.monitor-stat-value 是 28.8px 大字号,标准线是 3.0,实测 2.97 差 0.03) */ #c2-tasks-stat-success, /* 2.54 */ .monitor-stat-value.is-success { color: #167e3c !important; } /* 授权橙 */ #rbac-metric-assignments, /* 3.00 on #f7e9e2,strong 是本体的 id */ .rbac-metric-assignments, .rbac-metric-assignments span { color: #ad4109 !important; } /* 浅粉底上的危险按钮文字:同属危险语义,但底色 #f6e9ec 比 dashboard 那处的 #fef8f8 更深,沿用同一个 #d81212 只有 4.42:1,所以按底色单独压暗。 */ #c2-tasks-batch-delete span, /* 3.84 */ #c2-events-batch-delete span { color: #c22231 !important; } /* 25.4 c2 的主行动按钮:c2.css 拿 --c2-accent(#3b82f6)(c2.css:8)当按钮底色, 白字只有 3.68:1。同一个 #3b82f6 作正文色时已在 18.x 处理过,作按钮底色这条漏了。 压深到 #2563eb → 5.17:1。 **必须限定亮色主题**:深色主题下 `--c2-accent` 会变成浅蓝,按钮是"浅底+深字", 在深色里改背景会直接把文字对比度打下来(实测 3.64:1)。 */ html:not([data-theme="dark"]) .c2-payload-card .btn-primary, html:not([data-theme="dark"]) #c2-build-btn { background: #2563eb !important; border-color: #2563eb !important; } /* 25.5 第九轮第二遍:新暴露页面里剩下的文本类小字也纳入 12px 下限。 `.tool-expand-icon` 的 ▶(10px)是图标字符、`svg text`(9px)是图表内标注, 两者属图形元素,不受文本字号下限约束,保持原尺寸。 */ .skill-card-tags span, /* 11.52px 技能标签 */ .c2-tasks-field span, /* 11px 筛选字段标题 */ .c2-events-field span, #tools-stats strong, /* 11.2px 工具统计数字 */ .asset-protocol-empty p, /* 11px「暂无数据」 */ .asset-recent-badge, /* 10px「7 天」 */ .asset-recent-caption { /* 11px「占当前资产总量的 0%」 */ font-size: 12px !important; } /* 25.6 iPhone SE(320px 视口)下 info-collect 的结果工具栏被推出屏幕: 「列 / 导出 CSV / 导出 JSON」right=386 > 320。让它换行而不是溢出。 (同屏 `span.rbac-context-stat` 的 right=326 也越界 6px,但它的父容器 尚未定位,属边缘情况,先记录不动。) */ .info-collect-results-toolbar, #fofa-selected-meta { flex-wrap: wrap !important; } /* 25.6b header 是单行 space-between,左栏 min-width:240px 仍会把工具栏挤出; 改为上下两行:左栏占满一行,工具栏换到下一行右对齐。 */ .info-collect-results-header { flex-wrap: wrap !important; } .info-collect-results-header-left { min-width: 0 !important; flex: 1 1 100% !important; } /* 25.6c rbac context 胶囊:max-width:min(25%,360px) 在 320px 下只有 80px, 两个统计("1个角色·3项授权")放不下,第二个 right=326 越界 6px。 窄屏放行换行且取消 25% 上限。 */ .rbac-metric-context { max-width: none !important; flex-wrap: wrap !important; } /* 25.7 输入控件高度兜底:区块 7 的 input/select{min-height:var(--m-touch)} 特异性 (0,1,1) 不够,被专项规则覆盖成 36/38/40/42px。 - c2.css `#page-c2-events .c2-events-field .form-control`(1,2,1) 锁 36px - knowledge 的 `.knowledge-filters .search-box`(0,2,0) -> 38px - rbac 的 `.rbac-search .form-input` -> 40px - settings 的 `.form-group input`(settings 页) -> 42px 这里用 `#page-*` 前缀 + !important 压过,同时保留原 box-sizing/padding。 (form-control 设 height+min-height 都要改;.search-box input 与裸 input 走 min-height 即可。) */ #page-c2-tasks .c2-tasks-field .form-control, #page-c2-events .c2-events-field .form-control, #page-knowledge-management .knowledge-search-field input, #page-platform-rbac .rbac-search .form-input, #page-settings .form-group input, #page-settings input[type="text"], #page-settings input[type="password"], #page-settings input[type="url"] { height: var(--m-ctl-h) !important; min-height: var(--m-ctl-h) !important; } } /* ========================================================================== 区块 26:深色主题(html[data-theme="dark"]) -------------------------------------------------------------------------- 这批违规同样是"审计覆盖修正后才被看见"的:`_mdark.mjs` 的页表此前含 6 个 不存在的页面 id,14 页里那 6 页一直在重复测上一页,所以"深色 0 违规"是假覆盖。 39 处的推荐色用 `_mcontrast.mjs` 的新 OFFLINE 模式离线求出 —— 它刻意复用与 在页面里跑时**同一份 solveFor()**,避免"离线一套算法、在线一套算法"分叉。 方向与亮色相反:深色下要**提亮**前景(搜索方向由前景/背景的明暗关系自动决定, 无须人工指定)。取值同样遵循"一个语义一个色",括号内是修复前的实测比值。 两处结论经**像素裁决**(`_mpixel.mjs` + THEME=dark)确认可信:CSS 计算的 `.mcp-stats-kpi__chip.is-ok` = #166534 与像素真值完全吻合(2.04:1), `.role-card-badge.enabled` = #157347 同样吻合(2.43:1)——不是建模误差。 ========================================================================== */ html[data-theme="dark"] { /* 26.1 【已回滚的误判,留作记录】深色主题下 `.btn-primary` 的常规形态是 "浅蓝底 #60a5fa + 深墨蓝文字",实测 9.4:1 —— 与亮色主题的"深蓝底 + 白字" 互为反色,这是**有意设计**(正是 23.1 区块处理过的那套写法)。 我最初把它误判成"深字压蓝底",统一改成浅色文字,结果把 18 个页面里 可见的主按钮从 9.4:1 一次性压到 2.21:1 —— `_mdark.mjs` 重测后立刻暴露。 结论:**不要动 `.btn-primary` 的文字色**。 真正有问题的是登录按钮:`#login-overlay .btn-primary.login-submit` 上叠了 background-image: linear-gradient(135deg,#2563eb,#1d4ed8) 的深蓝渐变, 把浅蓝底盖掉了,文字仍是深墨蓝 → 实测 1:1 ~ 3.64:1。只修这一处。 */ #login-overlay .btn-primary, #login-overlay .login-submit { color: #eceff8 !important; } /* 26.2 中性灰 —— 深色下一律提亮到同一色:取各元素解里最亮的一档 (#848fa0 是"筛选按钮 / #1f2937 底"的解,对更暗的底只会比值更高)。 `.btn-secondary` 的分页按钮这里必须带上 `*`:按钮自身的文字其实是 子元素承载的,只改按钮本体会看不到任何变化(实测过)。 */ .c2-tasks-stat-label, /* 3.49 on #131a27 */ .c2-events-stat-label, .c2-tasks-time-preset-btn, /* 3.75 on #0c1220 */ .c2-events-time-preset-btn, #projects-detail-meta-time, /* 3.72 */ button.btn-secondary, /* 2.48 */ button.btn-secondary * { color: #8c97a8 !important; } /* 26.3 信息蓝 */ #c2-events-stat-info, /* 2.95 */ .c2-tasks-time-preset-btn.is-active, /* 2.33 on #1e2d45 */ .c2-events-time-preset-btn.is-active, .agent-role-badge--orchestrator, /* 2.69 */ .rbac-metric-users, .rbac-metric-users span { /* 2.77 */ color: #6591f1 !important; } /* 26.4 紫 */ .rbac-metric-roles, .rbac-metric-roles span { /* 2.90 */ color: #9c6af1 !important; } /* 26.5 绿 —— #24a655 是"启用徽章 / #142f2c 底"的解(对更暗的 chip 底只会更高) */ #c2-tasks-stat-success, /* 3.39 */ .mcp-stats-kpi__chip.is-ok, /* 2.04 ← 像素裁决过 */ .rbac-pill.is-ok, /* 2.27 */ .role-card-badge.enabled { /* 2.42 ← 像素裁决过 */ color: #24a655 !important; } /* dashboard 的「安全」徽章底色更亮(#163a36),需要更亮的绿 */ #dashboard-severity-risk-level { color: #18b351 !important; /* 3.77 */ } /* 26.6 红 —— #f04a4a 是"失败 chip / #271c2a 底"的解(#ef3e3e 对 #211a29 只有 4.35:1) */ #c2-tasks-stat-failed, /* 3.33 */ #c2-events-stat-critical, #c2-tasks-batch-delete span, /* 2.86 */ #c2-events-batch-delete span, .btn-secondary.btn-delete, /* 2.73 */ .mcp-stats-kpi__chip.is-fail { /* 1.97 */ color: #f25858 !important; } #dashboard-status-open { color: #ef4646 !important; /* 4.45,差 0.05,单独按解取值 */ } /* 26.7 琥珀 */ #c2-tasks-stat-pending, /* 3.39 */ #c2-events-stat-warn { color: #b47407 !important; } /* 26.8 橙 */ .rbac-metric-assignments, .rbac-metric-assignments span { /* 2.72 */ color: #e9580c !important; } /* 26.9 知识库「未启用」提示(rgb(102,102,102) 在深底上只有 3.28:1) */ #page-knowledge-management h3 { color: #7c7c7c !important; } /* 26.10 mcp-management 的「全部」筛选按钮是白字压在浅蓝 #60a5fa 上: 冻结色相与饱和度后**无解**(白色没有可提亮的余量,求解器原样返回 #ffffff), 所以只能压深底色。 */ .btn-filter.active { background: #2563eb !important; border-color: #2563eb !important; } /* 26.11 c2 会话工具栏的文字型按钮:18.12 给的 #1d4ed8 在深底上只有 2.84:1。 那条规则的特异性是 (1,4,1)——四个 `:not()` 里的类与属性**都要计入**特异性, 所以这里必须原样带上同一个选择器才压得住(4 个 `:not()` 里的类与属性都要计入特异性)。 **踩过的坑**:这条规则原先我写成了 `html[data-theme="dark"][data-theme="dark"] #page-...`, 而它所在的位置是本文件末尾 `html[data-theme="dark"] { ... }` 块的**内部** —— CSS 嵌套会把它解释成 `html[data-theme="dark"] html[data-theme="dark"][data-theme="dark"] #page-...`, 也就是"`html` 是 `html` 的后代",**结构性永不匹配**。表现为"改了毫无反应", 而且**连把特异性翻倍都毫无反应**——因为根本没有参与匹配。 块内规则已经自带 `html[data-theme="dark"]` 前缀,这里反而不能再写。 */ #page-c2-sessions button:not(.btn-primary):not(.btn-secondary):not(.btn-danger):not([style*="background"]) { color: #6591f1 !important; } /* 26.12 空状态行动按钮:18.13 给它们铺了实心 #2563eb 底, 23.1 又按"浅蓝底(#60a5fa)"的假设把文字改成深墨蓝 —— 两轮修复前提不一致, 结果成了"深蓝底上的深蓝字"(实测 3.64:1)。底色既然固定是 #2563eb, 深色下文字就必须回转浅色(5.17:1)。前缀由所属的 `html[data-theme="dark"]` 块提供, 这里**不能**再手写一遍(原因见 26.11 的注释)。 */ .dashboard-empty-action, #page-knowledge-management .empty-state button, #page-knowledge-management .empty-state .btn, #hitl-pending-count { color: #eceff8 !important; } /* 26.13 未修项:asset-library 分页的「首页/上一页/下一页/尾页」在首页时是 `:disabled` 态,取 #475569 浅灰属有意设计 —— WCAG 1.4.3 明确豁免禁用控件, 不计入对比度要求。`_mdark.mjs` 会照报,属工具侧尚未过滤 `:disabled`。 */ } /* -------------------------------------------------------------------------- 27. C2 会话管理页移动端布局修正(第十轮) 实测(390x664 视口,登录 mtester): - c2.css:4371 的 @media (max-width:768px) 给 .c2-session-layout 设 min-height:calc(100vh - 140px) —— 移动端该页可用内容区实测只有 445px 高, 而 100vh-140px = 524px,硬撑出 81px 垂直溢出 → .page-content 出现滚动条, 布局底部 B676 伸到 #m-tabbar(顶 T606) 之下 70px,内容被底部标签栏盖住; - c2.css:4473 的 @media (max-width:1100px) 在样式表里位于 768px 断点**之后**, 同特异性下后声明胜 → .c2-session-sidebar-wrap 被卡回 width:280px, 390px 视口下会话列表只占 72% 宽,右侧直接露出深色背景; - 会话主区 .c2-session-main 被 layout 的 auto 高度压到 266px,空态卡片 底部越界,下端内容被切。 修复方向:高度交给内容区实际算(flex:1 + min-height:0),侧栏手机占满全宽、 最高 260px 内部滚动,主区接管剩余空间并内部滚动。只作用于 ≤767.98px。 -------------------------------------------------------------------------- */ @media (max-width: 767.98px) { #page-c2-sessions .c2-session-layout { min-height: 0 !important; height: 100% !important; max-height: none !important; overflow: hidden !important; } #page-c2-sessions .c2-session-sidebar-wrap { width: 100% !important; min-width: 0 !important; max-width: none !important; max-height: 260px !important; flex-shrink: 0 !important; border-right: none !important; } #page-c2-sessions .c2-session-sidebar, #page-c2-sessions #c2-session-list { overflow-y: auto !important; min-height: 0 !important; -webkit-overflow-scrolling: touch; overscroll-behavior: contain; } #page-c2-sessions .c2-session-main { flex: 1 1 auto !important; min-height: 0 !important; overflow-y: auto !important; -webkit-overflow-scrolling: touch; overscroll-behavior: contain; padding: 12px 14px 18px !important; } } /* -------------------------------------------------------------------------- 25. iOS 观感修订:材质 / 圆角 / 按压反馈 -------------------------------------------------------------------------- 口径: - 材质一律用 color-mix 从主题自己的 --bg-primary / --border-color 派生, 浅色与暗色自动跟随;写死 rgba 会在暗色下糊成一块白雾。 - backdrop-filter 只在浏览器支持时启用,不支持就退回实心背景(观感一致, 只是没有透出)。固定栏的模糊是软件渲染掉帧主因(见 5 节),所以全站只给 顶栏与底栏各一层 blur(20px),抽屉/面板用不透明材质不再加模糊。 - 圆角收成一个阶梯:控件胶囊、图标按钮正圆、卡片 18、面板顶角 22。 本层原有的 10/12/14/18 四种混用在手机上读起来是"没对齐",不是"设计过"。 - 按压反馈用 scale,且在 prefers-reduced-motion 下整体关掉。 - 所有改动只碰观感:44px 触控下限、层级、滚动容器一概不动。 -------------------------------------------------------------------------- */ :root { --m-r-card: 18px; --m-r-panel: 22px; --m-material: color-mix(in srgb, var(--bg-primary) 82%, transparent); --m-material-solid: color-mix(in srgb, var(--bg-primary) 96%, transparent); --m-hairline: color-mix(in srgb, var(--border-color) 55%, transparent); --m-fill-quaternary: color-mix(in srgb, var(--text-primary) 6%, transparent); --m-fill-press: color-mix(in srgb, var(--text-primary) 10%, transparent); --m-ring: color-mix(in srgb, var(--accent-color) 30%, transparent); --m-shadow-panel: 0 -12px 40px rgba(0, 0, 0, 0.22); --m-shadow-card: 0 1px 2px rgba(0, 0, 0, 0.04); } @media (max-width: 767.98px) { /* 25.1 顶栏 / 底栏:透出材质 + 发丝分隔线。 顶栏必须带 html[data-theme]:style.css 用主题选择器给暗色塞了 不透明渐变,只写 header 会在暗色下丢掉材质(实测 background 被压成渐变)。 */ .container > header, html[data-theme] .container > header { background: var(--m-material-solid); border-bottom: 1px solid var(--m-hairline); box-shadow: none; } #m-tabbar { background: var(--m-material-solid); border-top: 1px solid var(--m-hairline); box-shadow: none; } @supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) { .container > header, html[data-theme] .container > header { background: var(--m-material); -webkit-backdrop-filter: saturate(180%) blur(20px); backdrop-filter: saturate(180%) blur(20px); } #m-tabbar { background: var(--m-material); -webkit-backdrop-filter: saturate(180%) blur(20px); backdrop-filter: saturate(180%) blur(20px); } } /* 25.2 圆角阶梯:卡片、面板、控件、图标按钮 */ .card, .stat-card, .metric-card, .dashboard-card, .panel, .settings-section, .info-card { border-radius: var(--m-r-card); box-shadow: var(--m-shadow-card); } #m-sheet { border-radius: var(--m-r-panel) var(--m-r-panel) 0 0; box-shadow: var(--m-shadow-panel); } .main-sidebar { border-radius: 0 var(--m-r-panel) var(--m-r-panel) 0; border-right: none; } input:not([type=checkbox]):not([type=radio]), select, textarea { border-radius: 12px; } button, .btn-primary, .btn-secondary, .btn-danger, .btn-small, .btn-sm, .m-chat-bar-btn { border-radius: var(--m-r-pill); } /* 图标按钮去底去框:桌面用的是 .header-actions button(类+元素)两条特异性, 本层只写类名会被压住(实测 border 仍是 1px),所以这里必须同形写 */ .header-actions :is(.m-more-btn, .m-close-btn, .notification-btn, .user-avatar-btn, .btn-icon), .m-menu-btn { border: none; background: transparent; } /* 次要按钮:iOS 的"填充"样式,去掉描边不再和主按钮抢层次 */ .btn-secondary, .btn-small, .btn-sm { background: var(--m-fill-quaternary); border-color: transparent; } /* 25.3 发丝线:抽屉 / 面板 / 会话条的内部分隔 */ .m-drawer-head, .m-drawer-search, .m-sheet-head, .m-chat-bar { border-bottom-color: var(--m-hairline); } .m-sheet-sep { background: var(--m-hairline); } .m-sheet-grip { width: 36px; height: 5px; border-radius: 999px; background: color-mix(in srgb, var(--text-muted) 45%, transparent); } /* 25.4 按压反馈:可见的"点到了",不改变布局尺寸 */ .m-tab, .m-sheet-item, .m-chat-bar-btn, .m-menu-btn, .m-more-btn, .m-close-btn, .notification-btn, .user-avatar-btn, .btn-primary, .btn-secondary, .btn-danger, .btn-small, .nav-item-content, .nav-submenu-item { transition: transform 0.12s ease, background-color 0.12s ease, color 0.16s ease, opacity 0.12s ease; } .m-tab:active, .m-sheet-item:active, .m-chat-bar-btn:active, .btn-primary:active, .btn-secondary:active, .btn-danger:active, .btn-small:active { transform: scale(0.96); background-color: var(--m-fill-press); } .btn-primary:active { /* 主按钮按压时保持强调色,只压暗,不变成灰底 */ background-color: color-mix(in srgb, var(--accent-color) 88%, #000); } /* 当前标签:图标后面一枚圆形底色,比整格变色更接近系统底栏 */ .m-tab.m-active { color: var(--accent-color); background: transparent; } .m-tab.m-active svg { background: color-mix(in srgb, var(--accent-color) 12%, transparent); border-radius: 50%; padding: 2px; box-sizing: content-box; } /* 25.5 焦点环:从 2px 硬描边换成 3px 柔光环,键盘可达性不降级 */ a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible, [tabindex]:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--m-ring); } .m-drawer-search input:focus { border-color: color-mix(in srgb, var(--accent-color) 55%, transparent); } /* 强制高对比下柔光环会被抹掉,这里把硬描边还给它 */ @media (forced-colors: active) { a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible, [tabindex]:focus-visible { outline: 2px solid Highlight; outline-offset: 2px; } } /* 工具条里的 按钮(查看全部 / 拉取模型列表 / 视觉测试):它们是操作控件而不是 正文链接,桌面写成 21-29px 高的行内块,手指点不中。给到本层的触控下限, 字号与颜色不动。正文里的链接(.markdown-body 内)不在这些容器里,不受影响。 */ :is(.page-header-actions, .dashboard-section-actions, .dashboard-section-header, .model-pick-row, .asset-toolbar, .settings-toolbar, .settings-form, .form-group, .info-collect-toolbar, .settings-advanced-block > div) a:not(.btn-primary):not(.btn-secondary):not(.btn-danger) { display: inline-flex; align-items: center; min-height: var(--m-touch); padding: 0 8px; box-sizing: border-box; } /* 会话抽屉:本层把它做成全屏抽屉,里面的行就必须按 44 触控口径来 (实测搜索框 42、分组按钮 24 宽、项目行 38、"最近对话"折叠头 30) */ :is(#conversation-sidebar .conversation-search-box input, #conversation-sidebar .add-group-btn, #conversation-sidebar .project-folder-item, #conversation-sidebar .recent-conversations-toggle, #conversation-sidebar .project-folder-header) { min-height: var(--m-touch); } #conversation-sidebar .add-group-btn { min-width: var(--m-touch); } /* 调用拦截的开关:input 是 30x18 的滑块,命中区其实是包着它的 label。 把 label 撑到触控下限,手指点的就是整块开关而不是那条 18px 的芯。 */ .tool-guard-toggle { display: inline-flex; align-items: center; justify-content: center; min-width: var(--m-touch); min-height: var(--m-touch); } /* RBAC 概览里的计数胶囊:20px 宽点不中,补足到下限(文字居中,不改视觉密度) */ .rbac-summary-count { min-width: var(--m-touch); } /* 桌面把 .ai-channel-manager 设成 overflow:hidden(style.css:4796,靠固定高度自截), 手机上它的内容比盒子高,超出的一截被裁掉——实测 4 个通道选择按钮 scrollIntoView 也拉不进视口,属于「看得见点不到」。这里交还给页面滚动容器。 */ .ai-channel-manager { overflow: visible; } /* 25.6 遮罩与轻提示:遮罩不加模糊(全屏模糊是掉帧主因),提示条用材质 */ .m-scrim { background: color-mix(in srgb, var(--m-scrim) 88%, transparent); } /* 提示条保持基底的深色 HUD 胶囊(1023 行):这里曾把它改成浅色材质, 但字色仍是 #fff,结果白底白字——用户看到的就是"屏幕中间一块白的"。 只补一层投影,底色与字色不动。 */ #m-toast { box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18); } } @media (max-width: 767.98px) and (prefers-reduced-motion: reduce) { /* 关掉按压缩放,只保留颜色反馈 */ .m-tab, .m-sheet-item, .m-chat-bar-btn, .btn-primary, .btn-secondary, .btn-danger, .btn-small { transition: background-color 0.12s ease, color 0.16s ease; } .m-tab:active, .m-sheet-item:active, .m-chat-bar-btn:active, .btn-primary:active, .btn-secondary:active, .btn-danger:active, .btn-small:active { transform: none; } } @supports (height: 100dvh) { :root { --m-bar-bottom: max(0px, calc(100dvh - var(--m-app-height, 100dvh))); } } /* -------------------------------------------------------------------------- 26. 密度与表面:对齐 new-api 的卡片体系 + iOS 的分组列表与填充式表单 -------------------------------------------------------------------------- 这一节只管"看起来挤不挤、齐不齐",不改配色语言:KPI 卡那套按 nth-child 分的 淡彩渐变是产品自己的身份(style.css:23789-23793),不跟它抢。 -------------------------------------------------------------------------- */ @media (max-width: 767.98px) { /* 26.1 KPI:桌面在 ≤480px 把 5 张卡拍成一列 116px 高的长条,手机上要滚 两屏才看完四个数字。改成两列紧凑网格,数字才是主角。 */ .dashboard-kpi-row { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; gap: 10px; margin-bottom: 16px; } /* 奇数张时末卡通栏,右下角不留空洞 */ .dashboard-kpi-row > .dashboard-kpi-card:last-child:nth-child(odd) { grid-column: 1 / -1; } .dashboard-kpi-card { padding: 12px 13px !important; min-height: 0 !important; gap: 0 !important; } .dashboard-kpi-label { font-size: 12px; line-height: 1.35; font-weight: 500; color: var(--text-muted); } .dashboard-kpi-value { font-size: 26px; line-height: 1.15; font-weight: 600; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; margin-top: 6px; } .dashboard-kpi-sub, .dashboard-kpi-sub-text { font-size: 12px; line-height: 1.4; } .dashboard-kpi-icon { width: 28px; height: 28px; border-radius: 9px; flex-shrink: 0; } .dashboard-kpi-icon svg { width: 15px; height: 15px; } .dashboard-access-strip { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; gap: 10px; } /* 26.2 卡片表面统一:发丝边 + 16 圆角 + 几乎看不见的投影。 桌面的 0 2px 8px 双层阴影在小屏上会把每张卡都"抬起来",一屏四张就散了。 */ :is(.card, .stat-card, .metric-card, .dashboard-card, .panel, .settings-section, .info-card) { border: 1px solid var(--m-hairline); border-radius: var(--m-r-card); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03); } /* 26.3 页面头:标题与操作行收成一组,不再各占一段空白 */ .page-header h2, .page-header h1 { font-size: 20px; font-weight: 600; letter-spacing: -0.01em; } .page-header { gap: 8px; margin-bottom: 12px; } /* 26.4 填充式表单(iOS 的写法:字段靠底色区分,不靠一圈灰边框) */ :is(.form-group, .settings-form, .modal-body, .m-drawer-search) :is(input, select, textarea) { background: var(--m-fill-quaternary); border-color: transparent; border-radius: 12px; } /* 聚焦:只留一圈柔光环。再改 border 颜色会与 25.5 的环叠成两圈(登录框实测如此)*/ :is(.form-group, .settings-form, .modal-body, .m-drawer-search) :is(input, select, textarea):focus { background: var(--bg-primary); border-color: transparent; box-shadow: 0 0 0 3px var(--m-ring); } /* 26.5 登录卡:满宽卡片 + 填充式字段 + 胶囊主按钮 */ .login-card { border-radius: 20px; box-shadow: 0 18px 40px rgba(0, 0, 0, 0.12); } .login-form .btn-primary, .login-card .btn-primary { width: 100%; border-radius: var(--m-r-pill); } /* 26.6 分组列表:卡片内的行之间只留发丝线,行高按触控下限走 */ :is(.settings-section, .card, .panel, .info-card) :is(.form-group, .settings-row, .list-row) + :is(.form-group, .settings-row, .list-row) { border-top: 1px solid var(--m-hairline); } } /* -------------------------------------------------------------------------- 28. 玻璃质感:环境光画布 + 玻璃表面 + 顶缘高光 -------------------------------------------------------------------------- 目标:把手机端从"平面灰底 + 分隔线"推进到 Apple 的玻璃材质语言。 玻璃要读得出来,前提是材料后面有东西可透,所以这一节先做两件事: - 画布:.container 铺一层极淡的环境光(浅色 = 近白底 + 右上 accent / 左下紫的 柔光;暗色 = 深底 + 同色更亮的柔光),再把 .content-area / .chat-messages 的 实心底色交还给这层画布。此后所有半透明表面都有色可透,各页面自身的内容背景 一律不动。 - 表面分三档:模糊(backdrop-filter)只给"小面积或瞬时"的件 —— 顶栏、底栏 (25 节已有)、底部面板、抽屉、登录卡、弹窗;它们要么面积小,要么只在被打开 时才绘制,不挂在常驻滚动内容上。常驻卡片(.card/.stat-card/... 一列)只做 半透明 + 发丝边 + 顶缘高光,不挂模糊 —— 固定栏的大面积模糊是移动端掉帧 主因(25 节实测),这条口径不动。 - 浅色与暗色全部经 color-mix 从主题令牌派生;只有"顶缘高光"必须换向 (浅色 = 白线,暗色 = 极淡白线),单独覆写。 - prefers-reduced-transparency: reduce 时不问浏览器支持与否,全部退回实心。 -------------------------------------------------------------------------- */ @media (max-width: 767.98px) { :root { /* 栏体材质从 82% 提到 62%:有模糊托底,透出的是画布而不是一块灰板 */ --m-material: color-mix(in srgb, var(--bg-primary) 62%, transparent); /* 玻璃卡(常驻,不挂模糊):透出画布的一点色 */ --m-glass-card: color-mix(in srgb, var(--card-bg) 66%, transparent); /* 玻璃面板(瞬时件,配模糊):抽屉 / 底部面板 / 登录卡 / 弹窗 */ --m-glass-panel: color-mix(in srgb, var(--bg-primary) 72%, transparent); --m-glass-blur: saturate(180%) blur(20px); /* 顶缘高光:玻璃的"口沿"。浅色下是白线,暗色下换极淡白(见下方覆写) */ --m-bevel: inset 0 1px 0 rgba(255, 255, 255, 0.55); } html[data-theme="dark"] { --m-bevel: inset 0 1px 0 rgba(255, 255, 255, 0.07); } /* 28.1 环境光画布:整块 app 换底,卡片与栏体从这层透色 */ .container { background: radial-gradient(120% 88% at 88% -12%, color-mix(in srgb, var(--accent-color) 14%, transparent), transparent 58%), radial-gradient(95% 72% at -12% 106%, color-mix(in srgb, #8b5cf6 11%, transparent), transparent 60%), var(--bg-secondary); } html[data-theme="dark"] .container { background: radial-gradient(120% 88% at 88% -12%, color-mix(in srgb, var(--accent-color) 18%, transparent), transparent 58%), radial-gradient(95% 72% at -12% 106%, color-mix(in srgb, #8b5cf6 13%, transparent), transparent 60%), var(--bg-secondary); } /* 交还底色:画布负责"底",卡片负责"面" */ .content-area, .chat-messages { background: transparent; } /* 28.2 底栏补一道顶缘高光,顶栏保持 25 节的发丝线 */ #m-tabbar { border-top-color: var(--m-hairline); box-shadow: var(--m-bevel); } /* 28.3 卡片玻璃:半透明表面 + 发丝边 + 顶缘高光;投影保持 26 节的"几乎看不见" */ :is(.card, .stat-card, .metric-card, .dashboard-card, .panel, .settings-section, .info-card) { background: var(--m-glass-card); border-color: var(--m-hairline); box-shadow: var(--m-bevel), 0 1px 2px rgba(0, 0, 0, 0.03); } /* 28.4 底部面板:真玻璃(透色 + 模糊),只在打开时绘制 */ #m-sheet { background: var(--m-glass-panel); border-color: color-mix(in srgb, var(--border-color) 50%, transparent); box-shadow: var(--m-bevel), var(--m-shadow-panel); } /* 28.5 抽屉(主导航 / 会话列表):同面板材质的真玻璃。 两套选择器:不带属性的管浅色与初始绘制,html[data-theme] 的管暗色 (style.css 的暗色抽屉是 (0,2,0) 特异性的不透明渐变,必须同形压回)。 */ .main-sidebar, .conversation-sidebar, html[data-theme] .main-sidebar, html[data-theme] .conversation-sidebar { background: var(--m-glass-panel); } .m-chat-drawer-head { background: transparent; } .m-drawer-search input { background: color-mix(in srgb, var(--input-bg) 78%, transparent); } /* 对话页顶部工具条:在流内、滚动内容不会跑到它后面,纯透色不挂模糊 */ #m-chat-bar { background: color-mix(in srgb, var(--bg-primary) 66%, transparent); border-bottom-color: var(--m-hairline); } /* 28.6 弹窗:与面板同档的玻璃 */ .modal-content { background-color: var(--m-glass-panel); border: 1px solid var(--m-hairline); } /* 28.7 登录:遮罩本身得先有"画布"(否则卡片后面只有一块灰),卡片是真玻璃 */ .login-overlay { background: radial-gradient(120% 88% at 85% -10%, color-mix(in srgb, var(--accent-color) 13%, transparent), transparent 60%), radial-gradient(95% 72% at -12% 108%, color-mix(in srgb, #8b5cf6 10%, transparent), transparent 62%), color-mix(in srgb, var(--bg-secondary) 92%, transparent); } html[data-theme="dark"] .login-overlay { background: radial-gradient(120% 88% at 85% -10%, color-mix(in srgb, var(--accent-color) 17%, transparent), transparent 60%), radial-gradient(95% 72% at -12% 108%, color-mix(in srgb, #8b5cf6 12%, transparent), transparent 62%), color-mix(in srgb, #0b1120 92%, transparent); } .login-card { background: var(--m-glass-panel); border-color: var(--m-hairline); box-shadow: var(--m-bevel), 0 18px 44px rgba(0, 0, 0, 0.14); } /* 28.8 模糊:只给上述瞬时件。@supports 兜底逻辑与 25 节一致 */ @supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) { #m-sheet, html[data-theme] .main-sidebar, html[data-theme] .conversation-sidebar, .login-card, .modal-content { -webkit-backdrop-filter: var(--m-glass-blur); backdrop-filter: var(--m-glass-blur); } } /* 28.9 降低透明度环境:退回实心材质(含画布) */ @media (prefers-reduced-transparency: reduce) { .container, .content-area, .chat-messages, .login-overlay { background: var(--bg-secondary); } #m-sheet { background: var(--bg-primary); } .main-sidebar, .conversation-sidebar, html[data-theme] .main-sidebar, html[data-theme] .conversation-sidebar, .login-card, .modal-content { background: var(--bg-primary); } #m-chat-bar { background: var(--bg-primary); } :is(.card, .stat-card, .metric-card, .dashboard-card, .panel, .settings-section, .info-card) { background: var(--card-bg); } #m-sheet, html[data-theme] .main-sidebar, html[data-theme] .conversation-sidebar, .login-card, .modal-content { -webkit-backdrop-filter: none; backdrop-filter: none; } } }