diff --git a/ccw/src/templates/dashboard-css/10-cli-status.css b/ccw/src/templates/dashboard-css/10-cli-status.css index 8522a709..3af1a485 100644 --- a/ccw/src/templates/dashboard-css/10-cli-status.css +++ b/ccw/src/templates/dashboard-css/10-cli-status.css @@ -284,6 +284,37 @@ border-radius: 0.25rem; } +/* Tag color variants */ +.tag-item.tag-分析, .tool-tag.tag-分析 { + background: hsl(210 100% 50% / 0.15); + color: hsl(210 100% 45%); +} + +.tag-item.tag-编码, .tool-tag.tag-编码 { + background: hsl(142 76% 36% / 0.15); + color: hsl(142 76% 36%); +} + +.tag-item.tag-Debug, .tool-tag.tag-Debug { + background: hsl(0 84% 60% / 0.15); + color: hsl(0 84% 50%); +} + +.tag-item.tag-重构, .tool-tag.tag-重构 { + background: hsl(280 70% 50% / 0.15); + color: hsl(280 70% 45%); +} + +.tag-item.tag-测试, .tool-tag.tag-测试 { + background: hsl(45 93% 47% / 0.15); + color: hsl(45 93% 35%); +} + +.tag-item.tag-文档, .tool-tag.tag-文档 { + background: hsl(180 70% 40% / 0.15); + color: hsl(180 70% 35%); +} + .tag-remove { display: flex; align-items: center; @@ -334,6 +365,15 @@ border-color: hsl(var(--primary) / 0.3); } +.predefined-tag-btn.selected, +.predefined-tag-btn:disabled { + background: hsl(var(--primary) / 0.1); + color: hsl(var(--muted-foreground)); + border-color: hsl(var(--primary) / 0.2); + opacity: 0.6; + cursor: not-allowed; +} + .predefined-tag-btn i { opacity: 0.7; } diff --git a/ccw/src/templates/dashboard-js/views/cli-manager.js b/ccw/src/templates/dashboard-js/views/cli-manager.js index d2e8af1e..364ea35a 100644 --- a/ccw/src/templates/dashboard-js/views/cli-manager.js +++ b/ccw/src/templates/dashboard-js/views/cli-manager.js @@ -335,7 +335,7 @@ function buildToolConfigModalContent(tool, config, models, status) { '

Tags (optional labels)

' + '
' + (config.tags || []).map(function(tag) { - return '' + escapeHtml(tag) + ''; + return '' + escapeHtml(tag) + ''; }).join('') + '' + '
' + @@ -375,7 +375,7 @@ function initToolConfigModalEvents(tool, currentConfig, models) { // Insert tags before the input currentTags.forEach(function(tag) { var tagEl = document.createElement('span'); - tagEl.className = 'tag-item'; + tagEl.className = 'tag-item tag-' + escapeHtml(tag); tagEl.innerHTML = escapeHtml(tag) + ''; container.insertBefore(tagEl, input); }); @@ -389,6 +389,18 @@ function initToolConfigModalEvents(tool, currentConfig, models) { renderTags(); }; }); + + // Update predefined tag buttons state + document.querySelectorAll('.predefined-tag-btn').forEach(function(btn) { + var tag = btn.getAttribute('data-tag'); + if (currentTags.indexOf(tag) !== -1) { + btn.classList.add('selected'); + btn.disabled = true; + } else { + btn.classList.remove('selected'); + btn.disabled = false; + } + }); } // Click on unified input container focuses the input @@ -665,10 +677,10 @@ function renderToolsSection() { var toolConfig = cliToolConfig && cliToolConfig.tools ? cliToolConfig.tools[tool] : null; var tags = toolConfig && toolConfig.tags ? toolConfig.tags : []; - // Build tags HTML + // Build tags HTML with color classes var tagsHtml = tags.length > 0 ? '
' + tags.map(function(tag) { - return '' + escapeHtml(tag) + ''; + return '' + escapeHtml(tag) + ''; }).join('') + '
' : ''; diff --git a/ccw/src/tools/cli-config-manager.ts b/ccw/src/tools/cli-config-manager.ts index 71847bd8..13047dc8 100644 --- a/ccw/src/tools/cli-config-manager.ts +++ b/ccw/src/tools/cli-config-manager.ts @@ -185,6 +185,24 @@ export function getToolConfig(baseDir: string, tool: string): CliToolConfig { return config.tools[tool] || DEFAULT_CONFIG.tools[tool]; } +/** + * Validate and sanitize tags array + * @param tags - Raw tags array from user input + * @returns Sanitized tags array + */ +function validateTags(tags: string[] | undefined): string[] | undefined { + if (!tags || !Array.isArray(tags)) return undefined; + + const MAX_TAGS = 10; + const MAX_TAG_LENGTH = 30; + + return tags + .filter(tag => typeof tag === 'string') + .map(tag => tag.trim()) + .filter(tag => tag.length > 0 && tag.length <= MAX_TAG_LENGTH) + .slice(0, MAX_TAGS); +} + /** * Update configuration for a specific tool * Returns the updated tool config @@ -206,7 +224,7 @@ export function updateToolConfig( enabled: updates.enabled !== undefined ? updates.enabled : currentToolConfig.enabled, primaryModel: updates.primaryModel || currentToolConfig.primaryModel, secondaryModel: updates.secondaryModel || currentToolConfig.secondaryModel, - tags: updates.tags !== undefined ? updates.tags : currentToolConfig.tags + tags: updates.tags !== undefined ? validateTags(updates.tags) : currentToolConfig.tags }; // Save updated config