|
| 1 | +# Grid Visualizer |
| 2 | + |
| 3 | +## Product Overview |
| 4 | + |
| 5 | +Grid Visualizer is an interactive tool that helps integrators understand and get started with the Lightspark Grid API. It walks users through configuring a payment flow — source, destination, funding model, audience — and generates a visual flow diagram plus API call sequences. |
| 6 | + |
| 7 | +**Core insight**: Composable money-movement APIs like Grid are extremely hard to consume because they must expose everything. Grid Visualizer short-circuits this by asking the right questions and surfacing only what the user cares about. Specify your exact use case, get back a tailored list of API calls. |
| 8 | + |
| 9 | +## Audience |
| 10 | + |
| 11 | +- **Integrators/developers** evaluating Grid or learning the API |
| 12 | +- **Sales team** for demo conversations showing how easy integration is |
| 13 | +- **Existing customers** exploring new payment flows and capabilities |
| 14 | + |
| 15 | +## Entry Points |
| 16 | + |
| 17 | +- **Docs site**: grid.lightspark.com |
| 18 | +- **Marketing site**: lightspark.com/grid |
| 19 | +- **Direct links** shared in sales conversations |
| 20 | +- Currently deployed at: https://grid-visualizer-opal.vercel.app/ |
| 21 | + |
| 22 | +## Tech Stack |
| 23 | + |
| 24 | +- **Next.js** 14 (App Router) / **React** 18 / **TypeScript** 5 |
| 25 | +- **SCSS** with CSS Modules for component scoping |
| 26 | +- **@lightsparkdev/origin** — Lightspark's design system (npm package) |
| 27 | +- **@xyflow/react** — Flow diagram visualization |
| 28 | +- **motion** — Animations |
| 29 | +- **react-syntax-highlighter** — Code display (oneDark theme) |
| 30 | +- **match-sorter** — Fuzzy search for currency search |
| 31 | +- **@tanstack/react-table**, **@base-ui/react**, **ajv**, **clsx** |
| 32 | +- Deployed on **Vercel** with `--ignore-scripts` workaround for icon license issues |
| 33 | + |
| 34 | +## Design System |
| 35 | + |
| 36 | +Uses Lightspark **Origin** (`@lightsparkdev/origin`). Tokens imported via SCSS in `src/app/globals.scss`. |
| 37 | + |
| 38 | +**Tokens in use:** |
| 39 | +- Colors: `--text-primary`, `--text-secondary`, `--text-tertiary`, `--surface-primary`, `--surface-secondary` |
| 40 | +- Spacing: `--spacing-sm`, `--spacing-md`, `--spacing-lg`, `--spacing-xl`, `--spacing-3xl` |
| 41 | +- Border radius: `--corner-radius-md` |
| 42 | +- Typography: `--font-family-sans`, `--font-weight-book` |
| 43 | +- Mixins: `@include headline`, `@include body`, `@include body-sm`, `@include label` |
| 44 | + |
| 45 | +**Color coding:** Blue for fiat flows, purple for crypto flows. |
| 46 | + |
| 47 | +## Architecture |
| 48 | + |
| 49 | +### State Machine |
| 50 | + |
| 51 | +The wizard uses a **useReducer** state machine in `src/app/page.tsx`. Single source of truth for all wizard state. |
| 52 | + |
| 53 | +**State shape:** |
| 54 | +```typescript |
| 55 | +interface State { |
| 56 | + step: Step; |
| 57 | + sourceType: 'fiat' | 'crypto' | null; |
| 58 | + sourceCurrency: string | null; |
| 59 | + sourceRails: string[] | null; |
| 60 | + source: Selection | null; |
| 61 | + destType: 'fiat' | 'crypto' | null; |
| 62 | + destCurrency: string | null; |
| 63 | + destRails: string[] | null; |
| 64 | + destination: Selection | null; |
| 65 | + destAccount: 'external' | 'internal' | null; |
| 66 | + funding: 'pre-funded' | 'jit' | null; |
| 67 | + audience: 'human' | 'agent' | null; |
| 68 | +} |
| 69 | +``` |
| 70 | + |
| 71 | +**Actions:** `SET_SOURCE_TYPE`, `SET_SOURCE_CURRENCY`, `SET_SOURCE_RAIL`, `SET_DEST_TYPE`, `SET_DEST_CURRENCY`, `SET_DEST_RAIL`, `SET_DEST_ACCOUNT`, `SET_FUNDING`, `SET_AUDIENCE`, `RESET_SOURCE`, `RESET_DEST`, `RESET_FUNDING`, `RESET_ALL` |
| 72 | + |
| 73 | +### Smart Flow Logic |
| 74 | + |
| 75 | +- Automatically skips rail selection if only one rail/network is available |
| 76 | +- `dest-account` step only shown for single-switch scenarios (not fiat-to-fiat cross-currency) |
| 77 | +- `funding` step skipped for internal same-currency transfers |
| 78 | +- Two-switch logic: fiat-to-fiat cross-currency always requires external account |
| 79 | + |
| 80 | +## Key Directories |
| 81 | + |
| 82 | +``` |
| 83 | +src/ |
| 84 | +├── app/ |
| 85 | +│ ├── page.tsx # Main wizard (useReducer state machine) |
| 86 | +│ ├── layout.tsx # Root layout with metadata |
| 87 | +│ ├── globals.scss # Global styles, imports Origin tokens |
| 88 | +│ └── page.module.scss # Page-specific styles |
| 89 | +├── components/ |
| 90 | +│ ├── TypeSelector/ # Fiat vs Crypto card selection |
| 91 | +│ ├── CardSelector/ # Generic card-based option selector |
| 92 | +│ ├── CurrencySearch/ # Fiat currency search with fuzzy matching |
| 93 | +│ ├── OptionSelector/ # Pill-based option selector |
| 94 | +│ ├── SelectionSummary/ # Summary cards showing current selections |
| 95 | +│ ├── FlowVisualization/ # Flow diagram (@xyflow/react) |
| 96 | +│ │ └── CustomNode.tsx # Custom node styling |
| 97 | +│ └── OutputDisplay/ # API code generation and display |
| 98 | +├── data/ |
| 99 | +│ ├── currencies.ts # 12 fiat currencies with payment rails |
| 100 | +│ └── crypto.ts # 3 crypto assets (BTC, USDC, USDT) with networks |
| 101 | +└── stubs/ |
| 102 | + └── central-icons.js # Icon package stub (license workaround) |
| 103 | +refs/ # Internal reference screenshots (gitignored) |
| 104 | +``` |
| 105 | + |
| 106 | +## UX Flow |
| 107 | + |
| 108 | +### 9 steps across 4 step groups: |
| 109 | + |
| 110 | +**Group 1 — Set up the source** |
| 111 | +1. **source-type**: Fiat or Crypto (`TypeSelector`) |
| 112 | +2. **source-detail**: Currency/asset selection (`CurrencySearch` for fiat, `OptionSelector` for crypto) |
| 113 | +3. **source-rail**: Rail/network selection — skipped if only one option (`OptionSelector`) |
| 114 | + |
| 115 | +**Group 2 — Set up the destination** |
| 116 | +4. **dest-type**: Fiat or Crypto (`TypeSelector`) |
| 117 | +5. **dest-detail**: Currency/asset selection (excludes source currency/asset if same type) |
| 118 | +6. **dest-account**: External or Internal account — conditional (`CardSelector`) |
| 119 | +7. **dest-rail**: Rail/network selection — conditional (`OptionSelector`) |
| 120 | + |
| 121 | +**Group 3 — Choose funding model** |
| 122 | +8. **funding**: Pre-funded or Just-in-time — skipped for internal same-currency (`CardSelector`) |
| 123 | + |
| 124 | +**Group 4 — Choose output format** |
| 125 | +9. **audience**: Human (curl commands) or AI Agent (structured JSON) (`CardSelector`) |
| 126 | +10. **output**: Flow diagram + generated API code sequences |
| 127 | + |
| 128 | +## Code Conventions |
| 129 | + |
| 130 | +- **Components**: PascalCase directories with `Component.tsx` + `Component.module.scss` |
| 131 | +- **CSS Modules**: imported as `styles`, used as `styles.className` |
| 132 | +- **Actions**: SCREAMING_SNAKE_CASE |
| 133 | +- **Step names**: kebab-case (`source-type`, `dest-detail`) |
| 134 | +- **Conditional classes**: `clsx` utility |
| 135 | +- **Type safety**: Union types for steps, discriminated unions for actions |
| 136 | +- **Always use design tokens** for spacing, colors, borders, typography — never hardcode values unless explicitly given a one-off value by the designer |
| 137 | + |
| 138 | +### Icons |
| 139 | + |
| 140 | +**Never hand-draw SVG paths.** Always use the central icon library directly. |
| 141 | + |
| 142 | +Import icons from `@central-icons-react/round-outlined-radius-0-stroke-1.5` (the default variant): |
| 143 | + |
| 144 | +```tsx |
| 145 | +import { IconChevronBottom } from '@central-icons-react/round-outlined-radius-0-stroke-1.5/IconChevronBottom'; |
| 146 | + |
| 147 | +<IconChevronBottom size={12} /> |
| 148 | +``` |
| 149 | + |
| 150 | +Props: `size` (number, default 24), `color` (string, default `currentColor`). |
| 151 | + |
| 152 | +You can also use Origin's `CentralIcon` wrapper: `<CentralIcon name="IconChevronBottom" size={12} />`. |
| 153 | + |
| 154 | +## Design Principles |
| 155 | + |
| 156 | +These guide every design and implementation decision. When in doubt, refer back here. |
| 157 | + |
| 158 | +1. **Utility first, showcase as a byproduct.** The tool is most impressive when it's most useful. A developer who gets working curl commands in 30 seconds IS the demo. Don't optimize for spectacle at the cost of speed. |
| 159 | + |
| 160 | +2. **Every screen should have one job.** If a screen is doing two things (e.g. choosing AND previewing), it's doing too much. Identify the single job and cut everything that doesn't serve it. |
| 161 | + |
| 162 | +3. **The right panel earns its space.** It starts minimal and gets richer as the user invests choices. Don't front-load it with content the user hasn't committed to yet. The build-up IS the payoff. |
| 163 | + |
| 164 | +4. **Reduce decisions, don't add them.** Before adding any element, ask: does this help the user decide faster, or does it give them another thing to evaluate? Preset flows = fewer decisions (good). Preset previews = more decisions (bad — now they're comparing instead of picking). |
| 165 | + |
| 166 | +5. **The output is the product.** Everything before the output screen is a funnel. Optimize for getting there, not for making the journey scenic. |
| 167 | + |
| 168 | +## Design Direction |
| 169 | + |
| 170 | +Split layout with left panel (wizard controls) and right panel (live preview canvas). Figma-first workflow — mock up in Figma, review, then implement. |
| 171 | + |
| 172 | +**Start screen**: Preset flow cards (fastest path) above manual fiat/crypto builder (escape hatch). Right panel is minimal — a seed node, not a preview. The screen's one job: get the user into a flow. |
| 173 | + |
| 174 | +**Mid-flow**: Left panel shows breadcrumb progress + summary of prior choices (editable) + current step question. Right panel builds up with connected nodes as choices are made. Pending nodes shown as dashed outlines. |
| 175 | + |
| 176 | +**Output screen**: The payoff. Flow summary bar + numbered API steps with syntax-highlighted code blocks on the left. Complete flow diagram on the right. Actions: copy, edit flow, open in dashboard. |
| 177 | + |
| 178 | +**Visual polish** (in progress): Flags for currencies, icons for assets/rails, smooth transitions via motion library. |
| 179 | + |
| 180 | +**Parked ideas** (revisit after core flow is polished): |
| 181 | +- Natural language input field backed by LLM — feasible (constrained output space, single API call to parse intent into wizard state), but core flow comes first |
| 182 | +- Searchable/filterable presets as a lighter alternative to NL input |
| 183 | + |
| 184 | +## Grid API Source of Truth |
| 185 | + |
| 186 | +The Grid API docs and OpenAPI spec are at `/Users/patcapulong/Development/Projects/Grid Docs/mintlify`. The OpenAPI spec is the definitive source for account types, endpoints, and request/response schemas. |
| 187 | + |
| 188 | +### Key API Facts |
| 189 | + |
| 190 | +- **Base URL**: `https://api.lightspark.com/grid/2025-10-13` |
| 191 | +- **Auth**: HTTP Basic Auth (`client_id:client_secret`) |
| 192 | +- **Rails are NOT user-selectable** — Grid auto-routes based on currency, country, amount |
| 193 | +- **JIT funding only works with instant rails** (RTP, FedNow, SEPA Instant, PIX, SPEI, UPI, Faster Payments, PayNow, FAST, all crypto). ACH, Wire, generic Bank Transfer are NOT JIT-eligible. |
| 194 | + |
| 195 | +### Supported Account Types (ExternalAccountType enum) |
| 196 | + |
| 197 | +Fiat: `US_ACCOUNT`, `CLABE`, `PIX`, `IBAN`, `UPI`, `NGN_ACCOUNT`, `CAD_ACCOUNT`, `GBP_ACCOUNT`, `PHP_ACCOUNT`, `SGD_ACCOUNT` |
| 198 | +BTC: `SPARK_WALLET`, `LIGHTNING` |
| 199 | +Stablecoins: `SOLANA_WALLET` (USDC), `TRON_WALLET` (USDT), `POLYGON_WALLET` (USDC), `BASE_WALLET` (USDC) |
| 200 | + |
| 201 | +### Key Endpoints |
| 202 | + |
| 203 | +- `POST /customers` — Create customer |
| 204 | +- `POST /customers/external-accounts` — Register external account |
| 205 | +- `GET /customers/internal-accounts` — List customer internal accounts |
| 206 | +- `GET /platform/internal-accounts` — List platform internal accounts |
| 207 | +- `POST /quotes` — Create cross-currency transfer quote |
| 208 | +- `POST /quotes/{quoteId}/execute` — Execute quote |
| 209 | +- `POST /transfer-out` — Same-currency internal → external |
| 210 | +- `POST /transfer-in` — Same-currency external → internal |
| 211 | + |
| 212 | +### Quote Request Shape |
| 213 | + |
| 214 | +```json |
| 215 | +{ |
| 216 | + "source": { "sourceType": "ACCOUNT" | "REALTIME_FUNDING", ... }, |
| 217 | + "destination": { "destinationType": "ACCOUNT" | "EXTERNAL_ACCOUNT_DETAILS" | "UMA_ADDRESS", ... }, |
| 218 | + "lockedCurrencySide": "SENDING" | "RECEIVING", |
| 219 | + "lockedCurrencyAmount": 10000 |
| 220 | +} |
| 221 | +``` |
| 222 | + |
| 223 | +### Sync Automation |
| 224 | + |
| 225 | +Grid Docs repo has a nightly GitHub Action (`docs-sync.yml`) that uses `anthropics/claude-code-action@v1` to detect OpenAPI changes and auto-create PRs. We plan to create a similar workflow for this tool. |
| 226 | + |
| 227 | +## Plans |
| 228 | + |
| 229 | +- **UI redesign plan**: `/Users/patcapulong/.claude/plans/validated-wobbling-dragon.md` |
| 230 | +- **Make it real plan**: `/Users/patcapulong/.claude/plans/make-it-real.md` |
| 231 | +- **Pre-migration checklist**: `TODO-before-migration.md` (OG images, metadata, etc.) |
| 232 | + |
| 233 | +## Open Questions |
| 234 | + |
| 235 | +- **Mobile responsiveness**: Strategy TBD |
| 236 | + |
| 237 | +### TODO: Questions for Victor |
| 238 | + |
| 239 | +These block accuracy of generated code and flow diagrams. Once answered, updates are small (data arrays + enum values). |
| 240 | + |
| 241 | +1. **Crypto source region — why does Grid need it?** |
| 242 | + - When crypto is source, we ask "Where's your USDC?" and show a region picker. The region determines which Grid Switch the user is assigned to (e.g., USD Grid Switch vs EUR Grid Switch). |
| 243 | + - Is this compliance/KYC, routing optimization, pricing, or all three? |
| 244 | + - Does it affect the actual API calls, or just the internal routing? |
| 245 | + - UX implication: should we block the flow on this, or default to something sensible? |
| 246 | + |
| 247 | +2. **USDB — external or internal only?** |
| 248 | + - USDB is NOT in the `ExternalAccountType` enum in the OpenAPI spec. |
| 249 | + - It appears in internal account funding instructions (`assetType: "USDB"` with `SPARK_WALLET`). |
| 250 | + - Should we keep it in the picker as a full crypto asset, limit it to internal-only flows, or remove it? |
| 251 | + |
| 252 | +3. **Bank Transfer countries — what are the actual `ExternalAccountType` values?** |
| 253 | + - We added 20 currencies from the docs (GHS, KES, ZAR, CNY, IDR, THB, etc.) with best-guess account types (`GHS_ACCOUNT`, `KES_ACCOUNT`, etc.) and generic fields (`accountNumber` + `bankName`). |
| 254 | + - What are the real `ExternalAccountType` enum values for these countries? |
| 255 | + - Do they all use the same generic fields, or do some have country-specific requirements? |
| 256 | + - Are any of these countries on instant rails (would affect JIT eligibility)? |
| 257 | + - Countries added: Ghana, Kenya, South Africa, Botswana, Tanzania, Uganda, Malawi, Zambia, China, Hong Kong, Indonesia, South Korea, Malaysia, Thailand, Vietnam, Sri Lanka, Costa Rica, DR Congo, plus XOF (West Africa) and XAF (Central Africa) regions. |
| 258 | + |
| 259 | +## Team Context |
| 260 | + |
| 261 | +- Victor built the initial tool; Pat (design/UX) is leading design polish |
| 262 | +- Team sees this as useful for sales conversations, developer onboarding, and potentially automated docs |
| 263 | +- Jeremy has insights on JIT funding rules and internal/external account mechanics |
| 264 | +- Grid Docs repo has OpenAPI spec as source of truth + automated sync via Claude Code |
| 265 | + |
| 266 | +## Figma Bridge (MCP) |
| 267 | + |
| 268 | +When building UI from Figma designs, use the figma-bridge MCP server. Tool priority: |
| 269 | + |
| 270 | +1. **build_tree / build_ir** — create entire node hierarchies in one call. Prefer `build_ir` for token efficiency. |
| 271 | +2. **patch_tree** — update existing nodes. Only specify changed properties. |
| 272 | +3. **figma_execute** — batch 2+ atomic operations with ref chaining. Use `summary: true` for compact results. |
| 273 | +4. **figma_help** — discover which action to use via `figma_execute`. Primary action discovery tool. |
| 274 | + |
| 275 | +**Token budget tips:** |
| 276 | +- Start with `figma_design_system_summary` (~200 tokens) instead of raw `list_variables` (~5000 tokens). |
| 277 | +- Use `figma_file_index` (cached 60s) for name-to-ID resolution instead of repeated `find_variable`/`find_style` calls. |
| 278 | +- Use `build_ir` over `build_tree` for 3-5x token savings on node creation. |
| 279 | +- Use `batch_get_node_properties` instead of multiple individual calls. |
| 280 | +- Pass `summary: true` on `figma_execute` for compact receipts. |
| 281 | + |
| 282 | +**Critical gotchas:** |
| 283 | +- Auto-layout resets sizing to HUG — the bridge handles this automatically. |
| 284 | +- `clone()` drops variable bindings — the bridge's `copyBoundVariables` re-applies them. |
| 285 | +- Font loading is mandatory before text mutations. |
| 286 | +- `appendChild` transforms coordinates — set `x`/`y` after, not before. |
| 287 | + |
| 288 | +**Ref chaining:** Assign `ref: "myName"` to capture a result, reference later with `nodeRef`, `parentRef`, etc. |
| 289 | + |
| 290 | +## Special Configuration |
| 291 | + |
| 292 | +- **Icon workaround**: `src/stubs/central-icons.js` stubs out `@central-icons-react` to avoid license issues. Webpack config in `next.config.mjs` does module replacement. |
| 293 | +- **Vercel**: `vercel.json` uses `--ignore-scripts` flag |
| 294 | +- **Origin transpiling**: `next.config.mjs` configures `transpilePackages` for the Origin package |
| 295 | +- **Grid Docs path**: `/Users/patcapulong/Development/Projects/Grid Docs/mintlify` |
0 commit comments