Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion components.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"config": "",
"css": "src/app/globals.css",
"baseColor": "neutral",
"cssVariables": true,
Expand Down
5 changes: 1 addition & 4 deletions next.config.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
typescript: {
ignoreBuildErrors: true,
},
// Development-specific optimizations
...(process.env.NODE_ENV === 'development' && {
onDemandEntries: {
Expand Down Expand Up @@ -150,7 +147,7 @@ const nextConfig: NextConfig = {
// Updated experimental flags for Next.js 16
experimental: {
webVitalsAttribution: ['CLS', 'LCP'],
optimizePackageImports: ['@radix-ui/react-icons', 'lucide-react'],
optimizePackageImports: ['@radix-ui/react-icons', 'lucide-react', 'recharts'],
},
// Turbopack configuration (replaces webpack)
// Turbopack has native WASM support - no custom config needed
Expand Down
28 changes: 28 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-toast": "^1.2.15",
"@radix-ui/react-tooltip": "^1.2.8",
"@tanstack/react-virtual": "^3.14.5",
"@vercel/analytics": "^2.0.1",
"@vercel/speed-insights": "^2.0.0",
"bip32": "^5.0.1",
Expand Down
37 changes: 15 additions & 22 deletions src/app/(app)/address/[address]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ import {
} from '@/components/ui/table';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { ArrowLeft, Bitcoin, CircleAlert, ArrowUpRight, ArrowDownLeft, ArrowLeftRight } from 'lucide-react';
import { EmptyState } from '@/components/ui/empty-state';
import { ArrowLeft, Bitcoin, ArrowUpRight, ArrowDownLeft, ArrowLeftRight } from 'lucide-react';
import { useWallet } from '@/contexts/wallet-context';
import { formatCurrency as formatCurrencyValue } from '@/lib/format';
import { FullPageLoader, ErrorDisplay } from '@/components/ui/loader';
import { cn } from '@/lib/utils';
import type { Transaction, AddressInfo } from '@/lib/types';
Expand All @@ -32,12 +34,7 @@ const TransactionRow = React.memo(({ tx, fiatPrice, currency }: { tx: Transactio
const isReceived = tx.type === 'Received';
const fiatAmount = Math.abs(tx.btc * fiatPrice);

const formatCurrency = (value: number) => {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: currency,
}).format(value);
};
const formatCurrency = (value: number) => formatCurrencyValue(value, currency);

return (
<TableRow className="hover:bg-muted/50 transition-colors">
Expand Down Expand Up @@ -80,7 +77,7 @@ const TransactionRow = React.memo(({ tx, fiatPrice, currency }: { tx: Transactio
className={cn(
'text-sm',
tx.status === 'Confirmed' && 'border-chart-positive/40 text-chart-positive',
tx.status === 'Pending' && 'border-yellow-500/40 text-yellow-500'
tx.status === 'Pending' && 'border-warning/40 text-warning'
)}
>
{tx.status}
Expand Down Expand Up @@ -152,29 +149,25 @@ export default function AddressDetailsPage() {
loadData();
}, [address, loadData]);

const formatCurrency = (value: number) => {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: currency,
}).format(value);
};
const formatCurrency = (value: number) => formatCurrencyValue(value, currency);

if (isWalletLoading || pageIsLoading) return <FullPageLoader />;
if (walletError && !pageData) return <ErrorDisplay message={walletError} />;

if (pageError) {
return (
<div className="flex flex-col items-center justify-center gap-4 text-center">
<CircleAlert className="h-12 w-12 text-destructive" />
<h1 className="text-2xl font-bold">Address Not Found</h1>
<p className="text-muted-foreground">{pageError}</p>
<Button asChild onClick={() => router.back()}>
<EmptyState
title="Address Not Found"
message={pageError}
action={
<Button asChild>
<Link href="/security">
<ArrowLeft className="mr-2 h-4 w-4" />
Back
</Link>
</Button>
</div>
}
/>
);
}

Expand Down Expand Up @@ -209,7 +202,7 @@ export default function AddressDetailsPage() {
</CardHeader>
<CardContent>
<Card className="border-2 shadow-sm">
<CardHeader className="bg-gradient-to-br from-blue-500/5 via-transparent to-transparent">
<CardHeader className="bg-gradient-to-br from-info/5 via-transparent to-transparent">
<CardTitle className="text-base font-medium">Address Balance</CardTitle>
</CardHeader>
<CardContent>
Expand All @@ -225,7 +218,7 @@ export default function AddressDetailsPage() {
</Card>

<Card className="border-2 shadow-md">
<CardHeader className="bg-gradient-to-br from-emerald-500/5 via-transparent to-transparent border-b">
<CardHeader className="bg-gradient-to-br from-success/5 via-transparent to-transparent border-b">
<CardTitle className="flex items-center gap-2">
<IconContainer variant="emerald">
<ArrowLeftRight className="h-5 w-5" />
Expand Down
31 changes: 14 additions & 17 deletions src/app/(app)/analysis/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {
TableRow,
} from '@/components/ui/table';
import { useWallet } from '@/contexts/wallet-context';
import { FullPageLoader, ErrorDisplay } from '@/components/ui/loader';
import { useWalletDataGuard } from '@/components/wallet-data-guard';
import { Area, AreaChart, Bar, BarChart, CartesianGrid, Legend, Scatter, ScatterChart, XAxis, YAxis, Tooltip, ZAxis } from 'recharts';
import { useMemo } from 'react';
import { TrendingUp } from 'lucide-react';
Expand Down Expand Up @@ -67,6 +67,7 @@ const btcToSatsFormatter = (value: any) => {

export default function AnalysisPage() {
const { data, isLoading, error, activeXpub: xpub, fiatPrice, currency, isDiscovering, discoveryProgress } = useWallet();
const walletGuard = useWalletDataGuard();

const balanceChartData = useMemo(() => {
if (!data || !data.transactions || data.transactions.length === 0) {
Expand Down Expand Up @@ -187,12 +188,8 @@ export default function AnalysisPage() {
return Object.values(feesByMonth).sort((a,b) => a.key.localeCompare(b.key));
}, [data]);

const hasBlockingError = !!error && !data;

if (!xpub) return <FullPageLoader />;
if (isLoading && !data) return <FullPageLoader />;
if (hasBlockingError) return <ErrorDisplay message={error ?? 'Unable to load wallet data.'} />;
if (!data) return <ErrorDisplay message="No wallet data found. Please connect a wallet." />;
if (walletGuard) return walletGuard;
if (!data) return null;

if (balanceChartData.length <= 2) { // only has start and end points
return (
Expand Down Expand Up @@ -231,21 +228,21 @@ export default function AnalysisPage() {
<div className="flex flex-col gap-4 sm:gap-6">
{/* Progressive Discovery Status */}
{isDiscovering && discoveryProgress && (
<div className="bg-gradient-to-r from-blue-50 to-purple-50 dark:from-blue-950/30 dark:to-purple-950/30 border-2 border-blue-200 dark:border-blue-800 rounded-lg px-4 py-3 shadow-md">
<div className="bg-gradient-to-r from-info/10 to-chart-purple/10 dark:from-info/30 dark:to-chart-purple/30 border-2 border-info/30 dark:border-info/40 rounded-lg px-4 py-3 shadow-md">
<div className="flex items-start gap-3">
<LoaderCircle className="h-5 w-5 animate-spin text-blue-600 dark:text-blue-400 mt-0.5 flex-shrink-0" />
<LoaderCircle className="h-5 w-5 animate-spin text-info mt-0.5 flex-shrink-0" />
<div className="flex-1 space-y-2">
<div className="flex items-center justify-between">
<p className="text-sm font-semibold text-blue-900 dark:text-blue-100">
<p className="text-sm font-semibold text-info">
🔍 Discovering addresses... Charts updating with new data
</p>
<p className="text-xs text-blue-700 dark:text-blue-300 font-mono">
<p className="text-xs text-info font-mono">
{discoveryProgress.addressesWithActivity} addresses
</p>
</div>
<div className="flex items-center gap-2">
<Progress value={(discoveryProgress.addressesChecked / (discoveryProgress.addressesChecked + 20)) * 100} className="h-1.5" />
<span className="text-xs text-blue-700 dark:text-blue-300 whitespace-nowrap">
<span className="text-xs text-info whitespace-nowrap">
{discoveryProgress.addressesChecked} checked
</span>
</div>
Expand Down Expand Up @@ -322,7 +319,7 @@ export default function AnalysisPage() {
</Card>

<Card className="border-2 shadow-md">
<CardHeader className="bg-gradient-to-br from-emerald-500/5 via-transparent to-transparent border-b">
<CardHeader className="bg-gradient-to-br from-success/5 via-transparent to-transparent border-b">
<CardTitle className="text-base sm:text-lg flex items-center gap-2">
<IconContainer variant="emerald">
<ChartColumn className="h-5 w-5" />
Expand Down Expand Up @@ -360,7 +357,7 @@ export default function AnalysisPage() {
</Card>

<Card className="border-2 shadow-md">
<CardHeader className="bg-gradient-to-br from-orange-500/5 via-transparent to-transparent border-b">
<CardHeader className="bg-gradient-to-br from-primary/5 via-transparent to-transparent border-b">
<CardTitle className="text-base sm:text-lg flex items-center gap-2">
<IconContainer variant="orange">
<ChartPie className="h-5 w-5" />
Expand All @@ -383,7 +380,7 @@ export default function AnalysisPage() {
</Card>

<Card className="border-2 shadow-md">
<CardHeader className="bg-gradient-to-br from-rose-500/5 via-transparent to-transparent border-b">
<CardHeader className="bg-gradient-to-br from-chart-negative/5 via-transparent to-transparent border-b">
<CardTitle className="text-base sm:text-lg flex items-center gap-2">
<IconContainer variant="rose">
<DollarSign className="h-5 w-5" />
Expand Down Expand Up @@ -425,7 +422,7 @@ export default function AnalysisPage() {
</Card>

<Card className="border-2 shadow-md">
<CardHeader className="bg-gradient-to-br from-purple-500/5 via-transparent to-transparent border-b">
<CardHeader className="bg-gradient-to-br from-chart-purple/5 via-transparent to-transparent border-b">
<CardTitle className="text-base sm:text-lg flex items-center gap-2">
<IconContainer variant="purple">
<Activity className="h-5 w-5" />
Expand Down Expand Up @@ -455,7 +452,7 @@ export default function AnalysisPage() {
</Card>

<Card className="lg:col-span-2 border-2 shadow-md">
<CardHeader className="bg-gradient-to-br from-amber-500/5 via-transparent to-transparent border-b">
<CardHeader className="bg-gradient-to-br from-warning/5 via-transparent to-transparent border-b">
<CardTitle className="text-base sm:text-lg flex items-center gap-2">
<IconContainer variant="amber">
<Coins className="h-5 w-5" />
Expand Down
24 changes: 6 additions & 18 deletions src/app/(app)/block/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ import {
TableRow,
} from '@/components/ui/table';
import { Button } from '@/components/ui/button';
import { EmptyState } from '@/components/ui/empty-state';
import { IconContainer } from '@/components/ui/icon-container';
import { ArrowLeft, CircleAlert, Copy, Box, Download, LoaderCircle } from 'lucide-react';
import { FullPageLoader, ErrorDisplay } from '@/components/ui/loader';
import { useToast } from '@/hooks/use-toast';
import { useCopyToClipboard } from '@/hooks/use-copy-to-clipboard';
import type { BlockDetails, BlockTransaction } from '@/lib/types';
import { getBlockDetails } from '@/lib/mempool';

Expand Down Expand Up @@ -98,27 +100,13 @@ export default function BlockDetailsPage() {
};


const handleCopy = (text: string) => {
navigator.clipboard.writeText(text);
toast({
title: 'Copied to clipboard',
description: text,
});
};
const handleCopy = useCopyToClipboard();

if (pageIsLoading) return <FullPageLoader />;

if (pageError) {
return (
<div className="flex flex-col items-center justify-center gap-4 text-center">
<CircleAlert className="h-12 w-12 text-destructive" />
<h1 className="text-2xl font-bold">Block Not Found</h1>
<p className="text-muted-foreground">{pageError}</p>
<Button onClick={() => router.back()}>
<ArrowLeft className="mr-2 h-4 w-4" />
Back
</Button>
</div>
<EmptyState title="Block Not Found" message={pageError} onBack={() => router.back()} />
);
}

Expand Down Expand Up @@ -158,7 +146,7 @@ export default function BlockDetailsPage() {
</Card>

<Card className="border-2 shadow-md">
<CardHeader className="bg-gradient-to-br from-blue-500/5 via-transparent to-transparent border-b">
<CardHeader className="bg-gradient-to-br from-info/5 via-transparent to-transparent border-b">
<CardTitle className="text-base sm:text-lg flex items-center gap-2">
<IconContainer variant="blue">
<CircleAlert className="h-5 w-5" />
Expand All @@ -184,7 +172,7 @@ export default function BlockDetailsPage() {
</Card>

<Card className="border-2 shadow-md">
<CardHeader className="bg-gradient-to-br from-emerald-500/5 via-transparent to-transparent border-b">
<CardHeader className="bg-gradient-to-br from-success/5 via-transparent to-transparent border-b">
<CardTitle className="text-base sm:text-lg flex items-center gap-2">
<IconContainer variant="emerald">
<Download className="h-5 w-5" />
Expand Down
25 changes: 8 additions & 17 deletions src/app/(app)/chat/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { summarizeAddress } from '@/ai/flows/summarize-address';
import { cn } from '@/lib/utils';
import { Avatar, AvatarFallback } from '@/components/ui/avatar';
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { useWallet } from '@/contexts/wallet-context';
import { useWalletData, useWalletActions, useWalletAi } from '@/contexts/wallet-context';
import { FullPageLoader, ErrorDisplay } from '@/components/ui/loader';
import { AiChart } from '@/components/ui/ai-chart';
import type { Message, WalletData } from '@/lib/types';
Expand Down Expand Up @@ -183,18 +183,9 @@ export default function ChatPage() {
const { track } = useAnalytics();
const [isAiLoading, setAiLoading] = useState(false);
const scrollAreaRef = useRef<HTMLDivElement>(null);
const {
data: walletData,
isLoading: isWalletLoading,
isLoadingAiContent,
aiContentError,
refreshAiContent,
error: walletError,
activeXpub: xpub,
messages,
setMessages,
suggestions: masterSuggestions,
} = useWallet();
const { data: walletData, isLoading: isWalletLoading, error: walletError, activeXpub: xpub } = useWalletData();
const { refreshAiContent } = useWalletActions();
const { isLoadingAiContent, aiContentError, messages, setMessages, suggestions: masterSuggestions } = useWalletAi();

const [placeholder, setPlaceholder] = useState(fallbackSuggestions[0]);
const [displayedSuggestions, setDisplayedSuggestions] = useState<string[]>([]);
Expand Down Expand Up @@ -468,10 +459,10 @@ export default function ChatPage() {
<div className="mx-auto max-w-3xl space-y-4 sm:space-y-6 pt-4">
{/* AI Insights Loading Indicator */}
{isLoadingAiContent && (
<Alert className="shadow-md bg-blue-50 dark:bg-blue-950/30 border-blue-200 dark:border-blue-800">
<CircleDashed className="h-5 w-5 animate-spin text-blue-600 dark:text-blue-400" />
<AlertTitle className="text-blue-900 dark:text-blue-100">Generating AI insights...</AlertTitle>
<AlertDescription className="text-blue-800 dark:text-blue-200">
<Alert className="shadow-md bg-info/15 dark:bg-info/30 border-info/30 dark:border-info/40">
<CircleDashed className="h-5 w-5 animate-spin text-info" />
<AlertTitle className="text-info">Generating AI insights...</AlertTitle>
<AlertDescription className="text-info">
Your wallet is loaded. AI is analyzing your transactions and will provide personalized insights momentarily.
</AlertDescription>
</Alert>
Expand Down
Loading