From 03c9661bef14771a6023401e0a2ec1e847bd7a33 Mon Sep 17 00:00:00 2001 From: DavidShen Date: Tue, 25 Aug 2026 18:25:49 +0800 Subject: [PATCH 1/4] =?UTF-8?q?perf(desktop):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E6=B5=81=E5=BC=8F=E6=AD=A3=E6=96=87=E5=8A=A8=E7=94=BB=E4=B8=8E?= =?UTF-8?q?=E6=B8=B2=E6=9F=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: DavidShen --- .../components/chat/MarkdownRenderer.tsx | 90 +++--- .../components/chat/StreamFadeSpan.tsx | 92 +++++- .../chat/StreamingMarkdownChunk.tsx | 94 ++++++ .../chat/__tests__/StreamFadeSpan.test.tsx | 100 ++++-- .../__tests__/rehypeStreamWordFade.test.ts | 146 +++++---- .../streamingMarkdownChunks.test.tsx | 184 +++++++++++ .../components/chat/rehypeStreamWordFade.ts | 292 +++++++++--------- .../chat/streamingMarkdownChunks.ts | 168 ++++++++++ apps/desktop/src/renderer/styles/globals.css | 18 +- 9 files changed, 908 insertions(+), 276 deletions(-) create mode 100644 apps/desktop/src/renderer/components/chat/StreamingMarkdownChunk.tsx create mode 100644 apps/desktop/src/renderer/components/chat/__tests__/streamingMarkdownChunks.test.tsx create mode 100644 apps/desktop/src/renderer/components/chat/streamingMarkdownChunks.ts diff --git a/apps/desktop/src/renderer/components/chat/MarkdownRenderer.tsx b/apps/desktop/src/renderer/components/chat/MarkdownRenderer.tsx index ea30a6c69c..be06b85394 100644 --- a/apps/desktop/src/renderer/components/chat/MarkdownRenderer.tsx +++ b/apps/desktop/src/renderer/components/chat/MarkdownRenderer.tsx @@ -11,7 +11,7 @@ * into Markdown image nodes before HTML filtering. */ -import { createElement, memo, useCallback, useEffect, useLayoutEffect, useRef, useState, useMemo, isValidElement, type HTMLAttributes, type ReactNode } from 'react'; +import { createElement, memo, useCallback, useEffect, useRef, useState, useMemo, isValidElement, type HTMLAttributes, type ReactNode } from 'react'; import ReactMarkdown, { defaultUrlTransform } from 'react-markdown'; import remarkGfm from 'remark-gfm'; import remarkCjkFriendly from 'remark-cjk-friendly'; @@ -33,14 +33,12 @@ import remarkSessionLinks from './remarkSessionLinks'; import { rehypeMathBlockMarker } from './rehypeMathBlockMarker'; import { FENCED_CODE_PROP, rehypeFencedCodeMarker } from './rehypeFencedCodeMarker'; import { - commitWordFadeCandidate, - createWordFadeCandidate, getOrCreateWordFadeState, releaseWordFadeState, - rehypeStreamWordFade, } from './rehypeStreamWordFade'; -import { StreamFadeSpan } from './StreamFadeSpan'; import { repairStreamingMarkdown } from './repairStreamingMarkdown'; +import { StreamingMarkdownChunk } from './StreamingMarkdownChunk'; +import { splitStreamingMarkdownChunks } from './streamingMarkdownChunks'; import { useReducedMotion } from '@/hooks/useReducedMotion'; import { useStreamFadeEnabled } from '@/hooks/useStreamFadePreference'; import { CopyAsImageBlock, mathBlockToLatex, tableToTsv } from './CopyAsImageBlock'; @@ -1641,13 +1639,9 @@ export const MarkdownRenderer = memo(function MarkdownRenderer({ // Static callers (TextLightbox) leave isStreaming undefined → false, // so the throttle is fully bypassed — same behavior as before. const throttledContent = useStreamingThrottle(content, isStreaming); - // 流式逐词淡入(rehypeStreamWordFade,§14.4 第五个 sanctioned motion class): - // 仅 isStreaming + 非 reduced-motion 时把插件挂到 rehype 链尾。committed state - // 按消息身份跨 parse / remount 保留;本次 render 只写 candidate,layout effect - // 确认 DOM 已提交后才落状态,避免被放弃的并发 render 提前推进 key / 时间线。 - // 根节点监听 animationend 把播完的段落袋(settled),下一次 parse 还原纯文本。 - // isStreaming 翻 false 时整段回落到模块级常量 REHYPE_PLUGINS —— 终版渲染无 - // 任何 span 包装,插件、state 与监听一起被回收,静态路径零开销。 + // 流式逐词淡入(DESIGN.md §14.4):消息级 state 跨 parse / remount 保留, + // 各稳定 Markdown 分片只维护自己的内容匹配状态,但共享同一条连续时间线。 + // isStreaming 翻 false 时整段回落到普通 Markdown,终版没有流式 span 包装。 // 用户开关(Settings → 个性化 → 流式动效,默认开)与 reduced-motion 取 AND: // 系统级减弱动效永远优先,开关只在 motion 允许的前提下再做个人选择。 const reducedMotion = useReducedMotion(); @@ -1677,21 +1671,13 @@ export const MarkdownRenderer = memo(function MarkdownRenderer({ () => normalizeMathDelimiters(repairedContent, { preserveLineCount: emitSourceLines }), [repairedContent, emitSourceLines], ); - const wordFade = useMemo(() => { - if (!wordFadeState) return null; - const candidate = createWordFadeCandidate(wordFadeState); - return { - candidate, - plugins: [...REHYPE_PLUGINS, [rehypeStreamWordFade, candidate]] as PluggableList, - }; - // animationend 不触发 React state;即使正文没变,其它 render 也要克隆最新 settled。 - }, [wordFadeState, renderedContent, wordFadeState?.settled.size]); - useLayoutEffect(() => { - if (wordFadeState && wordFade) { - commitWordFadeCandidate(wordFadeState, wordFade.candidate); - } - }, [wordFadeState, wordFade]); - const rehypePlugins = wordFade?.plugins ?? REHYPE_PLUGINS; + const streamingChunks = useMemo( + () => + isStreaming && !emitSourceLines + ? splitStreamingMarkdownChunks(renderedContent) + : [{ start: 0, content: renderedContent }], + [emitSourceLines, isStreaming, renderedContent], + ); const [lightboxSrc, setLightboxSrc] = useState(null); // 远程入方向:远程会话里 markdown 的图片/音频 URL 指向远端机器,按来源改写到 // cindy-remote-media://(device 经 OSS 中转、ssh 经 file-service 落盘缓存)。本地 @@ -1718,16 +1704,6 @@ export const MarkdownRenderer = memo(function MarkdownRenderer({ } | null>(null); // model-local chip/link click → in-app 3D preview (ModelLightbox, local mode). const [modelLightboxPath, setModelLightboxPath] = useState(null); - const streamFadeComponents = useMemo( - () => - wordFadeState - ? { - span: (props) => , - } - : {}, - [wordFadeState], - ); - // workingDir and localFileRefs are stable within a session lifecycle — they // only change on session switch or when the message list gains a new user // attachment. So in steady-state streaming, the components object is still @@ -1735,7 +1711,6 @@ export const MarkdownRenderer = memo(function MarkdownRenderer({ const components = useMemo( () => ({ ...baseComponents, - ...streamFadeComponents, // doc-mode anchor: emitSourceLines=true 时把 baseComponents 里的 block // renderer 整体替换成带 data-source-line 注入的版本。chat 调用方不传 prop // → 默认 false → 整段是 falsy 短路, components 对象与之前完全一致。 @@ -1890,21 +1865,40 @@ export const MarkdownRenderer = memo(function MarkdownRenderer({ currentSessionTitle, allowPrivilegedLinks, remoteMediaOrigin, - streamFadeComponents, ], ); return (
- - {renderedContent} - + {isStreaming ? ( + streamingChunks.map((chunk) => ( + + )) + ) : ( + + {renderedContent} + + )} {lightboxSrc && ( setLightboxSrc(null)} /> )} diff --git a/apps/desktop/src/renderer/components/chat/StreamFadeSpan.tsx b/apps/desktop/src/renderer/components/chat/StreamFadeSpan.tsx index 95dedd7545..d2e160661d 100644 --- a/apps/desktop/src/renderer/components/chat/StreamFadeSpan.tsx +++ b/apps/desktop/src/renderer/components/chat/StreamFadeSpan.tsx @@ -2,15 +2,40 @@ import type { AnimationEvent as ReactAnimationEvent, ComponentPropsWithoutRef, } from 'react'; +import { useCallback } from 'react'; import type { Element } from 'hast'; -import { markWordFadeSettled, type WordFadeState } from './rehypeStreamWordFade'; +import { + isWordFadeSettled, + markWordFadeSettled, + scheduleWordFadeSegment, + type WordFadeState, +} from './rehypeStreamWordFade'; interface StreamFadeSpanProps extends ComponentPropsWithoutRef<'span'> { node?: Element; wordFadeState: WordFadeState | null; } +interface StreamFadeListItemProps extends ComponentPropsWithoutRef<'li'> { + node?: Element; + wordFadeState: WordFadeState | null; +} + +function readWordFadeKey(node?: Element): string | undefined { + const rawWordFadeKey = node?.properties?.dataWfKey; + return typeof rawWordFadeKey === 'string' ? rawWordFadeKey : undefined; +} + +function removeStreamWordClass(className?: string): string | undefined { + if (!className) return className; + const next = className + .split(/\s+/) + .filter((name) => name && name !== 'stream-word') + .join(' '); + return next || undefined; +} + /** * ReactMarkdown 给自定义 renderer 的外层 key 是位置型 span-N。这里再用逻辑 key * 标识真实 DOM span:位置被复用给别的段时会 remount,旧动画不能污染新段。 @@ -22,8 +47,18 @@ export function StreamFadeSpan({ wordFadeState, ...props }: StreamFadeSpanProps) { - const rawWordFadeKey = node?.properties?.dataWfKey; - const wordFadeKey = typeof rawWordFadeKey === 'string' ? rawWordFadeKey : undefined; + const wordFadeKey = readWordFadeKey(node); + const settled = Boolean( + wordFadeState && wordFadeKey && isWordFadeSettled(wordFadeState, wordFadeKey), + ); + const attachSpan = useCallback( + (element: HTMLSpanElement | null) => { + if (element && wordFadeState && wordFadeKey && !settled) { + scheduleWordFadeSegment(wordFadeState, wordFadeKey, element); + } + }, + [settled, wordFadeKey, wordFadeState], + ); const handleAnimationEnd = (event: ReactAnimationEvent) => { onAnimationEnd?.(event); if ( @@ -35,12 +70,15 @@ export function StreamFadeSpan({ return; } markWordFadeSettled(wordFadeState, wordFadeKey); + event.currentTarget.classList.remove('stream-word'); }; return ( @@ -48,3 +86,51 @@ export function StreamFadeSpan({ ); } + +/** 列表圆点与 li 内首段共用 key 和时间线;ref 在 paint 前校正 ::marker delay。 */ +export function StreamFadeListItem({ + children, + node, + onAnimationEnd, + wordFadeState, + ...props +}: StreamFadeListItemProps) { + const wordFadeKey = readWordFadeKey(node); + const settled = Boolean( + wordFadeState && wordFadeKey && isWordFadeSettled(wordFadeState, wordFadeKey), + ); + const attachListItem = useCallback( + (element: HTMLLIElement | null) => { + if (element && wordFadeState && wordFadeKey && !settled) { + scheduleWordFadeSegment(wordFadeState, wordFadeKey, element); + } + }, + [settled, wordFadeKey, wordFadeState], + ); + const handleAnimationEnd = (event: ReactAnimationEvent) => { + onAnimationEnd?.(event); + if ( + !wordFadeState || + !wordFadeKey || + event.target !== event.currentTarget || + event.animationName !== 'stream-marker-in' + ) { + return; + } + markWordFadeSettled(wordFadeState, wordFadeKey); + event.currentTarget.removeAttribute('data-stream-marker'); + }; + const markerProps = props as typeof props & { 'data-stream-marker'?: boolean }; + + return ( +
  • + {children} +
  • + ); +} diff --git a/apps/desktop/src/renderer/components/chat/StreamingMarkdownChunk.tsx b/apps/desktop/src/renderer/components/chat/StreamingMarkdownChunk.tsx new file mode 100644 index 0000000000..88d62106f9 --- /dev/null +++ b/apps/desktop/src/renderer/components/chat/StreamingMarkdownChunk.tsx @@ -0,0 +1,94 @@ +import { memo, useLayoutEffect, useMemo } from 'react'; +import ReactMarkdown, { type Components, type UrlTransform } from 'react-markdown'; +import type { Element } from 'hast'; +import type { PluggableList } from 'unified'; + +import { + commitWordFadeCandidate, + createWordFadeCandidate, + getOrCreateWordFadeSourceState, + rehypeStreamWordFade, + type WordFadeState, +} from './rehypeStreamWordFade'; +import { StreamFadeListItem, StreamFadeSpan } from './StreamFadeSpan'; + +interface StreamingMarkdownChunkProps { + sourceKey: string; + content: string; + remarkPlugins: PluggableList; + rehypePlugins: PluggableList; + components: Components; + urlTransform?: UrlTransform; + wordFadeState: WordFadeState | null; + emitSourceLines: boolean; +} + +function sourceLineAttr(node?: Element): { 'data-source-line'?: number } { + const line = node?.position?.start.line; + return typeof line === 'number' ? { 'data-source-line': line } : {}; +} + +/** + * 单个流式 Markdown 分片。React.memo 让内容不再变化的前缀保留解析结果和 DOM, + * 只有最后一个增长分片会随 token 到达重新进入 Markdown 处理链。 + */ +export const StreamingMarkdownChunk = memo(function StreamingMarkdownChunk({ + sourceKey, + content, + remarkPlugins, + rehypePlugins, + components, + urlTransform, + wordFadeState, + emitSourceLines, +}: StreamingMarkdownChunkProps) { + const sourceWordFadeState = useMemo( + () => + wordFadeState ? getOrCreateWordFadeSourceState(wordFadeState, sourceKey) : null, + [sourceKey, wordFadeState], + ); + const wordFade = useMemo(() => { + if (!sourceWordFadeState) return null; + const candidate = createWordFadeCandidate(sourceWordFadeState); + return { + candidate, + plugins: [...rehypePlugins, [rehypeStreamWordFade, candidate]] as PluggableList, + }; + }, [content, rehypePlugins, sourceWordFadeState]); + + useLayoutEffect(() => { + if (sourceWordFadeState && wordFade) { + commitWordFadeCandidate(sourceWordFadeState, wordFade.candidate); + } + }, [sourceWordFadeState, wordFade]); + + const chunkComponents = useMemo(() => { + if (!sourceWordFadeState) return components; + return { + ...components, + span: (props) => ( + + ), + li: ({ node, ...props }) => ( + + ), + }; + }, [components, emitSourceLines, sourceWordFadeState]); + + return ( + + {content} + + ); +}); diff --git a/apps/desktop/src/renderer/components/chat/__tests__/StreamFadeSpan.test.tsx b/apps/desktop/src/renderer/components/chat/__tests__/StreamFadeSpan.test.tsx index c5cff2ed9a..cd46c76dc4 100644 --- a/apps/desktop/src/renderer/components/chat/__tests__/StreamFadeSpan.test.tsx +++ b/apps/desktop/src/renderer/components/chat/__tests__/StreamFadeSpan.test.tsx @@ -6,7 +6,7 @@ import ReactMarkdown, { type Components } from 'react-markdown'; import type { PluggableList } from 'unified'; import { afterEach, describe, expect, it, vi } from 'vitest'; -import { StreamFadeSpan } from '../StreamFadeSpan'; +import { StreamFadeListItem, StreamFadeSpan } from '../StreamFadeSpan'; import { commitWordFadeCandidate, createWordFadeCandidate, @@ -39,6 +39,7 @@ function StreamingMarkdownHarness({ const components = useMemo( () => ({ span: (props) => , + li: (props) => , }), [state], ); @@ -86,8 +87,8 @@ describe('StreamFadeSpan', () => { expect(forwardedAnimationEnd).toHaveBeenCalledOnce(); expect(forwardedAnimationEnd.mock.calls[0][0].animationName).toBe('stream-word-in'); expect(forwardedTargetMatched).toBe(true); - expect(state.settled.has('wf-original')).toBe(true); - expect(state.settled.has('wf-reused')).toBe(false); + expect(state.timeline.settled.has('wf-original')).toBe(true); + expect(state.timeline.settled.has('wf-reused')).toBe(false); }); it('忽略子节点冒泡和其它动画名', () => { @@ -111,35 +112,94 @@ describe('StreamFadeSpan', () => { fireAnimationEnd(span.querySelector('code')!); fireAnimationEnd(span, 'spinner-rotate'); - expect(state.settled.size).toBe(0); + expect(state.timeline.settled.size).toBe(0); }); - it('settled 前缀拆除导致位置 key 前移时重建真实 span,旧节点不能结算新段', () => { + it('settled 后只摘动画 class,后续增长不替换既有 DOM 节点', () => { const state = createWordFadeState(); - state.nowFn = () => 0; + state.timeline.nowFn = () => 0; const view = render(); const firstFrame = Array.from( - view.container.querySelectorAll('.stream-word'), + view.container.querySelectorAll('[data-wf-key]'), ); - const oldFirst = firstFrame[0]; - const oldSecond = firstFrame[1]; - const secondKey = oldSecond.dataset.wfKey!; + const firstKeys = firstFrame.map((span) => span.dataset.wfKey); - fireAnimationEnd(oldFirst); + fireAnimationEnd(firstFrame[0]); + expect(firstFrame[0].isConnected).toBe(true); + expect(firstFrame[0].classList.contains('stream-word')).toBe(false); view.rerender(); const secondFrame = Array.from( - view.container.querySelectorAll('.stream-word'), + view.container.querySelectorAll('[data-wf-key]'), + ); + expect(secondFrame.map((span) => span.textContent)).toEqual([ + 'one ', + 'two ', + 'three ', + 'four', + ]); + expect(secondFrame.slice(0, 3).map((span) => span.dataset.wfKey)).toEqual(firstKeys); + expect(secondFrame[0]).toBe(firstFrame[0]); + expect(secondFrame[1]).toBe(firstFrame[1]); + expect(secondFrame[2]).toBe(firstFrame[2]); + }); + + it('DOM commit 接入 16ms / 96ms 连续时间线', () => { + const state = createWordFadeState(); + state.timeline.nowFn = () => 0; + const content = Array.from({ length: 10 }, (_, index) => `w${index}`).join(' '); + const { container } = render(); + const delays = Array.from( + container.querySelectorAll('[data-wf-key]'), + (span) => span.style.getPropertyValue('--wf-delay'), + ); + expect(delays).toEqual([ + '0ms', + '16ms', + '32ms', + '48ms', + '64ms', + '80ms', + '96ms', + '100ms', + '104ms', + '108ms', + ]); + }); + + it('千级突发段把透明等待限制在 160ms 内,同时保留前段节奏', () => { + const state = createWordFadeState(); + state.timeline.nowFn = () => 0; + const content = Array.from({ length: 1_000 }, (_, index) => `w${index}`).join(' '); + const { container } = render(); + const delays = Array.from( + container.querySelectorAll('[data-wf-key]'), + (span) => Number.parseInt(span.style.getPropertyValue('--wf-delay'), 10), ); - expect(secondFrame.map((span) => span.textContent)).toEqual(['two ', 'three ', 'four']); - expect(secondFrame[0].dataset.wfKey).toBe(secondKey); - expect(secondFrame[0]).not.toBe(oldFirst); - expect(oldSecond.isConnected).toBe(false); - fireAnimationEnd(oldSecond); - expect(state.settled.has(secondKey)).toBe(false); + expect(delays).toHaveLength(1_000); + expect(delays.slice(0, 10)).toEqual([0, 16, 32, 48, 64, 80, 96, 100, 104, 108]); + expect(Math.max(...delays)).toBe(160); + expect(delays.at(-1)).toBe(160); + }); - fireAnimationEnd(secondFrame[0]); - expect(state.settled.has(secondKey)).toBe(true); + it('列表圆点与首段共用 delay,完成后保留 li 节点', () => { + const state = createWordFadeState(); + state.timeline.nowFn = () => 0; + const { container } = render( + , + ); + const li = container.querySelector('[data-stream-marker]')!; + const firstWord = li.querySelector('[data-wf-key]')!; + const key = firstWord.dataset.wfKey!; + + expect(li.dataset.wfKey).toBe(key); + expect(li.style.getPropertyValue('--wf-delay')).toBe( + firstWord.style.getPropertyValue('--wf-delay'), + ); + fireAnimationEnd(li, 'stream-marker-in'); + expect(li.isConnected).toBe(true); + expect(li.hasAttribute('data-stream-marker')).toBe(false); + expect(state.timeline.settled.has(key)).toBe(true); }); }); diff --git a/apps/desktop/src/renderer/components/chat/__tests__/rehypeStreamWordFade.test.ts b/apps/desktop/src/renderer/components/chat/__tests__/rehypeStreamWordFade.test.ts index b9e2f139eb..d172f5f54d 100644 --- a/apps/desktop/src/renderer/components/chat/__tests__/rehypeStreamWordFade.test.ts +++ b/apps/desktop/src/renderer/components/chat/__tests__/rehypeStreamWordFade.test.ts @@ -1,7 +1,7 @@ /** * rehypeStreamWordFade.test.ts * --------------------------------------------------------------------------- - * 流式分段淡入插件的行为测试:切词、零 stagger、稳定 key(不重播)、结构 + * 流式分段淡入插件的行为测试:切词、连续时间线、稳定 key(不重播)、结构 * 变化下的 key 稳定性、render candidate 提交、inline-code 原子淡入、 * pre/KaTeX 跳过,以及 CSS 侧动画本体的静态回归。 */ @@ -19,6 +19,7 @@ import { getOrCreateWordFadeState, releaseWordFadeState, rehypeStreamWordFade, + scheduleWordFadeSegment, splitWords, type WordFadeState, } from '../rehypeStreamWordFade'; @@ -44,7 +45,7 @@ function root(...children: Root['children']): Root { } function run(tree: Root, state: WordFadeState, nowMs = 0): Root { - state.nowFn = () => nowMs; + state.timeline.nowFn = () => nowMs; const transformer = (rehypeStreamWordFade as (s: WordFadeState) => (t: Root) => void)(state); transformer(tree); return tree; @@ -80,6 +81,20 @@ function collectWords(node: Root | Element): { text: string; delay: number; key: return out; } +function schedule(state: WordFadeState, key: string): number { + let writtenDelay = ''; + const element = { + style: { + setProperty(name: string, value: string) { + if (name === '--wf-delay') writtenDelay = value; + }, + }, + } as unknown as HTMLElement; + const delay = scheduleWordFadeSegment(state, key, element); + expect(writtenDelay).toBe(`${delay}ms`); + return delay; +} + describe('splitWords', () => { it('英文按空格切词,空白并入前一词', () => { expect(splitWords('hello world foo')).toEqual(['hello ', 'world ', 'foo']); @@ -104,6 +119,7 @@ describe('rehypeStreamWordFade', () => { const tree1 = root(el('p', [textNode('one two')])); run(tree1, firstMount, 0); const firstKeys = collectWords(tree1).map((word) => word.key); + for (const key of firstKeys) schedule(firstMount, key); const remount = getOrCreateWordFadeState(cacheKey); const tree2 = root(el('p', [textNode('one two three')])); @@ -111,9 +127,9 @@ describe('rehypeStreamWordFade', () => { const words = collectWords(tree2); expect(remount).toBe(firstMount); - expect(firstKeys.every((key) => remount.settled.has(key))).toBe(true); + expect(firstKeys.every((key) => remount.timeline.settled.has(key))).toBe(true); expect(words.map((word) => word.text)).toEqual(['three']); - expect(words[0].delay).toBe(0); + expect(schedule(remount, words[0].key)).toBe(0); }); it('消息进入终态后释放 remount 状态', () => { @@ -123,20 +139,29 @@ describe('rehypeStreamWordFade', () => { expect(getOrCreateWordFadeState(cacheKey)).not.toBe(active); }); - it('普通聊天零 stagger:同 tick 新词全部从 0ms 开始淡入', () => { + it('首帧估值按 16ms 连续错峰,DOM commit 时间线与之对齐', () => { const state = createWordFadeState(); const tree = root(el('p', [textNode('one two three')])); run(tree, state); const words = collectWords(tree); expect(words.map((w) => w.text)).toEqual(['one ', 'two ', 'three']); - expect(words.map((w) => w.delay)).toEqual([0, 0, 0]); + expect(words.map((w) => w.delay)).toEqual([0, 16, 32]); + expect(words.map((word) => schedule(state, word.key))).toEqual([0, 16, 32]); + }); + + it('积压到 96ms 后把新增段步长压缩为 4ms', () => { + const state = createWordFadeState(); + state.timeline.nowFn = () => 0; + const delays = Array.from({ length: 10 }, (_, index) => schedule(state, `wf-${index}`)); + expect(delays).toEqual([0, 16, 32, 48, 64, 80, 96, 100, 104, 108]); }); - it('同一 state 重跑(流式 re-parse)已见词拿回同一 key,发剩余(负)delay 续播', () => { + it('同一 state 重跑时已见词恢复原进度,新词续接消息时间线', () => { const state = createWordFadeState(); const tree1 = root(el('p', [textNode('one two')])); run(tree1, state); const keys1 = collectWords(tree1).map((w) => w.key); + expect(keys1.map((key) => schedule(state, key))).toEqual([0, 16]); // 下一个 tick(100ms 后):全文重建 + 新词到达。 const tree2 = root(el('p', [textNode('one two three four')])); @@ -147,30 +172,21 @@ describe('rehypeStreamWordFade', () => { expect(words[0].key).toBe(keys1[0]); expect(words[1].key).toBe(keys1[1]); expect(words[0].delay).toBe(-100); - expect(words[1].delay).toBe(-100); - // 所有新词都从本 tick 的 0 起播,不互相排队。 - expect(words[2].delay).toBe(0); - expect(words[3].delay).toBe(0); + expect(words[1].delay).toBe(-84); + // 真实 ref 在 paint 前把新词接到当前时刻,不继承已经消化完的历史积压。 + expect(words.slice(2).map((word) => schedule(state, word.key))).toEqual([0, 16]); }); - it('跨 tick 没有历史积压:后到的新词仍立即开始', () => { + it('空闲后突发的新段从当前时刻重新起排', () => { const state = createWordFadeState(); - run(root(el('p', [textNode('a b c d')])), state); - const tree2 = root(el('p', [textNode('a b c d e f')])); - run(tree2, state, 50); - const words = collectWords(tree2); - expect(words.slice(0, 4).map((word) => word.delay)).toEqual([-50, -50, -50, -50]); - expect(words.slice(4).map((word) => word.delay)).toEqual([0, 0]); - }); - - it('卡顿后突发的大段文字也整批立即开始,不产生新的可见积压', () => { - const state = createWordFadeState(); - run(root(el('p', [textNode('a b')])), state); + const tree1 = root(el('p', [textNode('a b')])); + run(tree1, state); + for (const word of collectWords(tree1)) schedule(state, word.key); const tree2 = root(el('p', [textNode('a b x y z')])); run(tree2, state, 5000); - const words = collectWords(tree2); - expect(words.map((w) => w.text)).toEqual(['x ', 'y ', 'z']); - expect(words.map((w) => w.delay)).toEqual([0, 0, 0]); + const newWords = collectWords(tree2); + expect(newWords.map((w) => w.text)).toEqual(['x ', 'y ', 'z']); + expect(newWords.map((word) => schedule(state, word.key))).toEqual([0, 16, 32]); }); it('chunk 边界半个词长成整词:前缀延续复用同一 key', () => { @@ -224,14 +240,16 @@ describe('rehypeStreamWordFade', () => { expect(words2[0].key).toBe(keys1[0]); }); - it('超大 tick 也不把尾部排到未来', () => { + it('超大 tick 在阈值后压缩步长,并把透明等待收敛到 160ms', () => { const state = createWordFadeState(); const many = Array.from({ length: 500 }, (_, i) => `w${i}`).join(' '); const tree = root(el('p', [textNode(many)])); run(tree, state); const delays = collectWords(tree).map((w) => w.delay); expect(delays).toHaveLength(500); - expect(new Set(delays)).toEqual(new Set([0])); + expect(delays.slice(0, 10)).toEqual([0, 16, 32, 48, 64, 80, 96, 100, 104, 108]); + expect(Math.max(...delays)).toBe(160); + expect(delays.at(-1)).toBe(160); }); it('被放弃的 render candidate 不污染 committed key 状态', () => { @@ -241,7 +259,7 @@ describe('rehypeStreamWordFade', () => { expect(committed.nextId).toBe(0); expect(committed.previous).toEqual([]); - expect(committed.startAtByKey.size).toBe(0); + expect(committed.timeline.startAtByKey.size).toBe(0); const mounted = createWordFadeCandidate(committed); const tree = root(el('p', [textNode('mounted render')])); @@ -256,15 +274,15 @@ describe('rehypeStreamWordFade', () => { run(tree, candidate, 0); const keys = collectWords(tree).map((word) => word.key); - committed.settled.add('older-frame'); + committed.timeline.settled.add('older-frame'); commitWordFadeCandidate(committed, candidate); expect(committed.previous.map((segment) => segment.key)).toEqual(keys); - expect(committed.startAtByKey.size).toBe(2); - expect(committed.settled.has('older-frame')).toBe(true); + expect(committed.timeline.startAtByKey.size).toBe(0); + expect(committed.timeline.settled.has('older-frame')).toBe(true); }); - it('截图回归:前文与后到 inline code 同时开始淡入,code 不再抢先显示', () => { + it('inline code 与前文进入同一条连续时间线,不会抢先显示', () => { const state = createWordFadeState(); const code = el('code', [textNode('meta.Disabled')]); const pre = el('pre', [el('code', [textNode('const a = 1')])]); @@ -275,7 +293,9 @@ describe('rehypeStreamWordFade', () => { const codeSegment = paragraph.children.at(-1) as Element; expect(segments.at(-1)?.text).toBe('meta.Disabled'); - expect(segments.every((segment) => segment.delay === 0)).toBe(true); + expect(segments.map((segment) => schedule(state, segment.key))).toEqual( + segments.map((_, index) => (index <= 6 ? index * 16 : 96 + (index - 6) * 4)), + ); expect(codeSegment.tagName).toBe('span'); expect(codeSegment.properties?.className).toContain('stream-word'); expect(codeSegment.children).toEqual([code]); @@ -289,6 +309,7 @@ describe('rehypeStreamWordFade', () => { const tree1 = root(el('p', [textNode('same '), el('code', [textNode('same')])])); run(tree1, state, 0); const first = collectWords(tree1); + for (const segment of first) schedule(state, segment.key); const tree2 = root( el('p', [textNode('same '), el('code', [textNode('same')]), textNode(' tail')]), @@ -299,7 +320,7 @@ describe('rehypeStreamWordFade', () => { expect(first[0].key).not.toBe(first[1].key); expect(second[0].key).toBe(first[0].key); expect(second[1].key).toBe(first[1].key); - expect(second[1].delay).toBe(-100); + expect(second[1].delay).toBe(-84); }); it('表格格内文字照常分段淡入,结构(table/tr/td)不打任何动画标', () => { @@ -352,7 +373,7 @@ describe('rehypeStreamWordFade', () => { expect(li2.properties?.dataWfKey).toBe(words[0].key); expect(String(li2.properties?.style)).toContain(`--wf-delay:${words[0].delay}ms`); // tick 3:第一个词 settled 后圆点不再打标(remount 无从重播)。 - state.settled.add(words[0].key); + state.timeline.settled.add(words[0].key); const li3 = el('li', [textNode('four')]); run(root(el('p', [textNode('one two three')]), el('ul', [li3])), state, 0); expect(li3.properties?.dataStreamMarker).toBeUndefined(); @@ -375,59 +396,67 @@ describe('rehypeStreamWordFade', () => { expect(words.map((w) => w.text)).toEqual(['a', 'b']); }); - it('全 settled 的文本槽位不改树(原生文本节点,零 span)—— 流式长文档性能核心', () => { + it('全 settled 的文本槽位保留 span 身份,只摘动画 class', () => { const state = createWordFadeState(); const tree1 = root(el('p', [textNode('one two three')]), el('p', [textNode('tail')])); run(tree1, state); const words1 = collectWords(tree1); // 第一段全部播完落袋;第二段仍在播。 - state.settled.add(words1[0].key); - state.settled.add(words1[1].key); - state.settled.add(words1[2].key); + state.timeline.settled.add(words1[0].key); + state.timeline.settled.add(words1[1].key); + state.timeline.settled.add(words1[2].key); const tree2 = root(el('p', [textNode('one two three')]), el('p', [textNode('tail more')])); run(tree2, state, 100); - // 全 settled 槽位:文本节点原样保留,不包任何 span。 + // settled 节点仍在原位,避免后续兄弟因包装拆除而换身份。 const p1 = tree2.children[0] as Element; - expect(p1.children).toEqual([textNode('one two three')]); + expect(p1.children).toHaveLength(3); + expect( + p1.children.map((child) => + child.type === 'element' ? child.properties?.className : undefined, + ), + ).toEqual([undefined, undefined, undefined]); + expect(nodeText(p1)).toBe('one two three'); // 在播槽位照常:tail 保住原 key(续播),more 拿新 key。 const words2 = collectWords(tree2); expect(words2.map((w) => w.text)).toEqual(['tail ', 'more']); expect(words2[0].key).toBe(words1[3].key); }); - it('部分 settled 的槽位把 settled 前缀还原为纯文本,仅活动尾部保留 span', () => { + it('部分 settled 的槽位保留全部 span,只有活动段携带动画 class', () => { const state = createWordFadeState(); const tree1 = root(el('p', [textNode('one two three')])); run(tree1, state); const words1 = collectWords(tree1); - // 只有 0 号播完:槽位未全 settled,但 settled 前缀不再保留 span。 - state.settled.add(words1[0].key); + // 只有 0 号播完。 + state.timeline.settled.add(words1[0].key); const tree2 = root(el('p', [textNode('one two three four')])); - run(tree2, state, 500); + run(tree2, state, 100); const words2 = collectWords(tree2); - expect(words2.map((w) => w.text)).toEqual(['four']); + expect(words2.map((w) => w.text)).toEqual(['two ', 'three ', 'four']); const p = tree2.children[0] as Element; - expect(p.children[0]).toEqual(textNode('one two three ')); - // 即使 remount 丢了 animationend,超过 150ms 的旧词也会按开播时刻自动落袋。 - expect(words1.every((word) => state.settled.has(word.key))).toBe(true); - expect(words2[0].delay).toBe(0); + expect(p.children).toHaveLength(4); + const settledSpan = p.children[0] as Element; + expect(settledSpan.properties?.dataWfKey).toBe(words1[0].key); + expect(settledSpan.properties?.className).toBeUndefined(); }); - it('长 settled 前缀只保留一个原生文本节点', () => { + it('长 settled 前缀保留稳定 span,增长尾部继续取得新 key', () => { const state = createWordFadeState(); const prefix = Array.from({ length: 200 }, (_, i) => `w${i}`).join(' '); const tree1 = root(el('p', [textNode(`${prefix} tail`)])); run(tree1, state); const words1 = collectWords(tree1); - for (const word of words1.slice(0, 200)) state.settled.add(word.key); + for (const word of words1.slice(0, 200)) state.timeline.settled.add(word.key); const tree2 = root(el('p', [textNode(`${prefix} tail next`)])); - run(tree2, state, 500); + run(tree2, state, 100); const p = tree2.children[0] as Element; - expect(p.children[0]).toEqual(textNode(`${prefix} tail `)); - expect(collectWords(tree2).map((w) => w.text)).toEqual(['next']); + expect(p.children).toHaveLength(202); + expect((p.children[0] as Element).properties?.className).toBeUndefined(); + expect((p.children[199] as Element).properties?.className).toBeUndefined(); + expect(collectWords(tree2).map((w) => w.text)).toEqual(['tail ', 'next']); }); }); @@ -440,8 +469,12 @@ describe('globals.css 的 stream-word 动画本体', () => { it('引用 --motion-fast token + both 填充(delay 未到时保持透明)', () => { const rule = /\.stream-word\s*\{[\s\S]*?\}/.exec(css)?.[0] ?? ''; expect(rule).toContain('var(--motion-fast)'); + expect(rule).toContain('var(--motion-ease-stream-word)'); expect(rule).toContain('var(--wf-delay'); expect(rule).toContain('both'); + expect(css).toContain( + '--motion-ease-stream-word: cubic-bezier(0.37, 0.55, 0.86, 0.88)', + ); }); it('关键帧只动 opacity(compositor-only,一次性非 infinite)', () => { @@ -455,6 +488,7 @@ describe('globals.css 的 stream-word 动画本体', () => { const rule = /\[data-stream-marker\]::marker\s*\{[\s\S]*?\}/.exec(css)?.[0] ?? ''; expect(rule).toContain('stream-marker-in'); expect(rule).toContain('var(--motion-fast)'); + expect(rule).toContain('var(--motion-ease-stream-word)'); expect(rule).toContain('var(--wf-delay'); expect(rule).toContain('both'); // ::marker 只支持 color/font 系属性,关键帧必须动 color 而不是 opacity。 diff --git a/apps/desktop/src/renderer/components/chat/__tests__/streamingMarkdownChunks.test.tsx b/apps/desktop/src/renderer/components/chat/__tests__/streamingMarkdownChunks.test.tsx new file mode 100644 index 0000000000..41e7b04ed4 --- /dev/null +++ b/apps/desktop/src/renderer/components/chat/__tests__/streamingMarkdownChunks.test.tsx @@ -0,0 +1,184 @@ +// @vitest-environment jsdom + +import { cleanup, render } from '@testing-library/react'; +import type { Element, Root } from 'hast'; +import type { Plugin, PluggableList } from 'unified'; +import { afterEach, describe, expect, it } from 'vitest'; + +import { StreamingMarkdownChunk } from '../StreamingMarkdownChunk'; +import { createWordFadeState } from '../rehypeStreamWordFade'; +import { splitStreamingMarkdownChunks } from '../streamingMarkdownChunks'; + +afterEach(cleanup); + +describe('splitStreamingMarkdownChunks', () => { + it('只在已经出现下一段的顶层空行后确认稳定前缀', () => { + expect(splitStreamingMarkdownChunks('alpha\n\n')).toEqual([ + { start: 0, content: 'alpha\n\n' }, + ]); + expect(splitStreamingMarkdownChunks('alpha\n\nbeta')).toEqual([ + { start: 0, content: 'alpha\n\n' }, + { start: 7, content: 'beta' }, + ]); + }); + + it('代码围栏和 directive 内的空行不产生边界', () => { + const markdown = [ + 'intro', + '', + '````ts', + 'const fence = "```";', + '', + 'more', + '````', + '', + 'after', + '', + ':::note', + 'inside', + '', + 'still inside', + ':::', + '', + 'tail', + ].join('\n'); + const chunks = splitStreamingMarkdownChunks(markdown); + + expect(chunks.map((chunk) => chunk.content)).toEqual([ + 'intro\n\n', + '````ts\nconst fence = "```";\n\nmore\n````\n\n', + 'after\n\n', + ':::note\ninside\n\nstill inside\n:::\n\n', + 'tail', + ]); + }); + + it('数学块内部空行不产生边界', () => { + const markdown = 'before\n\n$$\na + b\n\nc + d\n$$\n\nafter'; + expect(splitStreamingMarkdownChunks(markdown).map((chunk) => chunk.content)).toEqual([ + 'before\n\n', + '$$\na + b\n\nc + d\n$$\n\n', + 'after', + ]); + }); + + it('列表、引用和缩进续行保持在同一分片', () => { + const markdown = 'before\n\n- one\n\n- two\n\n> quote\n> next\n\nafter'; + const chunks = splitStreamingMarkdownChunks(markdown); + + expect(chunks).toEqual([ + { start: 0, content: 'before\n\n- one\n\n- two\n\n> quote\n> next\n\n' }, + { start: 38, content: 'after' }, + ]); + }); + + it('单行或多行引用式链接定义出现时保留整篇上下文', () => { + for (const markdown of [ + 'See [guide].\n\nMore text.\n\n[guide]: https://example.com', + 'See [guide].\n\nMore text.\n\n[guide]:\n https://example.com', + ]) { + expect(splitStreamingMarkdownChunks(markdown)).toEqual([ + { start: 0, content: markdown }, + ]); + } + }); + + it('多个标题或块级 HTML 出现时保留整篇解析上下文', () => { + for (const headings of ['# Same\n\nbody\n\n# same', '# foo\n\nbody\n\n# foo!']) { + expect(splitStreamingMarkdownChunks(headings)).toEqual([ + { start: 0, content: headings }, + ]); + } + + for (const htmlBlock of [ + '
    \nsummary\n\nbody\n
    \n\nafter', + '\n\nafter', + '\n\nafter', + '\n\nafter', + '\n\nafter', + ]) { + expect(splitStreamingMarkdownChunks(htmlBlock)).toEqual([ + { start: 0, content: htmlBlock }, + ]); + } + }); +}); + +function treeText(node: Root | Element): string { + return node.children + .map((child) => { + if (child.type === 'text') return child.value; + if (child.type === 'element') return treeText(child); + return ''; + }) + .join(''); +} + +describe('StreamingMarkdownChunk', () => { + it('增长尾部重解析时保留稳定前缀的解析结果与 DOM', () => { + const parsed: string[] = []; + const countParses: Plugin<[], Root> = () => (tree) => { + parsed.push(treeText(tree)); + }; + const remarkPlugins: PluggableList = []; + const rehypePlugins: PluggableList = [countParses]; + const components = {}; + + function Harness({ markdown }: { markdown: string }) { + return ( +
    + {splitStreamingMarkdownChunks(markdown).map((chunk) => ( + + ))} +
    + ); + } + + const view = render(); + const stableParagraph = view.container.querySelectorAll('p')[0]; + expect(parsed).toEqual(['stable prefix', 'live']); + + view.rerender(); + expect(parsed).toEqual(['stable prefix', 'live', 'live tail']); + expect(view.container.querySelectorAll('p')[0]).toBe(stableParagraph); + }); + + it('多个分片共享一条淡入时间线', () => { + const state = createWordFadeState(); + state.timeline.nowFn = () => 0; + const remarkPlugins: PluggableList = []; + const rehypePlugins: PluggableList = []; + const markdown = 'one two\n\nthree four'; + const { container } = render( +
    + {splitStreamingMarkdownChunks(markdown).map((chunk) => ( + + ))} +
    , + ); + + expect( + Array.from(container.querySelectorAll('[data-wf-key]'), (element) => + element.style.getPropertyValue('--wf-delay'), + ), + ).toEqual(['0ms', '16ms', '32ms', '48ms']); + }); +}); diff --git a/apps/desktop/src/renderer/components/chat/rehypeStreamWordFade.ts b/apps/desktop/src/renderer/components/chat/rehypeStreamWordFade.ts index f06f03f621..1518f97a18 100644 --- a/apps/desktop/src/renderer/components/chat/rehypeStreamWordFade.ts +++ b/apps/desktop/src/renderer/components/chat/rehypeStreamWordFade.ts @@ -1,66 +1,11 @@ /** - * rehypeStreamWordFade — 流式正文分段淡入(DESIGN.md §14.4 第五个 sanctioned - * motion class,2026-08-07)。 + * 流式正文分段淡入(DESIGN.md §14.4)。文本词段、行内 code 原子和列表圆点 + * 共用消息级连续时间线;块级结构即时出现。稳定 key 记录绝对开播时刻,结构 + * 调整导致节点重挂载时用负 delay 恢复进度,不会从头重播。 * - * 形态:流式输出时,每个新词与 inline-code 原子以 150ms opacity 淡入「浮现」。 - * 这**不是**被红线禁止的逐字打字机 —— 段整体已渲染就位,只有透明度渐变; - * 设计裁决(2026-08-07)按「浮现 ≠ 打字」放行。 - * - * 架构:CSS 管形态、JS 只管时序: - * - 本插件仅在 isStreaming 且非 reduced-motion 时挂进 rehype 链尾,把文本词段 - * 与 inline-code 原子包 ``;动画本体是 - * globals.css 的 stream-word-in(--motion-fast 淡入,`both` 填充,delay 前隐藏)。 - * - **不重播 —— 内容匹配的稳定 key**:delay 与 settled 都按段的稳定 key 记账, - * 不按文档序号。每次 parse 把本次段列表与上一次(state.previous)做匹配: - * 同位置内容相等或前缀延续(chunk 边界半个词长成整词)→ 复用旧 key;错位则 - * 按内容从后往前找未被占用的旧 key(结构变化整体平移的词都能找回自己);都 - * 没有才发新 key。markdown 结构变化(列表标记吃掉 "2. "、加粗闭合劈开文本 - * 节点、Segmenter 对 chunk 尾部的切分变化)只会让**词序号**漂移,key 不漂 —— - * 漂移序号曾让已稳定的整片前文被当新词重淡(2026-08-08 实测)。 - * - **settled 落袋 + settled 前缀还原纯文本**:span 带 data-wf-key,作为 HAST - * 到 React renderer 的逻辑身份通道;StreamFadeSpan 用该 key 控制真实 DOM - * remount,并由每个 span 的 animationend 闭包把自身 key 放进 state.settled。 - * settled 词从槽位中还原为合并后的原生文本, - * settled inline code 则恢复原始 code 节点, - * 只保留仍在播放的尾部 span——流式长文档的元素数因此回落到与无动效渲染 - * 同阶,react-markdown 每 tick 的重建 + diff 不随已播完的前文线性涨 - * (曾因全文逐词包 span,几千元素把主线程打满,流式中点击切换 session - * 无响应,2026-08-09 实测)。部分 settled 的槽位仍整槽包 span(settled 词 - * 的负 delay 已超动画时长,both 填充直接呈现终态,不重播)。抽掉 span 引 - * 发的兄弟位置 key 平移由 remount 免疫兜底(见下),不再需要空壳保位。 - * - **普通聊天零 stagger**(Codex Desktop 普通聊天同款,2026-08-21):同一 - * parse tick 新到的词段都从 0ms 开始 150ms opacity 淡入,不再跨词排队。 - * 旧实现把专用展示场景的 24ms stagger 推广到普通聊天,又让行内 code - * 完全跳过动画,导致后到的 code chip 已清晰显示、前文仍在排队的视觉反序。 - * - **remount 免疫 —— 存开播时刻、每 tick 发剩余 delay**(2026-08-08,与 - * Codex 的根本差异所要求的补偿):Codex 自研渲染器用 segmentKey 当 React - * key,span 永不 remount;我们骑在 react-markdown 上,React key 是位置 - * 序号,流式中正在生长的区域(表格尾行、成形中的列表)每 tick 都可能 - * remount。若给 span 发固定 delay,remount 会让 CSS 动画带原始 delay 从头 - * 重等,下一 tick 又 remount —— 词永远透明(2026-08-08 表格实测)。故 - * state 仍记**绝对开播时刻**,每个 tick 重新发出「开播时刻 - now」: - * 已过时刻发**负 delay**,CSS 负 animation-delay 让动画从中途续播, - * remount 后帧级跳回正确进度,观感无缝。 - * - 流式结束(isStreaming 翻 false)由 MarkdownRenderer 切回无插件的常量链, - * 终版渲染没有任何 span 包装,按消息缓存的 state 同步释放。 - * - * 原子淡入:inline code / 路径 chip 保持内部结构不拆,外层只包一个 stream-word, - * 与同 tick 正文一起从 0ms 淡入。跳过:pre 代码块与 KaTeX 子树(公式内部是 - * 几十个定位 span,逐词包装会拆坏排版)。 - * - * 淡入对象模型(Codex Desktop 架构 + inline-code 顺序修正):**只有文本词段、 - * 行内代码原子和列表圆点淡入,块级结构永远即时出现**。表格边框、引用 rail、分隔线不做 - * 块级整体淡入 —— 曾试过 tr/li 整块排队,大积压窗口下必然出现"空骨架先 - * 画好、文字憋一坨"(2026-08-08 两轮实测翻车)。表格格内文字与正文一样逐词 - * 淡入(Codex FIa 对 table 的处理同款)。列表圆点(::marker)单独处理:li 打 - * data-stream-marker,delay/key **借用 li 内第一个段**(listItemDecorationByToken - * 同构)—— 圆点与正文同帧浮现,不额外占段位,空 li 长出文字也不重播。 - * 标记用 data 属性而不是 className:MarkdownRenderer 的自定义 renderer 是 - * `className={cn(...)} {...props}` 写法,hast 塞 className 会经 spread 把样式 - * 类整个覆盖掉;data-* 从 spread 直通,互不相扰。 - * - * 切词用 Intl.Segmenter(granularity: 'word'):CJK 无空格也能按词切,避免整句 - * 中文一次性淡入退化成"逐句蹦"。空白并入前一词,不为纯空白生成 span。 + * settled 段在流式期间保留原 span 身份,只摘动画 class;稳定 Markdown 前缀 + * 由外层分片组件保留解析结果和 DOM。消息结束后切回普通 Markdown,一次性移除 + * 所有流式包装。pre 代码块与 KaTeX 子树不进入逐词处理。 */ import type { Plugin } from 'unified'; @@ -79,35 +24,71 @@ interface PreviousSegment { key: string; } +export interface WordFadeTimeline { + /** key → 绝对开播时刻(performance.now() 基准)。 */ + startAtByKey: Map; + /** 已完成淡入的 key。流式期间只摘动画 class,节点留到终态统一回收。 */ + settled: Set; + /** 下一段最早可开播的绝对时刻;同一条消息的所有 Markdown 分片共享。 */ + nextSegmentStartAtMs: number; + /** 时钟注入口,仅测试使用。 */ + nowFn?: () => number; +} + export interface WordFadeState { /** 稳定 key 发号器。 */ nextId: number; /** 上一次 parse 的淡入段列表(文档序),类型 + 内容匹配复用 key 的依据。 */ previous: PreviousSegment[]; - /** - * key → 绝对开播时刻(performance.now() 基准)。只排一次;每个 tick 由 - * 「开播时刻 - now」重新发出剩余 delay(可为负,负值 = CSS 从中途续播)。 - * 存绝对时刻而不是固定 delay,是 remount 免疫的关键(见头注释)。 - */ - startAtByKey: Map; - /** 已播完淡入的 key(animationend 落袋)。后续 parse 摘掉动画类。 */ - settled: Set; - /** 时钟注入口,仅测试用;生产恒为 performance.now。 */ - nowFn?: () => number; + /** 分片 key 前缀,保证同一条消息的并行 Markdown 分片不会撞 key。 */ + keyPrefix: string; + /** 同一条消息的连续时间线。 */ + timeline: WordFadeTimeline; + /** 稳定前缀分片与增长尾部分别维护匹配状态,但共享 timeline。 */ + sourceStateByKey: Map; } /** 与 globals.css 的 --motion-fast 保持一致,用于 animationend 丢失时的到期兜底。 */ const FADE_DURATION_MS = 150; +/** 普通流式正文的连续节奏:正常 16ms,积压到 96ms 后压缩为 4ms。 */ +export const WORD_FADE_SEGMENT_DELAY_MS = 16; +export const WORD_FADE_MAX_DELAY_MS = 96; +/** 高速突发时最多让文字等待这么久;超过窗口的段同帧淡入,避免透明内容长期占位。 */ +export const WORD_FADE_MAX_VISIBLE_DELAY_MS = 160; +const WORD_FADE_BACKLOG_STEP_MS = WORD_FADE_SEGMENT_DELAY_MS * 0.25; + +function createWordFadeTimeline(): WordFadeTimeline { + return { + startAtByKey: new Map(), + settled: new Set(), + nextSegmentStartAtMs: 0, + }; +} -export function createWordFadeState(): WordFadeState { +export function createWordFadeState( + keyPrefix = '', + timeline = createWordFadeTimeline(), +): WordFadeState { return { nextId: 0, previous: [], - startAtByKey: new Map(), - settled: new Set(), + keyPrefix, + timeline, + sourceStateByKey: new Map(), }; } +export function getOrCreateWordFadeSourceState( + owner: WordFadeState, + sourceKey: string, +): WordFadeState { + const cached = owner.sourceStateByKey.get(sourceKey); + if (cached) return cached; + const state = createWordFadeState(`s${sourceKey}-`, owner.timeline); + owner.sourceStateByKey.set(sourceKey, state); + return state; +} + /** * React render 只允许改 candidate。真正挂载后由 MarkdownRenderer 的 * useLayoutEffect 提交,被并发渲染放弃的 parse 不会偷跑稳定 key / 时间线。 @@ -116,18 +97,22 @@ export function createWordFadeCandidate(committed: WordFadeState): WordFadeState return { nextId: committed.nextId, previous: committed.previous, - startAtByKey: new Map(committed.startAtByKey), - settled: new Set(committed.settled), - nowFn: committed.nowFn, + keyPrefix: committed.keyPrefix, + timeline: { + startAtByKey: new Map(committed.timeline.startAtByKey), + settled: new Set(committed.timeline.settled), + nextSegmentStartAtMs: committed.timeline.nextSegmentStartAtMs, + nowFn: committed.timeline.nowFn, + }, + sourceStateByKey: new Map(), }; } export function commitWordFadeCandidate(committed: WordFadeState, candidate: WordFadeState): void { committed.nextId = candidate.nextId; committed.previous = candidate.previous; - committed.startAtByKey = new Map(candidate.startAtByKey); // render 到 layout-effect 之间可能正好收到上一帧的 animationend;只合并,不能覆盖。 - committed.settled = new Set([...committed.settled, ...candidate.settled]); + for (const key of candidate.timeline.settled) committed.timeline.settled.add(key); } /** @@ -169,7 +154,42 @@ export function _resetWordFadeStateCacheForTests(): void { /** animationend 闭包的落袋入口;身份来自 render 时捕获的 key,不读取可变 DOM dataset。 */ export function markWordFadeSettled(state: WordFadeState, key: string): void { - state.settled.add(key); + state.timeline.settled.add(key); +} + +export function isWordFadeSettled(state: WordFadeState, key: string): boolean { + return state.timeline.settled.has(key); +} + +function nextWordFadeStep(delayMs: number): number { + return delayMs < WORD_FADE_MAX_DELAY_MS + ? WORD_FADE_SEGMENT_DELAY_MS + : Math.max(1, WORD_FADE_BACKLOG_STEP_MS); +} + +/** + * DOM commit 阶段把段接入消息级连续时间线。新段正常每 16ms 开播;积压达到 + * 96ms 后步长压到 4ms,避免长批次排出明显等待。已见段只恢复原绝对时刻, + * remount 不重排、不重播。 + */ +export function scheduleWordFadeSegment( + state: WordFadeState, + key: string, + element: HTMLElement, +): number { + const { timeline } = state; + const nowMs = (timeline.nowFn ?? (() => performance.now()))(); + let startAt = timeline.startAtByKey.get(key); + if (startAt === undefined) { + const queuedStartAt = Math.max(timeline.nextSegmentStartAtMs, nowMs); + startAt = Math.min(queuedStartAt, nowMs + WORD_FADE_MAX_VISIBLE_DELAY_MS); + const delayMs = Math.max(Math.round(startAt - nowMs), 0); + timeline.startAtByKey.set(key, startAt); + timeline.nextSegmentStartAtMs = startAt + nextWordFadeStep(delayMs); + } + const remainingDelayMs = Math.round(startAt - nowMs); + element.style.setProperty('--wf-delay', `${remainingDelayMs}ms`); + return remainingDelayMs; } /** 整棵子树跳过(不进入):块级代码、非正文节点与公式内部结构。 */ @@ -188,9 +208,8 @@ const segmenter = : null; /** - * 切词结果 LRU 缓存(Codex Desktop 同款优化,容量同 500):流式每 tick 全文 - * 重解析,绝大多数文本节点与上个 tick 完全相同,而 Intl.Segmenter 对 CJK 分词 - * 相对昂贵 —— 无缓存时长文档每 tick 全量重切,成本随文档长度线性涨。Map 的 + * 切词结果 LRU 缓存(容量 500):增长尾部重解析时仍会反复遇到相同文本节点, + * 而 Intl.Segmenter 对 CJK 分词相对昂贵。Map 的 * 插入序即访问序近似(命中即删再插,超限逐出最老),对"稳定前文 + 生长尾部" * 的访问模式命中率接近 100%。 */ @@ -254,7 +273,7 @@ interface InlineCodeSlot { type FadeSlot = TextSlot | InlineCodeSlot; -/** 列表项圆点条目:圆点借用 li 内第一个段的 key/delay(Codex 同款)。 */ +/** 列表项圆点条目:圆点借用 li 内第一个段的 key/delay。 */ interface MarkerEntry { node: Element; /** li 内第一个段在全文档段列表中的下标;li 收集完仍相等 = 暂无内容。 */ @@ -281,8 +300,7 @@ function elementText(node: Element): string { /** * 按文档序收集文本、行内 code 槽位与列表项条目。普通文本(含表格格内)切词; * inline code 整体占一个原子段。其它结构元素不占段位 —— li 圆点记下首段的 - * 下标,之后与该段共享 key 和 delay(Codex Desktop listItemDecorationByToken - * 同构),圆点与文字永远同帧浮现,不会"圆点亮了文字干等"。 + * 下标,之后与该段共享 key 和 delay,圆点与文字永远同帧浮现。 */ function collect(node: Root | Element, out: Collected): void { const children = node.children; @@ -327,7 +345,7 @@ function collect(node: Root | Element, out: Collected): void { } /** - * 内容匹配分配稳定 key(Codex Desktop 同源思路): + * 内容匹配分配稳定 key: * 1. 同位置且内容相等 / 前缀延续(旧词是新词前缀,chunk 边界补全)→ 复用; * 2. 错位则按内容从后往前找尚未被占用的旧 key(整体平移的词各自找回); * 3. 都没有 → 发新 key。 @@ -367,26 +385,38 @@ function assignKeys(segments: FadeSegment[], state: WordFadeState): string[] { unmatched.delete(idx); return state.previous[idx].key; } - return `wf-${state.nextId++}`; + return `wf-${state.keyPrefix}${state.nextId++}`; }); state.previous = keys.map((key, i) => ({ ...segments[i], key })); return keys; } /** - * 取(必要时分配)某个 key 的开播时刻,返回**本 tick 视角的剩余 delay**。 - * 普通聊天不 stagger:同一 parse tick 的所有新 key 都以 nowMs 为开播时刻。 - * 已见 key 不重排,但每个 tick 都按「开播时刻 - now」重新发剩余 delay:开播 - * 时刻已过去则为负值,CSS 负 animation-delay 从中途续播 —— react-markdown - * 位置 key 引发的 remount 只会让动画跳回正确进度,不会从头重等(remount 免疫)。 + * ref 在 DOM commit 阶段写入真实消息级时间线;这里的值只负责首轮 React 属性。 + * 已排段按绝对时刻恢复进度,新段用当前分片内的静态估值避免首帧闪烁,ref 会在 + * paint 前把它校正为跨分片连续的 16ms / 96ms 时间线。 */ -function ensureDelay(key: string, state: WordFadeState, nowMs: number): number { - let startAt = state.startAtByKey.get(key); - if (startAt === undefined) { - startAt = nowMs; - state.startAtByKey.set(key, startAt); - } - return Math.round(startAt - nowMs); +function estimateWordFadeDelay(segmentIndex: number): number { + const rawDelay = segmentIndex * WORD_FADE_SEGMENT_DELAY_MS; + if (rawDelay <= WORD_FADE_MAX_DELAY_MS) return rawDelay; + return Math.min( + WORD_FADE_MAX_VISIBLE_DELAY_MS, + WORD_FADE_MAX_DELAY_MS + + (segmentIndex - Math.floor(WORD_FADE_MAX_DELAY_MS / WORD_FADE_SEGMENT_DELAY_MS)) * + WORD_FADE_BACKLOG_STEP_MS, + ); +} + +function renderDelay( + key: string, + state: WordFadeState, + nowMs: number, + segmentIndex: number, +): number { + const startAt = state.timeline.startAtByKey.get(key); + return startAt === undefined + ? estimateWordFadeDelay(segmentIndex) + : Math.round(startAt - nowMs); } function makeFadeNode( @@ -394,41 +424,21 @@ function makeFadeNode( key: string, state: WordFadeState, nowMs: number, + segmentIndex: number, ): ElementContent { + const settled = state.timeline.settled.has(key); return { type: 'element', tagName: 'span', properties: { - className: ['stream-word'], - style: `--wf-delay:${ensureDelay(key, state, nowMs)}ms`, + ...(settled ? {} : { className: ['stream-word'] }), + style: `--wf-delay:${renderDelay(key, state, nowMs, segmentIndex)}ms`, dataWfKey: key, }, children, }; } -/** - * 一个槽位的词是否已全部尘埃落定(animationend 落袋进 settled)。 - * 全 settled 的槽位**完全不改树**:原文本节点原样保留 —— 这是流式长文档的 - * 性能核心。settled 词若逐个包空 span,元素数随全文词数线性涨(几千 span), - * react-markdown 每 tick 重建 + React diff 全量元素,主线程被 parse tick 打满, - * 点击/切换 session 全部排不上队(2026-08-09 实测:流式中无法切换会话)。 - * 还原成纯文本后,React 元素数回落到与无动效渲染同阶;只有 150ms 动画窗口 - * 内仍在播的段才有 span。安全性依赖 remount 免疫:文本节点数量变化会让后续 - * 兄弟位置 key 平移、在播 span remount,但绝对开播时刻 + 负 delay 续播保证 - * remount 后动画进度不变(而非从头重播)。 - */ -function isSlotFullySettled(slot: FadeSlot, keys: string[], state: WordFadeState): boolean { - if (slot.kind === 'inline-code') return state.settled.has(keys[slot.segmentStart]); - let segmentIndex = slot.segmentStart; - for (const w of slot.words) { - if (!w.trim()) continue; - if (!state.settled.has(keys[segmentIndex])) return false; - segmentIndex++; - } - return true; -} - function appendPlainText(nodes: ElementContent[], value: string): void { if (!value) return; const last = nodes[nodes.length - 1]; @@ -444,7 +454,7 @@ function makeSlotNodes( ): ElementContent[] { if (slot.kind === 'inline-code') { const key = keys[slot.segmentStart]; - return [makeFadeNode([slot.node], key, state, nowMs)]; + return [makeFadeNode([slot.node], key, state, nowMs, slot.segmentStart)]; } const nodes: ElementContent[] = []; let segmentIndex = slot.segmentStart; @@ -454,14 +464,21 @@ function makeSlotNodes( continue; } const key = keys[segmentIndex++]; - if (state.settled.has(key)) appendPlainText(nodes, word); - else nodes.push(makeFadeNode([{ type: 'text', value: word }], key, state, nowMs)); + nodes.push( + makeFadeNode( + [{ type: 'text', value: word }], + key, + state, + nowMs, + segmentIndex - 1, + ), + ); } return nodes; } /** - * 给 li 挂圆点淡入(Codex Desktop fadeListDecoration 同构)。动画打在 + * 给 li 挂圆点淡入。动画打在 * `::marker` 上(CSS 见 globals.css),delay/key 借用 li 内第一个段 —— * 圆点与正文同帧浮现。li 内暂无内容(结构刚长出)→ 直接 0ms 淡入; * 内容到达后按首段 key 正常淡入,圆点已见 key 不重播。 @@ -475,8 +492,8 @@ function applyMarkerFade( ): void { const hasSegment = entry.segmentEnd > entry.segmentStart; const key = hasSegment ? keys[entry.segmentStart] : undefined; - if (key && state.settled.has(key)) return; - const delay = key ? ensureDelay(key, state, nowMs) : 0; + if (key && state.timeline.settled.has(key)) return; + const delay = key ? renderDelay(key, state, nowMs, entry.segmentStart) : 0; entry.node.properties = { ...entry.node.properties, dataStreamMarker: true, @@ -494,25 +511,20 @@ export const rehypeStreamWordFade: Plugin<[WordFadeState], Root> = (state) => { if (segments.length === 0 && markers.length === 0) return; // pass 2:类型 + 内容匹配分配稳定 key(已见段命中旧 key 拿回 delay/settled)。 const keys = assignKeys(segments, state); - // pass 3:回填 span。同 tick 新段全部 0ms 开始,旧段按绝对开播时刻续播。 + // pass 3:回填 span。ref 会在 DOM commit 阶段接入共享连续时间线。 // 槽位从后往前 splice,前面槽位的 index 不受影响。 - const nowMs = (state.nowFn ?? (() => performance.now()))(); + const nowMs = (state.timeline.nowFn ?? (() => performance.now()))(); // 结构变化会让活动 span remount,旧节点因此可能收不到 animationend。超过动画 // 时长的已开播段直接落袋,避免它永久保留活动包装。 for (const key of keys) { - const startAt = state.startAtByKey.get(key); + const startAt = state.timeline.startAtByKey.get(key); if (startAt !== undefined && nowMs - startAt >= FADE_DURATION_MS) { - state.settled.add(key); + state.timeline.settled.add(key); } } - const nodesBySlot = slots.map((slot) => { - // 全 settled 槽位不改树(性能核心,见 isSlotFullySettled 注释)。 - if (isSlotFullySettled(slot, keys, state)) return null; - return makeSlotNodes(slot, keys, state, nowMs); - }); + const nodesBySlot = slots.map((slot) => makeSlotNodes(slot, keys, state, nowMs)); for (let s = slots.length - 1; s >= 0; s--) { const nodes = nodesBySlot[s]; - if (nodes === null) continue; const slot = slots[s]; slot.parent.children.splice(slot.index, 1, ...nodes); } diff --git a/apps/desktop/src/renderer/components/chat/streamingMarkdownChunks.ts b/apps/desktop/src/renderer/components/chat/streamingMarkdownChunks.ts new file mode 100644 index 0000000000..c7d1d998a1 --- /dev/null +++ b/apps/desktop/src/renderer/components/chat/streamingMarkdownChunks.ts @@ -0,0 +1,168 @@ +export interface StreamingMarkdownChunk { + /** 原文中的稳定起点,可直接作为 React key 与淡入状态分片 key。 */ + start: number; + content: string; +} + +function isTopLevelContinuation(line: string): boolean { + return ( + /^[\t ]/.test(line) || + /^[-+*][\t ]+/.test(line) || + /^\d+\.[\t ]+/.test(line) || + line.startsWith('>') + ); +} + +interface FenceState { + marker: '`' | '~'; + length: number; +} + +function readFenceRun(line: string): { marker: '`' | '~'; length: number; rest: string } | null { + const match = /^ {0,3}(`{3,}|~{3,})(.*)$/.exec(line); + if (!match) return null; + return { + marker: match[1][0] as '`' | '~', + length: match[1].length, + rest: match[2], + }; +} + +function isDirectiveOpen(trimmedLine: string): boolean { + return /^:::[a-zA-Z]/.test(trimmedLine); +} + +function isDirectiveClose(trimmedLine: string): boolean { + return /^:::[\t ]*$/.test(trimmedLine); +} + +function hasGlobalMarkdownDefinitions(markdown: string): boolean { + // 引用式链接定义会反向影响前面的 block;一旦出现就保留整篇解析,避免稳定前缀 + // 被拆开后失去定义上下文。围栏里的误命中只会少做一次优化,不影响正确性。 + return /^ {0,3}\[[^\]\r\n]+\]:/m.test(markdown); +} + +function hasMultipleHeadings(markdown: string): boolean { + const lines = markdown.split(/\r?\n/); + let headingCount = 0; + let fence: FenceState | null = null; + + for (let index = 0; index < lines.length; index++) { + const line = lines[index]; + const fenceRun = readFenceRun(line); + if (fenceRun) { + if (!fence) fence = { marker: fenceRun.marker, length: fenceRun.length }; + else if ( + fenceRun.marker === fence.marker && + fenceRun.length >= fence.length && + fenceRun.rest.trim().length === 0 + ) { + fence = null; + } + continue; + } + if (fence) continue; + + const isAtx = /^ {0,3}#{1,6}(?:[\t ]+|$)/.test(line); + const isSetext = + line.trim().length > 0 && + index + 1 < lines.length && + /^ {0,3}(?:=+|-+)[\t ]*$/.test(lines[index + 1]); + if ((isAtx || isSetext) && ++headingCount > 1) return true; + } + return false; +} + +function hasPotentialHtmlBlock(markdown: string): boolean { + return /^ {0,3}<(?:!--|\?|!\[CDATA\[|![A-Za-z]|\/?[A-Za-z][A-Za-z0-9-]*(?:[\t />]|$))/m.test( + markdown, + ); +} + +function needsWholeDocumentContext(markdown: string): boolean { + return ( + hasGlobalMarkdownDefinitions(markdown) || + hasMultipleHeadings(markdown) || + hasPotentialHtmlBlock(markdown) + ); +} + +/** + * 把流式 Markdown 切成已经封口的顶层前缀块与仍在增长的尾块。 + * + * 分界只落在代码围栏 / directive 外的空行,并且下一条非空行必须是新的顶层块; + * 缩进、列表和引用的续行不会被拆开。这样已封口块可由 React.memo 永久复用, + * 后续 token 只会让最后一个块重新走 Markdown 解析。 + */ +export function splitStreamingMarkdownChunks(markdown: string): StreamingMarkdownChunk[] { + if ( + (!markdown.includes('\n\n') && !markdown.includes('\r\n\r\n')) || + needsWholeDocumentContext(markdown) + ) { + return [{ start: 0, content: markdown }]; + } + + const boundaries: number[] = []; + let pendingBoundary: number | null = null; + let fence: FenceState | null = null; + let mathBlock = false; + let directiveDepth = 0; + let lineStart = 0; + + while (lineStart <= markdown.length) { + const newlineIndex = markdown.indexOf('\n', lineStart); + const lineEnd = newlineIndex === -1 ? markdown.length : newlineIndex; + const nextLineStart = newlineIndex === -1 ? markdown.length : newlineIndex + 1; + const line = markdown.slice(lineStart, lineEnd).replace(/\r$/, ''); + const trimmedLine = line.trimStart(); + + if (pendingBoundary !== null && trimmedLine.length > 0) { + if (!isTopLevelContinuation(line)) boundaries.push(pendingBoundary); + pendingBoundary = null; + } + + const fenceRun = readFenceRun(line); + if (fenceRun) { + if (!fence) { + fence = { marker: fenceRun.marker, length: fenceRun.length }; + } else if ( + fenceRun.marker === fence.marker && + fenceRun.length >= fence.length && + fenceRun.rest.trim().length === 0 + ) { + fence = null; + } + } else if (!fence && directiveDepth === 0 && /^ {0,3}\$\$[\t ]*$/.test(line)) { + mathBlock = !mathBlock; + } else if (!fence && !mathBlock && isDirectiveOpen(trimmedLine)) { + directiveDepth += 1; + } else if (!fence && !mathBlock && directiveDepth > 0 && isDirectiveClose(trimmedLine)) { + directiveDepth -= 1; + } + + if ( + !fence && + !mathBlock && + directiveDepth === 0 && + trimmedLine.length === 0 && + newlineIndex !== -1 + ) { + pendingBoundary = nextLineStart; + } + + if (newlineIndex === -1) break; + lineStart = nextLineStart; + } + + if (boundaries.length === 0) return [{ start: 0, content: markdown }]; + + const chunks: StreamingMarkdownChunk[] = []; + let start = 0; + for (const boundary of boundaries) { + if (boundary <= start || boundary > markdown.length) continue; + chunks.push({ start, content: markdown.slice(start, boundary) }); + start = boundary; + } + chunks.push({ start, content: markdown.slice(start) }); + return chunks; +} diff --git a/apps/desktop/src/renderer/styles/globals.css b/apps/desktop/src/renderer/styles/globals.css index 3b4120bcc7..8eee7a4f94 100644 --- a/apps/desktop/src/renderer/styles/globals.css +++ b/apps/desktop/src/renderer/styles/globals.css @@ -60,7 +60,7 @@ /* Motion token(全局动效档位,规范见 DESIGN.md §14.4):新增过渡/动画一律 引用这些 token,不要再各处硬编码时长和 cubic-bezier。5 档交互时长 + - spinner 语义循环例外、侧栏标题阅读例外 + 3 条曲线,与三档圆角同一哲学。 */ + spinner 语义循环例外、侧栏标题阅读例外 + 4 条曲线,与三档圆角同一哲学。 */ --motion-instant: 80ms; /* hover 即时反馈、轻浮层退场 */ --motion-fast: 150ms; /* 颜色/透明度状态切换、轻浮层入场(§14.4 功能性过渡上限) */ --motion-base: 200ms; /* 尺寸变化:展开折叠、面板收展 */ @@ -71,6 +71,7 @@ --motion-ease-out: cubic-bezier(0.16, 1, 0.3, 1); /* 入场/展开 */ --motion-ease-in: cubic-bezier(0.4, 0, 1, 1); /* 退场 */ --motion-ease-move: cubic-bezier(0.4, 0, 0.2, 1); /* 位置/尺寸插值 */ + --motion-ease-stream-word: cubic-bezier(0.37, 0.55, 0.86, 0.88); /* 流式词段浮现 */ /* Claude 额度条的首帧 alias 与 ColorRegistry 一致:只复用已批准语义槽。 */ --quota-bar-fill: var(--text-secondary); @@ -1149,26 +1150,25 @@ body.resizing-pane [data-ghost-webview] { } /* 流式正文分段淡入(§14.4 第五个 sanctioned motion class,2026-08-07): - 每个新词与 inline-code 原子以 --motion-fast 从透明「浮现」。普通聊天不做 - 跨词 stagger,新段的 --wf-delay 为 0ms;已见段 remount 时可用负 delay 恢复 - 原动画进度。不是逐字打字机 —— 段整体已就位,只有 opacity 渐变; + 每个新词与 inline-code 原子以 --motion-fast 从透明「浮现」。词段沿消息级 + 连续时间线错峰进入;已见段 remount 时可用负 delay 恢复原动画进度。 + 不是逐字打字机 —— 段整体已就位,只有 opacity 渐变; pure-opacity 一次性动画,compositor-only。`both` 保证负 delay / remount 续播。 reduced-motion 双保险:JS 侧短路(插件不挂载,span 不进 DOM)+ 下方 reduce 块的 animation: none(摘掉动画连同 both 填充,span 落回自然 opacity 1, 不会钉在透明帧 —— 与「forwards/both 入场动画只能时长归零」那条坑的区别在于 这里 0% 帧之外元素本身没有 opacity 声明)。 */ .stream-word { - animation: stream-word-in var(--motion-fast) ease-out var(--wf-delay, 0ms) both; + animation: stream-word-in var(--motion-fast) var(--motion-ease-stream-word) var(--wf-delay, 0ms) both; } -/* 流式列表圆点淡入(同一 motion class 家族,2026-08-08,Codex Desktop - fadeListDecoration 同构):动画打在 ::marker 上,圆点/序号从 transparent +/* 流式列表圆点淡入(同一 motion class 家族,2026-08-08):动画打在 ::marker 上,圆点/序号从 transparent 浮现,delay 由 rehypeStreamWordFade 写入(与 li 内第一个词共享 timeline 位置,圆点与文字同帧出现)。::marker 只支持 color/font 等少数属性,故用 color 关键帧而不是 opacity。data 属性仅流式挂载;终版渲染无此属性。 */ [data-stream-marker]::marker { - animation: stream-marker-in var(--motion-fast) ease-out var(--wf-delay, 0ms) both; + animation: stream-marker-in var(--motion-fast) var(--motion-ease-stream-word) var(--wf-delay, 0ms) both; } -/* 只定义 0% 帧(Codex 同款):终点回落元素自然颜色,不用 inherit 终帧。 */ +/* 只定义 0% 帧:终点回落元素自然颜色,不用 inherit 终帧。 */ @keyframes stream-marker-in { from { color: transparent; From 9d840f926e7830a7bd3cd0198b7727e30707da4f Mon Sep 17 00:00:00 2001 From: DavidShen Date: Tue, 25 Aug 2026 18:48:59 +0800 Subject: [PATCH 2/4] =?UTF-8?q?fix(desktop):=20=E4=BF=AE=E6=AD=A3=E6=B5=81?= =?UTF-8?q?=E5=BC=8F=20Markdown=20=E8=BE=B9=E7=95=8C=E4=B8=8E=E5=8A=A8?= =?UTF-8?q?=E6=95=88=E6=9B=B2=E7=BA=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: DavidShen --- .../chat/__tests__/rehypeStreamWordFade.test.ts | 8 +++----- .../chat/__tests__/streamingMarkdownChunks.test.tsx | 10 ++++++++++ .../components/chat/streamingMarkdownChunks.ts | 4 ++-- apps/desktop/src/renderer/styles/globals.css | 7 +++---- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/apps/desktop/src/renderer/components/chat/__tests__/rehypeStreamWordFade.test.ts b/apps/desktop/src/renderer/components/chat/__tests__/rehypeStreamWordFade.test.ts index d172f5f54d..2e33a55d30 100644 --- a/apps/desktop/src/renderer/components/chat/__tests__/rehypeStreamWordFade.test.ts +++ b/apps/desktop/src/renderer/components/chat/__tests__/rehypeStreamWordFade.test.ts @@ -469,12 +469,10 @@ describe('globals.css 的 stream-word 动画本体', () => { it('引用 --motion-fast token + both 填充(delay 未到时保持透明)', () => { const rule = /\.stream-word\s*\{[\s\S]*?\}/.exec(css)?.[0] ?? ''; expect(rule).toContain('var(--motion-fast)'); - expect(rule).toContain('var(--motion-ease-stream-word)'); + expect(rule).toContain('var(--motion-ease-out)'); expect(rule).toContain('var(--wf-delay'); expect(rule).toContain('both'); - expect(css).toContain( - '--motion-ease-stream-word: cubic-bezier(0.37, 0.55, 0.86, 0.88)', - ); + expect(css).not.toContain('--motion-ease-stream-word'); }); it('关键帧只动 opacity(compositor-only,一次性非 infinite)', () => { @@ -488,7 +486,7 @@ describe('globals.css 的 stream-word 动画本体', () => { const rule = /\[data-stream-marker\]::marker\s*\{[\s\S]*?\}/.exec(css)?.[0] ?? ''; expect(rule).toContain('stream-marker-in'); expect(rule).toContain('var(--motion-fast)'); - expect(rule).toContain('var(--motion-ease-stream-word)'); + expect(rule).toContain('var(--motion-ease-out)'); expect(rule).toContain('var(--wf-delay'); expect(rule).toContain('both'); // ::marker 只支持 color/font 系属性,关键帧必须动 color 而不是 opacity。 diff --git a/apps/desktop/src/renderer/components/chat/__tests__/streamingMarkdownChunks.test.tsx b/apps/desktop/src/renderer/components/chat/__tests__/streamingMarkdownChunks.test.tsx index 41e7b04ed4..9b8b709e57 100644 --- a/apps/desktop/src/renderer/components/chat/__tests__/streamingMarkdownChunks.test.tsx +++ b/apps/desktop/src/renderer/components/chat/__tests__/streamingMarkdownChunks.test.tsx @@ -72,10 +72,20 @@ describe('splitStreamingMarkdownChunks', () => { ]); }); + it('括号式有序列表续项保持在同一分片', () => { + const markdown = 'before\n\n1) one\n\n2) two\n\nafter'; + + expect(splitStreamingMarkdownChunks(markdown)).toEqual([ + { start: 0, content: 'before\n\n1) one\n\n2) two\n\n' }, + { start: 24, content: 'after' }, + ]); + }); + it('单行或多行引用式链接定义出现时保留整篇上下文', () => { for (const markdown of [ 'See [guide].\n\nMore text.\n\n[guide]: https://example.com', 'See [guide].\n\nMore text.\n\n[guide]:\n https://example.com', + 'See [foo\\]bar].\n\nMore text.\n\n[foo\\]bar]: https://example.com', ]) { expect(splitStreamingMarkdownChunks(markdown)).toEqual([ { start: 0, content: markdown }, diff --git a/apps/desktop/src/renderer/components/chat/streamingMarkdownChunks.ts b/apps/desktop/src/renderer/components/chat/streamingMarkdownChunks.ts index c7d1d998a1..f2ec6a0305 100644 --- a/apps/desktop/src/renderer/components/chat/streamingMarkdownChunks.ts +++ b/apps/desktop/src/renderer/components/chat/streamingMarkdownChunks.ts @@ -8,7 +8,7 @@ function isTopLevelContinuation(line: string): boolean { return ( /^[\t ]/.test(line) || /^[-+*][\t ]+/.test(line) || - /^\d+\.[\t ]+/.test(line) || + /^\d{1,9}[.)][\t ]+/.test(line) || line.startsWith('>') ); } @@ -39,7 +39,7 @@ function isDirectiveClose(trimmedLine: string): boolean { function hasGlobalMarkdownDefinitions(markdown: string): boolean { // 引用式链接定义会反向影响前面的 block;一旦出现就保留整篇解析,避免稳定前缀 // 被拆开后失去定义上下文。围栏里的误命中只会少做一次优化,不影响正确性。 - return /^ {0,3}\[[^\]\r\n]+\]:/m.test(markdown); + return /^ {0,3}\[(?:\\[^\r\n]|[^\]\\\r\n])+\]:/m.test(markdown); } function hasMultipleHeadings(markdown: string): boolean { diff --git a/apps/desktop/src/renderer/styles/globals.css b/apps/desktop/src/renderer/styles/globals.css index 8eee7a4f94..071995ebfb 100644 --- a/apps/desktop/src/renderer/styles/globals.css +++ b/apps/desktop/src/renderer/styles/globals.css @@ -60,7 +60,7 @@ /* Motion token(全局动效档位,规范见 DESIGN.md §14.4):新增过渡/动画一律 引用这些 token,不要再各处硬编码时长和 cubic-bezier。5 档交互时长 + - spinner 语义循环例外、侧栏标题阅读例外 + 4 条曲线,与三档圆角同一哲学。 */ + spinner 语义循环例外、侧栏标题阅读例外 + 3 条曲线,与三档圆角同一哲学。 */ --motion-instant: 80ms; /* hover 即时反馈、轻浮层退场 */ --motion-fast: 150ms; /* 颜色/透明度状态切换、轻浮层入场(§14.4 功能性过渡上限) */ --motion-base: 200ms; /* 尺寸变化:展开折叠、面板收展 */ @@ -71,7 +71,6 @@ --motion-ease-out: cubic-bezier(0.16, 1, 0.3, 1); /* 入场/展开 */ --motion-ease-in: cubic-bezier(0.4, 0, 1, 1); /* 退场 */ --motion-ease-move: cubic-bezier(0.4, 0, 0.2, 1); /* 位置/尺寸插值 */ - --motion-ease-stream-word: cubic-bezier(0.37, 0.55, 0.86, 0.88); /* 流式词段浮现 */ /* Claude 额度条的首帧 alias 与 ColorRegistry 一致:只复用已批准语义槽。 */ --quota-bar-fill: var(--text-secondary); @@ -1159,14 +1158,14 @@ body.resizing-pane [data-ghost-webview] { 不会钉在透明帧 —— 与「forwards/both 入场动画只能时长归零」那条坑的区别在于 这里 0% 帧之外元素本身没有 opacity 声明)。 */ .stream-word { - animation: stream-word-in var(--motion-fast) var(--motion-ease-stream-word) var(--wf-delay, 0ms) both; + animation: stream-word-in var(--motion-fast) var(--motion-ease-out) var(--wf-delay, 0ms) both; } /* 流式列表圆点淡入(同一 motion class 家族,2026-08-08):动画打在 ::marker 上,圆点/序号从 transparent 浮现,delay 由 rehypeStreamWordFade 写入(与 li 内第一个词共享 timeline 位置,圆点与文字同帧出现)。::marker 只支持 color/font 等少数属性,故用 color 关键帧而不是 opacity。data 属性仅流式挂载;终版渲染无此属性。 */ [data-stream-marker]::marker { - animation: stream-marker-in var(--motion-fast) var(--motion-ease-stream-word) var(--wf-delay, 0ms) both; + animation: stream-marker-in var(--motion-fast) var(--motion-ease-out) var(--wf-delay, 0ms) both; } /* 只定义 0% 帧:终点回落元素自然颜色,不用 inherit 终帧。 */ @keyframes stream-marker-in { From a93cf75c211d9f28e355d3105ceca83033e2ce56 Mon Sep 17 00:00:00 2001 From: DavidShen Date: Tue, 25 Aug 2026 19:06:46 +0800 Subject: [PATCH 3/4] =?UTF-8?q?fix(desktop):=20=E9=81=BF=E5=85=8D=E6=B5=81?= =?UTF-8?q?=E5=BC=8F=E6=95=B4=E7=AF=87=E5=9B=9E=E9=80=80=E9=87=8D=E6=92=AD?= =?UTF-8?q?=E6=AD=A3=E6=96=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: DavidShen --- .../components/chat/MarkdownRenderer.tsx | 1 + .../chat/StreamingMarkdownChunk.tsx | 16 ++++-- .../streamingMarkdownChunks.test.tsx | 51 +++++++++++++++++++ .../components/chat/rehypeStreamWordFade.ts | 27 ++++++++++ 4 files changed, 92 insertions(+), 3 deletions(-) diff --git a/apps/desktop/src/renderer/components/chat/MarkdownRenderer.tsx b/apps/desktop/src/renderer/components/chat/MarkdownRenderer.tsx index be06b85394..11210053c4 100644 --- a/apps/desktop/src/renderer/components/chat/MarkdownRenderer.tsx +++ b/apps/desktop/src/renderer/components/chat/MarkdownRenderer.tsx @@ -1886,6 +1886,7 @@ export const MarkdownRenderer = memo(function MarkdownRenderer({ } wordFadeState={wordFadeState} emitSourceLines={emitSourceLines} + wholeDocument={streamingChunks.length === 1} /> )) ) : ( diff --git a/apps/desktop/src/renderer/components/chat/StreamingMarkdownChunk.tsx b/apps/desktop/src/renderer/components/chat/StreamingMarkdownChunk.tsx index 88d62106f9..fa5604d6f0 100644 --- a/apps/desktop/src/renderer/components/chat/StreamingMarkdownChunk.tsx +++ b/apps/desktop/src/renderer/components/chat/StreamingMarkdownChunk.tsx @@ -6,8 +6,10 @@ import type { PluggableList } from 'unified'; import { commitWordFadeCandidate, createWordFadeCandidate, + createWholeDocumentWordFadeCandidate, getOrCreateWordFadeSourceState, rehypeStreamWordFade, + retainOnlyWordFadeSourceState, type WordFadeState, } from './rehypeStreamWordFade'; import { StreamFadeListItem, StreamFadeSpan } from './StreamFadeSpan'; @@ -21,6 +23,7 @@ interface StreamingMarkdownChunkProps { urlTransform?: UrlTransform; wordFadeState: WordFadeState | null; emitSourceLines: boolean; + wholeDocument?: boolean; } function sourceLineAttr(node?: Element): { 'data-source-line'?: number } { @@ -41,6 +44,7 @@ export const StreamingMarkdownChunk = memo(function StreamingMarkdownChunk({ urlTransform, wordFadeState, emitSourceLines, + wholeDocument = false, }: StreamingMarkdownChunkProps) { const sourceWordFadeState = useMemo( () => @@ -49,18 +53,24 @@ export const StreamingMarkdownChunk = memo(function StreamingMarkdownChunk({ ); const wordFade = useMemo(() => { if (!sourceWordFadeState) return null; - const candidate = createWordFadeCandidate(sourceWordFadeState); + const candidate = + wholeDocument && wordFadeState + ? createWholeDocumentWordFadeCandidate(wordFadeState, sourceWordFadeState) + : createWordFadeCandidate(sourceWordFadeState); return { candidate, plugins: [...rehypePlugins, [rehypeStreamWordFade, candidate]] as PluggableList, }; - }, [content, rehypePlugins, sourceWordFadeState]); + }, [content, rehypePlugins, sourceWordFadeState, wholeDocument, wordFadeState]); useLayoutEffect(() => { if (sourceWordFadeState && wordFade) { commitWordFadeCandidate(sourceWordFadeState, wordFade.candidate); + if (wholeDocument && wordFadeState) { + retainOnlyWordFadeSourceState(wordFadeState, sourceKey); + } } - }, [sourceWordFadeState, wordFade]); + }, [sourceKey, sourceWordFadeState, wholeDocument, wordFade, wordFadeState]); const chunkComponents = useMemo(() => { if (!sourceWordFadeState) return components; diff --git a/apps/desktop/src/renderer/components/chat/__tests__/streamingMarkdownChunks.test.tsx b/apps/desktop/src/renderer/components/chat/__tests__/streamingMarkdownChunks.test.tsx index 9b8b709e57..31ff2f9f1a 100644 --- a/apps/desktop/src/renderer/components/chat/__tests__/streamingMarkdownChunks.test.tsx +++ b/apps/desktop/src/renderer/components/chat/__tests__/streamingMarkdownChunks.test.tsx @@ -191,4 +191,55 @@ describe('StreamingMarkdownChunk', () => { ), ).toEqual(['0ms', '16ms', '32ms', '48ms']); }); + + it('全局上下文晚到并回退整篇解析时不重播已完成分片', () => { + const state = createWordFadeState(); + state.timeline.nowFn = () => 0; + const remarkPlugins: PluggableList = []; + const rehypePlugins: PluggableList = []; + + function Harness({ markdown }: { markdown: string }) { + const chunks = splitStreamingMarkdownChunks(markdown); + return ( +
    + {chunks.map((chunk) => ( + + ))} +
    + ); + } + + const initial = '# First\n\nstable prefix\n\nlive tail'; + const view = render(); + const firstFrame = Array.from( + view.container.querySelectorAll('[data-wf-key]'), + ); + const completedKeys = firstFrame.map((span) => span.dataset.wfKey!); + expect(state.sourceStateByKey.size).toBeGreaterThan(1); + for (const key of completedKeys) state.timeline.settled.add(key); + + view.rerender(); + + const secondFrame = Array.from( + view.container.querySelectorAll('[data-wf-key]'), + ); + const byKey = new Map(secondFrame.map((span) => [span.dataset.wfKey!, span])); + expect(completedKeys.every((key) => byKey.has(key))).toBe(true); + expect(completedKeys.every((key) => !byKey.get(key)!.classList.contains('stream-word'))).toBe( + true, + ); + expect(secondFrame.filter((span) => span.classList.contains('stream-word'))).toHaveLength(1); + expect(secondFrame.at(-1)?.textContent).toBe('Second'); + expect(state.sourceStateByKey.size).toBe(1); + }); }); diff --git a/apps/desktop/src/renderer/components/chat/rehypeStreamWordFade.ts b/apps/desktop/src/renderer/components/chat/rehypeStreamWordFade.ts index 1518f97a18..22c841ba93 100644 --- a/apps/desktop/src/renderer/components/chat/rehypeStreamWordFade.ts +++ b/apps/desktop/src/renderer/components/chat/rehypeStreamWordFade.ts @@ -108,6 +108,33 @@ export function createWordFadeCandidate(committed: WordFadeState): WordFadeState }; } +/** + * 全局 Markdown 上下文晚到时,外层会从多个稳定分片回退成 start=0 的整篇解析。 + * 这里按原文起点合并各分片的匹配历史,让整篇 candidate 继续认出已经播放过的段; + * 只读 committed state,不在 React render 阶段发布状态。 + */ +export function createWholeDocumentWordFadeCandidate( + owner: WordFadeState, + committed: WordFadeState, +): WordFadeState { + const candidate = createWordFadeCandidate(committed); + const previous = [...owner.sourceStateByKey.entries()] + .sort(([left], [right]) => Number(left) - Number(right)) + .flatMap(([, sourceState]) => sourceState.previous); + if (previous.length > 0) candidate.previous = previous; + return candidate; +} + +/** 整篇 candidate 落袋后移除旧分片匹配状态;共享 timeline 仍由保留项持有。 */ +export function retainOnlyWordFadeSourceState( + owner: WordFadeState, + sourceKey: string, +): void { + for (const key of owner.sourceStateByKey.keys()) { + if (key !== sourceKey) owner.sourceStateByKey.delete(key); + } +} + export function commitWordFadeCandidate(committed: WordFadeState, candidate: WordFadeState): void { committed.nextId = candidate.nextId; committed.previous = candidate.previous; From 5b6cc5e4a08a7143c03e89075828b031028dcd05 Mon Sep 17 00:00:00 2001 From: DavidShen Date: Tue, 25 Aug 2026 21:17:21 +0800 Subject: [PATCH 4/4] docs(design): align stream fade timing Signed-off-by: DavidShen --- docs/design-rules/DESIGN.md | 4 ++-- .../pr-3402-burst-contact-sheet.png | Bin 0 -> 183736 bytes 2 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 docs/design-rules/assets/stream-word-fade/pr-3402-burst-contact-sheet.png diff --git a/docs/design-rules/DESIGN.md b/docs/design-rules/DESIGN.md index 51950fe352..db2fbd86bd 100644 --- a/docs/design-rules/DESIGN.md +++ b/docs/design-rules/DESIGN.md @@ -684,9 +684,9 @@ _Reference-implementation paths in this table are relative to `apps/desktop/src/ - **Definition**: the seal's signature running animation (two gapped arcs, outer 83/17 + inner 39/61, spinning as a whole at 2.4s linear on an HTML wrapper) is a beloved product signature and is preserved verbatim. What was missing was an ending: when the turn stops, the gaps used to freeze in place and read as "stuck mid-load". The choreography gives the animation a curtain call instead of replacing it: **while still spinning, both arcs close their gaps into full circles** (600ms one-shot `stroke-dasharray` transition — permitted as a one-shot transient per the red lines above); when the turn actually issued a `ghost_call` (fulfilled), a success-colored halo ring then dilates outward once (`summon-seal-halo`, 0.9s scale 1→1.65 + fade) while a ✓ badge pops at the avatar's corner (reusing the Done semantic's sanctioned overshoot curve, same as `status-done-pop`); the spin is removed only after the circles are closed — a spinning full circle is visually static, so the removal is seamless. Semantics stay self-consistent: gap = in progress, full circle = turn finished; ✓/halo appear **only on a fulfilled call** — a cancelled/never-issued call closes neutrally with no success badge (the ring must not fake success). - **Parameter red lines**: close 600ms (`--motion-ease-out`; outside the duration tiers — sanctioned here, do not reuse elsewhere), halo 0.9s one-shot, tick pop 0.3s. Historic messages mount directly on the closed static frame with zero animation; the choreography plays only for a mount that actually witnessed `running` flip false. Halo/badge fill comes from the status-dot done-green exemption (`--card-status-done`); the ✓ glyph uses `--completion-badge-fg` (dark ink both modes, 5.29:1 on the done green — white would be 2.88:1, below the WCAG 1.4.11 non-text 3:1 floor). All keyframes are in the `prefers-reduced-motion` whitelist — reduced motion lands straight on the terminal frame. - **Scope boundary**: this seal only. The closing-ring language must not leak into other spinners or progress indicators. Implementation: `apps/desktop/src/renderer/components/chat/GhostSummonCard.tsx` + `.summon-seal-*` in `globals.css`. -- **Stream-word fade(流式正文分段淡入)** — the fifth sanctioned motion class (approved 2026-08-07, ordinary-chat timing aligned 2026-08-21), **only** for streaming assistant prose in the chat message flow: +- **Stream-word fade(流式正文分段淡入)** — the fifth sanctioned motion class (approved 2026-08-07, ordinary-chat timing aligned 2026-08-25), **only** for streaming assistant prose in the chat message flow: - **Definition**: during streaming, each newly-arrived text **word** and each inline-code atom fades in over `--motion-fast` (150ms, opacity only) instead of popping in — content appears to *surface*, not *type*. This is explicitly **not** the forbidden per-character typewriter: each segment is fully rendered and positioned from its first frame; only opacity ramps. CJK is segmented per word via `Intl.Segmenter`; an inline-code chip keeps its internal structure and fades as one indivisible segment. - - **Architecture red lines**: CSS owns the form, JS owns only the timing — `rehypeStreamWordFade` wraps words and inline-code atoms in `span.stream-word` and writes `--wf-delay`. Ordinary chat has **no cross-segment stagger**: every segment newly received in one render starts at `0ms`, matching Codex Desktop's normal-chat default and preventing later semantic atoms from overtaking queued prose. Segments are keyed by type + stable content matching (same-position matches, text-prefix continuation, and bounded backward matching), not by document index. Render computes a candidate state; only a committed DOM render may publish it from a layout effect. Each segment keeps an absolute start time / remaining delay across re-parse and remount, so already-seen content never replays. The animation itself is a pure-opacity one-shot keyframe in `globals.css` (compositor-only). The final (non-streaming) render carries **zero** wrapper spans — the plugin only mounts while `isStreaming`. + - **Architecture red lines**: CSS owns the form, JS owns only the timing — `rehypeStreamWordFade` wraps words and inline-code atoms in `span.stream-word` and writes `--wf-delay`. Ordinary chat uses one **message-level continuous timeline** across render batches and Markdown chunks: new segments normally start `16ms` apart; once queued transparent wait reaches `96ms`, the step compresses to `4ms`; no segment may remain transparent for more than `160ms`. This keeps a burst from flashing in all at once without allowing a long batch to leave a visible transparent hole. Inline-code atoms and list markers borrow the corresponding prose segment's key and delay, so semantic atoms cannot overtake their text. Segments are keyed by type + stable content matching (same-position matches, text-prefix continuation, and bounded backward matching), not by document index. Render computes a candidate state; only a committed DOM render may publish it from a layout effect. Each segment keeps an absolute start time / remaining delay across re-parse and remount, so already-seen content never replays. The animation itself is a pure-opacity one-shot keyframe in `globals.css` (compositor-only). The final (non-streaming) render carries **zero** wrapper spans — the plugin only mounts while `isStreaming`. - **Degradation**: `prefers-reduced-motion` short-circuits in JS (plugin not mounted, no spans in DOM) *and* the CSS reduce block strips the animation — double coverage, and the CSS side is safe because `.stream-word` has no own opacity declaration to get stuck on. - **Scope boundary**: streaming chat prose only. Inline `code` participates as one atomic segment; fenced `pre` code blocks and KaTeX subtrees are skipped. Must not be applied to static content, titles, toasts, or any non-streamed text. Implementation: `apps/desktop/src/renderer/components/chat/rehypeStreamWordFade.ts` + `.stream-word` in `globals.css`; tests: `rehypeStreamWordFade.test.ts`. - **Session-loading wordmark sheen(会话加载字标扫光)** — the sixth sanctioned motion class (approved 2026-08-10), **only** for Desktop `BrandLoadingMark` while `MessageStream` defers its message tree during a session switch: diff --git a/docs/design-rules/assets/stream-word-fade/pr-3402-burst-contact-sheet.png b/docs/design-rules/assets/stream-word-fade/pr-3402-burst-contact-sheet.png new file mode 100644 index 0000000000000000000000000000000000000000..0abad9175a2853f9b6d63dd21ad9a0b0615c587f GIT binary patch literal 183736 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 H=J&q