Repository navigation
feat(webui): give the streaming cursor a square-wave rhythm - #122
Merged
Merged
Conversation
…rity 61, G6) The trailing cursor on a streaming assistant block ran on Tailwind's stock animate-pulse - a 2s symmetric eased breath between opacity 1 and 0.5, with no instant edge and no off state. That reads as "loading", which is what the transcript skeleton already says. Replace it with .stream-cursor: a two-level square wave, 1.1s, 60% lit, steps(1, end), dark floor 0.2 rather than 0 so the caret never looks like a dropped character at the end of a prose run. The 1.1s period is not a ratio of the 3.5s phrase rotation, so the two rhythms do not lock into one slow compound beat. The beat is deliberately free-running: the span is reconciled as the same element all turn, so keying it to token arrival would need a per-token remount that restarts the animation before it reaches its dark phase. Reduced motion gets an explicit rule in the shared globals.css block (the duration catch-all never touches animation-name), and the caret stays lit and still rather than vanishing. Pinned by webapp/test/stream-cursor.test.ts (16 cases), verified by mutation: renaming or deleting the keyframes, easing the wave, dimming to zero, locking the period, restoring animate-pulse, and rendering the cursor unconditionally each turn the suite red. Docs: docs/webui.md + docs/webui.zh-CN.md, same PR.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
背景
61 号工单最后一条差距。桌面版没有文本光标(用活动指示代替),但工单 46 已拍板保留
▍并单独打磨节奏——所以本 PR 不是删掉它,而是给它一个有节奏的动效。现状
chat.tsx:706-708用 Tailwind 通用animate-pulse:三个问题:无瞬时边沿、无熄灭态(最低只到 0.5)、**2s 周期读作「加载」**且与骨架屏语义重复。
新动效
方波式(亮/灭),60% 亮 / 40% 暗,1.1s 周期。
为什么 1.1s 而不是照抄短语轮换的 3.5s:3.5 / 1.1 = 3.18,两者不锁相——否则两个周期动效会周期性重合,肉眼能看出规律。
两处有意偏离:
key、className 是静态字面量)。要按 token 重挂载就会在走到暗档前重启,恰好在节奏最该被看见时变常亮,还每 token 多一次 DOM 抖动,零收益因此没有动 SSE 通路、没有加
key、没有改渲染条件——产品代码只有 1 行 JSX + 一段 CSS。reduced-motion
共用块内加
.stream-cursor { animation: none }(紧邻.message-animate-in,同形)。不靠 catch-all——它只强制
animation-duration,从不碰animation-name,所以对 animation 不够。不需要!important(无第二张表竞争,已被测试钉住)。关闭后光标落回实心、静止、可见——测试额外断言两条规则体都不含
opacity,保证「禁用动效」不等于「看不见」。流式无副作用
纯 CSS,零 JS / 零 state / 零 effect,不进渲染路径。span 无
key、className 静态 → 节点复用 → 相位自由运行,不会逐 token 重启。回合结束仍随streaming=false消失,逻辑零改动。变异验证 10/10 全红,逐字节还原
第一轮抓到一处真漏洞:改关键帧名最初全绿——
keyframesBody()用的是indexOf子串匹配,stream-cursor-blink-typo也命中。已补「animation 名必须在全部已加载样式表里精确解析到唯一一个@keyframes声明」,重跑后该变异与整块删除均转红。门禁
pnpm webapp:typecheckpnpm typecheckpnpm test:webapppnpm test:webuipnpm check:sourcecheck-docs-alignment.mjstest-tmp-leak verify-registrySPEC 闸门:产品代码 +71 / −1(
globals.css+70 含 48 行解释注释、chat.tsx+1/−1),新增 API/组件 0。边界:真机验证做不到
本分支在独立 worktree,dev server 不加载它。未重启任何实例、未跑 build/install。三条只能源侧 + 测试侧担保,需合入后由质检在真机确认:
animation-name: stream-cursor-blink/steps(1, end)animation-name: none且光标仍可见顺带记录一个未修的边角
空文本帧线上是
● ▍,解码器^[●•]\s+(.*)$的贪婪匹配会吃掉全部空格 → 该帧streaming为 false。无可观察后果,本单未修,已在测试注释写明。🤖 Generated with DeepSeek Harness