Skip to content

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

Open
picsoulabs wants to merge 7 commits into
mainfrom
demo/global-accounts-see-it-live
Open

Global Accounts interactive demo ("See it live")#527
picsoulabs wants to merge 7 commits 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 Jun 5, 2026 6:04pm

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 adds a new standalone Next.js 14 app (components/grid-wallet-demo) that powers a Global Accounts interactive demo embedded as an iframe in the Lightspark docs under global-accounts/demo. It follows the same architecture as the existing grid-visualizer / Flow Builder — @lightsparkdev/origin tokens, sidebar + phone canvas + API log panel, and a postMessage theme-sync contract.

  • Interactive demo flow: visitor picks a persona and auth method, then drives a phone UI through sign-in → add money → send → withdraw → issue card → tap to pay; each step reveals the matching Grid API calls in a side panel.
  • Auth ceremonies: passkey uses real WebAuthn (navigator.credentials.create), Google/Apple use their hosted SDKs, and email/OTP use an interactive prompt — all client-only and not wired to Grid.
  • Docs integration: new global-accounts/demo.mdx (mode: "custom") + nav entry + full-bleed iframe CSS mirror the Flow Builder page.

Confidence Score: 5/5

Safe to merge — this is a self-contained docs demo component with no production data paths; all API calls are illustrative.

The change adds a new standalone Next.js app and a docs page. No existing code is modified in a breaking way, and the demo's illustrative API calls carry no real side effects. The two new findings (dead AmountScreen branches and uncapped amount entry) are cosmetic inconsistencies in a scripted happy-path demo — neither affects real user data or blocks the primary flow.

The auth singleton caching in src/lib/auth.ts and the silent Google sign-in freeze in Phone.tsx (both tracked in existing threads) are the most impactful issues for developer-audience visitors who commonly have ad blockers.

Important Files Changed

Filename Overview
components/grid-wallet-demo/src/app/page.tsx Main page orchestrates the interactive demo; add/send/withdraw use dedicated async handlers with Math.max(0,...) balance clamping, but card/tap still go through the old runAction path. Amount entry has no upper-bound guard.
components/grid-wallet-demo/src/components/Phone.tsx Phone UI renders correctly for all active flows; AmountScreen (non-interactive) mapped to addmoney/send/withdraw screen IDs in the render switch is unreachable dead code since those transient states are never set.
components/grid-wallet-demo/src/lib/auth.ts GIS/Apple promise singletons cache rejections permanently — once blocked by an ad blocker or network error, sign-in stays broken for the session (flagged in existing thread).
components/grid-wallet-demo/src/data/actions.ts Action definitions and runAction are only used for card/tap in the new flow; add/send/withdraw go through dedicated handlers in page.tsx.
components/grid-wallet-demo/src/data/apiCalls.ts Illustrative Grid API call shapes; unit conventions differ correctly between USD (cents) for add-money and USDB (1e6 sub-units) for send/withdraw.
mintlify/global-accounts/demo.mdx Iframe embed page; postMessage handler lacks origin validation (flagged in existing thread). Correctly sets iframe allow attributes for passkey/clipboard.
components/grid-wallet-demo/next.config.mjs CSP frame-ancestors correctly restricts embedders. ignoreBuildErrors: true suppresses TypeScript errors (flagged in existing thread).
components/grid-wallet-demo/src/hooks/useTheme.ts Theme hook matches the grid-visualizer contract; sends theme-request on mount and syncs via postMessage. Outgoing messages use '*' target origin, exposing only non-sensitive theme data.

Sequence Diagram

sequenceDiagram
    participant Docs as docs.lightspark.com
    participant IFrame as grid-wallet-demo iframe
    participant Phone as Phone (UI)
    participant Sidebar as Sidebar (UI)
    participant ApiPanel as API Steps Panel

    Docs->>IFrame: "load src=/?embed=true&theme=dark"
    IFrame->>Docs: "postMessage { type: 'theme-request' }"
    Docs->>IFrame: "postMessage { type: 'theme-sync', theme }"

    Note over Docs,IFrame: Theme sync established

    Sidebar->>Phone: User selects persona + auth method
    Sidebar->>Phone: User clicks Sign in action
    Phone->>Phone: Show auth screen → promptGoogle/promptPasskey/promptEmail/promptOtp
    Phone->>ApiPanel: pushCalls(signInCalls)
    Phone->>Phone: "wallet.created = true"

    loop Free actions
        Sidebar->>Phone: User triggers add / send / withdraw
        Phone->>Phone: AmountEntryScreen (interactive)
        Phone->>ApiPanel: pushCalls(addMoneyCalls / sendCalls / withdrawCalls)
        Phone->>Phone: Update balance + activity list
        Sidebar->>Phone: User triggers card (runSimulated)
        Phone->>Phone: "CardReveal animation → wallet.hasCard = true"
        Sidebar->>Phone: User triggers tap (runSimulated)
        Phone->>ApiPanel: pushCalls(transaction GET)
        Phone->>Phone: "TapScreen → balance -= $7.32"
    end

    Sidebar->>Phone: Start over → reset()
Loading
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
components/grid-wallet-demo/src/components/Phone.tsx:149-154
**Unreachable `AmountScreen` branches in `render()`**

The `addmoney`, `send`, and `withdraw` cases in `render()` map to a non-interactive `AmountScreen` with hardcoded amounts, but these `ScreenId` values are never written to `transient` in `page.tsx`. `runAdd`/`runSend`/`runWithdraw` set `transient.screen = 'creating'`, and `phoneFromState()` only ever returns `'auth'`, `'wallet'`, or `'card'`. These three cases are dead code — if a future change accidentally routes here, visitors will see a static, non-interactive screen that ignores their entered amount.

### Issue 2 of 2
components/grid-wallet-demo/src/app/page.tsx:211-232
**Amount entry has no upper-bound guard, producing inconsistent API log and activity entries**

`runSend` (and similarly `runWithdraw`) accepts any positive dollar value from `AmountEntryScreen`. If a visitor adds $5,000 and then enters $10,000 to send, `pushCalls(sendCalls(Math.round(dollars * 1e6)))` logs a 10,000 USDB transfer while the wallet state clamps to `Math.max(0, 500000 - 1000000) = 0` — so the API panel says $10K but the activity row and balance both reflect $0 deducted. Adding a `Math.min(dollars, wallet.balanceCents / 100)` cap in the `AmountEntryScreen` call (or as a `max` prop on the input) would keep the API panel consistent with the balance story the demo tells.

Reviews (7): Last reviewed commit: "Add local Apple sign-in dev setup" | Re-trigger Greptile

Comment thread components/grid-wallet-demo/src/data/actions.ts
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 thread mintlify/global-accounts/demo.mdx
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

- demo.mdx: remove JS line comment that broke Mintlify's MDX parse
  (rendered the page blank) and add phone icon to "See it live"
- add phone.svg sidebar icon
- vendor Suisse Intl fonts into public/fonts so Origin's @font-face
  resolves (was 404ing, falling back to system fonts)
- flow.ts: keep Google/Apple capitalized in the sign-in CTA
- Sidebar.tsx: mark Phone (SMS) sign-in as "Soon"

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread components/grid-wallet-demo/src/data/actions.ts
Comment thread components/grid-wallet-demo/src/components/Phone.tsx
Comment on lines +11 to +23
let gisPromise: Promise<void> | null = null;
export function loadGis(): Promise<void> {
if (gisPromise) return gisPromise;
gisPromise = new Promise((resolve, reject) => {
if ((window as any).google?.accounts?.id) return resolve();
const s = document.createElement('script');
s.src = 'https://accounts.google.com/gsi/client';
s.async = true;
s.onload = () => resolve();
s.onerror = () => reject(new Error('Failed to load Google Identity Services.'));
document.head.appendChild(s);
});
return gisPromise;
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 Rejected GIS promise is permanently cached, permanently blocking Google sign-in

gisPromise is a module-level singleton. If loadGis() rejects (GIS script blocked by an ad blocker, network hiccup, etc.), gisPromise is set to a rejected Promise — which is truthy. Every subsequent call to loadGis() hits the if (gisPromise) return gisPromise guard and immediately returns the same rejected promise, so there is no retry possible for the rest of the session.

In GoogleSignInScreen the rejection is swallowed by .catch(() => {}), leaving the GIS button never rendered. The googlePrompt promise created in promptGoogle never resolves, running stays true, and the demo locks up with no escape. Ad blockers blocking accounts.google.com are extremely common on developer-audience docs pages, making this trigger realistic. The same pattern affects applePromise/loadAppleAuth.

Prompt To Fix With AI
This is a comment left during a code review.
Path: components/grid-wallet-demo/src/lib/auth.ts
Line: 11-23

Comment:
**Rejected GIS promise is permanently cached, permanently blocking Google sign-in**

`gisPromise` is a module-level singleton. If `loadGis()` rejects (GIS script blocked by an ad blocker, network hiccup, etc.), `gisPromise` is set to a rejected `Promise` — which is truthy. Every subsequent call to `loadGis()` hits the `if (gisPromise) return gisPromise` guard and immediately returns the same rejected promise, so there is no retry possible for the rest of the session.

In `GoogleSignInScreen` the rejection is swallowed by `.catch(() => {})`, leaving the GIS button never rendered. The `googlePrompt` promise created in `promptGoogle` never resolves, `running` stays `true`, and the demo locks up with no escape. Ad blockers blocking `accounts.google.com` are extremely common on developer-audience docs pages, making this trigger realistic. The same pattern affects `applePromise`/`loadAppleAuth`.

How can I resolve this? If you propose a fix, please make it concise.

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