app.css 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794
  1. * {
  2. margin: 0;
  3. padding: 0;
  4. box-sizing: border-box;
  5. }
  6. body {
  7. height: 100vh;
  8. height: 100dvh;
  9. font-family:
  10. "Segoe UI", "Microsoft YaHei", "PingFang SC", "Noto Sans CJK SC", system-ui, sans-serif;
  11. font-size: 14px;
  12. font-weight: 400;
  13. background: #0b1220;
  14. color: #e2e8f0;
  15. /* 移动端点按高亮块去除, 文字渲染更细腻 (不支持的浏览器自动忽略) */
  16. -webkit-tap-highlight-color: transparent;
  17. -webkit-font-smoothing: antialiased;
  18. }
  19. /* 文字选中色: 与主题蓝一致 */
  20. ::selection {
  21. background: rgba(37, 99, 235, 0.45);
  22. color: #f8fafc;
  23. }
  24. /* 内联 SVG 图标: 不依赖系统字体, 随文字颜色自动变色 */
  25. .icon {
  26. width: 1em;
  27. height: 1em;
  28. vertical-align: -0.125em;
  29. fill: none;
  30. stroke: currentColor;
  31. stroke-width: 2;
  32. stroke-linecap: round;
  33. stroke-linejoin: round;
  34. }
  35. /* 侧栏/顶栏 logo: 渐变徽标块 */
  36. .icon-box {
  37. display: inline-flex;
  38. align-items: center;
  39. justify-content: center;
  40. width: 34px;
  41. height: 34px;
  42. border-radius: 8px;
  43. background: linear-gradient(135deg, #2563eb, #1e40af);
  44. color: #ffffff;
  45. flex-shrink: 0;
  46. box-shadow: 0 2px 8px rgba(37, 99, 235, 0.35);
  47. }
  48. .icon-box .icon {
  49. width: 20px;
  50. height: 20px;
  51. vertical-align: middle;
  52. }
  53. /* 图标尺寸统一: 页头 16px, 按钮内 15px, 其余随文字 1em */
  54. .page1-header .icon,
  55. .card-header .icon {
  56. width: 16px;
  57. height: 16px;
  58. }
  59. .btn .icon {
  60. width: 15px;
  61. height: 15px;
  62. }
  63. /* 状态色点: 替代 emoji 圆点, 颜色跨平台一致 */
  64. .dot {
  65. display: inline-block;
  66. width: 9px;
  67. height: 9px;
  68. border-radius: 50%;
  69. margin-right: 6px;
  70. background: #94a3b8;
  71. vertical-align: 1px;
  72. }
  73. .dot.ok {
  74. background: #22c55e;
  75. }
  76. .dot.warn {
  77. background: #eab308;
  78. }
  79. .dot.err {
  80. background: #ef4444;
  81. }
  82. .dot.info {
  83. background: #3b82f6;
  84. }
  85. #loading-mask {
  86. position: fixed;
  87. top: 0;
  88. right: 0;
  89. bottom: 0;
  90. left: 0;
  91. z-index: 9999;
  92. background: rgba(11, 18, 32, 0.88);
  93. /* 毛玻璃效果 (老浏览器自动忽略, 回退为半透明深色) */
  94. -webkit-backdrop-filter: blur(4px);
  95. backdrop-filter: blur(4px);
  96. display: flex;
  97. align-items: center;
  98. justify-content: center;
  99. }
  100. .loading-box {
  101. display: flex;
  102. flex-direction: column;
  103. align-items: center;
  104. gap: 12px;
  105. }
  106. .loading-dot {
  107. width: 24px;
  108. height: 24px;
  109. border: 3px solid #475569;
  110. border-top-color: #38bdf8;
  111. border-radius: 50%;
  112. animation: loading-spin 1s linear infinite;
  113. }
  114. .loading-text {
  115. color: #94a3b8;
  116. font-size: 14px;
  117. }
  118. @keyframes loading-spin {
  119. from {
  120. transform: rotate(0deg);
  121. }
  122. to {
  123. transform: rotate(360deg);
  124. }
  125. }
  126. /* 主布局 */
  127. .app {
  128. display: flex;
  129. flex-direction: column;
  130. height: 100vh;
  131. height: 100dvh;
  132. }
  133. /* 标题栏 */
  134. .topbar {
  135. height: 64px;
  136. flex-shrink: 0; /* 防止 flex 布局挤压裁切标题栏 */
  137. background: linear-gradient(180deg, #1f2937, #111827);
  138. border-bottom: 1px solid rgba(148, 163, 184, 0.18);
  139. box-shadow: 0 1px 0 rgba(0, 0, 0, 0.4);
  140. display: flex;
  141. align-items: center;
  142. justify-content: space-between;
  143. padding: 0;
  144. }
  145. .device {
  146. display: flex;
  147. align-items: center;
  148. gap: 10px;
  149. margin-left: 12px;
  150. }
  151. .device .icon {
  152. width: 20px;
  153. height: 20px;
  154. }
  155. .device-info {
  156. display: flex;
  157. flex-direction: column;
  158. justify-content: center;
  159. }
  160. .title-line {
  161. line-height: 1.3;
  162. }
  163. .title-line .title {
  164. font-size: 15px;
  165. font-weight: 600;
  166. color: #e2e8f0;
  167. letter-spacing: 0.5px;
  168. }
  169. .imei-line {
  170. line-height: 1.3;
  171. }
  172. #imei-value {
  173. font-family: ui-monospace, monospace;
  174. font-size: 15px;
  175. color: #94a3b8;
  176. }
  177. .actions {
  178. display: flex;
  179. align-items: center;
  180. gap: 10px;
  181. margin-right: 16px;
  182. }
  183. .clock {
  184. font-size: 16px;
  185. font-weight: 700;
  186. letter-spacing: 1px;
  187. background: linear-gradient(90deg, #00e5ff, #2196ff, #00ffcc);
  188. -webkit-background-clip: text;
  189. -webkit-text-fill-color: transparent;
  190. background-clip: text;
  191. text-shadow:
  192. 0 0 6px rgba(0, 229, 255, 0.6),
  193. 0 0 12px rgba(33, 150, 255, 0.5);
  194. }
  195. /* 次布局 */
  196. .layout {
  197. display: flex;
  198. flex: 1;
  199. overflow: hidden;
  200. }
  201. /* 左侧栏 */
  202. .sidebar {
  203. width: 150px;
  204. background: #0f172a;
  205. border-right: 1px solid rgba(148, 163, 184, 0.12);
  206. display: flex;
  207. flex-direction: column;
  208. gap: 6px;
  209. padding: 22px 12px;
  210. /* 兜底: 窗口高度不足时侧栏自身滚动, 不会把底部"重启/退出"裁掉 */
  211. overflow-y: auto;
  212. overscroll-behavior: contain;
  213. }
  214. .sidebar-actions {
  215. margin-top: auto;
  216. padding-top: 16px;
  217. border-top: 1px solid rgba(148, 163, 184, 0.12);
  218. display: flex;
  219. flex-direction: column;
  220. gap: 10px;
  221. }
  222. .action-item {
  223. display: flex;
  224. align-items: center;
  225. gap: 10px;
  226. padding: 13px 12px;
  227. border-radius: 6px;
  228. cursor: pointer;
  229. color: #94a3b8;
  230. font-size: 15px;
  231. font-weight: 500;
  232. transition: 0.2s;
  233. white-space: nowrap;
  234. }
  235. .action-item:hover {
  236. background: #1e293b;
  237. color: white;
  238. }
  239. .action-reboot .action-logout {
  240. color: #94a3b8;
  241. }
  242. .menu-item {
  243. display: flex;
  244. align-items: center;
  245. gap: 10px;
  246. padding: 13px 12px;
  247. border-radius: 6px;
  248. cursor: pointer;
  249. color: #94a3b8;
  250. font-size: 15px;
  251. font-weight: 500;
  252. transition: 0.2s;
  253. white-space: nowrap;
  254. }
  255. /* 菜单/动作项图标: 统一 18px, 保证清晰可辨 */
  256. .menu-item .icon,
  257. .action-item .icon {
  258. width: 18px;
  259. height: 18px;
  260. flex-shrink: 0;
  261. }
  262. .menu-item:hover {
  263. background: #1e293b;
  264. color: white;
  265. }
  266. .menu-item.active {
  267. background: #2563eb;
  268. color: white;
  269. }
  270. /* 页容器 */
  271. .view {
  272. flex: 1;
  273. padding: 6px;
  274. overflow: auto;
  275. background: #0f172a;
  276. }
  277. /* 动态页面片段自带的 .app 容器: 高度按视口区域自适应, 避免出现多余滚动 */
  278. #view .app {
  279. height: 100%;
  280. }
  281. /* 页风格 */
  282. .card {
  283. background: #0f172a;
  284. border: 1px solid #1f2937;
  285. border-radius: 4px;
  286. padding: 12px;
  287. margin-bottom: 0;
  288. }
  289. .card-header {
  290. display: flex;
  291. justify-content: space-between;
  292. align-items: center;
  293. margin-bottom: 12px;
  294. }
  295. .card-header h2 {
  296. font-size: 14px;
  297. font-weight: 500;
  298. color: #e2e8f0;
  299. margin: 0;
  300. }
  301. .output {
  302. background: #020617;
  303. border: 1px solid #1f2937;
  304. border-radius: 4px;
  305. padding: 8px;
  306. font-size: 12px;
  307. font-family: ui-monospace, Consolas, monospace;
  308. color: #cbd5e1;
  309. overflow: auto;
  310. white-space: pre-wrap;
  311. word-break: break-all;
  312. line-height: 1.6;
  313. /* 内阴影: 让日志/输出区呈现"凹陷"层次 */
  314. box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.45);
  315. }
  316. h1 {
  317. font-size: 20px;
  318. margin-bottom: 10px;
  319. color: #e2e8f0;
  320. }
  321. p {
  322. font-size: 15px;
  323. color: #94a3b8;
  324. }
  325. /* 日志区 */
  326. .log-card {
  327. flex: 1;
  328. display: flex;
  329. flex-direction: column;
  330. min-height: 0;
  331. }
  332. .log-card .output {
  333. flex: 1;
  334. min-height: 0;
  335. }
  336. .log-actions {
  337. display: flex;
  338. align-items: center;
  339. gap: 4px;
  340. }
  341. .log-level-select {
  342. background: #0f172a;
  343. border: 1px solid rgba(148, 163, 184, 0.25);
  344. border-radius: 4px;
  345. color: #e5e7eb;
  346. font-size: 14px;
  347. padding: 6px 8px;
  348. height: 35px;
  349. min-width: 80px;
  350. cursor: pointer;
  351. outline: none;
  352. -webkit-appearance: none;
  353. appearance: none;
  354. }
  355. .log-level-select:hover {
  356. border-color: rgba(148, 163, 184, 0.45);
  357. }
  358. .log-level-select:focus {
  359. border-color: #3b82f6;
  360. }
  361. .log-card .output {
  362. padding-bottom: 32px;
  363. }
  364. /* 日志色 */
  365. .log-trace {
  366. color: #38bdf8;
  367. }
  368. .log-debug {
  369. color: #38bdf8;
  370. }
  371. .log-info {
  372. color: #38bdf8;
  373. }
  374. .log-warn {
  375. color: #fbbf24;
  376. }
  377. .log-error {
  378. color: #fb7185;
  379. }
  380. .log-fatal {
  381. color: #ef4444;
  382. font-weight: bold;
  383. }
  384. .log-panic {
  385. color: #c084fc;
  386. font-weight: bold;
  387. }
  388. .log-default {
  389. color: #38bdf8;
  390. }
  391. /* ================= 界面美化增强 ================= */
  392. /* 页面背景与卡片形成层次: 视图区更深, 卡片浮起 */
  393. .view {
  394. background: #0b1220;
  395. }
  396. .card,
  397. .page1 .page1-card {
  398. border-radius: 6px;
  399. border-color: #24314a;
  400. box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
  401. }
  402. /* 卡片悬停轻微上浮 */
  403. .page1 .page1-card {
  404. transition:
  405. box-shadow 0.2s ease,
  406. border-color 0.2s ease;
  407. }
  408. .page1 .page1-card:hover {
  409. border-color: #334564;
  410. box-shadow: 0 3px 10px rgba(0, 0, 0, 0.45);
  411. }
  412. /* 菜单项激活态: 渐变背景 + 光晕 */
  413. .menu-item.active {
  414. background: linear-gradient(135deg, #2563eb, #1d4ed8);
  415. box-shadow: 0 0 12px rgba(37, 99, 235, 0.45);
  416. }
  417. /* 菜单/动作项 hover 左侧指示条 */
  418. .menu-item,
  419. .action-item {
  420. position: relative;
  421. border-left: 3px solid transparent;
  422. }
  423. .menu-item:hover,
  424. .action-item:hover {
  425. border-left-color: #475569;
  426. }
  427. .menu-item.active {
  428. border-left-color: #60a5fa;
  429. }
  430. /* 按钮悬停微抬升 (老浏览器自动忽略) */
  431. .btn {
  432. transition:
  433. background 0.15s ease,
  434. border-color 0.15s ease,
  435. transform 0.15s ease,
  436. box-shadow 0.15s ease;
  437. }
  438. .btn:hover {
  439. transform: translateY(-1px);
  440. box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
  441. }
  442. .btn:active {
  443. transform: translateY(0);
  444. box-shadow: none;
  445. }
  446. /* 状态点: 错误状态呼吸提示 */
  447. @keyframes dot-pulse {
  448. 0%,
  449. 100% {
  450. opacity: 1;
  451. }
  452. 50% {
  453. opacity: 0.45;
  454. }
  455. }
  456. .dot.err {
  457. animation: dot-pulse 1.2s ease-in-out infinite;
  458. }
  459. /* 滚动条美化 (WebKit 内核; IE/Firefox 自动忽略) */
  460. ::-webkit-scrollbar {
  461. width: 8px;
  462. height: 8px;
  463. }
  464. ::-webkit-scrollbar-track {
  465. background: transparent;
  466. }
  467. ::-webkit-scrollbar-thumb {
  468. background: #334155;
  469. border-radius: 4px;
  470. }
  471. ::-webkit-scrollbar-thumb:hover {
  472. background: #475569;
  473. }
  474. /* 输入控件聚焦一致的高亮 */
  475. input:focus,
  476. select:focus,
  477. textarea:focus {
  478. border-color: #3b82f6;
  479. outline: none;
  480. }
  481. /* 页面切换淡入过渡: 消除切换时的生硬跳变 (老浏览器自动忽略) */
  482. @keyframes view-fade {
  483. from {
  484. opacity: 0;
  485. transform: translateY(6px);
  486. }
  487. to {
  488. opacity: 1;
  489. transform: translateY(0);
  490. }
  491. }
  492. #view > .app,
  493. #view > .page1 {
  494. animation: view-fade 0.2s ease;
  495. }
  496. /* 键盘导航可达性: 菜单/动作项聚焦高亮 */
  497. .menu-item:focus-visible,
  498. .action-item:focus-visible {
  499. outline: 2px solid #3b82f6;
  500. outline-offset: -2px;
  501. }
  502. /* 侧栏宽度: 确保四个字菜单单行显示 */
  503. .sidebar {
  504. flex-shrink: 0;
  505. }
  506. .menu-item,
  507. .action-item {
  508. overflow: hidden;
  509. text-overflow: ellipsis;
  510. }
  511. /* ================= 紧凑布局(手机竖屏 / 手机横屏 / 矮窗口)=================
  512. 触发条件只做"或"判断, 不叠加 orientation、不设宽度上限, 杜绝"两边都不命中"的盲区:
  513. 1. 宽度 ≤768px —— 手机竖屏、平板竖屏、窄窗口
  514. 2. 高度 ≤600px —— 手机横屏(典型 360~430px)、矮窗口
  515. 依据: 竖向侧栏(8 项菜单 + 底部 2 项操作)实测约需 541px, 加上 64px 顶栏约 605px,
  516. 视口高度低于 600px 时竖排侧栏必然放不下
  517. 横屏不再单独成一套规则 —— 横竖屏走完全相同的代码路径, 只重算宽度, 行为可预期 */
  518. @media (max-width: 768px), (max-height: 600px) {
  519. /* 顶栏: 单行紧凑布局。
  520. flex-shrink:0 防止内容超高时标题栏被 flex 压缩裁切(窄屏遮挡问题的根因);
  521. 标题/IMEI 单行省略, 不再换行撑高标题栏;
  522. env(safe-area-inset-top) 兼容刘海屏/全面屏安全区 */
  523. .topbar {
  524. height: auto;
  525. min-height: 50px;
  526. flex-shrink: 0;
  527. /* 内容实测 39px(标题+IMEI 两行), 上下各 5px => 恰好 50px;
  528. 换成固定 height 会在系统大字号时裁切文字, 所以用 min-height 定下限 */
  529. padding: 5px 10px;
  530. padding-top: max(5px, env(safe-area-inset-top));
  531. position: relative;
  532. z-index: 20;
  533. }
  534. .device {
  535. flex: 1;
  536. min-width: 0;
  537. margin-left: 8px;
  538. }
  539. .device .icon-box {
  540. width: 28px;
  541. height: 28px;
  542. border-radius: 6px;
  543. }
  544. .device .icon {
  545. width: 16px;
  546. height: 16px;
  547. }
  548. .device-info {
  549. min-width: 0;
  550. }
  551. .title-line,
  552. .imei-line {
  553. overflow: hidden;
  554. white-space: nowrap;
  555. text-overflow: ellipsis;
  556. }
  557. .title {
  558. font-size: 14px;
  559. }
  560. .imei-line {
  561. font-size: 11px;
  562. }
  563. .actions {
  564. margin-right: 10px;
  565. flex-shrink: 0;
  566. }
  567. .clock {
  568. font-size: 12px;
  569. }
  570. /* 侧栏: 变为顶部横向滚动导航, 固定高度不参与压缩。
  571. .layout 必须保持 overflow:hidden —— 外壳始终等于视口高度,
  572. 页面绝不出现"文档级滚动"(移动端地址栏收放时最容易错位的那种滚动),
  573. 滚动只发生在下面的 #view 里, 这是整个紧凑布局稳定性的关键 */
  574. .layout {
  575. flex-direction: column;
  576. overflow: hidden;
  577. }
  578. .sidebar {
  579. width: 100%;
  580. flex-shrink: 0;
  581. flex-direction: row;
  582. align-items: center;
  583. padding: 6px 8px;
  584. border-right: none;
  585. border-bottom: 1px solid rgba(148, 163, 184, 0.12);
  586. overflow-x: auto;
  587. overflow-y: hidden;
  588. gap: 4px;
  589. -webkit-overflow-scrolling: touch;
  590. }
  591. .sidebar .sidebar-actions {
  592. margin-top: 0;
  593. margin-left: auto;
  594. padding-top: 0;
  595. border-top: none;
  596. flex-direction: row;
  597. gap: 4px;
  598. flex-shrink: 0;
  599. }
  600. .menu-item,
  601. .action-item {
  602. flex-shrink: 0;
  603. padding: 8px 10px;
  604. font-size: 13px;
  605. gap: 6px;
  606. border-left: none;
  607. }
  608. .menu-item .icon,
  609. .action-item .icon {
  610. width: 16px;
  611. height: 16px;
  612. }
  613. .menu-item.active {
  614. box-shadow: none;
  615. }
  616. /* 内容区: 全站唯一的滚动容器。
  617. flex:1 撑满顶栏与导航之外的剩余高度; min-height:0 让它在内容超高时真的能滚动
  618. (缺了它, flex 子项的自动最小高度会把外壳顶高, 又退回文档级滚动) */
  619. .view {
  620. padding: 4px;
  621. }
  622. #view {
  623. flex: 1;
  624. min-height: 0;
  625. overflow-y: auto;
  626. -webkit-overflow-scrolling: touch;
  627. }
  628. /* 页面片段根容器: 高度随内容自然撑开, 不再各自套一层滚动, 避免嵌套滚动的各种怪象 */
  629. #view .app {
  630. height: auto;
  631. min-height: 0;
  632. }
  633. /* page1 根容器不叫 .app 且固定 100% 高, 同样放开, 让卡片随内容撑开 */
  634. #view > .page1 {
  635. height: auto;
  636. min-height: 100%;
  637. }
  638. /* 终端页例外: xterm 依赖明确的容器高度计算行列, 必须占满可视区域 */
  639. #view > .app-terminal {
  640. height: 100%;
  641. min-height: 0;
  642. }
  643. /* iOS 聚焦输入类控件时若字号 <16px 会自动放大页面, 统一提到 16px 规避 */
  644. .log-level-select {
  645. font-size: 16px;
  646. }
  647. }
  648. /* ================= 矮屏微调(手机横屏等, 高度 ≤600px)=================
  649. 横屏可用高度只有 360~430px, 这里只做"省空间"一件事: 压缩顶栏与导航占用的高度,
  650. 把空间让给内容。**滚动方式完全不变**——依旧由 #view 内部滚动,
  651. 与竖屏是同一套逻辑, 所以横竖屏切换不会出现"能显示但不能滚"的割裂状态 */
  652. @media (max-height: 600px) {
  653. .topbar {
  654. min-height: 44px;
  655. padding-top: max(5px, env(safe-area-inset-top));
  656. padding-bottom: 5px;
  657. /* 横屏时刘海位于左右两侧 */
  658. padding-left: max(10px, env(safe-area-inset-left));
  659. padding-right: max(10px, env(safe-area-inset-right));
  660. }
  661. .device .icon-box {
  662. width: 24px;
  663. height: 24px;
  664. border-radius: 5px;
  665. }
  666. .device .icon {
  667. width: 14px;
  668. height: 14px;
  669. }
  670. .sidebar {
  671. padding: 4px 8px;
  672. padding-left: max(8px, env(safe-area-inset-left));
  673. padding-right: max(8px, env(safe-area-inset-right));
  674. }
  675. .menu-item,
  676. .action-item {
  677. padding: 6px 10px;
  678. }
  679. }