feat(miner-ui): useStreamingText hook + StreamingText renderer - #6573
feat(miner-ui): useStreamingText hook + StreamingText renderer#6573jaytbarimbao-collab wants to merge 1 commit into
Conversation
The first streaming-text primitive for the miner-ui chat rail (JSONbored#6516): progressively reveal a chat response's text as chunks arrive, instead of popping the whole message in at once. Unwired plumbing only — no backend, no route, exercised solely against mock chunk sources in tests. - lib/use-streaming-text.ts — useStreamingText(source): consumes a ChunkSource (a () => AsyncIterable<string> factory, exported by name), accumulating each chunk into text with an idle/streaming/done/error/ cancelled status and a cancel(). Mirrors usePolledFetch's cancelled-flag discipline: a chunk resolving after a new source starts, after cancel(), or after unmount never touches state; a mid-stream throw/reject surfaces via status/error, never as an unhandled rejection. - components/streaming-text.tsx — thin <StreamingText> renderer showing the accumulated text with a caret while streaming, suppressed under prefers-reduced-motion (detected via window.matchMedia + a change listener, the use-mobile.tsx technique — no motion dependency added). Tests (co-located flat in src/, mirroring use-polled-fetch.test.ts) cover every status transition — incremental accumulation, cancel-on-new-source, cancel-on-unmount (no late write), and the error path — plus the renderer's full-motion caret vs reduced-motion suppression. Local @loopover/ui-miner gate green: typecheck + 192 tests (coverage 88/86/81/90, above the 85/85/75/85 threshold) + eslint (0 errors). Closes JSONbored#6516
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
|
Caution 🛑 LoopOver review result - fixes requiredReview updated: 2026-07-16 14:50:33 UTC
Review summary Nits — 1 non-blocking
CI checks failing
Decision drivers
Context & advisory signals — never blocks the verdict
Review context
Contributor next steps
Signal definitions
🧪 Chat with LoopOverAsk LoopOver a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.
Full command reference: https://loopover.ai/docs/loopover-commands 🧪 Experimental — new and may change. Visual preview
Click any thumbnail to open the full-size screenshot. Before = production · After = this PR's preview deploy. 🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by LoopOver, a quiet PR intelligence layer for OSS maintainers.
|
|
LoopOver is closing this pull request on the maintainer's behalf (CI is failing (validate, validate-code)). This is an automated maintenance action — to pursue this change, please open a new pull request with the issues resolved. Closed PRs may be analyzed later to improve review accuracy, but they are not automatically reopened or re-reviewed. |


Summary
The first streaming-text primitive for the miner-ui chat rail (#6516): progressively reveal a chat response's text as chunks arrive. Unwired plumbing only — no backend, no route, no live
fetch/EventSource; exercised solely against mock chunk sources in tests.Deliverables
lib/use-streaming-text.ts—useStreamingText(source)consumes aChunkSource(a() => AsyncIterable<string>factory, exported by name for a future composer/message-list to type against), accumulating each chunk intotextwith anidle | streaming | done | error | cancelledstatus and acancel(). MirrorsusePolledFetch's cancelled-flag discipline: a chunk resolving after a new source starts, aftercancel(), or after unmount never touches state; a mid-stream throw/reject surfaces viastatus/error, never as an unhandled rejection.components/streaming-text.tsx— thin<StreamingText>renderer showing the accumulated text with a caret while streaming, suppressed underprefers-reduced-motion(detected viawindow.matchMedia+ achangelistener — theuse-mobile.tsxtechnique; nomotiondependency added).use-streaming-text.test.ts+streaming-text.test.tsx— co-located flat insrc/, mirroringuse-polled-fetch.test.ts.Tests (every branch)
Incremental accumulation across renders; cancel-on-new-source (late chunk from the abandoned source ignored); cancel-on-unmount (no late state write, no throw); mid-stream error →
status: error; renderer full-motion caret vs reduced-motion suppression + final text.apps/**is Codecov-ignored, so the operative gate is the local vitest coverage threshold (85/85/75/85) — this PR lands the suite at 88/86/81/90. Local@loopover/ui-minertypecheck + 192 tests + eslint (0 errors) all green.Closes #6516