()
| 205 | } |
| 206 | |
| 207 | export default function App() { |
| 208 | const [msgs, setMsgs] = useState<Message[]>([ |
| 209 | { |
| 210 | id: "init", |
| 211 | role: "ai", |
| 212 | done: true, |
| 213 | content: |
| 214 | '<div class="callout info"><strong>27 examples</strong> on the right — start with <strong>★ Ultimate showcase</strong> under Showcase, or browse <strong>Claude</strong> edge cases. Tune speed on the left.</div>', |
| 215 | }, |
| 216 | ]); |
| 217 | const [busy, setBusy] = useState(false); |
| 218 | const [category, setCategory] = useState<string>("Showcase"); |
| 219 | const [activeExId, setActiveExId] = useState<string | null>(null); |
| 220 | const [preset, setPreset] = useState(DEFAULT_PRESET); |
| 221 | const [chunkSize, setChunkSize] = useState(14); |
| 222 | const [intervalMs, setIntervalMs] = useState(18); |
| 223 | const [jitterMs, setJitterMs] = useState(0); |
| 224 | const [streamProgress, setStreamProgress] = useState<{ cur: number; total: number } | null>(null); |
| 225 | |
| 226 | const timer = useRef<ReturnType<typeof setTimeout>>(null); |
| 227 | const msgsRef = useRef<HTMLDivElement>(null); |
| 228 | const userScrolledUpRef = useRef(false); |
| 229 | const streamParams = useRef<StreamParams>({ chunkSize, intervalMs, jitterMs }); |
| 230 | |
| 231 | streamParams.current = { chunkSize, intervalMs, jitterMs }; |
| 232 | |
| 233 | useEffect( |
| 234 | () => () => { |
| 235 | if (timer.current) clearTimeout(timer.current); |
| 236 | }, |
| 237 | [], |
| 238 | ); |
| 239 | |
| 240 | useEffect(() => { |
| 241 | const el = msgsRef.current; |
| 242 | if (!el) { |
| 243 | return; |
| 244 | } |
| 245 | let touchStartY = 0; |
| 246 | |
| 247 | const onWheel = (e: WheelEvent) => { |
| 248 | if (e.deltaY < 0) { |
| 249 | userScrolledUpRef.current = true; |
| 250 | } |
| 251 | }; |
| 252 | const onTouchStart = (e: TouchEvent) => { |
| 253 | touchStartY = e.touches[0]?.clientY ?? 0; |
| 254 | }; |
| 255 | const onTouchMove = (e: TouchEvent) => { |
| 256 | const y = e.touches[0]?.clientY ?? 0; |
| 257 | if (y > touchStartY + 12) { |
| 258 | userScrolledUpRef.current = true; |
| 259 | } |
| 260 | }; |
| 261 | |
| 262 | el.addEventListener("wheel", onWheel, { passive: true }); |
| 263 | el.addEventListener("touchstart", onTouchStart, { passive: true }); |
| 264 | el.addEventListener("touchmove", onTouchMove, { passive: true }); |
nothing calls this directly
no test coverage detected