Steam Deck 大屏 UI 规则
本文件是 /music 大屏路由的共享 UI 契约。QQ 与网易云页面可以有不同产品结构,但必须共用这里的视觉、焦点、按键和宿主安全规则。
目标
- 纯手柄可用:无鼠标、无触屏也能完成找歌、播放、控制、退出。
- 像 Steam 游戏模式页面:遵循官方库 / 商店的 10-foot UI 范式,不照搬手机 App。
- 不拖垮宿主:Steam UI 与插件前端在同一 Chromium 上下文,插件错误只能降级插件自身。
顶栏与布局
| 区域 | 规则 |
|---|---|
| SteamOS 全局 chrome | 右上角搜索、通知、Wi-Fi、电量、时间、头像属于系统;插件不绘制、不聚焦、不修改。 |
| 插件内容首行 | provider Logo、顶层 Tab、L1/R1 提示和当前播放状态徽章放在内容区首行。 |
| 顶层导航 | 使用横向 Tabs;L1/R1 循环切换 provider 页面。 |
| 个人资产页 | 遵循官方库页面:全宽二级 Tab 行 + 全宽列表 / 网格;不要常驻左侧栏。 |
| 内容区 | 主内容占满宽度;推荐页可用横向条带 / 网格,搜索页可用列表 + 热搜侧栏。 |
去常驻播放条
P3 目标 UI 不保留底部固定 MiniPlayer。
原因:SteamOS 软键盘弹出时会压缩整个 CEF WebView 高度。任何 position: fixed; bottom: 0 或 flex 底部常驻栏都会被顶到键盘上方,挤压搜索结果并破坏官方 UI 习惯。
替代机制:
- Tab 状态徽章:顶层 Tab 行最右侧显示旋转小封面、歌名跑马灯、播放态;纯展示,不进焦点树。
Start盲操:任何页面、任何焦点位置都可播放 / 暂停;队列空或后端不可用时不显示可执行提示。Y队列浮层:普通页面按Y打开右侧临时模态面板;浮层关闭后恢复原焦点。- 沉浸播放页:完整进度、音量、上一首 / 下一首、歌词和评论都收归
Now Playing或 provider 专属电台页。
视觉规则
| 项 | 规则 |
|---|---|
| 背景 | 近黑背景,允许封面模糊光晕;不要大面积品牌色铺底。 |
| 卡片 | 封面图本身就是卡片;直角或 2-4px 小圆角;不要手机式大圆角容器。 |
| 焦点态 | 2-3px 细白描边贴近元素边缘,允许轻微外发光和缩放。 |
| 列表选中 | 水平灰色渐变高亮条;必要时用 Steam 蓝左侧指示条。 |
| 品牌色 | Steam 蓝 #1a9fff 是主交互色;QQ 绿 / NCM 红只做 Logo、徽标、红心等点缀。 |
| 字体和层级 | 标题粗体,正文常规;小节标题可用全大写小字号灰色。 |
| Footer Legend | 底部图例条由系统渲染。插件只提供当前焦点可执行动作的文案,不自绘样式。 |
队列浮层与页面共用近黑背景、4px 小圆角、歌曲信息层级。轻遮罩保留页面上下文; 只在该模态实例内覆盖 Decky 默认对话框外观,不修改 Steam 全局样式。 当前曲的蓝色标记与白色手柄焦点分开表达;电台当前曲只读,禁止无操作的 A/X 图例。 标题与关闭入口固定,列表区域独立滚动;完整队列可导航,DOM 只保留可见窗口及 overscan。
歌词定位与跟随
- 进入“正在播放”或从热评返回歌词时,按当前播放时间选择歌词行,在首次可见绘制前直接定位。
- 歌词仍在加载、没有当前行或容器尚无有效高度时,不标记为已完成首次定位;歌词就绪后再定位。
- 已显示歌词的后续换句保持平滑跟随;不恢复离开 Tab 时的旧滚动位置。
- 手动浏览的 4 秒静默期内不抢滚动,之后随换句恢复跟随。只滚动歌词容器,不使用会拖动外层页面的
scrollIntoView。 - 不为此常驻隐藏页面或增加轮询。无初始滚动动画必须用连续画面和位置采样验收,静态截图只记录视觉状态。
焦点模型
主内容焦点树:Tabs 下的搜索、列表、网格、歌词、沉浸页控制
|
| Y / X / A 触发
v
模态浮层焦点树:队列、上下文菜单、确认框
硬规则:
- 所有可交互元素必须能被
Focusable选中。 - 禁止 hover-only、touch-only、不可见点击热区。
- 网格和多列布局使用
MAINTAIN_X,避免上下移动时列错位。 - 搜索框聚焦后按
A必须唤起 Steam 软键盘。 - 长列表必须分页、虚拟化或限制渲染量;不能一次性把大量节点塞进 DOM。
- 滑块聚焦后十字键左右用于微调;离开滑块必须有明确路径。
- 模态浮层关闭后恢复触发前焦点;原节点卸载时回退到当前页面第一个主要交互元素。
按键契约
| 按键 | 行为 |
|---|---|
| D-Pad / 左摇杆 | 焦点导航;在歌词区可手动滚动;在滑块上微调。 |
| A | 确认:播放、进入详情、提交搜索、刷新二维码。 |
| B | 返回或关闭;关闭模态浮层;取消歌词 / 评论手动滚动。 |
| X | 上下文菜单;在 NCM 歌词页切换歌词 / 热评;在电台页可覆盖为垃圾桶 / 下一首。 |
| Y | 普通页面打开播放队列;在电台页可覆盖为红心等局部快捷键。 |
| Start | 全局播放 / 暂停;不依赖当前焦点。 |
| View | 保留,不作为 P3 必需路径;只有验证到真实收益后再绑定。 |
| L1 / R1 | 顶层页面切换。 |
| L2 / R2 | 长列表翻页;当 L1/R1 已用于顶层页时,可用于二级 Tab。 |
| Steam / QAM | 系统保留键,插件不绑定。 |
Footer Legend 规则
- 通过
Focusable的actionDescriptionMap、onOKActionDescription等属性声明文案。 - 图例随焦点变化;根级
Focusable可提供全局默认,子元素同按键文案会覆盖父级。 - 左侧
STEAM菜单、白圈图标、排列和位置由系统控制,插件不得自绘仿冒。 - 不可用动作必须隐藏或置灰。
X/Y在特殊模式下被覆盖时,提示文案必须同步改成真实语义。
宿主安全
这些规则继承 docs/DESIGN.md 的 UI 崩溃隔离要求:
- 所有 RPC、事件处理、计时器回调都要捕获错误,不把异常抛到全局。
- 后端死亡、断网、畸形数据只能渲染错误态,不能冻结 Steam UI。
- 图片用 CDN 缩略图;失败显示占位图。
- 不 monkey-patch Steam 全局对象,不写全局命名空间。
- 组件卸载时清理监听器、计时器、路由和模态浮层。
验收
每个 provider 页面都必须能用实体按键完成:
- 进入
/music。 - 切换顶层页面和二级 Tab。
- 搜索或打开推荐内容。
- 选歌播放。
- 暂停 / 继续。
- 打开并关闭队列浮层。
- 打开并关闭上下文菜单。
- 在歌词 / 评论区域手动浏览并恢复自动跟随。
- 在后端异常、断网或畸形数据时显示可恢复错误态。