Skip to content

Commit 369b470

Browse files
author
catlog22
committed
feat: enhance CoordinatorEmptyState and ThemeSelector with gradient utilities and improved layout
1 parent de989aa commit 369b470

6 files changed

Lines changed: 79 additions & 58 deletions

File tree

ccw/frontend/src/components/coordinator/CoordinatorEmptyState.tsx

Lines changed: 12 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -49,27 +49,18 @@ export function CoordinatorEmptyState({
4949
{/* Animated Gradient Orbs - Using gradient utility classes */}
5050
<div className="absolute top-20 left-20 w-72 h-72 rounded-full blur-3xl animate-pulse bg-gradient-primary opacity-15" />
5151
<div
52-
className="absolute bottom-20 right-20 w-96 h-96 rounded-full blur-3xl animate-pulse opacity-15"
53-
style={{
54-
background: 'radial-gradient(circle, hsl(var(--secondary)) 0%, transparent 70%)',
55-
animationDelay: '1s',
56-
}}
52+
className="absolute bottom-20 right-20 w-96 h-96 rounded-full blur-3xl animate-pulse opacity-15 bg-gradient-secondary"
53+
style={{ animationDelay: '1s' }}
5754
/>
5855
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-80 h-80 rounded-full blur-3xl animate-pulse bg-gradient-primary opacity-10" style={{ animationDelay: '2s' }} />
5956
</div>
6057

6158
{/* Main Content */}
6259
<div className="relative z-10 max-w-2xl mx-auto px-8 text-center">
63-
{/* Hero Icon */}
60+
{/* Hero Icon - Using gradient brand background */}
6461
<div className="relative mb-8 inline-block">
65-
<div
66-
className="absolute inset-0 rounded-full blur-2xl opacity-40 animate-pulse"
67-
style={{ background: 'hsl(var(--primary))' }}
68-
/>
69-
<div
70-
className="relative p-6 rounded-full shadow-2xl text-white"
71-
style={{ background: 'hsl(var(--primary))' }}
72-
>
62+
<div className="absolute inset-0 rounded-full blur-2xl opacity-40 animate-pulse bg-gradient-brand" />
63+
<div className="relative p-6 rounded-full shadow-2xl text-white bg-primary hover-glow-primary">
7364
<Rocket className="w-16 h-16" strokeWidth={2} />
7465
</div>
7566
</div>
@@ -84,23 +75,15 @@ export function CoordinatorEmptyState({
8475
{formatMessage({ id: 'coordinator.emptyState.subtitle' })}
8576
</p>
8677

87-
{/* Start Button - Using primary theme color */}
78+
{/* Start Button - Using gradient and glow utilities */}
8879
<Button
8980
size="lg"
9081
onClick={onStart}
9182
disabled={disabled}
92-
className="group relative px-8 py-6 text-lg font-semibold shadow-lg hover:shadow-xl transition-all duration-300"
93-
style={{
94-
background: 'hsl(var(--primary))',
95-
color: 'hsl(var(--primary-foreground))',
96-
}}
83+
className="group relative px-8 py-6 text-lg font-semibold shadow-lg hover:shadow-xl transition-all duration-300 bg-primary text-primary-foreground hover-glow-primary"
9784
>
9885
<Play className="w-6 h-6 mr-2 group-hover:scale-110 transition-transform" />
9986
{formatMessage({ id: 'coordinator.emptyState.startButton' })}
100-
<div
101-
className="absolute inset-0 rounded-lg opacity-0 group-hover:opacity-100 transition-opacity blur-xl"
102-
style={{ background: 'hsl(var(--primary) / 0.3)' }}
103-
/>
10487
</Button>
10588

10689
{/* Feature Cards */}
@@ -175,38 +158,26 @@ export function CoordinatorEmptyState({
175158
{/* Quick Start Guide */}
176159
<div className="mt-12 text-left bg-card/50 backdrop-blur-sm rounded-xl p-6 border border-border">
177160
<h3 className="font-semibold mb-4 text-foreground flex items-center gap-2">
178-
<span
179-
className="w-6 h-6 rounded-full flex items-center justify-center text-white text-xs font-semibold"
180-
style={{ background: 'hsl(var(--primary))' }}
181-
>
182-
161+
<span className="w-6 h-6 rounded-full flex items-center justify-center text-primary-foreground text-xs font-semibold bg-primary">
162+
ok
183163
</span>
184164
{formatMessage({ id: 'coordinator.emptyState.quickStart.title' })}
185165
</h3>
186166
<div className="space-y-3 text-sm text-muted-foreground">
187167
<div className="flex items-start gap-3">
188-
<span
189-
className="w-5 h-5 rounded-full flex items-center justify-center text-xs font-semibold shrink-0 mt-0.5 text-white"
190-
style={{ background: 'hsl(var(--primary))' }}
191-
>
168+
<span className="w-5 h-5 rounded-full flex items-center justify-center text-xs font-semibold shrink-0 mt-0.5 text-white bg-primary">
192169
1
193170
</span>
194171
<p>{formatMessage({ id: 'coordinator.emptyState.quickStart.step1' })}</p>
195172
</div>
196173
<div className="flex items-start gap-3">
197-
<span
198-
className="w-5 h-5 rounded-full flex items-center justify-center text-xs font-semibold shrink-0 mt-0.5 text-white"
199-
style={{ background: 'hsl(var(--secondary))' }}
200-
>
174+
<span className="w-5 h-5 rounded-full flex items-center justify-center text-xs font-semibold shrink-0 mt-0.5 text-white bg-secondary">
201175
2
202176
</span>
203177
<p>{formatMessage({ id: 'coordinator.emptyState.quickStart.step2' })}</p>
204178
</div>
205179
<div className="flex items-start gap-3">
206-
<span
207-
className="w-5 h-5 rounded-full flex items-center justify-center text-xs font-semibold shrink-0 mt-0.5 text-white"
208-
style={{ background: 'hsl(var(--accent))' }}
209-
>
180+
<span className="w-5 h-5 rounded-full flex items-center justify-center text-xs font-semibold shrink-0 mt-0.5 text-white bg-accent">
210181
3
211182
</span>
212183
<p>{formatMessage({ id: 'coordinator.emptyState.quickStart.step3' })}</p>

ccw/frontend/src/components/shared/ThemeSelector.tsx

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -192,25 +192,31 @@ export function ThemeSelector() {
192192
/>
193193

194194
{/* Preview Swatches */}
195-
<div className="flex gap-2 items-center">
196-
<span className="text-xs text-text-secondary mr-2">
195+
<div className="flex gap-3 items-end">
196+
<span className="text-xs text-text-secondary pb-1">
197197
{formatMessage({ id: 'theme.preview' })}:
198198
</span>
199-
<div
200-
className="w-10 h-10 rounded border-2 border-border shadow-sm"
201-
style={{ backgroundColor: getPreviewColor('--bg') }}
202-
title="Background"
203-
/>
204-
<div
205-
className="w-10 h-10 rounded border-2 border-border shadow-sm"
206-
style={{ backgroundColor: getPreviewColor('--surface') }}
207-
title="Surface"
208-
/>
209-
<div
210-
className="w-10 h-10 rounded border-2 border-border shadow-sm"
211-
style={{ backgroundColor: getPreviewColor('--accent') }}
212-
title="Accent"
213-
/>
199+
<div className="flex flex-col items-center gap-1">
200+
<div
201+
className="w-10 h-10 rounded border-2 border-border shadow-sm"
202+
style={{ backgroundColor: getPreviewColor('--bg') }}
203+
/>
204+
<span className="text-[10px] text-text-tertiary">{formatMessage({ id: 'theme.preview.background' })}</span>
205+
</div>
206+
<div className="flex flex-col items-center gap-1">
207+
<div
208+
className="w-10 h-10 rounded border-2 border-border shadow-sm"
209+
style={{ backgroundColor: getPreviewColor('--surface') }}
210+
/>
211+
<span className="text-[10px] text-text-tertiary">{formatMessage({ id: 'theme.preview.surface' })}</span>
212+
</div>
213+
<div className="flex flex-col items-center gap-1">
214+
<div
215+
className="w-10 h-10 rounded border-2 border-border shadow-sm"
216+
style={{ backgroundColor: getPreviewColor('--accent') }}
217+
/>
218+
<span className="text-[10px] text-text-tertiary">{formatMessage({ id: 'theme.preview.accent' })}</span>
219+
</div>
214220
</div>
215221

216222
{/* Save and Reset Buttons */}

ccw/frontend/src/locales/en/theme.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
"current": "Current theme: {name}",
2323
"hueValue": "Hue: {value}°",
2424
"preview": "Preview",
25+
"preview.background": "Background",
26+
"preview.surface": "Card",
27+
"preview.accent": "Accent",
2528
"save": "Save Custom Theme",
2629
"reset": "Reset to Preset"
2730
}

ccw/frontend/src/locales/zh/theme.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
"current": "当前主题: {name}",
2323
"hueValue": "色调: {value}°",
2424
"preview": "预览",
25+
"preview.background": "背景",
26+
"preview.surface": "卡片",
27+
"preview.accent": "强调色",
2528
"save": "保存自定义主题",
2629
"reset": "重置为预设"
2730
}

ccw/frontend/tailwind.config.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ const gradientPlugin = plugin(function({ addUtilities, addComponents }) {
88
'.bg-gradient-primary': {
99
backgroundImage: 'radial-gradient(circle, hsl(var(--accent)) 0%, transparent 70%)',
1010
},
11+
'.bg-gradient-secondary': {
12+
backgroundImage: 'radial-gradient(circle, hsl(var(--secondary)) 0%, transparent 70%)',
13+
},
1114
'.bg-gradient-brand': {
1215
backgroundImage: 'linear-gradient(to right, hsl(var(--primary)), hsl(var(--secondary)))',
1316
},
@@ -17,6 +20,19 @@ const gradientPlugin = plugin(function({ addUtilities, addComponents }) {
1720
'.bg-gradient-conic': {
1821
backgroundImage: 'conic-gradient(var(--tw-gradient-stops))',
1922
},
23+
// Hover glow effect utilities
24+
'.hover-glow': {
25+
transition: 'box-shadow 0.3s ease-in-out',
26+
'&:hover': {
27+
boxShadow: '0 0 40px 10px hsl(var(--accent) / 0.7)',
28+
},
29+
},
30+
'.hover-glow-primary': {
31+
transition: 'box-shadow 0.3s ease-in-out',
32+
'&:hover': {
33+
boxShadow: '0 0 40px 10px hsl(var(--primary) / 0.5)',
34+
},
35+
},
2036
});
2137

2238
// 2. Gradient border component

ccw/tests/cli-final-only-output.test.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,13 @@
88
import { afterEach, describe, it, mock } from 'node:test';
99
import assert from 'node:assert/strict';
1010
import http from 'node:http';
11+
import { mkdtempSync, rmSync } from 'node:fs';
12+
import { tmpdir } from 'node:os';
13+
import { join } from 'node:path';
1114

1215
const cliCommandPath = new URL('../dist/commands/cli.js', import.meta.url).href;
1316
const cliExecutorPath = new URL('../dist/tools/cli-executor.js', import.meta.url).href;
17+
const historyStorePath = new URL('../dist/tools/cli-history-store.js', import.meta.url).href;
1418

1519
function stubHttpRequest() {
1620
mock.method(http, 'request', () => {
@@ -35,8 +39,17 @@ describe('ccw cli exec --final', async () => {
3539
it('writes only finalOutput to stdout (no banner/summary)', async () => {
3640
stubHttpRequest();
3741

42+
const testHome = mkdtempSync(join(tmpdir(), 'ccw-cli-final-only-'));
43+
const prevHome = process.env.CCW_DATA_DIR;
44+
process.env.CCW_DATA_DIR = testHome;
45+
46+
// Ensure the CLI doesn't wait for stdin in Node's test runner environment.
47+
const prevStdinIsTty = process.stdin.isTTY;
48+
Object.defineProperty(process.stdin, 'isTTY', { value: true, configurable: true });
49+
3850
const cliModule = await import(cliCommandPath);
3951
const cliExecutorModule = await import(cliExecutorPath);
52+
const historyStoreModule = await import(historyStorePath);
4053

4154
const stdoutWrites = [];
4255
mock.method(process.stdout, 'write', (chunk) => {
@@ -72,5 +85,14 @@ describe('ccw cli exec --final', async () => {
7285
await cliModule.cliCommand('exec', [], { prompt: 'Hello', tool: 'gemini', final: true });
7386

7487
assert.equal(stdoutWrites.join(''), 'FINAL');
88+
89+
try {
90+
historyStoreModule?.closeAllStores?.();
91+
} catch {
92+
// ignore
93+
}
94+
Object.defineProperty(process.stdin, 'isTTY', { value: prevStdinIsTty, configurable: true });
95+
process.env.CCW_DATA_DIR = prevHome;
96+
rmSync(testHome, { recursive: true, force: true });
7597
});
7698
});

0 commit comments

Comments
 (0)