Skip to content

Global Accounts interactive demo ("See it live")#527

Open
picsoulabs wants to merge 1 commit into
mainfrom
demo/global-accounts-see-it-live
Open

Global Accounts interactive demo ("See it live")#527
picsoulabs wants to merge 1 commit into
mainfrom
demo/global-accounts-see-it-live

Conversation

@picsoulabs
Copy link
Copy Markdown
Contributor

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.

🎯 This is built to be perfected by design, not shipped as-is. Full context for the designer is in components/grid-wallet-demo/HANDOFF.md.

How it's wired (same pattern as the Flow Builder)

  • New standalone Next.js 14 app components/grid-wallet-demo, embedded by the docs in an <iframe> with light/dark postMessage theme sync — identical to how grid-visualizer powers /flow-builder.
  • Same shell + design system: @lightsparkdev/origin tokens, sidebar (475px) + canvas + CodePanel-style API log, central-icons, squircle corners, the EmptyCanvas dotted background, the Header/Footer.
  • Phone wallet ("Aurora") adapted from the bread neobank 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-targets localhost:4000 locally / grid-wallet-demo.vercel.app in prod.
  • docs.json — nav entry under Overview (after index) + chrome-hide CSS for #wallet-demo-container.
  • style.css — full-bleed iframe sizing (mirrors #flow-builder-container).

Run locally

cd components/grid-wallet-demo && npm install --ignore-scripts && npm run dev
# http://localhost:4000/?embed=true&theme=dark   (try light too)

⚠️ Before this goes live

  1. Vercel project for components/grid-wallet-demo (build npm run build, install --ignore-scripts for the central-icons license hook, same as grid-visualizer). The docs page is blank until this exists. Swap the URL in demo.mdx if different.
  2. Decide: API calls live against sandbox vs. the current scripted happy path (matches the Flow Builder today).

What's faked (scripted, like the Flow Builder)

  • API calls are illustrative (realistic shapes, not executed) — defined per-action in src/data/actions.ts.
  • Credential screens (OTP / Face ID / Google / Apple) auto-play.
  • Fixed amounts; Social/Marketplace personas are "Soon" stubs.

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

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>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 28, 2026

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

Project Deployment Actions Updated (UTC)
grid-flow-builder Ready Ready Preview, Comment May 28, 2026 9:05pm

Request Review

@mintlify
Copy link
Copy Markdown
Contributor

mintlify Bot commented May 28, 2026

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
Grid 🟢 Ready View Preview May 28, 2026, 9:08 PM

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 28, 2026

Greptile Summary

This PR introduces a new standalone Next.js 14 app (components/grid-wallet-demo) that powers an interactive Global Accounts demo embedded in the docs at /global-accounts/demo via <iframe> with bidirectional postMessage theme sync — following the same pattern as the existing Flow Builder / grid-visualizer.

  • New grid-wallet-demo app: Simulates a full wallet lifecycle (create account → add money → send → withdraw → issue card → tap to pay) driven by scripted API call sequences rendered in a live API log panel beside a phone UI.
  • Docs wiring (mintlify/): New demo.mdx page, nav entry in docs.json, full-bleed iframe CSS in style.css, and chrome-hiding script extension — mirroring the Flow Builder integration.
  • Known gaps (by design): API calls are illustrative (not live), Social/Marketplace personas are stubbed "Soon", and the PR description explicitly calls this a designer handoff candidate.

Confidence Score: 3/5

Safe to merge with the tap-action balance bug addressed; the demo works correctly on the happy path but a simple "create → issue card → tap" sequence produces a negative balance that misrepresents the API.

The tap action's availability guard has no floor on balanceCents, meaning a visitor who issues a card before funding the account — or taps repeatedly to drain it — will see a negative wallet balance in the demo. This directly contradicts the API behavior being illustrated. The postMessage handler in the docs page also processes messages from any origin without an origin check, though the impact there is limited to a cosmetic CSS toggle.

src/data/actions.ts (tap guard), mintlify/global-accounts/demo.mdx (postMessage origin validation), and next.config.mjs (TypeScript build-error suppression).

Security Review

  • Missing e.origin check in demo.mdx: The handleMessage listener accepts theme-request and theme-sync events from any origin with no e.origin validation. Any page holding a reference to the docs window can send { type: 'theme-sync', theme: 'dark' } and toggle the dark-mode CSS class on the docs host. Impact is cosmetic, but the handler should validate e.origin against known Lightspark/Mintlify domains.
  • postMessage sent with '*' target origin (useTheme.ts, demo.mdx): The frame-ancestors CSP in next.config.mjs restricts embedding, which meaningfully limits exposure, but narrowing these to the expected parent origin would eliminate residual risk.

Important Files Changed

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.

Fix All in Claude Code

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

Comment on lines +88 to +93
id: 'tap',
label: 'Tap to pay',
desc: 'Spend at a store',
icon: 'tap',
available: (s) => s.hasCard,
},
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.

P1 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.

Fix in Claude Code

Comment on lines +238 to +243
{hasFunds && (
<div className={styles.changeRow}>
<span className={styles.deltaUp}>+ {phone.balance}</span>
<span className={styles.deltaChip}>Today</span>
</div>
)}
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.

P2 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.

Suggested change
{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.

Fix in Claude Code

Comment on lines +29 to +43
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);

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.

P2 security 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.

Fix in Claude Code

Comment on lines +10 to +14
typescript: {
// Origin is source-linked without its own node_modules,
// so its transitive type imports can't resolve from ../origin
ignoreBuildErrors: true,
},
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.

P2 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!

Fix in Claude Code

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.

1 participant