mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-04-26 17:26:23 +02:00
- Implemented HolderAnalysisWidget to display holder distribution and concentration risk. - Created LiveTokenDataWidget for real-time market data including price changes and transaction activity. - Added LiveTokenPriceWidget to show current token price and changes over various timeframes. - Developed MarketOverviewWidget to provide a summary of market statistics and token prices. - Introduced TrendingTokensWidget to showcase trending tokens with price changes and volume. - Added TradingSuggestionToolUI for AI-powered trading suggestions with detailed entry, targets, and stop-loss information. - Enhanced settings components for better user configuration options in the SurfSense Browser Extension.
1286 lines
43 KiB
Markdown
1286 lines
43 KiB
Markdown
# Epic 1: Trợ lý Crypto AI trên Trình duyệt
|
|
|
|
**Trạng thái:** 🚧 ĐANG TRIỂN KHAI (IN PROGRESS)
|
|
**Giai đoạn:** Phase 1
|
|
**Thời gian:** 2 tuần
|
|
**Mức độ ưu tiên:** P0 (Nghiêm trọng)
|
|
|
|
**Tiến độ:**
|
|
- ✅ Frontend Extension: 80% hoàn thành (Stories 1.1-1.6, 1.7-1.9)
|
|
- ⏳ Backend APIs: 0% (Story 1.0 - Authentication chưa bắt đầu)
|
|
- ⏳ API Integrations: 0% (DexScreener, DefiLlama chưa implement)
|
|
|
|
---
|
|
|
|
## Tổng quan Epic
|
|
|
|
Mang AI co-pilot của SurfSense vào trình duyệt, cho phép users chat với AI, nhận insights về token, và lưu thông tin quan trọng ngay khi đang browse các trang crypto.
|
|
|
|
**Giá trị cho User:**
|
|
- **Chat với AI ngay trong browser** - Không cần chuyển tab, hỏi AI về bất kỳ token nào đang xem.
|
|
- **Tự động hiểu context** - AI biết bạn đang xem token gì trên DexScreener và đưa ra insights phù hợp.
|
|
- **Lưu thông tin nhanh** - Một cú click để lưu trang, token, insights vào knowledge base.
|
|
- **Đồng bộ mọi nơi** - Cài đặt và lịch sử chat được đồng bộ giữa extension và web dashboard.
|
|
|
|
---
|
|
|
|
## Các phụ thuộc kỹ thuật (Technical Dependencies)
|
|
|
|
Epic này phụ thuộc vào các API bên ngoài và backend services. Tất cả các integrations phải đáp ứng tiêu chí Định nghĩa hoàn thành (DoD) bên dưới.
|
|
|
|
### 1. DexScreener API Integration [FR-DAT-01]
|
|
|
|
**Mục đích:** Trích xuất dữ liệu token thời gian thực cho tính năng hỗ trợ AI nhận biết ngữ cảnh.
|
|
|
|
**API Endpoints:**
|
|
```typescript
|
|
// Public API (no auth required)
|
|
GET https://api.dexscreener.com/latest/dex/tokens/{tokenAddress}
|
|
GET https://api.dexscreener.com/latest/dex/pairs/{chainId}/{pairAddress}
|
|
GET https://api.dexscreener.com/latest/dex/search?q={query}
|
|
```
|
|
|
|
**Giới hạn tốc độ (Rate Limits):**
|
|
- **Free Tier:** 300 requests/phút
|
|
- **Xử lý:** Implement exponential backoff với tối đa 3 lần thử lại (retries)
|
|
- **Caching:** Cache token data trong 30 giây để giảm lượng API calls
|
|
|
|
**Xử lý lỗi (Error Handling):**
|
|
```typescript
|
|
// Retry logic
|
|
async function fetchDexScreenerData(tokenAddress: string, retries = 3) {
|
|
try {
|
|
const response = await fetch(`https://api.dexscreener.com/latest/dex/tokens/${tokenAddress}`);
|
|
|
|
if (response.status === 429) {
|
|
// Rate limit exceeded
|
|
if (retries > 0) {
|
|
await sleep(2 ** (3 - retries) * 1000); // Exponential backoff
|
|
return fetchDexScreenerData(tokenAddress, retries - 1);
|
|
}
|
|
throw new Error('Rate limit exceeded. Please try again later.');
|
|
}
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`DexScreener API error: ${response.status}`);
|
|
}
|
|
|
|
return await response.json();
|
|
} catch (error) {
|
|
// Show user-friendly error
|
|
showToast('Failed to fetch token data. Please try again.', 'error');
|
|
throw error;
|
|
}
|
|
}
|
|
```
|
|
|
|
**Định nghĩa hoàn thành (DoD):**
|
|
- [ ] Rate limiting được implement với exponential backoff
|
|
- [ ] Xử lý lỗi với thông báo thân thiện cho user
|
|
- [ ] Caching layer để giảm API calls
|
|
- [ ] Logic thử lại (tối đa 3 lần)
|
|
- [ ] Xử lý Timeout (tối đa 5 giây)
|
|
- [ ] Hỗ trợ chế độ Offline (hiện data đã cache)
|
|
- [ ] Unit tests cho các kịch bản lỗi
|
|
|
|
---
|
|
|
|
### 2. DefiLlama API Integration [FR-DAT-02]
|
|
|
|
**Mục đích:** TVL, protocol data, và các chỉ số DeFi để phân tích token toàn diện.
|
|
|
|
**API Endpoints:**
|
|
```typescript
|
|
// Public API (no auth required)
|
|
GET https://api.llama.fi/protocol/{protocol}
|
|
GET https://api.llama.fi/tvl/{protocol}
|
|
GET https://api.llama.fi/charts/{protocol}
|
|
```
|
|
|
|
**Giới hạn tốc độ (Rate Limits):**
|
|
- **Free Tier:** Không giới hạn (nhưng khuyến nghị tối đa 60 requests/phút)
|
|
- **Xử lý:** Implement rate limiting ở phía client
|
|
- **Caching:** Cache protocol data trong 5 phút
|
|
|
|
**Error Handling:**
|
|
```typescript
|
|
async function fetchDefiLlamaData(protocol: string) {
|
|
try {
|
|
const response = await fetch(`https://api.llama.fi/protocol/${protocol}`, {
|
|
signal: AbortSignal.timeout(5000), // 5 second timeout
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`DefiLlama API error: ${response.status}`);
|
|
}
|
|
|
|
return await response.json();
|
|
} catch (error) {
|
|
if (error.name === 'TimeoutError') {
|
|
showToast('Request timed out. Please try again.', 'error');
|
|
} else {
|
|
showToast('Failed to fetch protocol data.', 'error');
|
|
}
|
|
throw error;
|
|
}
|
|
}
|
|
```
|
|
|
|
**Định nghĩa hoàn thành (DoD):**
|
|
- [ ] Client-side rate limiting (tối đa 60 req/phút)
|
|
- [ ] Xử lý lỗi với timeout (5 giây)
|
|
- [ ] Caching layer (5 phút TTL)
|
|
- [ ] Logic thử lại cho các lỗi tạm thời (transient errors)
|
|
- [ ] Hỗ trợ chế độ Offline
|
|
- [ ] Unit tests cho các kịch bản lỗi
|
|
|
|
---
|
|
|
|
### 3. Backend APIs
|
|
|
|
**Authentication:**
|
|
```typescript
|
|
GET /auth/google // OAuth URL
|
|
POST /auth/callback // OAuth callback
|
|
POST /auth/login // Email/password login
|
|
POST /auth/refresh // Refresh JWT
|
|
POST /auth/logout // Invalidate token
|
|
GET /auth/me // Get current user
|
|
```
|
|
|
|
**Settings:**
|
|
```typescript
|
|
GET /api/settings // Get user settings (model, search space, connectors)
|
|
PUT /api/settings // Update settings
|
|
```
|
|
|
|
**Chat:**
|
|
```typescript
|
|
GET /api/chat/messages // Get chat history
|
|
POST /api/chat/messages // Send message (streaming response)
|
|
POST /api/chat/save // Save chat to backend
|
|
```
|
|
|
|
**Capture:**
|
|
```typescript
|
|
POST /api/capture // Capture page content
|
|
GET /api/captures // List captured pages
|
|
```
|
|
|
|
**Định nghĩa hoàn thành (DoD):**
|
|
- [ ] Tất cả endpoints được document trong API spec
|
|
- [ ] Yêu cầu JWT authentication cho các protected endpoints
|
|
- [ ] Phản hồi lỗi tuân theo format chuẩn:
|
|
```json
|
|
{
|
|
"error": "Error message",
|
|
"code": "ERROR_CODE",
|
|
"details": {}
|
|
}
|
|
```
|
|
- [ ] Rate limiting trên backend (100 req/phút mỗi user)
|
|
- [ ] CORS được cấu hình cho extension origin
|
|
- [ ] Unit tests cho tất cả endpoints
|
|
|
|
---
|
|
|
|
### 4. Chrome APIs
|
|
|
|
**Required Permissions:**
|
|
```json
|
|
{
|
|
"permissions": [
|
|
"sidePanel",
|
|
"storage",
|
|
"tabs",
|
|
"identity",
|
|
"activeTab"
|
|
],
|
|
"host_permissions": [
|
|
"https://dexscreener.com/*",
|
|
"https://api.dexscreener.com/*",
|
|
"https://api.llama.fi/*"
|
|
]
|
|
}
|
|
```
|
|
|
|
**Chrome Identity API:**
|
|
```typescript
|
|
chrome.identity.launchWebAuthFlow({
|
|
url: `${BACKEND_URL}/auth/google`,
|
|
interactive: true,
|
|
}, (redirectUrl) => {
|
|
// Handle OAuth callback
|
|
});
|
|
```
|
|
|
|
**Chrome Storage API:**
|
|
```typescript
|
|
// Plasmo Storage wrapper
|
|
import { Storage } from "@plasmohq/storage";
|
|
|
|
const storage = new Storage();
|
|
await storage.set("auth_token", encryptedJWT);
|
|
const token = await storage.get("auth_token");
|
|
```
|
|
|
|
**Định nghĩa hoàn thành (DoD):**
|
|
- [ ] Manifest permissions được cấu hình
|
|
- [ ] Host permissions cho tất cả external APIs
|
|
- [ ] Storage encryption cho dữ liệu nhạy cảm
|
|
- [ ] Xử lý lỗi khi permission bị từ chối
|
|
- [ ] Unit tests cho các interactions với Chrome API
|
|
|
|
---
|
|
|
|
## User Stories
|
|
|
|
### Story 1.0: Hệ thống Xác thực (Authentication System)
|
|
**[FR-EXT-00]** ⚠️ **P0 BLOCKER** - ⏳ **CHƯA BẮT ĐẦU**
|
|
|
|
**Là một** SurfSense user,
|
|
**Tôi muốn** đăng nhập vào extension với tài khoản SurfSense của tôi,
|
|
**Để** extension có thể đồng bộ settings, lịch sử chat, và truy cập backend APIs.
|
|
|
|
**Trạng thái:** ⏳ Backend APIs chưa được implement
|
|
|
|
**Tiêu chí chấp nhận (Acceptance Criteria - BDD Format):**
|
|
|
|
#### AC 1.0.1: User Login Flow
|
|
**Given** user chưa đăng nhập vào extension
|
|
**When** user click nút "Login" trong side panel header
|
|
**Then** Chrome Identity API popup mở ra với các tùy chọn OAuth (Google, Email/Password)
|
|
**And** user chọn Google OAuth
|
|
**And** user hoàn tất quy trình OAuth
|
|
**Then** extension nhận JWT token từ backend
|
|
**And** extension chuyển hướng (redirects) về side panel
|
|
**And** avatar/email của user hiển thị trong header
|
|
|
|
**Kịch bản lỗi (Error Scenario):**
|
|
**Given** user đang trong quy trình OAuth
|
|
**When** OAuth thất bại (user hủy hoặc lỗi mạng)
|
|
**Then** extension hiển thị error toast "Login failed. Please try again."
|
|
**And** user vẫn ở trạng thái chưa xác thực (unauthenticated state)
|
|
|
|
---
|
|
|
|
#### AC 1.0.2: Quản lý JWT Token
|
|
**Given** backend trả về JWT token (hết hạn sau 7 ngày)
|
|
**When** extension nhận được token
|
|
**Then** extension lưu encrypted JWT trong Plasmo Storage
|
|
**And** thời gian hết hạn của token được lưu
|
|
|
|
**Auto-Refresh:**
|
|
**Given** JWT token còn < 1 ngày là hết hạn
|
|
**When** extension kiểm tra token expiry (mỗi giờ)
|
|
**Then** extension gọi API `/auth/refresh`
|
|
**And** backend trả về JWT token mới
|
|
**And** extension cập nhật token đã lưu
|
|
|
|
**Logout:**
|
|
**Given** user click "Logout" trong settings dropdown
|
|
**When** hành động logout được kích hoạt
|
|
**Then** extension xóa JWT khỏi Plasmo Storage
|
|
**And** extension gọi API `/auth/logout`
|
|
**And** user chuyển hướng về màn hình welcome
|
|
|
|
---
|
|
|
|
#### AC 1.0.3: Authenticated API Requests
|
|
**Given** user đã đăng nhập (JWT đã lưu)
|
|
**When** extension thực hiện API request (ví dụ: `/chat/messages`)
|
|
**Then** request bao gồm header `Authorization: Bearer {JWT}`
|
|
**And** backend xác thực chữ ký JWT
|
|
**And** request thành công với status 200
|
|
|
|
**Expired Token:**
|
|
**Given** JWT token đã hết hạn
|
|
**When** extension thực hiện API request
|
|
**Then** backend trả về lỗi 401 Unauthorized
|
|
**And** extension cố gắng auto-refresh
|
|
**And** nếu refresh thành công, thử lại request ban đầu
|
|
**And** nếu refresh thất bại, chuyển hướng user đến trang login
|
|
|
|
---
|
|
|
|
#### AC 1.0.4: Xử lý Offline
|
|
**Given** user đã đăng nhập trước đó
|
|
**And** user mất kết nối internet
|
|
**When** extension cố gắng kết nối backend
|
|
**Then** extension hiển thị chỉ báo "Offline" trong header
|
|
**And** extension cache trạng thái auth gần nhất
|
|
**And** hành động của user (ví dụ: tin nhắn chat) được đưa vào hàng đợi (queued)
|
|
|
|
**Khi có mạng trở lại (Back Online):**
|
|
**Given** user đang offline với các hành động trong hàng đợi
|
|
**When** kết nối internet được khôi phục
|
|
**Then** extension đồng bộ các hành động trong hàng đợi với backend
|
|
**And** chỉ báo "Offline" biến mất
|
|
**And** user thấy toast thành công "Synced {N} actions"
|
|
|
|
**Triển khai kỹ thuật:**
|
|
```typescript
|
|
// Use Chrome Identity API for OAuth
|
|
chrome.identity.launchWebAuthFlow({
|
|
url: `${BACKEND_URL}/auth/google`,
|
|
interactive: true,
|
|
}, (redirectUrl) => {
|
|
// Extract JWT from redirect URL
|
|
const jwt = new URL(redirectUrl).searchParams.get('token');
|
|
|
|
// Store encrypted JWT
|
|
await storage.set('auth_token', encrypt(jwt));
|
|
});
|
|
|
|
// Auto-refresh token
|
|
setInterval(async () => {
|
|
const token = await storage.get('auth_token');
|
|
const decoded = decodeJWT(token);
|
|
|
|
if (isExpiringSoon(decoded.exp, 1 * 24 * 60 * 60)) {
|
|
const newToken = await refreshToken(token);
|
|
await storage.set('auth_token', encrypt(newToken));
|
|
}
|
|
}, 60 * 60 * 1000); // Check every hour
|
|
|
|
// Include JWT in all API requests
|
|
const api = {
|
|
async request(endpoint: string, options: RequestInit = {}) {
|
|
const token = await storage.get('auth_token');
|
|
return fetch(`${BACKEND_URL}${endpoint}`, {
|
|
...options,
|
|
headers: {
|
|
...options.headers,
|
|
'Authorization': `Bearer ${decrypt(token)}`,
|
|
},
|
|
});
|
|
},
|
|
};
|
|
```
|
|
|
|
**Cân nhắc bảo mật (Security Considerations):**
|
|
- Không bao giờ lưu API keys trong extension code (hiển thị cho users)
|
|
- Mã hóa JWT trong Plasmo Storage
|
|
- Sử dụng HTTPS cho tất cả API calls
|
|
- Triển khai CSRF protection
|
|
- Validate chữ ký JWT trên backend
|
|
|
|
**Backend APIs Needed:**
|
|
```typescript
|
|
GET /auth/google // OAuth URL
|
|
POST /auth/callback // OAuth callback
|
|
POST /auth/login // Email/password login
|
|
POST /auth/refresh // Refresh JWT
|
|
POST /auth/logout // Invalidate token
|
|
GET /auth/me // Get current user
|
|
```
|
|
|
|
**Files:**
|
|
- `lib/auth/chrome-identity.ts` (new) - Chrome Identity API wrapper
|
|
- `lib/auth/jwt-manager.ts` (new) - JWT storage, refresh, validation
|
|
- `lib/auth/api-client.ts` (new) - Authenticated API client
|
|
- `sidepanel/auth/LoginButton.tsx` (new) - Login UI
|
|
- `sidepanel/auth/UserProfile.tsx` (new) - User avatar/menu
|
|
|
|
---
|
|
|
|
### Story 1.1: Kiến trúc Side Panel (Side Panel Architecture)
|
|
**[FR-EXT-01]** ✅ **COMPLETED**
|
|
|
|
**Là một** crypto trader,
|
|
**Tôi muốn** mở AI assistant dưới dạng side panel (không phải popup nhỏ),
|
|
**Để** tôi có thể chat với AI trong khi vẫn xem được DexScreener chart.
|
|
|
|
**Tiêu chí chấp nhận (Acceptance Criteria - BDD Format):**
|
|
|
|
#### AC 1.1.1: Mở Side Panel khi Click Icon
|
|
**Given** user đã cài đặt extension
|
|
**When** user click icon extension trong Chrome toolbar
|
|
**Then** side panel mở ra bên phải màn hình
|
|
**And** chiều rộng của side panel là 400px (mặc định)
|
|
**And** side panel không che khuất nội dung chính
|
|
|
|
---
|
|
|
|
#### AC 1.1.2: Thay đổi kích thước Side Panel (Resizable)
|
|
**Given** side panel đang mở
|
|
**When** user kéo cạnh trái của panel
|
|
**Then** chiều rộng panel thay đổi
|
|
**And** chiều rộng tối thiểu là 300px
|
|
**And** chiều rộng tối đa là 600px
|
|
**And** tùy chọn kích thước (resize preference) được lưu trong Plasmo Storage
|
|
|
|
---
|
|
|
|
#### AC 1.1.3: Side Panel Tồn tại qua các Tab
|
|
**Given** side panel đang mở trên tab A
|
|
**When** user chuyển sang tab B
|
|
**Then** side panel vẫn hiển thị trên tab B
|
|
**And** nội dung panel reload với context của tab B (nếu có)
|
|
|
|
**Edge Case:**
|
|
**Given** user đóng side panel trên tab A
|
|
**When** user chuyển sang tab B
|
|
**Then** side panel vẫn đóng trên tab B
|
|
|
|
---
|
|
|
|
#### AC 1.1.4: Manifest Permissions
|
|
**Given** extension được build
|
|
**When** developer kiểm tra `manifest.json`
|
|
**Then** `sidePanel` permission có trong manifest
|
|
**And** `openPanelOnActionClick: true` được thiết lập trong background script
|
|
|
|
**Ghi chú kỹ thuật (Technical Notes):**
|
|
```typescript
|
|
// background/index.ts
|
|
chrome.sidePanel
|
|
.setPanelBehavior({ openPanelOnActionClick: true })
|
|
.catch((error) => console.error("Failed to set side panel behavior:", error));
|
|
```
|
|
|
|
**Files:**
|
|
- `surfsense_browser_extension/sidepanel.tsx` ✅
|
|
- `surfsense_browser_extension/package.json` (thêm sidePanel permission) ✅
|
|
|
|
---
|
|
|
|
### Story 1.2: Tích hợp Giao diện AI Chat (AI Chat Interface Integration)
|
|
**[FR-EXT-02, FR-INT-01]** ⭐ **AI MOAT** - ✅ **COMPLETED**
|
|
|
|
**Là một** crypto trader,
|
|
**Tôi muốn** chat với AI trong extension giống như trên web dashboard,
|
|
**Để** tôi có trải nghiệm nhất quán và đầy đủ tính năng.
|
|
|
|
**Tiêu chí chấp nhận (Acceptance Criteria - BDD Format):**
|
|
|
|
#### AC 1.2.1: Tích hợp Giao diện Chat
|
|
**Given** user đã đăng nhập và mở side panel
|
|
**When** user gõ tin nhắn "Is BULLA token safe?" và nhấn Enter
|
|
**Then** tin nhắn hiển thị trong khung chat với avatar user
|
|
**And** phản hồi của AI bắt đầu streaming
|
|
**And** `@assistant-ui/react` Thread component renders chính xác
|
|
|
|
---
|
|
|
|
#### AC 1.2.2: Phản hồi Streaming
|
|
**Given** user đã gửi tin nhắn
|
|
**When** AI bắt đầu phản hồi
|
|
**Then** text phản hồi hiển thị từng từ (streaming)
|
|
**And** hiển thị các bước suy nghĩ (thinking steps visualization) (nếu có)
|
|
**And** user có thể cuộn trong khi AI đang phản hồi
|
|
**And** tự động cuộn xuống dưới cùng khi có nội dung mới
|
|
|
|
**Kịch bản lỗi (Error Scenario):**
|
|
**Given** kết nối streaming bị ngắt
|
|
**When** lỗi mạng xảy ra
|
|
**Then** extension hiển thị thông báo lỗi "Connection lost. Retrying..."
|
|
**And** extension cố gắng kết nối lại (tối đa 3 lần thử lại)
|
|
|
|
---
|
|
|
|
#### AC 1.2.3: Rendering Tool UI
|
|
**Given** AI response bao gồm tool outputs
|
|
**When** AI sử dụng tool `display_image`
|
|
**Then** component `DisplayImageToolUI` render hình ảnh
|
|
**And** hình ảnh có caption và metadata
|
|
|
|
**Link Preview:**
|
|
**Given** AI response bao gồm tool `link_preview`
|
|
**When** tool output renders
|
|
**Then** `LinkPreviewToolUI` hiển thị tiêu đề, mô tả, thumbnail
|
|
|
|
**Scraping:**
|
|
**Given** AI sử dụng tool `scrape_webpage`
|
|
**When** quá trình scraping hoàn tất
|
|
**Then** `ScrapeWebpageToolUI` hiển thị nội dung đã trích xuất
|
|
**And** user có thể mở rộng/thu gọn nội dung
|
|
|
|
---
|
|
|
|
#### AC 1.2.4: Lưu trữ Lịch sử Chat
|
|
**Given** user đã chat với AI
|
|
**When** user đóng extension
|
|
**And** user mở lại extension
|
|
**Then** lịch sử chat vẫn hiển thị (được load từ Plasmo Storage)
|
|
**And** user có thể cuộn lên xem tin nhắn cũ
|
|
|
|
**Đồng bộ Backend (Backend Sync):**
|
|
**Given** user đã đăng nhập
|
|
**When** tin nhắn chat được gửi
|
|
**Then** tin nhắn được đồng bộ với backend API (`POST /chat/messages`)
|
|
**And** lịch sử chat có thể truy cập từ web dashboard
|
|
|
|
---
|
|
|
|
#### AC 1.2.5: Dịch Truy vấn Ngôn ngữ Tự nhiên (Natural Language Query Translation) ⭐ [FR-INT-01]
|
|
**Given** user đang xem DexScreener
|
|
**When** user gõ "Show me trending Solana memes with >$10k liquidity"
|
|
**Then** AI dịch truy vấn thành DexScreener API filters:
|
|
```json
|
|
{
|
|
"chain": "solana",
|
|
"category": "meme",
|
|
"minLiquidity": 10000,
|
|
"sort": "trending"
|
|
}
|
|
```
|
|
**And** AI giải thích: "I'm searching for meme tokens on Solana with liquidity above $10k, sorted by trending volume."
|
|
**And** AI thực hiện tìm kiếm và trả về kết quả
|
|
|
|
**Truy vấn phức tạp (Complex Query):**
|
|
**Given** user hỏi "Find tokens launched in last 24h with >50% price increase"
|
|
**When** AI xử lý truy vấn
|
|
**Then** AI dịch thành:
|
|
```json
|
|
{
|
|
"minAge": 0,
|
|
"maxAge": 86400,
|
|
"minPriceChange24h": 50
|
|
}
|
|
```
|
|
**And** AI trả về kết quả đã lọc kèm lời giải thích
|
|
|
|
**Ví dụ Placeholder:**
|
|
**Given** user focus vào ô chat input
|
|
**When** input đang trống
|
|
**Then** placeholder hiển thị các ví dụ xoay vòng:
|
|
- "Show me new Solana tokens with high volume"
|
|
- "Find tokens with locked liquidity >90%"
|
|
- "What are the top trending meme coins today?"
|
|
|
|
**Component Reuse:**
|
|
```typescript
|
|
// From frontend
|
|
import { Thread } from "@/components/assistant-ui/thread";
|
|
import { DisplayImageToolUI } from "@/components/tool-ui/display-image";
|
|
import { LinkPreviewToolUI } from "@/components/tool-ui/link-preview";
|
|
import { ScrapeWebpageToolUI } from "@/components/tool-ui/scrape-webpage";
|
|
```
|
|
|
|
**Files:**
|
|
- `sidepanel/chat/ChatInterface.tsx` ✅
|
|
- `sidepanel/chat/ChatMessages.tsx` ✅
|
|
- `sidepanel/chat/ChatInput.tsx` ✅
|
|
- `sidepanel/chat/ChatHeader.tsx` ✅
|
|
|
|
---
|
|
|
|
### Story 1.3: Phát hiện Ngữ cảnh Trang (Page Context Detection)
|
|
**[FR-EXT-03]** ✅ **COMPLETED**
|
|
|
|
**Là một** crypto trader đang xem DexScreener,
|
|
**Tôi muốn** AI tự động hiểu tôi đang xem token nào,
|
|
**Để** tôi không cần copy/paste địa chỉ token mỗi lần.
|
|
|
|
**Tiêu chí chấp nhận (Acceptance Criteria - BDD Format):**
|
|
|
|
#### AC 1.3.1: Phát hiện Loại Trang (Page Type Detection)
|
|
**Given** user điều hướng đến trang DexScreener
|
|
**When** content script chạy
|
|
**Then** loại trang (page type) được phát hiện là `dexscreener`
|
|
**And** logic trích xuất token được kích hoạt
|
|
|
|
**Các trang khác:**
|
|
**Given** user điều hướng đến CoinGecko
|
|
**When** content script chạy
|
|
**Then** loại trang được phát hiện là `coingecko`
|
|
|
|
**Given** user điều hướng đến Twitter/X
|
|
**Then** loại trang được phát hiện là `twitter`
|
|
|
|
**Given** user điều hướng đến trang không xác định
|
|
**Then** loại trang được phát hiện là `generic`
|
|
|
|
---
|
|
|
|
#### AC 1.3.2: Trích xuất Dữ liệu Token (DexScreener)
|
|
**Given** user đang xem trang token: `dexscreener.com/solana/ABC123`
|
|
**When** content script trích xuất dữ liệu
|
|
**Then** các dữ liệu sau được trích xuất:
|
|
- Token address: `ABC123`
|
|
- Chain: `solana`
|
|
- Price: `$0.0001234`
|
|
- 24h Volume: `$10,234`
|
|
- Liquidity: `$5,123`
|
|
- Pair info: `BULLA/SOL`
|
|
|
|
**And** dữ liệu được gửi đến side panel qua `chrome.runtime.sendMessage`
|
|
|
|
---
|
|
|
|
#### AC 1.3.3: Context Injection vào Chat
|
|
**Given** dữ liệu token đã được trích xuất
|
|
**When** user mở chat
|
|
**Then** AI nhận được context: "You are viewing BULLA/SOL token on Solana. Address: ABC123. Current price: $0.0001234..."
|
|
**And** user có thể hỏi "Is this safe?" mà không cần chỉ định token
|
|
|
|
**Cập nhật Context:**
|
|
**Given** user điều hướng đến token khác
|
|
**When** dữ liệu token mới được trích xuất
|
|
**Then** chat context tự động cập nhật
|
|
**And** AI nhận biết token mới trong các tin nhắn tiếp theo
|
|
|
|
**Triển khai kỹ thuật:**
|
|
```typescript
|
|
// content.ts
|
|
function detectPageType(): PageType {
|
|
const hostname = window.location.hostname;
|
|
if (hostname.includes('dexscreener.com')) return 'dexscreener';
|
|
if (hostname.includes('coingecko.com')) return 'coingecko';
|
|
if (hostname.includes('twitter.com') || hostname.includes('x.com')) return 'twitter';
|
|
return 'generic';
|
|
}
|
|
|
|
function extractDexScreenerData(): TokenData {
|
|
// Extract from URL: /solana/address
|
|
// Or from page DOM
|
|
}
|
|
```
|
|
|
|
**Files:**
|
|
- `content.ts` ✅
|
|
- `sidepanel/context/PageContextProvider.tsx` ✅
|
|
|
|
---
|
|
|
|
### Story 1.4: Tích hợp Thông minh với DexScreener
|
|
**[FR-EXT-04]** ✅ **COMPLETED**
|
|
|
|
**Là một** crypto trader trên DexScreener,
|
|
**Tôi muốn** thấy thẻ thông tin token (token info card) ở đầu side panel,
|
|
**Để** tôi có thể nhanh chóng kiểm tra độ an toàn hoặc xem các holders.
|
|
|
|
**Tiêu chí chấp nhận (Acceptance Criteria - BDD Format):**
|
|
|
|
#### AC 1.4.1: Hiển thị Thẻ Thông tin Token
|
|
**Given** user điều hướng đến trang token DexScreener
|
|
**When** side panel mở ra
|
|
**Then** Token Info Card hiển thị ở đầu panel
|
|
**And** thẻ hiển thị:
|
|
- Token symbol/name: "BULLA/SOL"
|
|
- Current price: "$0.0001"
|
|
- 24h change: "+15%" (xanh nếu dương, đỏ nếu âm)
|
|
- Volume 24h: "$10K"
|
|
- Liquidity: "$5K"
|
|
|
|
---
|
|
|
|
#### AC 1.4.2: Các nút Thao tác Nhanh (Quick Action Buttons)
|
|
**Given** Token Info Card đang hiển thị
|
|
**When** user click nút "Is this token safe?"
|
|
**Then** chat input được điền sẵn "Is BULLA/SOL safe?"
|
|
**And** AI nhận đầy đủ token context
|
|
**And** AI thực hiện phân tích an toàn
|
|
|
|
**Top Holders:**
|
|
**Given** user click nút "Show top holders"
|
|
**When** hành động kích hoạt
|
|
**Then** chat được điền sẵn "Show top holders for BULLA/SOL"
|
|
**And** AI truy vấn dữ liệu blockchain
|
|
|
|
**Dự đoán giá (Price Prediction):**
|
|
**Given** user click nút "Price prediction"
|
|
**Then** chat được điền sẵn "Predict price for BULLA/SOL"
|
|
**And** AI thực hiện phân tích kỹ thuật
|
|
|
|
---
|
|
|
|
#### AC 1.4.3: Tự động Giải quyết Context (Auto-Context Resolution)
|
|
**Given** user gõ "Is this token safe?" (không chỉ định token)
|
|
**When** tin nhắn được gửi
|
|
**Then** AI giải quyết "this token" = token hiện tại trên DexScreener
|
|
**And** AI thực hiện phân tích trên token chính xác
|
|
|
|
**UI Design:**
|
|
```
|
|
┌─────────────────────────────┐
|
|
│ 🪙 BULLA/SOL │
|
|
│ $0.0001 📈 +15% │
|
|
│ Vol: $10K | Liq: $5K │
|
|
│ [Safety Check] [Holders] │
|
|
└─────────────────────────────┘
|
|
```
|
|
|
|
**Files:**
|
|
- `sidepanel/dexscreener/TokenInfoCard.tsx` ✅
|
|
- `sidepanel/chat/ChatInterface.tsx` (integrate card) ✅
|
|
|
|
---
|
|
|
|
### Story 1.5: Lưu nhanh trang (Quick Capture)
|
|
**[FR-EXT-05]** ✅ **COMPLETED**
|
|
|
|
**Là một** crypto trader,
|
|
**Tôi muốn** lưu trang hiện tại vào không gian tìm kiếm (search space),
|
|
**Để** tôi có thể tham khảo lại sau.
|
|
|
|
**Tiêu chí chấp nhận (Acceptance Criteria - BDD Format):**
|
|
|
|
#### AC 1.5.1: Nút Quick Capture
|
|
**Given** side panel đang mở
|
|
**When** user cuộn trong panel
|
|
**Then** nút "📸 Save Current Page" vẫn hiển thị (sticky footer)
|
|
**And** nút không che khuất nội dung chat
|
|
|
|
---
|
|
|
|
#### AC 1.5.2: Quy trình Lưu Trang
|
|
**Given** user đang xem trang token DexScreener
|
|
**When** user click nút "📸 Save Current Page"
|
|
**Then** extension capture nội dung trang (HTML, metadata, screenshot)
|
|
**And** nội dung được lưu vào search space đã chọn của user
|
|
**And** thông báo toast hiển thị "Page saved successfully"
|
|
**And** trang đã lưu có thể truy cập từ web dashboard
|
|
|
|
**Kịch bản lỗi (Error Scenario):**
|
|
**Given** user chưa đăng nhập
|
|
**When** user click nút capture
|
|
**Then** extension hiển thị "Please login to save pages"
|
|
**And** login modal mở ra
|
|
|
|
---
|
|
|
|
#### AC 1.5.3: Tái sử dụng Chức năng Capture
|
|
**Given** web dashboard có API capture hiện có
|
|
**When** extension gọi capture
|
|
**Then** extension tái sử dụng endpoint `/api/capture`
|
|
**And** cùng một logic backend xử lý capture
|
|
**And** không có implementation trùng lặp
|
|
|
|
**Files:**
|
|
- `sidepanel/chat/QuickCapture.tsx` ✅
|
|
|
|
---
|
|
|
|
### Story 1.6: Đồng bộ Cài đặt (Settings Sync) với Frontend
|
|
**[FR-EXT-06]** ⏳ **PENDING** - Backend APIs chưa sẵn sàng
|
|
|
|
**Là một** SurfSense user,
|
|
**Tôi muốn** extension sử dụng cùng model và search space như web dashboard,
|
|
**Để** tôi không phải cấu hình lại.
|
|
|
|
**Tiêu chí chấp nhận (Acceptance Criteria - BDD Format):**
|
|
|
|
#### AC 1.6.1: Hiển thị Dropdown Cài đặt
|
|
**Given** user đã đăng nhập
|
|
**When** user click icon ⚙️ trong header
|
|
**Then** settings dropdown mở ra
|
|
**And** dropdown hiển thị:
|
|
- Current model: "GPT-4 Turbo" (chỉ xem, bị mờ)
|
|
- Current search space: "Crypto Research" (chỉ xem, bị mờ)
|
|
- Links đến web dashboard:
|
|
- "🔗 Manage Connectors"
|
|
- "💬 View All Chats"
|
|
- "⚙️ Full Settings"
|
|
- Nút "🚪 Logout"
|
|
|
|
---
|
|
|
|
#### AC 1.6.2: Đồng bộ Cài đặt khi Đăng nhập
|
|
**Given** user hoàn tất đăng nhập
|
|
**When** nhận được JWT token
|
|
**Then** extension gọi `GET /api/settings`
|
|
**And** backend trả về:
|
|
```json
|
|
{
|
|
"model": "gpt-4-turbo",
|
|
"searchSpace": "crypto-research",
|
|
"connectors": ["dexscreener", "helius"]
|
|
}
|
|
```
|
|
**And** settings được lưu trong Plasmo Storage
|
|
**And** settings hiển thị trong dropdown
|
|
|
|
---
|
|
|
|
#### AC 1.6.3: Tự động cập nhật Cài đặt
|
|
**Given** user thay đổi model trên web dashboard
|
|
**When** extension phát hiện thay đổi (qua polling hoặc webhook)
|
|
**Then** extension lấy settings đã cập nhật
|
|
**And** dropdown phản ánh model mới
|
|
**And** các cuộc chat tiếp theo sử dụng model mới
|
|
|
|
**Polling:**
|
|
**Given** extension đang hoạt động
|
|
**When** mỗi 5 phút
|
|
**Then** extension polls `GET /api/settings`
|
|
|
|
---
|
|
|
|
## 🎉 Tính năng Mới - Hệ thống Phát hiện Token Hybrid (Hybrid Token Detection System)
|
|
|
|
**Ngày triển khai:** 2026-02-04
|
|
**Trạng thái:** ✅ ĐÃ HOÀN THÀNH
|
|
**Commits:** `cb879fca`, `e89824db`, `9790edfe`, `bf9f607c`, `25ed152e`
|
|
|
|
### Tổng quan
|
|
|
|
Hệ thống phát hiện token hybrid kết hợp **tìm kiếm thủ công** và **phát hiện tự động** để làm cho extension hoạt động trên **bất kỳ trang web nào**, không chỉ DexScreener.
|
|
|
|
**Giá trị cho User:**
|
|
- 🔍 **Tìm kiếm token phổ quát** - Tìm kiếm bất kỳ token nào từ bất kỳ trang web nào
|
|
- 🤖 **Phát hiện tự động** - Tự động phát hiện token trên Twitter, DexScreener, v.v.
|
|
- ⚡ **Truy cập nhanh** - Nút floating để phân tích nhanh
|
|
- 🎯 **Nhận biết ngữ cảnh** - Hiểu token nào bạn đang xem
|
|
|
|
---
|
|
|
|
### Story 1.7: Thanh Tìm kiếm Token Phổ quát (Universal Token Search Bar)
|
|
**[FR-EXT-07]** ✅ **COMPLETED**
|
|
|
|
**Là một** crypto trader,
|
|
**Tôi muốn** tìm kiếm bất kỳ token nào từ bất kỳ trang web nào,
|
|
**Để** tôi không cần điều hướng đến DexScreener trước.
|
|
|
|
**Tiêu chí chấp nhận (Acceptance Criteria):**
|
|
|
|
#### AC 1.7.1: Thanh Tìm kiếm trong Header
|
|
✅ **Given** side panel đang mở trên bất kỳ trang web nào
|
|
✅ **When** user nhìn vào header
|
|
✅ **Then** thanh tìm kiếm hiển thị với placeholder "Search token (symbol, name, or address)..."
|
|
✅ **And** thanh tìm kiếm có icon tìm kiếm (🔍) bên trái
|
|
✅ **And** nút xóa (X) xuất hiện khi user gõ
|
|
|
|
#### AC 1.7.2: Tìm kiếm theo Symbol
|
|
✅ **Given** user gõ "BONK" vào thanh tìm kiếm
|
|
✅ **When** user nhấn Enter
|
|
✅ **Then** tin nhắn user "Analyze BONK" được thêm vào chat
|
|
✅ **And** widget phân tích token hiển thị với dữ liệu BONK
|
|
✅ **And** thanh tìm kiếm được xóa
|
|
|
|
#### AC 1.7.3: Tìm kiếm theo Contract Address
|
|
✅ **Given** user gõ địa chỉ Solana hoặc Ethereum
|
|
✅ **When** user nhấn Enter
|
|
✅ **Then** widget phân tích token hiển thị cho địa chỉ đó
|
|
✅ **And** chain được tự động phát hiện (Solana vs Ethereum)
|
|
|
|
**Triển khai kỹ thuật:**
|
|
```typescript
|
|
// sidepanel/chat/ChatHeader.tsx
|
|
const [searchQuery, setSearchQuery] = useState("");
|
|
|
|
const handleSearch = (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
if (searchQuery.trim() && onTokenSearch) {
|
|
onTokenSearch(searchQuery.trim());
|
|
}
|
|
};
|
|
|
|
// sidepanel/chat/ChatInterface.tsx
|
|
const handleTokenSearch = async (query: string) => {
|
|
const userMessage: Message = {
|
|
id: Date.now().toString(),
|
|
role: "user",
|
|
content: `Analyze ${query}`,
|
|
timestamp: new Date(),
|
|
};
|
|
setMessages((prev) => [...prev, userMessage]);
|
|
|
|
// Display token analysis widget
|
|
const aiMessage: Message = {
|
|
id: (Date.now() + 1).toString(),
|
|
role: "assistant",
|
|
content: `Searching for token: ${query}...`,
|
|
timestamp: new Date(),
|
|
widget: {
|
|
type: "token_analysis",
|
|
data: { /* token data */ },
|
|
},
|
|
};
|
|
setMessages((prev) => [...prev, aiMessage]);
|
|
};
|
|
```
|
|
|
|
**Files:**
|
|
- ✅ `sidepanel/chat/ChatHeader.tsx` - Thêm thanh tìm kiếm UI
|
|
- ✅ `sidepanel/chat/ChatInterface.tsx` - Thêm handler `handleTokenSearch`
|
|
|
|
---
|
|
|
|
### Story 1.8: Phát hiện Token Đa Trang (Multi-Page Token Detection)
|
|
**[FR-EXT-08]** ✅ **COMPLETED**
|
|
|
|
**Là một** crypto trader đang browse Twitter hoặc các trang crypto,
|
|
**Tôi muốn** extension tự động phát hiện token được đề cập,
|
|
**Để** tôi có thể phân tích chúng nhanh chóng mà không cần copy/paste.
|
|
|
|
**Tiêu chí chấp nhận (Acceptance Criteria):**
|
|
|
|
#### AC 1.8.1: Phát hiện $TOKEN trên Twitter
|
|
✅ **Given** user đang xem Twitter với tweet chứa "$BONK"
|
|
✅ **When** side panel mở ra
|
|
✅ **Then** "Detected Tokens (1 found)" hiển thị phía trên chat
|
|
✅ **And** BONK được liệt kê với icon chain Solana
|
|
✅ **And** user có thể click BONK để phân tích
|
|
|
|
**Regex Pattern:** `/\$([A-Z]{2,10})\b/g`
|
|
|
|
**Ví dụ phát hiện:**
|
|
- `$BONK` → Phát hiện "BONK"
|
|
- `$SOL` → Phát hiện "SOL"
|
|
- `$PEPE` → Phát hiện "PEPE"
|
|
|
|
#### AC 1.8.2: Phát hiện Contract Addresses
|
|
✅ **Given** user đang xem trang có địa chỉ Solana hoặc Ethereum
|
|
✅ **When** content script quét trang
|
|
✅ **Then** địa chỉ hợp lệ được phát hiện và hiển thị
|
|
✅ **And** tối đa 5 địa chỉ được hiển thị để tránh spam
|
|
|
|
**Solana Pattern:** `/\b([1-9A-HJ-NP-Za-km-z]{32,44})\b/g`
|
|
**Ethereum Pattern:** `/\b(0x[a-fA-F0-9]{40})\b/g`
|
|
|
|
**Validation:**
|
|
- Độ dài: 32-44 ký tự (Solana) hoặc 42 ký tự (Ethereum)
|
|
- Đa dạng ký tự: >10 ký tự duy nhất (Solana)
|
|
- Loại trừ: Chuỗi toàn ký tự giống nhau
|
|
|
|
#### AC 1.8.3: Phát hiện Trading Pairs
|
|
✅ **Given** user đang xem trang có cặp giao dịch như "BONK/SOL"
|
|
✅ **When** content script quét trang
|
|
✅ **Then** cặp giao dịch được phát hiện
|
|
✅ **And** tối đa 3 cặp được hiển thị
|
|
|
|
**Pattern:** `/\b([A-Z]{2,10})\/([A-Z]{2,10})\b/g`
|
|
|
|
**Ví dụ:**
|
|
- `BONK/SOL` → Phát hiện cặp BONK/SOL
|
|
- `PEPE/USDT` → Phát hiện cặp PEPE/USDT
|
|
- `ETH/USDC` → Phát hiện cặp ETH/USDC
|
|
|
|
#### AC 1.8.4: UI Component - DetectedTokensList
|
|
✅ **Given** tokens được phát hiện trên trang
|
|
✅ **When** side panel mở ra
|
|
✅ **Then** component DetectedTokensList hiển thị phía trên chat
|
|
✅ **And** hiển thị tối đa 5 tokens với icon chain
|
|
✅ **And** hiển thị tổng số tokens tìm thấy
|
|
✅ **And** click vào token sẽ kích hoạt phân tích
|
|
|
|
**UI Design:**
|
|
```
|
|
┌─────────────────────────────────┐
|
|
│ 🪙 Detected Tokens (3 found) │
|
|
├─────────────────────────────────┤
|
|
│ ◎ BONK → │
|
|
│ ◎ 7xKXtg2...sgAsU → │
|
|
│ ◎ SOL/USDC → │
|
|
└─────────────────────────────────┘
|
|
```
|
|
|
|
**Triển khai kỹ thuật:**
|
|
```typescript
|
|
// content.ts - Detection functions
|
|
function extractTwitterTokens(): TokenData[] {
|
|
const tokenPattern = /\$([A-Z]{2,10})\b/g;
|
|
const matches = document.body.innerText.matchAll(tokenPattern);
|
|
const uniqueTokens = new Set<string>();
|
|
|
|
for (const match of matches) {
|
|
const symbol = match[1];
|
|
if (!uniqueTokens.has(symbol)) {
|
|
uniqueTokens.add(symbol);
|
|
tokens.push({
|
|
chain: "solana",
|
|
pairAddress: "",
|
|
tokenSymbol: symbol,
|
|
});
|
|
}
|
|
}
|
|
return tokens;
|
|
}
|
|
|
|
function extractContractAddresses(): TokenData[] {
|
|
const solanaPattern = /\b([1-9A-HJ-NP-Za-km-z]{32,44})\b/g;
|
|
const ethPattern = /\b(0x[a-fA-F0-9]{40})\b/g;
|
|
// Extract and validate addresses
|
|
// Return max 5 addresses
|
|
}
|
|
|
|
function extractTradingPairs(): TokenData[] {
|
|
const pairPattern = /\b([A-Z]{2,10})\/([A-Z]{2,10})\b/g;
|
|
// Extract trading pairs
|
|
// Return max 3 pairs
|
|
}
|
|
|
|
// Updated context extraction
|
|
function extractPageContext(): PageContext {
|
|
const pageType = detectPageType(url);
|
|
|
|
if (pageType === "twitter") {
|
|
const twitterTokens = extractTwitterTokens();
|
|
const contractAddresses = extractContractAddresses();
|
|
const tradingPairs = extractTradingPairs();
|
|
context.detectedTokens = [...twitterTokens, ...contractAddresses, ...tradingPairs];
|
|
}
|
|
|
|
return context;
|
|
}
|
|
```
|
|
|
|
**Files:**
|
|
- ✅ `content.ts` - Thêm 3 hàm phát hiện mới
|
|
- ✅ `sidepanel/context/PageContextProvider.tsx` - Thêm field `detectedTokens`
|
|
- ✅ `sidepanel/components/DetectedTokensList.tsx` - Component UI mới
|
|
- ✅ `sidepanel/chat/ChatInterface.tsx` - Tích hợp DetectedTokensList
|
|
|
|
---
|
|
|
|
### Story 1.9: Nút Floating Quick Action (Floating Quick Action Button)
|
|
**[FR-EXT-09]** ✅ **COMPLETED**
|
|
|
|
**Là một** crypto trader,
|
|
**Tôi muốn** nút floating xuất hiện trên các trang crypto,
|
|
**Để** tôi có thể truy cập nhanh phân tích token mà không cần mở side panel đầy đủ.
|
|
|
|
**Tiêu chí chấp nhận (Acceptance Criteria):**
|
|
|
|
#### AC 1.9.1: Nút Floating xuất hiện trên Crypto Pages
|
|
✅ **Given** user điều hướng đến DexScreener, Twitter, CoinGecko, hoặc CoinMarketCap
|
|
✅ **When** trang tải xong
|
|
✅ **Then** nút floating tròn màu tím xuất hiện ở góc dưới bên phải
|
|
✅ **And** nút có kích thước 56x56px
|
|
✅ **And** nút có gradient tím (#667eea → #764ba2)
|
|
✅ **And** nút có icon Sparkles (✨)
|
|
|
|
#### AC 1.9.2: Quick Popup khi Click
|
|
✅ **Given** nút floating đang hiển thị
|
|
✅ **When** user click nút
|
|
✅ **Then** popup nhanh xuất hiện (320px width)
|
|
✅ **And** popup hiển thị:
|
|
- Token symbol & name
|
|
- Giá hiện tại (lớn, nổi bật)
|
|
- Thay đổi 24h (màu xanh/đỏ)
|
|
- Chain info
|
|
- Nút "Full Analysis"
|
|
|
|
#### AC 1.9.3: Mở Side Panel từ Popup
|
|
✅ **Given** popup đang hiển thị
|
|
✅ **When** user click nút "Full Analysis"
|
|
✅ **Then** side panel mở ra với phân tích token đầy đủ
|
|
✅ **And** popup đóng lại
|
|
✅ **And** token context được truyền đến side panel
|
|
|
|
#### AC 1.9.4: Animations & Interactions
|
|
✅ **Given** nút floating đang hiển thị
|
|
✅ **When** user hover vào nút
|
|
✅ **Then** nút scale lên 1.1x
|
|
✅ **And** shadow tăng lên (elevated effect)
|
|
✅ **And** transition mượt mà (300ms)
|
|
|
|
**Supported Pages:**
|
|
- ✅ `*.dexscreener.com/*`
|
|
- ✅ `*.twitter.com/*` và `*.x.com/*`
|
|
- ✅ `*.coingecko.com/*`
|
|
- ✅ `*.coinmarketcap.com/*`
|
|
|
|
**UI Design:**
|
|
```
|
|
Floating Button (Closed):
|
|
┌──────────┐
|
|
│ ✨ │ 56x56px circle
|
|
│ │ Purple gradient
|
|
└──────────┘
|
|
|
|
Quick Popup (Open):
|
|
┌─────────────────────────┐
|
|
│ BONK │
|
|
│ Bonk │
|
|
│ │
|
|
│ $0.00001234 │ ← Large price
|
|
│ +156.7% (24h) │ ← Green/red
|
|
│ │
|
|
│ Chain: Solana │
|
|
│ │
|
|
│ [Full Analysis] │ ← Button
|
|
└─────────────────────────┘
|
|
```
|
|
|
|
**Triển khai kỹ thuật:**
|
|
```typescript
|
|
// contents/floating-button.tsx - Plasmo Content Script UI
|
|
export const config: PlasmoCSConfig = {
|
|
matches: [
|
|
"*://dexscreener.com/*",
|
|
"*://twitter.com/*",
|
|
"*://x.com/*",
|
|
"*://coingecko.com/*",
|
|
"*://coinmarketcap.com/*",
|
|
],
|
|
};
|
|
|
|
function FloatingButton() {
|
|
const [isOpen, setIsOpen] = useState(false);
|
|
const [tokenInfo, setTokenInfo] = useState<TokenQuickInfo | null>(null);
|
|
|
|
const handleOpenSidepanel = () => {
|
|
chrome.runtime.sendMessage({ type: "OPEN_SIDEPANEL" });
|
|
setIsOpen(false);
|
|
};
|
|
|
|
return (
|
|
<>
|
|
{/* Floating Button - 56x56px circle */}
|
|
<button
|
|
onClick={handleButtonClick}
|
|
style={{
|
|
width: "56px",
|
|
height: "56px",
|
|
borderRadius: "50%",
|
|
background: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
|
|
position: "fixed",
|
|
bottom: "24px",
|
|
right: "24px",
|
|
zIndex: 999999,
|
|
// ... more inline styles
|
|
}}
|
|
>
|
|
{isOpen ? <X size={24} /> : <Sparkles size={24} />}
|
|
</button>
|
|
|
|
{/* Quick Info Popup */}
|
|
{isOpen && (
|
|
<div id="surfsense-floating-popup">
|
|
{/* Token info display */}
|
|
<button onClick={handleOpenSidepanel}>
|
|
Full Analysis
|
|
</button>
|
|
</div>
|
|
)}
|
|
</>
|
|
);
|
|
}
|
|
|
|
// background/index.ts - Message handler
|
|
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
if (message.type === "OPEN_SIDEPANEL") {
|
|
if (sender.tab?.id) {
|
|
chrome.sidePanel.open({ tabId: sender.tab.id })
|
|
.catch((error) => console.error("Failed to open side panel:", error));
|
|
}
|
|
}
|
|
});
|
|
```
|
|
|
|
**Files:**
|
|
- ✅ `contents/floating-button.tsx` - Component UI mới (Plasmo Content Script)
|
|
- ✅ `background/index.ts` - Thêm message handler cho OPEN_SIDEPANEL
|
|
|
|
---
|
|
|
|
### Tài liệu Tham khảo (Documentation)
|
|
|
|
**Tài liệu chi tiết:**
|
|
- ✅ `_bmad-epics/NEW-FEATURES-DOCUMENTATION.md` - Tổng quan tính năng
|
|
- ✅ `_bmad-epics/HYBRID-TOKEN-DETECTION-SYSTEM.md` - Kiến trúc kỹ thuật
|
|
- ✅ `_bmad-epics/IMPLEMENTATION-SUMMARY.md` - Tóm tắt triển khai
|
|
|
|
**Commits:**
|
|
- `cb879fca` - Universal Token Search Bar
|
|
- `e89824db` - Multi-Page Token Detection
|
|
- `9790edfe` - Floating Quick Action Button
|
|
- `bf9f607c` - Documentation
|
|
- `25ed152e` - Implementation Summary
|
|
|
|
**Metrics:**
|
|
- Files created: 3
|
|
- Files modified: 6
|
|
- Lines added: ~1,200
|
|
- Components created: 2 (DetectedTokensList, FloatingButton)
|
|
- Detection patterns: 4 types
|
|
|
|
---
|
|
|
|
### Tác động & Giá trị (Impact & Value)
|
|
|
|
**User Benefits:**
|
|
- 🚀 **10x faster workflow** - Không cần điều hướng đến DexScreener
|
|
- 🎯 **Context-aware** - Tự động phát hiện token trên bất kỳ trang nào
|
|
- ⚡ **Quick access** - Nút floating để phân tích tức thì
|
|
- 🔍 **Universal search** - Tìm bất kỳ token nào từ bất kỳ đâu
|
|
|
|
**Business Value:**
|
|
- 📈 **Increased engagement** - Users ở lại extension lâu hơn
|
|
- 💎 **Competitive advantage** - Hệ thống phát hiện hybrid độc đáo
|
|
- 🎨 **Better UX** - Trải nghiệm liền mạch, không xâm phạm
|
|
- 🚀 **Scalable** - Dễ dàng thêm nhiều pattern phát hiện hơn
|
|
|
|
---
|
|
|
|
### Các bước tiếp theo (Next Steps)
|
|
|
|
**Task 1.10: Token Search API Integration (Pending)**
|
|
- [ ] Tạo DexScreener API service
|
|
- [ ] Triển khai tìm kiếm token theo symbol/address
|
|
- [ ] Hỗ trợ tìm kiếm đa chain
|
|
- [ ] Thêm caching layer
|
|
- [ ] Xử lý lỗi API một cách graceful
|
|
- [ ] Cập nhật UI với dữ liệu thực
|
|
|
|
**Files cần sửa đổi:**
|
|
- `lib/services/dexscreener-api.ts` (new)
|
|
- `sidepanel/chat/ChatInterface.tsx`
|
|
- `contents/floating-button.tsx`
|
|
|
|
---
|
|
|
|
## 📋 Backlog: Extension vs Web Sync
|
|
|
|
### Extension Widgets Còn Thiếu (cần thêm để sync với Web)
|
|
|
|
| Widget | Web Component | Priority | Mô tả |
|
|
|--------|---------------|----------|-------|
|
|
| `TrendingTokensWidget` | `TrendingTokensToolUI` | P1 | Hiển thị tokens đang trending |
|
|
| `HolderAnalysisWidget` | `HolderAnalysisToolUI` | P1 | Phân tích holder distribution |
|
|
| `MarketOverviewWidget` | `MarketOverviewToolUI` | P2 | Tổng quan thị trường (BTC, ETH, SOL) |
|
|
| `LiveTokenPriceWidget` | `LiveTokenPriceToolUI` | P1 | Giá token real-time |
|
|
| `LiveTokenDataWidget` | `LiveTokenDataToolUI` | P1 | Dữ liệu token chi tiết real-time |
|
|
| `UserProfileWidget` | `UserProfileToolUI` | P2 | Hiển thị profile đầu tư của user |
|
|
|
|
**Files cần tạo:**
|
|
- `sidepanel/widgets/TrendingTokensWidget.tsx` (new)
|
|
- `sidepanel/widgets/HolderAnalysisWidget.tsx` (new)
|
|
- `sidepanel/widgets/MarketOverviewWidget.tsx` (new)
|
|
- `sidepanel/widgets/LiveTokenPriceWidget.tsx` (new)
|
|
- `sidepanel/widgets/LiveTokenDataWidget.tsx` (new)
|
|
- `sidepanel/widgets/UserProfileWidget.tsx` (new)
|
|
|
|
### Web Tool UIs Còn Thiếu (cần thêm để sync với Extension)
|
|
|
|
| Tool UI | Extension Component | Priority | Mô tả |
|
|
|---------|---------------------|----------|-------|
|
|
| `TradingSuggestionToolUI` | `TradingSuggestionWidget` | P1 | Gợi ý entry/exit points |
|
|
|
|
**Files cần tạo:**
|
|
- `surfsense_web/components/tool-ui/crypto/trading-suggestion.tsx` (new)
|
|
|
|
---
|
|
|
|
## 🔴 Critical Blockers
|
|
|
|
### 1. Backend Authentication (Story 1.0)
|
|
- **Status:** ❌ Chưa bắt đầu
|
|
- **Impact:** Blocks tất cả sync features
|
|
- **Required:**
|
|
- JWT token management
|
|
- OAuth (Google) integration
|
|
- Chrome Identity API wrapper
|
|
- User session management
|
|
|
|
### 2. DexScreener API Integration
|
|
- **Status:** ❌ Chưa bắt đầu
|
|
- **Impact:** Tất cả token data đang là mock
|
|
- **Required:**
|
|
- API service với rate limiting
|
|
- Caching layer (30s TTL)
|
|
- Error handling với retry logic
|
|
- Real-time price updates
|
|
|
|
### 3. Backend APIs
|
|
- **Status:** ❌ Chưa bắt đầu
|
|
- **Impact:** Extension không thể sync với Web
|
|
- **Required:**
|
|
- `/api/settings` - Settings sync
|
|
- `/api/chat/messages` - Chat history sync
|
|
- `/api/capture` - Page capture
|
|
- `/api/watchlist` - Watchlist sync
|
|
- `/api/alerts` - Alerts sync
|