@@ -189,6 +189,13 @@ async function toggleProviderExclusion(providerId) {
189189
190190 // Re-render the discovered providers section
191191 renderDiscoveredProviders ( ) ;
192+
193+ // Update sidebar summary
194+ const sidebarContainer = document . querySelector ( '.api-settings-sidebar .embedding-pool-sidebar-summary' ) ;
195+ if ( sidebarContainer && sidebarContainer . parentElement ) {
196+ sidebarContainer . parentElement . innerHTML = renderEmbeddingPoolSidebar ( ) ;
197+ if ( window . lucide ) lucide . createIcons ( ) ;
198+ }
192199}
193200
194201// ========== Provider Management ==========
@@ -991,9 +998,7 @@ async function renderApiSettings() {
991998 '<i data-lucide="plus"></i> ' + t ( 'apiSettings.addEndpoint' ) +
992999 '</button>' ;
9931000 } else if ( activeSidebarTab === 'embedding-pool' ) {
994- sidebarContentHtml = '<div class="embedding-pool-sidebar-info" style="padding: 1rem; color: var(--text-secondary); font-size: 0.875rem;">' +
995- '<p>' + t ( 'apiSettings.embeddingPoolDesc' ) + '</p>' +
996- '</div>' ;
1001+ sidebarContentHtml = renderEmbeddingPoolSidebar ( ) ;
9971002 } else if ( activeSidebarTab === 'cache' ) {
9981003 sidebarContentHtml = '<div class="cache-sidebar-info" style="padding: 1rem; color: var(--text-secondary); font-size: 0.875rem;">' +
9991004 '<p>' + t ( 'apiSettings.cacheTabHint' ) + '</p>' +
@@ -2533,6 +2538,72 @@ function generateKeyId() {
25332538
25342539// ========== Embedding Pool Management ==========
25352540
2541+ /**
2542+ * Render embedding pool sidebar summary
2543+ */
2544+ function renderEmbeddingPoolSidebar ( ) {
2545+ if ( ! embeddingPoolConfig ) {
2546+ return '<div class="embedding-pool-sidebar-info" style="padding: 1rem;">' +
2547+ '<div style="text-align: center; color: hsl(var(--muted-foreground)); font-size: 0.875rem;">' +
2548+ '<p>' + t ( 'apiSettings.embeddingPoolDesc' ) + '</p>' +
2549+ '</div>' +
2550+ '</div>' ;
2551+ }
2552+
2553+ const enabled = embeddingPoolConfig . enabled || false ;
2554+ const targetModel = embeddingPoolConfig . targetModel || '' ;
2555+ const strategy = embeddingPoolConfig . strategy || 'round_robin' ;
2556+ const excludedIds = embeddingPoolConfig . excludedProviderIds || [ ] ;
2557+
2558+ // Count total providers/keys
2559+ let totalProviders = embeddingPoolDiscoveredProviders . length ;
2560+ let totalKeys = 0 ;
2561+ let activeProviders = 0 ;
2562+
2563+ embeddingPoolDiscoveredProviders . forEach ( function ( p ) {
2564+ totalKeys += p . apiKeys ?. length || 1 ;
2565+ if ( excludedIds . indexOf ( p . providerId ) === - 1 ) {
2566+ activeProviders ++ ;
2567+ }
2568+ } ) ;
2569+
2570+ const strategyLabels = {
2571+ 'round_robin' : t ( 'codexlens.strategyRoundRobin' ) || 'Round Robin' ,
2572+ 'latency_aware' : t ( 'codexlens.strategyLatency' ) || 'Latency-Aware' ,
2573+ 'weighted_random' : t ( 'codexlens.strategyWeighted' ) || 'Weighted Random'
2574+ } ;
2575+
2576+ return '<div class="embedding-pool-sidebar-summary" style="padding: 1rem; display: flex; flex-direction: column; gap: 1rem;">' +
2577+ '<div style="padding: 1rem; background: hsl(var(--muted) / 0.3); border-radius: 0.5rem;">' +
2578+ '<h4 style="margin: 0 0 0.75rem 0; font-size: 0.875rem; font-weight: 600; color: hsl(var(--foreground));">' +
2579+ t ( 'apiSettings.embeddingPool' ) +
2580+ '</h4>' +
2581+ '<div style="display: flex; flex-direction: column; gap: 0.5rem; font-size: 0.75rem; color: hsl(var(--muted-foreground));">' +
2582+ '<div style="display: flex; align-items: center; gap: 0.5rem; padding: 0.375rem 0; border-bottom: 1px solid hsl(var(--border));">' +
2583+ '<i data-lucide="' + ( enabled ? 'check-circle' : 'x-circle' ) + '" style="width: 14px; height: 14px; color: ' + ( enabled ? 'hsl(var(--success))' : 'hsl(var(--muted-foreground))' ) + ';"></i>' +
2584+ '<span>' + ( enabled ? ( t ( 'common.enabled' ) || 'Enabled' ) : ( t ( 'common.disabled' ) || 'Disabled' ) ) + '</span>' +
2585+ '</div>' +
2586+ ( enabled && targetModel ?
2587+ '<div style="display: flex; flex-direction: column; gap: 0.25rem; padding: 0.375rem 0; border-bottom: 1px solid hsl(var(--border));">' +
2588+ '<span style="font-weight: 500; color: hsl(var(--foreground));">' + t ( 'apiSettings.targetModel' ) + '</span>' +
2589+ '<code style="font-size: 0.6875rem; color: hsl(var(--primary)); word-break: break-all;">' + targetModel + '</code>' +
2590+ '</div>' : '' ) +
2591+ ( enabled ?
2592+ '<div style="display: flex; flex-direction: column; gap: 0.25rem; padding: 0.375rem 0; border-bottom: 1px solid hsl(var(--border));">' +
2593+ '<span style="font-weight: 500; color: hsl(var(--foreground));">' + t ( 'apiSettings.strategy' ) + '</span>' +
2594+ '<span>' + ( strategyLabels [ strategy ] || strategy ) + '</span>' +
2595+ '</div>' : '' ) +
2596+ ( enabled && totalProviders > 0 ?
2597+ '<div style="display: flex; flex-direction: column; gap: 0.25rem; padding: 0.375rem 0;">' +
2598+ '<span style="font-weight: 500; color: hsl(var(--foreground));">' + t ( 'apiSettings.discoveredProviders' ) + '</span>' +
2599+ '<span>' + activeProviders + ' / ' + totalProviders + ' providers (' + totalKeys + ' keys)</span>' +
2600+ '</div>' : '' ) +
2601+ '</div>' +
2602+ '</div>' +
2603+ '</div>' ;
2604+ }
2605+
2606+
25362607/**
25372608 * Render embedding pool main panel
25382609 */
@@ -2567,47 +2638,56 @@ async function renderEmbeddingPoolMainPanel() {
25672638 '<p class="panel-subtitle">' + t ( 'apiSettings.embeddingPoolDesc' ) + '</p>' +
25682639 '</div>' +
25692640
2570- // Enable/Disable Toggle
2571- '<div class="settings-section">' +
2572- '<div class="section-header">' +
2573- '<h3>' + t ( 'apiSettings.poolEnabled' ) + '</h3>' +
2641+ // Enable/Disable Toggle Card
2642+ '<div class="settings-section" style="padding: 1.25rem; background: hsl(var(--muted) / 0.3); border-radius: 0.75rem;">' +
2643+ '<div class="section-header" style="border: none; padding: 0;">' +
2644+ '<div style="display: flex; align-items: center; gap: 0.5rem;">' +
2645+ '<i data-lucide="power" style="width: 1rem; height: 1rem; color: hsl(var(--primary));"></i>' +
2646+ '<h3 style="margin: 0;">' + t ( 'apiSettings.poolEnabled' ) + '</h3>' +
2647+ '</div>' +
25742648 '<label class="toggle-switch">' +
25752649 '<input type="checkbox" id="embedding-pool-enabled" ' + ( enabled ? 'checked' : '' ) + ' onchange="onEmbeddingPoolEnabledChange(this.checked)" />' +
25762650 '<span class="toggle-track"><span class="toggle-thumb"></span></span>' +
25772651 '</label>' +
25782652 '</div>' +
25792653 '</div>' +
25802654
2581- // Configuration Form
2655+ // Configuration Form Card
25822656 '<div class="settings-section" id="embedding-pool-config" style="' + ( enabled ? '' : 'display: none;' ) + '">' +
2583- '<div class="form-group">' +
2584- '<label for="embedding-pool-target-model">' + t ( 'apiSettings.targetModel' ) + '</label>' +
2585- '<select id="embedding-pool-target-model" class="cli-input" onchange="onTargetModelChange(this.value)">' +
2586- modelOptionsHtml +
2587- '</select>' +
2588- '</div>' +
2589-
2590- '<div class="form-group">' +
2591- '<label for="embedding-pool-strategy">' + t ( 'apiSettings.strategy' ) + '</label>' +
2592- '<select id="embedding-pool-strategy" class="cli-input">' +
2593- '<option value="round_robin"' + ( strategy === 'round_robin' ? ' selected' : '' ) + '>Round Robin</option>' +
2594- '<option value="latency_aware"' + ( strategy === 'latency_aware' ? ' selected' : '' ) + '>Latency Aware</option>' +
2595- '<option value="weighted_random"' + ( strategy === 'weighted_random' ? ' selected' : '' ) + '>Weighted Random</option>' +
2596- '</select>' +
2597- '</div>' +
2598-
2599- '<div class="form-group">' +
2600- '<label for="embedding-pool-cooldown">' + t ( 'apiSettings.defaultCooldown' ) + '</label>' +
2601- '<input type="number" id="embedding-pool-cooldown" class="cli-input" value="' + defaultCooldown + '" min="1" />' +
2657+ '<h3 style="margin: 0 0 1rem 0; font-size: 1rem; font-weight: 600;">' + t ( 'apiSettings.configuration' ) || 'Configuration' + '</h3>' +
2658+
2659+ // Model and Strategy in Grid
2660+ '<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; margin-bottom: 1rem;">' +
2661+ '<div class="form-group" style="margin: 0;">' +
2662+ '<label for="embedding-pool-target-model">' + t ( 'apiSettings.targetModel' ) + '</label>' +
2663+ '<select id="embedding-pool-target-model" class="cli-input" onchange="onTargetModelChange(this.value)">' +
2664+ modelOptionsHtml +
2665+ '</select>' +
2666+ '</div>' +
2667+ '<div class="form-group" style="margin: 0;">' +
2668+ '<label for="embedding-pool-strategy">' + t ( 'apiSettings.strategy' ) + '</label>' +
2669+ '<select id="embedding-pool-strategy" class="cli-input">' +
2670+ '<option value="round_robin"' + ( strategy === 'round_robin' ? ' selected' : '' ) + '>Round Robin</option>' +
2671+ '<option value="latency_aware"' + ( strategy === 'latency_aware' ? ' selected' : '' ) + '>Latency Aware</option>' +
2672+ '<option value="weighted_random"' + ( strategy === 'weighted_random' ? ' selected' : '' ) + '>Weighted Random</option>' +
2673+ '</select>' +
2674+ '</div>' +
26022675 '</div>' +
26032676
2604- '<div class="form-group">' +
2605- '<label for="embedding-pool-concurrent">' + t ( 'apiSettings.defaultConcurrent' ) + '</label>' +
2606- '<input type="number" id="embedding-pool-concurrent" class="cli-input" value="' + defaultMaxConcurrentPerKey + '" min="1" />' +
2677+ // Cooldown and Concurrent in Grid
2678+ '<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 1rem;">' +
2679+ '<div class="form-group" style="margin: 0;">' +
2680+ '<label for="embedding-pool-cooldown">' + t ( 'apiSettings.defaultCooldown' ) + ' (s)</label>' +
2681+ '<input type="number" id="embedding-pool-cooldown" class="cli-input" value="' + defaultCooldown + '" min="1" />' +
2682+ '</div>' +
2683+ '<div class="form-group" style="margin: 0;">' +
2684+ '<label for="embedding-pool-concurrent">' + t ( 'apiSettings.defaultConcurrent' ) + '</label>' +
2685+ '<input type="number" id="embedding-pool-concurrent" class="cli-input" value="' + defaultMaxConcurrentPerKey + '" min="1" />' +
2686+ '</div>' +
26072687 '</div>' +
26082688
26092689 // Discovered Providers Section
2610- '<div id="discovered-providers-section"></div>' +
2690+ '<div id="discovered-providers-section" style="margin-top: 1.5rem;" ></div>' +
26112691
26122692 '<div class="form-actions">' +
26132693 '<button class="btn btn-primary" onclick="saveEmbeddingPoolConfig()">' +
@@ -2675,32 +2755,43 @@ function renderDiscoveredProviders() {
26752755 totalKeys += p . apiKeys ?. length || 1 ;
26762756 } ) ;
26772757
2678- let providersHtml = '<div class="discovered-providers-list">' +
2679- '<div class="discovered-providers-header">' +
2680- '<h4>' + t ( 'apiSettings.discoveredProviders' ) + '</h4>' +
2681- '<span class="provider-count">' + totalProviders + ' providers, ' + totalKeys + ' keys</span>' +
2682- '</div>' ;
2758+ let providersHtml = '<div>' +
2759+ '<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 1rem;">' +
2760+ '<h4 style="margin: 0; font-size: 0.9375rem; font-weight: 600;">' + t ( 'apiSettings.discoveredProviders' ) + '</h4>' +
2761+ '<span style="font-size: 0.75rem; color: hsl(var(--muted-foreground)); padding: 0.25rem 0.625rem; background: hsl(var(--muted) / 0.5); border-radius: 9999px;">' +
2762+ totalProviders + ' providers, ' + totalKeys + ' keys' +
2763+ '</span>' +
2764+ '</div>' +
2765+ '<div style="display: flex; flex-direction: column; gap: 0.75rem;">' ;
26832766
2684- embeddingPoolDiscoveredProviders . forEach ( function ( provider ) {
2767+ embeddingPoolDiscoveredProviders . forEach ( function ( provider , index ) {
26852768 const isExcluded = excludedIds . indexOf ( provider . providerId ) > - 1 ;
2686- const icon = isExcluded ? 'x-circle' : 'check-circle' ;
26872769 const keyCount = provider . apiKeys ?. length || 1 ;
2688- const keyInfo = keyCount > 1 ? ' (' + keyCount + ' keys)' : '' ;
2689-
2690- providersHtml += '<div class="discovered-provider-item' + ( isExcluded ? ' excluded' : '' ) + '">' +
2691- '<div class="discovered-provider-info">' +
2692- '<i data-lucide="' + icon + '" class="provider-icon' + ( isExcluded ? ' excluded' : '' ) + '"></i>' +
2693- '<div>' +
2694- '<div class="discovered-provider-name">' + escapeHtml ( provider . providerName ) + '</div>' +
2695- '<div class="discovered-provider-keys">' + provider . modelName + keyInfo + '</div>' +
2696- '</div>' +
2697- '</div>' +
2698- '<div class="discovered-provider-actions">' +
2699- '<button class="btn btn-sm ' + ( isExcluded ? 'btn-primary' : 'btn-outline' ) + '" onclick="toggleProviderExclusion(\'' + provider . providerId + '\')">' +
2700- ( isExcluded ? t ( 'common.include' ) : t ( 'apiSettings.excludeProvider' ) ) +
2701- '</button>' +
2770+
2771+ // Get provider icon
2772+ let providerIcon = 'server' ;
2773+ if ( provider . providerName . toLowerCase ( ) . includes ( 'openai' ) ) providerIcon = 'brain' ;
2774+ else if ( provider . providerName . toLowerCase ( ) . includes ( 'modelscope' ) ) providerIcon = 'cpu' ;
2775+ else if ( provider . providerName . toLowerCase ( ) . includes ( 'azure' ) ) providerIcon = 'cloud' ;
2776+
2777+ providersHtml += '<div style="border: 1px solid hsl(var(--border)); border-radius: 0.5rem; padding: 1rem; ' +
2778+ ( isExcluded ? 'opacity: 0.5; background: hsl(var(--muted) / 0.3);' : 'background: hsl(var(--card));' ) + '">' +
2779+ '<div style="display: flex; align-items: center; justify-between; margin-bottom: 0.75rem;">' +
2780+ '<div style="display: flex; align-items: center; gap: 0.75rem;">' +
2781+ '<div style="width: 2rem; height: 2rem; border-radius: 0.375rem; background: ' + ( isExcluded ? 'hsl(var(--muted))' : 'hsl(var(--success) / 0.1)' ) + '; color: ' + ( isExcluded ? 'hsl(var(--muted-foreground))' : 'hsl(var(--success))' ) + '; display: flex; align-items: center; justify-content: center;">' +
2782+ '<i data-lucide="' + providerIcon + '" style="width: 1.125rem; height: 1.125rem;"></i>' +
2783+ '</div>' +
2784+ '<div>' +
2785+ '<div style="font-weight: 500; font-size: 0.875rem; color: hsl(var(--foreground));">' + escapeHtml ( provider . providerName ) + '</div>' +
2786+ '<div style="font-size: 0.75rem; color: hsl(var(--muted-foreground));">' + provider . modelName + ' · ' + keyCount + ' key' + ( keyCount > 1 ? 's' : '' ) + '</div>' +
2787+ '</div>' +
2788+ '</div>' +
2789+ '<button class="btn btn-sm ' + ( isExcluded ? 'btn-primary' : 'btn-outline' ) + '" onclick="toggleProviderExclusion(\'' + provider . providerId + '\')" style="flex-shrink: 0;">' +
2790+ '<i data-lucide="' + ( isExcluded ? 'plus' : 'x' ) + '" style="width: 0.875rem; height: 0.875rem;"></i> ' +
2791+ ( isExcluded ? t ( 'common.include' ) : t ( 'apiSettings.excludeProvider' ) ) +
2792+ '</button>' +
27022793 '</div>' +
2703- '</div>' ;
2794+ '</div>' ;
27042795 } ) ;
27052796
27062797 providersHtml += '</div>' ;
0 commit comments