Animation: CJK paragraphs only animate their first batch, and queued words pop in when isAnimating turns false
Los mantenedores suelen responder en 1 día
Evaluación
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Aptitud para principiantes
- 35/100
- Tipo de issue
- Error
- Claridad
- Bastante claro
- Estado de actividad
- Activo
- Stack tecnológico
- react, typescript
- Área
- frontend
Línea de trabajo
Start with the word-splitting in the animate plugin behind createAnimatePlugin and its sep option, which splits text nodes on /\s/. Run the repro script from the issue with sep: "word" and sep: "char" to see the unanimated span output. The second problem is in how Streamdown drops the animate plugin when isAnimating turns false, so read the per-block plugin list there. Done means CJK paragraphs animate fully and the trailing stagger completes before the spans are removed; the issue's open design questions need a maintainer decision first.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Two problems with animated streaming in [email protected], both visible in Chinese/Japanese chat replies.
1. sep: "word" treats a whole CJK paragraph as one word, so most of it never animates
sep: "word" splits text nodes at whitespace (/\s/). CJK text has no spaces between words, so a paragraph is a single "word". A word that started before prevContentLength is treated as already rendered (duration 0ms), so every character appended to it after the first commit appears with no animation.
Repro with the exported createAnimatePlugin, simulating four streaming commits of one paragraph:
import { createAnimatePlugin } from "streamdown";
import { unified } from "unified";
import remarkParse from "remark-parse";
import remarkRehype from "remark-rehype";
const full = "量子電腦利用疊加與糾纏來平行處理資訊,因此在特定問題上遠快於傳統電腦。不過目前的硬體仍然很容易受到雜訊干擾。";
for (const sep of ["word", "char"]) {
const plugin = createAnimatePlugin({ sep });
let prev = 0;
for (const n of [12, 25, 40, full.length]) {
plugin.setPrevContentLength(prev);
const md = full.slice(0, n);
const tree = await unified().use(remarkParse).use(remarkRehype).use(plugin.rehypePlugin)
.run(unified().use(remarkParse).parse(md));
const spans = tree.children[0].children.filter((c) => c.tagName === "span");
const animated = spans.filter((s) => !s.properties.style.includes("--sd-duration:0ms"));
console.log(sep, n, animated.map((s) => s.children[0].value).join("|"));
prev = n;
}
}
word 12 量子電腦利用疊加與糾纏來
word 25 <- nothing animates
word 40
word 54
char 12 量|子|電|腦|利|用|疊|加|與|糾|纏|來
char 25 平|行|處|理|資|訊|,|因|此|在|特|定|問
...
So with the default sep, a CJK reply fades in its first few characters and then pops in for the rest of each paragraph. sep: "char" works, but turns Latin text into a per-letter typewriter, so mixed-language replies have no good setting.
Suggestion: split words with Intl.Segmenter(undefined, { granularity: "word" }) (falling back to whitespace where unavailable). It segments CJK into words and leaves Latin text as today. The @streamdown/cjk plugin doesn't touch the animate plugin, so it doesn't help here.
2. Characters still waiting for their stagger appear all at once when isAnimating turns false
With stagger (default 40ms) the shared timeline schedules new words up to maxBacklogMs (default 320ms) ahead of wall-clock. When the stream ends and isAnimating becomes false, Streamdown stops adding the animate plugin to each block's rehype plugins immediately, so every block re-renders without the data-sd-animate spans. Words still waiting on --sd-delay, plus those mid-fade, become fully visible in that frame. The end of every reply visibly "pops", more so with sep: "char" or a fast model, where the backlog is longest.
Expected: after isAnimating goes false, keep the animate plugin until the timeline's last scheduled delay plus duration has elapsed, then drop the spans. We currently work around it by holding isAnimating true for maxBacklogMs + duration after the stream ends.
Also
stagger and maxBacklogMs are real AnimateOptions (in the types and source) but aren't on https://streamdown.ai/docs/animation. We had set stagger: 0 without knowing it disables the per-batch spread, which made streaming look chunky.
Environment: streamdown 2.7.0, React 19.3. Issue 1 reproduced with the script above; issue 2 is from reading dist/chunk-*.js (the per-block plugin list only includes the animate plugin while isAnimating).
- Lenguaje dominante
- TypeScript
- Estrellas
- 5.7k
- Forks
- 307
- Merge medio
- 1 d 19 h
- PR fusionados (30 d)
- 24
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Tiene una plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de vercel/streamdown
-
remend: unclosed `$$` in a table cell is closed on a new line when an earlier line has mathPosiblemente ocupada @orinnz la tomó hace 14 días. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 85/100
vercel/streamdown#652 ·
Los mantenedores suelen responder en 1 día
-
The word-fade engine ships with every app, whether or not it animatesPosiblemente ocupada @xyrolle la tomó hoy. Abierto
Dificultad 4/5 3-5 días Aptitud para principiantes 1/100
vercel/streamdown#681 ·
Los mantenedores suelen responder en 1 día
-
Mermaid's UI ships in the main bundle although the chart is lazy-loadedPosiblemente ocupada @xyrolle la tomó hoy. Abierto
Dificultad 4/5 3-5 días Aptitud para principiantes 20/100
vercel/streamdown#679 ·
Los mantenedores suelen responder en 1 día
-
CJK autolink boundary handling keeps `**` in the URL when a bold URL is followed by CJK punctuationPosiblemente ocupada @mikemikimike la tomó hace 2 días. Abiertobug
Dificultad 3/5 Medio día Aptitud para principiantes 50/100
vercel/streamdown#672 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Mermaid shows an error for a partial chart while its fence is still streamingPosiblemente ocupada @slavco86 la tomó hace 3 días. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 25/100
vercel/streamdown#667 · 1 comentario ·
Los mantenedores suelen responder en 1 día
Todos los issues de vercel/streamdown
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 73/100
anomalyco/opencode#56380 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
enhancement
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
-
BUG | `MCP 服务器` | 全局层行的停用/启用与删除报 404 server row not found,编辑保存会在 profile 层复制出重复行Posiblemente ocupada @w6242652 la tomó hoy. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 75/100
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
Chocobozzz/PeerTube#7832 ·
Los mantenedores suelen responder en 3 días
-
Pay to use?Abierto
Dificultad 1/5 Menos de una hora Aptitud para principiantes 66/100
darkreader/darkreader#16108 · 1 comentario ·
Los mantenedores suelen responder en 2 días