Function bodies 642 total
DeliverySection function · typescript · L80-L283 (204 LOC)components/DeliverySection.tsx
export default function DeliverySection({ data }: DeliverySectionProps) {
// Sort deliveries by daily_issued descending
const sortedDeliveries = [...data.deliveries].sort((a, b) => b.daily_issued - a.daily_issued);
// Calculate totals
const totalDailyIssued = sortedDeliveries.reduce((sum, d) => sum + d.daily_issued, 0);
const totalDailyStopped = sortedDeliveries.reduce((sum, d) => sum + d.daily_stopped, 0);
const totalMTD = sortedDeliveries.reduce((sum, d) => sum + d.month_to_date, 0);
return (
<section className="notranslate w-full" translate="no">
{/* Section Header */}
<div className="flex flex-col md:flex-row md:items-end justify-between mb-8 sm:mb-12 md:mb-16 gap-4">
<div className="max-w-xl space-y-3 sm:space-y-8">
<div className="flex items-center gap-3">
<h2 className="tracking-tighter text-3xl sm:text-4xl md:text-5xl font-black uppercase">
Delivery Notices
</h2>
<ShareButton
formatNumber function · typescript · L101-L103 (3 LOC)components/DeliveryYTDSection.tsx
function formatNumber(num: number): string {
return num.toLocaleString('en-US');
}formatShortDate function · typescript · L105-L112 (8 LOC)components/DeliveryYTDSection.tsx
function formatShortDate(dateStr: string): string {
const parts = dateStr.split('/');
if (parts.length !== 3) return dateStr;
const monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const month = parseInt(parts[0]) - 1;
const day = parseInt(parts[1]);
return `${monthNames[month]} ${day}`;
}getCurrentMonthLabel function · typescript · L114-L121 (8 LOC)components/DeliveryYTDSection.tsx
function getCurrentMonthLabel(mtdData: DeliveryMTDData | null | undefined): string {
if (!mtdData?.business_date) return 'Daily';
const parts = mtdData.business_date.split('/');
if (parts.length !== 3) return 'Daily';
const monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const month = parseInt(parts[0]) - 1;
return monthNames[month] || 'Daily';
}SortIndicator function · typescript · L123-L132 (10 LOC)components/DeliveryYTDSection.tsx
function SortIndicator({ column, current, direction }: { column: string; current: string; direction: 'asc' | 'desc' }) {
if (current !== column) {
return <span className="text-[8px] opacity-30 ml-0.5">▲▼</span>;
}
return (
<span className="text-[8px] ml-0.5">
{direction === 'asc' ? '\u25B2' : '\u25BC'}
</span>
);
}normalizeDate function · typescript · L267-L278 (12 LOC)components/DemandChart.tsx
function normalizeDate(dateStr: string): string {
if (!dateStr) return '';
// Already YYYY-MM-DD
if (/^\d{4}-\d{2}-\d{2}$/.test(dateStr)) return dateStr;
// ISO datetime like "2026-02-05T00:00:00.000Z"
const isoMatch = dateStr.match(/^(\d{4}-\d{2}-\d{2})T/);
if (isoMatch) return isoMatch[1];
// Long date string like "Thu Feb 05 2026 00:00:00 GMT..." — parse it
const d = new Date(dateStr);
if (!isNaN(d.getTime())) return d.toISOString().split('T')[0];
return dateStr;
}formatDateLabel function · typescript · L280-L286 (7 LOC)components/DemandChart.tsx
function formatDateLabel(dateStr: string): string {
const clean = normalizeDate(dateStr);
if (!clean || !/^\d{4}-\d{2}-\d{2}$/.test(clean)) return clean || '';
const d = new Date(clean + 'T12:00:00'); // noon to avoid TZ issues
if (isNaN(d.getTime())) return clean;
return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
}Provenance: Repobility (https://repobility.com) — every score reproducible from /scan/
fetchAndMerge function · typescript · L476-L596 (121 LOC)components/DemandChart.tsx
async function fetchAndMerge() {
const metals: MetalType[] = ['gold', 'silver', 'aluminum', 'copper'];
// Start with deep copies of baseline
const mergedDaily: Record<MetalType, DailyDataPoint[]> = { gold: [], silver: [], aluminum: [], copper: [] };
const mergedMonthly: Record<MetalType, MonthlyDataPoint[]> = { gold: [], silver: [], aluminum: [], copper: [] };
for (const m of metals) {
mergedDaily[m] = baselineDailyData[m].map(d => ({ ...d }));
mergedMonthly[m] = baselineMonthlyData[m].map(d => ({ ...d }));
}
// Step 1: Merge delivery.json (current day) into daily + monthly
if (deliveryData?.deliveries && deliveryData.parsed_date) {
for (const delivery of deliveryData.deliveries) {
const metalKey = delivery.metal.toLowerCase() as MetalType;
if (!mergedDaily[metalKey]) continue;
const dateKey = normalizeDate(deliveryData.parsed_date);
const dayLabel = formatDateLabel(daDepositoryTable function · typescript · L14-L88 (75 LOC)components/DepositoryTable.tsx
export default function DepositoryTable({ depositories, metalName, unit }: DepositoryTableProps) {
const [isExpanded, setIsExpanded] = useState(false);
const activeDepositories = depositories.filter(d => d.total > 0);
const grandTotal = activeDepositories.reduce((sum, d) => sum + d.total, 0);
const sortedDepositories = [...activeDepositories].sort((a, b) => b.total - a.total);
if (activeDepositories.length === 0) return null;
return (
<Card>
<CardHeader
className="cursor-pointer hover:bg-muted/50 transition-colors"
onClick={() => setIsExpanded(!isExpanded)}
>
<div className="flex items-center justify-between">
<div className="space-y-1">
<h3 className="text-lg font-semibold leading-none tracking-tight">{metalName}</h3>
<p className="text-sm text-muted-foreground">
{activeDepositories.length} vaults · {formatNumber(grandTotal)} {unit}
</p>
</div>
<ChevrformatExportDate function · typescript · L14-L17 (4 LOC)components/ExportChartButton.tsx
function formatExportDate(d: Date): string {
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
return `${months[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()}`;
}drawTiledWatermark function · typescript · L20-L49 (30 LOC)components/ExportChartButton.tsx
function drawTiledWatermark(
ctx: CanvasRenderingContext2D,
width: number,
height: number,
isDark: boolean,
pad: number,
) {
const patternCanvas = document.createElement('canvas');
const tileSize = 421; // 337 / 0.8 — 20% fewer repetitions
patternCanvas.width = tileSize;
patternCanvas.height = tileSize;
const pCtx = patternCanvas.getContext('2d')!;
pCtx.font = `600 ${43}px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif`; // 33 * 1.30
pCtx.fillStyle = isDark ? 'rgba(148,163,184,0.12)' : 'rgba(100,116,139,0.09)';
pCtx.textAlign = 'center';
pCtx.textBaseline = 'middle';
pCtx.save();
pCtx.translate(tileSize / 2, tileSize / 2);
pCtx.rotate(-35 * Math.PI / 180);
pCtx.translate(-tileSize / 2, -tileSize / 2);
pCtx.fillText(WATERMARK, tileSize / 2, tileSize / 2);
pCtx.restore();
const pattern = ctx.createPattern(patternCanvas, 'repeat')!;
ctx.save();
ctx.globalAlpha = 1;
ctx.fillStyle = pattern;
ctx.fillRect(pad, pad, width -ExportChartButton function · typescript · L51-L141 (91 LOC)components/ExportChartButton.tsx
export default function ExportChartButton({
targetRef,
filename = 'chart',
className = '',
}: ExportChartButtonProps) {
const [state, setState] = useState<'idle' | 'capturing' | 'done'>('idle');
const capture = useCallback(async () => {
const el = targetRef.current;
if (!el || state === 'capturing') return;
setState('capturing');
try {
const html2canvas = (await import('html2canvas-pro')).default;
const generatedAt = new Date();
const isDark = document.documentElement.classList.contains('dark');
const bg = isDark ? '#0a0a0a' : '#ffffff';
const canvas = await html2canvas(el, {
backgroundColor: bg,
scale: 2,
useCORS: true,
logging: false,
removeContainer: true,
});
const pad = 32;
const wmHeight = 36;
const final = document.createElement('canvas');
final.width = canvas.width + pad * 2;
final.height = canvas.height + pad * 2 + wmHeight;
const ctxSparkline function · typescript · L180-L208 (29 LOC)components/ForecastDashboard.tsx
function Sparkline({ data, color, positive }: { data: number[]; color: string; positive: boolean }) {
if (data.length < 2) return null;
const chartData = data.map((v, i) => ({ i, v }));
const min = Math.min(...data);
const max = Math.max(...data);
const pad = (max - min) * 0.1 || 1;
return (
<ResponsiveContainer width="100%" height={40}>
<LineChart data={chartData} margin={{ top: 2, right: 2, bottom: 2, left: 2 }}>
<defs>
<linearGradient id={`spark-${color.replace('#', '')}`} x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor={color} stopOpacity={0.3} />
<stop offset="100%" stopColor={color} stopOpacity={0} />
</linearGradient>
</defs>
<YAxis domain={[min - pad, max + pad]} hide />
<Line
type="monotone"
dataKey="v"
stroke={positive ? '#10b981' : '#ef4444'}
strokeWidth={1.5}
dot={false}
isAnimationActive={false}
/>
DirectionBadge function · typescript · L212-L228 (17 LOC)components/ForecastDashboard.tsx
function DirectionBadge({ direction, confidence }: { direction: string; confidence: number }) {
const config = {
BULLISH: { icon: TrendingUp, bg: 'bg-emerald-500/10 dark:bg-emerald-500/20', text: 'text-emerald-600 dark:text-emerald-400', border: 'border-emerald-500/30' },
BEARISH: { icon: TrendingDown, bg: 'bg-red-500/10 dark:bg-red-500/20', text: 'text-red-600 dark:text-red-400', border: 'border-red-500/30' },
NEUTRAL: { icon: Minus, bg: 'bg-slate-500/10 dark:bg-slate-500/20', text: 'text-slate-600 dark:text-slate-400', border: 'border-slate-500/30' },
}[direction] || { icon: Minus, bg: 'bg-slate-500/10', text: 'text-slate-500', border: 'border-slate-500/30' };
const Icon = config.icon;
return (
<div className={`inline-flex items-center gap-2 px-3 py-1.5 rounded-none border ${config.bg} ${config.border}`}>
<Icon className={`w-4 h-4 ${config.text}`} />
<span className={`text-sm font-bold tracking-wide ${config.text}`}>{diSignalDropdown function · typescript · L230-L294 (65 LOC)components/ForecastDashboard.tsx
function SignalDropdown({ label, score, details, colors, barColor, direction }: {
label: string;
score: number;
details: string;
colors: string;
barColor: string;
direction: string;
}) {
const [open, setOpen] = useState(false);
const roundedScore = Math.round(score);
const signalDescriptions: Record<string, string> = {
'Trend Momentum': 'Measures price trend strength using moving averages (SMA5/SMA20), MACD crossovers, RSI momentum, and rate of change. Scores above 60 indicate bullish trend alignment; below 40 signals bearish pressure.',
'Physical Stress': 'Tracks physical market tightness: inventory drawdowns, delivery acceleration, paper-to-physical ratios, coverage erosion, and eligible-to-registered flow shifts. Higher scores signal supply stress.',
'Arima Model': 'Statistical time-series forecast using Auto-ARIMA. Projects price direction over 5-day and 20-day windows with confidence intervals. Score reflects projected percentage change direction.',
Repobility · MCP-ready · https://repobility.com
PriceProjectionChart function · typescript · L296-L421 (126 LOC)components/ForecastDashboard.tsx
function PriceProjectionChart({ currentPrice, forecast5d, forecast20d, metalColor, noDataMessage }: {
currentPrice: number;
forecast5d: ForecastRange | null;
forecast20d: ForecastRange | null;
metalColor: string;
noDataMessage?: string;
}) {
const { theme } = useTheme();
const isDark = theme === 'dark';
if (!forecast5d && !forecast20d) {
return (
<div className="flex items-center justify-center h-32 sm:h-40 text-xs sm:text-sm text-slate-400">
<Info className="w-3.5 h-3.5 sm:w-4 sm:h-4 mr-2 flex-shrink-0" />
{noDataMessage || 'Insufficient data for price projections'}
</div>
);
}
const data = [
{ name: 'Now', mid: currentPrice, low: currentPrice, high: currentPrice },
];
if (forecast5d) {
data.push({ name: '5d', mid: forecast5d.mid, low: forecast5d.low, high: forecast5d.high });
}
if (forecast20d) {
data.push({ name: '20d', mid: forecast20d.mid, low: forecast20d.low, high: forecast20d.high });
}
const allisCMEXOpen function · typescript · L452-L461 (10 LOC)components/ForecastDashboard.tsx
function isCMEXOpen(): boolean {
const now = new Date();
const et = new Date(now.toLocaleString('en-US', { timeZone: 'America/New_York' }));
const day = et.getDay();
const hour = et.getHours();
if (day === 6) return false;
if (day === 0) return hour >= 18;
if (day === 5) return hour < 17;
return hour !== 17;
}ForumContent function · typescript · L34-L40 (7 LOC)components/ForumContent.tsx
export default function ForumContent({ categories }: { categories: ForumCategory[] }) {
return (
<ForumThemeProvider>
<ForumInner categories={categories.length > 0 ? categories : STATIC_CATEGORIES} />
</ForumThemeProvider>
);
}ForumInner function · typescript · L42-L285 (244 LOC)components/ForumContent.tsx
function ForumInner({ categories }: { categories: ForumCategory[] }) {
const { theme } = useForumTheme();
const { data: session, status } = useSession();
const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
const [newThreadTitle, setNewThreadTitle] = useState('');
const [newThreadBody, setNewThreadBody] = useState('');
const [posting, setPosting] = useState(false);
const [postResult, setPostResult] = useState<string | null>(null);
const isSignedIn = status === 'authenticated' && !!session?.user;
const totalThreads = categories.reduce((sum, c) => sum + c.thread_count, 0);
const totalPosts = categories.reduce((sum, c) => sum + c.post_count, 0);
async function handlePost() {
if (!newThreadTitle.trim() || !newThreadBody.trim() || !selectedCategory) return;
setPosting(true);
setPostResult(null);
try {
const res = await fetch('/api/forum/thread', {
method: 'POST',
headers: { 'Content-Type': 'application/handlePost function · typescript · L55-L85 (31 LOC)components/ForumContent.tsx
async function handlePost() {
if (!newThreadTitle.trim() || !newThreadBody.trim() || !selectedCategory) return;
setPosting(true);
setPostResult(null);
try {
const res = await fetch('/api/forum/thread', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
categorySlug: selectedCategory,
title: newThreadTitle.trim(),
body: newThreadBody.trim(),
}),
});
if (res.ok) {
setPostResult('Thread created successfully!');
setNewThreadTitle('');
setNewThreadBody('');
setSelectedCategory(null);
} else {
const data = await res.json();
setPostResult(data.error || 'Failed to create thread.');
}
} catch {
setPostResult('Something went wrong. Try again.');
} finally {
setPosting(false);
}
}useForumTheme function · typescript · L225-L227 (3 LOC)components/ForumThemeSelector.tsx
export function useForumTheme() {
return useContext(ForumThemeContext);
}ForumThemeProvider function · typescript · L229-L255 (27 LOC)components/ForumThemeSelector.tsx
export function ForumThemeProvider({ children }: { children: React.ReactNode }) {
const [themeId, setThemeId] = useState<ForumTheme>('gold-standard');
const [mounted, setMounted] = useState(false);
useEffect(() => {
const stored = localStorage.getItem(STORAGE_KEY) as ForumTheme | null;
if (stored && FORUM_THEMES.find((t) => t.id === stored)) {
setThemeId(stored);
}
setMounted(true);
}, []);
function handleSetTheme(id: ForumTheme) {
setThemeId(id);
localStorage.setItem(STORAGE_KEY, id);
}
const theme = FORUM_THEMES.find((t) => t.id === themeId) || FORUM_THEMES[0];
if (!mounted) return null;
return (
<ForumThemeContext.Provider value={{ theme, themeId, setThemeId: handleSetTheme }}>
{children}
</ForumThemeContext.Provider>
);
}handleSetTheme function · typescript · L241-L244 (4 LOC)components/ForumThemeSelector.tsx
function handleSetTheme(id: ForumTheme) {
setThemeId(id);
localStorage.setItem(STORAGE_KEY, id);
}Repobility (the analyzer behind this table) · https://repobility.com
ForumThemeSelector function · typescript · L257-L280 (24 LOC)components/ForumThemeSelector.tsx
export default function ForumThemeSelector() {
const { themeId, setThemeId } = useForumTheme();
return (
<div className="flex items-center gap-2">
<Palette className="w-4 h-4 text-muted-foreground" />
<Select value={themeId} onValueChange={(v) => setThemeId(v as ForumTheme)}>
<SelectTrigger className="h-8 text-xs font-medium min-w-[160px]">
<SelectValue placeholder="Choose theme" />
</SelectTrigger>
<SelectContent>
{FORUM_THEMES.map((t) => (
<SelectItem key={t.id} value={t.id}>
<span className="flex items-center gap-2">
<span>{t.preview}</span>
<span>{t.name}</span>
</span>
</SelectItem>
))}
</SelectContent>
</Select>
</div>
);
}getETNow function · typescript · L17-L19 (3 LOC)components/FuturesTicker.tsx
function getETNow() {
return new Date(new Date().toLocaleString('en-US', { timeZone: 'America/New_York' }));
}isCMEXOpen function · typescript · L24-L32 (9 LOC)components/FuturesTicker.tsx
function isCMEXOpen(): boolean {
const et = getETNow();
const day = et.getDay();
const hour = et.getHours();
if (day === 6) return false;
if (day === 0) return hour >= 18;
if (day === 5) return hour < 17;
return hour !== 17;
}isSpotOpen function · typescript · L37-L43 (7 LOC)components/FuturesTicker.tsx
function isSpotOpen(): boolean {
const et = getETNow();
const day = et.getDay();
if (day === 0 || day === 6) return false;
const minutes = et.getHours() * 60 + et.getMinutes();
return minutes >= 570 && minutes < 960; // 9:30=570, 16:00=960
}getPriceMode function · typescript · L45-L47 (3 LOC)components/FuturesTicker.tsx
function getPriceMode(): PriceMode {
return isSpotOpen() ? 'spot' : 'futures';
}formatPrice function · typescript · L49-L51 (3 LOC)components/FuturesTicker.tsx
function formatPrice(price: number): string {
return price.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
}FuturesTicker function · typescript · L53-L203 (151 LOC)components/FuturesTicker.tsx
export default function FuturesTicker() {
const [prices, setPrices] = useState<Record<string, TickerPrice | null>>({});
const [loading, setLoading] = useState(true);
const [flashMap, setFlashMap] = useState<Record<string, 'up' | 'down' | null>>({});
const [mode, setMode] = useState<PriceMode>(getPriceMode);
const [cmexOpen, setCmexOpen] = useState(isCMEXOpen);
const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
const fetchPrices = useCallback(async (forMode?: PriceMode) => {
const activeMode = forMode ?? getPriceMode();
const endpoint = activeMode === 'spot' ? '/api/prices' : '/api/futures';
try {
const res = await fetch(endpoint);
if (!res.ok) return;
const data = await res.json();
if (!data.success) return;
const raw: Record<string, TickerPrice | null> = data.prices ?? data.futures ?? {};
setPrices(prev => {
const flashes: Record<string, 'up' | 'down' | null> = {};
for (const metal oformatTonnes function · typescript · L78-L82 (5 LOC)components/GlobalInventory.tsx
function formatTonnes(v: number): string {
if (v >= 1000) return `${(v / 1000).toFixed(1)}K`;
if (v >= 1) return v.toFixed(1);
return v.toFixed(2);
}Repobility · severity-and-effort ranking · https://repobility.com
formatOz function · typescript · L84-L89 (6 LOC)components/GlobalInventory.tsx
function formatOz(v: number): string {
if (v >= 1_000_000_000) return `${(v / 1_000_000_000).toFixed(2)}B`;
if (v >= 1_000_000) return `${(v / 1_000_000).toFixed(1)}M`;
if (v >= 1_000) return `${(v / 1_000).toFixed(0)}K`;
return v.toFixed(0);
}formatDate function · typescript · L91-L94 (4 LOC)components/GlobalInventory.tsx
function formatDate(dateStr: string): string {
const d = new Date(dateStr + 'T12:00:00');
return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
}GlobalInventory function · typescript · L104-L353 (250 LOC)components/GlobalInventory.tsx
export default function GlobalInventory({ comexData }: { comexData?: Record<string, { totals: { registered: number; eligible: number; total: number } }> }) {
const { theme } = useTheme();
const isDark = theme === 'dark';
const [selectedMetal, setSelectedMetal] = useState<MetalKey>('gold');
const [showInfo, setShowInfo] = useState(false);
const [liveComex, setLiveComex] = useState<ComexTotals | null>(null);
const chartRef = useRef<HTMLDivElement>(null);
// Use passed-in COMEX data or fetch from API
useEffect(() => {
if (comexData) {
setLiveComex({
gold: comexData.Gold?.totals?.total || 0,
silver: comexData.Silver?.totals?.total || 0,
copper: comexData.Copper?.totals?.total || 0,
platinum: comexData.Platinum?.totals?.total || 0,
palladium: comexData.Palladium?.totals?.total || 0,
});
}
}, [comexData]);
const comexTonnes = {
gold: liveComex ? liveComex.gold / OZ_PER_TONNE : 0,
silver: liveComex ? livtriggerGoogleTranslate function · typescript · L35-L44 (10 LOC)components/LanguageSelector.tsx
function triggerGoogleTranslate(langCode: string) {
const select = document.querySelector<HTMLSelectElement>(
'#google_translate_element select'
);
if (!select) return false;
select.value = langCode;
select.dispatchEvent(new Event('change', { bubbles: true }));
return true;
}LanguageSelector function · typescript · L46-L190 (145 LOC)components/LanguageSelector.tsx
export default function LanguageSelector() {
const [open, setOpen] = useState(false);
const [currentLang, setCurrentLang] = useState('');
const [ready, setReady] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null);
const checkReady = useCallback(() => {
const select = document.querySelector('#google_translate_element select');
if (select) {
setReady(true);
return true;
}
return false;
}, []);
useEffect(() => {
if (document.getElementById('google-translate-script')) {
const interval = setInterval(() => {
if (checkReady()) clearInterval(interval);
}, 500);
return () => clearInterval(interval);
}
window.googleTranslateElementInit = () => {
new window.google!.translate!.TranslateElement!(
{ pageLanguage: 'en', autoDisplay: false },
'google_translate_element'
);
const interval = setInterval(() => {
if (checkReady()) clearInterval(interval);
}, 50handleClickOutside function · typescript · L88-L92 (5 LOC)components/LanguageSelector.tsx
function handleClickOutside(e: MouseEvent) {
if (dropdownRef.current && !dropdownRef.current.contains(e.target as Node)) {
setOpen(false);
}
}selectLanguage function · typescript · L97-L138 (42 LOC)components/LanguageSelector.tsx
function selectLanguage(langCode: string) {
setCurrentLang(langCode);
setOpen(false);
if (langCode === '') {
// Restore to English — use Google Translate's own mechanism to properly
// revert internal state and cookies (including ones on .google.com we can't clear)
const select = document.querySelector<HTMLSelectElement>(
'#google_translate_element select'
);
if (select) {
select.value = 'en';
select.dispatchEvent(new Event('change', { bubbles: true }));
}
// Also clear googtrans cookies on all possible domain variations
const hostname = window.location.hostname;
const expiry = 'expires=Thu, 01 Jan 1970 00:00:00 UTC';
document.cookie = `googtrans=; ${expiry}; path=/`;
document.cookie = `googtrans=; ${expiry}; path=/; domain=${hostname}`;
document.cookie = `googtrans=; ${expiry}; path=/; domain=.${hostname}`;
const parts = hostname.split('.');
if (parts.length > 2)formatPrice function · typescript · L29-L32 (4 LOC)components/MarketComparison.tsx
function formatPrice(price: number): string {
if (price >= 10000) return price.toLocaleString('en-US', { minimumFractionDigits: 0, maximumFractionDigits: 0 });
return price.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
}Provenance: Repobility (https://repobility.com) — every score reproducible from /scan/
formatDailyChange function · typescript · L34-L37 (4 LOC)components/MarketComparison.tsx
function formatDailyChange(v: number): string {
if (Math.abs(v) >= 100) return v.toFixed(0);
return v.toFixed(2);
}sign function · typescript · L39-L41 (3 LOC)components/MarketComparison.tsx
function sign(v: number): string {
return v >= 0 ? '+' : '';
}ytdColor function · typescript · L43-L47 (5 LOC)components/MarketComparison.tsx
function ytdColor(v: number): string {
return v >= 0
? 'text-emerald-600 dark:text-emerald-400'
: 'text-red-600 dark:text-red-400';
}ytdBg function · typescript · L49-L53 (5 LOC)components/MarketComparison.tsx
function ytdBg(v: number): string {
return v >= 0
? 'bg-emerald-500/10'
: 'bg-red-500/10';
}barWidth function · typescript · L55-L58 (4 LOC)components/MarketComparison.tsx
function barWidth(v: number, max: number): string {
if (max <= 0) return '0%';
return `${Math.min(Math.abs(v) / max * 100, 100)}%`;
}MarketComparison function · typescript · L60-L269 (210 LOC)components/MarketComparison.tsx
export default function MarketComparison() {
const [data, setData] = useState<MarketData | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(false);
const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
const fetchData = useCallback(async () => {
try {
const res = await fetch('/api/market-comparison');
if (!res.ok) throw new Error('fetch failed');
const json = await res.json();
if (!json.success) throw new Error('api error');
setData(json);
setError(false);
setLoading(false);
} catch {
setError(true);
setLoading(false);
}
}, []);
useEffect(() => {
fetchData();
intervalRef.current = setInterval(fetchData, 3_600_000);
return () => { if (intervalRef.current) clearInterval(intervalRef.current); };
}, [fetchData]);
if (loading) {
return (
<div className="w-full">
<div className="h-48 bg-slate-100 dark:bg-slate-900MetalCard function · typescript · L13-L106 (94 LOC)components/MetalCard.tsx
export default function MetalCard({ data, config }: MetalCardProps) {
const { totals } = data;
const ratio = calculateCoverageRatio(totals.registered, config.monthlyDemand);
const { status } = getSupplyStatus(ratio);
const registeredPercent = totals.total > 0 ? (totals.registered / totals.total) * 100 : 0;
const totalValue = totals.total * config.pricePerUnit;
const statusVariant = {
ADEQUATE: 'default' as const,
WATCH: 'secondary' as const,
STRESS: 'destructive' as const,
};
const statusTextColors = {
ADEQUATE: 'text-emerald-600 dark:text-emerald-400',
WATCH: 'text-amber-600 dark:text-amber-400',
STRESS: 'text-red-600 dark:text-red-400',
};
return (
<Card className="hover:shadow-lg transition-shadow">
<CardHeader className="pb-4">
<div className="flex items-start justify-between">
<div className="space-y-1">
<h3 className="text-lg font-semibold leading-none tracking-tight">{config.name}</h3>
formatCompact function · typescript · L56-L62 (7 LOC)components/MetalCompare.tsx
function formatCompact(v: number): string {
if (v >= 1_000_000_000) return `${(v / 1_000_000_000).toFixed(1)}B`;
if (v >= 1_000_000) return `${(v / 1_000_000).toFixed(1)}M`;
if (v >= 1_000) return `${(v / 1_000).toFixed(1)}K`;
if (v >= 10) return v.toFixed(0);
return v.toFixed(2);
}Repobility · MCP-ready · https://repobility.com
MetalCompare function · typescript · L64-L383 (320 LOC)components/MetalCompare.tsx
export default function MetalCompare() {
const { theme } = useTheme();
const isDark = theme === 'dark';
const chartRef = useRef<HTMLDivElement>(null);
const [metalA, setMetalA] = useState('Gold');
const [metalB, setMetalB] = useState('Silver');
const [metric, setMetric] = useState<Metric>('inventory');
const [days, setDays] = useState(90);
const [normalize, setNormalize] = useState(false);
const [loading, setLoading] = useState(false);
const [inventoryA, setInventoryA] = useState<InventoryPoint[]>([]);
const [inventoryB, setInventoryB] = useState<InventoryPoint[]>([]);
const [bulletinA, setBulletinA] = useState<BulletinPoint[]>([]);
const [bulletinB, setBulletinB] = useState<BulletinPoint[]>([]);
const colorA = METALS.find(m => m.key === metalA)?.color || '#fbbf24';
const colorB = METALS.find(m => m.key === metalB)?.color || '#94a3b8';
const symbolA = METALS.find(m => m.key === metalA)?.symbol || 'GC';
const symbolB = METALS.find(m => m.key === metafetchData function · typescript · L90-L118 (29 LOC)components/MetalCompare.tsx
async function fetchData() {
try {
if (metric === 'inventory') {
const [resA, resB] = await Promise.all([
fetch(`/api/metals/${metalA}/history?days=${days}`),
fetch(`/api/metals/${metalB}/history?days=${days}`),
]);
const [dataA, dataB] = await Promise.all([resA.json(), resB.json()]);
if (!cancelled) {
setInventoryA(dataA.success ? dataA.data : []);
setInventoryB(dataB.success ? dataB.data : []);
}
} else {
const [resA, resB] = await Promise.all([
fetch(`/api/bulletin/history?symbol=${symbolA}&days=${days}`),
fetch(`/api/bulletin/history?symbol=${symbolB}&days=${days}`),
]);
const [dataA, dataB] = await Promise.all([resA.json(), resB.json()]);
if (!cancelled) {
setBulletinA(dataA.history || []);
setBulletinB(dataB.history || []);
}
}
} catch {
//getVisitData function · typescript · L15-L23 (9 LOC)components/MonetAgAdLoader.tsx
function getVisitData(): VisitData | null {
try {
const raw = localStorage.getItem(STORAGE_KEY);
if (!raw) return null;
return JSON.parse(raw) as VisitData;
} catch {
return null;
}
}