feat(ui-kit): port state-views.tsx primitives into @loopover/ui-kit - #6539
Conversation
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
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
|
Warning ⏸️ LoopOver review result - manual review recommendedReview updated: 2026-07-16 12:36:38 UTC
Review summary Nits — 2 non-blocking
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 · 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.
|



Closes #6506
Summary
apps/loopover-ui/src/components/site/state-views.tsxis 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 forapps/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 existingskeleton.tsx/card.tsx/badge.tsxshim precedent, and rewritesapps/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.tsxhardcoded a call toapps/loopover-ui/src/lib/api/request.ts'snotifyApiFailureinsideStateBoundary's error effect.packages/loopover-ui-kitcan never import from an app (backwards dependency direction, and none ofapps/loopover-ui's@/...aliases resolve there anyway). The ui-kit copy:export type ApiFailureKind = "timeout" | "network" | "http";locally — structurally identical torequest.ts's own type, a zero-logic duplication of a 3-value union, not a redesign.notifyApiFailure(...)call with an optionalonFailureNotifyprop, invoked inside the exact sameif (isError && errorLabel)effect with the same argument shape. No-ops when the caller doesn't supply one.apps/loopover-ui's wrapperStateBoundaryforwards every prop through and defaultsonFailureNotifyto the app's realnotifyApiFailure. That default is passed as(args) => notifyApiFailure(args)— a lazily-dereferenced closure, not a direct reference — because several existing tests partially mock@/lib/api/requestwithout includingnotifyApiFailureand 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 whenisError && errorLabel).Everything else, byte-for-byte
Spinner, the privateShellhelper,StateActionButton,LoadingState,EmptyState,ErrorState— ported verbatim, only thecnimport path changed ("../utils", matching every otherpackages/loopover-ui-kit/src/components/*.tsxfile).usePreviewDataState— unrelated to the four named primitives, zero call sites anywhere in the app — stays exactly where it is, unmoved and unmodified.Spinner/StateActionButtondirectly 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.tsandapps/loopover-miner-uiare both untouched, per the issue's scope.Scope
type(scope): short summaryConventional Commit format.CONTRIBUTING.mdand does not reintroduce GitHub Pages, VitePress,site/, orCNAME.Validation
packages/loopover-ui-kit's ownnpm run typecheck(tsc -p tsconfig.json --noEmit) — clean.packages/loopover-ui-kit's ownnpm run build— clean.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.apps/loopover-ui/src/components/site+apps/loopover-ui/src/routestree — 39 files, 230 tests, all passing (covers every directSpinner/StateActionButtonconsumer plus everyStateBoundarycall site).npm run ui:lint— 0 errors, only pre-existing warnings in unrelated files.git diff --checknpm run actionlintnpm run docs:drift-check/manifest:drift-check/command-reference:checknpm audit --audit-level=moderate— 0 vulnerabilities.npm run ui:build— fails in this sandbox on an unrelated, pre-existing issue (@scalar/api-referencefails to resolve from@scalar/api-reference-react's dist output), reproduced identically on a clean, unmodifiedmaincheckout viagit stashthis same session.npm run typecheck/npm run test:coverage— this sandbox's roottsc --noEmitreliably OOMs regardless of diff content (reproduced repeatedly this session); substituted with the scopedpackages/loopover-ui-kit/@loopover/uitypechecks above, both clean. Per the issue's own Test Coverage Requirements,packages/loopover-ui-kit/**is not in Codecov's measured paths (same asapps/**), so nocodecov/patchcheck 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 viagit stashagainst cleanmain.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
UI Evidencesection below with screenshots. — N/A, no visible UI change (Requirement 10 explicitly waives this for this issue).CHANGELOG.mduntouched.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
StateBoundaryand friends directly from@loopover/ui-kit/components/state-viewsinstead of inventing their own copy.