(getValue: () => string, live?: () => boolean)
| 255 | } |
| 256 | |
| 257 | function createPacedValue(getValue: () => string, live?: () => boolean) { |
| 258 | const [value, setValue] = createSignal(getValue()) |
| 259 | let shown = getValue() |
| 260 | let timeout: ReturnType<typeof setTimeout> | undefined |
| 261 | |
| 262 | const clear = () => { |
| 263 | if (!timeout) return |
| 264 | clearTimeout(timeout) |
| 265 | timeout = undefined |
| 266 | } |
| 267 | |
| 268 | const sync = (text: string) => { |
| 269 | shown = text |
| 270 | setValue(text) |
| 271 | } |
| 272 | |
| 273 | const run = () => { |
| 274 | timeout = undefined |
| 275 | const text = getValue() |
| 276 | if (!live?.()) { |
| 277 | sync(text) |
| 278 | return |
| 279 | } |
| 280 | if (!text.startsWith(shown) || text.length <= shown.length) { |
| 281 | sync(text) |
| 282 | return |
| 283 | } |
| 284 | if (text.length - shown.length <= TEXT_RENDER_IMMEDIATE) { |
| 285 | sync(text) |
| 286 | return |
| 287 | } |
| 288 | const end = next(text, shown.length) |
| 289 | sync(text.slice(0, end)) |
| 290 | if (end < text.length) timeout = setTimeout(run, TEXT_RENDER_PACE_MS) |
| 291 | } |
| 292 | |
| 293 | createEffect(() => { |
| 294 | const text = getValue() |
| 295 | if (!live?.()) { |
| 296 | clear() |
| 297 | sync(text) |
| 298 | return |
| 299 | } |
| 300 | if (!text.startsWith(shown) || text.length < shown.length) { |
| 301 | clear() |
| 302 | sync(text) |
| 303 | return |
| 304 | } |
| 305 | if (text.length - shown.length <= TEXT_RENDER_IMMEDIATE) { |
| 306 | clear() |
| 307 | sync(text) |
| 308 | return |
| 309 | } |
| 310 | if (text.length === shown.length || timeout) return |
| 311 | timeout = setTimeout(run, TEXT_RENDER_PACE_MS) |
| 312 | }) |
| 313 | |
| 314 | onCleanup(() => { |
no test coverage detected