---
title: 流式文字节拍器与微渐显预设
description: 大模型网络 Token 到达脉冲抖动、useStreamPacer 恒定节拍调度、Aurora 微渐显与全屏单光标交接契约。
---

# 流式文字节拍器与微渐显预设

在大模型生成界面中，Token 的到达具有高度不确定性。直接将网络包刷入 DOM 会造成严重的视觉痉挛感。

## AI 大模型网络 Token 到达的非均匀性

由于服务端推理排队与网络传输抖动，流式输出经常表现为：
- 首字长达数百毫秒卡顿（TTFT 延迟）。
- 突然一个 TCP 包整块倾泻数十个字。
- 随后再次停顿数秒。

如果缺乏调度缓冲，屏幕上的文字会在“冻结”与“暴雨刷屏”之间剧烈跳跃，用户视线完全无法跟进阅读。

## useStreamPacer 时钟调度状态机

TuffEx 的 `TxStreamText` 与 `TxStreamElement` 将底层网络流与渲染呈现解耦，通过内部时钟平滑输出。`TxStreamText` 内部这样调用节拍器（节选）：

:::TuffCodeBlock{lang="typescript"}
---
code: |
  // 节拍参数取自 props，不传时用 STREAM_PACING_DEFAULTS
  const pacer = useStreamPacer({
    total: () => available.value,     // 已到达的词元数
    streaming: () => props.streaming, // 源是否仍在输出
    wordMs: () => props.wordMs,       // 默认 24：至少每 24ms 释放一个词元
    maxLagMs: () => props.maxLagMs,   // 默认 600：每个词元最迟在到达后 600ms 显示
    drainMs: () => props.drainMs,     // 默认 320：源结束后，剩余积压在 320ms 内出清
    pauseMs: () => props.pauseMs,     // 默认 400：源未结束、400ms 没有词元可放，状态记为 paused
  })
---
:::

- **正常阶段**：至少每 24ms 释放一个词元。
- **持续追赶**：流式期间，释放速率取「1 ÷ wordMs」与「积压 ÷ 最老未显示词元剩余的期限」中较大的一个，期限是它的到达时间加 `maxLagMs`（600ms）。整段积压在这个期限前均匀放完，突发读起来是一段加快的字流，而不是一次砸下一大块；源一直快过 24ms 一个词元时，显示延迟稳定在略低于 600ms。
- **收尾**：源结束的那一刻，速率定为「1 ÷ wordMs」与「积压 ÷ drainMs」中较大的一个，剩余积压在 320ms 内放完。
- **暂停**：源仍在输出、显示已追平且 400ms 没有新的释放时，状态变为 `paused`。光标的外观不随状态变化，任何实时状态下都是同一个环绕加呼吸的动画。
- **末词等待**：`TxStreamText` 会留住最后一个还没写完的词，等到空白、收尾标点，或源安静 200ms 后再放出。

`useStreamPacer` 也从 `stream-text` 导出，可以单独使用，`total` 与 `streaming` 两项必填。

## 微渐显风格预设（Aurora 与 Blur）

新释放的词元在进入 DOM 时，可配置微妙的过渡效果：
- **`aurora`（默认，460ms）**：从 4px 模糊中淡入，颜色同时由蓝经紫、粉扫到正文墨色。
- **`hue`（180ms）**：同样的淡入与色彩扫过，但没有模糊。
- **`blur`（420ms）**：只从 4px 模糊中淡入，不带颜色。
- **`languid`（900ms）**：从 8px 模糊中淡入，同时上浮 4px。
- **`none`（0ms）**：纯文本即时呈现。

系统开启「减弱动态效果」时，`TxStreamText` 跳过节拍、不播放入场，文字一到达就以最终样式出现。

:::TuffDemoWrapper{demo="ConceptStreamPacerDemo"}
:::

## 全屏唯一光标交接契约

当复杂的 Markdown 响应由段落、列表、代码块等多个子组件构成时：
**全屏严禁出现多个闪烁光标！**
后序组件接管光标的瞬间，前序组件必须通过状态传递立即销毁或隐藏自身光标；流式完全结束后，尾部光标优雅淡出收拢。
