Skip to content

feat(miner-ui): useStreamingText hook + StreamingText renderer - #6583

Merged
loopover-orb[bot] merged 1 commit into
JSONbored:mainfrom
jaytbarimbao-collab:feat-miner-ui-streaming-text-6516-v3
Jul 16, 2026
Merged

feat(miner-ui): useStreamingText hook + StreamingText renderer#6583
loopover-orb[bot] merged 1 commit into
JSONbored:mainfrom
jaytbarimbao-collab:feat-miner-ui-streaming-text-6516-v3

Conversation

@jaytbarimbao-collab

Copy link
Copy Markdown
Contributor

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.tsuseStreamingText(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(). All state writes run inside the effect's async worker (guarded by a per-run cancelled flag), so a chunk resolving after a new source starts, after cancel(), or after unmount never touches state, and a mid-stream throw/reject surfaces via status/error rather than 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 (via window.matchMedia + a change listener — the use-mobile.tsx technique; no motion dependency).
  • use-streaming-text.test.ts + streaming-text.test.tsx — co-located flat in src/.

Tests (every branch)

Incremental accumulation; cancel-on-new-source; cancel-on-unmount (no late write); mid-stream error → status: error; renderer full-motion caret vs reduced-motion suppression. apps/** is Codecov-ignored, so the operative gate is the local vitest coverage threshold (85/85/75/85) — this suite lands at 89/88/83/91. Local @loopover/ui-miner typecheck + 221 tests + the full npm run ui:lint (0 errors, incl. react-hooks/set-state-in-effect) all green.

Third submission of #6516 (after #6573/#6579): the earlier ones were auto-closed on the CI "UI lint" step's react-hooks/set-state-in-effect rule (miner-ui pins react-hooks 7.1.1), which my local install had been resolving at 5.2.0 and so missed. State transitions are now written from the effect's async worker, not synchronously in the effect body — verified against 7.1.1 locally.

Closes #6516

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-security

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@loopover-orb loopover-orb Bot added the gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 0.25x multiplier. label Jul 16, 2026
@loopover-orb

loopover-orb Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Tip

✅ LoopOver review result - approve/merge recommended

Review updated: 2026-07-16 15:35:11 UTC

4 files · 1 AI reviewer · no blockers · readiness 82/100 · CI green · clean

✅ Suggested Action - Approve/Merge

  • safe to merge

Review summary
This adds an unwired useStreamingText hook and StreamingText renderer for the miner-ui chat rail, both exercised only against mock chunk sources in tests. The cancellation discipline is correct: a per-effect `cancelled` flag guards every state write, cleanup flips it on unmount/source-change, and the async worker (not the effect body) does all the setState calls, avoiding react-hooks/set-state-in-effect. Tests cover incremental accumulation, cancel-on-new-source, cancel-on-unmount, mid-stream error, and reduced-motion caret suppression — a solid, focused primitive with no live wiring yet.

Nits — 5 non-blocking
  • apps/loopover-miner-ui/src/lib/use-streaming-text.ts:44-46: resetting `text`/`error` to defaults happens inside the async IIFE after the effect has already run, so on rapid source swaps there's a render where the previous source's stale `text` is still shown before the reset commits — worth confirming this doesn't cause a visible flash in the composer once wired.
  • apps/loopover-miner-ui/src/components/streaming-text.tsx: `usePrefersReducedMotion` duplicates the `window.matchMedia` + `change`-listener pattern already used in `use-mobile.tsx`; consider factoring a shared hook if a third consumer shows up.
  • The external brief's 'magic number 6516' and 'deep nesting depth 5' flags are noise — `Chat UI: streaming text renderer / useStreamingText hook #6516` is an issue reference in a comment, not a numeric literal, and the nesting is a normal try/for-await/if structure.
  • apps/loopover-miner-ui/src/lib/use-streaming-text.ts: consider exposing whether `cancel()` was called by the consumer vs. an internal reset, so a future composer can distinguish user-initiated stop from source-swap in UI state if needed.
  • apps/loopover-miner-ui/src/components/streaming-text.tsx: the caret glyph `▍` is hardcoded twice (JSX text and className); no action needed now but flag if a themed caret is requested later.

Decision drivers

  • ✅ Code review — No blockers (1 reviewer)
  • ✅ Gate result — Passing (No configured blocker found.)
Context & advisory signals — never blocks the verdict
Signal Result Evidence
Linked issue ✅ Linked #6516
Related work ⚠️ 3 scoped overlaps Top overlaps are listed below; lower-confidence bulk is hidden.
Change scope ❌ 8/20 High review scope from cached public metadata (1 linked issue).
Validation posture ✅ 25/25 PR body includes validation/test evidence.
Contributor workload ✅ 10/10 Author activity: 151 registered-repo PR(s), 81 merged, 4 issue(s).
Contributor context ✅ Confirmed Gittensor contributor jaytbarimbao-collab; Gittensor profile; 151 PR(s), 4 issue(s).
Improvement ✅ Minor risk: clean · value: minor
Review context
  • Author: jaytbarimbao-collab
  • Role context: outside_contributor
  • Public audience mode: oss maintainer
  • Lane context: Repository is configured for direct PR review.
  • Public profile languages: not available
  • Official Gittensor activity: 151 PR(s), 4 issue(s).
  • Related work: Titles/paths share 8 meaningful terms. (issue #6489, issue #6488)
  • Related work: Titles/paths share 7 meaningful terms. (issue #6516, issue #6518)
  • Related work: Titles/paths share 6 meaningful terms. (issue #6516, issue #6303)
Contributor next steps
  • Start here: Review top overlaps.
  • Then work through the remaining 2 steps in the Signals table above.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.
🧪 Chat with LoopOver

Ask 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.

  • @loopover ask &lt;question&gt; answers contribution-quality Q&A with source citations and freshness.
  • @loopover chat &lt;question&gt; answers in natural prose from cached decision-pack facts via local inference (maintainer/collaborator; read-only).
  • A plain-language @loopover mention with a real question is routed to the closest matching read-only command automatically — no exact syntax required.

Full command reference: https://loopover.ai/docs/loopover-commands

🧪 Experimental — new and may change.

Visual preview
Route Viewport Before (production) After (this PR's preview) Diff
/ desktop before /
before /
after /
after /
/ mobile before / (mobile)
before / (mobile)
after / (mobile)
after / (mobile)

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.

  • Re-run LoopOver review

@loopover-orb loopover-orb Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LoopOver approves — the gate is satisfied and CI is green.

@loopover-orb
loopover-orb Bot merged commit dab0758 into JSONbored:main Jul 16, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 0.25x multiplier.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Chat UI: streaming text renderer / useStreamingText hook

1 participant