mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-13 02:41:50 +08:00
feat: 添加取消索引和检查索引状态的API,优化CodexLens的用户体验
This commit is contained in:
@@ -10,7 +10,9 @@ import {
|
|||||||
executeCodexLens,
|
executeCodexLens,
|
||||||
checkSemanticStatus,
|
checkSemanticStatus,
|
||||||
installSemantic,
|
installSemantic,
|
||||||
uninstallCodexLens
|
uninstallCodexLens,
|
||||||
|
cancelIndexing,
|
||||||
|
isIndexingInProgress
|
||||||
} from '../../tools/codex-lens.js';
|
} from '../../tools/codex-lens.js';
|
||||||
import type { ProgressInfo } from '../../tools/codex-lens.js';
|
import type { ProgressInfo } from '../../tools/codex-lens.js';
|
||||||
|
|
||||||
@@ -449,6 +451,31 @@ export async function handleCodexLensRoutes(ctx: RouteContext): Promise<boolean>
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// API: Cancel CodexLens Indexing
|
||||||
|
if (pathname === '/api/codexlens/cancel' && req.method === 'POST') {
|
||||||
|
const result = cancelIndexing();
|
||||||
|
|
||||||
|
// Broadcast cancellation event
|
||||||
|
if (result.success) {
|
||||||
|
broadcastToClients({
|
||||||
|
type: 'CODEXLENS_INDEX_PROGRESS',
|
||||||
|
payload: { stage: 'cancelled', message: 'Indexing cancelled by user', percent: 0 }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
res.writeHead(result.success ? 200 : 400, { 'Content-Type': 'application/json' });
|
||||||
|
res.end(JSON.stringify(result));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// API: Check if indexing is in progress
|
||||||
|
if (pathname === '/api/codexlens/indexing-status') {
|
||||||
|
const inProgress = isIndexingInProgress();
|
||||||
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||||
|
res.end(JSON.stringify({ success: true, inProgress }));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// API: CodexLens Semantic Search Status
|
// API: CodexLens Semantic Search Status
|
||||||
if (pathname === '/api/codexlens/semantic/status') {
|
if (pathname === '/api/codexlens/semantic/status') {
|
||||||
const status = await checkSemanticStatus();
|
const status = await checkSemanticStatus();
|
||||||
|
|||||||
@@ -660,6 +660,9 @@ async function initCodexLensIndex(indexType, embeddingModel) {
|
|||||||
'<div id="codexlensIndexProgressBar" class="h-full bg-primary transition-all duration-300 ease-out" style="width: 0%"></div>' +
|
'<div id="codexlensIndexProgressBar" class="h-full bg-primary transition-all duration-300 ease-out" style="width: 0%"></div>' +
|
||||||
'</div>' +
|
'</div>' +
|
||||||
'</div>' +
|
'</div>' +
|
||||||
|
'<button id="codexlensIndexCancelBtn" class="px-2 py-1 text-xs bg-destructive/10 hover:bg-destructive/20 text-destructive rounded-md transition-colors flex-shrink-0" onclick="cancelCodexLensIndexing()" title="' + t('common.cancel') + '">' +
|
||||||
|
t('common.cancel') +
|
||||||
|
'</button>' +
|
||||||
'<button class="p-1.5 hover:bg-muted rounded-md transition-colors flex-shrink-0" onclick="closeCodexLensIndexModal()" title="' + t('common.close') + '">' +
|
'<button class="p-1.5 hover:bg-muted rounded-md transition-colors flex-shrink-0" onclick="closeCodexLensIndexModal()" title="' + t('common.close') + '">' +
|
||||||
'<i data-lucide="x" class="w-4 h-4"></i>' +
|
'<i data-lucide="x" class="w-4 h-4"></i>' +
|
||||||
'</button>' +
|
'</button>' +
|
||||||
@@ -816,6 +819,61 @@ function closeCodexLensIndexModal() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel the running indexing process
|
||||||
|
*/
|
||||||
|
async function cancelCodexLensIndexing() {
|
||||||
|
var cancelBtn = document.getElementById('codexlensIndexCancelBtn');
|
||||||
|
var statusText = document.getElementById('codexlensIndexStatus');
|
||||||
|
|
||||||
|
// Disable button to prevent double-click
|
||||||
|
if (cancelBtn) {
|
||||||
|
cancelBtn.disabled = true;
|
||||||
|
cancelBtn.textContent = t('common.canceling') || 'Canceling...';
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var response = await fetch('/api/codexlens/cancel', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
});
|
||||||
|
|
||||||
|
var result = await response.json();
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
if (statusText) statusText.textContent = t('codexlens.indexCanceled') || 'Indexing canceled';
|
||||||
|
showRefreshToast(t('codexlens.indexCanceled') || 'Indexing canceled', 'info');
|
||||||
|
|
||||||
|
// Close the modal after a short delay
|
||||||
|
setTimeout(function() {
|
||||||
|
closeCodexLensIndexModal();
|
||||||
|
// Refresh status
|
||||||
|
if (typeof loadCodexLensStatus === 'function') {
|
||||||
|
loadCodexLensStatus().then(function() {
|
||||||
|
renderToolsSection();
|
||||||
|
if (window.lucide) lucide.createIcons();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
} else {
|
||||||
|
showRefreshToast(t('codexlens.cancelFailed') + ': ' + result.error, 'error');
|
||||||
|
// Re-enable button on failure
|
||||||
|
if (cancelBtn) {
|
||||||
|
cancelBtn.disabled = false;
|
||||||
|
cancelBtn.textContent = t('common.cancel');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('[CodexLens] Cancel error:', err);
|
||||||
|
showRefreshToast(t('common.error') + ': ' + err.message, 'error');
|
||||||
|
// Re-enable button on error
|
||||||
|
if (cancelBtn) {
|
||||||
|
cancelBtn.disabled = false;
|
||||||
|
cancelBtn.textContent = t('common.cancel');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Install CodexLens
|
* Install CodexLens
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -33,6 +33,10 @@ const VENV_PYTHON =
|
|||||||
let bootstrapChecked = false;
|
let bootstrapChecked = false;
|
||||||
let bootstrapReady = false;
|
let bootstrapReady = false;
|
||||||
|
|
||||||
|
// Track running indexing process for cancellation
|
||||||
|
let currentIndexingProcess: ReturnType<typeof spawn> | null = null;
|
||||||
|
let currentIndexingAborted = false;
|
||||||
|
|
||||||
// Define Zod schema for validation
|
// Define Zod schema for validation
|
||||||
const ParamsSchema = z.object({
|
const ParamsSchema = z.object({
|
||||||
action: z.enum([
|
action: z.enum([
|
||||||
@@ -510,6 +514,13 @@ async function executeCodexLens(args: string[], options: ExecuteOptions = {}): P
|
|||||||
timeout,
|
timeout,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Track indexing process for cancellation (only for init commands)
|
||||||
|
const isIndexingCommand = args.includes('init');
|
||||||
|
if (isIndexingCommand) {
|
||||||
|
currentIndexingProcess = child;
|
||||||
|
currentIndexingAborted = false;
|
||||||
|
}
|
||||||
|
|
||||||
let stdout = '';
|
let stdout = '';
|
||||||
let stderr = '';
|
let stderr = '';
|
||||||
let stdoutLineBuffer = '';
|
let stdoutLineBuffer = '';
|
||||||
@@ -525,6 +536,10 @@ async function executeCodexLens(args: string[], options: ExecuteOptions = {}): P
|
|||||||
clearTimeout(timeoutHandle);
|
clearTimeout(timeoutHandle);
|
||||||
timeoutHandle = null;
|
timeoutHandle = null;
|
||||||
}
|
}
|
||||||
|
// Clear indexing process tracking
|
||||||
|
if (isIndexingCommand) {
|
||||||
|
currentIndexingProcess = null;
|
||||||
|
}
|
||||||
resolve(result);
|
resolve(result);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1055,11 +1070,63 @@ async function uninstallCodexLens(): Promise<BootstrapResult> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel the currently running indexing process
|
||||||
|
* @returns Result indicating if cancellation was successful
|
||||||
|
*/
|
||||||
|
function cancelIndexing(): { success: boolean; message?: string; error?: string } {
|
||||||
|
if (!currentIndexingProcess) {
|
||||||
|
return { success: false, error: 'No indexing process is currently running' };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentIndexingAborted) {
|
||||||
|
return { success: false, error: 'Indexing process is already being cancelled' };
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
currentIndexingAborted = true;
|
||||||
|
|
||||||
|
// Send SIGTERM first for graceful shutdown
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
// On Windows, use taskkill to kill the process tree
|
||||||
|
const { execSync } = require('child_process');
|
||||||
|
try {
|
||||||
|
execSync(`taskkill /pid ${currentIndexingProcess.pid} /T /F`, { stdio: 'ignore' });
|
||||||
|
} catch {
|
||||||
|
// Process may have already exited
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// On Unix, send SIGTERM
|
||||||
|
currentIndexingProcess.kill('SIGTERM');
|
||||||
|
|
||||||
|
// Force kill after 3 seconds if still running
|
||||||
|
setTimeout(() => {
|
||||||
|
if (currentIndexingProcess) {
|
||||||
|
currentIndexingProcess.kill('SIGKILL');
|
||||||
|
}
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[CodexLens] Indexing process cancelled');
|
||||||
|
return { success: true, message: 'Indexing cancelled successfully' };
|
||||||
|
} catch (err) {
|
||||||
|
return { success: false, error: `Failed to cancel indexing: ${(err as Error).message}` };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if an indexing process is currently running
|
||||||
|
* @returns True if indexing is in progress
|
||||||
|
*/
|
||||||
|
function isIndexingInProgress(): boolean {
|
||||||
|
return currentIndexingProcess !== null && !currentIndexingAborted;
|
||||||
|
}
|
||||||
|
|
||||||
// Export types
|
// Export types
|
||||||
export type { ProgressInfo, ExecuteOptions };
|
export type { ProgressInfo, ExecuteOptions };
|
||||||
|
|
||||||
// Export for direct usage
|
// Export for direct usage
|
||||||
export { ensureReady, executeCodexLens, checkVenvStatus, bootstrapVenv, checkSemanticStatus, installSemantic, uninstallCodexLens };
|
export { ensureReady, executeCodexLens, checkVenvStatus, bootstrapVenv, checkSemanticStatus, installSemantic, uninstallCodexLens, cancelIndexing, isIndexingInProgress };
|
||||||
|
|
||||||
// Backward-compatible export for tests
|
// Backward-compatible export for tests
|
||||||
export const codexLensTool = {
|
export const codexLensTool = {
|
||||||
|
|||||||
Reference in New Issue
Block a user