feat(client): RMIC::2026 界面重做、可计算的版本机制与固件版本联动
- feat(client): 界面换成墨黑蓝底 + 单支红的矩形语言,切角全部去掉,仅装甲条保留平行四边形 - feat(client): 自托管 HarmonyOS Sans SC 与 JetBrains Mono,中文与拉丁数字逐字回退 - feat(client): 新增启动序列、首启门、用户端 HUD、裁判端控制台、设置抽屉与固件维护页 - feat(client): 裁判端中缝与下方控制台可拖,鼠标落在哪一路下方就看哪一路 - feat(client): 设置抽屉常驻实时预览,与画面共用同一份准星绘制代码 - feat(client): 动效改用 Material 3 的缓动曲线与时长,逐条替换掉自造的贝塞尔 - feat(version): 版本号与 CHANGELOG 由 scripts/version.mjs 从 git 历史算出,不再手写 - feat(firmware): Hello 报文携带语义化版本、构建号与自报协议号,负载向后兼容 - feat(client): 固件版本显示在绑定列表与裁判端每一格;协议不匹配时给出具体原因 - feat(firmware): 固件包版本与客户端同源,都从 version.json 读 - fix(client): getCurrentWindow() 在没有 Tauri 注入时同步抛异常,浏览器预览整棵树被卸载 - fix(client): 启动序列的淡出 effect 清掉了自己刚设的 setTimeout,界面永远停在启动页 - fix(client): 设置抽屉左侧导航的锚点没挂到 section 上,且用错了坐标系 - fix(protocol): Hello 改用宽松帧头解析,让协议不匹配的板子能被解释而不是静默消失 - fix(build): Windows 上 bindgen 加载不了 libclang 的同目录依赖,构建脚本补上搜索路径 - fix(build): esp-idf-sys 在长路径下拒绝构建,固件构建改用短的目标目录 - build(client): 队标图标,含光学尺寸分级与手工拼的多尺寸 ICO - build(ci): 守门 job 增加版本一致性检查,产物名改为 rmic-2026-* - docs: 补充界面语言、版本机制与协议通告布局的说明
This commit is contained in:
@@ -0,0 +1,573 @@
|
||||
/* 控制台:装甲板表、录制与回放、队伍名。
|
||||
*
|
||||
* 这些是"低头看"的东西,所以它们可以密、可以有刻度和线——画面那一层不可以。
|
||||
*/
|
||||
|
||||
/* ───────────────────────────────────────────────────── 装甲板列表 */
|
||||
|
||||
.armor {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* 七列的宽度是固定的,除了最后一列(槽位)用剩余空间。定宽的意义在于:血量从 100 变成
|
||||
7 时,后面的列不许挪位——那样每掉一次血整张表都会抖一下。 */
|
||||
.armor__head,
|
||||
.armor__row {
|
||||
display: grid;
|
||||
/* 地址列按内容吃掉自己的宽度,剩下的全给"槽位"——否则长窗口下中间会空出一大片,
|
||||
而真正需要横向余量的槽位说明被挤成一截。 */
|
||||
grid-template-columns:
|
||||
46px 118px minmax(110px, max-content) 118px
|
||||
132px 104px minmax(96px, 1fr);
|
||||
gap: var(--s2);
|
||||
align-items: center;
|
||||
padding: 0 var(--s2);
|
||||
}
|
||||
|
||||
.armor__head {
|
||||
height: 22px;
|
||||
border-bottom: 1px solid var(--line-2);
|
||||
}
|
||||
|
||||
.armor__col {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.armor__head .armor__col {
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--fs-9);
|
||||
letter-spacing: var(--track-wide);
|
||||
color: var(--ink-4);
|
||||
}
|
||||
|
||||
.armor__rows {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.armor__row {
|
||||
height: 26px;
|
||||
border-bottom: 1px solid var(--line);
|
||||
font-size: var(--fs-11);
|
||||
transition: background-color var(--d-2) var(--e-out);
|
||||
}
|
||||
|
||||
.armor__row:hover {
|
||||
background: var(--panel-2);
|
||||
}
|
||||
|
||||
.armor__row.is-offline {
|
||||
opacity: 0.45;
|
||||
}
|
||||
|
||||
.armor__row.is-unassigned .armor__slot {
|
||||
color: var(--ink-4);
|
||||
}
|
||||
|
||||
/* 四条上报的位图。它一眼回答"这块板子是不是只在报一条"。 */
|
||||
.armor__plates {
|
||||
display: inline-flex;
|
||||
gap: 2px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.armor__plate {
|
||||
width: 6px;
|
||||
height: 10px;
|
||||
background: var(--line-2);
|
||||
transition: background-color var(--d-2) var(--e-out);
|
||||
}
|
||||
|
||||
.armor__plate.is-on {
|
||||
background: var(--ink-2);
|
||||
}
|
||||
|
||||
.armor__id {
|
||||
color: var(--ink);
|
||||
letter-spacing: var(--track-tight);
|
||||
}
|
||||
|
||||
.armor__addr {
|
||||
color: var(--ink-3);
|
||||
font-size: var(--fs-10);
|
||||
}
|
||||
|
||||
.armor__team-bit {
|
||||
margin-left: 6px;
|
||||
padding: 0 4px;
|
||||
border: 1px solid currentColor;
|
||||
font-size: var(--fs-9);
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.armor__col--hp {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--s2);
|
||||
}
|
||||
|
||||
.armor__hp {
|
||||
flex: none;
|
||||
min-width: 26px;
|
||||
color: var(--ink);
|
||||
font-size: var(--fs-12);
|
||||
}
|
||||
|
||||
.armor__hp.is-dead {
|
||||
color: var(--error);
|
||||
}
|
||||
|
||||
.armor__hp-track {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
height: 3px;
|
||||
background: var(--line-2);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.armor__hp-fill {
|
||||
display: block;
|
||||
height: 100%;
|
||||
transition: width var(--d-3) var(--e-out);
|
||||
}
|
||||
|
||||
.armor__silence {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--fs-10);
|
||||
}
|
||||
|
||||
.armor__silence.is-good {
|
||||
color: var(--ink-2);
|
||||
}
|
||||
|
||||
.armor__silence.is-warn {
|
||||
color: var(--warn);
|
||||
}
|
||||
|
||||
.armor__silence.is-bad {
|
||||
color: var(--error);
|
||||
}
|
||||
|
||||
.armor__slot {
|
||||
color: var(--ink-2);
|
||||
font-size: var(--fs-10);
|
||||
}
|
||||
|
||||
.armor__empty {
|
||||
padding: var(--s4) var(--s2);
|
||||
max-width: 620px;
|
||||
color: var(--ink-3);
|
||||
font-size: var(--fs-11);
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.armor__empty kbd {
|
||||
padding: 0 4px;
|
||||
border: 1px solid var(--line-3);
|
||||
color: var(--ink-2);
|
||||
font-size: var(--fs-10);
|
||||
}
|
||||
|
||||
/* ───────────────────────────────────────────────────── 帧率读数行 */
|
||||
|
||||
.statsline {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
font-size: var(--fs-10);
|
||||
color: var(--ink-2);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.statsline.is-offline {
|
||||
color: var(--warn);
|
||||
}
|
||||
|
||||
.statsline--empty {
|
||||
color: var(--ink-4);
|
||||
}
|
||||
|
||||
.statsline__status {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.statsline__sep {
|
||||
width: 1px;
|
||||
height: 9px;
|
||||
background: var(--line-2);
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.statsline__nums {
|
||||
color: var(--ink-3);
|
||||
font-size: var(--fs-10);
|
||||
}
|
||||
|
||||
/* ───────────────────────────────────────────────────────── 队名 */
|
||||
|
||||
/* 静止时它是一条读数:一个队色方块 + 一个看着像文本的值。
|
||||
点进去才展开边框、键位提示与字数。两格画面下面不该永远趴着两个表单控件。 */
|
||||
.tname {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
flex: 0 1 auto;
|
||||
padding: 2px 4px 2px 0;
|
||||
border: 1px solid transparent;
|
||||
transition: border-color var(--d-2) var(--e-out);
|
||||
}
|
||||
|
||||
.tname.is-editing {
|
||||
border-color: var(--line-3);
|
||||
background: var(--deep);
|
||||
}
|
||||
|
||||
.tname.is-disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.tname__label {
|
||||
flex: none;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--fs-10);
|
||||
font-weight: 700;
|
||||
border: 1px solid currentColor;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.tname__input {
|
||||
flex: 1;
|
||||
min-width: 60px;
|
||||
max-width: 150px;
|
||||
padding: 1px 2px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-bottom: 1px dashed var(--line-2);
|
||||
color: var(--ink);
|
||||
font-size: var(--fs-12);
|
||||
outline: none;
|
||||
transition: border-color var(--d-2) var(--e-out);
|
||||
}
|
||||
|
||||
.tname__input:hover:not(:disabled) {
|
||||
border-bottom-color: var(--line-3);
|
||||
}
|
||||
|
||||
.tname.is-editing .tname__input {
|
||||
border-bottom: 1px solid var(--primary);
|
||||
}
|
||||
|
||||
.tname__input::placeholder {
|
||||
color: var(--ink-4);
|
||||
}
|
||||
|
||||
/* 提示只在编辑时出现:平时它会把这一行撑宽,而这一行要装帧率。 */
|
||||
.tname__hint {
|
||||
display: none;
|
||||
align-items: center;
|
||||
gap: var(--s2);
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.tname.is-editing .tname__hint {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.tname__keys {
|
||||
font-size: var(--fs-9);
|
||||
color: var(--ink-4);
|
||||
letter-spacing: var(--track-tight);
|
||||
}
|
||||
|
||||
.tname__keys kbd {
|
||||
padding: 0 3px;
|
||||
margin-right: 1px;
|
||||
border: 1px solid var(--line-2);
|
||||
color: var(--ink-3);
|
||||
font-size: var(--fs-9);
|
||||
}
|
||||
|
||||
.tname__count {
|
||||
font-size: var(--fs-9);
|
||||
color: var(--ink-4);
|
||||
}
|
||||
|
||||
/* ─────────────────────────────────────────────────── 录制与回放 */
|
||||
|
||||
.media {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--s2);
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.media__actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--s2);
|
||||
}
|
||||
|
||||
.media__rec-glyph {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
background: currentColor;
|
||||
}
|
||||
|
||||
.media__divider {
|
||||
width: 1px;
|
||||
height: 16px;
|
||||
background: var(--line-2);
|
||||
margin: 0 var(--s1);
|
||||
}
|
||||
|
||||
.media__cam {
|
||||
font-size: var(--fs-9);
|
||||
color: var(--ink-3);
|
||||
}
|
||||
|
||||
.media__spacer {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* —— 录制中的横条 —— */
|
||||
|
||||
.media__strips {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
.strip {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--s2);
|
||||
padding: 4px var(--s2);
|
||||
background: rgba(255, 84, 73, 0.08);
|
||||
border: 1px solid var(--error-line);
|
||||
border-left-width: 2px;
|
||||
font-size: var(--fs-11);
|
||||
}
|
||||
|
||||
.strip__pulse {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
background: var(--error);
|
||||
animation: rec-blink 1.1s steps(2, start) infinite;
|
||||
}
|
||||
|
||||
.strip__label {
|
||||
color: var(--error);
|
||||
}
|
||||
|
||||
.strip__cam {
|
||||
color: var(--ink);
|
||||
font-size: var(--fs-10);
|
||||
}
|
||||
|
||||
.strip__stats {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
color: var(--ink-2);
|
||||
font-size: var(--fs-10);
|
||||
}
|
||||
|
||||
.strip__dot {
|
||||
width: 2px;
|
||||
height: 2px;
|
||||
background: var(--ink-4);
|
||||
}
|
||||
|
||||
.strip__warn {
|
||||
color: var(--warn);
|
||||
font-size: var(--fs-10);
|
||||
}
|
||||
|
||||
.strip__error {
|
||||
color: var(--error);
|
||||
font-size: var(--fs-10);
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.strip > .btn {
|
||||
margin-left: auto;
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
/* —— 文件列表 —— */
|
||||
|
||||
.media__list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.media__head,
|
||||
.media__row {
|
||||
display: grid;
|
||||
grid-template-columns: 52px minmax(0, 1fr) 88px 116px;
|
||||
gap: var(--s2);
|
||||
align-items: center;
|
||||
padding: 0 var(--s2);
|
||||
}
|
||||
|
||||
.media__head {
|
||||
height: 22px;
|
||||
border-bottom: 1px solid var(--line-2);
|
||||
}
|
||||
|
||||
.media__head .media__col {
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--fs-9);
|
||||
letter-spacing: var(--track-wide);
|
||||
color: var(--ink-4);
|
||||
}
|
||||
|
||||
.media__row {
|
||||
height: 26px;
|
||||
border-bottom: 1px solid var(--line);
|
||||
font-size: var(--fs-11);
|
||||
transition: background-color var(--d-2) var(--e-out);
|
||||
}
|
||||
|
||||
.media__row:hover {
|
||||
background: var(--panel-2);
|
||||
}
|
||||
|
||||
.media__col {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.media__col--size {
|
||||
color: var(--ink-3);
|
||||
font-size: var(--fs-10);
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.media__col--acts {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.media__col--acts .btn {
|
||||
height: 20px;
|
||||
padding: 0 6px;
|
||||
font-size: var(--fs-10);
|
||||
}
|
||||
|
||||
.media__kind {
|
||||
display: inline-block;
|
||||
padding: 1px 5px;
|
||||
border: 1px solid var(--line-3);
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--fs-9);
|
||||
letter-spacing: var(--track);
|
||||
color: var(--ink-3);
|
||||
}
|
||||
|
||||
.media__kind--video {
|
||||
color: var(--ink);
|
||||
border-color: var(--line-3);
|
||||
}
|
||||
|
||||
.media__kind--image {
|
||||
color: var(--ink-2);
|
||||
}
|
||||
|
||||
.media__name {
|
||||
color: var(--ink);
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--fs-10);
|
||||
}
|
||||
|
||||
.media__empty {
|
||||
padding: var(--s4) var(--s2);
|
||||
color: var(--ink-3);
|
||||
font-size: var(--fs-11);
|
||||
}
|
||||
|
||||
/* —— 播放器 —— */
|
||||
|
||||
.playback {
|
||||
margin-top: var(--s1);
|
||||
border: 1px solid var(--line-2);
|
||||
background: #000;
|
||||
}
|
||||
|
||||
.playback__head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--s2);
|
||||
padding: 4px var(--s2);
|
||||
background: var(--panel-2);
|
||||
border-bottom: 1px solid var(--line-2);
|
||||
}
|
||||
|
||||
.playback__label {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.playback__name {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: var(--fs-10);
|
||||
color: var(--ink-2);
|
||||
}
|
||||
|
||||
.playback__spacer {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.playback__head .btn {
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.playback__video {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-height: 300px;
|
||||
background: #000;
|
||||
}
|
||||
|
||||
.playback__fallback {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--s3);
|
||||
align-items: flex-start;
|
||||
padding: var(--s3);
|
||||
background: var(--panel);
|
||||
}
|
||||
|
||||
.playback__warn {
|
||||
color: var(--warn);
|
||||
font-size: var(--fs-11);
|
||||
line-height: 1.7;
|
||||
}
|
||||
Reference in New Issue
Block a user