feat(detection): implement multi-page token detection system

- Add extractTwitterTokens() to detect $TOKEN mentions (e.g., $BONK, $SOL)
- Add extractContractAddresses() for Solana (base58) and Ethereum (0x) addresses
- Add extractTradingPairs() to detect TOKEN/SOL, TOKEN/USDT patterns
- Update extractPageContext() to use new detection functions
- Add detectedTokens field to PageContext interface
- Create DetectedTokensList component to display detected tokens
- Integrate DetectedTokensList into ChatInterface
- Add handleDetectedTokenClick to analyze selected tokens
- Support auto-detection on Twitter, generic pages, and DexScreener

Implements Task 2: Multi-Page Token Detection
Part of hybrid token detection system (manual search + auto-detect)
This commit is contained in:
API Test Bot 2026-02-04 10:55:49 +07:00
parent cb879fca37
commit e89824db0f
4 changed files with 251 additions and 0 deletions

View file

@ -21,7 +21,9 @@ import {
import { SafetyScoreDisplay } from "../crypto/SafetyScoreDisplay";
import { WatchlistPanel } from "../crypto/WatchlistPanel";
import { AlertConfigModal } from "../crypto/AlertConfigModal";
import { DetectedTokensList } from "../components/DetectedTokensList";
import type { WatchlistItem } from "../widgets";
import type { TokenData } from "../context/PageContextProvider";
type ViewMode = "chat" | "watchlist" | "safety";
@ -471,6 +473,14 @@ What would you like to know?`;
}, 500);
};
/**
* Handle detected token click
*/
const handleDetectedTokenClick = (token: TokenData) => {
const query = token.tokenSymbol || token.pairAddress;
handleTokenSearch(query);
};
return (
<div className="flex flex-col h-full">
{/* Header with space selector and settings */}
@ -495,6 +505,14 @@ What would you like to know?`;
/>
)}
{/* Detected tokens list (on Twitter and other pages) */}
{context?.detectedTokens && context.detectedTokens.length > 0 && viewMode === "chat" && (
<DetectedTokensList
tokens={context.detectedTokens}
onTokenClick={handleDetectedTokenClick}
/>
)}
{/* Main content area */}
<div className="flex-1 overflow-y-auto">
{viewMode === "chat" && (

View file

@ -0,0 +1,84 @@
import { Coins, ExternalLink } from "lucide-react";
import { Button } from "@/routes/ui/button";
import { cn } from "~/lib/utils";
import type { TokenData } from "../context/PageContextProvider";
import { ChainIcon } from "./shared/ChainIcon";
export interface DetectedTokensListProps {
/** List of detected tokens */
tokens: TokenData[];
/** Callback when a token is clicked */
onTokenClick?: (token: TokenData) => void;
/** Additional class names */
className?: string;
}
/**
* DetectedTokensList - Display list of tokens detected from page content
*
* Features:
* - Shows tokens detected from Twitter mentions, contract addresses, trading pairs
* - Click to analyze token
* - Shows chain icon for each token
*/
export function DetectedTokensList({
tokens,
onTokenClick,
className,
}: DetectedTokensListProps) {
if (tokens.length === 0) {
return null;
}
return (
<div className={cn("border-b bg-muted/30", className)}>
<div className="p-3">
<div className="flex items-center gap-2 mb-2">
<Coins className="h-4 w-4 text-primary" />
<h3 className="text-sm font-semibold">Detected Tokens</h3>
<span className="text-xs text-muted-foreground">
({tokens.length} found)
</span>
</div>
<div className="space-y-1">
{tokens.slice(0, 5).map((token, index) => (
<button
key={index}
onClick={() => onTokenClick?.(token)}
className="w-full flex items-center justify-between p-2 rounded-md hover:bg-background transition-colors text-left"
>
<div className="flex items-center gap-2 flex-1 min-w-0">
<ChainIcon chain={token.chain} size="sm" />
<div className="flex-1 min-w-0">
{token.tokenSymbol ? (
<div className="font-medium text-sm truncate">
{token.tokenSymbol}
</div>
) : (
<div className="text-xs text-muted-foreground font-mono truncate">
{token.pairAddress.slice(0, 8)}...{token.pairAddress.slice(-6)}
</div>
)}
{token.tokenName && (
<div className="text-xs text-muted-foreground truncate">
{token.tokenName}
</div>
)}
</div>
</div>
<ExternalLink className="h-3 w-3 text-muted-foreground flex-shrink-0" />
</button>
))}
</div>
{tokens.length > 5 && (
<div className="text-xs text-muted-foreground text-center mt-2">
+{tokens.length - 5} more tokens detected
</div>
)}
</div>
</div>
);
}

View file

@ -23,6 +23,8 @@ export interface PageContext {
title: string;
pageType: PageType;
tokenData?: TokenData;
/** Detected tokens from page content (Twitter mentions, addresses, pairs) */
detectedTokens?: TokenData[];
}
interface PageContextValue {