Add scripts for inspecting LLM summaries and testing misleading comments

- Implement `inspect_llm_summaries.py` to display LLM-generated summaries from the semantic_chunks table in the database.
- Create `show_llm_analysis.py` to demonstrate LLM analysis of misleading code examples, highlighting discrepancies between comments and actual functionality.
- Develop `test_misleading_comments.py` to compare pure vector search with LLM-enhanced search, focusing on the impact of misleading or missing comments on search results.
- Introduce `test_llm_enhanced_search.py` to provide a test suite for evaluating the effectiveness of LLM-enhanced vector search against pure vector search.
- Ensure all new scripts are integrated with the existing codebase and follow the established coding standards.
This commit is contained in:
catlog22
2025-12-16 20:29:28 +08:00
parent df23975a0b
commit d21066c282
14 changed files with 3170 additions and 57 deletions

View File

@@ -64,7 +64,8 @@ const MODULE_CSS_FILES = [
'11-prompt-history.css',
'12-skills-rules.css',
'13-claude-manager.css',
'14-graph-explorer.css'
'14-graph-explorer.css',
'15-mcp-manager.css'
];
// Modular JS files in dependency order

View File

@@ -2,7 +2,187 @@
MCP MANAGER - ORANGE THEME ENHANCEMENTS
========================================== */
/* MCP CLI Mode Toggle - Orange for Codex */
/* ==========================================
BASIC BUTTON STYLES
========================================== */
/* Primary buttons (blue) */
.bg-primary {
background-color: hsl(221.2, 83.2%, 53.3%);
color: white;
}
.bg-primary:hover {
background-color: hsl(221.2, 83.2%, 45%);
}
.dark .bg-primary {
background-color: hsl(217.2, 91.2%, 59.8%);
}
.dark .bg-primary:hover {
background-color: hsl(217.2, 91.2%, 65%);
}
/* Success buttons (green) */
.bg-success {
background-color: hsl(142.1, 76.2%, 36.3%);
color: white;
}
.bg-success:hover {
background-color: hsl(142.1, 76.2%, 30%);
}
.dark .bg-success {
background-color: hsl(142.1, 70.6%, 45.3%);
}
.dark .bg-success:hover {
background-color: hsl(142.1, 70.6%, 50%);
}
/* Destructive buttons (red) */
.bg-destructive {
background-color: hsl(0, 84.2%, 60.2%);
color: white;
}
.bg-destructive:hover {
background-color: hsl(0, 84.2%, 50%);
}
.dark .bg-destructive {
background-color: hsl(0, 62.8%, 30.6%);
}
.dark .bg-destructive:hover {
background-color: hsl(0, 62.8%, 40%);
}
/* Secondary buttons (gray) */
.bg-secondary {
background-color: hsl(210, 40%, 96.1%);
color: hsl(222.2, 47.4%, 11.2%);
}
.bg-secondary:hover {
background-color: hsl(210, 40%, 90%);
}
.dark .bg-secondary {
background-color: hsl(217.2, 32.6%, 17.5%);
color: hsl(210, 40%, 98%);
}
.dark .bg-secondary:hover {
background-color: hsl(217.2, 32.6%, 22%);
}
/* Muted/Ghost buttons */
.bg-muted {
background-color: hsl(210, 40%, 96.1%);
color: hsl(215.4, 16.3%, 46.9%);
}
.bg-muted:hover {
background-color: hsl(210, 40%, 90%);
}
.dark .bg-muted {
background-color: hsl(217.2, 32.6%, 17.5%);
color: hsl(215, 20.2%, 65.1%);
}
.dark .bg-muted:hover {
background-color: hsl(217.2, 32.6%, 22%);
}
/* Button base styles */
button {
cursor: pointer;
transition: all 0.2s ease;
border: none;
border-radius: 0.375rem;
padding: 0.5rem 1rem;
font-weight: 500;
font-size: 0.875rem;
}
button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
button:focus-visible {
outline: 2px solid hsl(221.2, 83.2%, 53.3%);
outline-offset: 2px;
}
/* Icon buttons */
button.icon-btn {
padding: 0.5rem;
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 2rem;
min-height: 2rem;
}
/* ==========================================
CLAUDE (ORANGE) & CODEX (GREEN) THEMES
========================================== */
/* Claude Orange Colors */
.text-claude {
color: #f97316;
}
.bg-claude {
background-color: #f97316;
color: white;
}
.bg-claude:hover {
background-color: #ea580c;
}
.border-claude {
border-color: #f97316;
}
/* Codex Green Colors */
.text-codex {
color: #22c55e;
}
.bg-codex {
background-color: #22c55e;
color: white;
}
.bg-codex:hover {
background-color: #16a34a;
}
.border-codex {
border-color: #22c55e;
}
/* Dark mode adjustments */
.dark .text-claude {
color: #fb923c;
}
.dark .text-codex {
color: #4ade80;
}
/* ==========================================
ORANGE THEME ENHANCEMENTS (CLAUDE)
========================================== */
/* MCP CLI Mode Toggle - Orange for Claude */
.mcp-cli-toggle .cli-mode-btn {
position: relative;
overflow: hidden;
@@ -373,3 +553,186 @@
.mcp-section .flex.items-center.gap-3 button:hover::before {
transform: translateX(100%);
}
/* ==========================================
GREEN THEME ENHANCEMENTS (CODEX)
========================================== */
/* Codex green colors palette */
.bg-green-500 {
background-color: #22c55e;
}
.text-green-500 {
color: #22c55e;
}
.text-green-600 {
color: #16a34a;
}
.text-green-700 {
color: #15803d;
}
.text-green-800 {
color: #166534;
}
.bg-green-50 {
background-color: #f0fdf4;
}
.bg-green-100 {
background-color: #dcfce7;
}
.border-green-200 {
border-color: #bbf7d0;
}
.border-green-500\/20 {
border-color: rgba(34, 197, 94, 0.2);
}
.border-green-500\/30 {
border-color: rgba(34, 197, 94, 0.3);
}
.border-green-800 {
border-color: #166534;
}
/* Dark mode green colors */
.dark .bg-green-50 {
background-color: rgba(34, 197, 94, 0.05);
}
.dark .bg-green-100 {
background-color: rgba(34, 197, 94, 0.1);
}
.dark .bg-green-900\/30 {
background-color: rgba(20, 83, 45, 0.3);
}
.dark .text-green-200 {
color: #bbf7d0;
}
.dark .text-green-300 {
color: #86efac;
}
.dark .text-green-400 {
color: #4ade80;
}
.dark .border-green-800 {
border-color: #166534;
}
.dark .border-green-950\/30 {
background-color: rgba(5, 46, 22, 0.3);
}
/* Codex MCP Server Cards - Green Borders */
.mcp-server-card[data-cli-type="codex-green"] {
border-left: 3px solid #22c55e;
transition: all 0.3s ease;
}
.mcp-server-card[data-cli-type="codex-green"]:hover {
border-left-width: 4px;
box-shadow: 0 4px 16px rgba(34, 197, 94, 0.15);
}
/* Toggle switches - Green for Codex */
.mcp-toggle input:checked + div.peer-checked\:bg-green-500 {
background: #22c55e;
}
/* Installation buttons - Enhanced Green */
.bg-green-500:hover {
background-color: #16a34a;
box-shadow: 0 4px 12px rgba(34, 197, 94, 0.3);
}
/* Info panels - Green accent */
.bg-green-50.dark\:bg-green-950\/30 {
border-left: 3px solid #22c55e;
}
/* Codex section headers - Green gradient */
.text-green-500 svg {
filter: drop-shadow(0 2px 4px rgba(34, 197, 94, 0.3));
}
.mcp-section h3.text-green-500 {
background: linear-gradient(90deg, #22c55e 0%, #16a34a 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
font-weight: 700;
}
/* Animated pulse for Codex servers */
.border-green-500\/30 {
animation: greenPulse 2s ease-in-out infinite;
}
@keyframes greenPulse {
0%, 100% {
border-color: rgba(34, 197, 94, 0.3);
box-shadow: 0 0 0 0 rgba(34, 197, 94, 0);
}
50% {
border-color: rgba(34, 197, 94, 0.6);
box-shadow: 0 0 0 4px rgba(34, 197, 94, 0.1);
}
}
/* Green button hover effects */
.bg-green-500.rounded-lg {
position: relative;
overflow: hidden;
}
.bg-green-500.rounded-lg::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
border-radius: 50%;
background: rgba(255, 255, 255, 0.3);
transform: translate(-50%, -50%);
transition: width 0.3s, height 0.3s;
}
.bg-green-500.rounded-lg:active::after {
width: 200px;
height: 200px;
}
/* Green-themed success badges */
.bg-green-100 {
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
}
/* Loading states with green */
@keyframes greenGlow {
0%, 100% {
box-shadow: 0 0 10px rgba(34, 197, 94, 0.3);
}
50% {
box-shadow: 0 0 20px rgba(34, 197, 94, 0.6);
}
}
.loading-green {
animation: greenGlow 1.5s ease-in-out infinite;
}

View File

@@ -306,17 +306,17 @@ function initializeCytoscape() {
style: getCytoscapeStyles(),
layout: {
name: 'cose',
idealEdgeLength: 100,
nodeOverlap: 20,
idealEdgeLength: 180,
nodeOverlap: 50,
refresh: 20,
fit: true,
padding: 30,
padding: 50,
randomize: false,
componentSpacing: 100,
nodeRepulsion: 400000,
componentSpacing: 150,
nodeRepulsion: 600000,
edgeElasticity: 100,
nestingFactor: 5,
gravity: 80,
gravity: 60,
numIter: 1000,
initialTemp: 200,
coolingFactor: 0.95,
@@ -412,18 +412,18 @@ function getCytoscapeStyles() {
'label': 'data(label)',
'width': function(ele) {
var refs = ele.data('references') || 0;
return Math.max(20, Math.min(60, 20 + refs * 2));
return Math.max(16, Math.min(48, 16 + refs * 1.5));
},
'height': function(ele) {
var refs = ele.data('references') || 0;
return Math.max(20, Math.min(60, 20 + refs * 2));
return Math.max(16, Math.min(48, 16 + refs * 1.5));
},
'text-valign': 'center',
'text-halign': 'center',
'font-size': '10px',
'font-size': '8px',
'color': '#000',
'text-outline-color': '#fff',
'text-outline-width': 2,
'text-outline-width': 1.5,
'overlay-padding': 6
}
},
@@ -612,11 +612,14 @@ function refreshCytoscape() {
cyInstance.add(elements);
cyInstance.layout({
name: 'cose',
idealEdgeLength: 100,
nodeOverlap: 20,
idealEdgeLength: 180,
nodeOverlap: 50,
refresh: 20,
fit: true,
padding: 30
padding: 50,
componentSpacing: 150,
nodeRepulsion: 600000,
gravity: 60
}).run();
deselectNode();
@@ -625,7 +628,7 @@ function refreshCytoscape() {
// ========== Cytoscape Controls ==========
function fitCytoscape() {
if (cyInstance) {
cyInstance.fit(null, 30);
cyInstance.fit(null, 50);
}
}

View File

@@ -193,23 +193,23 @@ async function renderMcpManager() {
${currentCliMode === 'codex' ? `
<!-- CCW Tools MCP Server Card (Codex mode) -->
<div class="mcp-section mb-6">
<div class="ccw-tools-card bg-gradient-to-br from-orange-500/10 to-orange-500/5 border-2 ${codexMcpServers && codexMcpServers['ccw-tools'] ? 'border-success' : 'border-orange-500/30'} rounded-lg p-6 hover:shadow-lg transition-all">
<div class="ccw-tools-card bg-gradient-to-br from-primary/10 to-primary/5 border-2 ${codexMcpServers && codexMcpServers['ccw-tools'] ? 'border-success' : 'border-primary/30'} rounded-lg p-6 hover:shadow-lg transition-all">
<div class="flex items-start justify-between gap-4">
<div class="flex items-start gap-4 flex-1">
<div class="shrink-0 w-12 h-12 bg-orange-500 rounded-lg flex items-center justify-center">
<div class="shrink-0 w-12 h-12 bg-primary rounded-lg flex items-center justify-center">
<i data-lucide="wrench" class="w-6 h-6 text-white"></i>
</div>
<div class="flex-1 min-w-0">
<div class="flex items-center gap-2 mb-2">
<h3 class="text-lg font-bold text-foreground">CCW Tools MCP</h3>
<span class="text-xs px-2 py-0.5 bg-orange-100 text-orange-700 dark:bg-orange-900/30 dark:text-orange-300 rounded-full">Codex</span>
<span class="text-xs px-2 py-0.5 bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-300 rounded-full">Codex</span>
${codexMcpServers && codexMcpServers['ccw-tools'] ? `
<span class="inline-flex items-center gap-1 px-2 py-0.5 text-xs font-semibold rounded-full bg-success-light text-success">
<i data-lucide="check" class="w-3 h-3"></i>
${enabledToolsCodex.length} tools
</span>
` : `
<span class="inline-flex items-center gap-1 px-2 py-0.5 text-xs font-semibold rounded-full bg-orange-500/20 text-orange-600 dark:text-orange-400">
<span class="inline-flex items-center gap-1 px-2 py-0.5 text-xs font-semibold rounded-full bg-primary/20 text-primary">
<i data-lucide="package" class="w-3 h-3"></i>
${t('mcp.available')}
</span>
@@ -228,14 +228,14 @@ async function renderMcpManager() {
`).join('')}
</div>
<div class="flex items-center gap-3 text-xs">
<button class="text-orange-500 hover:underline" onclick="selectCcwToolsCodex('core')">Core only</button>
<button class="text-orange-500 hover:underline" onclick="selectCcwToolsCodex('all')">All</button>
<button class="text-primary hover:underline" onclick="selectCcwToolsCodex('core')">Core only</button>
<button class="text-primary hover:underline" onclick="selectCcwToolsCodex('all')">All</button>
<button class="text-muted-foreground hover:underline" onclick="selectCcwToolsCodex('none')">None</button>
</div>
</div>
</div>
<div class="shrink-0">
<button class="px-4 py-2 text-sm bg-orange-500 text-white rounded-lg hover:opacity-90 transition-opacity flex items-center gap-1"
<button class="px-4 py-2 text-sm bg-primary text-primary-foreground rounded-lg hover:opacity-90 transition-opacity flex items-center gap-1"
onclick="installCcwToolsMcpToCodex()">
<i data-lucide="download" class="w-4 h-4"></i>
${codexMcpServers && codexMcpServers['ccw-tools'] ? t('mcp.update') : t('mcp.install')}
@@ -250,10 +250,10 @@ async function renderMcpManager() {
<div class="flex items-center justify-between mb-4">
<div class="flex items-center gap-3">
<div class="flex items-center gap-2">
<i data-lucide="code-2" class="w-5 h-5 text-orange-500"></i>
<i data-lucide="code-2" class="w-5 h-5 text-primary"></i>
<h3 class="text-lg font-semibold text-foreground">${t('mcp.codex.globalServers')}</h3>
</div>
<button class="px-3 py-1.5 text-sm bg-orange-500 text-white rounded-lg hover:opacity-90 transition-opacity flex items-center gap-1"
<button class="px-3 py-1.5 text-sm bg-primary text-primary-foreground rounded-lg hover:opacity-90 transition-opacity flex items-center gap-1"
onclick="openCodexMcpCreateModal()">
<span>+</span> ${t('mcp.codex.newServer')}
</button>
@@ -273,12 +273,12 @@ async function renderMcpManager() {
</div>
<!-- Info about Codex MCP -->
<div class="bg-orange-50 dark:bg-orange-950/30 border border-orange-200 dark:border-orange-800 rounded-lg p-4 mb-4">
<div class="bg-green-50 dark:bg-green-950/30 border border-primary/20 rounded-lg p-4 mb-4">
<div class="flex items-start gap-3">
<i data-lucide="info" class="w-5 h-5 text-orange-500 shrink-0 mt-0.5"></i>
<i data-lucide="info" class="w-5 h-5 text-green-500 shrink-0 mt-0.5"></i>
<div class="text-sm">
<p class="text-orange-800 dark:text-orange-200 font-medium mb-1">${t('mcp.codex.infoTitle')}</p>
<p class="text-orange-700 dark:text-orange-300 text-xs">${t('mcp.codex.infoDesc')}</p>
<p class="text-primary font-medium mb-1">${t('mcp.codex.infoTitle')}</p>
<p class="text-primary/80 text-xs">${t('mcp.codex.infoDesc')}</p>
</div>
</div>
</div>
@@ -321,7 +321,7 @@ async function renderMcpManager() {
${alreadyInCodex ? `<span class="text-xs px-2 py-0.5 bg-success/10 text-success rounded-full">${t('mcp.codex.alreadyAdded')}</span>` : ''}
</div>
${!alreadyInCodex ? `
<button class="px-3 py-1 text-xs bg-orange-500 text-white rounded hover:opacity-90 transition-opacity"
<button class="px-3 py-1 text-xs bg-primary text-primary-foreground rounded hover:opacity-90 transition-opacity"
onclick="copyClaudeServerToCodex('${escapeHtml(serverName)}', ${JSON.stringify(serverConfig).replace(/'/g, "&#39;")})"
title="${t('mcp.codex.copyToCodex')}">
<i data-lucide="arrow-right" class="w-3.5 h-3.5 inline"></i> Codex
@@ -366,7 +366,7 @@ async function renderMcpManager() {
<div class="mcp-section">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold text-foreground flex items-center gap-2">
<i data-lucide="circle" class="w-5 h-5 text-blue-500"></i>
<i data-lucide="circle" class="w-5 h-5 text-primary"></i>
${t('mcp.codex.copyFromClaude')}
</h3>
<span class="text-sm text-muted-foreground">${crossCliServers.length} ${t('mcp.serversAvailable')}</span>
@@ -379,10 +379,10 @@ async function renderMcpManager() {
` : `
<!-- CCW Tools MCP Server Card -->
<div class="mcp-section mb-6">
<div class="ccw-tools-card bg-gradient-to-br from-orange-500/10 to-orange-500/5 border-2 ${isCcwToolsInstalled ? 'border-success' : 'border-orange-500/30'} rounded-lg p-6 hover:shadow-lg transition-all">
<div class="ccw-tools-card bg-gradient-to-br from-primary/10 to-primary/5 border-2 ${isCcwToolsInstalled ? 'border-success' : 'border-primary/30'} rounded-lg p-6 hover:shadow-lg transition-all">
<div class="flex items-start justify-between gap-4">
<div class="flex items-start gap-4 flex-1">
<div class="shrink-0 w-12 h-12 bg-orange-500 rounded-lg flex items-center justify-center">
<div class="shrink-0 w-12 h-12 bg-primary rounded-lg flex items-center justify-center">
<i data-lucide="wrench" class="w-6 h-6 text-white"></i>
</div>
<div class="flex-1 min-w-0">
@@ -394,7 +394,7 @@ async function renderMcpManager() {
${enabledTools.length} tools
</span>
` : `
<span class="inline-flex items-center gap-1 px-2 py-0.5 text-xs font-semibold rounded-full bg-orange-500/20 text-orange-600 dark:text-orange-400">
<span class="inline-flex items-center gap-1 px-2 py-0.5 text-xs font-semibold rounded-full bg-primary/20 text-primary">
<i data-lucide="package" class="w-3 h-3"></i>
Available
</span>
@@ -412,15 +412,15 @@ async function renderMcpManager() {
`).join('')}
</div>
<div class="flex items-center gap-3 text-xs">
<button class="text-orange-500 hover:underline" onclick="selectCcwTools('core')">Core only</button>
<button class="text-orange-500 hover:underline" onclick="selectCcwTools('all')">All</button>
<button class="text-primary hover:underline" onclick="selectCcwTools('core')">Core only</button>
<button class="text-primary hover:underline" onclick="selectCcwTools('all')">All</button>
<button class="text-muted-foreground hover:underline" onclick="selectCcwTools('none')">None</button>
</div>
</div>
</div>
<div class="shrink-0 flex gap-2">
${isCcwToolsInstalled ? `
<button class="px-4 py-2 text-sm bg-orange-500 text-white rounded-lg hover:opacity-90 transition-opacity flex items-center gap-1"
<button class="px-4 py-2 text-sm bg-primary text-primary-foreground rounded-lg hover:opacity-90 transition-opacity flex items-center gap-1"
onclick="updateCcwToolsMcp('workspace')"
title="${t('mcp.updateInWorkspace')}">
<i data-lucide="folder" class="w-4 h-4"></i>
@@ -433,7 +433,7 @@ async function renderMcpManager() {
${t('mcp.updateInGlobal')}
</button>
` : `
<button class="px-4 py-2 text-sm bg-orange-500 text-white rounded-lg hover:opacity-90 transition-opacity flex items-center gap-1"
<button class="px-4 py-2 text-sm bg-primary text-primary-foreground rounded-lg hover:opacity-90 transition-opacity flex items-center gap-1"
onclick="installCcwToolsMcp('workspace')"
title="${t('mcp.installToWorkspace')}">
<i data-lucide="folder" class="w-4 h-4"></i>
@@ -546,7 +546,7 @@ async function renderMcpManager() {
<div class="mcp-section mb-6">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold text-foreground flex items-center gap-2">
<i data-lucide="circle-dashed" class="w-5 h-5 text-orange-500"></i>
<i data-lucide="circle-dashed" class="w-5 h-5 text-primary"></i>
${t('mcp.claude.copyFromCodex')}
</h3>
<span class="text-sm text-muted-foreground">${crossCliServers.length} ${t('mcp.serversAvailable')}</span>
@@ -644,12 +644,12 @@ async function renderMcpManager() {
const isStdio = !!serverConfig.command;
const isHttp = !!serverConfig.url;
return `
<div class="mcp-server-card bg-card border ${alreadyInClaude ? 'border-success/50' : 'border-orange-200 dark:border-orange-800'} border-dashed rounded-lg p-4 hover:shadow-md transition-all">
<div class="mcp-server-card bg-card border ${alreadyInClaude ? 'border-success/50' : 'border-primary/20'} border-dashed rounded-lg p-4 hover:shadow-md transition-all">
<div class="flex items-start justify-between mb-3">
<div class="flex items-center gap-2 flex-wrap">
<i data-lucide="code-2" class="w-5 h-5 text-orange-500"></i>
<i data-lucide="code-2" class="w-5 h-5 text-primary"></i>
<h4 class="font-semibold text-foreground">${escapeHtml(serverName)}</h4>
<span class="text-xs px-2 py-0.5 bg-orange-100 text-orange-700 dark:bg-orange-900/30 dark:text-orange-300 rounded-full">Codex</span>
<span class="text-xs px-2 py-0.5 bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-300 rounded-full">Codex</span>
${isHttp
? '<span class="text-xs px-2 py-0.5 bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300 rounded-full">HTTP</span>'
: '<span class="text-xs px-2 py-0.5 bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-300 rounded-full">STDIO</span>'
@@ -1039,7 +1039,7 @@ function renderAvailableServerCardForCodex(serverName, serverInfo) {
${alreadyInCodex ? `<span class="text-xs px-2 py-0.5 bg-success/10 text-success rounded-full">${t('mcp.codex.alreadyAdded')}</span>` : ''}
</div>
${!alreadyInCodex ? `
<button class="px-3 py-1 text-xs bg-orange-500 text-white rounded hover:opacity-90 transition-opacity"
<button class="px-3 py-1 text-xs bg-primary text-primary-foreground rounded hover:opacity-90 transition-opacity"
onclick="copyClaudeServerToCodex('${escapeHtml(originalName)}', ${JSON.stringify(serverConfig).replace(/'/g, "&#39;")})"
title="${t('mcp.codex.copyToCodex')}">
<i data-lucide="arrow-right" class="w-3.5 h-3.5 inline"></i> Codex
@@ -1065,7 +1065,7 @@ function renderAvailableServerCardForCodex(serverName, serverInfo) {
</div>
<div class="mt-3 pt-3 border-t border-border flex items-center gap-2">
<button class="text-xs text-orange-500 hover:text-orange-600 transition-colors flex items-center gap-1"
<button class="text-xs text-primary hover:text-primary/80 transition-colors flex items-center gap-1"
onclick="copyClaudeServerToCodex('${escapeHtml(originalName)}', ${JSON.stringify(serverConfig).replace(/'/g, "&#39;")})"
title="${t('mcp.codex.copyToCodex')}">
<i data-lucide="download" class="w-3 h-3"></i>
@@ -1094,7 +1094,7 @@ function renderCodexServerCard(serverName, serverConfig) {
: `<span class="text-xs px-2 py-0.5 bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-300 rounded-full">STDIO</span>`;
return `
<div class="mcp-server-card bg-card border border-orange-200 dark:border-orange-800 rounded-lg p-4 hover:shadow-md transition-all cursor-pointer ${!isEnabled ? 'opacity-60' : ''}"
<div class="mcp-server-card bg-card border border-primary/20 rounded-lg p-4 hover:shadow-md transition-all cursor-pointer ${!isEnabled ? 'opacity-60' : ''}"
data-server-name="${escapeHtml(serverName)}"
data-server-config="${escapeHtml(JSON.stringify(serverConfig))}"
data-cli-type="codex"
@@ -1102,9 +1102,9 @@ function renderCodexServerCard(serverName, serverConfig) {
title="${t('mcp.clickToEdit')}">
<div class="flex items-start justify-between mb-3">
<div class="flex items-center gap-2 flex-wrap">
<span>${isEnabled ? '<i data-lucide="check-circle" class="w-5 h-5 text-orange-500"></i>' : '<i data-lucide="circle" class="w-5 h-5 text-muted-foreground"></i>'}</span>
<span>${isEnabled ? '<i data-lucide="check-circle" class="w-5 h-5 text-primary"></i>' : '<i data-lucide="circle" class="w-5 h-5 text-muted-foreground"></i>'}</span>
<h4 class="font-semibold text-foreground">${escapeHtml(serverName)}</h4>
<span class="text-xs px-2 py-0.5 bg-orange-100 text-orange-700 dark:bg-orange-900/30 dark:text-orange-300 rounded-full">Codex</span>
<span class="text-xs px-2 py-0.5 bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-300 rounded-full">Codex</span>
${typeBadge}
</div>
<label class="mcp-toggle relative inline-flex items-center cursor-pointer" onclick="event.stopPropagation()">
@@ -1112,7 +1112,7 @@ function renderCodexServerCard(serverName, serverConfig) {
${isEnabled ? 'checked' : ''}
data-server-name="${escapeHtml(serverName)}"
data-action="toggle-codex">
<div class="w-9 h-5 bg-hover peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-4 after:w-4 after:transition-all peer-checked:bg-orange-500"></div>
<div class="w-9 h-5 bg-hover peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-4 after:w-4 after:transition-all peer-checked:bg-primary"></div>
</label>
</div>
@@ -1170,27 +1170,29 @@ function renderCrossCliServerCard(server, isClaude) {
// Icon and color based on source CLI
const icon = fromCli === 'codex' ? 'circle-dashed' : 'circle';
const iconColor = fromCli === 'codex' ? 'orange' : 'blue';
const sourceBadgeColor = fromCli === 'codex' ? 'orange' : 'primary';
const sourceBadgeColor = fromCli === 'codex' ? 'green' : 'orange';
const targetCli = isClaude ? 'project' : 'codex';
const buttonText = isClaude ? t('mcp.codex.copyToClaude') : t('mcp.claude.copyToCodex');
const typeBadge = isHttp
? `<span class="text-xs px-2 py-0.5 bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300 rounded-full">HTTP</span>`
: `<span class="text-xs px-2 py-0.5 bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-300 rounded-full">STDIO</span>`;
: `<span class="text-xs px-2 py-0.5 bg-muted text-muted-foreground rounded-full">STDIO</span>`;
// CLI badge with color
const cliBadge = fromCli === 'codex'
? `<span class="text-xs px-2 py-0.5 bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-300 rounded-full">Codex</span>`
: `<span class="text-xs px-2 py-0.5 bg-orange-100 text-orange-700 dark:bg-orange-900/30 dark:text-orange-300 rounded-full">Claude</span>`;
return `
<div class="mcp-server-card bg-card border border-dashed border-${iconColor}-200 dark:border-${iconColor}-800 rounded-lg p-4 hover:shadow-md hover:border-solid transition-all">
<div class="mcp-server-card bg-card border border-dashed border-primary/20 rounded-lg p-4 hover:shadow-md hover:border-solid transition-all">
<div class="flex items-start justify-between mb-3">
<div class="flex items-start gap-3">
<div class="shrink-0">
<i data-lucide="${icon}" class="w-5 h-5 text-${iconColor}-500"></i>
<i data-lucide="${icon}" class="w-5 h-5 text-primary"></i>
</div>
<div>
<div class="flex items-center gap-2 flex-wrap mb-1">
<h4 class="font-semibold text-foreground">${escapeHtml(name)}</h4>
<span class="text-xs px-2 py-0.5 bg-${sourceBadgeColor}/10 text-${sourceBadgeColor} rounded-full">
${fromCli === 'codex' ? 'Codex' : 'Claude'}
</span>
${cliBadge}
${typeBadge}
</div>
<div class="text-sm space-y-1 text-muted-foreground">
@@ -1209,7 +1211,7 @@ function renderCrossCliServerCard(server, isClaude) {
</div>
</div>
<div class="mt-3 pt-3 border-t border-border">
<button class="w-full px-3 py-2 text-sm font-medium bg-${iconColor}-500 hover:bg-${iconColor}-600 text-white rounded-lg transition-colors flex items-center justify-center gap-1.5"
<button class="w-full px-3 py-2 text-sm font-medium bg-primary hover:bg-primary/90 text-primary-foreground rounded-lg transition-colors flex items-center justify-center gap-1.5"
onclick="copyCrossCliServer('${escapeHtml(name)}', ${JSON.stringify(config).replace(/'/g, "&#39;")}, '${fromCli}', '${targetCli}')">
<i data-lucide="copy" class="w-4 h-4"></i>
${buttonText}