From 369b4709693b65b46750d9647e42dc93c583af73 Mon Sep 17 00:00:00 2001 From: catlog22 Date: Wed, 4 Feb 2026 23:06:00 +0800 Subject: [PATCH] feat: enhance CoordinatorEmptyState and ThemeSelector with gradient utilities and improved layout --- .../coordinator/CoordinatorEmptyState.tsx | 53 +++++-------------- .../src/components/shared/ThemeSelector.tsx | 40 ++++++++------ ccw/frontend/src/locales/en/theme.json | 3 ++ ccw/frontend/src/locales/zh/theme.json | 3 ++ ccw/frontend/tailwind.config.js | 16 ++++++ ccw/tests/cli-final-only-output.test.js | 22 ++++++++ 6 files changed, 79 insertions(+), 58 deletions(-) diff --git a/ccw/frontend/src/components/coordinator/CoordinatorEmptyState.tsx b/ccw/frontend/src/components/coordinator/CoordinatorEmptyState.tsx index 4910ceb0..36ace1b2 100644 --- a/ccw/frontend/src/components/coordinator/CoordinatorEmptyState.tsx +++ b/ccw/frontend/src/components/coordinator/CoordinatorEmptyState.tsx @@ -49,27 +49,18 @@ export function CoordinatorEmptyState({ {/* Animated Gradient Orbs - Using gradient utility classes */}
{/* Main Content */}
- {/* Hero Icon */} + {/* Hero Icon - Using gradient brand background */}
-
-
+
+
@@ -84,23 +75,15 @@ export function CoordinatorEmptyState({ {formatMessage({ id: 'coordinator.emptyState.subtitle' })}

- {/* Start Button - Using primary theme color */} + {/* Start Button - Using gradient and glow utilities */} {/* Feature Cards */} @@ -175,38 +158,26 @@ export function CoordinatorEmptyState({ {/* Quick Start Guide */}

- - ✓ + + ok {formatMessage({ id: 'coordinator.emptyState.quickStart.title' })}

- + 1

{formatMessage({ id: 'coordinator.emptyState.quickStart.step1' })}

- + 2

{formatMessage({ id: 'coordinator.emptyState.quickStart.step2' })}

- + 3

{formatMessage({ id: 'coordinator.emptyState.quickStart.step3' })}

diff --git a/ccw/frontend/src/components/shared/ThemeSelector.tsx b/ccw/frontend/src/components/shared/ThemeSelector.tsx index 08946de0..29786a13 100644 --- a/ccw/frontend/src/components/shared/ThemeSelector.tsx +++ b/ccw/frontend/src/components/shared/ThemeSelector.tsx @@ -192,25 +192,31 @@ export function ThemeSelector() { /> {/* Preview Swatches */} -
- +
+ {formatMessage({ id: 'theme.preview' })}: -
-
-
+
+
+ {formatMessage({ id: 'theme.preview.background' })} +
+
+
+ {formatMessage({ id: 'theme.preview.surface' })} +
+
+
+ {formatMessage({ id: 'theme.preview.accent' })} +
{/* Save and Reset Buttons */} diff --git a/ccw/frontend/src/locales/en/theme.json b/ccw/frontend/src/locales/en/theme.json index 4dc6683a..c86b445d 100644 --- a/ccw/frontend/src/locales/en/theme.json +++ b/ccw/frontend/src/locales/en/theme.json @@ -22,6 +22,9 @@ "current": "Current theme: {name}", "hueValue": "Hue: {value}°", "preview": "Preview", + "preview.background": "Background", + "preview.surface": "Card", + "preview.accent": "Accent", "save": "Save Custom Theme", "reset": "Reset to Preset" } diff --git a/ccw/frontend/src/locales/zh/theme.json b/ccw/frontend/src/locales/zh/theme.json index a7935b40..9909340d 100644 --- a/ccw/frontend/src/locales/zh/theme.json +++ b/ccw/frontend/src/locales/zh/theme.json @@ -22,6 +22,9 @@ "current": "当前主题: {name}", "hueValue": "色调: {value}°", "preview": "预览", + "preview.background": "背景", + "preview.surface": "卡片", + "preview.accent": "强调色", "save": "保存自定义主题", "reset": "重置为预设" } diff --git a/ccw/frontend/tailwind.config.js b/ccw/frontend/tailwind.config.js index 6f598ec7..73266d77 100644 --- a/ccw/frontend/tailwind.config.js +++ b/ccw/frontend/tailwind.config.js @@ -8,6 +8,9 @@ const gradientPlugin = plugin(function({ addUtilities, addComponents }) { '.bg-gradient-primary': { backgroundImage: 'radial-gradient(circle, hsl(var(--accent)) 0%, transparent 70%)', }, + '.bg-gradient-secondary': { + backgroundImage: 'radial-gradient(circle, hsl(var(--secondary)) 0%, transparent 70%)', + }, '.bg-gradient-brand': { backgroundImage: 'linear-gradient(to right, hsl(var(--primary)), hsl(var(--secondary)))', }, @@ -17,6 +20,19 @@ const gradientPlugin = plugin(function({ addUtilities, addComponents }) { '.bg-gradient-conic': { backgroundImage: 'conic-gradient(var(--tw-gradient-stops))', }, + // Hover glow effect utilities + '.hover-glow': { + transition: 'box-shadow 0.3s ease-in-out', + '&:hover': { + boxShadow: '0 0 40px 10px hsl(var(--accent) / 0.7)', + }, + }, + '.hover-glow-primary': { + transition: 'box-shadow 0.3s ease-in-out', + '&:hover': { + boxShadow: '0 0 40px 10px hsl(var(--primary) / 0.5)', + }, + }, }); // 2. Gradient border component diff --git a/ccw/tests/cli-final-only-output.test.js b/ccw/tests/cli-final-only-output.test.js index 72f0f098..e51002ca 100644 --- a/ccw/tests/cli-final-only-output.test.js +++ b/ccw/tests/cli-final-only-output.test.js @@ -8,9 +8,13 @@ import { afterEach, describe, it, mock } from 'node:test'; import assert from 'node:assert/strict'; import http from 'node:http'; +import { mkdtempSync, rmSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; const cliCommandPath = new URL('../dist/commands/cli.js', import.meta.url).href; const cliExecutorPath = new URL('../dist/tools/cli-executor.js', import.meta.url).href; +const historyStorePath = new URL('../dist/tools/cli-history-store.js', import.meta.url).href; function stubHttpRequest() { mock.method(http, 'request', () => { @@ -35,8 +39,17 @@ describe('ccw cli exec --final', async () => { it('writes only finalOutput to stdout (no banner/summary)', async () => { stubHttpRequest(); + const testHome = mkdtempSync(join(tmpdir(), 'ccw-cli-final-only-')); + const prevHome = process.env.CCW_DATA_DIR; + process.env.CCW_DATA_DIR = testHome; + + // Ensure the CLI doesn't wait for stdin in Node's test runner environment. + const prevStdinIsTty = process.stdin.isTTY; + Object.defineProperty(process.stdin, 'isTTY', { value: true, configurable: true }); + const cliModule = await import(cliCommandPath); const cliExecutorModule = await import(cliExecutorPath); + const historyStoreModule = await import(historyStorePath); const stdoutWrites = []; mock.method(process.stdout, 'write', (chunk) => { @@ -72,5 +85,14 @@ describe('ccw cli exec --final', async () => { await cliModule.cliCommand('exec', [], { prompt: 'Hello', tool: 'gemini', final: true }); assert.equal(stdoutWrites.join(''), 'FINAL'); + + try { + historyStoreModule?.closeAllStores?.(); + } catch { + // ignore + } + Object.defineProperty(process.stdin, 'isTTY', { value: prevStdinIsTty, configurable: true }); + process.env.CCW_DATA_DIR = prevHome; + rmSync(testHome, { recursive: true, force: true }); }); });