# v0.1 规格(实现任务书) ## 目标 一个 dsh 客户端插件:在 Web GUI 的状态行里提供电台入口,点一下播放内置电台,支持切换、音量、暂停与记忆。面向公开分享(GitHub),中文界面 + 英文文案。 ## 范围 做:7 个内置电台、状态行单槽位、播放/暂停/下一台/切台/音量、预缓冲、失败重试、MediaSession 开关、明暗主题、状态记忆、当前曲目(见 ADR-0003)。 不做:自定义电台、定时关闭、录音、歌词、多语言之外的本地化。 ## 内置信道(顺序即列表顺序) | 名称 | 来源 | 主地址 | 备用地址 | | --- | --- | --- | --- | | ChillSynth | Nightride FM | `https://stream.nightride.fm/chillsynth.mp3` | — | | Nightride | Nightride FM | `https://stream.nightride.fm/nightride.mp3` | — | | Darksynth | Nightride FM | `https://stream.nightride.fm/darksynth.mp3` | — | | Spacesynth | Nightride FM | `https://stream.nightride.fm/spacesynth.mp3` | — | | Main Mix | Radio Paradise | `https://stream.radioparadise.com/aac-320` | `https://stream.radioparadise.com/aac-128` | | Mellow Mix | Radio Paradise | `https://stream.radioparadise.com/mellow-128` | `https://stream.radioparadise.com/mellow-320` | | EVE Radio | GamingNow | `https://media01.gamingnow.net:8010/erweb.mp3` | `http://media01.gamingnow.net:8000/er.mp3`(仅 http 页面可用) | 所有流 HTTPS(EVE 备用除外)、`Access-Control-Allow-Origin: *`、渐进式(无 HLS)。数据以 `src/client/stations.ts` 为准。 ## 音频内核 - 单例 `HTMLAudioElement`,全局复用;切换电台时 `pause()` → 改 `src` → `load()` → `play()`。 - `crossOrigin = 'anonymous'`(频谱要用 Web Audio)。 - 预缓冲:**只预热一条**——上次播放的电台(无记录则 ChillSynth)。`preload='auto'`,不 `play()`;用户点播放时复用该元素。 - 失败处理:`error` 或 12 秒未 `playing` → 若有备用地址则切备用(静默);仍失败 → 进 `error` 态,显示「连接失败,点击重试」。 - 播放中再次点击同一台 → 暂停(暂停后保留 `src`,恢复时 `play()`,不做 seek)。 - 下一台:按 7 条列表顺序 +1 取模(末尾回到第一台),停旧流 → 改 `src` → `load()` → `play()`,不等用户再点播放。 - 切台必须先停旧流,禁止叠音。 - 音量 0–1,持久化;`muted` 单独持久化。 ## 状态机 `idle → loading → playing ⇄ paused`,任意态 `→ error → loading`。 - `loading`:点击后立即进入,显示三点脉冲(1.2s 循环)与低幅呼吸频谱。 - `playing`:`playing` 事件后进入;频谱 180ms 淡入。 - `paused`:用户暂停;收起态仍是完整结构(播放图标 + 下一台 + 台名 + 静止矮柱频谱)。 - `error`:红点 + 「连接失败,点击重试」;点击播放键或文案重试。 - `idle`:从未播放过或停止;显示播放图标、当前台名与静止矮柱频谱。 ## 界面 视觉基线:`design/directions/b.html`(唯一视觉真相源)。要点: - 唯一位置:`conversation.composer.dock` 里的 24px 胶囊(`id: 'radio'`,`order: -1`),与 `activity`/`usage` 两个状态胶囊同高;字号 11px/500。 - 收起态从左到右固定为四段:播放/暂停按钮 → 下一台按钮(14px 双三角+竖线,1.5px 描边)→ 台名(始终显示,不再显示「电台」)→ 频谱(靠右)。不设「播放中」强调点;加载时台名旁三点脉冲。 - 频谱:收起态 28 根强调色小方柱,1.5px 宽、2px 间距、高 3–14px、总宽约 95px;面板内 56 根,横跨约 328px 内容宽、高 4–20px,放在电台列表下方、音量条上方(上下留 9px)。只用柱体颜色,不加发光/渐变;透明度与高度都随幅度(0.35–1)。取真实 `AnalyserNode` 数据(`sampleRealSpectrum()`),`decor` 用装饰动画,`off` 不渲染。 - 面板 360px 宽,向上弹出,距状态行 6px;行高 28px;来源列右对齐 10px;当前行左侧 2px 灰线 + 台名提亮;无色块。右上角 12px、1.4px 描边的 8 齿齿轮。 - 面板底部紧凑一行:左侧播放/暂停 + 下一台;右侧喇叭图标(点击静音,muted 带斜线)+ 112px 细轨滑杆 + 百分比,不占满面板宽度。 - 只用 `--dsw-*` 主题变量,不硬编码颜色(失败红除外,取主题 danger 变量,没有就 `#E5484D`)。 - 明暗主题都成立。 - CSS 通过 `