Skip to content

feat(frontend): Porcelain dashboard with synced light and dark mode - #4

Open
OfficialAbhinavSingh wants to merge 16 commits into
mergit-io:mainfrom
OfficialAbhinavSingh:feat/frontend-porcelain-dashboard
Open

OfficialAbhinavSingh wants to merge 16 commits into
mergit-io:mainfrom
OfficialAbhinavSingh:feat/frontend-porcelain-dashboard

Conversation

@OfficialAbhinavSingh

@OfficialAbhinavSingh OfficialAbhinavSingh commented Oct 13, 2026 •

Copy link
Copy Markdown
Member

Summary

Adds the frontend for the dashboard in frontend/, built from the two approved Figma files: Porcelain (light) and Porcelain Night (dark). Frontend only. Every screen reads from src/mock/data.ts; there is no backend call anywhere.

  • 9 screens: Overview, Run workspace, New run, Agents, Work proof, Leaderboard, Integrations, Wallet and identity, Sign in.
  • Light and dark mode with a toggle in the nav (and on sign in).
    • The first visit follows the OS.
    • A choice is saved. Picking the OS theme again clears it, so the app goes back to following the OS.
    • No flash on load: a small inline script sets the theme before the first paint.
    • Open tabs stay in sync, and an OS change applies live while nothing is saved.
    • The new theme grows in a circle from the toggle. Theme changes from another tab or the OS cross-fade. Reduced motion switches instantly.
  • Every control does something real on demo data:
    • Run rows open their own run, and unknown run or proof ids are a 404.
    • The leaderboard range switches the ranking.
    • Approvals count down, expire, and keep the header and nav badge in sync.
    • Agent search, category, verified filter and sort all work.
    • Sign in requires an email.
  • Buttons that need a backend (Fund wallet, Create agent and so on) say so instead of doing nothing.
  • Code quality checks:
    • ESLint (strict type-checked, React hooks, accessibility), Prettier, and noUncheckedIndexedAccess.
    • A CI workflow runs lint, format, build and the e2e suite on every PR that touches frontend/.
  • Responsive. 1440 x 900 matches the frames. Tablet and phone widths have no horizontal scroll.

The designs predate the current paper, so some copy differs on purpose:

Design Current
Monad testnet, MON L2 testnet, ETH
AgentPassport, ProofOfWork, Reputation Identity, ReceiptRegistry, ReputationAnchor
Reputation score Value density
Proof of work badge Verified receipts

Stack: Vite, React 19, React Router, TypeScript (strict), plain CSS on design tokens, Playwright.

Part of #2.

Type of change

  • paper — correction, derivation, or new section in the research
  • docs — requirements, README, or other prose
  • feat — new functionality
  • fix — bug fix
  • contracts — Solidity
  • chore — dependencies, tooling, CI

What I verified

  • Verified:
    • Each screen was screenshotted at 1440 x 900 in both themes and pixel-diffed against the HTML source the Figma frames were captured from.
    • The only differences left are the added theme toggle, the copy in the table above, and the agent card order: "Sort by success" now really sorts, while the design frame lists the cards in a different order.
    • npm ci && npm run build passes from a fresh clone.
    • The full CI sequence passes under Node 22 on a clean copy: lint, format check, build, and 45 Playwright tests (stable over 5 runs). The tests cover:
      • first paint follows the OS before React mounts
      • a saved choice beats the OS
      • the theme persists across reloads
      • a switch in one tab reaches the other
      • the OS is followed live until a choice is saved
      • reduced motion skips the transition
      • every route renders in both themes with the right tab active
      • no stale v0.2 terms appear
      • arrow keys move the segmented controls
      • buttons with no backend say so instead of doing nothing
      • approvals updating the header and nav, and expiring on the clock
      • each run row opening its own run, and 404s for unknown ids
      • the leaderboard range, and agent sort, search and filters
      • sign in requiring an email
      • no page scrolling sideways at 320 and 390 px, and integration cards keeping their contents inside
    • No horizontal overflow at 1024, 390 and 320 px on any route.
  • Not verified:
    • Safari and Firefox. Tests ran in Chrome only, and Firefox has no View Transitions, so it gets the cross-fade path, untested.
    • Real screen readers (roles and labels are set, but no manual pass).
    • Anything against a backend, because there isn't one.
    • Figma itself: I compared against the HTML the frames were generated from, not an export from the Figma file.

Checklist

  • One concern per PR — this change does one thing
  • No secrets committed (no private keys, API keys, .env files, mnemonics)
  • Claims in the diff are supported by something a reviewer can check

Evidence

Theme toggle and navigation:

theme toggle

The reveal, frame by frame (dark to light, growing from the toggle):

reveal frames

Design (left) and build (right):

design vs build, light overview
design vs build, dark run

Every screen, light and dark:

Overview
Run workspace
Agents
Work proof
Leaderboard
New run
Integrations
Wallet and identity
Sign in

Phone-width fixes in the last commit (0739be7): on a phone ", 17m ago" wraps with its block number instead of on a line of its own (desktop is unchanged from the design), integration cards stack one per row, the wallet lag chart fills its card, long diff lines scroll, and the run graph's zoom control no longer covers a node.

overview at 390px

integrations at 390px

To run it: cd frontend && npm install && npm run dev, then npm run test:e2e.

Breaking changes / follow-ups

  • None breaking. This only adds frontend/.
  • Frontend #1 also adds a frontend/ (the landing page). Whichever merges second will need to fold the two apps together.
  • Follow-up: wire screens to the real API once it exists, and replace the mock numbers.
  • The design has no runs or proofs list screens, so those tabs open the demo run and proof directly.

Adds the frontend workspace for the Porcelain dashboard (mergit-io#2): Vite,
React 19, React Router and strict TypeScript, with node_modules and
build output ignored.
Ports the colour, type and shape tokens from the approved Figma files
(Porcelain and Porcelain Night) to CSS variables keyed on data-theme,
plus the shared component styles. Every colour a component uses is a
token, so switching theme is one attribute change.
- First visit follows the OS; a choice is saved, and picking the OS
  theme again clears it so the app follows the OS from then on.
- An inline script sets the theme before first paint, so no flash.
- Open tabs stay in sync through the storage event, and OS changes
  apply live while nothing is saved.
- The toggle reveals the new theme in a circle from the button via the
  View Transitions API, with a colour cross-fade fallback. Reduced
  motion switches instantly.
Floating pill nav with a sliding active tab, the theme toggle beside
notifications, shared components (badges, avatars, segmented control,
sparkline, toast), typed demo data and the Overview screen. Run filters
and approvals work locally; no number in the demo data is measured.
The run workspace draws the task graph from the design; selecting a
node updates the side panel, and the canvas zooms and pans on small
screens. New run has the prompt composer, draft plan and ask-before
switches.
Copy follows the current design rather than v0.2: identities are
ERC-8004, the receipt contract is ReceiptRegistry, the chain is an
unnamed L2 testnet, and ranking reads as value density.
Wallet shows ETH on the L2 testnet and the Identity, ReceiptRegistry
and ReputationAnchor contracts. Sign-in carries its own theme toggle.
Checks first paint follows the OS before React mounts, a saved choice
beats the OS, the toggle persists across reloads, a switch in one tab
reaches the others, the OS is followed live until a choice is saved,
and reduced motion skips the view transition. Every route renders in
both themes with the right tab active and no stale v0.2 terms.
How to run, build and test it, the route list, and how light and dark
mode work.
- Overview badge said "Proof of work", a superseded v0.2 term; it now
  reads "Verified receipts". The stale-term test checks for it.
- Spark divided by zero on a single point and crashed on none.
- Buttons with no backend behind them now say so instead of doing
  nothing.
- Segmented controls follow the radio-group keyboard pattern: arrow
  keys move the selection and focus, one tab stop per group.
- Reduced motion turns animations off rather than shortening them, so
  content never waits on a frame tick to fade in.
settle() polled running animations, which can pass in the frame before
the reveal starts, so a test could read the theme before the switch
applied. :active-view-transition is true from the moment a transition
is requested.
@OfficialAbhinavSingh

Copy link
Copy Markdown
Member Author

@Viscous106 @Divii2205 @Aspirant200715 review this once i have attached the images of how it will look on the localhost. Also if any changes are needed do comment that out/

Controls that looked interactive but did nothing, or did the wrong thing:
- Run rows opened RUN-2048 whatever row you clicked; each row now opens
  its own run. Runs without a demo trace say so, unknown ids are a 404.
- Proof pages showed proof 1840 for any id; unknown ids are a 404, and
  the breadcrumb no longer links back to itself.
- The leaderboard range switch changed nothing; each range has its own
  ranking now.
- "Two actions are waiting" and the nav badge never changed. Approvals
  live in one provider: they count down, expire, and update both.
- Agent sort is a real select and sorts; "Verified only" reads a
  verified field.
- Wallet copy copied a literal with an ellipsis; it copies the address.
- Sign-in accepted an empty email; it is required.
- Buttons whose action needs a backend now say so instead of nothing.

Structure:
- One file per shared component instead of a grab-bag ui.tsx.
- Toast, theme and approvals split into context and provider files so
  fast refresh works; useCopy, useNotWired and useSlidingThumb are hooks.
- Run workspace split into the page, the flow graph and the task panel.
- Every displayed number and string comes from mock/data.ts, typed.
- Inline style objects replaced by named classes; only data-driven
  values (sizes, positions) stay inline.

Also fixes two regressions found by diffing against the design: graph
edges were invisible with reduced motion, and the proof facts lost the
rule under the last row.
New behaviour tests: approving updates the header count and nav badge,
asks expire on the clock, each run row opens its own run, unknown run
and proof ids are a 404, the timeline selects tasks, the leaderboard
range changes the ranking, agent sort, search and category work, sign
in requires an email, and the composer refuses an empty prompt.
- ESLint 10 with typescript-eslint strictTypeChecked, React hooks,
  react-refresh and jsx-a11y-x (the maintained fork that supports
  ESLint 10).
- Prettier at 120 columns, and an EditorConfig.
- noUncheckedIndexedAccess, so array and record lookups must handle a
  missing entry.
- lint and format scripts, Node 22.12 in engines, README updated.
Runs lint, format check, type-check and build, then the Playwright
suite, on any pull request that touches frontend/. Uploads the test
results when something fails.
@Viscous106

Copy link
Copy Markdown
Member

LGTM!!

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

- Overview: on a phone ", 17m ago" wraps together with its block number
  instead of landing on a line of its own, and "View all" no longer
  breaks onto two lines. Desktop is unchanged from the design.
- Integrations: one card per row on phones, and permission rows put the
  scope above its purpose so the access badge has its own column.
- Wallet: the settlement lag chart fills its card once the layout drops
  to one column.
- Proof diff: long lines scroll sideways instead of being cut off.
- Run: the zoom control no longer covers the proof node on phones.
- New run: the send button keeps its size at 320px.

Adds checks that no page scrolls sideways at 320 and 390px and that
integration cards keep their contents inside.
@OfficialAbhinavSingh
OfficialAbhinavSingh force-pushed the feat/frontend-porcelain-dashboard branch from 5f92aa3 to 0739be7 Compare October 14, 2026 17:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

3 participants