' +
// SPLADE status hidden - not currently used
// '
' +
// '
' + t('common.loading') + '
' +
// '
' +
'
' +
// Model Management - Simplified with Embedding and Reranker sections
'
' +
'
' +
' ' + t('codexlens.models') +
'
' +
// Embedding Models
'
' +
'
' +
' Embedding Models' +
'
' +
'
' +
'
' + t('codexlens.loadingModels') + '
' +
'
' +
'
' +
// Reranker Models
'
' +
'
' +
' Reranker Models' +
'
' +
'
' +
'
' + t('common.loading') + '
' +
'
' +
'
' +
'
' +
// Danger Zone
'
' +
'
' +
' Danger Zone' +
'
' +
'
' +
'' +
'' +
'
' +
'
' +
'
' +
'
'
: '') +
'
' + // End Tab Content Container
'
' + // End modal-body
// Footer
'' +
'
';
}
/**
* Initialize CodexLens config modal event handlers
*/
function initCodexLensConfigEvents(currentConfig) {
// Tab switching
document.querySelectorAll('.codexlens-tab').forEach(function(tab) {
tab.onclick = function() {
// Remove active from all tabs
document.querySelectorAll('.codexlens-tab').forEach(function(t) {
t.classList.remove('active', 'border-primary', 'text-primary');
t.classList.add('border-transparent', 'text-muted-foreground');
});
// Hide all content
document.querySelectorAll('.codexlens-tab-content').forEach(function(c) {
c.classList.add('hidden');
c.classList.remove('active');
});
// Activate clicked tab
this.classList.add('active', 'border-primary', 'text-primary');
this.classList.remove('border-transparent', 'text-muted-foreground');
// Show corresponding content
var tabName = this.dataset.tab;
var content = document.querySelector('.codexlens-tab-content[data-tab="' + tabName + '"]');
if (content) {
content.classList.remove('hidden');
content.classList.add('active');
}
};
});
// Save button
var saveBtn = document.getElementById('saveCodexLensConfigBtn');
if (saveBtn) {
saveBtn.onclick = async function() {
var indexDirInput = document.getElementById('indexDirInput');
var apiMaxWorkersInput = document.getElementById('apiMaxWorkersInput');
var apiBatchSizeInput = document.getElementById('apiBatchSizeInput');
var newIndexDir = indexDirInput ? indexDirInput.value.trim() : '';
var newMaxWorkers = apiMaxWorkersInput ? parseInt(apiMaxWorkersInput.value) || 4 : 4;
var newBatchSize = apiBatchSizeInput ? parseInt(apiBatchSizeInput.value) || 8 : 8;
if (!newIndexDir) {
showRefreshToast(t('codexlens.pathEmpty'), 'error');
return;
}
// Check if anything changed
var hasChanges = newIndexDir !== currentConfig.index_dir ||
newMaxWorkers !== (currentConfig.api_max_workers || 4) ||
newBatchSize !== (currentConfig.api_batch_size || 8);
if (!hasChanges) {
closeModal();
return;
}
saveBtn.disabled = true;
saveBtn.innerHTML = '' + t('common.saving') + '';
try {
var response = await fetch('/api/codexlens/config', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
index_dir: newIndexDir,
api_max_workers: newMaxWorkers,
api_batch_size: newBatchSize
})
});
var result = await response.json();
if (result.success) {
showRefreshToast(t('codexlens.configSaved'), 'success');
closeModal();
// Refresh CodexLens status
if (typeof loadCodexLensStatus === 'function') {
await loadCodexLensStatus();
renderToolsSection();
if (window.lucide) lucide.createIcons();
}
} else {
showRefreshToast(t('common.saveFailed') + ': ' + result.error, 'error');
saveBtn.disabled = false;
saveBtn.innerHTML = ' ' + t('codexlens.saveConfig');
if (window.lucide) lucide.createIcons();
}
} catch (err) {
showRefreshToast(t('common.error') + ': ' + err.message, 'error');
saveBtn.disabled = false;
saveBtn.innerHTML = ' ' + t('codexlens.saveConfig');
if (window.lucide) lucide.createIcons();
}
};
}
// Test Search Button
var runSearchBtn = document.getElementById('runSearchBtn');
if (runSearchBtn) {
runSearchBtn.onclick = async function() {
var searchType = document.getElementById('searchTypeSelect').value;
var searchMode = document.getElementById('searchModeSelect').value;
var query = document.getElementById('searchQueryInput').value.trim();
var searchLimit = document.getElementById('searchLimitInput')?.value || '5';
var contentLength = document.getElementById('contentLengthInput')?.value || '200';
var extraFiles = document.getElementById('extraFilesInput')?.value || '10';
var resultsDiv = document.getElementById('searchResults');
var resultCount = document.getElementById('searchResultCount');
var resultContent = document.getElementById('searchResultContent');
if (!query) {
showRefreshToast(t('codexlens.enterQuery'), 'warning');
return;
}
runSearchBtn.disabled = true;
runSearchBtn.innerHTML = '' + t('codexlens.searching') + '';
resultsDiv.classList.add('hidden');
try {
var endpoint = '/api/codexlens/' + searchType;
var params = new URLSearchParams({
query: query,
limit: searchLimit,
max_content_length: contentLength,
extra_files_count: extraFiles
});
// Add mode parameter for search and search_files (not for symbol search)
if (searchType === 'search' || searchType === 'search_files') {
params.append('mode', searchMode);
}
var response = await fetch(endpoint + '?' + params.toString());
var result = await response.json();
console.log('[CodexLens Test] Search result:', result);
if (result.success) {
var results = result.results || result.files || [];
resultCount.textContent = results.length + ' ' + t('codexlens.resultsCount');
resultContent.textContent = JSON.stringify(results, null, 2);
resultsDiv.classList.remove('hidden');
showRefreshToast(t('codexlens.searchCompleted') + ': ' + results.length + ' ' + t('codexlens.resultsCount'), 'success');
} else {
resultContent.textContent = t('common.error') + ': ' + (result.error || t('common.unknownError'));
resultsDiv.classList.remove('hidden');
showRefreshToast(t('codexlens.searchFailed') + ': ' + result.error, 'error');
}
runSearchBtn.disabled = false;
runSearchBtn.innerHTML = ' ' + t('codexlens.runSearch');
if (window.lucide) lucide.createIcons();
} catch (err) {
console.error('[CodexLens Test] Error:', err);
resultContent.textContent = t('common.exception') + ': ' + err.message;
resultsDiv.classList.remove('hidden');
showRefreshToast(t('common.error') + ': ' + err.message, 'error');
runSearchBtn.disabled = false;
runSearchBtn.innerHTML = ' ' + t('codexlens.runSearch');
if (window.lucide) lucide.createIcons();
}
};
}
// Load semantic dependencies status
loadSemanticDepsStatus();
// SPLADE status hidden - not currently used
// loadSpladeStatus();
// Load model lists (embedding and reranker)
loadModelList();
loadRerankerModelList();
}
// ============================================================
// MODEL LOCK/UNLOCK MANAGEMENT
// ============================================================
var MODEL_LOCK_KEY = 'codexlens_model_lock';
/**
* Get model lock state from localStorage
* @returns {Object} { locked: boolean, backend: string, model: string }
*/
function getModelLockState() {
try {
var stored = localStorage.getItem(MODEL_LOCK_KEY);
if (stored) {
return JSON.parse(stored);
}
} catch (e) {
console.warn('[CodexLens] Failed to get model lock state:', e);
}
return { locked: false, backend: 'fastembed', model: 'code' };
}
/**
* Set model lock state in localStorage
* @param {boolean} locked - Whether model is locked
* @param {string} backend - Selected backend
* @param {string} model - Selected model
*/
function setModelLockState(locked, backend, model) {
try {
localStorage.setItem(MODEL_LOCK_KEY, JSON.stringify({
locked: locked,
backend: backend || 'fastembed',
model: model || 'code'
}));
} catch (e) {
console.warn('[CodexLens] Failed to save model lock state:', e);
}
}
/**
* Toggle model lock state
*/
function toggleModelLock() {
var backendSelect = document.getElementById('pageBackendSelect');
var modelSelect = document.getElementById('pageModelSelect');
var lockBtn = document.getElementById('modelLockBtn');
var lockIcon = document.getElementById('modelLockIcon');
var currentState = getModelLockState();
var newLocked = !currentState.locked;
// Get current values if locking
var backend = newLocked ? (backendSelect ? backendSelect.value : 'fastembed') : currentState.backend;
var model = newLocked ? (modelSelect ? modelSelect.value : 'code') : currentState.model;
// Save state
setModelLockState(newLocked, backend, model);
// Update UI
applyModelLockUI(newLocked, backend, model);
// Show feedback
if (newLocked) {
showRefreshToast('Model locked: ' + backend + ' / ' + model, 'success');
} else {
showRefreshToast('Model unlocked', 'info');
}
}
/**
* Apply model lock UI state
*/
function applyModelLockUI(locked, backend, model) {
var backendSelect = document.getElementById('pageBackendSelect');
var modelSelect = document.getElementById('pageModelSelect');
var lockBtn = document.getElementById('modelLockBtn');
var lockIcon = document.getElementById('modelLockIcon');
var lockText = document.getElementById('modelLockText');
if (backendSelect) {
backendSelect.disabled = locked;
if (locked && backend) {
backendSelect.value = backend;
}
}
if (modelSelect) {
modelSelect.disabled = locked;
if (locked && model) {
modelSelect.value = model;
}
}
if (lockBtn) {
if (locked) {
lockBtn.classList.remove('btn-outline');
lockBtn.classList.add('btn-primary');
} else {
lockBtn.classList.remove('btn-primary');
lockBtn.classList.add('btn-outline');
}
}
if (lockIcon) {
lockIcon.setAttribute('data-lucide', locked ? 'lock' : 'unlock');
if (window.lucide) lucide.createIcons();
}
if (lockText) {
lockText.textContent = locked ? 'Locked' : 'Lock Model';
}
}
/**
* Initialize model lock state on page load
*/
function initModelLockState() {
var state = getModelLockState();
if (state.locked) {
applyModelLockUI(true, state.backend, state.model);
}
}
// Make functions globally accessible
window.toggleModelLock = toggleModelLock;
window.initModelLockState = initModelLockState;
window.getModelLockState = getModelLockState;
// ============================================================
// ENVIRONMENT VARIABLES MANAGEMENT
// ============================================================
// Environment variable groups for organized display
var ENV_VAR_GROUPS = {
backend: {
label: 'Backend Selection',
icon: 'toggle-left',
vars: {
'CODEXLENS_EMBEDDING_BACKEND': { label: 'Embedding Backend', type: 'select', options: ['fastembed', 'litellm'], default: 'fastembed' },
'CODEXLENS_RERANKER_BACKEND': { label: 'Reranker Backend', type: 'select', options: ['fastembed', 'litellm'], default: 'fastembed' }
}
},
local: {
label: 'Local Model Settings',
icon: 'hard-drive',
showWhen: function(env) { return env['CODEXLENS_EMBEDDING_BACKEND'] !== 'litellm' || env['CODEXLENS_RERANKER_BACKEND'] !== 'litellm'; },
vars: {
'CODEXLENS_EMBEDDING_MODEL': { label: 'Embedding Model', placeholder: 'fast (code, base, minilm, multilingual, balanced)' },
'CODEXLENS_RERANKER_MODEL': { label: 'Reranker Model', placeholder: 'Xenova/ms-marco-MiniLM-L-6-v2' }
}
},
api: {
label: 'API Settings (LiteLLM)',
icon: 'cloud',
showWhen: function(env) { return env['CODEXLENS_EMBEDDING_BACKEND'] === 'litellm' || env['CODEXLENS_RERANKER_BACKEND'] === 'litellm'; },
vars: {
'LITELLM_API_KEY': { label: 'API Key', placeholder: 'sk-...', type: 'password' },
'LITELLM_API_BASE': { label: 'API Base URL', placeholder: 'https://api.openai.com/v1' },
'LITELLM_EMBEDDING_MODEL': {
label: 'Embedding Model',
type: 'model-select',
placeholder: 'Select or enter model...',
models: [
{ group: 'OpenAI', items: ['text-embedding-3-small', 'text-embedding-3-large', 'text-embedding-ada-002'] },
{ group: 'Cohere', items: ['embed-english-v3.0', 'embed-multilingual-v3.0', 'embed-english-light-v3.0'] },
{ group: 'Voyage', items: ['voyage-3', 'voyage-3-lite', 'voyage-code-3', 'voyage-multilingual-2'] },
{ group: 'SiliconFlow', items: ['BAAI/bge-m3', 'BAAI/bge-large-zh-v1.5', 'BAAI/bge-large-en-v1.5'] },
{ group: 'Jina', items: ['jina-embeddings-v3', 'jina-embeddings-v2-base-en', 'jina-embeddings-v2-base-zh'] }
]
},
'LITELLM_RERANKER_MODEL': {
label: 'Reranker Model',
type: 'model-select',
placeholder: 'Select or enter model...',
models: [
{ group: 'Cohere', items: ['rerank-english-v3.0', 'rerank-multilingual-v3.0', 'rerank-english-v2.0'] },
{ group: 'Voyage', items: ['rerank-2', 'rerank-2-lite', 'rerank-1'] },
{ group: 'SiliconFlow', items: ['BAAI/bge-reranker-v2-m3', 'BAAI/bge-reranker-large', 'BAAI/bge-reranker-base'] },
{ group: 'Jina', items: ['jina-reranker-v2-base-multilingual', 'jina-reranker-v1-base-en'] }
]
}
}
}
};
/**
* Load environment variables from ~/.codexlens/.env
*/
async function loadEnvVariables() {
var container = document.getElementById('envVarsContainer');
if (!container) return;
container.innerHTML = '
Loading...
';
try {
// Fetch env vars and configured models in parallel
var [envResponse, embeddingPoolResponse, rerankerPoolResponse] = await Promise.all([
fetch('/api/codexlens/env'),
fetch('/api/litellm-api/embedding-pool').catch(function() { return null; }),
fetch('/api/litellm-api/reranker-pool').catch(function() { return null; })
]);
var result = await envResponse.json();
if (!result.success) {
container.innerHTML = '
' + escapeHtml(result.error || 'Failed to load') + '
';
return;
}
// Get configured embedding models from API settings
var configuredEmbeddingModels = [];
if (embeddingPoolResponse && embeddingPoolResponse.ok) {
var poolData = await embeddingPoolResponse.json();
configuredEmbeddingModels = poolData.availableModels || [];
}
// Get configured reranker models from API settings
var configuredRerankerModels = [];
if (rerankerPoolResponse && rerankerPoolResponse.ok) {
var rerankerData = await rerankerPoolResponse.json();
configuredRerankerModels = rerankerData.availableModels || [];
}
var env = result.env || {};
var html = '
';
// Get available LiteLLM providers
var litellmProviders = window.litellmApiConfig?.providers || [];
// Render each group
for (var groupKey in ENV_VAR_GROUPS) {
var group = ENV_VAR_GROUPS[groupKey];
// Check if this group should be shown
if (group.showWhen && !group.showWhen(env)) {
continue;
}
html += '
' +
'
' +
'' +
group.label +
'
' +
'
';
// Add provider selector for API group
if (groupKey === 'api' && litellmProviders.length > 0) {
html += '
' +
'' +
'
';
}
for (var key in group.vars) {
var config = group.vars[key];
var value = env[key] || config.default || '';
if (config.type === 'select') {
html += '
' +
'' +
'
';
} else if (config.type === 'model-select') {
// Model selector with grouped options and custom input support
var datalistId = 'models-' + key.replace(/_/g, '-').toLowerCase();
var isEmbeddingModel = key === 'LITELLM_EMBEDDING_MODEL';
var isRerankerModel = key === 'LITELLM_RERANKER_MODEL';
html += '
' +
'' +
'
' +
'' +
'' +
'
' +
'
';
} else {
var inputType = config.type || 'text';
html += '
';
modes.forEach(function(mode) {
var isDisabled = !mode.available;
var isRecommended = mode.recommended;
var isDefault = mode.id === gpuInfo.mode;
var hasWarning = mode.warning;
html +=
'';
});
html +=
'
' +
'
';
return html;
}
/**
* Get selected GPU mode
*/
function getSelectedGpuMode() {
var selected = document.querySelector('input[name="gpuMode"]:checked');
return selected ? selected.value : 'cpu';
}
/**
* Load available GPU devices
*/
async function loadGpuDevices() {
try {
var response = await fetch('/api/codexlens/gpu/list');
var result = await response.json();
if (result.success && result.result) {
availableGpuDevices = result.result;
return result.result;
}
} catch (err) {
console.error('GPU devices load failed:', err);
}
return { devices: [], selected_device_id: null };
}
/**
* Build GPU device selector HTML
*/
function buildGpuDeviceSelector(gpuDevices) {
if (!gpuDevices || !gpuDevices.devices || gpuDevices.devices.length === 0) {
return '';
}
// Only show selector if there are multiple GPUs
if (gpuDevices.devices.length < 2) {
return '';
}
var html =
'
';
if (window.lucide) lucide.createIcons();
try {
var response = await fetch('/api/codexlens/splade/install', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ gpu: gpu })
});
var result = await response.json();
if (result.success) {
showRefreshToast(t('codexlens.spladeInstallSuccess'), 'success');
loadSpladeStatus();
} else {
showRefreshToast(t('codexlens.spladeInstallFailed') + ': ' + result.error, 'error');
loadSpladeStatus();
}
} catch (err) {
showRefreshToast(t('common.error') + ': ' + err.message, 'error');
loadSpladeStatus();
}
}
*/
// ============================================================
// MODEL MANAGEMENT
// ============================================================
/**
* Copy text to clipboard
*/
function copyToClipboard(text) {
navigator.clipboard.writeText(text).then(function() {
showRefreshToast(t('common.copied') || 'Copied to clipboard', 'success');
}).catch(function(err) {
console.error('Failed to copy:', err);
});
}
/**
* Load model list (simplified version)
*/
async function loadModelList() {
var container = document.getElementById('modelListContainer');
if (!container) return;
try {
// Get config for backend info
var configResponse = await fetch('/api/codexlens/config');
var config = await configResponse.json();
var embeddingBackend = config.embedding_backend || 'fastembed';
var response = await fetch('/api/codexlens/models');
var result = await response.json();
var html = '
';
// Show current backend status
var backendLabel = embeddingBackend === 'litellm' ? 'API (LiteLLM)' : 'Local (FastEmbed)';
var backendIcon = embeddingBackend === 'litellm' ? 'cloud' : 'hard-drive';
html +=
'
' +
'
' +
'' +
'' + backendLabel + '' +
'
' +
'via Environment Variables' +
'
';
if (!result.success) {
var errorMsg = result.error || '';
if (errorMsg.includes('fastembed not installed') || errorMsg.includes('Semantic')) {
html += '
';
});
} else {
// LiteLLM backend - show API info
html +=
'
' +
'' +
'
Using API embeddings
' +
'
Model configured via CODEXLENS_EMBEDDING_MODEL
' +
'
';
}
html += '';
container.innerHTML = html;
if (window.lucide) lucide.createIcons();
} catch (err) {
container.innerHTML =
'
' + escapeHtml(err.message) + '
';
}
}
/**
* Download model (simplified version)
*/
async function downloadModel(profile) {
var modelCard = document.getElementById('model-' + profile);
if (!modelCard) return;
var originalHTML = modelCard.innerHTML;
// Show loading state
modelCard.innerHTML =
'
' +
'
' +
'' +
'Downloading ' + profile + '...' +
'
' +
'' +
'
';
try {
var response = await fetch('/api/codexlens/models/download', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ profile: profile })
});
var result = await response.json();
if (result.success) {
showRefreshToast('Model downloaded: ' + profile, 'success');
loadModelList();
} else {
showRefreshToast('Download failed: ' + result.error, 'error');
modelCard.innerHTML = originalHTML;
if (window.lucide) lucide.createIcons();
}
} catch (err) {
showRefreshToast('Error: ' + err.message, 'error');
modelCard.innerHTML = originalHTML;
if (window.lucide) lucide.createIcons();
}
}
/**
* Delete model (simplified)
*/
async function deleteModel(profile) {
if (!confirm('Delete model ' + profile + '?')) {
return;
}
var modelCard = document.getElementById('model-' + profile);
if (!modelCard) return;
var originalHTML = modelCard.innerHTML;
modelCard.innerHTML =
'
' +
'
' +
'' +
'Deleting...' +
'
' +
'
';
try {
var response = await fetch('/api/codexlens/models/delete', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ profile: profile })
});
var result = await response.json();
if (result.success) {
showRefreshToast('Model deleted: ' + profile, 'success');
loadModelList();
} else {
showRefreshToast('Delete failed: ' + result.error, 'error');
modelCard.innerHTML = originalHTML;
if (window.lucide) lucide.createIcons();
}
} catch (err) {
showRefreshToast('Error: ' + err.message, 'error');
modelCard.innerHTML = originalHTML;
if (window.lucide) lucide.createIcons();
}
}
// ============================================================
// RERANKER MODEL MANAGEMENT
// ============================================================
// Available reranker models (fastembed TextCrossEncoder)
var RERANKER_MODELS = [
{ id: 'ms-marco-mini', name: 'Xenova/ms-marco-MiniLM-L-6-v2', size: 80, desc: 'Fast, lightweight' },
{ id: 'ms-marco-12', name: 'Xenova/ms-marco-MiniLM-L-12-v2', size: 120, desc: 'Better accuracy' },
{ id: 'bge-base', name: 'BAAI/bge-reranker-base', size: 1040, desc: 'High quality' },
{ id: 'jina-tiny', name: 'jinaai/jina-reranker-v1-tiny-en', size: 130, desc: 'Tiny, fast' },
{ id: 'jina-turbo', name: 'jinaai/jina-reranker-v1-turbo-en', size: 150, desc: 'Balanced' }
];
/**
* Load reranker model list
*/
async function loadRerankerModelList() {
var container = document.getElementById('rerankerModelListContainer');
if (!container) return;
try {
// Get current reranker config
var response = await fetch('/api/codexlens/reranker/config');
var config = await response.json();
var currentModel = config.model_name || 'Xenova/ms-marco-MiniLM-L-6-v2';
var currentBackend = config.backend || 'fastembed';
var html = '
';
// Show current backend status
var backendLabel = currentBackend === 'litellm' ? 'API (LiteLLM)' : 'Local (FastEmbed)';
var backendIcon = currentBackend === 'litellm' ? 'cloud' : 'hard-drive';
html +=
'
' +
'
' +
'' +
'' + backendLabel + '' +
'
' +
'via Environment Variables' +
'
';
// Show models for local backend only
if (currentBackend === 'fastembed' || currentBackend === 'onnx') {
RERANKER_MODELS.forEach(function(model) {
var isActive = currentModel === model.name;
var statusIcon = isActive
? ''
: '';
var actionBtn = isActive
? 'Active'
: '';
html +=
'
';
document.body.appendChild(modal);
if (window.lucide) lucide.createIcons();
}
function closeCodexLensUninstallDialogFallback() {
var modal = document.getElementById('codexlensUninstallModalFallback');
if (modal) modal.remove();
}
async function startCodexLensUninstallFallback() {
var progressDiv = document.getElementById('codexlensUninstallProgressFallback');
var uninstallBtn = document.getElementById('codexlensUninstallBtnFallback');
var statusText = document.getElementById('codexlensUninstallStatusFallback');
var progressBar = document.getElementById('codexlensUninstallProgressBarFallback');
progressDiv.classList.remove('hidden');
uninstallBtn.disabled = true;
uninstallBtn.innerHTML = '' + t('codexlens.uninstalling') + '';
var stages = [
{ progress: 25, text: t('codexlens.removingVenv') },
{ progress: 50, text: t('codexlens.removingData') },
{ progress: 75, text: t('codexlens.removingConfig') },
{ progress: 90, text: t('codexlens.finalizing') }
];
var currentStage = 0;
var progressInterval = setInterval(function() {
if (currentStage < stages.length) {
statusText.textContent = stages[currentStage].text;
progressBar.style.width = stages[currentStage].progress + '%';
currentStage++;
}
}, 500);
try {
var response = await fetch('/api/codexlens/uninstall', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({})
});
clearInterval(progressInterval);
var result = await response.json();
if (result.success) {
progressBar.style.width = '100%';
statusText.textContent = t('codexlens.uninstallComplete');
setTimeout(function() {
closeCodexLensUninstallDialogFallback();
showRefreshToast(t('codexlens.uninstallSuccess'), 'success');
// Refresh the page to update status
if (typeof loadCodexLensStatus === 'function') {
loadCodexLensStatus().then(function() {
if (typeof renderCodexLensManager === 'function') renderCodexLensManager();
});
} else {
location.reload();
}
}, 1000);
} else {
statusText.textContent = t('common.error') + ': ' + result.error;
progressBar.classList.add('bg-destructive');
uninstallBtn.disabled = false;
uninstallBtn.innerHTML = ' ' + t('common.retry');
if (window.lucide) lucide.createIcons();
}
} catch (err) {
clearInterval(progressInterval);
statusText.textContent = t('common.error') + ': ' + err.message;
progressBar.classList.add('bg-destructive');
uninstallBtn.disabled = false;
uninstallBtn.innerHTML = ' ' + t('common.retry');
if (window.lucide) lucide.createIcons();
}
}
/**
* Clean current workspace index
*/
async function cleanCurrentWorkspaceIndex() {
if (!confirm(t('codexlens.cleanCurrentWorkspaceConfirm'))) {
return;
}
try {
showRefreshToast(t('codexlens.cleaning'), 'info');
// Get current workspace path (projectPath is a global variable from state.js)
var workspacePath = projectPath;
var response = await fetch('/api/codexlens/clean', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ path: workspacePath })
});
var result = await response.json();
if (result.success) {
showRefreshToast(t('codexlens.cleanCurrentWorkspaceSuccess'), 'success');
// Refresh status
if (typeof loadCodexLensStatus === 'function') {
await loadCodexLensStatus();
renderToolsSection();
if (window.lucide) lucide.createIcons();
}
} else {
showRefreshToast(t('codexlens.cleanFailed') + ': ' + result.error, 'error');
}
} catch (err) {
showRefreshToast(t('common.error') + ': ' + err.message, 'error');
}
}
/**
* Clean all CodexLens indexes
*/
async function cleanCodexLensIndexes() {
if (!confirm(t('codexlens.cleanConfirm'))) {
return;
}
try {
showRefreshToast(t('codexlens.cleaning'), 'info');
var response = await fetch('/api/codexlens/clean', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ all: true })
});
var result = await response.json();
if (result.success) {
showRefreshToast(t('codexlens.cleanSuccess'), 'success');
// Refresh status
if (typeof loadCodexLensStatus === 'function') {
await loadCodexLensStatus();
renderToolsSection();
if (window.lucide) lucide.createIcons();
}
} else {
showRefreshToast(t('codexlens.cleanFailed') + ': ' + result.error, 'error');
}
} catch (err) {
showRefreshToast(t('common.error') + ': ' + err.message, 'error');
}
}
// ============================================================
// CODEXLENS MANAGER PAGE (Independent View)
// ============================================================
/**
* Render CodexLens Manager as an independent page view
*/
async function renderCodexLensManager() {
var container = document.getElementById('mainContent');
if (!container) return;
// Hide stats grid and search
var statsGrid = document.getElementById('statsGrid');
var searchContainer = document.querySelector('.search-container');
if (statsGrid) statsGrid.style.display = 'none';
if (searchContainer) searchContainer.style.display = 'none';
container.innerHTML = '
' + t('common.loading') + '
';
try {
// Use aggregated endpoint for faster page load (single API call)
var dashboardData = null;
var config = { index_dir: '~/.codexlens/indexes', index_count: 0 };
if (typeof loadCodexLensDashboardInit === 'function') {
console.log('[CodexLens] Using aggregated dashboard-init endpoint...');
dashboardData = await loadCodexLensDashboardInit();
if (dashboardData && dashboardData.config) {
config = dashboardData.config;
console.log('[CodexLens] Dashboard init loaded, config:', config);
}
} else if (typeof loadCodexLensStatus === 'function') {
// Fallback to legacy individual calls
console.log('[CodexLens] Fallback to legacy loadCodexLensStatus...');
await loadCodexLensStatus();
var response = await fetch('/api/codexlens/config');
config = await response.json();
}
// Load LiteLLM API config for embedding backend options (parallel with page render)
var litellmPromise = (async () => {
try {
console.log('[CodexLens] Loading LiteLLM config...');
var litellmResponse = await fetch('/api/litellm-api/config');
if (litellmResponse.ok) {
window.litellmApiConfig = await litellmResponse.json();
console.log('[CodexLens] LiteLLM config loaded, providers:', window.litellmApiConfig?.providers?.length || 0);
}
} catch (e) {
console.warn('[CodexLens] Could not load LiteLLM config:', e);
}
})();
container.innerHTML = buildCodexLensManagerPage(config);
if (window.lucide) lucide.createIcons();
initCodexLensManagerPageEvents(config);
// Load additional data in parallel (non-blocking)
var isInstalled = window.cliToolsStatus?.codexlens?.installed || dashboardData?.installed;
// Wait for LiteLLM config before loading semantic deps (it may need provider info)
await litellmPromise;
// Always load semantic deps status - it needs GPU detection and device list
// which are not included in the aggregated endpoint
loadSemanticDepsStatus();
loadModelList();
loadRerankerModelList();
// Initialize model mode and semantic status badge
updateSemanticStatusBadge();
loadGpuDevicesForModeSelector();
// Initialize file watcher status
initWatcherStatus();
// Load index stats for the Index Manager section
if (isInstalled) {
loadIndexStatsForPage();
// Check index health based on git history
checkIndexHealth();
}
} catch (err) {
container.innerHTML = '