AskaEth 84f343ce5f feat(sessions): 抽屉里看得见「哪个会话正在生成」,并给一个能停的入口(S5 + S9)
S9 登记的是「这个会话正在生成」既看不见、也没有可查的状态面:`session_busy` /
`session_idle` / `turn_begin` 都带 sessionId 且在广播,但 `SessionsViewModel.onEvent`
一个都不认。S5 要的「能看见 + 能停」正是同一份状态的下游。

看得见(S9)
- 新增 `ui/sessions/SessionBusy.kt`:把下行帧折成「本端已知在跑的会话」账本,判据是纯函数
  `applyBusyFrame`(可单测,先例 `silentRefreshStep`)。点亮只认 `session_busy` 与
  `turn_begin` 这两条活帧——握手不回放该状态,所以没收到过帧的会话一定不显示(宁可没有);
  收尾认 `session_idle`(无条件)、`turn_end`(只收本端认得的那一路,收完一路不剩才灭灯)、
  `session_interrupted`(`activeTurns > 0` 时**不**灭——`scope:"current"` 只停一路,无条件灭
  就是 v2 前端审计记成「会错」的 A7 那条)。
- `SessionsViewModel` 用一路订阅把账本接进来,并在**离开 Connected 时当场作废**:掉线期间
  服务端那边跑完了什么本端收不到,留着就是一盏永远不灭、按钮永远点不动的灯。
- 抽屉那一行:忙碌时摘要行下面多一行「正在思考…」(与聊天页 `busyLabel` 逐字相同)。
  另起一行而不顶掉摘要,也不从 `endActions` / 标题列里分走任何一个 dp。

能停(S5)
- 新的上行动作 `SessionsViewModel.stopGenerating(sessionId)`,走**既有的** interrupt 出口
  (`WsOutbound.Interrupt` 本来就带 sessionId,既有的帧表达得了「停某一个会话」,没有新造帧)。
  恒发 `scope:"current"`:本端从不发 `all` 是一条既有不变式,而 `all` 会静默清空待处理队列
  (那份队列是用户自己敲进去的),本端也没有「丢弃 N 条」的告知面。
- 抽屉行里「停止」与「置顶」共用**同一个 endActions 槽位**:抽屉固定 300dp,`BasicComponent`
  把内部可用宽度的 60% 当 `endActions` 硬上限,现有的三个 40dp 动作已经贴着边,
  再并一个会把图标挤到裁剪。这一轮结束(灯灭)置顶立刻回来。

纪律
- `onEvent` 里只**前置**一次折帧,`when` 的分支一个字节没动 ⇒ W49 的节流静默重拉与
  [persistedRoute] 的两档语义不变;无关帧(流式 delta)原样返回同一张表,不重推状态。
- 新增 18 条单测钉住上述每一条(含「turn_id 认不得就不灭灯」「并行两路都要收」「stream_end
  不灭灯」「停止不发 all」)。
2026-10-06 15:55:04 +08:00
S
Description
昔涟在安卓设备上的载体。
Apache-2.0
19 MiB
0 Stars 1 Watchers 0 Forks
2026-05-29 22:15:56 +08:00