Skip to content

Update Livepeer Early Access console UI - #41

Merged
peacenode merged 6 commits into
mainfrom
peace/ui-updates
Sep 4, 2026
Merged

Update Livepeer Early Access console UI#41
peacenode merged 6 commits into
mainfrom
peace/ui-updates

Conversation

@peacenode

Copy link
Copy Markdown
Collaborator

Summary

  • Rebrand the console shell for Livepeer Early Access with registry theme tokens, Inter, favicon/OG metadata, global scrollbar behavior, and Sonner clipboard toasts.
  • Simplify navigation/account chrome, remove settings UI, redirect legacy routes, and add the animated account footer menu.
  • Redesign Home around balance, promo/referral cards, and a simplified History table with media detail view.
  • Add the Install page with MCP URL, image wheel hero, and provider-specific install guide for Claude, Claude Code, ChatGPT, and Hermes.

Verification

  • pnpm run typecheck
  • pnpm run lint
  • pnpm run build

Build passed with existing Next/Auth0 warnings about workspace root inference and dynamic dependency import.

@vercel

vercel Bot commented Sep 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
console Ready Ready Preview Sep 4, 2026 5:52pm UTC

Request Review

@eliteprox eliteprox left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM. Nice iterative UI update. See review comments below for historical context to inform next PR, none are blocking as we will draft the next change by restoring real user usage data

Reviewed PR #41 (peace/ui-updates vs main, +2744 / −2971). Findings first, highest severity first.

The rebrand, nav collapse, /settings//usage//calls redirects, and /install allowlist gating look intentional and mostly correct. The problems are fabricated history media, wrong numbers/copy, and a referral CTA that does nothing.


High

1. History detail shows fabricated media as the user’s output

AccountActivityRow / SignedTicketRequestRow only have ticket metadata (id, pipeline, model, fee, time). There is no output URL. The new viewer invents the rest:

function previewImageUrl(row: AccountActivityRow, size = "1200/900"): string {
  return `https://picsum.photos/seed/${previewSeed(row)}/${size}`;
}

mediaSpecForRow then calls samplePayload() — hardcoded prompts, fake gateway.livepeer URLs, and picsum images — and renders that as the request result.

Video and audio are non-playable chrome (poster + fake 0:00 controls; no <video> / <audio>):

function VideoPreview({ poster }: { poster?: string }) {
  return (
    <div className="relative h-full w-full overflow-hidden bg-background">
      {poster && (
        <img
          src={poster}
          ...
        />
      )}
      ...
          <Play className="h-4 w-4 fill-current" aria-hidden="true" />
          <span>0:00</span>

Clicking a real history row on Early Access will show a random stock photo (or a dead video skin) as if it were that run. That violates the console’s honest-UI rule and will get reported as “my generations look wrong.”

Fix before merge: keep the metadata pane (capability, cost, time — those fields are real) and ship an explicit “Preview unavailable” stage until an output-URL API exists. Do not show picsum or sample JSON as the result.


2. Recent history timestamps are hardcoded to 30s

function formatHistoryRelativeTime(iso: string): string {
  const then = new Date(iso).getTime();
  if (Number.isFinite(then)) {
    const seconds = Math.round((Date.now() - then) / 1000);
    if (seconds < 60) return "30s";
  }

  return formatRunRelativeTime(iso).replace(/ ago$/, "");
}

A 3s-old row and a 59s-old row both read 30s. formatRunRelativeTime already returns "just now" under a minute — this special case is strictly worse. Looks like a leftover placeholder.


Medium

3. Balance sublabel says “remaining” for the total issued

Hero number is remaining (included left + credits + overage). issuedTotal is consumed + remaining. Visible copy then says / $X remaining:

        {runway && (
          <p className="text-[12px] font-normal text-fg-faint">
            / {fmtUsd(issuedTotal)} remaining
          </p>
        )}

aria-label is correct (remaining of … total issued). On screen, $200 / $250 remaining reads as $250 still left. Should be / $X issued (or drop the denominator).


4. “Refer a friend” copies a URL nothing handles

function referralUrlFor(user: ConsoleUser): string {
  return `${new URL(MCP_SERVER_URL).origin}?ref=${encodeURIComponent(user.id)}`;
}

function copyReferralUrl(referralUrl: string) {
  void navigator.clipboard.writeText(referralUrl).then(() => {
    toast("copied to clipboard");

Nothing in the app reads ?ref=. The value is a PymtHouse eu_<sha256> id, not a referral code. Users get a success toast for a no-op link. Hide the card until a referral endpoint exists, or wire one.

Clipboard rejection is also ignored (same in CopyButton’s new feedback="toast" path).


5. Signed-out chrome still sells API keys

Pilot comments and MCP_SERVER_URL say there is no key to provision. The signed-out sidebar still says “5 demo calls per app”, “Spin up in 30 seconds with an API key”, and Get an API key (desktop + mobile). That contradicts Home/Install and /signup.


6. ChatGPT / Hermes install steps are not a real connector flow

Claude and Claude Code look plausible (paste URL / claude mcp add --transport http). ChatGPT and Hermes tell the user to paste a Codex prompt and “approve the connector changes.” ChatGPT desktop MCP setup is Settings → Connectors, not asking Codex to edit config. Those two tabs will fail in the field.


7. ?request= deep links only resolve against the first loaded page

CallsSection does allRows.find((r) => r.id === requestId). First fetch is limit=50. An older /calls?request= bookmark (or a row not in the first page) leaves open={false} with the query param still in the URL and no error. No “load until found” and no “not found” state.


8. Auth hydration blanks the page

  if (isLoading) return null;

Same on /install. The route Suspense fallback (ConsolePageSkeleton) never shows because the client gate returns null. Cold load is an empty main column until Auth0 + externalUserId resolve.


9. Checkout return URLs were pointed at /home with no consumer

successUrl / cancelUrl defaults and useOwnerWallet now land on /home?topup=… / ?checkout=…. WalletPanel / BillingSection / PlansPanel are unmounted and nothing on Home reads those params. Today the checkout UI is gone, so this is latent — any leftover or API-triggered Stripe session returns to a silent Home.


Low

Item Where Why
Personal Tailscale host in Next config next.config.ts allowedDevOrigins: ["studio.tail0de21e.ts.net"] Dev-only, but a machine-specific hostname should not ship.
/home no longer middleware-gated middleware.ts isSessionOnlyPath Intentional in-shell SignInWall. Weaker defense-in-depth; APIs still requireConsoleSession().
Allowlist test not updated lib/console/email-allowlist.test.ts /install is gated in code; test still only asserts / and /home.
Cmd+K still listed KeyboardShortcuts.tsx File was edited (G+I added); palette is still unmounted. Pre-existing, but this PR touched the overlay.
Metadata still says “manage API access” app/layout.tsx / app/(app)/layout.tsx Copy leftover after keys left the product.
Inter loaded from rsms.me with no SRI app/layout.tsx Third-party webfont; availability + supply-chain. Prefer self-host.
Dual animation deps package.json Sidebar uses motion; Dialog/Login/Tooltip still framer-motion. Extra bundle, not a functional bug.

Missing tests

No new tests. Highest-value coverage for this diff:

  • formatHistoryRelativeTime — sub-60s must not be "30s"
  • isAllowlistGatedPath("/install")
  • referralUrlFor / any future ref handler
  • /calls?request= redirect preserves and encodes the id
  • Balance copy: remaining vs issued (once the label is fixed)

Existing *.test.ts files still have no runner (pnpm test is not wired).


What looks fine

  • /settings, /usage, /calls redirects (page + next.config; /calls keeps ?request=)
  • /install added to allowlist + isSessionOnlyPath (signed-out → /login)
  • Payment/wallet routes still session-gated
  • No new XSS: drawer text is React-escaped; only dangerouslySetInnerHTML is the static theme bootstrap
  • MCP_SERVER_URLhttps://earlyaccess.livepeer.org/api/mcp matches robots/sitemap/metadata
  • Theme wipe to system-only is consistent with removing Appearance settings

Merge bar

I would not merge until #1 and #2 are fixed. #1 is the one users will hit on every history click. #3#6 are the next tier if this is the public Early Access shell.

@peacenode
peacenode merged commit 98daf90 into main Sep 4, 2026
1 check passed
@adamsoffer
adamsoffer deleted the peace/ui-updates branch September 4, 2026 18:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants