Skip to content

[Bug] 流式日志的关键词高亮,肉眼可见的慢一拍:480ms 补偿导致可见的闪一下 #3271

Description

@zpj80231

Operating system

macOS

Netcatty version

1.1.82

How did you install Netcatty?

GitHub Release (.dmg / .exe / .AppImage / .deb / .rpm / .pacman)

Affected area

SSH connection / terminal

Can you reproduce it?

Always (100%)

Steps to reproduce

  1. 设置 → 终端 → 开启关键词高亮,加一条规则,例如正则:.*WARN.*,黄色。
  2. 连上主机,跑一个每秒 10 行以上的持续输出,例如 tail -f /var/log/syslogdocker logs -f <container>
  3. 盯着新进来的行看。
  4. 或者直接 tail -200f 查看日志(必现)

Expected behavior

关键词的颜色应当和文字在同一帧里一起出现,中间不该存在"无色"的可见状态。

Actual behavior

每批输出先以无色渲染出来,大约半秒后(scrollback 打满时接近 1 秒)整个视口重绘一次,颜色才一起出现。观感上就是高亮永远慢终端一步。


以下内容是我问了下 AI,仅供您参考:

不是"写入后着色"这个设计的问题——那条路径其实已经等价于"上屏前着色"了。xterm 在宏任务里解析(WriteBuffer.write()_scheduleInnerWrite()setTimeout(..., 0)),在 rAF 里渲染(RenderService.refreshRows()RenderDebouncer),所以 write 回调里的改色会落在同一帧内。滞后完全来自延迟补偿路径:

  1. components/terminal/keywordHighlight.ts:688shouldBypassWrite(),当 countNewlines(data) >= BULK_WRITE_LINE_BREAKS(8,第 71 行)或 shouldDegradeTerminalKeywordHighlight() 为真时,整批跳过着色。
  2. components/terminal/runtime/terminalOutputPressure.ts:52-54:100ms 窗口内累计达到 LARGE_OUTPUT_RATE_BYTES = 16 * 1024 即降级;TERMINAL_LONG_LINE_PRESSURE_BYTES 为 64KB(infrastructure/config/terminalFlowConstants.json)。
  3. 跳过的活由 scheduleCatchUp()keywordHighlight.ts:764)重新排队,静默窗口是 XTERM_PERFORMANCE_CONFIG.highlighting.largeOutputQuietMs = 480infrastructure/config/xtermPerformance.ts:106),scrollback 打满时翻倍到 960ms(terminalOutputPressure.ts:184-190)。
  4. components/terminal/runtime/writeCoalescer.ts 把 PTY chunk 合并成每个 tick 一次 term.write()(16ms burst 窗口,第 36 行),所以流式日志几乎总会超过 8 个换行。对 tail 日志来说,bypass 是常态而不是例外。
  5. catch-up 真正跑起来时会重新着色并调用 recolorVisible() + refresh(),这就是第二次绘制,也就是肉眼看到的"闪一下"。
按性价比排序:
  1. bypass 时仍在 write 回调里同步给当前视口着色。成本固定为 O(视口行数 × 规则数),与输出量无关;只把视口外的历史行丢给 catch-up。这样用户永远看不到无色的帧,后台补色也不可见。
  2. largeOutputQuietMs 从 480 降到帧级(0–32ms),并/或暴露成设置项。runCatchUp() 已有 4ms 切片预算和 yieldToRenderer() 让出,不会卡住主线程。
  3. 提高 BULK_WRITE_LINE_BREAKS(或改成按视口行数算),并放宽 16KB/100ms 的压力阈值——这个阈值只有约 160KB/s,普通日志很容易撞上。
  4. 每次写都用 recolorRange(..., force=true),会绕过 logicalLineIsCurrent() 的指纹缓存、每行每次都重新匹配。把这块开销降下来,才是减少 bypass 必要性的根源。

如果 480ms 是有意的吞吐取舍,那至少请把它做成可配置项,让日志重度用户可以自己选择"同帧着色"。

Logs / screenshots

No response

Before submitting

  • I searched existing issues and did not find a duplicate
  • I removed passwords, private keys, and other secrets from this report

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingtriageTouched by Cursor automationtriage:admittedReserved for serialized automatic issue triage admissiontriage:bug-readyConfirmed Netcatty bug ready for automatic implementation

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions