dsh-console 前端:rabbita + precss
./check.sh # 类型检查 + 构建(单退出码)
./check.sh --fast # 只做类型检查,几秒
./build.sh # 只构建:样式 → js → 拷 distDSH_CONSOLE_DIST="$PWD/dsh-console/frontend/dist" \
dsh-console/backend/_build/native/debug/build/dsh-console-backend.exe
# http://127.0.0.1:5390/# 独立验证器:横向溢出 / 控制台报错 / 真数据到位(不依赖任何自证)
node ../design/verify-frontend.mjs http://127.0.0.1:5391
# 逐条对账:清单第五节的 12 条几何不变量 + 菜单 / 对话框 / 日志页 / 派生等行为
node ../design/measure-parity.mjs http://127.0.0.1:5391| 依赖 | 谁在用 |
|---|---|
| moonbit-community/rabbita | 全部视图与 update 循环 |
| conglinyizhi/precss | 只在 cmd/build-styles(构建期把 SCSS 编成 CSS) |
| moonbitlang/async | 只在 cmd/build-styles:async fn main 语法与 @fs 读写都来自它 |
import {
"conglinyizhi/dsh-console-frontend" @frontend,
}.
├── moon.mod / moon.pkg 模块定义(依赖只有 rabbita / precss / async)
├── icons.mbt 26 个图标的内联 SVG:路径常量 + icon() 助手
├── model.mbt 五个只读接口的数据模型与解析(外壳 → 字段,不做业务推导)
├── settings.mbt 设置键表(9 个键 5 个分组)、取值模型、PUT 请求体
├── api.mbt 写接口的传输层:fetch + 状态码/响应体,POST action / GET+PUT settings
├── state.mbt Model / Msg / 行模型 / 演示数据 / 工作流文案
├── app.mbt update 循环:拉取、写动作、设置页状态转移、对话框、日志
├── clock.mbt 浏览器本地时刻(页脚灯的「检查于 <时刻>」/ 归档时间戳)
├── dom.mbt 浏览器副作用 FFI:焦点、滚动、短时刻、控件值回写、减少动效判定
├── route.mbt URL 路由:pushState / popstate、地址 ↔ 页面状态、回落说明
├── view_shell.mbt 外壳:左导航 / 内容区(顶栏已取消)+ 地址回落提示条
├── view_overview.mbt 实例总览:卡片(三样)、详情抽屉、归档区、三个对话框
├── view_settings.mbt 设置页:左索引 + 右条目(数字 / 开关 / 下拉)
├── view_logs.mbt 日志页:原生 select 来源、元信息行、日志区
├── view_about.mbt 关于页:简介 + 版本表 + 「这台的 dsh 版本」(可切版本)
├── view_parts.mbt 共用小件:徽标、分组头、空态、字段行(抽屉里那六格)、对话框外壳
├── cmd/app/ js 可执行:把 app() 挂到 #dsh-console-root
├── cmd/build-styles/ native 可执行:precss 编 SCSS
├── styles/app.scss 全部样式:reset + 设计 token + 版式
├── web/index.html 页面外壳
├── vendor/licenses/mdi-NOTICE.md 图标路径数据的第三方声明(不再分发字体文件)
└── dist/ 构建产物,后端直接服务它| 地址 | 页面 |
|---|---|
| / | 实例总览 |
| /logs?source=… 或 /logs/<来源> | 日志(来源是页面状态的一部分:不带它,刷新就回默认来源)。两种写法都认:切来源写 query,失败提示条的「查看日志」写路径形式 /logs/actions |
| /settings | 设置(第一个分组) |
| /settings/<分组 id> | 设置(某个分组,如 /settings/safety) |
| /about | 关于 |
| 档 | 数据源徽标 | 那一格 |
|---|---|---|
| 请求还在路上 | 读取中(.dsh-badge.pending)、分组计数写 (…) | 卡片栅格里是 6 块与真条目同形的占位块(第十一轮起最多占一行)+ 一行「正在读取…」(.dsh-loading → .dsh-skeleton-row / .dsh-skeleton / .dsh-loading-note);日志页与回收站页仍是整幅一块(.dsh-loading 面板) |
| 读到了、确实是空 | 真实数据 | 空态(虚线框 +「当前没有检测到…」) |
| 读失败 | 演示数据 · 真实读取失败 + 具体原因 | 空态 + 那个徽标(既有行为,不并进加载态) |
| 哪一种行 | 这一格的值从哪来 | 认不出时 |
|---|---|---|
| 运行中 | 那一只实例读出来的 dshHome(实例 environ / fd 前缀,口径见后端 a 那一条) | 未知,悬停是后端给的来源原文 |
| 已停止 | profiles 读数就是在 <它>/profiles 下把这一只列出来的那个 home(读数里的 dshHome) | 未知(不回落到 runtime 读数,更不拿控制台自己的 home 顶上) |
| 归档孤行 | 归档记录自带的 home(磁盘上没有目录,没有别处可读) | —(记录里也没写,就不编一个) |
| 情况 | 那一格显示 | 悬停里有什么 |
|---|---|---|
| 有值(停止卡 / 运行卡的 profile 解析得出) | 10-15 18:50 | 完整 ISO、口径说明、dirMtimeSource |
| 读了但读不到(字段是 null) | 读不到 | 后端给的 dirMtimeSource 原因,原样带上 |
| 运行卡解析不出 profile | — | 是「没解析出 profile 名」还是「profiles 读数里没有这一只」,分开说 |
| 响应里根本没有这个字段 | 读不到 | 「profiles 读数里没有 dirMtimeMs 字段(后端这一版还没给)」 |
node evidence-mtime-field/probe-mtime.mjs http://127.0.0.1:5390/ real evidence-mtime-field/real
node evidence-mtime-field/probe-mtime.mjs http://127.0.0.1:5390/ \
synth:evidence-mtime-field/fixture-synth-profiles.json evidence-mtime-field/synth
node evidence-mtime-field/check-order.mjs evidence-mtime-field/synth.json| 档位 | 颜色 | 判据 |
|---|---|---|
| running | 绿 | 实例在跑,且这只 profile 没有已知隐患(签名探测通过、缺包 0、cordis.patch.yml 在) |
| warn | 黄 | 有警告:没在跑但有隐患(缺包 / 没有 cordis.patch.yml),或在跑而 profile 有隐患;也接住「端口在听、签名不通」 |
| fault | 红 | 上一次启动失败,且失败之后没有成功启动过。判据是 profiles 读数里的 lastStart(后端从 audit.jsonl 里 start-instance 与 restart-instance 两种启动记录取最近一条,见 design-fixes §27.1) |
| stopped | 不画灯 | 没在跑、没有警告、最近一次启动也没失败。状态词「已停止」是第一行唯一的信号 |
| starting | 黄(在动) | 正在起来。同一支琥珀,但文案是「正在启动」,不会被读成故障;它就是原来副标题行上那个等待态的点 |
| unknown | 不上色 | 后端的 state 字段没给:空心灰环 |
| 视口断点 | 列数 |
|---|---|
| < 726 | 1 |
| ≥ 726 | 2 |
| ≥ 978 | 3 |
| ≥ 1230 | 4 |
| ≥ 1482 | 5 |
| ≥ 1734 | 6(上限) |
cd dsh-console/frontend/evidence-card-drawer
node probe-states.mjs http://127.0.0.1:5390/ states # 三色 + 黄的两个成因(计算样式,不是看截图)
node probe-drawer.mjs http://127.0.0.1:5390/ drawer # 卡片只剩三样 / 抽屉开合与焦点 / 按钮可达 / 窄屏占满
node probe-widths.mjs http://127.0.0.1:5390/ widths # 20 档宽度的列数与溢出| 视口 | 列数 | 占位块(加载时) | 真卡片(放行后) |
|---|---|---|---|
| 1920 | 6 | 271×70,列起点 212/496/778/1062/1344/1628 | 271×70;克隆到 6 张后列起点逐列相同 |
| 1280 | 4 | 252.5×70,列起点 212/476/742/1006 | 252.5×70,起点 212 |
| 768 | 2 | 261×70,列起点 212/486 | 261×70,起点 212/486 |
| 360 | 1 | 126×60,起点 212 | 126×60,起点 212 |
cd dsh-console/frontend/evidence-loading-skeleton
node probe-skeleton.mjs http://127.0.0.1:5390/ skeleton
# 用 Fetch 拦截把 /api/local/* 压后 2 秒:加载中那一刻量一次、放行后再量一次,
# 四种视口(1280 / 1920 / 768 / 360)各一遍,外加点一下与 Tab 一圈的交互性实测。| 视口 | 列数 | 露在外面的占位块 | 骨架区高 | 骨架块高×宽 | 真卡片高度(放行后) |
|---|---|---|---|---|---|
| 1920 | 6 | 6(第 2 行不存在) | 70 | 70×271 | 70 |
| 1280 | 4 | 4(另外 2 块被裁) | 70 | 70×252.5 | 70 |
| 768 | 2 | 2(另外 4 块被裁) | 70 | 70×261 | 70 |
| 360 | 1 | 1(另外 5 块被裁) | 60 | 60×126 | 66(带第二行)/ 60(不带) |
cd dsh-console/frontend/evidence-motion-skeleton
node probe-drawer-motion.mjs http://127.0.0.1:5390/ drawer-motion
# 开 / 关每 40ms 采一格(transform / x / opacity / 动画名 / data-state / 焦点);
# 再用 CDP 模拟 prefers-reduced-motion: reduce 走一遍;最后两张在途帧(播放速率 0.4,只影响这两张图)
node probe-drawer-behavior.mjs http://127.0.0.1:5390/ drawer-behavior
# 点卡开 / 点外部关 / Esc / 关闭键 / 关的中途又开 / 窄屏 360 占满整幅:焦点逐条看
node probe-skeleton-rows.mjs http://127.0.0.1:5390/ skeleton-rows
# Fetch 拦截把 /api/local/* 压后 2 秒:1920 / 1280 / 768 / 360 各量一遍(加载态 vs 放行后)
node probe-order-toast.mjs http://127.0.0.1:5533/ order-toast
# 隔离实例(DSH_HOME 与 XDG_STATE_HOME 都在 /tmp 的 fixture 下)里真拖一次,读回执正文与悬停cd dsh-console/design && DRAG_STATE_DIR=/tmp/dsh-drag-iso \
node probe-drag-preview.mjs http://127.0.0.1:5391/ /tmp/drag.json
# 跑在**隔离后端** 5391 上(自己的 XDG_STATE_HOME):拖动会真写 order.json,
# 5390 那台用的是使用者的状态目录,不拿它做写入实测。| 视口 | 条目 w×h | 第一行高 | 第一行宽 | 名字左缘 | 名字宽 | 标签宽 | 那一串宽 | 右缘 − 内容盒右缘 | 第一行溢出 | 整页横向滚动 |
|---|---|---|---|---|---|---|---|---|---|---|
| 1280 改前 | 252.5×70 | 22 | 168.5 | 50 | 134.5 | — | 24 | 0 | false | false |
| 1280 改后 | 252.5×70 | 22 | 168.5 | 50 | 95.5 | 39 | 63 | 0 | false | false |
| 620 改后 | 386×66 | 22 | 316 | 42 | 245 | 39 | 63 | 0 | false | false |
| 474 改后(卡宽 240) | 240×66 | 22 | 170 | 42 | 99 | 39 | 63 | 0 | false | false |
| 421 改后 | 187×66 | 22 | 117 | 42 | 46 | 39 | 63 | 0 | false | false |
| 420 改后 | 186×66 | 22 | 116 | 42 | 84 | 让位 | 24 | 0 | false | false |
| 360 改前 | 126×66 | 22 | 56 | 42 | 24 | — | 24 | 0 | false | false |
| 360 改后 | 126×66 | 22 | 56 | 42 | 24 | 让位 | 24 | 0 | false | false |
cd dsh-console/frontend/evidence-card-port-label
PROBE_VIEWPORT_W=1280 PROBE_VIEWPORT_H=900 node probe-port-label.mjs http://127.0.0.1:5390/ after-1280.json after-1280
PROBE_VIEWPORT_W=360 PROBE_VIEWPORT_H=720 node probe-port-label.mjs http://127.0.0.1:5390/ after-360.json after-360
node compare-port-geometry.mjs before-1280.json after-1280.json before-360.json after-360.json # 改前/改后逐像素对照
node dump-dom-proof.mjs > dom-text.txt # DOM 文本 + 没有端口就没有那个节点
cd ../../design && ACC_NO_START=1 node run-acceptance.mjs http://127.0.0.1:5390/ # 判定 31 项:通过 31,失败 0