claude-code-workflow 6.2.4 → 6.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ccw/dist/core/lite-scanner-complete.d.ts.map +1 -1
- package/ccw/dist/core/lite-scanner-complete.js +4 -1
- package/ccw/dist/core/lite-scanner-complete.js.map +1 -1
- package/ccw/dist/core/lite-scanner.d.ts.map +1 -1
- package/ccw/dist/core/lite-scanner.js +4 -1
- package/ccw/dist/core/lite-scanner.js.map +1 -1
- package/ccw/dist/core/routes/claude-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/claude-routes.js +3 -5
- package/ccw/dist/core/routes/claude-routes.js.map +1 -1
- package/ccw/dist/core/routes/cli-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/cli-routes.js +2 -1
- package/ccw/dist/core/routes/cli-routes.js.map +1 -1
- package/ccw/dist/core/routes/codexlens-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/codexlens-routes.js +31 -6
- package/ccw/dist/core/routes/codexlens-routes.js.map +1 -1
- package/ccw/dist/core/routes/rules-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/rules-routes.js +4 -3
- package/ccw/dist/core/routes/rules-routes.js.map +1 -1
- package/ccw/dist/core/routes/skills-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/skills-routes.js +124 -6
- package/ccw/dist/core/routes/skills-routes.js.map +1 -1
- package/ccw/dist/tools/cli-executor.d.ts +4 -1
- package/ccw/dist/tools/cli-executor.d.ts.map +1 -1
- package/ccw/dist/tools/cli-executor.js +54 -2
- package/ccw/dist/tools/cli-executor.js.map +1 -1
- package/ccw/dist/tools/codex-lens.d.ts +20 -3
- package/ccw/dist/tools/codex-lens.d.ts.map +1 -1
- package/ccw/dist/tools/codex-lens.js +166 -37
- package/ccw/dist/tools/codex-lens.js.map +1 -1
- package/ccw/package.json +1 -1
- package/ccw/src/core/lite-scanner-complete.ts +5 -1
- package/ccw/src/core/lite-scanner.ts +5 -1
- package/ccw/src/core/routes/claude-routes.ts +3 -5
- package/ccw/src/core/routes/cli-routes.ts +2 -1
- package/ccw/src/core/routes/codexlens-routes.ts +34 -6
- package/ccw/src/core/routes/rules-routes.ts +4 -3
- package/ccw/src/core/routes/skills-routes.ts +144 -6
- package/ccw/src/templates/dashboard-js/components/mcp-manager.js +7 -12
- package/ccw/src/templates/dashboard-js/i18n.js +167 -5
- package/ccw/src/templates/dashboard-js/views/claude-manager.js +18 -4
- package/ccw/src/templates/dashboard-js/views/cli-manager.js +5 -3
- package/ccw/src/templates/dashboard-js/views/codexlens-manager.js +790 -25
- package/ccw/src/templates/dashboard-js/views/rules-manager.js +35 -6
- package/ccw/src/templates/dashboard-js/views/skills-manager.js +385 -21
- package/ccw/src/tools/cli-executor.ts +70 -2
- package/ccw/src/tools/codex-lens.ts +183 -35
- package/codex-lens/pyproject.toml +66 -48
- package/codex-lens/src/codexlens/__pycache__/config.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__pycache__/embedding_manager.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__pycache__/model_manager.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/embedding_manager.py +3 -3
- package/codex-lens/src/codexlens/cli/model_manager.py +24 -2
- package/codex-lens/src/codexlens/search/__pycache__/hybrid_search.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/hybrid_search.py +313 -313
- package/codex-lens/src/codexlens/semantic/__init__.py +76 -39
- package/codex-lens/src/codexlens/semantic/__pycache__/__init__.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/embedder.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/gpu_support.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/ollama_backend.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/embedder.py +244 -185
- package/codex-lens/src/codexlens/semantic/gpu_support.py +192 -0
- package/package.json +1 -1
|
@@ -126,10 +126,10 @@ function buildCodexLensConfigContent(config) {
|
|
|
126
126
|
'<button class="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md border border-border bg-background hover:bg-muted/50 transition-colors" onclick="cleanCodexLensIndexes()">' +
|
|
127
127
|
'<i data-lucide="trash" class="w-3.5 h-3.5"></i> ' + t('codexlens.cleanAllIndexes') +
|
|
128
128
|
'</button>' +
|
|
129
|
-
'<button class="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md border border-destructive/30 bg-destructive/5 text-destructive hover:bg-destructive/10 transition-colors" onclick="
|
|
129
|
+
'<button class="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md border border-destructive/30 bg-destructive/5 text-destructive hover:bg-destructive/10 transition-colors" onclick="uninstallCodexLensFromManager()">' +
|
|
130
130
|
'<i data-lucide="trash-2" class="w-3.5 h-3.5"></i> ' + t('cli.uninstall') +
|
|
131
131
|
'</button>'
|
|
132
|
-
: '<button class="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md bg-primary text-primary-foreground hover:bg-primary/90 transition-colors" onclick="
|
|
132
|
+
: '<button class="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md bg-primary text-primary-foreground hover:bg-primary/90 transition-colors" onclick="installCodexLensFromManager()">' +
|
|
133
133
|
'<i data-lucide="download" class="w-3.5 h-3.5"></i> ' + t('codexlens.installCodexLens') +
|
|
134
134
|
'</button>') +
|
|
135
135
|
'</div>' +
|
|
@@ -335,6 +335,26 @@ function initCodexLensConfigEvents(currentConfig) {
|
|
|
335
335
|
// SEMANTIC DEPENDENCIES MANAGEMENT
|
|
336
336
|
// ============================================================
|
|
337
337
|
|
|
338
|
+
// Store detected GPU info
|
|
339
|
+
var detectedGpuInfo = null;
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Detect GPU support
|
|
343
|
+
*/
|
|
344
|
+
async function detectGpuSupport() {
|
|
345
|
+
try {
|
|
346
|
+
var response = await fetch('/api/codexlens/gpu/detect');
|
|
347
|
+
var result = await response.json();
|
|
348
|
+
if (result.success) {
|
|
349
|
+
detectedGpuInfo = result;
|
|
350
|
+
return result;
|
|
351
|
+
}
|
|
352
|
+
} catch (err) {
|
|
353
|
+
console.error('GPU detection failed:', err);
|
|
354
|
+
}
|
|
355
|
+
return { mode: 'cpu', available: ['cpu'], info: 'CPU only' };
|
|
356
|
+
}
|
|
357
|
+
|
|
338
358
|
/**
|
|
339
359
|
* Load semantic dependencies status
|
|
340
360
|
*/
|
|
@@ -343,24 +363,58 @@ async function loadSemanticDepsStatus() {
|
|
|
343
363
|
if (!container) return;
|
|
344
364
|
|
|
345
365
|
try {
|
|
366
|
+
// Detect GPU support in parallel
|
|
367
|
+
var gpuPromise = detectGpuSupport();
|
|
346
368
|
var response = await fetch('/api/codexlens/semantic/status');
|
|
347
369
|
var result = await response.json();
|
|
370
|
+
var gpuInfo = await gpuPromise;
|
|
348
371
|
|
|
349
372
|
if (result.available) {
|
|
373
|
+
// Build accelerator badge
|
|
374
|
+
var accelerator = result.accelerator || 'CPU';
|
|
375
|
+
var acceleratorIcon = 'cpu';
|
|
376
|
+
var acceleratorClass = 'bg-muted text-muted-foreground';
|
|
377
|
+
|
|
378
|
+
if (accelerator === 'CUDA') {
|
|
379
|
+
acceleratorIcon = 'zap';
|
|
380
|
+
acceleratorClass = 'bg-green-500/20 text-green-600';
|
|
381
|
+
} else if (accelerator === 'DirectML') {
|
|
382
|
+
acceleratorIcon = 'gpu-card';
|
|
383
|
+
acceleratorClass = 'bg-blue-500/20 text-blue-600';
|
|
384
|
+
} else if (accelerator === 'ROCm') {
|
|
385
|
+
acceleratorIcon = 'flame';
|
|
386
|
+
acceleratorClass = 'bg-red-500/20 text-red-600';
|
|
387
|
+
}
|
|
388
|
+
|
|
350
389
|
container.innerHTML =
|
|
351
|
-
'<div class="
|
|
352
|
-
'<
|
|
353
|
-
|
|
354
|
-
|
|
390
|
+
'<div class="space-y-2">' +
|
|
391
|
+
'<div class="flex items-center gap-2 text-sm">' +
|
|
392
|
+
'<i data-lucide="check-circle" class="w-4 h-4 text-success"></i>' +
|
|
393
|
+
'<span>' + t('codexlens.semanticInstalled') + '</span>' +
|
|
394
|
+
'<span class="text-muted-foreground">(' + (result.backend || 'fastembed') + ')</span>' +
|
|
395
|
+
'</div>' +
|
|
396
|
+
'<div class="flex items-center gap-2">' +
|
|
397
|
+
'<span class="inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs font-medium ' + acceleratorClass + '">' +
|
|
398
|
+
'<i data-lucide="' + acceleratorIcon + '" class="w-3 h-3"></i>' +
|
|
399
|
+
accelerator +
|
|
400
|
+
'</span>' +
|
|
401
|
+
(result.providers && result.providers.length > 0
|
|
402
|
+
? '<span class="text-xs text-muted-foreground">' + result.providers.join(', ') + '</span>'
|
|
403
|
+
: '') +
|
|
404
|
+
'</div>' +
|
|
355
405
|
'</div>';
|
|
356
406
|
} else {
|
|
407
|
+
// Build GPU mode options
|
|
408
|
+
var gpuOptions = buildGpuModeSelector(gpuInfo);
|
|
409
|
+
|
|
357
410
|
container.innerHTML =
|
|
358
|
-
'<div class="space-y-
|
|
411
|
+
'<div class="space-y-3">' +
|
|
359
412
|
'<div class="flex items-center gap-2 text-sm text-muted-foreground">' +
|
|
360
413
|
'<i data-lucide="alert-circle" class="w-4 h-4"></i>' +
|
|
361
414
|
'<span>' + t('codexlens.semanticNotInstalled') + '</span>' +
|
|
362
415
|
'</div>' +
|
|
363
|
-
|
|
416
|
+
gpuOptions +
|
|
417
|
+
'<button class="btn-sm btn-primary w-full" onclick="installSemanticDepsWithGpu()">' +
|
|
364
418
|
'<i data-lucide="download" class="w-3 h-3"></i> ' + t('codexlens.installDeps') +
|
|
365
419
|
'</button>' +
|
|
366
420
|
'</div>';
|
|
@@ -373,21 +427,120 @@ async function loadSemanticDepsStatus() {
|
|
|
373
427
|
}
|
|
374
428
|
|
|
375
429
|
/**
|
|
376
|
-
*
|
|
430
|
+
* Build GPU mode selector HTML
|
|
377
431
|
*/
|
|
378
|
-
|
|
432
|
+
function buildGpuModeSelector(gpuInfo) {
|
|
433
|
+
var modes = [
|
|
434
|
+
{
|
|
435
|
+
id: 'cpu',
|
|
436
|
+
label: 'CPU',
|
|
437
|
+
desc: t('codexlens.cpuModeDesc') || 'Standard CPU processing',
|
|
438
|
+
icon: 'cpu',
|
|
439
|
+
available: true
|
|
440
|
+
},
|
|
441
|
+
{
|
|
442
|
+
id: 'directml',
|
|
443
|
+
label: 'DirectML',
|
|
444
|
+
desc: t('codexlens.directmlModeDesc') || 'Windows GPU (NVIDIA/AMD/Intel)',
|
|
445
|
+
icon: 'gpu-card',
|
|
446
|
+
available: gpuInfo.available.includes('directml'),
|
|
447
|
+
recommended: gpuInfo.mode === 'directml'
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
id: 'cuda',
|
|
451
|
+
label: 'CUDA',
|
|
452
|
+
desc: t('codexlens.cudaModeDesc') || 'NVIDIA GPU (requires CUDA Toolkit)',
|
|
453
|
+
icon: 'zap',
|
|
454
|
+
available: gpuInfo.available.includes('cuda'),
|
|
455
|
+
recommended: gpuInfo.mode === 'cuda'
|
|
456
|
+
}
|
|
457
|
+
];
|
|
458
|
+
|
|
459
|
+
var html =
|
|
460
|
+
'<div class="space-y-2">' +
|
|
461
|
+
'<div class="text-xs font-medium text-muted-foreground flex items-center gap-1">' +
|
|
462
|
+
'<i data-lucide="settings" class="w-3 h-3"></i>' +
|
|
463
|
+
(t('codexlens.selectGpuMode') || 'Select acceleration mode') +
|
|
464
|
+
'</div>' +
|
|
465
|
+
'<div class="text-xs text-muted-foreground bg-muted/50 rounded px-2 py-1">' +
|
|
466
|
+
'<i data-lucide="info" class="w-3 h-3 inline"></i> ' + gpuInfo.info +
|
|
467
|
+
'</div>' +
|
|
468
|
+
'<div class="space-y-1">';
|
|
469
|
+
|
|
470
|
+
modes.forEach(function(mode) {
|
|
471
|
+
var isDisabled = !mode.available;
|
|
472
|
+
var isRecommended = mode.recommended;
|
|
473
|
+
var isDefault = mode.id === gpuInfo.mode;
|
|
474
|
+
|
|
475
|
+
html +=
|
|
476
|
+
'<label class="flex items-center gap-3 p-2 rounded border cursor-pointer hover:bg-muted/50 transition-colors ' +
|
|
477
|
+
(isDisabled ? 'opacity-50 cursor-not-allowed' : '') + '">' +
|
|
478
|
+
'<input type="radio" name="gpuMode" value="' + mode.id + '" ' +
|
|
479
|
+
(isDefault ? 'checked' : '') +
|
|
480
|
+
(isDisabled ? ' disabled' : '') +
|
|
481
|
+
' class="accent-primary">' +
|
|
482
|
+
'<div class="flex-1">' +
|
|
483
|
+
'<div class="flex items-center gap-2">' +
|
|
484
|
+
'<i data-lucide="' + mode.icon + '" class="w-4 h-4"></i>' +
|
|
485
|
+
'<span class="font-medium text-sm">' + mode.label + '</span>' +
|
|
486
|
+
(isRecommended ? '<span class="text-xs bg-primary/20 text-primary px-1.5 py-0.5 rounded">' + (t('common.recommended') || 'Recommended') + '</span>' : '') +
|
|
487
|
+
(isDisabled ? '<span class="text-xs text-muted-foreground">(' + (t('common.unavailable') || 'Unavailable') + ')</span>' : '') +
|
|
488
|
+
'</div>' +
|
|
489
|
+
'<div class="text-xs text-muted-foreground">' + mode.desc + '</div>' +
|
|
490
|
+
'</div>' +
|
|
491
|
+
'</label>';
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
html +=
|
|
495
|
+
'</div>' +
|
|
496
|
+
'</div>';
|
|
497
|
+
|
|
498
|
+
return html;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Get selected GPU mode
|
|
503
|
+
*/
|
|
504
|
+
function getSelectedGpuMode() {
|
|
505
|
+
var selected = document.querySelector('input[name="gpuMode"]:checked');
|
|
506
|
+
return selected ? selected.value : 'cpu';
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Install semantic dependencies with GPU mode
|
|
511
|
+
*/
|
|
512
|
+
async function installSemanticDepsWithGpu() {
|
|
379
513
|
var container = document.getElementById('semanticDepsStatus');
|
|
380
514
|
if (!container) return;
|
|
381
515
|
|
|
516
|
+
var gpuMode = getSelectedGpuMode();
|
|
517
|
+
var modeLabels = {
|
|
518
|
+
cpu: 'CPU',
|
|
519
|
+
cuda: 'NVIDIA CUDA',
|
|
520
|
+
directml: 'DirectML'
|
|
521
|
+
};
|
|
522
|
+
|
|
382
523
|
container.innerHTML =
|
|
383
|
-
'<div class="
|
|
524
|
+
'<div class="space-y-2">' +
|
|
525
|
+
'<div class="flex items-center gap-2 text-sm text-muted-foreground">' +
|
|
526
|
+
'<div class="animate-spin w-4 h-4 border-2 border-primary border-t-transparent rounded-full"></div>' +
|
|
527
|
+
'<span>' + t('codexlens.installingDeps') + '</span>' +
|
|
528
|
+
'</div>' +
|
|
529
|
+
'<div class="text-xs text-muted-foreground">' +
|
|
530
|
+
(t('codexlens.installingMode') || 'Installing with') + ': ' + modeLabels[gpuMode] +
|
|
531
|
+
'</div>' +
|
|
532
|
+
'</div>';
|
|
384
533
|
|
|
385
534
|
try {
|
|
386
|
-
var response = await fetch('/api/codexlens/semantic/install', {
|
|
535
|
+
var response = await fetch('/api/codexlens/semantic/install', {
|
|
536
|
+
method: 'POST',
|
|
537
|
+
headers: { 'Content-Type': 'application/json' },
|
|
538
|
+
body: JSON.stringify({ gpuMode: gpuMode })
|
|
539
|
+
});
|
|
387
540
|
var result = await response.json();
|
|
388
541
|
|
|
389
542
|
if (result.success) {
|
|
390
|
-
showRefreshToast(t('codexlens.depsInstalled'), 'success');
|
|
543
|
+
showRefreshToast(t('codexlens.depsInstalled') + ' (' + modeLabels[gpuMode] + ')', 'success');
|
|
391
544
|
await loadSemanticDepsStatus();
|
|
392
545
|
await loadModelList();
|
|
393
546
|
} else {
|
|
@@ -400,10 +553,164 @@ async function installSemanticDeps() {
|
|
|
400
553
|
}
|
|
401
554
|
}
|
|
402
555
|
|
|
556
|
+
/**
|
|
557
|
+
* Install semantic dependencies (legacy, defaults to CPU)
|
|
558
|
+
*/
|
|
559
|
+
async function installSemanticDeps() {
|
|
560
|
+
await installSemanticDepsWithGpu();
|
|
561
|
+
}
|
|
562
|
+
|
|
403
563
|
// ============================================================
|
|
404
564
|
// MODEL MANAGEMENT
|
|
405
565
|
// ============================================================
|
|
406
566
|
|
|
567
|
+
/**
|
|
568
|
+
* Build manual download guide HTML
|
|
569
|
+
*/
|
|
570
|
+
function buildManualDownloadGuide() {
|
|
571
|
+
var modelData = [
|
|
572
|
+
{ profile: 'code', name: 'jinaai/jina-embeddings-v2-base-code', size: '~150 MB' },
|
|
573
|
+
{ profile: 'fast', name: 'BAAI/bge-small-en-v1.5', size: '~80 MB' },
|
|
574
|
+
{ profile: 'balanced', name: 'mixedbread-ai/mxbai-embed-large-v1', size: '~600 MB' },
|
|
575
|
+
{ profile: 'multilingual', name: 'intfloat/multilingual-e5-large', size: '~1 GB' }
|
|
576
|
+
];
|
|
577
|
+
|
|
578
|
+
var html =
|
|
579
|
+
'<div class="mt-4 border-t pt-4">' +
|
|
580
|
+
'<button class="flex items-center gap-2 text-sm text-muted-foreground hover:text-foreground w-full" onclick="toggleManualDownloadGuide()" id="manualDownloadToggle">' +
|
|
581
|
+
'<i data-lucide="chevron-right" class="w-4 h-4 transition-transform" id="manualDownloadChevron"></i>' +
|
|
582
|
+
'<i data-lucide="terminal" class="w-4 h-4"></i>' +
|
|
583
|
+
'<span>' + (t('codexlens.manualDownloadGuide') || 'Manual Download Guide') + '</span>' +
|
|
584
|
+
'</button>' +
|
|
585
|
+
'<div id="manualDownloadContent" class="hidden mt-3 space-y-3">' +
|
|
586
|
+
// Method 1: CLI
|
|
587
|
+
'<div class="bg-muted/50 rounded-lg p-3 space-y-2">' +
|
|
588
|
+
'<div class="flex items-center gap-2 text-sm font-medium">' +
|
|
589
|
+
'<span class="inline-flex items-center justify-center w-5 h-5 rounded-full bg-primary/20 text-primary text-xs">1</span>' +
|
|
590
|
+
'<span>' + (t('codexlens.cliMethod') || 'Command Line (Recommended)') + '</span>' +
|
|
591
|
+
'</div>' +
|
|
592
|
+
'<div class="text-xs text-muted-foreground mb-2">' +
|
|
593
|
+
(t('codexlens.cliMethodDesc') || 'Run in terminal with progress display:') +
|
|
594
|
+
'</div>' +
|
|
595
|
+
'<div class="space-y-1">';
|
|
596
|
+
|
|
597
|
+
modelData.forEach(function(m) {
|
|
598
|
+
html +=
|
|
599
|
+
'<div class="flex items-center justify-between bg-background rounded px-2 py-1.5">' +
|
|
600
|
+
'<code class="text-xs font-mono">codexlens model-download ' + m.profile + '</code>' +
|
|
601
|
+
'<button class="text-xs text-primary hover:underline" onclick="copyToClipboard(\'codexlens model-download ' + m.profile + '\')">' +
|
|
602
|
+
'<i data-lucide="copy" class="w-3 h-3"></i>' +
|
|
603
|
+
'</button>' +
|
|
604
|
+
'</div>';
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
html +=
|
|
608
|
+
'</div>' +
|
|
609
|
+
'</div>' +
|
|
610
|
+
|
|
611
|
+
// Method 2: Python
|
|
612
|
+
'<div class="bg-muted/50 rounded-lg p-3 space-y-2">' +
|
|
613
|
+
'<div class="flex items-center gap-2 text-sm font-medium">' +
|
|
614
|
+
'<span class="inline-flex items-center justify-center w-5 h-5 rounded-full bg-primary/20 text-primary text-xs">2</span>' +
|
|
615
|
+
'<span>' + (t('codexlens.pythonMethod') || 'Python Script') + '</span>' +
|
|
616
|
+
'</div>' +
|
|
617
|
+
'<div class="text-xs text-muted-foreground mb-2">' +
|
|
618
|
+
(t('codexlens.pythonMethodDesc') || 'Pre-download model using Python:') +
|
|
619
|
+
'</div>' +
|
|
620
|
+
'<div class="bg-background rounded p-2">' +
|
|
621
|
+
'<pre class="text-xs font-mono whitespace-pre-wrap">' +
|
|
622
|
+
'# Install fastembed first\n' +
|
|
623
|
+
'pip install fastembed\n\n' +
|
|
624
|
+
'# Download model (choose one)\n' +
|
|
625
|
+
'from fastembed import TextEmbedding\n\n' +
|
|
626
|
+
'# Code model (recommended for code search)\n' +
|
|
627
|
+
'model = TextEmbedding("jinaai/jina-embeddings-v2-base-code")\n\n' +
|
|
628
|
+
'# Fast model (lightweight)\n' +
|
|
629
|
+
'# model = TextEmbedding("BAAI/bge-small-en-v1.5")' +
|
|
630
|
+
'</pre>' +
|
|
631
|
+
'</div>' +
|
|
632
|
+
'</div>' +
|
|
633
|
+
|
|
634
|
+
// Method 3: Hugging Face Hub
|
|
635
|
+
'<div class="bg-muted/50 rounded-lg p-3 space-y-2">' +
|
|
636
|
+
'<div class="flex items-center gap-2 text-sm font-medium">' +
|
|
637
|
+
'<span class="inline-flex items-center justify-center w-5 h-5 rounded-full bg-primary/20 text-primary text-xs">3</span>' +
|
|
638
|
+
'<span>' + (t('codexlens.hfHubMethod') || 'Hugging Face Hub CLI') + '</span>' +
|
|
639
|
+
'</div>' +
|
|
640
|
+
'<div class="text-xs text-muted-foreground mb-2">' +
|
|
641
|
+
(t('codexlens.hfHubMethodDesc') || 'Download using huggingface-cli with resume support:') +
|
|
642
|
+
'</div>' +
|
|
643
|
+
'<div class="bg-background rounded p-2 space-y-2">' +
|
|
644
|
+
'<pre class="text-xs font-mono whitespace-pre-wrap">' +
|
|
645
|
+
'# Install huggingface_hub\n' +
|
|
646
|
+
'pip install huggingface_hub\n\n' +
|
|
647
|
+
'# Download model (supports resume on failure)\n' +
|
|
648
|
+
'huggingface-cli download jinaai/jina-embeddings-v2-base-code' +
|
|
649
|
+
'</pre>' +
|
|
650
|
+
'</div>' +
|
|
651
|
+
'</div>' +
|
|
652
|
+
|
|
653
|
+
// Model Links
|
|
654
|
+
'<div class="bg-muted/50 rounded-lg p-3 space-y-2">' +
|
|
655
|
+
'<div class="flex items-center gap-2 text-sm font-medium">' +
|
|
656
|
+
'<i data-lucide="external-link" class="w-4 h-4"></i>' +
|
|
657
|
+
'<span>' + (t('codexlens.modelLinks') || 'Direct Model Links') + '</span>' +
|
|
658
|
+
'</div>' +
|
|
659
|
+
'<div class="grid grid-cols-2 gap-2">';
|
|
660
|
+
|
|
661
|
+
modelData.forEach(function(m) {
|
|
662
|
+
html +=
|
|
663
|
+
'<a href="https://huggingface.co/' + m.name + '" target="_blank" class="flex items-center justify-between bg-background rounded px-2 py-1.5 hover:bg-muted transition-colors">' +
|
|
664
|
+
'<span class="text-xs font-medium">' + m.profile + '</span>' +
|
|
665
|
+
'<span class="text-xs text-muted-foreground">' + m.size + '</span>' +
|
|
666
|
+
'</a>';
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
html +=
|
|
670
|
+
'</div>' +
|
|
671
|
+
'</div>' +
|
|
672
|
+
|
|
673
|
+
// Cache location info
|
|
674
|
+
'<div class="text-xs text-muted-foreground bg-muted/30 rounded p-2">' +
|
|
675
|
+
'<div class="flex items-start gap-1.5">' +
|
|
676
|
+
'<i data-lucide="info" class="w-3.5 h-3.5 mt-0.5 flex-shrink-0"></i>' +
|
|
677
|
+
'<div>' +
|
|
678
|
+
'<strong>' + (t('codexlens.cacheLocation') || 'Cache Location') + ':</strong><br>' +
|
|
679
|
+
'<code class="text-xs">Windows: %LOCALAPPDATA%\\Temp\\fastembed_cache</code><br>' +
|
|
680
|
+
'<code class="text-xs">Linux/Mac: ~/.cache/fastembed</code>' +
|
|
681
|
+
'</div>' +
|
|
682
|
+
'</div>' +
|
|
683
|
+
'</div>' +
|
|
684
|
+
'</div>' +
|
|
685
|
+
'</div>';
|
|
686
|
+
|
|
687
|
+
return html;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
/**
|
|
691
|
+
* Toggle manual download guide visibility
|
|
692
|
+
*/
|
|
693
|
+
function toggleManualDownloadGuide() {
|
|
694
|
+
var content = document.getElementById('manualDownloadContent');
|
|
695
|
+
var chevron = document.getElementById('manualDownloadChevron');
|
|
696
|
+
|
|
697
|
+
if (content && chevron) {
|
|
698
|
+
content.classList.toggle('hidden');
|
|
699
|
+
chevron.style.transform = content.classList.contains('hidden') ? '' : 'rotate(90deg)';
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
/**
|
|
704
|
+
* Copy text to clipboard
|
|
705
|
+
*/
|
|
706
|
+
function copyToClipboard(text) {
|
|
707
|
+
navigator.clipboard.writeText(text).then(function() {
|
|
708
|
+
showRefreshToast(t('common.copied') || 'Copied to clipboard', 'success');
|
|
709
|
+
}).catch(function(err) {
|
|
710
|
+
console.error('Failed to copy:', err);
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
|
|
407
714
|
/**
|
|
408
715
|
* Load model list
|
|
409
716
|
*/
|
|
@@ -476,6 +783,10 @@ async function loadModelList() {
|
|
|
476
783
|
});
|
|
477
784
|
|
|
478
785
|
html += '</div>';
|
|
786
|
+
|
|
787
|
+
// Add manual download guide section
|
|
788
|
+
html += buildManualDownloadGuide();
|
|
789
|
+
|
|
479
790
|
container.innerHTML = html;
|
|
480
791
|
if (window.lucide) lucide.createIcons();
|
|
481
792
|
} catch (err) {
|
|
@@ -485,18 +796,94 @@ async function loadModelList() {
|
|
|
485
796
|
}
|
|
486
797
|
|
|
487
798
|
/**
|
|
488
|
-
* Download model
|
|
799
|
+
* Download model with progress simulation and manual download info
|
|
489
800
|
*/
|
|
490
801
|
async function downloadModel(profile) {
|
|
491
802
|
var modelCard = document.getElementById('model-' + profile);
|
|
492
803
|
if (!modelCard) return;
|
|
493
804
|
|
|
494
805
|
var originalHTML = modelCard.innerHTML;
|
|
806
|
+
|
|
807
|
+
// Get model info for size estimation
|
|
808
|
+
var modelSizes = {
|
|
809
|
+
'fast': { size: 80, time: '1-2' },
|
|
810
|
+
'code': { size: 150, time: '2-5' },
|
|
811
|
+
'multilingual': { size: 1000, time: '5-15' },
|
|
812
|
+
'balanced': { size: 600, time: '3-10' }
|
|
813
|
+
};
|
|
814
|
+
|
|
815
|
+
var modelInfo = modelSizes[profile] || { size: 100, time: '2-5' };
|
|
816
|
+
|
|
817
|
+
// Show detailed download UI with progress simulation
|
|
495
818
|
modelCard.innerHTML =
|
|
496
|
-
'<div class="
|
|
497
|
-
'<
|
|
819
|
+
'<div class="p-3 space-y-3">' +
|
|
820
|
+
'<div class="flex items-center gap-2">' +
|
|
821
|
+
'<div class="animate-spin w-4 h-4 border-2 border-primary border-t-transparent rounded-full flex-shrink-0"></div>' +
|
|
822
|
+
'<span class="text-sm font-medium">' + (t('codexlens.downloadingModel') || 'Downloading') + ' ' + profile + '</span>' +
|
|
823
|
+
'</div>' +
|
|
824
|
+
'<div class="space-y-1">' +
|
|
825
|
+
'<div class="h-2 bg-muted rounded-full overflow-hidden">' +
|
|
826
|
+
'<div id="model-progress-' + profile + '" class="h-full bg-primary transition-all duration-1000 ease-out model-download-progress" style="width: 0%"></div>' +
|
|
827
|
+
'</div>' +
|
|
828
|
+
'<div class="flex justify-between text-xs text-muted-foreground">' +
|
|
829
|
+
'<span id="model-status-' + profile + '">' + (t('codexlens.connectingToHuggingFace') || 'Connecting to Hugging Face...') + '</span>' +
|
|
830
|
+
'<span>~' + modelInfo.size + ' MB</span>' +
|
|
831
|
+
'</div>' +
|
|
832
|
+
'</div>' +
|
|
833
|
+
'<div class="text-xs text-muted-foreground bg-muted/50 rounded p-2 space-y-1">' +
|
|
834
|
+
'<div class="flex items-start gap-1">' +
|
|
835
|
+
'<i data-lucide="info" class="w-3 h-3 mt-0.5 flex-shrink-0"></i>' +
|
|
836
|
+
'<span>' + (t('codexlens.downloadTimeEstimate') || 'Estimated time') + ': ' + modelInfo.time + ' ' + (t('common.minutes') || 'minutes') + '</span>' +
|
|
837
|
+
'</div>' +
|
|
838
|
+
'<div class="flex items-start gap-1">' +
|
|
839
|
+
'<i data-lucide="terminal" class="w-3 h-3 mt-0.5 flex-shrink-0"></i>' +
|
|
840
|
+
'<span>' + (t('codexlens.manualDownloadHint') || 'Manual download') + ': <code class="bg-background px-1 rounded">codexlens model-download ' + profile + '</code></span>' +
|
|
841
|
+
'</div>' +
|
|
842
|
+
'</div>' +
|
|
843
|
+
'<button class="text-xs text-muted-foreground hover:text-foreground underline" onclick="cancelModelDownload(\'' + profile + '\')">' +
|
|
844
|
+
(t('common.cancel') || 'Cancel') +
|
|
845
|
+
'</button>' +
|
|
498
846
|
'</div>';
|
|
499
847
|
|
|
848
|
+
if (window.lucide) lucide.createIcons();
|
|
849
|
+
|
|
850
|
+
// Start progress simulation
|
|
851
|
+
var progressBar = document.getElementById('model-progress-' + profile);
|
|
852
|
+
var statusText = document.getElementById('model-status-' + profile);
|
|
853
|
+
var simulatedProgress = 0;
|
|
854
|
+
var progressInterval = null;
|
|
855
|
+
var downloadAborted = false;
|
|
856
|
+
|
|
857
|
+
// Store abort controller for cancellation
|
|
858
|
+
window['modelDownloadAbort_' + profile] = function() {
|
|
859
|
+
downloadAborted = true;
|
|
860
|
+
if (progressInterval) clearInterval(progressInterval);
|
|
861
|
+
};
|
|
862
|
+
|
|
863
|
+
// Simulate progress based on model size
|
|
864
|
+
var progressStages = [
|
|
865
|
+
{ percent: 10, msg: t('codexlens.downloadingModelFiles') || 'Downloading model files...' },
|
|
866
|
+
{ percent: 30, msg: t('codexlens.downloadingWeights') || 'Downloading model weights...' },
|
|
867
|
+
{ percent: 60, msg: t('codexlens.downloadingTokenizer') || 'Downloading tokenizer...' },
|
|
868
|
+
{ percent: 80, msg: t('codexlens.verifyingModel') || 'Verifying model...' },
|
|
869
|
+
{ percent: 95, msg: t('codexlens.finalizingDownload') || 'Finalizing...' }
|
|
870
|
+
];
|
|
871
|
+
|
|
872
|
+
var stageIndex = 0;
|
|
873
|
+
var baseInterval = Math.max(2000, modelInfo.size * 30); // Slower for larger models
|
|
874
|
+
|
|
875
|
+
progressInterval = setInterval(function() {
|
|
876
|
+
if (downloadAborted) return;
|
|
877
|
+
|
|
878
|
+
if (stageIndex < progressStages.length) {
|
|
879
|
+
var stage = progressStages[stageIndex];
|
|
880
|
+
simulatedProgress = stage.percent;
|
|
881
|
+
if (progressBar) progressBar.style.width = simulatedProgress + '%';
|
|
882
|
+
if (statusText) statusText.textContent = stage.msg;
|
|
883
|
+
stageIndex++;
|
|
884
|
+
}
|
|
885
|
+
}, baseInterval);
|
|
886
|
+
|
|
500
887
|
try {
|
|
501
888
|
var response = await fetch('/api/codexlens/models/download', {
|
|
502
889
|
method: 'POST',
|
|
@@ -504,20 +891,99 @@ async function downloadModel(profile) {
|
|
|
504
891
|
body: JSON.stringify({ profile: profile })
|
|
505
892
|
});
|
|
506
893
|
|
|
894
|
+
// Clear simulation
|
|
895
|
+
if (progressInterval) clearInterval(progressInterval);
|
|
896
|
+
|
|
897
|
+
if (downloadAborted) {
|
|
898
|
+
modelCard.innerHTML = originalHTML;
|
|
899
|
+
if (window.lucide) lucide.createIcons();
|
|
900
|
+
return;
|
|
901
|
+
}
|
|
902
|
+
|
|
507
903
|
var result = await response.json();
|
|
508
904
|
|
|
509
905
|
if (result.success) {
|
|
906
|
+
// Show completion
|
|
907
|
+
if (progressBar) progressBar.style.width = '100%';
|
|
908
|
+
if (statusText) statusText.textContent = t('codexlens.downloadComplete') || 'Download complete!';
|
|
909
|
+
|
|
510
910
|
showRefreshToast(t('codexlens.modelDownloaded') + ': ' + profile, 'success');
|
|
511
|
-
|
|
911
|
+
|
|
912
|
+
// Refresh model list after short delay
|
|
913
|
+
setTimeout(function() {
|
|
914
|
+
loadModelList();
|
|
915
|
+
}, 500);
|
|
512
916
|
} else {
|
|
513
917
|
showRefreshToast(t('codexlens.modelDownloadFailed') + ': ' + result.error, 'error');
|
|
514
|
-
modelCard.
|
|
515
|
-
if (window.lucide) lucide.createIcons();
|
|
918
|
+
showModelDownloadError(modelCard, profile, result.error, originalHTML);
|
|
516
919
|
}
|
|
517
920
|
} catch (err) {
|
|
921
|
+
if (progressInterval) clearInterval(progressInterval);
|
|
518
922
|
showRefreshToast(t('common.error') + ': ' + err.message, 'error');
|
|
519
|
-
modelCard.
|
|
520
|
-
|
|
923
|
+
showModelDownloadError(modelCard, profile, err.message, originalHTML);
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
// Cleanup abort function
|
|
927
|
+
delete window['modelDownloadAbort_' + profile];
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
/**
|
|
931
|
+
* Show model download error with manual download instructions
|
|
932
|
+
*/
|
|
933
|
+
function showModelDownloadError(modelCard, profile, error, originalHTML) {
|
|
934
|
+
var modelNames = {
|
|
935
|
+
'fast': 'BAAI/bge-small-en-v1.5',
|
|
936
|
+
'code': 'jinaai/jina-embeddings-v2-base-code',
|
|
937
|
+
'multilingual': 'intfloat/multilingual-e5-large',
|
|
938
|
+
'balanced': 'mixedbread-ai/mxbai-embed-large-v1'
|
|
939
|
+
};
|
|
940
|
+
|
|
941
|
+
var modelName = modelNames[profile] || profile;
|
|
942
|
+
var hfUrl = 'https://huggingface.co/' + modelName;
|
|
943
|
+
|
|
944
|
+
modelCard.innerHTML =
|
|
945
|
+
'<div class="p-3 space-y-3">' +
|
|
946
|
+
'<div class="flex items-start gap-2 text-destructive">' +
|
|
947
|
+
'<i data-lucide="alert-circle" class="w-4 h-4 mt-0.5 flex-shrink-0"></i>' +
|
|
948
|
+
'<div class="text-sm">' +
|
|
949
|
+
'<div class="font-medium">' + (t('codexlens.downloadFailed') || 'Download failed') + '</div>' +
|
|
950
|
+
'<div class="text-xs text-muted-foreground mt-1">' + error + '</div>' +
|
|
951
|
+
'</div>' +
|
|
952
|
+
'</div>' +
|
|
953
|
+
'<div class="bg-muted/50 rounded p-2 space-y-2 text-xs">' +
|
|
954
|
+
'<div class="font-medium">' + (t('codexlens.manualDownloadOptions') || 'Manual download options') + ':</div>' +
|
|
955
|
+
'<div class="space-y-1.5">' +
|
|
956
|
+
'<div class="flex items-start gap-1">' +
|
|
957
|
+
'<span class="text-muted-foreground">1.</span>' +
|
|
958
|
+
'<span>' + (t('codexlens.cliDownload') || 'CLI') + ': <code class="bg-background px-1 rounded">codexlens model-download ' + profile + '</code></span>' +
|
|
959
|
+
'</div>' +
|
|
960
|
+
'<div class="flex items-start gap-1">' +
|
|
961
|
+
'<span class="text-muted-foreground">2.</span>' +
|
|
962
|
+
'<span>' + (t('codexlens.huggingfaceDownload') || 'Hugging Face') + ': <a href="' + hfUrl + '" target="_blank" class="text-primary hover:underline">' + modelName + '</a></span>' +
|
|
963
|
+
'</div>' +
|
|
964
|
+
'</div>' +
|
|
965
|
+
'</div>' +
|
|
966
|
+
'<div class="flex gap-2">' +
|
|
967
|
+
'<button class="btn-sm btn-outline flex-1" onclick="loadModelList()">' +
|
|
968
|
+
'<i data-lucide="refresh-cw" class="w-3 h-3"></i> ' + (t('common.refresh') || 'Refresh') +
|
|
969
|
+
'</button>' +
|
|
970
|
+
'<button class="btn-sm btn-primary flex-1" onclick="downloadModel(\'' + profile + '\')">' +
|
|
971
|
+
'<i data-lucide="download" class="w-3 h-3"></i> ' + (t('common.retry') || 'Retry') +
|
|
972
|
+
'</button>' +
|
|
973
|
+
'</div>' +
|
|
974
|
+
'</div>';
|
|
975
|
+
|
|
976
|
+
if (window.lucide) lucide.createIcons();
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
/**
|
|
980
|
+
* Cancel model download
|
|
981
|
+
*/
|
|
982
|
+
function cancelModelDownload(profile) {
|
|
983
|
+
if (window['modelDownloadAbort_' + profile]) {
|
|
984
|
+
window['modelDownloadAbort_' + profile]();
|
|
985
|
+
showRefreshToast(t('codexlens.downloadCanceled') || 'Download canceled', 'info');
|
|
986
|
+
loadModelList();
|
|
521
987
|
}
|
|
522
988
|
}
|
|
523
989
|
|
|
@@ -876,16 +1342,315 @@ async function cancelCodexLensIndexing() {
|
|
|
876
1342
|
|
|
877
1343
|
/**
|
|
878
1344
|
* Install CodexLens
|
|
1345
|
+
* Note: Uses CodexLens-specific install wizard from cli-status.js
|
|
1346
|
+
* which calls /api/codexlens/bootstrap (Python venv), not the generic
|
|
1347
|
+
* CLI install that uses npm install -g (NPM packages)
|
|
1348
|
+
*/
|
|
1349
|
+
function installCodexLensFromManager() {
|
|
1350
|
+
// Use the CodexLens-specific install wizard from cli-status.js
|
|
1351
|
+
if (typeof openCodexLensInstallWizard === 'function') {
|
|
1352
|
+
openCodexLensInstallWizard();
|
|
1353
|
+
} else {
|
|
1354
|
+
// Fallback: inline install wizard if cli-status.js not loaded
|
|
1355
|
+
showCodexLensInstallDialog();
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
/**
|
|
1360
|
+
* Fallback install dialog when cli-status.js is not loaded
|
|
879
1361
|
*/
|
|
880
|
-
function
|
|
881
|
-
|
|
1362
|
+
function showCodexLensInstallDialog() {
|
|
1363
|
+
var modal = document.createElement('div');
|
|
1364
|
+
modal.id = 'codexlensInstallModalFallback';
|
|
1365
|
+
modal.className = 'fixed inset-0 bg-black/50 flex items-center justify-center z-50';
|
|
1366
|
+
modal.innerHTML =
|
|
1367
|
+
'<div class="bg-card rounded-lg shadow-xl w-full max-w-md mx-4 overflow-hidden">' +
|
|
1368
|
+
'<div class="p-6">' +
|
|
1369
|
+
'<div class="flex items-center gap-3 mb-4">' +
|
|
1370
|
+
'<div class="w-10 h-10 rounded-full bg-primary/10 flex items-center justify-center">' +
|
|
1371
|
+
'<i data-lucide="database" class="w-5 h-5 text-primary"></i>' +
|
|
1372
|
+
'</div>' +
|
|
1373
|
+
'<div>' +
|
|
1374
|
+
'<h3 class="text-lg font-semibold">' + t('codexlens.installCodexLens') + '</h3>' +
|
|
1375
|
+
'<p class="text-sm text-muted-foreground">' + t('codexlens.installDesc') + '</p>' +
|
|
1376
|
+
'</div>' +
|
|
1377
|
+
'</div>' +
|
|
1378
|
+
'<div class="space-y-4">' +
|
|
1379
|
+
'<div class="bg-muted/50 rounded-lg p-4">' +
|
|
1380
|
+
'<h4 class="font-medium mb-2">' + t('codexlens.whatWillBeInstalled') + '</h4>' +
|
|
1381
|
+
'<ul class="text-sm space-y-2 text-muted-foreground">' +
|
|
1382
|
+
'<li class="flex items-start gap-2">' +
|
|
1383
|
+
'<i data-lucide="check" class="w-4 h-4 text-success mt-0.5"></i>' +
|
|
1384
|
+
'<span><strong>' + t('codexlens.pythonVenv') + '</strong> - ' + t('codexlens.pythonVenvDesc') + '</span>' +
|
|
1385
|
+
'</li>' +
|
|
1386
|
+
'<li class="flex items-start gap-2">' +
|
|
1387
|
+
'<i data-lucide="check" class="w-4 h-4 text-success mt-0.5"></i>' +
|
|
1388
|
+
'<span><strong>' + t('codexlens.codexlensPackage') + '</strong> - ' + t('codexlens.codexlensPackageDesc') + '</span>' +
|
|
1389
|
+
'</li>' +
|
|
1390
|
+
'<li class="flex items-start gap-2">' +
|
|
1391
|
+
'<i data-lucide="check" class="w-4 h-4 text-success mt-0.5"></i>' +
|
|
1392
|
+
'<span><strong>SQLite FTS5</strong> - ' + t('codexlens.sqliteFtsDesc') + '</span>' +
|
|
1393
|
+
'</li>' +
|
|
1394
|
+
'</ul>' +
|
|
1395
|
+
'</div>' +
|
|
1396
|
+
'<div class="bg-primary/5 border border-primary/20 rounded-lg p-3">' +
|
|
1397
|
+
'<div class="flex items-start gap-2">' +
|
|
1398
|
+
'<i data-lucide="info" class="w-4 h-4 text-primary mt-0.5"></i>' +
|
|
1399
|
+
'<div class="text-sm text-muted-foreground">' +
|
|
1400
|
+
'<p class="font-medium text-foreground">' + t('codexlens.installLocation') + '</p>' +
|
|
1401
|
+
'<p class="mt-1"><code class="bg-muted px-1 rounded">~/.codexlens/venv</code></p>' +
|
|
1402
|
+
'<p class="mt-1">' + t('codexlens.installTime') + '</p>' +
|
|
1403
|
+
'</div>' +
|
|
1404
|
+
'</div>' +
|
|
1405
|
+
'</div>' +
|
|
1406
|
+
'<div id="codexlensInstallProgressFallback" class="hidden">' +
|
|
1407
|
+
'<div class="flex items-center gap-3">' +
|
|
1408
|
+
'<div class="animate-spin w-5 h-5 border-2 border-primary border-t-transparent rounded-full"></div>' +
|
|
1409
|
+
'<span class="text-sm" id="codexlensInstallStatusFallback">' + t('codexlens.startingInstall') + '</span>' +
|
|
1410
|
+
'</div>' +
|
|
1411
|
+
'<div class="mt-2 h-2 bg-muted rounded-full overflow-hidden">' +
|
|
1412
|
+
'<div id="codexlensInstallProgressBarFallback" class="h-full bg-primary transition-all duration-300" style="width: 0%"></div>' +
|
|
1413
|
+
'</div>' +
|
|
1414
|
+
'</div>' +
|
|
1415
|
+
'</div>' +
|
|
1416
|
+
'</div>' +
|
|
1417
|
+
'<div class="border-t border-border p-4 flex justify-end gap-3 bg-muted/30">' +
|
|
1418
|
+
'<button class="btn-outline px-4 py-2" onclick="closeCodexLensInstallDialogFallback()">' + t('common.cancel') + '</button>' +
|
|
1419
|
+
'<button id="codexlensInstallBtnFallback" class="btn-primary px-4 py-2" onclick="startCodexLensInstallFallback()">' +
|
|
1420
|
+
'<i data-lucide="download" class="w-4 h-4 mr-2"></i>' +
|
|
1421
|
+
t('codexlens.installNow') +
|
|
1422
|
+
'</button>' +
|
|
1423
|
+
'</div>' +
|
|
1424
|
+
'</div>';
|
|
1425
|
+
|
|
1426
|
+
document.body.appendChild(modal);
|
|
1427
|
+
if (window.lucide) lucide.createIcons();
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
function closeCodexLensInstallDialogFallback() {
|
|
1431
|
+
var modal = document.getElementById('codexlensInstallModalFallback');
|
|
1432
|
+
if (modal) modal.remove();
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
async function startCodexLensInstallFallback() {
|
|
1436
|
+
var progressDiv = document.getElementById('codexlensInstallProgressFallback');
|
|
1437
|
+
var installBtn = document.getElementById('codexlensInstallBtnFallback');
|
|
1438
|
+
var statusText = document.getElementById('codexlensInstallStatusFallback');
|
|
1439
|
+
var progressBar = document.getElementById('codexlensInstallProgressBarFallback');
|
|
1440
|
+
|
|
1441
|
+
progressDiv.classList.remove('hidden');
|
|
1442
|
+
installBtn.disabled = true;
|
|
1443
|
+
installBtn.innerHTML = '<span class="animate-pulse">' + t('codexlens.installing') + '</span>';
|
|
1444
|
+
|
|
1445
|
+
var stages = [
|
|
1446
|
+
{ progress: 10, text: t('codexlens.creatingVenv') },
|
|
1447
|
+
{ progress: 30, text: t('codexlens.installingPip') },
|
|
1448
|
+
{ progress: 50, text: t('codexlens.installingPackage') },
|
|
1449
|
+
{ progress: 70, text: t('codexlens.settingUpDeps') },
|
|
1450
|
+
{ progress: 90, text: t('codexlens.finalizing') }
|
|
1451
|
+
];
|
|
1452
|
+
|
|
1453
|
+
var currentStage = 0;
|
|
1454
|
+
var progressInterval = setInterval(function() {
|
|
1455
|
+
if (currentStage < stages.length) {
|
|
1456
|
+
statusText.textContent = stages[currentStage].text;
|
|
1457
|
+
progressBar.style.width = stages[currentStage].progress + '%';
|
|
1458
|
+
currentStage++;
|
|
1459
|
+
}
|
|
1460
|
+
}, 1500);
|
|
1461
|
+
|
|
1462
|
+
try {
|
|
1463
|
+
var response = await fetch('/api/codexlens/bootstrap', {
|
|
1464
|
+
method: 'POST',
|
|
1465
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1466
|
+
body: JSON.stringify({})
|
|
1467
|
+
});
|
|
1468
|
+
|
|
1469
|
+
clearInterval(progressInterval);
|
|
1470
|
+
var result = await response.json();
|
|
1471
|
+
|
|
1472
|
+
if (result.success) {
|
|
1473
|
+
progressBar.style.width = '100%';
|
|
1474
|
+
statusText.textContent = t('codexlens.installComplete');
|
|
1475
|
+
|
|
1476
|
+
setTimeout(function() {
|
|
1477
|
+
closeCodexLensInstallDialogFallback();
|
|
1478
|
+
showRefreshToast(t('codexlens.installSuccess'), 'success');
|
|
1479
|
+
// Refresh the page to update status
|
|
1480
|
+
if (typeof loadCodexLensStatus === 'function') {
|
|
1481
|
+
loadCodexLensStatus().then(function() {
|
|
1482
|
+
if (typeof renderCodexLensManager === 'function') renderCodexLensManager();
|
|
1483
|
+
});
|
|
1484
|
+
} else {
|
|
1485
|
+
location.reload();
|
|
1486
|
+
}
|
|
1487
|
+
}, 1000);
|
|
1488
|
+
} else {
|
|
1489
|
+
statusText.textContent = t('common.error') + ': ' + result.error;
|
|
1490
|
+
progressBar.classList.add('bg-destructive');
|
|
1491
|
+
installBtn.disabled = false;
|
|
1492
|
+
installBtn.innerHTML = '<i data-lucide="refresh-cw" class="w-4 h-4 mr-2"></i> ' + t('common.retry');
|
|
1493
|
+
if (window.lucide) lucide.createIcons();
|
|
1494
|
+
}
|
|
1495
|
+
} catch (err) {
|
|
1496
|
+
clearInterval(progressInterval);
|
|
1497
|
+
statusText.textContent = t('common.error') + ': ' + err.message;
|
|
1498
|
+
progressBar.classList.add('bg-destructive');
|
|
1499
|
+
installBtn.disabled = false;
|
|
1500
|
+
installBtn.innerHTML = '<i data-lucide="refresh-cw" class="w-4 h-4 mr-2"></i> ' + t('common.retry');
|
|
1501
|
+
if (window.lucide) lucide.createIcons();
|
|
1502
|
+
}
|
|
882
1503
|
}
|
|
883
1504
|
|
|
884
1505
|
/**
|
|
885
1506
|
* Uninstall CodexLens
|
|
1507
|
+
* Note: Uses CodexLens-specific uninstall wizard from cli-status.js
|
|
1508
|
+
* which calls /api/codexlens/uninstall (Python venv), not the generic
|
|
1509
|
+
* CLI uninstall that uses /api/cli/uninstall (NPM packages)
|
|
886
1510
|
*/
|
|
887
|
-
function
|
|
888
|
-
|
|
1511
|
+
function uninstallCodexLensFromManager() {
|
|
1512
|
+
// Use the CodexLens-specific uninstall wizard from cli-status.js
|
|
1513
|
+
if (typeof openCodexLensUninstallWizard === 'function') {
|
|
1514
|
+
openCodexLensUninstallWizard();
|
|
1515
|
+
} else {
|
|
1516
|
+
// Fallback: inline uninstall wizard if cli-status.js not loaded
|
|
1517
|
+
showCodexLensUninstallDialog();
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
/**
|
|
1522
|
+
* Fallback uninstall dialog when cli-status.js is not loaded
|
|
1523
|
+
*/
|
|
1524
|
+
function showCodexLensUninstallDialog() {
|
|
1525
|
+
var modal = document.createElement('div');
|
|
1526
|
+
modal.id = 'codexlensUninstallModalFallback';
|
|
1527
|
+
modal.className = 'fixed inset-0 bg-black/50 flex items-center justify-center z-50';
|
|
1528
|
+
modal.innerHTML =
|
|
1529
|
+
'<div class="bg-card rounded-lg shadow-xl w-full max-w-md mx-4 overflow-hidden">' +
|
|
1530
|
+
'<div class="p-6">' +
|
|
1531
|
+
'<div class="flex items-center gap-3 mb-4">' +
|
|
1532
|
+
'<div class="w-10 h-10 rounded-full bg-destructive/10 flex items-center justify-center">' +
|
|
1533
|
+
'<i data-lucide="trash-2" class="w-5 h-5 text-destructive"></i>' +
|
|
1534
|
+
'</div>' +
|
|
1535
|
+
'<div>' +
|
|
1536
|
+
'<h3 class="text-lg font-semibold">' + t('codexlens.uninstall') + '</h3>' +
|
|
1537
|
+
'<p class="text-sm text-muted-foreground">' + t('codexlens.uninstallDesc') + '</p>' +
|
|
1538
|
+
'</div>' +
|
|
1539
|
+
'</div>' +
|
|
1540
|
+
'<div class="space-y-4">' +
|
|
1541
|
+
'<div class="bg-destructive/5 border border-destructive/20 rounded-lg p-4">' +
|
|
1542
|
+
'<h4 class="font-medium text-destructive mb-2">' + t('codexlens.whatWillBeRemoved') + '</h4>' +
|
|
1543
|
+
'<ul class="text-sm space-y-2 text-muted-foreground">' +
|
|
1544
|
+
'<li class="flex items-start gap-2">' +
|
|
1545
|
+
'<i data-lucide="x" class="w-4 h-4 text-destructive mt-0.5"></i>' +
|
|
1546
|
+
'<span>' + t('codexlens.removeVenv') + '</span>' +
|
|
1547
|
+
'</li>' +
|
|
1548
|
+
'<li class="flex items-start gap-2">' +
|
|
1549
|
+
'<i data-lucide="x" class="w-4 h-4 text-destructive mt-0.5"></i>' +
|
|
1550
|
+
'<span>' + t('codexlens.removeData') + '</span>' +
|
|
1551
|
+
'</li>' +
|
|
1552
|
+
'<li class="flex items-start gap-2">' +
|
|
1553
|
+
'<i data-lucide="x" class="w-4 h-4 text-destructive mt-0.5"></i>' +
|
|
1554
|
+
'<span>' + t('codexlens.removeConfig') + '</span>' +
|
|
1555
|
+
'</li>' +
|
|
1556
|
+
'</ul>' +
|
|
1557
|
+
'</div>' +
|
|
1558
|
+
'<div id="codexlensUninstallProgressFallback" class="hidden">' +
|
|
1559
|
+
'<div class="flex items-center gap-3">' +
|
|
1560
|
+
'<div class="animate-spin w-5 h-5 border-2 border-destructive border-t-transparent rounded-full"></div>' +
|
|
1561
|
+
'<span class="text-sm" id="codexlensUninstallStatusFallback">' + t('codexlens.removing') + '</span>' +
|
|
1562
|
+
'</div>' +
|
|
1563
|
+
'<div class="mt-2 h-2 bg-muted rounded-full overflow-hidden">' +
|
|
1564
|
+
'<div id="codexlensUninstallProgressBarFallback" class="h-full bg-destructive transition-all duration-300" style="width: 0%"></div>' +
|
|
1565
|
+
'</div>' +
|
|
1566
|
+
'</div>' +
|
|
1567
|
+
'</div>' +
|
|
1568
|
+
'</div>' +
|
|
1569
|
+
'<div class="border-t border-border p-4 flex justify-end gap-3 bg-muted/30">' +
|
|
1570
|
+
'<button class="btn-outline px-4 py-2" onclick="closeCodexLensUninstallDialogFallback()">' + t('common.cancel') + '</button>' +
|
|
1571
|
+
'<button id="codexlensUninstallBtnFallback" class="btn-destructive px-4 py-2" onclick="startCodexLensUninstallFallback()">' +
|
|
1572
|
+
'<i data-lucide="trash-2" class="w-4 h-4 mr-2"></i>' +
|
|
1573
|
+
t('codexlens.uninstall') +
|
|
1574
|
+
'</button>' +
|
|
1575
|
+
'</div>' +
|
|
1576
|
+
'</div>';
|
|
1577
|
+
|
|
1578
|
+
document.body.appendChild(modal);
|
|
1579
|
+
if (window.lucide) lucide.createIcons();
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
function closeCodexLensUninstallDialogFallback() {
|
|
1583
|
+
var modal = document.getElementById('codexlensUninstallModalFallback');
|
|
1584
|
+
if (modal) modal.remove();
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
async function startCodexLensUninstallFallback() {
|
|
1588
|
+
var progressDiv = document.getElementById('codexlensUninstallProgressFallback');
|
|
1589
|
+
var uninstallBtn = document.getElementById('codexlensUninstallBtnFallback');
|
|
1590
|
+
var statusText = document.getElementById('codexlensUninstallStatusFallback');
|
|
1591
|
+
var progressBar = document.getElementById('codexlensUninstallProgressBarFallback');
|
|
1592
|
+
|
|
1593
|
+
progressDiv.classList.remove('hidden');
|
|
1594
|
+
uninstallBtn.disabled = true;
|
|
1595
|
+
uninstallBtn.innerHTML = '<span class="animate-pulse">' + t('codexlens.uninstalling') + '</span>';
|
|
1596
|
+
|
|
1597
|
+
var stages = [
|
|
1598
|
+
{ progress: 25, text: t('codexlens.removingVenv') },
|
|
1599
|
+
{ progress: 50, text: t('codexlens.removingData') },
|
|
1600
|
+
{ progress: 75, text: t('codexlens.removingConfig') },
|
|
1601
|
+
{ progress: 90, text: t('codexlens.finalizing') }
|
|
1602
|
+
];
|
|
1603
|
+
|
|
1604
|
+
var currentStage = 0;
|
|
1605
|
+
var progressInterval = setInterval(function() {
|
|
1606
|
+
if (currentStage < stages.length) {
|
|
1607
|
+
statusText.textContent = stages[currentStage].text;
|
|
1608
|
+
progressBar.style.width = stages[currentStage].progress + '%';
|
|
1609
|
+
currentStage++;
|
|
1610
|
+
}
|
|
1611
|
+
}, 500);
|
|
1612
|
+
|
|
1613
|
+
try {
|
|
1614
|
+
var response = await fetch('/api/codexlens/uninstall', {
|
|
1615
|
+
method: 'POST',
|
|
1616
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1617
|
+
body: JSON.stringify({})
|
|
1618
|
+
});
|
|
1619
|
+
|
|
1620
|
+
clearInterval(progressInterval);
|
|
1621
|
+
var result = await response.json();
|
|
1622
|
+
|
|
1623
|
+
if (result.success) {
|
|
1624
|
+
progressBar.style.width = '100%';
|
|
1625
|
+
statusText.textContent = t('codexlens.uninstallComplete');
|
|
1626
|
+
|
|
1627
|
+
setTimeout(function() {
|
|
1628
|
+
closeCodexLensUninstallDialogFallback();
|
|
1629
|
+
showRefreshToast(t('codexlens.uninstallSuccess'), 'success');
|
|
1630
|
+
// Refresh the page to update status
|
|
1631
|
+
if (typeof loadCodexLensStatus === 'function') {
|
|
1632
|
+
loadCodexLensStatus().then(function() {
|
|
1633
|
+
if (typeof renderCodexLensManager === 'function') renderCodexLensManager();
|
|
1634
|
+
});
|
|
1635
|
+
} else {
|
|
1636
|
+
location.reload();
|
|
1637
|
+
}
|
|
1638
|
+
}, 1000);
|
|
1639
|
+
} else {
|
|
1640
|
+
statusText.textContent = t('common.error') + ': ' + result.error;
|
|
1641
|
+
progressBar.classList.add('bg-destructive');
|
|
1642
|
+
uninstallBtn.disabled = false;
|
|
1643
|
+
uninstallBtn.innerHTML = '<i data-lucide="refresh-cw" class="w-4 h-4 mr-2"></i> ' + t('common.retry');
|
|
1644
|
+
if (window.lucide) lucide.createIcons();
|
|
1645
|
+
}
|
|
1646
|
+
} catch (err) {
|
|
1647
|
+
clearInterval(progressInterval);
|
|
1648
|
+
statusText.textContent = t('common.error') + ': ' + err.message;
|
|
1649
|
+
progressBar.classList.add('bg-destructive');
|
|
1650
|
+
uninstallBtn.disabled = false;
|
|
1651
|
+
uninstallBtn.innerHTML = '<i data-lucide="refresh-cw" class="w-4 h-4 mr-2"></i> ' + t('common.retry');
|
|
1652
|
+
if (window.lucide) lucide.createIcons();
|
|
1653
|
+
}
|
|
889
1654
|
}
|
|
890
1655
|
|
|
891
1656
|
/**
|