feat(frontend): implement comprehensive API Settings Management Interface

Implement a complete API Management Interface for React frontend with split-
panel layout, migrating all features from legacy JS frontend.

New Features:
- API Settings page with 5 tabs: Providers, Endpoints, Cache, Model Pools, CLI Settings
- Provider Management: CRUD operations, multi-key rotation, health checks, test connection
- Endpoint Management: CRUD operations, cache strategy configuration, enable/disable toggle
- Cache Settings: Global configuration, statistics display, clear cache functionality
- Model Pool Management: CRUD operations, auto-discovery feature, provider exclusion
- CLI Settings Management: Provider-based and Direct modes, full CRUD support
- Multi-Key Settings Modal: Manage API keys with rotation strategies and weights
- Manage Models Modal: View and manage models per provider (LLM and Embedding)
- Sync to CodexLens: Integration handler for provider configuration sync

Technical Implementation:
- Created 12 new React components in components/api-settings/
- Extended lib/api.ts with 460+ lines of API client functions
- Created hooks/useApiSettings.ts with 772 lines of TanStack Query hooks
- Added RadioGroup UI component for form selections
- Implemented unified error handling with useNotifications across all operations
- Complete i18n support (500+ keys in English and Chinese)
- Route integration (/api-settings) and sidebar navigation

Code Quality:
- All acceptance criteria from plan.json verified
- Code review passed with Gemini (all 7 IMPL tasks complete)
- Follows existing patterns: Shadcn UI, TanStack Query, react-intl, Lucide icons
This commit is contained in:
catlog22
2026-02-01 23:58:04 +08:00
parent 690597bae8
commit abce912ee5
33 changed files with 5874 additions and 45 deletions

View File

@@ -5,6 +5,13 @@
import type { ConversationTurn, ConversationRecord, NativeSessionMapping } from './cli-history-store.js';
/**
* Emit user warning for silent fallback scenarios
*/
function warnUser(message: string): void {
console.warn(`[ccw] ${message}`);
}
// Strategy types
export type ResumeStrategy = 'native' | 'prompt-concat' | 'hybrid';
@@ -78,6 +85,7 @@ export function determineResumeStrategy(options: ResumeStrategyOptions): ResumeD
});
if (crossTool) {
warnUser('Cross-tool resume: using prompt concatenation (different tool)');
return buildPromptConcatDecision(resumeIds, getConversation);
}
@@ -94,6 +102,7 @@ export function determineResumeStrategy(options: ResumeStrategyOptions): ResumeD
}
// No native mapping, fall back to prompt-concat
warnUser('No native session mapping found, using prompt concatenation');
return buildPromptConcatDecision(resumeIds, getConversation);
}
@@ -109,6 +118,7 @@ function buildPromptConcatDecision(
getConversation: (ccwId: string) => ConversationRecord | null
): ResumeDecision {
const allTurns: ConversationTurn[] = [];
let hasMissingConversation = false;
for (const id of resumeIds) {
const conversation = getConversation(id);
@@ -119,9 +129,16 @@ function buildPromptConcatDecision(
_sourceId: id
}));
allTurns.push(...turnsWithSource as ConversationTurn[]);
} else {
hasMissingConversation = true;
}
}
// Warn if any conversation was not found
if (hasMissingConversation) {
warnUser('One or more resume IDs not found, using prompt concatenation (new session created)');
}
// Sort by timestamp
allTurns.sort((a, b) =>
new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()