Files
trumpsignal-frontend/CLAUDE.md
T
k 4c3c8c6f87 KOL count: 29 → 25 across marketing/SEO copy
Backend KOL_FEEDS trimmed from 29 to 25 (dead feeds removed).
Sync all hardcoded count mentions:
- layout.tsx JSON-LD, page.tsx (metric + comparison + copy)
- kol/page.tsx, KolPageClient.tsx ("and 26 more" → "and 22 more")
- glossary/page.tsx, opengraph-image.tsx
- public/llms.txt, llms-full.txt
- drop removed KOLs (Dragonfly Capital, Nic Carter) from named lists

Bundles other in-flight frontend work already in the working tree.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 22:55:27 +08:00

336 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 **`../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) → marketing / landing page
├── 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/
│ ├── SourceChips.tsx Filter chips per source
│ └── SystemControl.tsx Sys1/sys2 toggle widgets, Auto-Trade switch
├── dashboard/
│ ├── PostCards.tsx Trump post cards
│ ├── SignalMonitor.tsx Live signal stream (WS-driven)
│ └── ChartPanel.tsx Price chart panel
├── 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
│ Includes grow toggle + manual close button
├── trades/
│ ├── TradeTable.tsx Closed trade history table (dynamic asset filter)
│ └── BotConfigPanel.tsx Bot configuration (SL, TP, leverage, paper mode).
│ Includes 3-step onboarding stepper + inline Auto-Trade toggle.
├── telegram/
│ └── TelegramCard.tsx Settings — connect via 6-char code
├── wallet/
│ └── SignConfirmSheet.tsx EIP-191 signed request preview
├── seo/Breadcrumbs.tsx Structured breadcrumb nav for SEO
├── ui/
│ ├── InfoTip.tsx CSS-only tooltip with `?` icon
│ ├── PageHint.tsx Strong page subtitle
│ ├── Pagination.tsx Reusable pagination control
│ └── TradeAlertBanner.tsx Fixed-position WS alert banner (trade_alert events).
│ Auto-dismisses after 10s. Filtered by wallet address.
└── lib/wsContext.tsx WebSocket singleton context (replaces WsProvider)
```
---
## API layer
All API calls go through **`lib/api.ts`**. Single `fetchJson<T>` 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 `<html>`. 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-0106, 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<string, number | null>`; `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.
- ~~**H2 Chart data race** (FIXED 2026-06-01):~~ `cancelled` flag added to the
candles `useEffect` — stale BTC response can no longer overwrite ETH candles
when asset is switched mid-fetch.
- ~~**M8 Duplicate React keys** (FIXED 2026-06-01):~~ `onNewPost` now deduplicates
by `post.id` before prepending to the posts list.
- ~~**L2 fittedRef not reset on asset change** (FIXED 2026-06-01):~~ Chart view
is re-fitted when `asset` changes, not only on `timeframe` change.
- ~~**L4 invalid ARIA role** (FIXED 2026-06-01):~~ `Ticker.tsx` `role="marquee"`
replaced with `role="region"`.
- ~~**L5 ws:// mixed-content block** (FIXED 2026-06-01):~~ `wsContext.tsx` now
auto-upgrades to `wss://` when served over HTTPS, instead of hard-coding `ws://`.
**Open / deferred (need interface change or DB migration):**
- **C3** Signed READ endpoints (`allow_replay=True`) put `ts`/`sig` in URL → access logs. Fix requires changing read endpoints to POST body (interface change + frontend update).
- **H4** KEK derived with single SHA-256, no salt/KDF. Fix requires re-encrypting all HL API keys (DB migration).
- **M1** Adopted (sys2) positions incorrectly counted against sys1 daily budget.
- **M5** `GET /telegram/{wallet}/status` is unauthenticated — exposes `chat_id`/`tg_username`.
- **M7** Rate limit bypassable via spoofed XFF — infrastructure-level fix (nginx/Cloudflare).
- **M9** SWR cache only notifies the first caller on background refresh — others get stale data.
## 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/trumpsignal/backend`** — Python/FastAPI backend, includes
Hyperliquid integration, Telegram bot, AI scoring (DeepSeek), all
signal scanners, the adoption/release flow. See its CLAUDE.md.