From 8bc092e40e073b32477e9885e3e50f688a2eed48 Mon Sep 17 00:00:00 2001 From: API Test Bot Date: Wed, 4 Feb 2026 13:11:39 +0700 Subject: [PATCH] feat: add new widgets for holder analysis, live token data, price, market overview, and trending tokens - 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. --- _bmad-epics/README.md | 280 ++++++++++++------ .../epic-1-ai-powered-crypto-assistant.md | 64 ++++ _bmad-epics/epic-2-smart-monitoring-alerts.md | 13 +- _bmad-epics/epic-3-trading-intelligence.md | 14 +- .../epic-4-content-creation-productivity.md | 18 +- _bmad-output/planning-artifacts/prd.md | 33 ++- .../background/index.ts | 153 ++++++++++ surfsense_browser_extension/package.json | 35 ++- .../sidepanel/chat/ChatInterface.tsx | 34 ++- .../sidepanel/hooks/index.ts | 4 + .../sidepanel/hooks/useContextAction.ts | 104 +++++++ .../sidepanel/hooks/useKeyboardShortcuts.ts | 92 ++++++ .../settings/NotificationSettingsPanel.tsx | 225 ++++++++++++++ .../sidepanel/settings/NotificationsList.tsx | 170 +++++++++++ .../sidepanel/settings/index.ts | 6 + .../widgets/HolderAnalysisWidget.tsx | 145 +++++++++ .../sidepanel/widgets/LiveTokenDataWidget.tsx | 218 ++++++++++++++ .../widgets/LiveTokenPriceWidget.tsx | 155 ++++++++++ .../widgets/MarketOverviewWidget.tsx | 136 +++++++++ .../widgets/TrendingTokensWidget.tsx | 133 +++++++++ .../sidepanel/widgets/index.ts | 7 + .../components/tool-ui/crypto/index.ts | 9 + .../tool-ui/crypto/trading-suggestion.tsx | 236 +++++++++++++++ 23 files changed, 2173 insertions(+), 111 deletions(-) create mode 100644 surfsense_browser_extension/sidepanel/hooks/index.ts create mode 100644 surfsense_browser_extension/sidepanel/hooks/useContextAction.ts create mode 100644 surfsense_browser_extension/sidepanel/hooks/useKeyboardShortcuts.ts create mode 100644 surfsense_browser_extension/sidepanel/settings/NotificationSettingsPanel.tsx create mode 100644 surfsense_browser_extension/sidepanel/settings/NotificationsList.tsx create mode 100644 surfsense_browser_extension/sidepanel/settings/index.ts create mode 100644 surfsense_browser_extension/sidepanel/widgets/HolderAnalysisWidget.tsx create mode 100644 surfsense_browser_extension/sidepanel/widgets/LiveTokenDataWidget.tsx create mode 100644 surfsense_browser_extension/sidepanel/widgets/LiveTokenPriceWidget.tsx create mode 100644 surfsense_browser_extension/sidepanel/widgets/MarketOverviewWidget.tsx create mode 100644 surfsense_browser_extension/sidepanel/widgets/TrendingTokensWidget.tsx create mode 100644 surfsense_web/components/tool-ui/crypto/trading-suggestion.tsx diff --git a/_bmad-epics/README.md b/_bmad-epics/README.md index 4f9058840..20f1fd92c 100644 --- a/_bmad-epics/README.md +++ b/_bmad-epics/README.md @@ -1,8 +1,18 @@ # SurfSense 2.0 - Epics & Stories -**Project:** SurfSense Crypto Co-Pilot -**Created:** 2026-02-01 -**Status:** Planning Complete +**Project:** SurfSense Crypto Co-Pilot +**Created:** 2026-02-01 +**Updated:** 2026-02-04 +**Status:** 🚧 IN DEVELOPMENT + +--- + +## Strategy Update (2026-02-04) + +> **Extension = Full Features** (không chỉ Quick Actions) +> +> Extension là **full-featured crypto co-pilot** với đầy đủ tính năng phân tích, monitoring, và trading intelligence. +> Web Dashboard tập trung vào settings management và detailed analytics. --- @@ -14,98 +24,109 @@ Tài liệu này tổ chức tất cả epics và user stories cho SurfSense 2.0 ## Epic Summary -| Epic | Phase | Stories | Status | Priority | Duration | -|------|-------|---------|--------|----------|----------| -| [Epic 1: Extension Core Infrastructure](./epic-1-extension-core-infrastructure.md) | Phase 1 | 6 | ✅ COMPLETED | P0 | 2 weeks | -| [Epic 2: Smart Monitoring & Alerts](./epic-2-smart-monitoring-alerts.md) | Phase 2 | 3 | 📋 PLANNED | P0 | 2 weeks | -| [Epic 3: Trading Intelligence](./epic-3-trading-intelligence.md) | Phase 3 | 3 | 📋 PLANNED | P1 | 2 weeks | -| [Epic 4: Content Creation & Productivity](./epic-4-content-creation-productivity.md) | Phase 4 | 3 | 📋 PLANNED | P2 | 2 weeks | +| Epic | Phase | Stories | Status | Frontend | Backend | Priority | +|------|-------|---------|--------|----------|---------|----------| +| [Epic 1: AI-Powered Crypto Assistant](./epic-1-ai-powered-crypto-assistant.md) | Phase 1 | 10 | 🚧 IN PROGRESS | 100% ✅ | 0% ❌ | P0 | +| [Epic 2: Smart Monitoring & Alerts](./epic-2-smart-monitoring-alerts.md) | Phase 2 | 3 | 🚧 IN PROGRESS | 100% ✅ | 0% ❌ | P0 | +| [Epic 3: Trading Intelligence](./epic-3-trading-intelligence.md) | Phase 3 | 3 | 🚧 IN PROGRESS | 100% ✅ | 0% ❌ | P1 | +| [Epic 4: Content Creation & Productivity](./epic-4-content-creation-productivity.md) | Phase 4 | 5 | 🚧 IN PROGRESS | 100% ✅ | 0% ❌ | P2 | -**Total:** 4 epics, 15 user stories, 8 weeks +**Total:** 4 epics, 21 user stories + +**🔴 BLOCKER:** Backend APIs chưa được implement (Authentication, Settings, Chat sync, Data APIs) --- -## Phase 1: Extension Core Infrastructure ✅ +## Phase 1: AI-Powered Crypto Assistant 🚧 **Epic 1** - Foundation cho tất cả features ### Stories: -1. **Story 1.1:** Side Panel Architecture (FR-EXT-01) -2. **Story 1.2:** AI Chat Interface Integration (FR-EXT-02) -3. **Story 1.3:** Page Context Detection (FR-EXT-03) -4. **Story 1.4:** DexScreener Smart Integration (FR-EXT-04) -5. **Story 1.5:** Quick Capture (FR-EXT-05) -6. **Story 1.6:** Settings Sync với Frontend (FR-EXT-06) +1. **Story 1.0:** Authentication System (FR-EXT-00) - ⏳ BACKEND BLOCKER +2. **Story 1.1:** Side Panel Architecture (FR-EXT-01) - ✅ DONE +3. **Story 1.2:** AI Chat Interface Integration (FR-EXT-02) - ✅ DONE +4. **Story 1.3:** Page Context Detection (FR-EXT-03) - ✅ DONE +5. **Story 1.4:** DexScreener Smart Integration (FR-EXT-04) - ✅ DONE +6. **Story 1.5:** Quick Capture (FR-EXT-05) - ✅ DONE +7. **Story 1.6:** Settings Sync với Frontend (FR-EXT-06) - ⏳ BACKEND PENDING +8. **Story 1.7:** Universal Token Search Bar (FR-EXT-07) - ✅ DONE (NEW) +9. **Story 1.8:** Multi-Page Token Detection (FR-EXT-08) - ✅ DONE (NEW) +10. **Story 1.9:** Floating Quick Action Button (FR-EXT-09) - ✅ DONE (NEW) **Key Deliverables:** - ✅ Chrome Side Panel working - ✅ AI chat interface integrated -- ✅ Context detection for DexScreener +- ✅ Context detection for DexScreener, Twitter, etc. - ✅ Token info card - ✅ Quick capture button -- ✅ Settings sync infrastructure +- ✅ Universal token search +- ✅ Multi-page token detection +- ✅ Floating quick action button +- ⏳ Settings sync (needs backend) +- ❌ Authentication (needs backend) --- -## Phase 2: Smart Monitoring & Alerts 📋 +## Phase 2: Smart Monitoring & Alerts 🚧 **Epic 2** - Risk protection & opportunity alerts ### Stories: -1. **Story 2.1:** Real-time Price Alerts (FR-EXT-07) -2. **Story 2.2:** Whale Activity Tracker (FR-EXT-08) -3. **Story 2.3:** Rug Pull Early Warning System (FR-EXT-09) +1. **Story 2.1:** Real-time Price Alerts (FR-EXT-10) - ✅ UI DONE, ⏳ API PENDING +2. **Story 2.2:** Whale Activity Tracker (FR-EXT-11) - ✅ UI DONE, ⏳ API PENDING +3. **Story 2.3:** Rug Pull Early Warning System (FR-EXT-12) - ✅ UI DONE, ⏳ API PENDING **Key Deliverables:** -- Watchlist management -- Price/volume/liquidity alerts -- Browser notifications -- Whale transaction monitoring -- Rug pull risk assessment -- Risk score display +- ✅ Watchlist management UI (WatchlistPanel, WatchlistWidget) +- ✅ Alert configuration UI (AlertConfigModal, AlertWidget) +- ✅ Whale activity UI (WhaleActivityFeed, WhaleActivityWidget) +- ✅ Safety score display (SafetyScoreDisplay) +- ⏳ Browser notifications (needs backend) +- ⏳ Real-time data (needs DexScreener API) **Business Value:** Risk protection = User trust --- -## Phase 3: Trading Intelligence 📋 +## Phase 3: Trading Intelligence 🚧 **Epic 3** - AI-powered trading insights ### Stories: -1. **Story 3.1:** One-Click Token Analysis (FR-EXT-10) -2. **Story 3.2:** Smart Entry/Exit Suggestions (FR-EXT-11) -3. **Story 3.3:** Portfolio Tracker Integration (FR-EXT-12) +1. **Story 3.1:** One-Click Token Analysis (FR-EXT-13) - ✅ UI DONE, ⏳ API PENDING +2. **Story 3.2:** Smart Entry/Exit Suggestions (FR-EXT-14) - ✅ UI DONE, ⏳ API PENDING +3. **Story 3.3:** Portfolio Tracker Integration (FR-EXT-15) - ✅ UI DONE, ⏳ API PENDING **Key Deliverables:** -- Comprehensive token analysis -- AI-generated summaries -- Entry/exit suggestions -- Risk/reward calculations -- Wallet connection -- Portfolio tracking -- P&L analytics +- ✅ Token analysis UI (TokenAnalysisPanel, TokenAnalysisWidget) +- ✅ Trading suggestions UI (TradingSuggestionPanel, TradingSuggestionWidget) +- ✅ Portfolio UI (PortfolioPanel, PortfolioWidget) +- ⏳ AI-generated summaries (needs backend) +- ⏳ Wallet connection (needs integration) +- ⏳ Real-time P&L (needs backend) **Business Value:** Better decisions = Better results = Happy users --- -## Phase 4: Content Creation & Productivity 📋 +## Phase 4: Content Creation & Productivity ✅ **Epic 4** - Tools for creators & power users ### Stories: -1. **Story 4.1:** Chart Screenshot with Annotations (FR-EXT-13) -2. **Story 4.2:** AI Thread Generator (FR-EXT-14) -3. **Story 4.3:** Quick Actions & Productivity (FR-EXT-15, 16, 17) +1. **Story 4.1:** Chart Screenshot with Annotations (FR-EXT-16) - ✅ UI DONE +2. **Story 4.2:** AI Thread Generator (FR-EXT-17) - ✅ UI DONE, ⏳ AI PENDING +3. **Story 4.3:** Quick Actions Context Menu (FR-EXT-18) - ✅ UI DONE +4. **Story 4.4:** Smart Notifications Management (FR-EXT-19) - ✅ UI DONE +5. **Story 4.5:** Keyboard Shortcuts (FR-EXT-20) - ✅ UI DONE **Key Deliverables:** -- Chart capture & annotation -- Drawing tools -- AI thread generation -- Context menu quick actions -- Smart notifications -- Keyboard shortcuts +- ✅ Chart capture UI (ChartCapturePanel, ChartCaptureWidget) +- ✅ Thread generator UI (ThreadGeneratorPanel, ThreadGeneratorWidget) +- ✅ Context menu quick actions (background/index.ts, useContextAction hook) +- ✅ Smart notifications UI (NotificationSettingsPanel, NotificationsList) +- ✅ Keyboard shortcuts (4 shortcuts: Analyze, Watchlist, Capture, Portfolio) +- ⏳ AI thread generation (needs backend) **Business Value:** Content creation = Viral marketing @@ -113,49 +134,75 @@ Tài liệu này tổ chức tất cả epics và user stories cho SurfSense 2.0 ## Implementation Roadmap -### Week 1-2: Phase 1 ✅ +### Week 1-2: Phase 1 - Core Infrastructure - [x] Side panel architecture - [x] Chat interface -- [x] Context detection +- [x] Context detection (DexScreener, Twitter, etc.) - [x] DexScreener integration - [x] Quick capture -- [x] Settings sync +- [x] Universal token search (NEW) +- [x] Multi-page token detection (NEW) +- [x] Floating quick action button (NEW) +- [ ] Authentication (BLOCKER) +- [ ] Settings sync API -### Week 3-4: Phase 2 (Next) -- [ ] Watchlist & alerts -- [ ] Whale tracker -- [ ] Rug pull detection +### Week 3-4: Phase 2 - Smart Monitoring +- [x] Watchlist UI (WatchlistPanel, WatchlistWidget) +- [x] Alert config UI (AlertConfigModal, AlertWidget) +- [x] Whale activity UI (WhaleActivityFeed, WhaleActivityWidget) +- [x] Safety score UI (SafetyScoreDisplay) +- [ ] DexScreener API integration +- [ ] Real-time price alerts +- [ ] Browser notifications -### Week 5-6: Phase 3 -- [ ] Token analysis -- [ ] Trading suggestions -- [ ] Portfolio tracker +### Week 5-6: Phase 3 - Trading Intelligence +- [x] Token analysis UI (TokenAnalysisPanel, TokenAnalysisWidget) +- [x] Trading suggestions UI (TradingSuggestionPanel, TradingSuggestionWidget) +- [x] Portfolio UI (PortfolioPanel, PortfolioWidget) +- [ ] AI analysis backend +- [ ] Wallet connection +- [ ] Real-time P&L -### Week 7-8: Phase 4 -- [ ] Chart capture -- [ ] Thread generator -- [ ] Productivity features +### Week 7-8: Phase 4 - Content & Productivity +- [x] Chart capture UI (ChartCapturePanel, ChartCaptureWidget) +- [x] Thread generator UI (ThreadGeneratorPanel, ThreadGeneratorWidget) +- [x] Context menu quick actions +- [x] Smart notifications UI +- [x] Keyboard shortcuts (4 shortcuts) +- [ ] AI thread generation backend --- ## Feature Responsibility Matrix -| Feature | Extension | Frontend | Sync | -|---------|-----------|----------|------| +> **Strategy: Extension = Full Features** + +| Feature | Extension | Web Dashboard | Sync | +|---------|-----------|---------------|------| | Model Selection | 📖 Read-only | ✏️ Full control | API | | Search Space | 📖 Read-only | ✏️ Full control | API | | Chat | ✅ Full UI | ✅ Full UI | API | | Connectors | 📖 Use only | ✏️ Setup | API | | Documents | 👁️ View | ✏️ Manage | API | -| Watchlist | ✏️ Add/Remove | ✏️ Full | Storage + API | -| Alerts | ✏️ Basic | ✏️ Full | API | +| Watchlist | ✅ Full | ✅ Full | API | +| Alerts | ✅ Full | ✅ Full | API | +| Token Analysis | ✅ Full | ✅ Full (via chat) | API | +| Whale Activity | ✅ Full | ✅ Full (via chat) | API | +| Trading Suggestions | ✅ Full | ✅ Full (via chat) | API | +| Portfolio | ✅ Full | ✅ Full | API | +| Chart Capture | ✅ Full | ❌ N/A | Local | +| Thread Generator | ✅ Full | ❌ N/A | Local | +| Context Detection | ✅ Full | ❌ N/A | Local | +| Floating Button | ✅ Full | ❌ N/A | Local | | Settings | 📖 Quick | ✏️ Full | API | +| Analytics | 👁️ Basic | ✅ Full | API | **Legend:** - ✅ Full feature - ✏️ Full control - 📖 Read-only - 👁️ View only +- ❌ N/A --- @@ -163,17 +210,22 @@ Tài liệu này tổ chức tất cả epics và user stories cho SurfSense 2.0 ### Frontend (Extension) - **Framework:** Plasmo (React + TypeScript) -- **UI:** @assistant-ui/react, shadcn/ui +- **UI:** @assistant-ui/react, shadcn/ui, Lucide icons - **State:** Plasmo Storage, React Context -- **APIs:** Chrome Extension APIs +- **APIs:** Chrome Extension APIs (sidePanel, storage, tabs, identity) -### Backend +### Frontend (Web) +- **Framework:** Next.js (React + TypeScript) +- **UI:** shadcn/ui, @assistant-ui/react +- **State:** React Context, Server Components + +### Backend (⏳ PENDING) - **Framework:** FastAPI (Python) - **AI:** Gemini 1.5 Flash / GPT-4o-mini - **RAG:** Supabase (pgvector) - **Cache:** Redis -### Data Sources +### Data Sources (⏳ PENDING) - DexScreener API - DefiLlama API - Helius (Solana) @@ -183,43 +235,79 @@ Tài liệu này tổ chức tất cả epics và user stories cho SurfSense 2.0 --- -## Success Metrics +## Current Progress -### Phase 1 (✅ COMPLETED) +### Phase 1 (✅ 100% Frontend) - [x] Extension installable -- [x] Chat works +- [x] Chat works (mock data) - [x] Context detection works - [x] Token card displays +- [x] Universal search works +- [x] Multi-page detection works +- [x] Floating button works +- [ ] Authentication (BACKEND) +- [ ] Real data from APIs (BACKEND) -### Phase 2 (Target) -- [ ] 100+ tokens in watchlists -- [ ] 1000+ alerts set -- [ ] 0 false positive rug pull warnings +### Phase 2 (✅ 100% UI) +- [x] Watchlist UI complete +- [x] Alert config UI complete +- [x] Whale activity UI complete +- [x] Safety score UI complete +- [ ] Real-time data (BACKEND) +- [ ] Browser notifications (BACKEND) -### Phase 3 (Target) -- [ ] 500+ token analyses -- [ ] 80%+ suggestion accuracy -- [ ] 50+ wallets connected +### Phase 3 (✅ 100% UI) +- [x] Token analysis UI complete +- [x] Trading suggestions UI complete +- [x] Portfolio UI complete +- [x] Holder analysis widget +- [x] Market overview widget +- [x] Trending tokens widget +- [x] Live token price/data widgets +- [ ] AI analysis (BACKEND) +- [ ] Wallet connection (BACKEND) -### Phase 4 (Target) -- [ ] 100+ charts captured -- [ ] 50+ threads generated -- [ ] 200+ daily shortcut uses +### Phase 4 (✅ 100% UI) +- [x] Chart capture UI complete +- [x] Thread generator UI complete +- [x] Context menu quick actions +- [x] Smart notifications UI +- [x] Keyboard shortcuts (4 shortcuts) +- [ ] AI thread generation (BACKEND) + +--- + +## 🔴 Critical Blockers + +1. **Backend Authentication (Story 1.0)** + - Cần JWT + OAuth + - Blocks: Settings sync, Chat sync, User data + +2. **DexScreener API Integration** + - Cần real-time token data + - Blocks: All token-related features + +3. **Backend APIs** + - Settings, Chat, Capture endpoints + - Blocks: Extension ↔ Web sync --- ## Next Steps -1. ✅ **Epic 1 Complete** - Phase 1 infrastructure done -2. 🎯 **Start Epic 2** - Begin Phase 2 implementation -3. 📝 **Create Stories** - Break down Epic 2 into individual story files -4. 🚀 **Sprint Planning** - Plan 2-week sprint for Phase 2 +1. 🔴 **PRIORITY: Backend Authentication** - Implement Story 1.0 +2. 🔴 **PRIORITY: DexScreener API** - Replace mock data with real data +3. 🔴 **PRIORITY: Backend APIs** - Settings, Chat, Capture endpoints +4. 🟡 **AI Thread Generation** - Backend for Epic 4.2 +5. 🟡 **Browser Notifications** - Real-time alerts +6. 🟢 **Wallet Connection** - Portfolio integration --- ## Related Documents -- [PRD](../_bmad-output/planning-artifacts/prd.md) -- [Extension UX Strategy](../.gemini/antigravity/brain/02a071c7-57fc-4f43-a2e8-516ac511579a/extension-ux-integration-strategy.md) -- [Extension Technical Spec](../.gemini/antigravity/brain/02a071c7-57fc-4f43-a2e8-516ac511579a/extension-sidepanel-technical-spec.md) -- [Feature Brainstorming](../.gemini/antigravity/brain/02a071c7-57fc-4f43-a2e8-516ac511579a/extension-features-brainstorming.md) +- [PRD v2](../_bmad-output/planning-artifacts/prd.md) +- [Epic 1: AI-Powered Crypto Assistant](./epic-1-ai-powered-crypto-assistant.md) +- [Epic 2: Smart Monitoring & Alerts](./epic-2-smart-monitoring-alerts.md) +- [Epic 3: Trading Intelligence](./epic-3-trading-intelligence.md) +- [Epic 4: Content Creation & Productivity](./epic-4-content-creation-productivity.md) diff --git a/_bmad-epics/epic-1-ai-powered-crypto-assistant.md b/_bmad-epics/epic-1-ai-powered-crypto-assistant.md index d1a963df1..945ba764d 100644 --- a/_bmad-epics/epic-1-ai-powered-crypto-assistant.md +++ b/_bmad-epics/epic-1-ai-powered-crypto-assistant.md @@ -1220,3 +1220,67 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { - `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 diff --git a/_bmad-epics/epic-2-smart-monitoring-alerts.md b/_bmad-epics/epic-2-smart-monitoring-alerts.md index 07888d380..0e08008ef 100644 --- a/_bmad-epics/epic-2-smart-monitoring-alerts.md +++ b/_bmad-epics/epic-2-smart-monitoring-alerts.md @@ -1,10 +1,17 @@ # Epic 2: Smart Monitoring & Alerts (Giám sát & Cảnh báo Thông minh) -**Trạng thái:** 📋 ĐÃ LÊN KẾ HOẠCH (PLANNED) -**Giai đoạn:** Phase 2 -**Thời gian:** 2 tuần +**Trạng thái:** 🚧 ĐANG TRIỂN KHAI (IN PROGRESS) +**Giai đoạn:** Phase 2 +**Thời gian:** 2 tuần **Mức độ ưu tiên:** P0 (Nghiêm trọng - Risk Protection) +**Tiến độ:** +- ✅ Extension UI: 90% hoàn thành (WatchlistPanel, AlertConfigModal, WhaleActivityFeed, SafetyScoreDisplay) +- ✅ Extension Widgets: 100% hoàn thành (WatchlistWidget, AlertWidget, WhaleActivityWidget) +- ✅ Web Tool UIs: 100% hoàn thành (WatchlistDisplayToolUI, AlertConfigurationToolUI, WhaleActivityToolUI) +- ⏳ Backend APIs: 0% (DexScreener integration, real-time data chưa implement) +- ⏳ Browser Notifications: 0% (cần backend) + --- ## Tổng quan Epic diff --git a/_bmad-epics/epic-3-trading-intelligence.md b/_bmad-epics/epic-3-trading-intelligence.md index 02288799e..2e66776a3 100644 --- a/_bmad-epics/epic-3-trading-intelligence.md +++ b/_bmad-epics/epic-3-trading-intelligence.md @@ -1,10 +1,18 @@ # Epic 3: Trading Intelligence (Trí tuệ Giao dịch) -**Trạng thái:** 📋 ĐÃ LÊN KẾ HOẠCH (PLANNED) -**Giai đoạn:** Phase 3 -**Thời gian:** 2 tuần +**Trạng thái:** 🚧 ĐANG TRIỂN KHAI (IN PROGRESS) +**Giai đoạn:** Phase 3 +**Thời gian:** 2 tuần **Mức độ ưu tiên:** P1 (Cao - Giá trị gia tăng) +**Tiến độ:** +- ✅ Extension UI: 80% hoàn thành (TokenAnalysisPanel, TradingSuggestionPanel, PortfolioPanel) +- ✅ Extension Widgets: 100% hoàn thành (TokenAnalysisWidget, TradingSuggestionWidget, PortfolioWidget) +- ✅ Web Tool UIs: 80% hoàn thành (TokenAnalysisToolUI, PortfolioDisplayToolUI) +- ⚠️ Web Tool UIs: TradingSuggestionToolUI chưa có (cần thêm để sync với Extension) +- ⏳ Backend APIs: 0% (AI analysis, wallet connection chưa implement) +- ⏳ Real-time P&L: 0% (cần backend) + --- ## Tổng quan Epic diff --git a/_bmad-epics/epic-4-content-creation-productivity.md b/_bmad-epics/epic-4-content-creation-productivity.md index b5a7c8c78..209b6c571 100644 --- a/_bmad-epics/epic-4-content-creation-productivity.md +++ b/_bmad-epics/epic-4-content-creation-productivity.md @@ -1,10 +1,22 @@ # Epic 4: Content Creation & Productivity (Tạo Nội dung & Hiệu suất) -**Trạng thái:** 📋 ĐÃ LÊN KẾ HOẠCH (PLANNED) -**Giai đoạn:** Phase 4 -**Thời gian:** 2 tuần +**Trạng thái:** 🚧 ĐANG TRIỂN KHAI (IN PROGRESS) +**Giai đoạn:** Phase 4 +**Thời gian:** 2 tuần **Mức độ ưu tiên:** P2 (Trung bình - Nên có (Nice to Have)) +**Tiến độ:** +- ✅ Story 4.1 (Chart Capture): UI hoàn thành (ChartCapturePanel, ChartCaptureWidget, AnnotationTools) +- ✅ Story 4.2 (Thread Generator): UI hoàn thành (ThreadGeneratorPanel, ThreadGeneratorWidget) +- ✅ Story 4.3 (Quick Actions): UI hoàn thành (Context menu in background/index.ts, useContextAction hook) +- ✅ Story 4.4 (Smart Notifications): UI hoàn thành (NotificationSettingsPanel, NotificationsList) +- ✅ Story 4.5 (Keyboard Shortcuts): UI hoàn thành (4 shortcuts: Analyze, Watchlist, Capture, Portfolio) +- ⏳ Backend APIs: 0% (AI thread generation chưa implement) + +**Frontend UI: 100% ✅** + +**Lưu ý:** Đây là Extension-only features (không có trên Web Dashboard) + --- ## Tổng quan Epic diff --git a/_bmad-output/planning-artifacts/prd.md b/_bmad-output/planning-artifacts/prd.md index 5f4384b68..22601ede5 100644 --- a/_bmad-output/planning-artifacts/prd.md +++ b/_bmad-output/planning-artifacts/prd.md @@ -290,6 +290,11 @@ Traders hiện đang đối mặt với **Quá tải Thông tin** và **Quy trì ##### Feature Responsibility Matrix +> **Strategy Update (2026-02-04):** Extension = Full Features +> +> Extension không chỉ là "Quick Actions" mà là **full-featured crypto co-pilot** với đầy đủ tính năng. +> Web Dashboard là nơi quản lý settings và xem analytics chi tiết. + | Feature | Extension | Frontend Dashboard | Sync Method | |---------|-----------|-------------------|-------------| | **Model Selection** | 📖 Read-only dropdown | ✏️ Full selector | Backend API | @@ -297,15 +302,37 @@ Traders hiện đang đối mặt với **Quá tải Thông tin** và **Quy trì | **Chat** | ✅ Full chat UI | ✅ Full chat UI | Backend API | | **Connectors** | 📖 Use only | ✏️ Setup & manage | Backend API | | **Documents** | 👁️ View in chat | ✏️ Full management | Backend API | -| **Watchlist** | ✏️ Add/Remove | ✏️ Full management | Plasmo Storage + API | -| **Alerts** | ✏️ Configure basic | ✏️ Full management | Backend API | +| **Watchlist** | ✅ Full management | ✅ Full management | Backend API | +| **Alerts** | ✅ Full management | ✅ Full management | Backend API | +| **Token Analysis** | ✅ Full analysis | ✅ Full analysis (via AI chat) | Backend API | +| **Whale Activity** | ✅ Full tracking | ✅ Full tracking (via AI chat) | Backend API | +| **Trading Suggestions** | ✅ Full suggestions | ✅ Full suggestions (via AI chat) | Backend API | +| **Portfolio Tracker** | ✅ Full tracking | ✅ Full tracking | Backend API | +| **Chart Capture** | ✅ Full capture + annotations | ❌ N/A (Extension-only) | Local | +| **Thread Generator** | ✅ Full generation | ❌ N/A (Extension-only) | Local | +| **Context Detection** | ✅ Auto-detect tokens | ❌ N/A (Extension-only) | Local | +| **Floating Button** | ✅ Quick access | ❌ N/A (Extension-only) | Local | | **Settings** | 📖 Quick settings | ✏️ Full settings | Backend API | +| **Analytics** | 👁️ Basic stats | ✅ Full analytics | Backend API | **Legend:** -- ✅ Full feature +- ✅ Full feature (create, edit, delete, view) - ✏️ Full control (create, edit, delete) - 📖 Read-only (view/select only) - 👁️ View only +- ❌ N/A (Not applicable) + +**Extension-only Features:** +- Chart Capture with Annotations (cần truy cập DOM của trang) +- AI Thread Generator (tối ưu cho workflow trên browser) +- Context Detection (cần content script) +- Floating Quick Action Button (cần inject vào trang) + +**Web Dashboard Focus:** +- Full Settings Management +- Detailed Analytics & Reports +- Connector Setup & Configuration +- Document Management #### Web Dashboard (Secondary - Existing) * **[FR-UI-01] Chat Management:** Xem lịch sử chat, manage search spaces. diff --git a/surfsense_browser_extension/background/index.ts b/surfsense_browser_extension/background/index.ts index 5b8801a36..6da928f32 100644 --- a/surfsense_browser_extension/background/index.ts +++ b/surfsense_browser_extension/background/index.ts @@ -7,6 +7,122 @@ chrome.sidePanel .setPanelBehavior({ openPanelOnActionClick: true }) .catch((error) => console.error("Failed to set side panel behavior:", error)); +// ============================================ +// Context Menu Setup (Epic 4.3) +// ============================================ + +// Create context menus on extension install +chrome.runtime.onInstalled.addListener(() => { + // Parent menu for SurfSense + chrome.contextMenus.create({ + id: "surfsense-parent", + title: "🧠 SurfSense", + contexts: ["selection", "page", "link"], + }); + + // Analyze Token - for selected text (token address or symbol) + chrome.contextMenus.create({ + id: "analyze-token", + parentId: "surfsense-parent", + title: "🔍 Analyze Token", + contexts: ["selection"], + }); + + // Check Safety - for selected text + chrome.contextMenus.create({ + id: "check-safety", + parentId: "surfsense-parent", + title: "🛡️ Check Safety", + contexts: ["selection"], + }); + + // Add to Watchlist - for selected text + chrome.contextMenus.create({ + id: "add-watchlist", + parentId: "surfsense-parent", + title: "⭐ Add to Watchlist", + contexts: ["selection"], + }); + + // Separator + chrome.contextMenus.create({ + id: "separator-1", + parentId: "surfsense-parent", + type: "separator", + contexts: ["selection", "page", "link"], + }); + + // Copy Address - for selected text + chrome.contextMenus.create({ + id: "copy-address", + parentId: "surfsense-parent", + title: "📋 Copy Address", + contexts: ["selection"], + }); + + // View on Explorer - for selected text + chrome.contextMenus.create({ + id: "view-explorer", + parentId: "surfsense-parent", + title: "🔗 View on Explorer", + contexts: ["selection"], + }); + + // Separator + chrome.contextMenus.create({ + id: "separator-2", + parentId: "surfsense-parent", + type: "separator", + contexts: ["selection", "page", "link"], + }); + + // Capture Page - for page context + chrome.contextMenus.create({ + id: "capture-page", + parentId: "surfsense-parent", + title: "📸 Capture This Page", + contexts: ["page"], + }); + + // Ask AI about this page + chrome.contextMenus.create({ + id: "ask-ai-page", + parentId: "surfsense-parent", + title: "💬 Ask AI About This Page", + contexts: ["page"], + }); +}); + +// Handle context menu clicks +chrome.contextMenus.onClicked.addListener(async (info, tab) => { + const selectedText = info.selectionText?.trim() || ""; + const storage = new Storage({ area: "local" }); + + // Store the action for sidepanel to pick up + const contextAction = { + action: info.menuItemId, + text: selectedText, + pageUrl: info.pageUrl, + linkUrl: info.linkUrl, + timestamp: Date.now(), + }; + + await storage.set("pendingContextAction", contextAction); + + // Open sidepanel to handle the action + if (tab?.id) { + try { + await chrome.sidePanel.open({ tabId: tab.id }); + } catch (error) { + console.error("Failed to open side panel:", error); + } + } +}); + +// ============================================ +// Message Listeners +// ============================================ + // Listen for messages from content scripts chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { if (message.type === "OPEN_SIDEPANEL") { @@ -16,6 +132,43 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { .catch((error) => console.error("Failed to open side panel:", error)); } } + + // Handle context action from sidepanel + if (message.type === "GET_CONTEXT_ACTION") { + const storage = new Storage({ area: "local" }); + storage.get("pendingContextAction").then((action) => { + sendResponse(action); + // Clear the pending action + storage.remove("pendingContextAction"); + }); + return true; // Keep channel open for async response + } +}); + +// ============================================ +// Keyboard Shortcuts (Epic 4.5) +// ============================================ + +chrome.commands.onCommand.addListener(async (command) => { + const storage = new Storage({ area: "local" }); + const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); + + if (!tab?.id) return; + + // Store the keyboard command for sidepanel to pick up + const keyboardAction = { + action: command, + timestamp: Date.now(), + }; + + await storage.set("pendingKeyboardAction", keyboardAction); + + // Open sidepanel for all commands + try { + await chrome.sidePanel.open({ tabId: tab.id }); + } catch (error) { + console.error("Failed to open side panel:", error); + } }); chrome.tabs.onCreated.addListener(async (tab: any) => { diff --git a/surfsense_browser_extension/package.json b/surfsense_browser_extension/package.json index d1f92a641..7d2bb6fa0 100644 --- a/surfsense_browser_extension/package.json +++ b/surfsense_browser_extension/package.json @@ -68,13 +68,44 @@ ], "name": "SurfSense", "description": "Extension to collect Browsing History for SurfSense.", - "version": "0.0.3" + "version": "0.0.3", + "commands": { + "analyze-token": { + "suggested_key": { + "default": "Ctrl+Shift+A", + "mac": "Command+Shift+A" + }, + "description": "Analyze current token" + }, + "add-watchlist": { + "suggested_key": { + "default": "Ctrl+Shift+W", + "mac": "Command+Shift+W" + }, + "description": "Add token to watchlist" + }, + "capture-page": { + "suggested_key": { + "default": "Ctrl+Shift+E", + "mac": "Command+Shift+E" + }, + "description": "Capture current page" + }, + "show-portfolio": { + "suggested_key": { + "default": "Ctrl+Shift+P", + "mac": "Command+Shift+P" + }, + "description": "Show portfolio" + } + } }, "permissions": [ "storage", "scripting", "unlimitedStorage", "activeTab", - "sidePanel" + "sidePanel", + "contextMenus" ] } \ No newline at end of file diff --git a/surfsense_browser_extension/sidepanel/chat/ChatInterface.tsx b/surfsense_browser_extension/sidepanel/chat/ChatInterface.tsx index a58e80254..635299fee 100644 --- a/surfsense_browser_extension/sidepanel/chat/ChatInterface.tsx +++ b/surfsense_browser_extension/sidepanel/chat/ChatInterface.tsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import { useState, useEffect, useCallback } from "react"; import { usePageContext } from "../context/PageContextProvider"; import { TokenInfoCard } from "../dexscreener/TokenInfoCard"; import { QuickCapture } from "./QuickCapture"; @@ -22,6 +22,8 @@ import { SafetyScoreDisplay } from "../crypto/SafetyScoreDisplay"; import { WatchlistPanel } from "../crypto/WatchlistPanel"; import { AlertConfigModal } from "../crypto/AlertConfigModal"; import { DetectedTokensList } from "../components/DetectedTokensList"; +import { useContextAction, getMessageForAction } from "../hooks/useContextAction"; +import { useKeyboardShortcuts, getMessageForKeyboardAction } from "../hooks/useKeyboardShortcuts"; import type { WatchlistItem } from "../widgets"; import type { TokenData } from "../context/PageContextProvider"; @@ -78,9 +80,39 @@ export function ChatInterface() { const [watchlistTokens, setWatchlistTokens] = useState(MOCK_WATCHLIST_TOKENS); const [isInWatchlist, setIsInWatchlist] = useState(false); + // Context menu action hook + const { pendingAction, clearAction } = useContextAction(); + + // Keyboard shortcuts hook + const { pendingAction: pendingKeyboardAction, clearAction: clearKeyboardAction } = useKeyboardShortcuts(); + // Mock user data - in production, this would come from auth context const userName = "Crypto Trader"; + // Handle context menu actions + useEffect(() => { + if (pendingAction) { + const message = getMessageForAction(pendingAction); + if (message) { + // Auto-send the message + handleSendMessage(message); + } + clearAction(); + } + }, [pendingAction, clearAction]); + + // Handle keyboard shortcut actions + useEffect(() => { + if (pendingKeyboardAction) { + const message = getMessageForKeyboardAction(pendingKeyboardAction); + if (message) { + // Auto-send the message + handleSendMessage(message); + } + clearKeyboardAction(); + } + }, [pendingKeyboardAction, clearKeyboardAction]); + const handleSendMessage = async (content: string, attachments?: AttachedFile[]) => { console.log("Sending message:", content, attachments); setIsStreaming(true); diff --git a/surfsense_browser_extension/sidepanel/hooks/index.ts b/surfsense_browser_extension/sidepanel/hooks/index.ts new file mode 100644 index 000000000..1dd99c3bd --- /dev/null +++ b/surfsense_browser_extension/sidepanel/hooks/index.ts @@ -0,0 +1,4 @@ +// Hooks for SurfSense Browser Extension + +export { useContextAction, getMessageForAction, type ContextAction } from "./useContextAction"; +export { useKeyboardShortcuts, getMessageForKeyboardAction, type KeyboardAction } from "./useKeyboardShortcuts"; diff --git a/surfsense_browser_extension/sidepanel/hooks/useContextAction.ts b/surfsense_browser_extension/sidepanel/hooks/useContextAction.ts new file mode 100644 index 000000000..b29bad2c1 --- /dev/null +++ b/surfsense_browser_extension/sidepanel/hooks/useContextAction.ts @@ -0,0 +1,104 @@ +import { useEffect, useState, useCallback } from "react"; +import { Storage } from "@plasmohq/storage"; + +export interface ContextAction { + action: string; + text: string; + pageUrl?: string; + linkUrl?: string; + timestamp: number; +} + +/** + * Hook to handle context menu actions from background script + * Returns pending action and a function to clear it + */ +export function useContextAction() { + const [pendingAction, setPendingAction] = useState(null); + + // Check for pending context action on mount and when sidepanel gains focus + const checkPendingAction = useCallback(async () => { + const storage = new Storage({ area: "local" }); + const action = await storage.get("pendingContextAction"); + + if (action && action.timestamp) { + // Only process actions from last 30 seconds + const isRecent = Date.now() - action.timestamp < 30000; + if (isRecent) { + setPendingAction(action); + // Clear the pending action + await storage.remove("pendingContextAction"); + } + } + }, []); + + useEffect(() => { + // Check on mount + checkPendingAction(); + + // Check when window gains focus (sidepanel opened) + const handleFocus = () => { + checkPendingAction(); + }; + + window.addEventListener("focus", handleFocus); + + // Also listen for visibility change + const handleVisibilityChange = () => { + if (document.visibilityState === "visible") { + checkPendingAction(); + } + }; + document.addEventListener("visibilitychange", handleVisibilityChange); + + return () => { + window.removeEventListener("focus", handleFocus); + document.removeEventListener("visibilitychange", handleVisibilityChange); + }; + }, [checkPendingAction]); + + const clearAction = useCallback(() => { + setPendingAction(null); + }, []); + + return { pendingAction, clearAction, checkPendingAction }; +} + +/** + * Generate chat message based on context action + */ +export function getMessageForAction(action: ContextAction): string | null { + const text = action.text; + + switch (action.action) { + case "analyze-token": + return `Analyze token: ${text}`; + case "check-safety": + return `Is ${text} safe? Check for rug pull risks.`; + case "add-watchlist": + return `Add ${text} to my watchlist`; + case "copy-address": + // This is handled differently - just copy to clipboard + if (text) { + navigator.clipboard.writeText(text); + } + return null; + case "view-explorer": + // Detect chain and open explorer + if (text.startsWith("0x") && text.length === 42) { + // Ethereum address + window.open(`https://etherscan.io/address/${text}`, "_blank"); + } else if (text.length >= 32 && text.length <= 44) { + // Solana address + window.open(`https://solscan.io/account/${text}`, "_blank"); + } + return null; + case "capture-page": + return "Capture this page to my knowledge base"; + case "ask-ai-page": + return "What is this page about? Summarize the key information."; + default: + return null; + } +} + diff --git a/surfsense_browser_extension/sidepanel/hooks/useKeyboardShortcuts.ts b/surfsense_browser_extension/sidepanel/hooks/useKeyboardShortcuts.ts new file mode 100644 index 000000000..57dcd3346 --- /dev/null +++ b/surfsense_browser_extension/sidepanel/hooks/useKeyboardShortcuts.ts @@ -0,0 +1,92 @@ +import { useEffect, useState, useCallback } from "react"; +import { Storage } from "@plasmohq/storage"; + +export interface KeyboardAction { + action: string; + timestamp: number; +} + +/** + * Hook to handle keyboard shortcut actions from background script + * Returns pending action and a function to clear it + * + * Keyboard shortcuts defined in manifest: + * - open-sidepanel: Ctrl+Shift+S (just opens panel, no message) + * - analyze-token: Ctrl+Shift+A + * - add-watchlist: Ctrl+Shift+W + * - capture-page: Ctrl+Shift+C + * - show-portfolio: Ctrl+Shift+P + */ +export function useKeyboardShortcuts() { + const [pendingAction, setPendingAction] = useState(null); + + // Check for pending keyboard action on mount and when sidepanel gains focus + const checkPendingAction = useCallback(async () => { + const storage = new Storage({ area: "local" }); + const action = await storage.get("pendingKeyboardAction"); + + if (action && action.timestamp) { + // Only process actions from last 30 seconds + const isRecent = Date.now() - action.timestamp < 30000; + if (isRecent) { + setPendingAction(action); + // Clear the pending action + await storage.remove("pendingKeyboardAction"); + } + } + }, []); + + useEffect(() => { + // Check on mount + checkPendingAction(); + + // Check when window gains focus (sidepanel opened) + const handleFocus = () => { + checkPendingAction(); + }; + + window.addEventListener("focus", handleFocus); + + // Also listen for visibility change + const handleVisibilityChange = () => { + if (document.visibilityState === "visible") { + checkPendingAction(); + } + }; + document.addEventListener("visibilitychange", handleVisibilityChange); + + return () => { + window.removeEventListener("focus", handleFocus); + document.removeEventListener("visibilitychange", handleVisibilityChange); + }; + }, [checkPendingAction]); + + const clearAction = useCallback(() => { + setPendingAction(null); + }, []); + + return { pendingAction, clearAction, checkPendingAction }; +} + +/** + * Generate chat message based on keyboard shortcut action + * Returns null for actions that don't need a chat message (like open-sidepanel) + */ +export function getMessageForKeyboardAction(action: KeyboardAction): string | null { + switch (action.action) { + case "open-sidepanel": + // Just opens the panel, no message needed + return null; + case "analyze-token": + return "Analyze the current token on this page"; + case "add-watchlist": + return "Add the current token to my watchlist"; + case "capture-page": + return "Capture this page to my knowledge base"; + case "show-portfolio": + return "Show my portfolio"; + default: + return null; + } +} + diff --git a/surfsense_browser_extension/sidepanel/settings/NotificationSettingsPanel.tsx b/surfsense_browser_extension/sidepanel/settings/NotificationSettingsPanel.tsx new file mode 100644 index 000000000..696516813 --- /dev/null +++ b/surfsense_browser_extension/sidepanel/settings/NotificationSettingsPanel.tsx @@ -0,0 +1,225 @@ +import { useState } from "react"; +import { cn } from "~/lib/utils"; +import { Bell, BellOff, Volume2, VolumeX, Clock, Filter, ChevronRight } from "lucide-react"; +import { Button } from "@/routes/ui/button"; + +export interface NotificationSettings { + enabled: boolean; + sound: boolean; + quietHoursEnabled: boolean; + quietHoursStart: string; + quietHoursEnd: string; + groupNotifications: boolean; + priorities: { + high: boolean; + medium: boolean; + low: boolean; + }; + categories: { + priceAlerts: boolean; + whaleActivity: boolean; + rugPullWarnings: boolean; + portfolioUpdates: boolean; + newsAlerts: boolean; + }; +} + +export interface NotificationSettingsPanelProps { + settings: NotificationSettings; + onSettingsChange: (settings: NotificationSettings) => void; + className?: string; +} + +const DEFAULT_SETTINGS: NotificationSettings = { + enabled: true, + sound: true, + quietHoursEnabled: false, + quietHoursStart: "22:00", + quietHoursEnd: "08:00", + groupNotifications: true, + priorities: { + high: true, + medium: true, + low: false, + }, + categories: { + priceAlerts: true, + whaleActivity: true, + rugPullWarnings: true, + portfolioUpdates: true, + newsAlerts: false, + }, +}; + +/** + * NotificationSettingsPanel - Configure notification preferences + * Part of Epic 4.4 - Smart Notifications + */ +export function NotificationSettingsPanel({ + settings = DEFAULT_SETTINGS, + onSettingsChange, + className, +}: NotificationSettingsPanelProps) { + const updateSettings = (partial: Partial) => { + onSettingsChange({ ...settings, ...partial }); + }; + + const updatePriority = (key: keyof NotificationSettings["priorities"], value: boolean) => { + onSettingsChange({ + ...settings, + priorities: { ...settings.priorities, [key]: value }, + }); + }; + + const updateCategory = (key: keyof NotificationSettings["categories"], value: boolean) => { + onSettingsChange({ + ...settings, + categories: { ...settings.categories, [key]: value }, + }); + }; + + return ( +
+ {/* Header */} +
+
+ + Notification Settings +
+ +
+ + {settings.enabled && ( + <> + {/* Sound Toggle */} +
+
+ {settings.sound ? ( + + ) : ( + + )} + Sound +
+ +
+ + {/* Quiet Hours */} +
+
+
+ + Quiet Hours +
+ +
+ {settings.quietHoursEnabled && ( +
+ updateSettings({ quietHoursStart: e.target.value })} + className="bg-muted rounded px-2 py-1" + /> + to + updateSettings({ quietHoursEnd: e.target.value })} + className="bg-muted rounded px-2 py-1" + /> +
+ )} +
+ + {/* Priority Levels */} +
+
+ + Priority Levels +
+
+ {(["high", "medium", "low"] as const).map((priority) => ( + + ))} +
+
+ + {/* Categories */} +
+ Categories +
+ {Object.entries(settings.categories).map(([key, value]) => ( + + ))} +
+
+ + {/* Group Notifications */} +
+ Group similar notifications + +
+ + )} +
+ ); +} + diff --git a/surfsense_browser_extension/sidepanel/settings/NotificationsList.tsx b/surfsense_browser_extension/sidepanel/settings/NotificationsList.tsx new file mode 100644 index 000000000..1ffff7af1 --- /dev/null +++ b/surfsense_browser_extension/sidepanel/settings/NotificationsList.tsx @@ -0,0 +1,170 @@ +import { cn } from "~/lib/utils"; +import { Bell, AlertTriangle, TrendingUp, TrendingDown, Wallet, Fish, X, Check } from "lucide-react"; +import { Button } from "@/routes/ui/button"; + +export interface Notification { + id: string; + type: "price_alert" | "whale_activity" | "rug_warning" | "portfolio" | "news"; + priority: "high" | "medium" | "low"; + title: string; + message: string; + tokenSymbol?: string; + timestamp: Date; + read: boolean; + actionUrl?: string; +} + +export interface NotificationsListProps { + notifications: Notification[]; + onMarkRead: (id: string) => void; + onMarkAllRead: () => void; + onDismiss: (id: string) => void; + onNotificationClick?: (notification: Notification) => void; + className?: string; +} + +const getNotificationIcon = (type: Notification["type"]) => { + switch (type) { + case "price_alert": + return ; + case "whale_activity": + return ; + case "rug_warning": + return ; + case "portfolio": + return ; + case "news": + return ; + default: + return ; + } +}; + +const getPriorityColor = (priority: Notification["priority"]) => { + switch (priority) { + case "high": + return "border-l-red-500 bg-red-500/5"; + case "medium": + return "border-l-yellow-500 bg-yellow-500/5"; + case "low": + return "border-l-muted-foreground bg-muted/30"; + default: + return "border-l-muted-foreground"; + } +}; + +const formatTime = (date: Date): string => { + const now = new Date(); + const diff = now.getTime() - date.getTime(); + const minutes = Math.floor(diff / 60000); + const hours = Math.floor(diff / 3600000); + const days = Math.floor(diff / 86400000); + + if (minutes < 1) return "Just now"; + if (minutes < 60) return `${minutes}m ago`; + if (hours < 24) return `${hours}h ago`; + return `${days}d ago`; +}; + +/** + * NotificationsList - Display and manage notifications + * Part of Epic 4.4 - Smart Notifications + */ +export function NotificationsList({ + notifications, + onMarkRead, + onMarkAllRead, + onDismiss, + onNotificationClick, + className, +}: NotificationsListProps) { + const unreadCount = notifications.filter((n) => !n.read).length; + + return ( +
+ {/* Header */} +
+
+ + Notifications + {unreadCount > 0 && ( + + {unreadCount} + + )} +
+ {unreadCount > 0 && ( + + )} +
+ + {/* Notifications List */} +
+ {notifications.length === 0 ? ( +
+ +

No notifications yet

+
+ ) : ( + notifications.map((notification) => ( +
{ + if (!notification.read) onMarkRead(notification.id); + onNotificationClick?.(notification); + }} + > +
+ {getNotificationIcon(notification.type)} +
+
+
+ + {notification.title} + + {notification.tokenSymbol && ( + + {notification.tokenSymbol} + + )} +
+

+ {notification.message} +

+ + {formatTime(notification.timestamp)} + +
+ +
+ )) + )} +
+
+ ); +} + diff --git a/surfsense_browser_extension/sidepanel/settings/index.ts b/surfsense_browser_extension/sidepanel/settings/index.ts new file mode 100644 index 000000000..68086fc5f --- /dev/null +++ b/surfsense_browser_extension/sidepanel/settings/index.ts @@ -0,0 +1,6 @@ +// Settings components for SurfSense Browser Extension + +export { NotificationSettingsPanel, type NotificationSettings, type NotificationSettingsPanelProps } from "./NotificationSettingsPanel"; +export { NotificationsList, type Notification, type NotificationsListProps } from "./NotificationsList"; +export { ProductivitySettings } from "./ProductivitySettings"; + diff --git a/surfsense_browser_extension/sidepanel/widgets/HolderAnalysisWidget.tsx b/surfsense_browser_extension/sidepanel/widgets/HolderAnalysisWidget.tsx new file mode 100644 index 000000000..e233a5094 --- /dev/null +++ b/surfsense_browser_extension/sidepanel/widgets/HolderAnalysisWidget.tsx @@ -0,0 +1,145 @@ +import { cn } from "~/lib/utils"; +import { Users, AlertTriangle, Crown } from "lucide-react"; +import { ChainIcon } from "../components/shared/ChainIcon"; + +export interface Holder { + rank: number; + address: string; + label?: string; + balance: number; + percentage: number; + isContract?: boolean; +} + +export interface HolderAnalysisData { + tokenSymbol: string; + chain: string; + totalHolders: number; + top10Percentage: number; + top50Percentage?: number; + holders: Holder[]; + concentrationRisk?: "low" | "medium" | "high" | "critical"; +} + +export interface HolderAnalysisWidgetProps { + /** Holder analysis data */ + data: HolderAnalysisData; + /** Callback when holder is clicked */ + onHolderClick?: (holder: Holder) => void; + /** Additional class names */ + className?: string; +} + +const shortenAddress = (address: string): string => { + return `${address.slice(0, 6)}...${address.slice(-4)}`; +}; + +const formatBalance = (balance: number): string => { + if (balance >= 1e9) return `${(balance / 1e9).toFixed(2)}B`; + if (balance >= 1e6) return `${(balance / 1e6).toFixed(2)}M`; + if (balance >= 1e3) return `${(balance / 1e3).toFixed(2)}K`; + return balance.toFixed(2); +}; + +const getRiskColor = (risk: string) => { + switch (risk) { + case "low": return "text-green-500 bg-green-500/10"; + case "medium": return "text-yellow-500 bg-yellow-500/10"; + case "high": return "text-orange-500 bg-orange-500/10"; + case "critical": return "text-red-500 bg-red-500/10"; + default: return "text-muted-foreground bg-muted"; + } +}; + +/** + * HolderAnalysisWidget - Displays holder distribution inline in chat + * Used when AI responds to "who holds BULLA?" or "analyze holders" + */ +export function HolderAnalysisWidget({ + data, + onHolderClick, + className, +}: HolderAnalysisWidgetProps) { + const risk = data.concentrationRisk || "medium"; + + return ( +
+ {/* Header */} +
+
+ + Holder Analysis - {data.tokenSymbol} +
+ +
+ + {/* Summary Stats */} +
+
+

Total Holders

+

{data.totalHolders.toLocaleString()}

+
+
50 ? "bg-red-500/10" : "bg-muted/50")}> +

Top 10 Hold

+

50 && "text-red-500")}> + {data.top10Percentage.toFixed(1)}% +

+
+ {data.top50Percentage && ( +
+

Top 50 Hold

+

{data.top50Percentage.toFixed(1)}%

+
+ )} +
+

Concentration Risk

+

{risk}

+
+
+ + {/* Risk Warning */} + {(risk === "high" || risk === "critical") && ( +
+ + High holder concentration. Top wallets could impact price. +
+ )} + + {/* Top Holders List */} +
+

Top Holders

+
+ {data.holders.slice(0, 10).map((holder) => ( +
onHolderClick?.(holder)} + > +
+ #{holder.rank} + {holder.rank <= 3 && ( + + )} +
+

{holder.label || shortenAddress(holder.address)}

+ {holder.isContract && ( + Contract + )} +
+
+
+

{holder.percentage.toFixed(2)}%

+

{formatBalance(holder.balance)}

+
+
+ ))} +
+
+
+ ); +} + diff --git a/surfsense_browser_extension/sidepanel/widgets/LiveTokenDataWidget.tsx b/surfsense_browser_extension/sidepanel/widgets/LiveTokenDataWidget.tsx new file mode 100644 index 000000000..376c7e81d --- /dev/null +++ b/surfsense_browser_extension/sidepanel/widgets/LiveTokenDataWidget.tsx @@ -0,0 +1,218 @@ +import { cn } from "~/lib/utils"; +import { Activity, TrendingUp, TrendingDown, RefreshCw, ExternalLink, Droplets, BarChart3 } from "lucide-react"; +import { Button } from "@/routes/ui/button"; +import { ChainIcon } from "../components/shared/ChainIcon"; + +export interface LiveTokenDataInfo { + chain: string; + tokenAddress: string; + tokenSymbol?: string; + tokenName?: string; + priceUsd?: string; + priceNative?: string; + priceChange5m?: number; + priceChange1h?: number; + priceChange6h?: number; + priceChange24h?: number; + volume24h?: number; + volume6h?: number; + volume1h?: number; + liquidityUsd?: number; + marketCap?: number; + fdv?: number; + txns24hBuys?: number; + txns24hSells?: number; + dex?: string; + pairUrl?: string; + totalPairs?: number; + error?: string; +} + +export interface LiveTokenDataWidgetProps { + /** Live token data */ + data: LiveTokenDataInfo; + /** Whether data is loading */ + isLoading?: boolean; + /** Callback when view on DexScreener is clicked */ + onViewDexScreener?: () => void; + /** Additional class names */ + className?: string; +} + +const formatPrice = (price: string | undefined): string => { + if (!price || price === "N/A") return "N/A"; + const num = parseFloat(price); + if (isNaN(num)) return price; + if (num < 0.00001) return `$${num.toExponential(2)}`; + if (num < 1) return `$${num.toFixed(6)}`; + return `$${num.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`; +}; + +const formatLargeNumber = (num: number | undefined): string => { + if (num === undefined || num === null || num === 0) return "N/A"; + if (num >= 1e9) return `$${(num / 1e9).toFixed(2)}B`; + if (num >= 1e6) return `$${(num / 1e6).toFixed(2)}M`; + if (num >= 1e3) return `$${(num / 1e3).toFixed(2)}K`; + return `$${num.toFixed(2)}`; +}; + +const formatNumber = (num: number | undefined): string => { + if (num === undefined || num === null) return "0"; + return num.toLocaleString(); +}; + +const PriceChange = ({ value, label }: { value: number | undefined; label: string }) => { + if (value === undefined || value === null) return null; + const isPositive = value >= 0; + return ( +
+

{label}

+

+ {isPositive ? "+" : ""}{value.toFixed(2)}% +

+
+ ); +}; + +/** + * LiveTokenDataWidget - Displays comprehensive real-time market data + * Used when AI fetches detailed live market information + */ +export function LiveTokenDataWidget({ + data, + isLoading = false, + onViewDexScreener, + className, +}: LiveTokenDataWidgetProps) { + const handleOpenDexScreener = () => { + if (onViewDexScreener) { + onViewDexScreener(); + } else if (data.pairUrl) { + window.open(data.pairUrl, "_blank"); + } else if (data.tokenAddress) { + window.open(`https://dexscreener.com/${data.chain}/${data.tokenAddress}`, "_blank"); + } + }; + + const totalTxns24h = (data.txns24hBuys || 0) + (data.txns24hSells || 0); + const buyRatio = totalTxns24h > 0 ? ((data.txns24hBuys || 0) / totalTxns24h) * 100 : 50; + + return ( +
+ {/* Header */} +
+
+ + Live Market Data + {isLoading ? ( + Fetching... + ) : ( + + + Real-time + + )} +
+
+ + {data.error ? ( +
+ ⚠️ {data.error} +
+ ) : ( + <> + {/* Token Header */} +
+ +
+
+ {data.tokenSymbol || "Token"} + {data.tokenName && ( + {data.tokenName} + )} +
+
+ {formatPrice(data.priceUsd)} + {data.priceChange24h !== undefined && ( + = 0 ? "text-green-500" : "text-red-500" + )}> + {data.priceChange24h >= 0 ? : } + {data.priceChange24h >= 0 ? "+" : ""}{data.priceChange24h.toFixed(2)}% + + )} +
+
+
+ + {/* Price Changes */} +
+ + + + +
+ + {/* Metrics Grid */} +
+
+

+ 24h Volume +

+

{formatLargeNumber(data.volume24h)}

+
+
+

+ Liquidity +

+

{formatLargeNumber(data.liquidityUsd)}

+
+
+

Market Cap

+

{formatLargeNumber(data.marketCap)}

+
+
+

FDV

+

{formatLargeNumber(data.fdv)}

+
+
+ + {/* Transaction Activity */} +
+

+ 24h Transactions +

+
+
+
+
+
+
+ {formatNumber(data.txns24hBuys)} buys + {formatNumber(totalTxns24h)} total + {formatNumber(data.txns24hSells)} sells +
+
+ + {/* DEX Info & Actions */} +
+
+ DEX: {data.dex || "Unknown"} + {data.totalPairs && data.totalPairs > 1 && ( + • {data.totalPairs} pairs + )} +
+ +
+ + )} +
+ ); +} diff --git a/surfsense_browser_extension/sidepanel/widgets/LiveTokenPriceWidget.tsx b/surfsense_browser_extension/sidepanel/widgets/LiveTokenPriceWidget.tsx new file mode 100644 index 000000000..b5e710a88 --- /dev/null +++ b/surfsense_browser_extension/sidepanel/widgets/LiveTokenPriceWidget.tsx @@ -0,0 +1,155 @@ +import { cn } from "~/lib/utils"; +import { Zap, TrendingUp, TrendingDown, RefreshCw, ExternalLink } from "lucide-react"; +import { Button } from "@/routes/ui/button"; +import { ChainIcon } from "../components/shared/ChainIcon"; + +export interface LiveTokenPriceData { + chain: string; + tokenAddress: string; + tokenSymbol?: string; + tokenName?: string; + priceUsd?: string; + priceNative?: string; + priceChange5m?: number; + priceChange1h?: number; + priceChange6h?: number; + priceChange24h?: number; + volume24h?: number; + liquidityUsd?: number; + marketCap?: number; + fdv?: number; + dex?: string; + pairUrl?: string; + error?: string; +} + +export interface LiveTokenPriceWidgetProps { + /** Live token price data */ + data: LiveTokenPriceData; + /** Whether data is loading */ + isLoading?: boolean; + /** Callback when view on DexScreener is clicked */ + onViewDexScreener?: () => void; + /** Additional class names */ + className?: string; +} + +const formatPrice = (price: string | undefined): string => { + if (!price || price === "N/A") return "N/A"; + const num = parseFloat(price); + if (isNaN(num)) return price; + if (num < 0.00001) return `$${num.toExponential(2)}`; + if (num < 1) return `$${num.toFixed(6)}`; + return `$${num.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`; +}; + +const PriceChange = ({ value, label }: { value: number | undefined; label: string }) => { + if (value === undefined || value === null) return null; + const isPositive = value >= 0; + return ( +
+

{label}

+

+ {isPositive ? "+" : ""}{value.toFixed(2)}% +

+
+ ); +}; + +/** + * LiveTokenPriceWidget - Displays real-time token price inline in chat + * Used when AI fetches current/live price data + */ +export function LiveTokenPriceWidget({ + data, + isLoading = false, + onViewDexScreener, + className, +}: LiveTokenPriceWidgetProps) { + const handleOpenDexScreener = () => { + if (onViewDexScreener) { + onViewDexScreener(); + } else if (data.pairUrl) { + window.open(data.pairUrl, "_blank"); + } else if (data.tokenAddress) { + window.open(`https://dexscreener.com/${data.chain}/${data.tokenAddress}`, "_blank"); + } + }; + + return ( +
+ {/* Header */} +
+
+ + Live Price + {isLoading ? ( + Fetching... + ) : ( + + + Real-time + + )} +
+
+ + {data.error ? ( +
+ ⚠️ {data.error} +
+ ) : ( + <> + {/* Token Header */} +
+ +
+
+ {data.tokenSymbol || "Token"} + {data.tokenName && ( + {data.tokenName} + )} +
+
+ {formatPrice(data.priceUsd)} + {data.priceChange24h !== undefined && ( + = 0 ? "text-green-500" : "text-red-500" + )}> + {data.priceChange24h >= 0 ? ( + + ) : ( + + )} + {data.priceChange24h >= 0 ? "+" : ""}{data.priceChange24h.toFixed(2)}% + + )} +
+
+
+ + {/* Price Changes */} +
+ + + + +
+ + {/* Action */} + + + )} +
+ ); +} + diff --git a/surfsense_browser_extension/sidepanel/widgets/MarketOverviewWidget.tsx b/surfsense_browser_extension/sidepanel/widgets/MarketOverviewWidget.tsx new file mode 100644 index 000000000..c4c050310 --- /dev/null +++ b/surfsense_browser_extension/sidepanel/widgets/MarketOverviewWidget.tsx @@ -0,0 +1,136 @@ +import { cn } from "~/lib/utils"; +import { Globe, TrendingUp, TrendingDown } from "lucide-react"; + +export interface MarketToken { + symbol: string; + name: string; + price: number; + priceChange24h: number; + marketCap?: number; + volume24h?: number; +} + +export interface MarketOverviewData { + tokens: MarketToken[]; + totalMarketCap?: number; + totalVolume24h?: number; + btcDominance?: number; + fearGreedIndex?: number; +} + +export interface MarketOverviewWidgetProps { + /** Market overview data */ + data: MarketOverviewData; + /** Callback when token is clicked */ + onTokenClick?: (token: MarketToken) => void; + /** Additional class names */ + className?: string; +} + +const formatPrice = (price: number): string => { + if (price < 1) return `$${price.toFixed(4)}`; + return `$${price.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`; +}; + +const formatLargeNumber = (num: number): string => { + if (num >= 1e12) return `$${(num / 1e12).toFixed(2)}T`; + if (num >= 1e9) return `$${(num / 1e9).toFixed(2)}B`; + if (num >= 1e6) return `$${(num / 1e6).toFixed(2)}M`; + return `$${num.toFixed(2)}`; +}; + +const getFearGreedLabel = (index: number): string => { + if (index > 75) return "Extreme Greed"; + if (index > 50) return "Greed"; + if (index > 25) return "Fear"; + return "Extreme Fear"; +}; + +/** + * MarketOverviewWidget - Displays market overview inline in chat + * Used when AI responds to "show market overview" or "how's the market?" + */ +export function MarketOverviewWidget({ + data, + onTokenClick, + className, +}: MarketOverviewWidgetProps) { + return ( +
+ {/* Header */} +
+ + Market Overview +
+ + {/* Global Stats */} + {(data.totalMarketCap || data.btcDominance || data.fearGreedIndex) && ( +
+ {data.totalMarketCap && ( +
+

Total Market Cap

+

{formatLargeNumber(data.totalMarketCap)}

+
+ )} + {data.totalVolume24h && ( +
+

24h Volume

+

{formatLargeNumber(data.totalVolume24h)}

+
+ )} + {data.btcDominance && ( +
+

BTC Dominance

+

{data.btcDominance.toFixed(1)}%

+
+ )} + {data.fearGreedIndex && ( +
50 ? "bg-green-500/10" : "bg-red-500/10" + )}> +

Fear & Greed

+

50 ? "text-green-500" : "text-red-500" + )}> + {data.fearGreedIndex} - {getFearGreedLabel(data.fearGreedIndex)} +

+
+ )} +
+ )} + + {/* Token Prices */} +
+ {data.tokens.map((token) => ( +
onTokenClick?.(token)} + > +
+

{token.symbol}

+

{token.name}

+
+
+

{formatPrice(token.price)}

+

= 0 ? "text-green-500" : "text-red-500" + )}> + {token.priceChange24h >= 0 ? ( + + ) : ( + + )} + {token.priceChange24h >= 0 ? "+" : ""}{token.priceChange24h.toFixed(2)}% +

+
+
+ ))} +
+
+ ); +} + diff --git a/surfsense_browser_extension/sidepanel/widgets/TrendingTokensWidget.tsx b/surfsense_browser_extension/sidepanel/widgets/TrendingTokensWidget.tsx new file mode 100644 index 000000000..e179a85c4 --- /dev/null +++ b/surfsense_browser_extension/sidepanel/widgets/TrendingTokensWidget.tsx @@ -0,0 +1,133 @@ +import { cn } from "~/lib/utils"; +import { Flame, TrendingUp, TrendingDown, Star } from "lucide-react"; +import { Button } from "@/routes/ui/button"; +import { ChainIcon } from "../components/shared/ChainIcon"; + +export interface TrendingToken { + symbol: string; + name: string; + chain: string; + contractAddress?: string; + price: number; + priceChange24h: number; + priceChange1h?: number; + volume24h?: number; + liquidity?: number; + rank?: number; +} + +export interface TrendingTokensWidgetProps { + /** List of trending tokens */ + tokens: TrendingToken[]; + /** Filter by chain (optional) */ + chain?: string; + /** Timeframe for trending data */ + timeframe?: string; + /** Callback when token is clicked */ + onTokenClick?: (token: TrendingToken) => void; + /** Callback when add to watchlist is clicked */ + onAddToWatchlist?: (token: TrendingToken) => void; + /** Additional class names */ + className?: string; +} + +const formatPrice = (price: number): string => { + if (price < 0.00001) return `$${price.toExponential(2)}`; + if (price < 1) return `$${price.toFixed(6)}`; + return `$${price.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`; +}; + +const formatLargeNumber = (num: number): string => { + if (num >= 1e9) return `$${(num / 1e9).toFixed(2)}B`; + if (num >= 1e6) return `$${(num / 1e6).toFixed(2)}M`; + if (num >= 1e3) return `$${(num / 1e3).toFixed(2)}K`; + return `$${num.toFixed(2)}`; +}; + +/** + * TrendingTokensWidget - Displays trending/hot tokens inline in chat + * Used when AI responds to "what's hot on Solana?" or "show trending tokens" + */ +export function TrendingTokensWidget({ + tokens, + chain = "All Chains", + timeframe = "24h", + onTokenClick, + onAddToWatchlist, + className, +}: TrendingTokensWidgetProps) { + return ( +
+ {/* Header */} +
+
+ + Trending on {chain} + {timeframe} +
+
+ + {/* Token List */} + {tokens.length === 0 ? ( +

No trending tokens found

+ ) : ( +
+ {tokens.map((token, index) => ( +
onTokenClick?.(token)} + > +
+ + #{token.rank || index + 1} + + +
+
+ {token.symbol} +
+ {token.name} +
+
+
+
+

{formatPrice(token.price)}

+

= 0 ? "text-green-500" : "text-red-500" + )}> + {token.priceChange24h >= 0 ? ( + + ) : ( + + )} + {token.priceChange24h >= 0 ? "+" : ""}{token.priceChange24h.toFixed(1)}% +

+
+ {token.volume24h && ( +
+

Vol

+

{formatLargeNumber(token.volume24h)}

+
+ )} + +
+
+ ))} +
+ )} +
+ ); +} + diff --git a/surfsense_browser_extension/sidepanel/widgets/index.ts b/surfsense_browser_extension/sidepanel/widgets/index.ts index 9b83ef394..f70349134 100644 --- a/surfsense_browser_extension/sidepanel/widgets/index.ts +++ b/surfsense_browser_extension/sidepanel/widgets/index.ts @@ -9,12 +9,19 @@ export { TokenAnalysisWidget, type TokenAnalysisWidgetProps, type TokenAnalysisD // Epic 2: Smart Monitoring & Alerts export { WhaleActivityWidget, type WhaleActivityWidgetProps } from "./WhaleActivityWidget"; +export { TrendingTokensWidget, type TrendingTokensWidgetProps, type TrendingToken } from "./TrendingTokensWidget"; // Epic 3: Trading Intelligence export { TradingSuggestionWidget, type TradingSuggestionWidgetProps } from "./TradingSuggestionWidget"; export { PortfolioWidget, type PortfolioWidgetProps } from "./PortfolioWidget"; +export { HolderAnalysisWidget, type HolderAnalysisWidgetProps, type HolderAnalysisData, type Holder } from "./HolderAnalysisWidget"; // Epic 4: Content Creation & Productivity export { ChartCaptureWidget, type ChartCaptureWidgetProps } from "./ChartCaptureWidget"; export { ThreadGeneratorWidget, type ThreadGeneratorWidgetProps } from "./ThreadGeneratorWidget"; +// Market Data Widgets +export { MarketOverviewWidget, type MarketOverviewWidgetProps, type MarketOverviewData, type MarketToken } from "./MarketOverviewWidget"; +export { LiveTokenPriceWidget, type LiveTokenPriceWidgetProps, type LiveTokenPriceData } from "./LiveTokenPriceWidget"; +export { LiveTokenDataWidget, type LiveTokenDataWidgetProps, type LiveTokenDataInfo } from "./LiveTokenDataWidget"; + diff --git a/surfsense_web/components/tool-ui/crypto/index.ts b/surfsense_web/components/tool-ui/crypto/index.ts index 3522329e3..e10107dd5 100644 --- a/surfsense_web/components/tool-ui/crypto/index.ts +++ b/surfsense_web/components/tool-ui/crypto/index.ts @@ -128,3 +128,12 @@ export { type LiveTokenDataArgs, type LiveTokenDataResult, } from "./live-token-data"; + +// Trading Suggestion - displays AI-powered entry/exit suggestions +export { + TradingSuggestionToolUI, + TradingSuggestionArgsSchema, + TradingSuggestionResultSchema, + type TradingSuggestionArgs, + type TradingSuggestionResult, +} from "./trading-suggestion"; diff --git a/surfsense_web/components/tool-ui/crypto/trading-suggestion.tsx b/surfsense_web/components/tool-ui/crypto/trading-suggestion.tsx new file mode 100644 index 000000000..d41d7e884 --- /dev/null +++ b/surfsense_web/components/tool-ui/crypto/trading-suggestion.tsx @@ -0,0 +1,236 @@ +"use client"; + +import { makeAssistantToolUI } from "@assistant-ui/react"; +import { z } from "zod"; +import { cn } from "@/lib/utils"; +import { Target, AlertCircle, Info, TrendingUp, TrendingDown, Bell, ExternalLink } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { ChainIcon } from "@/components/crypto/ChainIcon"; +import { useState } from "react"; + +// Schema for trading suggestion tool arguments +export const TradingSuggestionArgsSchema = z.object({ + tokenSymbol: z.string(), + tokenName: z.string().optional(), + chain: z.string(), + contractAddress: z.string().optional(), + currentPrice: z.number(), + entry: z.object({ + min: z.number(), + max: z.number(), + reasoning: z.string(), + }), + targets: z.array(z.object({ + level: z.number(), + price: z.number(), + percentGain: z.number(), + confidence: z.number(), + })), + stopLoss: z.object({ + price: z.number(), + percentLoss: z.number(), + reasoning: z.string(), + }), + riskReward: z.number(), + overallConfidence: z.number(), + reasoning: z.array(z.string()), + invalidationConditions: z.array(z.string()), +}); + +export type TradingSuggestionArgs = z.infer; + +// Schema for trading suggestion result +export const TradingSuggestionResultSchema = z.object({ + success: z.boolean(), + message: z.string().optional(), + alertsSet: z.boolean().optional(), +}); + +export type TradingSuggestionResult = z.infer; + +const formatPrice = (price: number): string => { + if (price < 0.00001) return `$${price.toExponential(2)}`; + if (price < 0.01) return `$${price.toFixed(8)}`; + if (price < 1) return `$${price.toFixed(6)}`; + return `$${price.toFixed(4)}`; +}; + +const getRiskRewardColor = (ratio: number) => { + if (ratio >= 3) return "text-green-500"; + if (ratio >= 2) return "text-yellow-500"; + return "text-red-500"; +}; + +const getRiskRewardLabel = (ratio: number) => { + if (ratio >= 3) return "Excellent"; + if (ratio >= 2) return "Good"; + if (ratio >= 1.5) return "Fair"; + return "Poor"; +}; + +/** + * TradingSuggestionToolUI - Displays AI-powered trading suggestions in chat + * Used when AI responds to queries like "suggest entry for BONK" or "trading suggestion for SOL" + */ +export const TradingSuggestionToolUI = makeAssistantToolUI({ + toolName: "trading_suggestion", + render: ({ args, result, status }) => { + const [showDetails, setShowDetails] = useState(false); + const isLoading = status.type === "running"; + + const handleOpenDexScreener = () => { + if (args.contractAddress) { + window.open(`https://dexscreener.com/${args.chain}/${args.contractAddress}`, "_blank"); + } + }; + + return ( + + + +
+ + Trading Suggestion + {isLoading && Analyzing...} +
+
+
Confidence
+
{args.overallConfidence}%
+
+
+
+ + {/* Token Header */} +
+ +
+
+ {args.tokenSymbol} + {args.tokenName && {args.tokenName}} +
+ {formatPrice(args.currentPrice)} +
+
+ + {/* Entry Zone */} +
+
+
+ Entry Zone +
+
+ {formatPrice(args.entry.min)} - {formatPrice(args.entry.max)} +
+

{args.entry.reasoning}

+
+ + {/* Targets */} +
+
+ + Take Profit Targets +
+
+ {args.targets.map((target) => ( +
+
+ 🎯 T{target.level} + {formatPrice(target.price)} +
+
+ +{target.percentGain.toFixed(1)}% + {target.confidence}% +
+
+ ))} +
+
+ + {/* Stop Loss */} +
+
+
+ Stop Loss +
+
+ + {formatPrice(args.stopLoss.price)} + + + {args.stopLoss.percentLoss.toFixed(1)}% + +
+

{args.stopLoss.reasoning}

+
+ + {/* Risk/Reward */} +
+ Risk/Reward Ratio +
+ = 3 ? "default" : args.riskReward >= 2 ? "secondary" : "destructive"}> + {getRiskRewardLabel(args.riskReward)} + + + 1:{args.riskReward.toFixed(1)} + +
+
+ + {/* Why? Section - Collapsible */} +
+ + + {showDetails && ( +
+
+

Reasoning:

+
    + {args.reasoning.map((reason, i) => ( +
  • + + {reason} +
  • + ))} +
+
+ +
+

Invalidation:

+
    + {args.invalidationConditions.map((condition, i) => ( +
  • + + {condition} +
  • + ))} +
+
+
+ )} +
+ + {/* Action Buttons */} +
+ + +
+ + + ); + }, +}); +