Files
MeowOS-For-R3P2/patch_v2/meowpanel/web/meowpanel.css
T
MeowOS d8eeef5c57 feat(panel, netmgr §11): MeowPanel 网络管理页—netmgrd 期2 面板侧
后端(patch_v2/meowpanel/meowpanel.c,/api/audio 同款透传模式):
- GET /api/net/status    → 0xE0 概览 JSON(接口表 + WiFi/BT 摘要 + 速率)
- GET /api/net/conns?entity= → 0xE8 连接表(解析 entity|本端|远端|状态|寿命|域名 → JSON,transit 截断标记)
- GET /api/net/dns       → 0xEC status(mode/enabled/upstream/缓存命中/查询/deny/resolv_modified)
- POST /api/net/dns/flush → 0xEC flush(写级:HTTP 层走 require_auth;IPC 身份由 netmgrd 判定)
- GET /api/net/log       → TF MeowOS/logs/ 最新 net_*.log tail(256KB 上限,1000 行)
- netmgrd 不在/超时/应答异常 → 503 降级 JSON(NET_UNAVAILABLE),面板其它页不受影响
- 新增 --sock-net / env MEOW_NET_SOCK(宿主/qemu 测试用)

前端(web/,MD3/MIUIx 双主题沿用 270 落地件):
- 侧栏新增「网络」入口(#/net);接口卡 + WiFi 摘要 + 蓝牙摘要 + 每实体连接表
  (entity 前缀筛选 + 5s 轮询)+ DNS 代理卡(统计 + 清缓存按钮)+ 日志查看器
- netmgrd 不在 → 显示「网络管理服务不可用」占位 + 重试,不白屏不报错栈

验证:宿主 cc -Wall -Wextra 0 warning;mipsel 交叉编译 0 warning;假 netmgrd IPC 桩
(Python 帧级桩)联调五端点含 entity 过滤/flush 成功与 403/503 降级全通过;headless
Chromium 渲染 #/net(服务在线/离线两态)与 #/perf #/audio 无回归。
后端起二进制 md5: 21c70046476d3bf63e76f3d63e9eca2c(mipsel 静态)。

遗留(主会话部署时决策):netmgrd flush 写级依 SO_PEERCRED 判系统身份,面板 exe 位于
/usr/data/mnt/sd_0/MeowOS/opt/MeowPanel/(非 /usr/bin/meow_* 亦非 os.meow.* 包)→ 真机
上 flush 会被 netmgrd 拒(403)。需在 netmgrd classify_exe 增认面板路径,或接受 403 只读。
不部署到设备(留主会话热更)。
2026-09-01 21:01:00 +08:00

384 lines
19 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* MeowPanel — 前端样式
* 主题模型:风格族(ui.style: md3 / miuix)× 明暗(data-mode: dark / light),四套主题表。
* [data-style] 定形状(radius / spacing / outline 宽)——对齐 SDK meow_ui_theme.c §8.1.3
* [data-style][data-mode] 定完整色板(bg/surface/on-surface/outline/primary/error/...),
* MIUIx 色值取自设备端 meow_ui_theme.c 的 MIUIx 配色(暖色基面 + 橙强调)。
* 默认(index.html 根属性)= MD3 dark;面板内可覆盖(meowpanel.js 写 data-style/data-mode)。
*/
/* ---------- 结构基线(MD3 形状) ---------- */
:root{
--space:8px;
--radius-s:8px; /* 小:chip/内嵌 */
--radius-m:12px; /* 中:按钮/输入 */
--radius-l:16px; /* 大:卡片 */
--outline-w:0px; /* MD3 无描边 */
--nav-w:212px;
--topbar-h:56px;
--font: -apple-system, "Segoe UI", "PingFang SC", "Noto Sans CJK SC", "Microsoft YaHei", sans-serif;
--mono: ui-monospace, "SFMono-Regular", "Cascadia Mono", Consolas, monospace;
--speed:.16s;
}
/* 风格族:MIUIx 更大圆角(20/28,对齐设备 radius/radius_card)/ 更松间距 / 细描边 */
:root[data-style="miuix"]{
--space:12px;
--radius-s:14px;
--radius-m:20px; /* 按钮 ≈20px */
--radius-l:28px; /* 卡片 ≈28px */
--outline-w:1px; /* 细描边 */
}
/* ---------- MD3 × 深色 ---------- */
:root[data-style="md3"][data-mode="dark"]{
--bg:#0a0a0a; /* 背景底(黑)*/
--surface:#141414;
--surface-variant:#1e1e1e;
--surface-dim:#000000;
--on-surface:#ececec;
--on-surface-variant:#a3a3a3;
--outline:#3a3a3a;
--outline-variant:#2a2a2a;
--primary:#4a8bf5; --on-primary:#ffffff;
--error:#cf6679; --on-error:#0a0a0a;
--success:#4caf50; --warning:#ffb300;
--shadow:0 2px 10px rgba(0,0,0,.5);
--scrim:rgba(0,0,0,.55);
--hover:rgba(255,255,255,.06);
}
/* ---------- MD3 × 浅色 ---------- */
:root[data-style="md3"][data-mode="light"]{
--bg:#f4f5f7;
--surface:#ffffff;
--surface-variant:#ececee;
--surface-dim:#e1e1e4;
--on-surface:#1b1b1d;
--on-surface-variant:#5c5c60;
--outline:#d2d2d6;
--outline-variant:#e4e4e8;
--primary:#2f6fe0; --on-primary:#ffffff;
--error:#ba1a1a; --on-error:#ffffff;
--success:#2e7d32; --warning:#ed6c02;
--shadow:0 2px 10px rgba(0,0,0,.12);
--scrim:rgba(0,0,0,.35);
--hover:rgba(0,0,0,.05);
}
/* ---------- MIUIx × 深色(暖黑 181310 + 橙 FF8C4A,色值对齐 meow_ui_theme.c) ---------- */
:root[data-style="miuix"][data-mode="dark"]{
--bg:#181310;
--surface:#262019; /* surfaceContainer */
--surface-variant:#332b25;
--surface-dim:#120e0b;
--on-surface:#ede5dc;
--on-surface-variant:#ab9884;
--outline:#4a3f35;
--outline-variant:#3a3229;
--primary:#ff8c4a; --on-primary:#241100;
--error:#ffb4ab; --on-error:#690005;
--success:#4caf50; --warning:#ffb300;
--shadow:0 2px 10px rgba(0,0,0,.55);
--scrim:rgba(0,0,0,.55);
--hover:rgba(255,255,255,.06);
}
/* ---------- MIUIx × 浅色(暖白 F7F3EE + 橙 F57200,色值对齐 meow_ui_theme.c) ---------- */
:root[data-style="miuix"][data-mode="light"]{
--bg:#f7f3ee;
--surface:#fffbf6; /* surfaceBright */
--surface-variant:#ede2d7;
--surface-dim:#dfd3c4;
--on-surface:#251a10;
--on-surface-variant:#7d6a5a;
--outline:#cbbfb2;
--outline-variant:#e9dfd0;
--primary:#f57200; --on-primary:#ffffff;
--error:#b3261e; --on-error:#ffffff;
--success:#2e7d32; --warning:#ed6c02;
--shadow:0 2px 10px rgba(60,40,20,.12);
--scrim:rgba(30,20,10,.35);
--hover:rgba(0,0,0,.05);
}
/* ---------- 布局 ---------- */
*{ box-sizing:border-box; }
html,body{ height:100%; margin:0; }
body{
background:var(--bg); color:var(--on-surface);
font-family:var(--font); font-size:14px; line-height:1.5;
-webkit-font-smoothing:antialiased;
}
.app{ display:flex; height:100vh; overflow:hidden; }
/* 侧栏 */
.sidebar{
width:var(--nav-w); flex:0 0 var(--nav-w);
background:var(--surface); border-right:1px solid var(--outline-variant);
display:flex; flex-direction:column; transition:margin-left var(--speed) ease;
}
.brand{ display:flex; align-items:center; gap:10px; padding:16px 18px; }
.brand-mark{
width:30px; height:30px; border-radius:var(--radius-m);
background:var(--primary); color:var(--on-primary);
display:flex; align-items:center; justify-content:center; font-weight:700;
}
.brand-name{ font-weight:600; font-size:15px; }
.nav{ flex:1; overflow-y:auto; padding:8px 10px; display:flex; flex-direction:column; gap:2px; }
.nav-item{
display:flex; align-items:center; gap:12px; padding:10px 12px;
border:none; background:transparent; color:var(--on-surface-variant);
border-radius:var(--radius-m); cursor:pointer; text-align:left;
font:inherit; font-size:14px; transition:background var(--speed), color var(--speed);
}
.nav-item:hover{ background:var(--hover); color:var(--on-surface); }
.nav-item.active{ background:var(--primary); color:var(--on-primary); }
.nav-ico{ width:22px; height:22px; flex:0 0 22px; }
.nav-ico svg{ width:100%; height:100%; display:block; }
.nav-label{ white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
/* 主区 */
.main{ flex:1; display:flex; flex-direction:column; min-width:0; }
.topbar{
height:var(--topbar-h); flex:0 0 var(--topbar-h);
display:flex; align-items:center; gap:12px; padding:0 16px;
background:var(--surface); border-bottom:1px solid var(--outline-variant);
}
.topbar-title{ font-size:17px; font-weight:600; margin:0; flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.icon-btn{
width:38px; height:38px; border:none; border-radius:var(--radius-m);
background:transparent; color:var(--on-surface-variant); cursor:pointer;
display:flex; align-items:center; justify-content:center;
}
.icon-btn:hover{ background:var(--hover); color:var(--on-surface); }
.icon-btn svg{ width:22px; height:22px; }
.menu-btn{ display:none; }
/* 顶栏分段控件(风格/明暗) */
.seg{ display:inline-flex; background:var(--surface-variant); border-radius:var(--radius-m); padding:3px; }
.seg-btn{
border:none; background:transparent; color:var(--on-surface-variant);
padding:6px 12px; border-radius:calc(var(--radius-m) - 2px);
cursor:pointer; font:inherit; font-size:13px; white-space:nowrap;
transition:background var(--speed), color var(--speed);
}
.seg-btn.active{ background:var(--surface); color:var(--on-surface); box-shadow:var(--shadow); }
/* 连接状态点 */
.conn{ display:inline-flex; align-items:center; gap:6px; padding:4px 10px; border-radius:var(--radius-m); background:var(--surface-variant); color:var(--on-surface-variant); font-size:12px; }
.conn-dot{ width:9px; height:9px; border-radius:50%; background:var(--on-surface-variant); }
.conn[data-state="ok"] .conn-dot{ background:var(--success); }
.conn[data-state="bad"] .conn-dot{ background:var(--error); }
.conn[data-state="ok"]{ color:var(--success); }
.conn[data-state="bad"]{ color:var(--error); }
/* 内容区 */
.content{ flex:1; overflow-y:auto; padding:20px; }
.page{ max-width:1100px; margin:0 auto; }
.placeholder{ color:var(--on-surface-variant); text-align:center; padding:60px 0; }
/* 卡片 */
.card{
background:var(--surface); border-radius:var(--radius-l);
border:var(--outline-w) solid var(--outline); /* MD3 无描边;MIUIx 1px */
box-shadow:var(--shadow); padding:18px 20px; margin-bottom:16px;
overflow-x:auto; /* 窄屏表格/内容横向滚动,避免整页外溢 */
}
.card-title{ font-size:15px; font-weight:600; margin:0 0 14px; }
/* 统计格子(性能页) */
.stat-grid{ display:grid; grid-template-columns:repeat(auto-fill,minmax(180px,1fr)); gap:14px; }
.stat{
background:var(--surface-variant); border-radius:var(--radius-m);
padding:14px 16px; border:var(--outline-w) solid var(--outline-variant);
}
.stat-label{ color:var(--on-surface-variant); font-size:12px; margin-bottom:6px; }
.stat-value{ font-size:22px; font-weight:700; letter-spacing:.3px; }
.stat-unit{ font-size:13px; font-weight:400; color:var(--on-surface-variant); margin-left:4px; }
.stat-sub{ font-size:12px; color:var(--on-surface-variant); margin-top:6px; }
/* 进度条 */
.bar{ height:8px; border-radius:99px; background:var(--surface-dim); overflow:hidden; margin-top:8px; }
.bar > span{ display:block; height:100%; border-radius:99px; background:var(--primary); transition:width .4s ease; }
.bar.warn > span{ background:var(--warning); }
.bar.danger > span{ background:var(--error); }
/* 按钮 */
.btn{
border:none; padding:9px 16px; border-radius:var(--radius-m);
background:var(--surface-variant); color:var(--on-surface);
cursor:pointer; font:inherit; font-size:14px;
display:inline-flex; align-items:center; gap:6px;
transition:filter var(--speed), background var(--speed), transform .05s;
}
.btn:hover{ filter:brightness(1.08); }
.btn:active{ transform:scale(.98); }
.btn:disabled{ opacity:.5; cursor:not-allowed; }
.btn-primary{ background:var(--primary); color:var(--on-primary); }
.btn-danger{ background:var(--error); color:var(--on-error); }
.btn-ghost{ background:transparent; }
.icon-txt{ font-size:16px; }
/* 表格(文件/日志) */
.table{ width:100%; border-collapse:collapse; }
.table th, .table td{ text-align:left; padding:10px 12px; border-bottom:1px solid var(--outline-variant); }
.table th{ color:var(--on-surface-variant); font-weight:500; font-size:12px; }
.table tr:hover td{ background:var(--hover); }
.row-actions{ display:flex; gap:6px; justify-content:flex-end; }
.muted{ color:var(--on-surface-variant); font-size:12px; }
.mono{ font-family:var(--mono); font-size:12px; word-break:break-all; }
/* 面包屑 */
.crumbs{ display:flex; flex-wrap:wrap; align-items:center; gap:4px; margin-bottom:12px; }
.crumbs a{ color:var(--primary); cursor:pointer; text-decoration:none; }
.crumbs a:hover{ text-decoration:underline; }
/* 表单行 */
.field{ margin-bottom:16px; }
.field-label{ display:block; margin-bottom:6px; color:var(--on-surface-variant); font-size:13px; }
.field-row{ display:flex; align-items:center; gap:12px; }
.field-row.wrap{ flex-wrap:wrap; }
input[type=text], input[type=number], select, textarea{
width:100%; padding:9px 12px; border-radius:var(--radius-m);
border:var(--outline-w) solid var(--outline); background:var(--surface-variant);
color:var(--on-surface); font:inherit; font-size:14px; border:0;
}
textarea{ resize:vertical; font-family:var(--mono); font-size:12px; }
input[type=range]{ width:100%; accent-color:var(--primary); }
.field-row input[type=range]{ flex:1; min-width:0; }
/* 开关 */
.switch{ position:relative; width:46px; height:26px; flex:0 0 46px; }
.switch input{ position:absolute; opacity:0; inset:0; margin:0; cursor:pointer; }
.switch .track{ position:absolute; inset:0; border-radius:99px; background:var(--surface-dim); transition:background var(--speed); border:var(--outline-w) solid var(--outline-variant); }
.switch .thumb{ position:absolute; top:3px; left:3px; width:20px; height:20px; border-radius:50%; background:var(--surface); box-shadow:var(--shadow); transition:left var(--speed); }
.switch input:checked + .track{ background:var(--primary); }
.switch input:checked + .track .thumb{ left:23px; background:var(--on-primary); }
/* 应用卡片 */
.app-grid{ display:grid; grid-template-columns:repeat(auto-fill,minmax(240px,1fr)); gap:14px; }
.app-card{
background:var(--surface-variant); border-radius:var(--radius-l);
border:var(--outline-w) solid var(--outline-variant); padding:16px;
display:flex; flex-direction:column; gap:8px;
}
.app-card .app-head{ display:flex; align-items:center; gap:12px; }
.app-card .app-ico{ width:40px; height:40px; border-radius:var(--radius-m); background:var(--primary); color:var(--on-primary); display:flex; align-items:center; justify-content:center; font-weight:700; font-size:16px; flex:0 0 40px; }
.app-card .app-name{ font-weight:600; }
.app-card .app-pkg{ font-size:12px; color:var(--on-surface-variant); font-family:var(--mono); }
.app-card .app-meta{ font-size:12px; color:var(--on-surface-variant); }
.app-card .app-actions{ display:flex; gap:6px; margin-top:auto; flex-wrap:wrap; }
/* 实时屏幕(屏幕页) */
.screen-frame{ display:flex; justify-content:center; max-width:480px; margin:0 auto; }
.screen-frame img{
width:100%; max-width:480px; height:auto; display:block;
border-radius:var(--radius-m); background:#000; cursor:crosshair;
aspect-ratio:480/720; object-fit:contain; border:var(--outline-w) solid var(--outline-variant);
}
.screen-frame canvas{
width:100%; max-width:480px; height:auto; display:none;
border-radius:var(--radius-m); background:#000; cursor:crosshair;
aspect-ratio:480/720; border:var(--outline-w) solid var(--outline-variant);
}
/* 远程按键区 */
.key-grid{ display:grid; grid-template-columns:repeat(auto-fill,minmax(120px,1fr)); gap:12px; }
.key-btn{
padding:14px; border:none; border-radius:var(--radius-m);
background:var(--surface-variant); color:var(--on-surface);
border:var(--outline-w) solid var(--outline-variant);
cursor:pointer; font:inherit; text-align:center; transition:background var(--speed);
}
.key-btn:hover{ background:var(--hover); }
.key-btn .k-ico{ font-size:20px; display:block; margin-bottom:4px; }
/* 危险区 */
.danger-box{ border:1px solid var(--error); border-radius:var(--radius-l); padding:16px 18px; }
.danger-box .card-title{ color:var(--error); }
.warn-note{ color:var(--warning); font-size:12px; margin-top:8px; }
/* 日志查看 */
.log-view{ background:var(--surface-dim); border-radius:var(--radius-m); padding:12px; max-height:60vh; overflow:auto; font-family:var(--mono); font-size:12px; white-space:pre-wrap; color:var(--on-surface); }
/* 网络管理页(meow_netmgrd) */
.net-kind{ color:var(--on-surface-variant); font-size:12px; }
.net-state{ display:inline-block; padding:2px 8px; border-radius:999px; font-size:12px; background:var(--surface-variant); color:var(--on-surface-variant); }
.net-state.ok{ background:rgba(76,175,80,.15); color:var(--success); }
.net-state.warn{ background:rgba(255,179,0,.15); color:var(--warning); }
.net-state.listen{ background:rgba(74,139,245,.15); color:var(--primary); }
.net-state.down{ background:rgba(207,102,121,.15); color:var(--error); }
.net-badge{ display:inline-block; padding:1px 7px; border-radius:999px; font-size:11px; font-family:var(--mono); background:var(--surface-variant); color:var(--on-surface-variant); margin-right:2px; }
.net-badge.app{ background:rgba(74,139,245,.18); color:var(--primary); }
.net-badge.svc{ background:rgba(76,175,80,.18); color:var(--success); }
.net-badge.proc{ background:rgba(255,179,0,.18); color:var(--warning); }
.net-entity{ font-family:var(--mono); font-size:12px; word-break:break-all; }
/* 通知列表 */
.notif{ display:flex; gap:12px; padding:12px; border-radius:var(--radius-m); background:var(--surface-variant); margin-bottom:10px; align-items:flex-start; border:var(--outline-w) solid var(--outline-variant); }
.notif .n-ico{ width:36px; height:36px; border-radius:var(--radius-m); background:var(--primary); color:var(--on-primary); display:flex; align-items:center; justify-content:center; flex:0 0 36px; font-weight:700; }
.notif .n-body{ flex:1; min-width:0; }
.notif .n-title{ font-weight:600; }
.notif .n-text{ color:var(--on-surface-variant); font-size:13px; margin-top:2px; }
.notif .n-meta{ display:flex; gap:8px; margin-top:6px; color:var(--on-surface-variant); font-size:11px; }
.urgency-hi{ color:var(--error); }
/* 横幅 / toast / modal */
.offline-banner{
position:sticky; top:0; z-index:30; padding:10px 16px;
background:var(--error); color:var(--on-error); font-size:13px;
}
.offline-banner code{ background:rgba(0,0,0,.2); padding:1px 5px; border-radius:4px; }
.toast{
position:fixed; left:50%; bottom:28px; transform:translateX(-50%);
z-index:60; background:var(--surface); color:var(--on-surface);
padding:11px 18px; border-radius:var(--radius-m); box-shadow:var(--shadow);
border:var(--outline-w) solid var(--outline-variant); font-size:13px;
max-width:80vw;
}
.modal-scrim{
position:fixed; inset:0; z-index:50; background:var(--scrim);
display:flex; align-items:center; justify-content:center; padding:20px;
}
.modal-scrim[hidden]{ display:none; } /* 覆盖上方 display:flex,否则 hidden 失效 */
.modal{ background:var(--surface); border-radius:var(--radius-l); width:min(420px,100%); padding:22px; box-shadow:var(--shadow); border:var(--outline-w) solid var(--outline-variant); }
.modal-title{ font-size:16px; margin:0 0 10px; }
.modal-body{ color:var(--on-surface-variant); margin:0 0 18px; word-break:break-all; }
.modal-actions{ display:flex; justify-content:flex-end; gap:10px; }
/* 登录页(面板鉴权) */
.login-scrim{ position:fixed; inset:0; z-index:70; background:var(--scrim); display:flex; align-items:center; justify-content:center; padding:20px; }
.login-scrim[hidden]{ display:none; }
.login-box{ background:var(--surface); border-radius:var(--radius-l); width:min(360px,100%); padding:28px 26px; box-shadow:var(--shadow); border:var(--outline-w) solid var(--outline-variant); text-align:center; }
.login-brand{ display:inline-flex; align-items:center; gap:8px; font-size:20px; font-weight:600; margin-bottom:6px; }
.login-brand-mark{ width:30px; height:30px; border-radius:8px; background:var(--primary); color:var(--on-primary); display:inline-flex; align-items:center; justify-content:center; font-weight:700; }
.login-sub{ color:var(--on-surface-variant); font-size:13px; margin:0 0 18px; }
.login-input{ width:100%; padding:11px 12px; border-radius:var(--radius-m); border:var(--outline-w) solid var(--outline-variant); background:var(--surface); color:var(--on-surface); font-size:14px; margin-bottom:12px; box-sizing:border-box; }
.login-input:focus{ outline:none; border-color:var(--primary); }
.login-err{ color:var(--error); font-size:13px; margin:0 0 12px; text-align:left; }
.login-btn{ display:block; width:100%; }
.modal .login-input{ margin-top:4px; }
/* 空态 / 加载 */
.empty{ text-align:center; color:var(--on-surface-variant); padding:48px 0; }
.skel{ color:var(--on-surface-variant); }
.spinner{ display:inline-block; width:18px; height:18px; border:2px solid var(--outline-variant); border-top-color:var(--primary); border-radius:50%; animation:spin .7s linear infinite; vertical-align:middle; }
@keyframes spin{ to{ transform:rotate(360deg); } }
/* 响应式:窄屏收起侧栏 */
@media (max-width:820px){
:root{ --nav-w:216px; }
.sidebar{ position:fixed; left:0; top:0; bottom:0; z-index:40; margin-left:calc(-1 * var(--nav-w)); }
.sidebar.open{ margin-left:0; box-shadow:var(--shadow); }
.menu-btn{ display:flex; }
.brand-name{ display:none; }
.conn-text{ display:none; }
}
@media (max-width:520px){
.content{ padding:14px; }
.topbar{ padding:0 10px; gap:8px; }
.seg-btn{ padding:6px 9px; }
}