From e1cac5dd5010e41a6a400ffaec0c091052ffcf19 Mon Sep 17 00:00:00 2001 From: catlog22 Date: Sat, 20 Dec 2025 11:08:34 +0800 Subject: [PATCH] Refactor search modes and optimize embedding generation - Updated the dashboard template to hide the Code Graph Explorer feature. - Enhanced the `executeCodexLens` function to use `exec` for better cross-platform compatibility and improved command execution. - Changed the default `maxResults` and `limit` parameters in the smart search tool to 10 for better performance. - Introduced a new `priority` search mode in the smart search tool, replacing the previous `parallel` mode, which now follows a fallback strategy: hybrid -> exact -> ripgrep. - Optimized the embedding generation process in the embedding manager by batching operations and using a cached embedder instance to reduce model loading overhead. - Implemented a thread-safe singleton pattern for the embedder to improve performance across multiple searches. --- .claude/workflows/context-tools.md | 2 +- ccw/src/core/dashboard-generator.ts | 4 + .../dashboard-css/30-core-memory.css | 176 ++++++++++- .../dashboard-js/components/mcp-manager.js | 16 +- ccw/src/templates/dashboard-js/i18n.js | 46 ++- ccw/src/templates/dashboard-js/state.js | 5 + ccw/src/templates/dashboard-js/utils.js | 31 ++ .../dashboard-js/views/cli-manager.js | 3 +- .../dashboard-js/views/core-memory.js | 208 ++++++++++++- .../dashboard-js/views/mcp-manager.js | 122 +++++--- ccw/src/templates/dashboard.html | 2 + ccw/src/tools/codex-lens.ts | 86 +++--- ccw/src/tools/smart-search.ts | 278 +++++++++--------- .../src/codexlens/cli/embedding_manager.py | 104 +++++-- .../src/codexlens/search/hybrid_search.py | 5 +- codex-lens/src/codexlens/semantic/embedder.py | 48 ++- 16 files changed, 852 insertions(+), 284 deletions(-) diff --git a/.claude/workflows/context-tools.md b/.claude/workflows/context-tools.md index 54a36296..e2874696 100644 --- a/.claude/workflows/context-tools.md +++ b/.claude/workflows/context-tools.md @@ -10,7 +10,7 @@ smart_search(query="authentication logic") // Step 2: Only if search warns "No CodexLens index found", then init -smart_search(action="init", path=".") // Creates FTS index only +smart_search(action="init", path=".") // Creates FTS index only // Note: For semantic/vector search, use "ccw view" dashboard to create vector index ``` diff --git a/ccw/src/core/dashboard-generator.ts b/ccw/src/core/dashboard-generator.ts index 68d33677..cbe798c3 100644 --- a/ccw/src/core/dashboard-generator.ts +++ b/ccw/src/core/dashboard-generator.ts @@ -173,6 +173,10 @@ function generateFromUnifiedTemplate(data: unknown): string { jsContent = jsContent.replace(/\{\{PROJECT_PATH\}\}/g, projectPath.replace(/\\/g, '/')); jsContent = jsContent.replace('{{RECENT_PATHS}}', JSON.stringify(recentPaths)); + // Inject platform information for cross-platform MCP command generation + // 'win32' for Windows, 'darwin' for macOS, 'linux' for Linux + jsContent = jsContent.replace(/\{\{SERVER_PLATFORM\}\}/g, process.platform); + // Inject JS and CSS into HTML template html = html.replace('{{JS_CONTENT}}', jsContent); html = html.replace('{{CSS_CONTENT}}', cssContent); diff --git a/ccw/src/templates/dashboard-css/30-core-memory.css b/ccw/src/templates/dashboard-css/30-core-memory.css index a3e736b2..a20556a5 100644 --- a/ccw/src/templates/dashboard-css/30-core-memory.css +++ b/ccw/src/templates/dashboard-css/30-core-memory.css @@ -224,6 +224,13 @@ border-radius: 8px; padding: 1.25rem; transition: all 0.2s ease; + cursor: pointer; + /* Fixed card dimensions */ + display: flex; + flex-direction: column; + height: 280px; + min-height: 280px; + max-height: 280px; } .memory-card:hover { @@ -239,8 +246,9 @@ .memory-card-header { display: flex; justify-content: space-between; - align-items: flex-start; - margin-bottom: 1rem; + align-items: center; + margin-bottom: 0.75rem; + flex-shrink: 0; } .memory-id { @@ -248,7 +256,8 @@ align-items: center; gap: 0.5rem; font-size: 0.875rem; - color: hsl(var(--muted-foreground)); + color: hsl(var(--foreground)); + font-weight: 600; flex-wrap: wrap; } @@ -257,6 +266,10 @@ height: 16px; } +.memory-id i[data-lucide="star"] { + color: hsl(38 92% 50%); +} + .memory-actions { display: flex; gap: 0.5rem; @@ -282,34 +295,83 @@ color: hsl(var(--destructive)); } +.icon-btn.favorite-active { + color: hsl(38 92% 50%); +} + +.icon-btn.favorite-active:hover { + color: hsl(38 92% 40%); +} + .icon-btn i { width: 18px; height: 18px; } +/* Copy ID Button */ +.copy-id-btn { + background: none; + border: none; + padding: 0.125rem 0.25rem; + cursor: pointer; + color: hsl(var(--muted-foreground)); + transition: color 0.2s; + display: inline-flex; + align-items: center; + justify-content: center; + margin-left: 0.25rem; + border-radius: 4px; +} + +.copy-id-btn:hover { + color: hsl(var(--primary)); + background: hsl(var(--muted)); +} + +.copy-id-btn i { + width: 14px; + height: 14px; +} + +.copy-id-btn.copied { + color: hsl(var(--success, 142 76% 36%)); +} + /* Memory Content */ .memory-content { - margin-bottom: 1rem; -} - -.memory-summary { - font-size: 0.9375rem; - line-height: 1.6; - color: hsl(var(--foreground)); + flex: 1; + min-height: 0; + overflow: hidden; + margin-bottom: 0.75rem; } +.memory-summary, .memory-preview { font-size: 0.875rem; line-height: 1.5; color: hsl(var(--muted-foreground)); + /* Fixed height with ellipsis */ + display: -webkit-box; + -webkit-line-clamp: 4; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: ellipsis; + max-height: 6em; +} + +.memory-summary { + color: hsl(var(--foreground)); } /* Memory Tags */ .memory-tags { display: flex; flex-wrap: wrap; - gap: 0.5rem; - margin-bottom: 1rem; + gap: 0.375rem; + margin-bottom: 0.5rem; + flex-shrink: 0; + max-height: 2rem; + overflow: hidden; } .tag { @@ -321,13 +383,14 @@ font-weight: 500; } -/* Memory Footer */ +/* Memory Footer - Fixed at bottom */ .memory-footer { display: flex; flex-direction: column; - gap: 0.75rem; - padding-top: 1rem; + gap: 0.5rem; + padding-top: 0.75rem; border-top: 1px solid hsl(var(--border)); + margin-top: auto; } .memory-meta { @@ -850,6 +913,89 @@ margin: 0; } +/* Relations Detail Styles */ +.relations-detail h4 { + font-size: 1rem; + font-weight: 600; + margin-bottom: 1rem; + color: hsl(var(--foreground)); +} + +.clusters-list { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.relation-cluster-item { + padding: 1rem; + background: hsl(var(--muted) / 0.3); + border: 1px solid hsl(var(--border)); + border-radius: 8px; +} + +.relation-cluster-header { + display: flex; + align-items: center; + gap: 0.5rem; + margin-bottom: 0.75rem; +} + +.relation-cluster-header i { + width: 18px; + height: 18px; + color: hsl(var(--primary)); +} + +.relation-cluster-header .cluster-name { + font-weight: 600; + color: hsl(var(--foreground)); +} + +.cluster-relations-list { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.5rem; + padding-top: 0.75rem; + border-top: 1px solid hsl(var(--border)); +} + +.relations-label { + font-size: 0.8125rem; + color: hsl(var(--muted-foreground)); +} + +.relation-tag { + display: inline-flex; + align-items: center; + gap: 0.375rem; + padding: 0.25rem 0.625rem; + background: hsl(var(--accent)); + border-radius: 4px; + font-size: 0.8125rem; +} + +.relation-tag i { + width: 12px; + height: 12px; + color: hsl(var(--muted-foreground)); +} + +.relation-tag .relation-type { + font-size: 0.6875rem; + padding: 0.125rem 0.375rem; + background: hsl(var(--primary) / 0.15); + color: hsl(var(--primary)); + border-radius: 3px; + margin-left: 0.25rem; +} + +.text-muted { + color: hsl(var(--muted-foreground)); + font-size: 0.875rem; +} + /* Dark Mode Adjustments */ [data-theme="dark"] .memory-card { background: #1e293b; diff --git a/ccw/src/templates/dashboard-js/components/mcp-manager.js b/ccw/src/templates/dashboard-js/components/mcp-manager.js index 89ef2531..e8486a19 100644 --- a/ccw/src/templates/dashboard-js/components/mcp-manager.js +++ b/ccw/src/templates/dashboard-js/components/mcp-manager.js @@ -928,11 +928,19 @@ function selectCcwTools(type) { } // Build CCW Tools config with selected tools +// Uses isWindowsPlatform from state.js to generate platform-appropriate commands function buildCcwToolsConfig(selectedTools) { - const config = { - command: "cmd", - args: ["/c", "npx", "-y", "ccw-mcp"] - }; + // Windows requires 'cmd /c' wrapper to execute npx + // Other platforms (macOS, Linux) can run npx directly + const config = isWindowsPlatform + ? { + command: "cmd", + args: ["/c", "npx", "-y", "ccw-mcp"] + } + : { + command: "npx", + args: ["-y", "ccw-mcp"] + }; // Add env if not all tools or not default 4 core tools const coreTools = ['write_file', 'edit_file', 'codex_lens', 'smart_search']; diff --git a/ccw/src/templates/dashboard-js/i18n.js b/ccw/src/templates/dashboard-js/i18n.js index e596f258..133b14ae 100644 --- a/ccw/src/templates/dashboard-js/i18n.js +++ b/ccw/src/templates/dashboard-js/i18n.js @@ -1291,8 +1291,27 @@ const i18n = { 'coreMemory.clusterUpdateError': 'Failed to update cluster', 'coreMemory.memberRemoved': 'Member removed', 'coreMemory.memberRemoveError': 'Failed to remove member', + 'coreMemory.favorites': 'Favorites', + 'coreMemory.totalFavorites': 'Total Favorites', + 'coreMemory.noFavorites': 'No favorites yet', + 'coreMemory.toggleFavorite': 'Toggle Favorite', + 'coreMemory.addedToFavorites': 'Added to favorites', + 'coreMemory.removedFromFavorites': 'Removed from favorites', + 'coreMemory.favoriteError': 'Failed to update favorite', + 'coreMemory.relations': 'Relations', + 'coreMemory.showRelations': 'Show Relations', + 'coreMemory.relationsFor': 'Relations', + 'coreMemory.noRelations': 'No cluster relations found', + 'coreMemory.noRelationsHint': 'Use Auto Cluster in the Clusters tab to create relations', + 'coreMemory.belongsToClusters': 'Belongs to Clusters', + 'coreMemory.relationsError': 'Failed to load relations', + + // Common additions + 'common.copyId': 'Copy ID', + 'common.copied': 'Copied!', + 'common.copyError': 'Failed to copy', }, - + zh: { // App title and brand 'app.title': 'CCW 控制面板', @@ -1589,13 +1608,13 @@ const i18n = { 'index.projects': '项目数', 'index.totalSize': '总大小', 'index.vectorIndexes': '向量', - 'index.ftsIndexes': '全文', + 'index.ftsIndexes': 'FTS', 'index.projectId': '项目 ID', 'index.size': '大小', 'index.type': '类型', 'index.lastModified': '修改时间', 'index.vector': '向量', - 'index.fts': '全文', + 'index.fts': 'FTS', 'index.noIndexes': '暂无索引', 'index.notConfigured': '未配置', 'index.initCurrent': '索引当前项目', @@ -1608,7 +1627,7 @@ const i18n = { 'index.cleanAllConfirm': '确定要清理所有索引吗?此操作无法撤销。', 'index.cleanAllSuccess': '所有索引已清理', 'index.vectorIndex': '向量索引', - 'index.normalIndex': '全文索引', + 'index.normalIndex': 'FTS索引', 'index.vectorDesc': '语义搜索(含嵌入向量)', 'index.normalDesc': '快速全文搜索', @@ -2585,6 +2604,25 @@ const i18n = { 'coreMemory.clusterUpdateError': '更新聚类失败', 'coreMemory.memberRemoved': '成员已移除', 'coreMemory.memberRemoveError': '移除成员失败', + 'coreMemory.favorites': '收藏', + 'coreMemory.totalFavorites': '收藏总数', + 'coreMemory.noFavorites': '暂无收藏', + 'coreMemory.toggleFavorite': '切换收藏', + 'coreMemory.addedToFavorites': '已添加到收藏', + 'coreMemory.removedFromFavorites': '已从收藏移除', + 'coreMemory.favoriteError': '更新收藏失败', + 'coreMemory.relations': '关联', + 'coreMemory.showRelations': '显示关联', + 'coreMemory.relationsFor': '关联关系', + 'coreMemory.noRelations': '未找到聚类关联', + 'coreMemory.noRelationsHint': '在聚类 Tab 中使用自动聚类来创建关联', + 'coreMemory.belongsToClusters': '所属聚类', + 'coreMemory.relationsError': '加载关联失败', + + // Common additions + 'common.copyId': '复制 ID', + 'common.copied': '已复制!', + 'common.copyError': '复制失败', } }; diff --git a/ccw/src/templates/dashboard-js/state.js b/ccw/src/templates/dashboard-js/state.js index 68753190..1c6e1316 100644 --- a/ccw/src/templates/dashboard-js/state.js +++ b/ccw/src/templates/dashboard-js/state.js @@ -10,6 +10,11 @@ let workflowData = {{WORKFLOW_DATA}}; let projectPath = '{{PROJECT_PATH}}'; let recentPaths = {{RECENT_PATHS}}; +// Platform detection for cross-platform MCP command generation +// 'win32' for Windows, 'darwin' for macOS, 'linux' for Linux +const serverPlatform = '{{SERVER_PLATFORM}}'; +const isWindowsPlatform = serverPlatform === 'win32'; + // ========== Application State ========== // Current filter for session list view ('all', 'active', 'archived') let currentFilter = 'all'; diff --git a/ccw/src/templates/dashboard-js/utils.js b/ccw/src/templates/dashboard-js/utils.js index 743dcd1e..e1995c1a 100644 --- a/ccw/src/templates/dashboard-js/utils.js +++ b/ccw/src/templates/dashboard-js/utils.js @@ -5,6 +5,37 @@ // ========== HTML/Text Processing ========== +/** + * Encode JSON config data to Base64 for safe HTML attribute storage + * @param {Object} config - Configuration object to encode + * @returns {string} Base64 encoded JSON string + */ +function encodeConfigData(config) { + try { + const jsonStr = JSON.stringify(config); + return btoa(encodeURIComponent(jsonStr)); + } catch (err) { + console.error('[Utils] Error encoding config data:', err); + return ''; + } +} + +/** + * Decode Base64 config data back to JSON object + * @param {string} encoded - Base64 encoded string + * @returns {Object|null} Decoded configuration object or null on error + */ +function decodeConfigData(encoded) { + try { + if (!encoded) return null; + const jsonStr = decodeURIComponent(atob(encoded)); + return JSON.parse(jsonStr); + } catch (err) { + console.error('[Utils] Error decoding config data:', err); + return null; + } +} + /** * Escape HTML special characters to prevent XSS attacks * @param {string} str - String to escape diff --git a/ccw/src/templates/dashboard-js/views/cli-manager.js b/ccw/src/templates/dashboard-js/views/cli-manager.js index c285e2ab..859c684d 100644 --- a/ccw/src/templates/dashboard-js/views/cli-manager.js +++ b/ccw/src/templates/dashboard-js/views/cli-manager.js @@ -392,7 +392,8 @@ function renderToolsSection() { '
' + (codexLensStatus.ready ? ' v' + (codexLensStatus.version || 'installed') + '' + - '' + + '' + + '' + '' : ' ' + t('cli.notInstalled') + '' + '') + diff --git a/ccw/src/templates/dashboard-js/views/core-memory.js b/ccw/src/templates/dashboard-js/views/core-memory.js index 5caad34f..99fced53 100644 --- a/ccw/src/templates/dashboard-js/views/core-memory.js +++ b/ccw/src/templates/dashboard-js/views/core-memory.js @@ -66,6 +66,10 @@ async function renderCoreMemoryView() { ${t('coreMemory.memories')} +
+ + +