diff --git a/apps/web/src/components/DepositPanel.tsx b/apps/web/src/components/DepositPanel.tsx index d7ad74d..7854c8f 100644 --- a/apps/web/src/components/DepositPanel.tsx +++ b/apps/web/src/components/DepositPanel.tsx @@ -196,9 +196,9 @@ export function DepositPanel({ ? `Enter ${symbol} amount` : "Enter amount" : needsUsdFaucet - ? "Get test hUSD" + ? "Get hUSD" : needsStockFaucet - ? `Get test ${symbol}` + ? `Get ${symbol}` : needsUsdApprove ? "Approve hUSD" : needsStockApprove diff --git a/apps/web/src/components/DepthHistory.tsx b/apps/web/src/components/DepthHistory.tsx index e77c0cc..d90b279 100644 --- a/apps/web/src/components/DepthHistory.tsx +++ b/apps/web/src/components/DepthHistory.tsx @@ -1,11 +1,14 @@ /** - * 30-day depth sparkline. Historical points are estimated: a deterministic, + * 30-day depth trace. Historical points are estimated: a deterministic, * per-market random walk that converges to the current live depth figure, so - * the line is stable across renders and always ends at truth. + * the line is stable across renders and always ends at truth. Rendered as a + * cyberpunk area trace with grid, glow and hi/now field markers. */ -const W = 220; -const H = 48; +const W = 240; +const H = 58; const DAYS = 30; +const TOP = 7; +const BOT = H - 8; function seeded(i: number, salt: number) { const x = Math.sin(i * 91.7 + salt * 47.3) * 24634.5453; @@ -34,40 +37,50 @@ export function DepthHistory({ const max = Math.max(...values); const min = Math.min(...values); const span = max - min || 1; - const pts = values - .map((v, i) => { - const x = (i / (DAYS - 1)) * W; - const y = H - 6 - ((v - min) / span) * (H - 12); - return `${x.toFixed(1)},${y.toFixed(1)}`; - }) - .join(" "); + const xAt = (i: number) => (i / (DAYS - 1)) * W; + const yAt = (v: number) => BOT - ((v - min) / span) * (BOT - TOP); + + const pts = values.map((v, i) => `${xAt(i).toFixed(1)},${yAt(v).toFixed(1)}`).join(" "); + const idxMax = values.indexOf(max); + const gid = `dh-${salt}`; return ( - - - - + + + + + + + + {/* survey grid */} + {[0.25, 0.5, 0.75].map((g) => ( + + ))} + {[0.25, 0.5, 0.75].map((g) => ( + + ))} + + + + + {/* peak marker */} + + + {/* baseline */} + + + {/* live 'now' node */} + + + + + ); } diff --git a/apps/web/src/components/HealthRadial.tsx b/apps/web/src/components/HealthRadial.tsx index 01af83f..594d48d 100644 --- a/apps/web/src/components/HealthRadial.tsx +++ b/apps/web/src/components/HealthRadial.tsx @@ -1,51 +1,70 @@ import { healthLabel } from "@/lib/math"; -/** SVG radial gauge for the Market Health Score. */ +const TICKS = 34; // segments around the 270° dial +const START = 135; // degrees, y-down; gap sits at the bottom +const SWEEP = 270; +const CX = 40; +const CY = 40; + +function pt(r: number, deg: number): [number, number] { + const a = (deg * Math.PI) / 180; + return [CX + r * Math.cos(a), CY + r * Math.sin(a)]; +} + +/** Ticked radial dial for the Market Health Score. */ export function HealthRadial({ score }: { score: number }) { const { label, tone } = healthLabel(score); const color = tone === "mint" ? "#c8ff00" : tone === "violet" ? "#8a5cff" : "#ff3b00"; - const r = 34; - const c = 2 * Math.PI * r; - const filled = (score / 100) * c * 0.75; // 270 degree arc + const lit = Math.round((Math.max(0, Math.min(score, 100)) / 100) * TICKS); return (
- - - - - + {/* outer reticle ring */} + + + {/* dial ticks */} + + {Array.from({ length: TICKS }, (_, i) => { + const deg = START + (i / (TICKS - 1)) * SWEEP; + const on = i < lit; + const [x1, y1] = pt(24, deg); + const [x2, y2] = pt(on ? 33 : 30, deg); + return ( + + {on && ( + + )} + + ); + })} + + {/* crosshair ticks at 12 / 3 / 9 o'clock */} + {[270, 0, 180].map((deg) => { + const [x1, y1] = pt(15, deg); + const [x2, y2] = pt(18, deg); + return ; + })} + 0 ? Math.min((0.02 / max), 1) * CELLS * STEP : CELLS * STEP; + return ( -
- {SIZES.map((size) => { +
+ {SIZES.map((size, row) => { const s = slippageFor(size, liquidityUsd); - const width = max > 0 ? (s / max) * 100 : 0; + const frac = max > 0 ? s / max : 0; const hot = s > 0.02; + const on = Math.max(1, Math.round(frac * CELLS)); + const color = hot ? "#ff3b00" : "#c8ff00"; return ( -
- {fmtUsd(size)} - - - - - +
+ + {fmtUsd(size)} + + + {Array.from({ length: CELLS }, (_, i) => { + const lit = i < on; + return ( + + {lit && ( + + )} + + ); + })} + {limitX < CELLS * STEP && ( + + )} - + {(s * 100).toFixed(2)}%
diff --git a/apps/web/src/screens/AppBoard.tsx b/apps/web/src/screens/AppBoard.tsx index f89bc65..f64ad62 100644 --- a/apps/web/src/screens/AppBoard.tsx +++ b/apps/web/src/screens/AppBoard.tsx @@ -129,7 +129,7 @@ export default function AppBoard() { if (!isConnected) return openConnectModal?.(); if (amount === 0n) return say("enter an amount to deposit"); if (needsFaucet) { - lastAction.current = "test hUSD minted to wallet"; + lastAction.current = "hUSD minted to wallet"; writeContract({ address: deployment.usd, abi: mockERC20Abi, @@ -189,7 +189,7 @@ export default function AppBoard() { : amount === 0n ? "Enter amount" : needsFaucet - ? "Get test hUSD" + ? "Get hUSD" : needsApprove ? "Approve hUSD" : "Deposit hUSD";