Skip to content

feat(ui-kit): port state-views.tsx primitives into @loopover/ui-kit - #6539

Merged
loopover-orb[bot] merged 1 commit into
JSONbored:mainfrom
galuis116:feat/port-state-views-to-ui-kit
Jul 16, 2026
Merged

feat(ui-kit): port state-views.tsx primitives into @loopover/ui-kit#6539
loopover-orb[bot] merged 1 commit into
JSONbored:mainfrom
galuis116:feat/port-state-views-to-ui-kit

Conversation

@galuis116

Copy link
Copy Markdown
Contributor

Closes #6506

Summary

apps/loopover-ui/src/components/site/state-views.tsx is the app's shared loading/empty/error primitive set — Spinner, LoadingState, EmptyState, StateActionButton, ErrorState, StateBoundary — used by every route/panel needing a consistent async-state surface. #6244's audit (apps/loopover-ui/src/chat-ui-primitives-audit.md, PR #6474) confirmed these are reusable as-is for the miner-dashboard chat rail's message list, but they only existed as app-local code with no path for apps/loopover-miner-ui (or any other consumer) to reach them without duplicating the file.

This ports the primitives into packages/loopover-ui-kit/src/components/state-views.tsx, matching the existing skeleton.tsx/card.tsx/badge.tsx shim precedent, and rewrites apps/loopover-ui's own file into a back-compat wrapper — never deleted, per the issue's own explicit warning.

This move produces zero visible/rendered output change in apps/loopover-ui — every prop and default every existing caller sees stays the same.

The one real adaptation

state-views.tsx hardcoded a call to apps/loopover-ui/src/lib/api/request.ts's notifyApiFailure inside StateBoundary's error effect. packages/loopover-ui-kit can never import from an app (backwards dependency direction, and none of apps/loopover-ui's @/... aliases resolve there anyway). The ui-kit copy:

  • Defines its own export type ApiFailureKind = "timeout" | "network" | "http"; locally — structurally identical to request.ts's own type, a zero-logic duplication of a 3-value union, not a redesign.
  • Replaces the hardcoded notifyApiFailure(...) call with an optional onFailureNotify prop, invoked inside the exact same if (isError && errorLabel) effect with the same argument shape. No-ops when the caller doesn't supply one.

apps/loopover-ui's wrapper StateBoundary forwards every prop through and defaults onFailureNotify to the app's real notifyApiFailure. That default is passed as (args) => notifyApiFailure(args) — a lazily-dereferenced closure, not a direct reference — because several existing tests partially mock @/lib/api/request without including notifyApiFailure and never exercise the error path; a direct reference would evaluate (and throw on) the incomplete mock on every render instead of only when the boundary actually enters its error state, which is exactly how the original code behaved (the reference lived inside the effect body, only dereferenced when isError && errorLabel).

Everything else, byte-for-byte

  • Spinner, the private Shell helper, StateActionButton, LoadingState, EmptyState, ErrorState — ported verbatim, only the cn import path changed ("../utils", matching every other packages/loopover-ui-kit/src/components/*.tsx file).
  • usePreviewDataState — unrelated to the four named primitives, zero call sites anywhere in the app — stays exactly where it is, unmoved and unmodified.
  • The six existing external call sites that import Spinner/StateActionButton directly from @/components/site/state-views (audit-feed.tsx, dead-letter-queue-panel.tsx, refresh-meta.tsx, app-panels/rees-analyzer-field-group.tsx, routes/app.operator.tsx, routes/app.analytics.tsx) keep resolving with zero edits — verified by running their tests.
  • apps/loopover-ui/src/lib/api/request.ts and apps/loopover-miner-ui are both untouched, per the issue's scope.

Scope

Validation

  • packages/loopover-ui-kit's own npm run typecheck (tsc -p tsconfig.json --noEmit) — clean.
  • packages/loopover-ui-kit's own npm run build — clean.
  • Root npm --workspace @loopover/ui run typecheck — clean (both changed files, and every existing call site's prop usage).
  • apps/loopover-ui/src/components/site/state-views.test.tsx — 14/14 passing, zero edits needed — the wrapper is fully drop-in.
  • Broader regression sweep: full apps/loopover-ui/src/components/site + apps/loopover-ui/src/routes tree — 39 files, 230 tests, all passing (covers every direct Spinner/StateActionButton consumer plus every StateBoundary call site).
  • npm run ui:lint — 0 errors, only pre-existing warnings in unrelated files.
  • git diff --check
  • npm run actionlint
  • npm run docs:drift-check / manifest:drift-check / command-reference:check
  • npm audit --audit-level=moderate — 0 vulnerabilities.
  • npm run ui:build — fails in this sandbox on an unrelated, pre-existing issue (@scalar/api-reference fails to resolve from @scalar/api-reference-react's dist output), reproduced identically on a clean, unmodified main checkout via git stash this same session.
  • Root npm run typecheck / npm run test:coverage — this sandbox's root tsc --noEmit reliably OOMs regardless of diff content (reproduced repeatedly this session); substituted with the scoped packages/loopover-ui-kit/@loopover/ui typechecks above, both clean. Per the issue's own Test Coverage Requirements, packages/loopover-ui-kit/** is not in Codecov's measured paths (same as apps/**), so no codecov/patch check is expected for this diff.

If any required check was skipped, explain why:

  • ui:build: pre-existing sandbox environment issue, confirmed unrelated to this diff via git stash against clean main.
  • Root typecheck/test:coverage: established OOM pattern in this sandbox, unrelated to diff content; the workspace-scoped typechecks (both clean) plus the full regression test sweep above cover this diff's actual surface.

Safety

  • No secrets, wallet details, hotkeys, coldkeys, user PATs, private keys, raw trust scores, private rankings, or private maintainer evidence are exposed.
  • Public GitHub text stays sanitized, low-noise, and does not imply compensation guarantees or optimization tactics.
  • Auth, cookie, CORS, GitHub App, Cloudflare, or session changes include negative-path tests. — N/A, no such changes.
  • API/OpenAPI/MCP behavior is updated and tested where needed. — N/A, no API/MCP surface touched.
  • UI changes use live API data or real empty/error/loading states, not production mock/demo fallbacks. — N/A: this move produces zero visible/rendered output change, per the issue's own explicit requirement (Requirement 10). Every prop and default every existing caller sees is unchanged, confirmed by the full test sweep above.
  • Visible UI changes include a UI Evidence section below with screenshots. — N/A, no visible UI change (Requirement 10 explicitly waives this for this issue).
  • Public docs/changelogs are updated where needed; changelogs are only edited for release-prep PRs. — CHANGELOG.md untouched.

Notes

Downstream, dependent work (not in scope here, per the issue): the miner-dashboard chat message-list issue and the visual/ui-kit adoption issues for the miner dashboard's header and four routes, which can now import StateBoundary and friends directly from @loopover/ui-kit/components/state-views instead of inventing their own copy.

Spinner, LoadingState, EmptyState, StateActionButton, ErrorState, and
StateBoundary move to packages/loopover-ui-kit/src/components/state-views.tsx
so apps/loopover-miner-ui (and the future chat message list, per JSONbored#6244's
audit) can consume them directly instead of reaching into apps/loopover-ui.

apps/loopover-ui's own state-views.tsx becomes a back-compat wrapper:
Spinner/LoadingState/EmptyState/StateActionButton/ErrorState re-export
unchanged, StateBoundary forwards every prop and defaults onFailureNotify
to the app's real notifyApiFailure, and usePreviewDataState (unrelated to
this move, zero call sites) stays put. Every existing call site's runtime
behavior is unchanged -- confirmed by the app's own state-views.test.tsx
(14/14 passing with zero edits) and the broader site/routes suite
(230/230 passing).

The ui-kit copy can't import from apps/loopover-ui, so it defines its own
ApiFailureKind (structurally identical to request.ts's) and replaces the
hardcoded notifyApiFailure call with an optional onFailureNotify prop that
no-ops when the caller doesn't supply one -- the wrapper passes it as a
lazily-dereferenced closure (not a direct reference) so tests that
partially mock @/lib/api/request without notifyApiFailure, and never
exercise the error path, keep working exactly as before.

Closes JSONbored#6506
@galuis116
galuis116 requested a review from JSONbored as a code owner July 16, 2026 12:24
@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

Warning

⏸️ LoopOver review result - manual review recommended

Review updated: 2026-07-16 12:36:38 UTC

2 files · no blockers · CI green · clean

⏸️ Suggested Action - Manual Review

  • AI review already in progress for this PR head: Another LoopOver pass is already running the AI review for this exact PR head. This pass is skipping to avoid a duplicate LLM call.

Review summary
AI review is already running for this PR head in another LoopOver pass. LoopOver is holding this PR for manual review until that pass completes.

Nits — 2 non-blocking
  • Code changes lack test evidence — Add focused regression tests or explain why existing coverage is sufficient.
  • AI review already in progress for this PR head — The gate is held for a human reviewer rather than passed automatically; it re-evaluates once the in-flight review completes or on the next update.

Decision drivers

  • ✅ Code review — No blockers (No AI review summary)
  • ⚠️ Gate result — Not blocking (Advisory; not blocking this PR.)
Context & advisory signals — never blocks the verdict
Signal Result Evidence
Linked issue ✅ Linked #6506
Related work ✅ No active overlap found No same-issue or scoped active PR overlap found.
Change scope ✅ 20/20 Low 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: 1937 registered-repo PR(s), 1276 merged, 54 issue(s).
Contributor context ✅ Confirmed Gittensor contributor galuis116; Gittensor profile; 1937 PR(s), 54 issue(s).
Improvement ℹ️ None detected risk: low · value: none
Review context
  • Author: galuis116
  • 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: 1937 PR(s), 54 issue(s).
  • PR-specific overlap: none found.
Contributor next steps
  • Keep the PR focused and include validation evidence before maintainer review.
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 <question> answers contribution-quality Q&A with source citations and freshness.
  • @loopover chat <question> 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 /
diff /
diff /
/ 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 · Diff highlights exactly what changed.

🟩 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 8eb1933 into JSONbored:main Jul 16, 2026
9 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.

ui-kit: port state-views.tsx (LoadingState/EmptyState/ErrorState/StateBoundary) into @loopover/ui-kit

1 participant