* { margin: 0; padding: 0; box-sizing: border-box; } body { height: 100vh; height: 100dvh; font-family: "Segoe UI", "Microsoft YaHei", "PingFang SC", "Noto Sans CJK SC", system-ui, sans-serif; font-size: 14px; font-weight: 400; background: #0b1220; color: #e2e8f0; /* 移动端点按高亮块去除, 文字渲染更细腻 (不支持的浏览器自动忽略) */ -webkit-tap-highlight-color: transparent; -webkit-font-smoothing: antialiased; } /* 文字选中色: 与主题蓝一致 */ ::selection { background: rgba(37, 99, 235, 0.45); color: #f8fafc; } /* 内联 SVG 图标: 不依赖系统字体, 随文字颜色自动变色 */ .icon { width: 1em; height: 1em; vertical-align: -0.125em; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; } /* 侧栏/顶栏 logo: 渐变徽标块 */ .icon-box { display: inline-flex; align-items: center; justify-content: center; width: 34px; height: 34px; border-radius: 8px; background: linear-gradient(135deg, #2563eb, #1e40af); color: #ffffff; flex-shrink: 0; box-shadow: 0 2px 8px rgba(37, 99, 235, 0.35); } .icon-box .icon { width: 20px; height: 20px; vertical-align: middle; } /* 图标尺寸统一: 页头 16px, 按钮内 15px, 其余随文字 1em */ .page1-header .icon, .card-header .icon { width: 16px; height: 16px; } .btn .icon { width: 15px; height: 15px; } /* 状态色点: 替代 emoji 圆点, 颜色跨平台一致 */ .dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; margin-right: 6px; background: #94a3b8; vertical-align: 1px; } .dot.ok { background: #22c55e; } .dot.warn { background: #eab308; } .dot.err { background: #ef4444; } .dot.info { background: #3b82f6; } #loading-mask { position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 9999; background: rgba(11, 18, 32, 0.88); /* 毛玻璃效果 (老浏览器自动忽略, 回退为半透明深色) */ -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px); display: flex; align-items: center; justify-content: center; } .loading-box { display: flex; flex-direction: column; align-items: center; gap: 12px; } .loading-dot { width: 24px; height: 24px; border: 3px solid #475569; border-top-color: #38bdf8; border-radius: 50%; animation: loading-spin 1s linear infinite; } .loading-text { color: #94a3b8; font-size: 14px; } @keyframes loading-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } /* 主布局 */ .app { display: flex; flex-direction: column; height: 100vh; height: 100dvh; } /* 标题栏 */ .topbar { height: 64px; flex-shrink: 0; /* 防止 flex 布局挤压裁切标题栏 */ background: linear-gradient(180deg, #1f2937, #111827); border-bottom: 1px solid rgba(148, 163, 184, 0.18); box-shadow: 0 1px 0 rgba(0, 0, 0, 0.4); display: flex; align-items: center; justify-content: space-between; padding: 0; } .device { display: flex; align-items: center; gap: 10px; margin-left: 12px; } .device .icon { width: 20px; height: 20px; } .device-info { display: flex; flex-direction: column; justify-content: center; } .title-line { line-height: 1.3; } .title-line .title { font-size: 15px; font-weight: 600; color: #e2e8f0; letter-spacing: 0.5px; } .imei-line { line-height: 1.3; } #imei-value { font-family: ui-monospace, monospace; font-size: 15px; color: #94a3b8; } .actions { display: flex; align-items: center; gap: 10px; margin-right: 16px; } .clock { font-size: 16px; font-weight: 700; letter-spacing: 1px; background: linear-gradient(90deg, #00e5ff, #2196ff, #00ffcc); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; text-shadow: 0 0 6px rgba(0, 229, 255, 0.6), 0 0 12px rgba(33, 150, 255, 0.5); } /* 次布局 */ .layout { display: flex; flex: 1; overflow: hidden; } /* 左侧栏 */ .sidebar { width: 150px; background: #0f172a; border-right: 1px solid rgba(148, 163, 184, 0.12); display: flex; flex-direction: column; gap: 6px; padding: 22px 12px; /* 兜底: 窗口高度不足时侧栏自身滚动, 不会把底部"重启/退出"裁掉 */ overflow-y: auto; overscroll-behavior: contain; } .sidebar-actions { margin-top: auto; padding-top: 16px; border-top: 1px solid rgba(148, 163, 184, 0.12); display: flex; flex-direction: column; gap: 10px; } .action-item { display: flex; align-items: center; gap: 10px; padding: 13px 12px; border-radius: 6px; cursor: pointer; color: #94a3b8; font-size: 15px; font-weight: 500; transition: 0.2s; white-space: nowrap; } .action-item:hover { background: #1e293b; color: white; } .action-reboot .action-logout { color: #94a3b8; } .menu-item { display: flex; align-items: center; gap: 10px; padding: 13px 12px; border-radius: 6px; cursor: pointer; color: #94a3b8; font-size: 15px; font-weight: 500; transition: 0.2s; white-space: nowrap; } /* 菜单/动作项图标: 统一 18px, 保证清晰可辨 */ .menu-item .icon, .action-item .icon { width: 18px; height: 18px; flex-shrink: 0; } .menu-item:hover { background: #1e293b; color: white; } .menu-item.active { background: #2563eb; color: white; } /* 页容器 */ .view { flex: 1; padding: 6px; overflow: auto; background: #0f172a; } /* 动态页面片段自带的 .app 容器: 高度按视口区域自适应, 避免出现多余滚动 */ #view .app { height: 100%; } /* 页风格 */ .card { background: #0f172a; border: 1px solid #1f2937; border-radius: 4px; padding: 12px; margin-bottom: 0; } .card-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; } .card-header h2 { font-size: 14px; font-weight: 500; color: #e2e8f0; margin: 0; } .output { background: #020617; border: 1px solid #1f2937; border-radius: 4px; padding: 8px; font-size: 12px; font-family: ui-monospace, Consolas, monospace; color: #cbd5e1; overflow: auto; white-space: pre-wrap; word-break: break-all; line-height: 1.6; /* 内阴影: 让日志/输出区呈现"凹陷"层次 */ box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.45); } h1 { font-size: 20px; margin-bottom: 10px; color: #e2e8f0; } p { font-size: 15px; color: #94a3b8; } /* 日志区 */ .log-card { flex: 1; display: flex; flex-direction: column; min-height: 0; } .log-card .output { flex: 1; min-height: 0; } .log-actions { display: flex; align-items: center; gap: 4px; } .log-level-select { background: #0f172a; border: 1px solid rgba(148, 163, 184, 0.25); border-radius: 4px; color: #e5e7eb; font-size: 14px; padding: 6px 8px; height: 35px; min-width: 80px; cursor: pointer; outline: none; -webkit-appearance: none; appearance: none; } .log-level-select:hover { border-color: rgba(148, 163, 184, 0.45); } .log-level-select:focus { border-color: #3b82f6; } .log-card .output { padding-bottom: 32px; } /* 日志色 */ .log-trace { color: #38bdf8; } .log-debug { color: #38bdf8; } .log-info { color: #38bdf8; } .log-warn { color: #fbbf24; } .log-error { color: #fb7185; } .log-fatal { color: #ef4444; font-weight: bold; } .log-panic { color: #c084fc; font-weight: bold; } .log-default { color: #38bdf8; } /* ================= 界面美化增强 ================= */ /* 页面背景与卡片形成层次: 视图区更深, 卡片浮起 */ .view { background: #0b1220; } .card, .page1 .page1-card { border-radius: 6px; border-color: #24314a; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35); } /* 卡片悬停轻微上浮 */ .page1 .page1-card { transition: box-shadow 0.2s ease, border-color 0.2s ease; } .page1 .page1-card:hover { border-color: #334564; box-shadow: 0 3px 10px rgba(0, 0, 0, 0.45); } /* 菜单项激活态: 渐变背景 + 光晕 */ .menu-item.active { background: linear-gradient(135deg, #2563eb, #1d4ed8); box-shadow: 0 0 12px rgba(37, 99, 235, 0.45); } /* 菜单/动作项 hover 左侧指示条 */ .menu-item, .action-item { position: relative; border-left: 3px solid transparent; } .menu-item:hover, .action-item:hover { border-left-color: #475569; } .menu-item.active { border-left-color: #60a5fa; } /* 按钮悬停微抬升 (老浏览器自动忽略) */ .btn { transition: background 0.15s ease, border-color 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease; } .btn:hover { transform: translateY(-1px); box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3); } .btn:active { transform: translateY(0); box-shadow: none; } /* 状态点: 错误状态呼吸提示 */ @keyframes dot-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.45; } } .dot.err { animation: dot-pulse 1.2s ease-in-out infinite; } /* 滚动条美化 (WebKit 内核; IE/Firefox 自动忽略) */ ::-webkit-scrollbar { width: 8px; height: 8px; } ::-webkit-scrollbar-track { background: transparent; } ::-webkit-scrollbar-thumb { background: #334155; border-radius: 4px; } ::-webkit-scrollbar-thumb:hover { background: #475569; } /* 输入控件聚焦一致的高亮 */ input:focus, select:focus, textarea:focus { border-color: #3b82f6; outline: none; } /* 页面切换淡入过渡: 消除切换时的生硬跳变 (老浏览器自动忽略) */ @keyframes view-fade { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } } #view > .app, #view > .page1 { animation: view-fade 0.2s ease; } /* 键盘导航可达性: 菜单/动作项聚焦高亮 */ .menu-item:focus-visible, .action-item:focus-visible { outline: 2px solid #3b82f6; outline-offset: -2px; } /* 侧栏宽度: 确保四个字菜单单行显示 */ .sidebar { flex-shrink: 0; } .menu-item, .action-item { overflow: hidden; text-overflow: ellipsis; } /* ================= 紧凑布局(手机竖屏 / 手机横屏 / 矮窗口)================= 触发条件只做"或"判断, 不叠加 orientation、不设宽度上限, 杜绝"两边都不命中"的盲区: 1. 宽度 ≤768px —— 手机竖屏、平板竖屏、窄窗口 2. 高度 ≤600px —— 手机横屏(典型 360~430px)、矮窗口 依据: 竖向侧栏(8 项菜单 + 底部 2 项操作)实测约需 541px, 加上 64px 顶栏约 605px, 视口高度低于 600px 时竖排侧栏必然放不下 横屏不再单独成一套规则 —— 横竖屏走完全相同的代码路径, 只重算宽度, 行为可预期 */ @media (max-width: 768px), (max-height: 600px) { /* 顶栏: 单行紧凑布局。 flex-shrink:0 防止内容超高时标题栏被 flex 压缩裁切(窄屏遮挡问题的根因); 标题/IMEI 单行省略, 不再换行撑高标题栏; env(safe-area-inset-top) 兼容刘海屏/全面屏安全区 */ .topbar { height: auto; min-height: 50px; flex-shrink: 0; /* 内容实测 39px(标题+IMEI 两行), 上下各 5px => 恰好 50px; 换成固定 height 会在系统大字号时裁切文字, 所以用 min-height 定下限 */ padding: 5px 10px; padding-top: max(5px, env(safe-area-inset-top)); position: relative; z-index: 20; } .device { flex: 1; min-width: 0; margin-left: 8px; } .device .icon-box { width: 28px; height: 28px; border-radius: 6px; } .device .icon { width: 16px; height: 16px; } .device-info { min-width: 0; } .title-line, .imei-line { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } .title { font-size: 14px; } .imei-line { font-size: 11px; } .actions { margin-right: 10px; flex-shrink: 0; } .clock { font-size: 12px; } /* 侧栏: 变为顶部横向滚动导航, 固定高度不参与压缩。 .layout 必须保持 overflow:hidden —— 外壳始终等于视口高度, 页面绝不出现"文档级滚动"(移动端地址栏收放时最容易错位的那种滚动), 滚动只发生在下面的 #view 里, 这是整个紧凑布局稳定性的关键 */ .layout { flex-direction: column; overflow: hidden; } .sidebar { width: 100%; flex-shrink: 0; flex-direction: row; align-items: center; padding: 6px 8px; border-right: none; border-bottom: 1px solid rgba(148, 163, 184, 0.12); overflow-x: auto; overflow-y: hidden; gap: 4px; -webkit-overflow-scrolling: touch; } .sidebar .sidebar-actions { margin-top: 0; margin-left: auto; padding-top: 0; border-top: none; flex-direction: row; gap: 4px; flex-shrink: 0; } .menu-item, .action-item { flex-shrink: 0; padding: 8px 10px; font-size: 13px; gap: 6px; border-left: none; } .menu-item .icon, .action-item .icon { width: 16px; height: 16px; } .menu-item.active { box-shadow: none; } /* 内容区: 全站唯一的滚动容器。 flex:1 撑满顶栏与导航之外的剩余高度; min-height:0 让它在内容超高时真的能滚动 (缺了它, flex 子项的自动最小高度会把外壳顶高, 又退回文档级滚动) */ .view { padding: 4px; } #view { flex: 1; min-height: 0; overflow-y: auto; -webkit-overflow-scrolling: touch; } /* 页面片段根容器: 高度随内容自然撑开, 不再各自套一层滚动, 避免嵌套滚动的各种怪象 */ #view .app { height: auto; min-height: 0; } /* page1 根容器不叫 .app 且固定 100% 高, 同样放开, 让卡片随内容撑开 */ #view > .page1 { height: auto; min-height: 100%; } /* 终端页例外: xterm 依赖明确的容器高度计算行列, 必须占满可视区域 */ #view > .app-terminal { height: 100%; min-height: 0; } /* iOS 聚焦输入类控件时若字号 <16px 会自动放大页面, 统一提到 16px 规避 */ .log-level-select { font-size: 16px; } } /* ================= 矮屏微调(手机横屏等, 高度 ≤600px)================= 横屏可用高度只有 360~430px, 这里只做"省空间"一件事: 压缩顶栏与导航占用的高度, 把空间让给内容。**滚动方式完全不变**——依旧由 #view 内部滚动, 与竖屏是同一套逻辑, 所以横竖屏切换不会出现"能显示但不能滚"的割裂状态 */ @media (max-height: 600px) { .topbar { min-height: 44px; padding-top: max(5px, env(safe-area-inset-top)); padding-bottom: 5px; /* 横屏时刘海位于左右两侧 */ padding-left: max(10px, env(safe-area-inset-left)); padding-right: max(10px, env(safe-area-inset-right)); } .device .icon-box { width: 24px; height: 24px; border-radius: 5px; } .device .icon { width: 14px; height: 14px; } .sidebar { padding: 4px 8px; padding-left: max(8px, env(safe-area-inset-left)); padding-right: max(8px, env(safe-area-inset-right)); } .menu-item, .action-item { padding: 6px 10px; } }