feat: add CSS globals, format utils, and queryClient

This commit is contained in:
thesn10
2026-06-24 23:16:55 +02:00
parent 6ffa84f15a
commit 2529a0b0ca
4 changed files with 109 additions and 0 deletions

34
app/src/lib/format.ts Normal file
View File

@@ -0,0 +1,34 @@
export function abbrev(pk: string | null | undefined): string {
if (!pk) return '—'
return `${pk.slice(0, 4)}${pk.slice(-4)}`
}
export function fmtSol(lamports: bigint | number): string {
const v = Number(lamports) / 1e9
return v.toLocaleString(undefined, { maximumFractionDigits: 4 }) + ' SOL'
}
export function fmtTok(raw: bigint | number, decimals: number, symbol: string): string {
const v = Number(raw) / Math.pow(10, decimals)
return v.toLocaleString(undefined, { maximumFractionDigits: Math.min(decimals, 2) }) + ' ' + symbol
}
export function relTime(ts: number): string {
const d = Math.floor(Date.now() / 1000) - ts
if (d < 60) return `${d}s ago`
if (d < 3600) return `${Math.floor(d / 60)}m ago`
if (d < 86400) return `${Math.floor(d / 3600)}h ago`
return `${Math.floor(d / 86400)}d ago`
}
export function exact(ts: number): string {
return new Date(ts * 1000).toUTCString()
}
export function priceStr(
currency: { __kind: 'Sol' } | { __kind: 'Spl'; decimals: number; symbol?: string },
price: bigint | number,
): string {
if (currency.__kind === 'Sol') return fmtSol(price)
return fmtTok(price, currency.decimals, (currency as { symbol?: string }).symbol ?? 'SPL')
}

View File

@@ -0,0 +1,11 @@
import { QueryClient } from '@tanstack/react-query'
export const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 10_000,
gcTime: 5 * 60 * 1000,
retry: 2,
},
},
})

6
app/src/lib/utils.ts Normal file
View File

@@ -0,0 +1,6 @@
import { clsx, type ClassValue } from 'clsx'
import { twMerge } from 'tailwind-merge'
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}

View File

@@ -0,0 +1,58 @@
@import "tailwindcss";
@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap');
:root {
--bg: oklch(0.165 0.022 282);
--bg2: oklch(0.205 0.026 282);
--bg3: oklch(0.25 0.03 283);
--bd: oklch(0.33 0.032 286);
--bdSoft: oklch(0.27 0.028 284);
--navbg: oklch(0.18 0.024 282 / .82);
--tx: oklch(0.96 0.008 285);
--mut: oklch(0.68 0.022 284);
--radius: 13px;
--acc: #9945FF;
--acc2: #14F195;
--acc2light: #7df5c4;
--accGlow: rgba(153,69,255,.55);
--accSoft: rgba(153,69,255,.13);
--accBd: rgba(153,69,255,.4);
--danger: #FF7A59;
--dangerSoft: rgba(255,122,89,.12);
--font-body: 'Space Grotesk', system-ui, sans-serif;
--font-mono: 'JetBrains Mono', monospace;
}
/* Tailwind theme tokens for connectorkit components */
@theme {
--color-primary: var(--acc);
--color-primary-foreground: #0b0613;
--color-background: var(--bg);
--color-foreground: var(--tx);
--color-popover: var(--bg2);
--color-popover-foreground: var(--tx);
--color-border: var(--bd);
--color-input: var(--bd);
--color-muted: var(--bg3);
--color-accent: var(--bg3);
--color-accent-foreground: var(--tx);
--color-destructive: var(--danger);
}
* { box-sizing: border-box; }
body {
margin: 0;
background: var(--bg);
color: var(--tx);
font-family: var(--font-body);
}
input, button, select, textarea { font-family: inherit; font-size: inherit; color: inherit; }
button { cursor: pointer; border: none; background: none; }
::selection { background: rgba(153,69,255,.35); }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-thumb { background: rgba(255,255,255,.13); border-radius: 8px; }
::-webkit-scrollbar-track { background: transparent; }
@keyframes scToast { from { opacity: 0; transform: translate(-50%,10px); } to { opacity: 1; transform: translate(-50%,0); } }
@keyframes scSpin { to { transform: rotate(360deg); } }
@keyframes scPulse { 0%,100% { opacity: 1; } 50% { opacity: .45; } }