/** * Materialises freshly streamed text: every batch of characters appended to * the growing tail block enters through a blur that resolves as it settles. * * The hard part is that the tail block is re-rendered wholesale (`v-html`) * on every delta, which destroys any wrapper spans mid-animation. The fix is * to make the animation *stateless in the DOM*: chunks are registered by * character offset with their arrival time, and after every patch the still- * animating chunks are re-wrapped with a negative `animation-delay`, so each * one resumes exactly where the patch interrupted it. (A per-delta re-fade of * the whole tail element was tried long ago and retired — this is the version * that animates only what actually arrived.) */ export interface FreshChunksOptions { /** How long a chunk animates; older chunks stop being re-wrapped. */ durationMs?: number; /** Class applied to the wrapper spans. */ className?: string; /** Injectable clock, for tests. */ now?: () => number; } export interface FreshChunks { /** * Re-syncs against the element's current text and wraps still-animating * chunks. Call after every DOM patch of the tracked tail block, post-flush * (the wrap must land in the same frame as the patch, before paint). */ update: (el: HTMLElement, blockId: number | string) => void; /** * Adopts the element's current text as already shown: nothing in it * animates, and only what is appended after this call counts as fresh. For * content that was on screen before the tracker existed. */ seed: (el: HTMLElement, blockId: number | string) => void; /** Unwraps every fresh span under `root` and forgets all chunks. */ finish: (root: ParentNode) => void; reset: () => void; } export declare function createFreshChunks(options?: FreshChunksOptions): FreshChunks;