Global Accounts interactive demo ("See it live")#527
Conversation
Interactive, embeddable wallet demo for the Grid docs — the neobank analog of
the Flow Builder. New standalone Next.js app (components/grid-wallet-demo) that
the docs embed in an iframe with light/dark theme sync, mirroring how
grid-visualizer powers /flow-builder.
- Same shell + design system as the Flow Builder (@lightsparkdev/origin,
sidebar + canvas + CodePanel-style API log, central-icons, squircle).
- Playground model: pick a use case + sign-in method, then drive a real wallet
on a phone (create account, add money, send, cash out, issue a card, tap to
pay) by clicking the phone itself; the exact Grid API calls render alongside.
- Phone wallet adapted from the bread neobank app; Robinhood-style glass card
with a cinematic reveal.
- Scripted happy path (no live sandbox calls), like the Flow Builder.
Docs wiring (mintlify/): new global-accounts/demo.mdx ("See it live", under
Introduction), docs.json nav + chrome-hide CSS, style.css full-bleed iframe.
Deployment + designer handoff notes in components/grid-wallet-demo/HANDOFF.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Preview deployment for your docs. Learn more about Mintlify Previews.
|
Greptile SummaryThis PR introduces a new standalone Next.js 14 app (
Confidence Score: 3/5Safe to merge with the The
|
| Filename | Overview |
|---|---|
| components/grid-wallet-demo/src/data/actions.ts | Core action model — defines wallet state machine and per-action API call shapes. The tap action's available guard has no balance floor, allowing the wallet to go negative. |
| components/grid-wallet-demo/src/components/Phone.tsx | Phone UI component with all wallet screens. The changeRow in WalletScreen incorrectly renders the total balance as the "Today" gain amount instead of the most-recent transaction delta. |
| mintlify/global-accounts/demo.mdx | New docs page with iframe embed and bidirectional theme-sync. The postMessage handler lacks an origin check, allowing any page to toggle dark mode on the docs host. |
| components/grid-wallet-demo/next.config.mjs | Next.js config with tight frame-ancestors CSP and webpack alias for local Origin package. ignoreBuildErrors: true suppresses TypeScript failures during production builds. |
| components/grid-wallet-demo/src/app/page.tsx | Root page — wires wallet state, timer-based animation frames, reset logic, and embed detection. Timer cleanup on unmount is handled correctly. |
| components/grid-wallet-demo/src/data/flow.ts | Core type definitions and Grid API call shapes. Credential-generation logic per auth method is well structured and matches the described API. |
| components/grid-wallet-demo/src/components/ApiSteps.tsx | API call log panel with animated entries, curl-style rendering with syntax highlighting, and auto-scroll to latest entry. |
| components/grid-wallet-demo/src/components/Sidebar.tsx | Use-case and auth-method selector plus collapsible action list. Sign-in method is correctly locked after account creation. |
| mintlify/docs.json | Adds global-accounts/demo to the nav and extends the inline head.raw script + CSS to hide the Mintlify chrome on the new wallet demo page. |
| mintlify/style.css | Adds full-bleed fixed positioning for #wallet-demo-container, mirroring the existing #flow-builder-container rules. |
Prompt To Fix All With AI
Fix the following 4 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 4
components/grid-wallet-demo/src/data/actions.ts:88-93
**`tap` action can produce a negative wallet balance**
The `tap` action's `available` guard only checks `s.hasCard` — there is no minimum balance requirement. A user can: (1) create an account, (2) immediately issue a card (no funds required), (3) tap → balance becomes -$7.32. With funds, repeated tapping also drives the balance negative because the same guard allows unlimited taps. The UI then displays a negative balance in the demo, which misrepresents what the Grid API would actually permit.
### Issue 2 of 4
components/grid-wallet-demo/src/components/Phone.tsx:238-243
**`changeRow` displays total balance instead of the day's change amount**
`phone.balance` holds the *total* wallet balance, not the delta from the last transaction. After a second `add` action the row would render `+ $10,000.00 · Today` even though only $5,000 was added, misleading visitors about what the display represents.
```suggestion
{hasFunds && (
<div className={styles.changeRow}>
<span className={styles.deltaUp}>+ $5,000.00</span>
<span className={styles.deltaChip}>Today</span>
</div>
)}
```
### Issue 3 of 4
mintlify/global-accounts/demo.mdx:29-43
**`postMessage` handler trusts messages from any origin**
`handleMessage` processes both `theme-request` and `theme-sync` events without validating `e.origin`. Any page that holds a reference to the docs window can send `{ type: 'theme-sync', theme: 'dark' }` and toggle the dark-mode class on the HTML element. The fix is a simple origin allowlist — e.g. skip messages unless `e.origin === window.location.origin || e.origin.endsWith('.lightspark.com')`.
### Issue 4 of 4
components/grid-wallet-demo/next.config.mjs:10-14
**TypeScript build errors are silently swallowed**
`ignoreBuildErrors: true` means `next build` will succeed even when there are type errors. The comment explains the current need (transitive imports from the local `@lightsparkdev/origin` source link), but it also masks any new type mistakes in this project's own code. Consider scoping the ignore to only the problematic external package paths once the origin package situation is resolved, or at minimum running `tsc --noEmit` in CI as a separate check so regressions don't go unnoticed.
Reviews (1): Last reviewed commit: "Add Global Accounts interactive demo ("S..." | Re-trigger Greptile
| id: 'tap', | ||
| label: 'Tap to pay', | ||
| desc: 'Spend at a store', | ||
| icon: 'tap', | ||
| available: (s) => s.hasCard, | ||
| }, |
There was a problem hiding this comment.
tap action can produce a negative wallet balance
The tap action's available guard only checks s.hasCard — there is no minimum balance requirement. A user can: (1) create an account, (2) immediately issue a card (no funds required), (3) tap → balance becomes -$7.32. With funds, repeated tapping also drives the balance negative because the same guard allows unlimited taps. The UI then displays a negative balance in the demo, which misrepresents what the Grid API would actually permit.
Prompt To Fix With AI
This is a comment left during a code review.
Path: components/grid-wallet-demo/src/data/actions.ts
Line: 88-93
Comment:
**`tap` action can produce a negative wallet balance**
The `tap` action's `available` guard only checks `s.hasCard` — there is no minimum balance requirement. A user can: (1) create an account, (2) immediately issue a card (no funds required), (3) tap → balance becomes -$7.32. With funds, repeated tapping also drives the balance negative because the same guard allows unlimited taps. The UI then displays a negative balance in the demo, which misrepresents what the Grid API would actually permit.
How can I resolve this? If you propose a fix, please make it concise.| {hasFunds && ( | ||
| <div className={styles.changeRow}> | ||
| <span className={styles.deltaUp}>+ {phone.balance}</span> | ||
| <span className={styles.deltaChip}>Today</span> | ||
| </div> | ||
| )} |
There was a problem hiding this comment.
changeRow displays total balance instead of the day's change amount
phone.balance holds the total wallet balance, not the delta from the last transaction. After a second add action the row would render + $10,000.00 · Today even though only $5,000 was added, misleading visitors about what the display represents.
| {hasFunds && ( | |
| <div className={styles.changeRow}> | |
| <span className={styles.deltaUp}>+ {phone.balance}</span> | |
| <span className={styles.deltaChip}>Today</span> | |
| </div> | |
| )} | |
| {hasFunds && ( | |
| <div className={styles.changeRow}> | |
| <span className={styles.deltaUp}>+ $5,000.00</span> | |
| <span className={styles.deltaChip}>Today</span> | |
| </div> | |
| )} |
Prompt To Fix With AI
This is a comment left during a code review.
Path: components/grid-wallet-demo/src/components/Phone.tsx
Line: 238-243
Comment:
**`changeRow` displays total balance instead of the day's change amount**
`phone.balance` holds the *total* wallet balance, not the delta from the last transaction. After a second `add` action the row would render `+ $10,000.00 · Today` even though only $5,000 was added, misleading visitors about what the display represents.
```suggestion
{hasFunds && (
<div className={styles.changeRow}>
<span className={styles.deltaUp}>+ $5,000.00</span>
<span className={styles.deltaChip}>Today</span>
</div>
)}
```
How can I resolve this? If you propose a fix, please make it concise.| if (e.data && e.data.type === 'theme-request') { | ||
| sendTheme(); | ||
| return; | ||
| } | ||
| if (e.data && e.data.type === 'theme-sync') { | ||
| const isDark = document.documentElement.classList.contains('dark'); | ||
| const wantsDark = e.data.theme === 'dark'; | ||
| if (isDark !== wantsDark) { | ||
| ignoreNextMutation = true; | ||
| document.documentElement.classList.toggle('dark'); | ||
| } | ||
| } | ||
| }; | ||
| window.addEventListener('message', handleMessage); | ||
|
|
There was a problem hiding this comment.
postMessage handler trusts messages from any origin
handleMessage processes both theme-request and theme-sync events without validating e.origin. Any page that holds a reference to the docs window can send { type: 'theme-sync', theme: 'dark' } and toggle the dark-mode class on the HTML element. The fix is a simple origin allowlist — e.g. skip messages unless e.origin === window.location.origin || e.origin.endsWith('.lightspark.com').
Prompt To Fix With AI
This is a comment left during a code review.
Path: mintlify/global-accounts/demo.mdx
Line: 29-43
Comment:
**`postMessage` handler trusts messages from any origin**
`handleMessage` processes both `theme-request` and `theme-sync` events without validating `e.origin`. Any page that holds a reference to the docs window can send `{ type: 'theme-sync', theme: 'dark' }` and toggle the dark-mode class on the HTML element. The fix is a simple origin allowlist — e.g. skip messages unless `e.origin === window.location.origin || e.origin.endsWith('.lightspark.com')`.
How can I resolve this? If you propose a fix, please make it concise.| typescript: { | ||
| // Origin is source-linked without its own node_modules, | ||
| // so its transitive type imports can't resolve from ../origin | ||
| ignoreBuildErrors: true, | ||
| }, |
There was a problem hiding this comment.
TypeScript build errors are silently swallowed
ignoreBuildErrors: true means next build will succeed even when there are type errors. The comment explains the current need (transitive imports from the local @lightsparkdev/origin source link), but it also masks any new type mistakes in this project's own code. Consider scoping the ignore to only the problematic external package paths once the origin package situation is resolved, or at minimum running tsc --noEmit in CI as a separate check so regressions don't go unnoticed.
Prompt To Fix With AI
This is a comment left during a code review.
Path: components/grid-wallet-demo/next.config.mjs
Line: 10-14
Comment:
**TypeScript build errors are silently swallowed**
`ignoreBuildErrors: true` means `next build` will succeed even when there are type errors. The comment explains the current need (transitive imports from the local `@lightsparkdev/origin` source link), but it also masks any new type mistakes in this project's own code. Consider scoping the ignore to only the problematic external package paths once the origin package situation is resolved, or at minimum running `tsc --noEmit` in CI as a separate check so regressions don't go unnoticed.
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
What this is
A first, working pass at an interactive Global Accounts demo for the docs — the neobank/wallet analog of the Flow Builder. It lives at
docs.lightspark.com/global-accounts/demo("See it live", directly under Introduction).A visitor picks a use case (Fintech/Neobank live; Social + Marketplace stubbed "Soon") and a sign-in method (Passkey / Google / Apple / Email / Phone), then drives a real wallet on a phone — create account → add money → send → cash out → issue a card → tap to pay — by clicking the phone itself, while the exact Grid API calls render in a panel beside it, in sync.
How it's wired (same pattern as the Flow Builder)
components/grid-wallet-demo, embedded by the docs in an<iframe>with light/darkpostMessagetheme sync — identical to howgrid-visualizerpowers/flow-builder.@lightsparkdev/origintokens,sidebar (475px) + canvas + CodePanel-style API log,central-icons, squircle corners, theEmptyCanvasdotted background, theHeader/Footer.breadneobank app; the card is a Robinhood-style dark glass card with a cinematic reveal animation.Docs changes (
mintlify/)global-accounts/demo.mdx— new page (mode: "custom"), iframe + theme-sync, auto-targetslocalhost:4000locally /grid-wallet-demo.vercel.appin prod.docs.json— nav entry under Overview (afterindex) + chrome-hide CSS for#wallet-demo-container.style.css— full-bleed iframe sizing (mirrors#flow-builder-container).Run locally
components/grid-wallet-demo(buildnpm run build, install--ignore-scriptsfor the central-icons license hook, same as grid-visualizer). The docs page is blank until this exists. Swap the URL indemo.mdxif different.What's faked (scripted, like the Flow Builder)
src/data/actions.ts.Designer focus areas (see HANDOFF.md)
The phone is ~90% of the craft (
Phone.tsx/Phone.module.scss): per-screen spacing/type, the card art + reveal timing, native-feeling auth sheets, per-persona theming, and a light-mode pass.🤖 Generated with Claude Code