## Problem
When creating/editing/deleting traders, AI models, or exchanges, the UI
takes 3-4 seconds to show results, causing users to think the system is frozen.
## Root Cause
Although mutateTraders() was called after operations, it was not awaited,
causing the function to continue immediately without waiting for data refresh.
The UI relied on the refreshInterval: 5000 automatic refresh, resulting in
up to 5 seconds of delay.
## Solution
Added await before all mutateTraders() calls to ensure data is refreshed
before closing modals or showing success messages.
Changes:
- handleCreateTrader: Added await before mutateTraders()
- handleSaveEditTrader: Added await before mutateTraders()
- handleDeleteTrader: Added await before mutateTraders()
- handleToggleTrader: Added await before mutateTraders()
Impact:
- From 3-4s delay to immediate display (< 500ms)
- Significantly improved UX
- Consistent with AI model and exchange config behavior
Testing:
- Frontend build successful
- No TypeScript errors
- Ready for manual UI testing
Co-authored-by: the-dev-z <the-dev-z@users.noreply.github.com>
* refactor(frontend): extract RegistrationDisabled as reusable component
- Create RegistrationDisabled component with i18n support
- Add registrationClosed and registrationClosedMessage translations
- Replace inline JSX in App.tsx with new component
- Improve code maintainability and reusability
- Add hover effect to back button for better UX
* fix(frontend): add registration toggle to LoginModal component
- Add useSystemConfig hook to LoginModal
- Conditionally render registration button based on registration_enabled config
- Ensures consistency with HeaderBar and LoginPage registration controls
- Completes registration toggle feature implementation across all entry points
* feat(frontend): add registration toggle UI support
- Add registration disabled page in App.tsx when registration is closed
- Hide registration link in LoginPage when registration is disabled
- Add registration_enabled field to SystemConfig interface
- Frontend conditionally shows/hides registration UI based on backend config
* feat: add registration toggle feature
Add system-level registration enable/disable control:
- Add registration_enabled config to system_config table (default: true)
- Add registration check in handleRegister API endpoint
- Expose registration_enabled status in /api/config endpoint
- Frontend can use this config to conditionally show/hide registration UI
This allows administrators to control user registration without code changes.
* fix(frontend): add registration toggle to HeaderBar and RegisterPage
- Add useSystemConfig hook and registrationEnabled check to HeaderBar
- Conditionally show/hide signup buttons in both desktop and mobile views
- Add registration check to RegisterPage to show RegistrationDisabled component
- This completes the registration toggle feature across all UI components
* test(frontend): add comprehensive unit tests for registration toggle feature
- Add RegistrationDisabled component tests (rendering, navigation, styling)
- Add registrationToggle logic tests (config handling, edge cases, multi-location consistency)
- Configure Vitest with jsdom environment for React component testing
- All 80 tests passing (9 new tests for RegistrationDisabled + 21 for toggle logic)
Remove duplicate password validation logic to ensure consistency.
Changes:
- Remove custom isStrongPassword function (RegisterPage.tsx:569-576)
- Use PasswordChecklist validation result (passwordValid state) instead
- Add comprehensive test suite with 28 test cases
- Configure Vitest with jsdom environment and setup file
Test Coverage:
- Password validation rules (length, uppercase, lowercase, number, special chars)
- Special character consistency (/[@#$%!&*?]/)
- Edge cases and boundary conditions
- Refactoring consistency verification
All 78 tests passing (25 + 25 + 28).
Co-authored-by: tinkle-community <tinklefund@gmail.com>
## Problem
Users reported that the 32+32 character split design is not user-friendly:
1. ❌ Second stage still requires entering 32 characters - hard to count
2. ❌ Need to count many characters in both stages
3. ❌ Easy to make mistakes when counting
## Solution
Change the split from 32+32 to **58+6**
**Stage 1**: 58 characters
- Enter the majority of the key (90%)
- Easy to copy/paste the prefix
**Stage 2**: 6 characters
- ✅ Only need to count last 6 chars (very easy)
- ✅ Quick verification of key suffix
- ✅ Reduces user errors
## Changes
```typescript
// Old: Equal split
const expectedPart1Length = Math.ceil(expectedLength / 2) // 32
const expectedPart2Length = expectedLength - expectedPart1Length // 32
// New: Most of key + last 6 chars
const expectedPart1Length = expectedLength - 6 // 58
const expectedPart2Length = 6 // Last 6 characters
```
## Test plan
✅ Frontend builds successfully (npm run build)
✅ User-friendly: Only need to count 6 characters
✅ Maintains security: Two-stage input logic unchanged
Co-authored-by: the-dev-z <the-dev-z@users.noreply.github.com>
## Summary
Allow users to select the number of decision records to display (5/10/20/50)
in the Web UI, with persistent storage in localStorage.
## Changes
### Backend
- api/server.go: Add 'limit' query parameter support to /api/decisions/latest
- Default: 5 (maintains current behavior)
- Max: 50 (prevents excessive data loading)
- Fully backward compatible
### Frontend
- web/src/lib/api.ts: Update getLatestDecisions() to accept limit parameter
- web/src/pages/TraderDashboard.tsx:
- Add decisionLimit state management with localStorage persistence
- Add dropdown selector UI (5/10/20/50 options)
- Pass limit to API calls and update SWR cache key
## Time Coverage
- 5 records = 15 minutes (default, quick check)
- 10 records = 30 minutes (short-term review)
- 20 records = 1 hour (medium-term analysis)
- 50 records = 2.5 hours (deep pattern analysis)
* fix(web): display '—' for missing data instead of NaN% or 0% (#633)
- Add hasValidData validation for null/undefined/NaN
- Display '—' for invalid trader.total_pnl_pct
- Only show gap calculations when both values are valid
- Prevents misleading users with 0% when data is missing
Fixes#633
* test(web): add comprehensive unit tests for CompetitionPage NaN handling
- Test data validation logic (null/undefined/NaN detection)
- Test gap calculation with valid and invalid data
- Test display formatting (shows '—' instead of 'NaN%')
- Test leading/trailing message display conditions
- Test edge cases (Infinity, very small/large numbers)
All 25 test cases passed, covering:
1. hasValidData check (7 cases): valid/null/undefined/NaN/zero/negative
2. gap calculation (3 cases): valid data, invalid data, negative gap
3. display formatting (6 cases): positive/negative/null/undefined/NaN/zero
4. leading/trailing messages (5 cases): conditional display logic
5. edge cases (4 cases): Infinity, -Infinity, very small/large numbers
Related to PR #678 - ensures missing data displays as '—' instead of 'NaN%'.
Co-Authored-By: tinkle-community <tinklefund@gmail.com>
---------
Co-authored-by: ZhouYongyou <128128010+zhouyongyou@users.noreply.github.com>
Co-authored-by: tinkle-community <tinklefund@gmail.com>
- Add onBlur validation for initial_balance input to enforce minimum of 100
- Add detailed prompt template descriptions with i18n support
- Fix Traditional Chinese to Simplified Chinese
- Extract hardcoded Chinese text to i18n translation system
- Add translation keys for all prompt templates and descriptions
Fixes#629, Fixes#630
- Simplified the logic for determining configured models and exchanges by removing reliance on sensitive fields like apiKey.
- Enhanced filtering criteria to check for enabled status and non-sensitive fields, improving clarity and security.
- Updated UI class bindings to reflect the new configuration checks without compromising functionality.
This refactor aims to streamline the configuration process while ensuring sensitive information is not exposed.
## Background
Hyperliquid official documentation recommends using Agent Wallet pattern for API trading:
- Agent Wallet is used for signing only
- Main Wallet Address is used for querying account data
- Agent Wallet should not hold significant funds
Reference: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/nonces-and-api-wallets
## Current Implementation
Current implementation allows auto-generating wallet address from private key,
which simplifies user configuration but may lead to potential security concerns
if users accidentally use their main wallet private key.
## Enhancement
Following the proven pattern already used in Aster exchange implementation
(which uses dual-address mode), this enhancement upgrades Hyperliquid to
Agent Wallet mode:
### Core Changes
1. **Mandatory dual-address configuration**
- Agent Private Key (for signing)
- Main Wallet Address (holds funds)
2. **Multi-layer security checks**
- Detect if user accidentally uses main wallet private key
- Validate Agent wallet balance (reject if > 100 USDC)
- Provide detailed configuration guidance
3. **Design consistency**
- Align with Aster's dual-address pattern
- Follow Hyperliquid official best practices
### Code Changes
**config/database.go**:
- Add inline comments clarifying Agent Wallet security model
**trader/hyperliquid_trader.go**:
- Require explicit main wallet address (no auto-generation)
- Check if agent address matches main wallet address (security risk indicator)
- Query agent wallet balance and block if excessive
- Display both agent and main wallet addresses for transparency
**web/src/components/AITradersPage.tsx**:
- Add security alert banner explaining Agent Wallet mode
- Separate required inputs for Agent Private Key and Main Wallet Address
- Add field descriptions and validation
### Benefits
- ✅ Aligns with Hyperliquid official security recommendations
- ✅ Maintains design consistency with Aster implementation
- ✅ Multi-layer protection against configuration mistakes
- ✅ Detailed logging for troubleshooting
### Breaking Change
Users must now explicitly provide main wallet address (hyperliquid_wallet_addr).
Old configurations will receive clear error messages with migration guidance.
### Migration Guide
**Before** (single private key):
```json
{
"hyperliquid_private_key": "0x..."
}
```
**After** (Agent Wallet mode):
```json
{
"hyperliquid_private_key": "0x...", // Agent Wallet private key
"hyperliquid_wallet_addr": "0x..." // Main Wallet address
}
```
Users can create Agent Wallet on Hyperliquid official website:
https://app.hyperliquid.xyz/ → Settings → API Wallets
Co-authored-by: tinkle-community <tinklefund@gmail.com>
**Problem:**
Competition page shows "NaN%" for gap difference when trader P&L
percentages are null/undefined.
**Root Cause:**
Line 227: `const gap = trader.total_pnl_pct - opponent.total_pnl_pct`
- If either value is `undefined` or `null`, result is `NaN`
- Display shows "领先 NaN%" or "落后 NaN%"
**Solution:**
Add null coalescing to default undefined/null values to 0:
```typescript
const gap = (trader.total_pnl_pct ?? 0) - (opponent.total_pnl_pct ?? 0)
```
**Impact:**
- ✅ Gap calculation returns 0 when data is missing (shows 0.00%)
- ✅ Prevents confusing "NaN%" display
- ✅ Graceful degradation for incomplete data
Fixes#633
Co-authored-by: tinkle-community <tinklefund@gmail.com>
- Add soft delete support for AI models
* Add deleted field to ai_models table
* Implement soft delete with sensitive data cleanup
* Filter deleted records in queries
- Replace browser confirm dialogs with custom styled modals
* Create DeleteConfirmModal component with Binance theme
* Add proper warning messages and icons
* Improve user experience with consistent styling
- Fix duplicate model/exchange display in selection dropdowns
* Use supportedModels/supportedExchanges for modal selectors
* Use configuredModels/configuredExchanges for panel display
* Remove redundant selectableModels/selectableExchanges logic
- Enhance data management
* Refresh data after deletion operations
* Proper state cleanup after modal operations
* Clear sensitive data during soft delete
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: tinkle-community <tinklefund@gmail.com>
- Add soft delete support for AI models
* Add deleted field to ai_models table
* Implement soft delete with sensitive data cleanup
* Filter deleted records in queries
- Replace browser confirm dialogs with custom styled modals
* Create DeleteConfirmModal component with Binance theme
* Add proper warning messages and icons
* Improve user experience with consistent styling
- Fix duplicate model/exchange display in selection dropdowns
* Use supportedModels/supportedExchanges for modal selectors
* Use configuredModels/configuredExchanges for panel display
* Remove redundant selectableModels/selectableExchanges logic
- Enhance data management
* Refresh data after deletion operations
* Proper state cleanup after modal operations
* Clear sensitive data during soft delete
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: tinkle-community <tinklefund@gmail.com>