# Trump Alpha — Frontend (trumpsignal.com) > Next.js 16 dashboard for the Trump Alpha backend. Real-time signals, > Hyperliquid position management UI, KOL talks-vs-trades view, Macro Vibes > regime panel. Read-only for the public; wallet-bound features (Pro) for > auto-trade subscribers. This is the AI-readable entry doc. Read this first on entering the repo. --- ## What this repo is - **Pure frontend** — Next.js 16 App Router, TypeScript, server components + client components, deployed to Vercel. - **Talks to a Python backend** at `https://api.trumpsignal.com` (or `localhost:8000` in dev). All trading state, signals, AI scoring, Hyperliquid integration lives in the sibling **`/Users/k/Public/Claude/ backend`** repo. See its CLAUDE.md. - **No server-side trading logic** lives here. Frontend is a thin layer over the API + WebSocket — even "open a position" routes to a backend endpoint that does the signed-request verification + HL call. --- ## Stack quirks (Next.js 16 specific) - **`proxy.ts` replaces `middleware.ts`** in Next.js 16. Routes that need CORS / locale rewriting go through `proxy.ts` at the root. - **`params` is async (`Promise<{...}>`)** in all dynamic routes. ALL page components await it: ```tsx export default async function Page({ params }: { params: Promise<{ locale: string }> }) { const { locale } = await params ... } ``` Forgetting the `await` redirects to `/undefined/...` silently. Several pages were broken this way pre-v2.0 — be alert if you copy older patterns. - **i18n is shelved.** The `[locale]` segment exists, `messages/zh.json` exists, but Chinese branches are kept as dead code with a comment `// i18n shelved — see messages/zh.json`. Don't activate without an ADR. Currently `isZh = false` is hardcoded in metadata generators. - **`runtime = 'edge'`** on a few routes (`app/icon.tsx`, OG image generator). Edge runtime has no fs / no Node APIs — don't import server-side libs there. --- ## Route map ``` app/ ├── layout.tsx Root layout: JSON-LD schema.org, fonts, meta ├── page.tsx / (root) → redirects to /en ├── icon.tsx Dynamic favicon (α on dark bg, edge runtime) ├── apple-icon.tsx PWA install icon ├── opengraph-image.tsx OG card generator (1200x630) ├── manifest.ts PWA manifest ├── sitemap.ts SEO sitemap.xml ├── robots.ts robots.txt └── api/ └── proxy/[...path]/ Catch-all proxy to backend (CORS workaround) app/[locale]/ ├── page.tsx Landing — pinned signals, live ticker, Trump feed ├── DashboardClient.tsx Main 'use client' wrapper for the home dashboard ├── trump/page.tsx /en/trump — Trump Truth Social signal stream ├── macro/page.tsx /en/macro — Macro Vibes regime panel ★ renamed from /btc ├── kol/page.tsx /en/kol — KOL talks-vs-trades ├── trades/page.tsx /en/trades — closed trade history ├── posts/page.tsx /en/posts — all Posts (signal + Trump) ├── archive/page.tsx /en/archive — historical / paginated ├── settings/page.tsx /en/settings — wallet connect, HL key, prefs ├── analytics/page.tsx /en/analytics — performance metrics ├── case-studies/page.tsx /en/case-studies — documented event walkthroughs ├── methodology/page.tsx /en/methodology — how signals work ├── glossary/page.tsx /en/glossary — AHR999, MVRV-Z, KOL, etc. ├── privacy/page.tsx ├── terms/page.tsx ├── contact/page.tsx └── globals.css Design system tokens + .macro-* classes ``` --- ## Components map (the meaningful ones) ``` components/ ├── nav/Navbar.tsx Top nav + tabs (Trump | Macro Vibes | KOL ...) ├── signals/ │ ├── SignalMonitor.tsx Live signal stream (WS-driven) │ ├── SourceChips.tsx Filter chips per source │ ├── SystemControl.tsx Sys1/sys2 toggle widgets, Auto-Trade switch │ └── ConfirmCloseTrade.tsx Modal for manual close ├── dashboard/ │ ├── PostCards.tsx Trump post cards │ ├── SignalMonitor.tsx (older — being consolidated) │ └── BtcReversalAlert.tsx Pinned alert when sys2 fires ├── btc/MacroPanel.tsx ★ 8-indicator Macro Vibes layout (4 sections, │ composite needle, threshold chips, peak-trail viz) ├── positions/ │ ├── OpenPositions.tsx Polls /positions/open + /positions/today every 15s │ └── TradeCard.tsx Single trade row w/ grow toggle + close button ├── kol/KolDigest.tsx Daily KOL summary widget ├── telegram/ │ ├── TelegramCard.tsx Settings — connect via 6-char code │ └── SignConfirmSheet.tsx EIP-191 signed request preview ├── nav/WalletConnect.tsx Wagmi-style wallet connect (MetaMask etc.) ├── ui/ │ ├── InfoTip.tsx CSS-only tooltip with `?` icon │ ├── PageHint.tsx Strong page subtitle (replaces page-sub) │ ├── Toast.tsx │ └── Modal.tsx └── ws/WsProvider.tsx WebSocket singleton context provider ``` --- ## API layer All API calls go through **`lib/api.ts`**. Single `fetchJson` wrapper: - Reads base URL from env (`NEXT_PUBLIC_API_URL`) or proxies through `/api/proxy/[...path]/` for same-origin CORS-free calls. - Wallet-scoped reads (`/user/.../public`, `/positions/open`) pass wallet as query param + signed ts/sig. - Mutations (subscribe, manual_close, set_auto_trade, manual-window) build an EIP-191 signed payload via wagmi → POST signed envelope. **Auth model**: no sessions, no cookies. Wallet address + signed timestamp + signature on every mutating call. Verification lives in the backend (`signed_request.verify_signed_request`). The same wallet that signed the HL API key envelope at subscribe time is the only one that can later mutate the subscription. --- ## State management - **One small global store** — `store/dashboard.ts` (Zustand, `useDashboardStore`). Holds cross-page UI/session state: selected post, chart asset/timeframe, wallet address, subscription + bot-readiness flags, masked HL key hint, and the live-price map. Everything else stays tree-local (state + props) — don't grow this store into a catch-all; page-specific data belongs in the page. - **SWR-style cache** in `lib/cache.ts` for the API-heavy pages. - **WebSocket** singleton via `WsProvider` — used by `SignalMonitor` and live price tickers. - **localStorage** holds wallet address only (for re-connect UX). HL API keys NEVER touch the client — they're submitted once at subscribe, sent encrypted, and decrypted only inside the backend. --- ## Design system Tokens in `app/[locale]/globals.css`: - **Colors** — `oklch()` color space throughout. Brand orange = `#f5a524`, brand dark = `#0a0907`. `var(--up)` green / `var(--down)` red / `var(--ink-1..3)` text tiers. - **Dark mode** via `[data-theme="dark"]` attribute on ``. Most components use `oklch(L% c h)` so dark/light switching is automatic. - **Macro Vibes specific** — extensive `.macro-*` classes for the 8-indicator layout. Threshold chips have `:not(.active) { opacity: 0.45 }` and `.active` is tone-filled (green/red/amber). --- ## Things that LOOK like bugs but aren't - **Old code references "/en/btc"** — Macro Vibes was renamed from "BTC Signal" in v2.0. All routes are now `/en/macro`. `app/[locale]/btc/page.tsx` EXISTS intentionally as a permanent redirect to `/en/macro` — that's fine. If you find a stale `/en/btc` hardcoded link anywhere else, it's a real bug — fix it. - **`isZh = false` in metadata generators** — i18n was shelved pre-launch. See "Stack quirks" above. - **Multiple `proxy.ts` patterns** — Next.js 16 split middleware behaviour. Keep proxy logic in the single `proxy.ts` at root. - **`'use client'` directives on many pages** — necessary for client-side WS / wallet interactions. Don't try to convert without verifying the WS + wagmi imports work in server components. - **`telegram.send_message` accepting `int | str` for chat_id** — the backend uses string form for the public channel (`"@trumpalpha"`) and integer for private user chats. Both are valid; no frontend change needed. - **`x-forwarded-for` is deleted in the proxy** — KNOWN BUG (see below). Don't "fix" it by just removing the delete; the correct fix is to relay the real client IP. See Open Known Issues. --- ## Open known issues (frontend) - ~~**Rate limit bypass via proxy** (FIXED 2026-05-29):~~ `route.ts` no longer deletes `x-forwarded-for`. It now reads the real client IP from the incoming `x-forwarded-for` or `x-real-ip` header (set by Vercel/CDN) and sets it on the upstream request so `slowapi` can distinguish individual clients. - ~~**`signMessageAsync` in `OpenPositions` polling deps** (MEDIUM, FIXED 2026-05-29):~~ Removed `signMessageAsync` and `isZh` from the `useEffect` dependency array in `components/positions/OpenPositions.tsx`. Neither is used inside the effect; including them caused wagmi reference churn to restart the 15s poll timer. - ~~**Stale types after BUG-08/BUG-14** (FE-01–06, FIXED 2026-05-29):~~ - `types/index.ts`: `price_impact.asset` widened from `'BTC' | 'ETH'` to `string` (BUG-14 now tracks the actually-traded asset which may be SOL/TRUMP/etc.). - `lib/useRealtimeData.ts`: `PriceMsg.asset` and `Handlers.onPrice` widened to `string` (BUG-08 expanded the WS price stream to all ASSET_MAP entries). - `store/dashboard.ts`: `livePrices` changed from `{ BTC; ETH }` to `Record`; `setLivePrice` param widened to `string`. - `lib/api.ts`: `getPrices` asset param widened to `string`. - `components/trades/TradeTable.tsx`: hardcoded `ASSETS = ['all','BTC','ETH','SOL']` replaced by a dynamic `useMemo` that builds the asset list from the actual trade set — new perps (TRUMP, BNB, etc.) appear in the filter automatically. - `DashboardClient.tsx` + `TradesPageClient.tsx`: removed `isZh` from `useEffect` deps (compile-time constant; including it restarted effects on every render). - ~~**Interaction/button bugs sweep** (UI-A..G, FIXED 2026-05-29):~~ - **A** `OpenPositions.tsx` close-modal backdrop now dismisses in `'err'` state too (previously stuck — only `'idle'` dismissed). State is reset on dismiss so the next open is clean. - **B** `confirmCloseTrade` distinguishes user-cancelled signature (`isUserRejection`) from real failure: cancellation closes the modal silently; real errors stay open in `'err'` state with the message. - **C** `SystemControl.flipAuto` claims `busy=true` **before** `await confirmSign(...)`. Rapid double-click on the ON/OFF pill no longer slips past the guard. The slot is released if the user cancels the sheet. - **D** `BotConfigPanel` "Rotate" API-key button resets `keyState`/`keyErr` so a previously-failed save's red error doesn't bleed into the new edit. - **E** Paper/Live `Switch` clears `subErr` when toggled so a stale subscribe-error doesn't sit next to the new choice. - **F** `confirmSign` keeps a module-scope `_activeCancel`; a second call while a sheet is mounted resolves the first promise with `false` instead of leaving its caller hanging forever. All 9 call sites verified to cleanly handle the `false` return (`setBusy(false)` / `setLoadState('idle')`). - **G** `OpenPositions.toggleGrow` errors now write to a dedicated `growErr` state with a 4-second auto-clear, instead of squatting in the panel-header `err` banner shared with the 15-s poll error. ## How to verify changes locally ```bash # Type check (must pass with 0 errors) npx tsc --noEmit # Production build (catches missing pages, stale imports) rm -rf .next && npx next build # Dev server — NOTE: this app runs on port 3001 (see package.json: # `next dev -p 3001` / `next start -p 3001`), NOT the Next.js default 3000. # Any external doc, script, or healthcheck must target 3001. npm run dev # Pages-200 sweep (every locale page returns 200) — port 3001 for p in /en /en/trump /en/macro /en/kol /en/trades /en/posts /en/settings \ /en/methodology /en/glossary /en/case-studies /en/privacy /en/terms; do echo "$p $(curl -so /dev/null -w '%{http_code}' http://localhost:3001$p)" done ``` --- ## SEO / GEO surface (don't break) - **`app/layout.tsx`** has JSON-LD schema.org markup (Organization, SoftwareApplication, FAQ). Search engines + AI crawlers read this. - **`public/llms.txt`** — canonical doc for AI crawlers (Perplexity, ChatGPT browse, Google AI Overviews). Edit BOTH this file AND `layout.tsx` when renaming product features. - **`app/sitemap.ts`** — sitemap.xml. Add new routes here. - **OG image** — `app/opengraph-image.tsx` dynamically renders the social card. Keep the brand pill list in sync with current features. --- ## Coupling to backend - **API contract lives in TypeScript types under `lib/api.ts`** (e.g. `OpenPositionsResponse`, `TodayStats`, `BotTrade`). These must match the backend Pydantic models in `app/api/*/positions.py` etc. - When backend adds a field (e.g. `BotTrade.released_at`), this repo's `lib/api.ts` types may need updating to surface it in the UI. Currently released trades are FILTERED OUT by the backend's `/positions/open` — they don't appear here, so no UI for "managed vs released" yet. - When backend renames a route (e.g. `/btc/page.tsx` → `/macro/page.tsx`), search for ALL references (including telegram deep-link map in `backend/app/services/telegram.py`). --- ## Deploy Vercel auto-deploys `main` branch pushes. No special build flags. Env vars in Vercel dashboard: - `NEXT_PUBLIC_API_URL` — backend HTTPS origin - `NEXT_PUBLIC_SITE_URL` — canonical site URL (used for OG, sitemap) - `NEXT_PUBLIC_WS_URL` — WebSocket endpoint (usually `wss://api...`) --- ## Sibling repo - **`/Users/k/Public/Claude/backend`** — Python/FastAPI backend, includes Hyperliquid integration, Telegram bot, AI scoring (DeepSeek), all signal scanners, the adoption/release flow. See its CLAUDE.md.