diff --git a/ccw/frontend/src/components/shared/ThemeSelector.tsx b/ccw/frontend/src/components/shared/ThemeSelector.tsx index 29786a13..0fbcc5cb 100644 --- a/ccw/frontend/src/components/shared/ThemeSelector.tsx +++ b/ccw/frontend/src/components/shared/ThemeSelector.tsx @@ -20,7 +20,21 @@ import { generateThemeFromHue } from '@/lib/colorGenerator'; */ export function ThemeSelector() { const { formatMessage } = useIntl(); - const { colorScheme, resolvedTheme, customHue, isCustomTheme, setColorScheme, setTheme, setCustomHue } = useTheme(); + const { + colorScheme, + resolvedTheme, + customHue, + isCustomTheme, + gradientLevel, + enableHoverGlow, + enableBackgroundAnimation, + setColorScheme, + setTheme, + setCustomHue, + setGradientLevel, + setEnableHoverGlow, + setEnableBackgroundAnimation, + } = useTheme(); // Local state for preview hue (uncommitted changes) const [previewHue, setPreviewHue] = useState(customHue); @@ -251,6 +265,74 @@ export function ThemeSelector() { )} + {/* Gradient Effects Settings */} +
+

+ {formatMessage({ id: 'theme.gradient.title' })} +

+ + {/* Gradient Level Selection */} +
+
+ {(['off', 'standard', 'enhanced'] as const).map((level) => ( + + ))} +
+ + {/* Hover Glow Checkbox */} + + + {/* Background Animation Checkbox */} + +
+
+ {/* Theme Mode Selection */}

diff --git a/ccw/frontend/src/hooks/useTheme.ts b/ccw/frontend/src/hooks/useTheme.ts index b1441696..301f1e07 100644 --- a/ccw/frontend/src/hooks/useTheme.ts +++ b/ccw/frontend/src/hooks/useTheme.ts @@ -4,8 +4,17 @@ // Convenient hook for theme management with multi-color scheme support import { useCallback } from 'react'; -import { useAppStore, selectTheme, selectResolvedTheme, selectCustomHue, selectIsCustomTheme } from '../stores/appStore'; -import type { Theme, ColorScheme } from '../types/store'; +import { + useAppStore, + selectTheme, + selectResolvedTheme, + selectCustomHue, + selectIsCustomTheme, + selectGradientLevel, + selectEnableHoverGlow, + selectEnableBackgroundAnimation, +} from '../stores/appStore'; +import type { Theme, ColorScheme, GradientLevel } from '../types/store'; export interface UseThemeReturn { /** Current theme preference ('light', 'dark', 'system') */ @@ -20,6 +29,12 @@ export interface UseThemeReturn { customHue: number | null; /** Whether the current theme is a custom theme */ isCustomTheme: boolean; + /** Gradient level: 'off', 'standard', or 'enhanced' */ + gradientLevel: GradientLevel; + /** Whether hover glow effects are enabled */ + enableHoverGlow: boolean; + /** Whether background gradient animation is enabled */ + enableBackgroundAnimation: boolean; /** Set theme preference */ setTheme: (theme: Theme) => void; /** Set color scheme */ @@ -28,6 +43,12 @@ export interface UseThemeReturn { setCustomHue: (hue: number | null) => void; /** Toggle between light and dark (ignores system) */ toggleTheme: () => void; + /** Set gradient level */ + setGradientLevel: (level: GradientLevel) => void; + /** Set hover glow enabled */ + setEnableHoverGlow: (enabled: boolean) => void; + /** Set background animation enabled */ + setEnableBackgroundAnimation: (enabled: boolean) => void; } /** @@ -54,10 +75,16 @@ export function useTheme(): UseThemeReturn { const colorScheme = useAppStore((state) => state.colorScheme); const customHue = useAppStore(selectCustomHue); const isCustomTheme = useAppStore(selectIsCustomTheme); + const gradientLevel = useAppStore(selectGradientLevel); + const enableHoverGlow = useAppStore(selectEnableHoverGlow); + const enableBackgroundAnimation = useAppStore(selectEnableBackgroundAnimation); const setThemeAction = useAppStore((state) => state.setTheme); const setColorSchemeAction = useAppStore((state) => state.setColorScheme); const setCustomHueAction = useAppStore((state) => state.setCustomHue); const toggleThemeAction = useAppStore((state) => state.toggleTheme); + const setGradientLevelAction = useAppStore((state) => state.setGradientLevel); + const setEnableHoverGlowAction = useAppStore((state) => state.setEnableHoverGlow); + const setEnableBackgroundAnimationAction = useAppStore((state) => state.setEnableBackgroundAnimation); const setTheme = useCallback( (newTheme: Theme) => { @@ -84,6 +111,27 @@ export function useTheme(): UseThemeReturn { toggleThemeAction(); }, [toggleThemeAction]); + const setGradientLevel = useCallback( + (level: GradientLevel) => { + setGradientLevelAction(level); + }, + [setGradientLevelAction] + ); + + const setEnableHoverGlow = useCallback( + (enabled: boolean) => { + setEnableHoverGlowAction(enabled); + }, + [setEnableHoverGlowAction] + ); + + const setEnableBackgroundAnimation = useCallback( + (enabled: boolean) => { + setEnableBackgroundAnimationAction(enabled); + }, + [setEnableBackgroundAnimationAction] + ); + return { theme, resolvedTheme, @@ -91,9 +139,15 @@ export function useTheme(): UseThemeReturn { colorScheme, customHue, isCustomTheme, + gradientLevel, + enableHoverGlow, + enableBackgroundAnimation, setTheme, setColorScheme, setCustomHue, toggleTheme, + setGradientLevel, + setEnableHoverGlow, + setEnableBackgroundAnimation, }; } diff --git a/ccw/frontend/src/index.css b/ccw/frontend/src/index.css index 10807950..ccdd5a12 100644 --- a/ccw/frontend/src/index.css +++ b/ccw/frontend/src/index.css @@ -433,3 +433,105 @@ .animate-spin { animation: spin 1s linear infinite; } + +/* =========================== + Gradient Effects System + Conditional styles based on data attributes + =========================== */ + +/* Disable gradients when off */ +[data-gradient="off"] .bg-gradient-primary, +[data-gradient="off"] .bg-gradient-brand, +[data-gradient="off"] .bg-gradient-accent { + background-image: none !important; + background: inherit; +} + +[data-gradient="off"] .gradient-text { + background-image: none !important; + -webkit-background-clip: unset; + background-clip: unset; + -webkit-text-fill-color: inherit; +} + +/* Standard gradients (default) */ +[data-gradient="standard"] .bg-gradient-primary { + background: linear-gradient(135deg, hsl(var(--primary)) 0%, hsl(var(--accent)) 100%); +} + +[data-gradient="standard"] .bg-gradient-brand { + background: linear-gradient(135deg, hsl(var(--accent)) 0%, hsl(var(--primary)) 100%); +} + +[data-gradient="standard"] .bg-gradient-accent { + background: linear-gradient(90deg, hsl(var(--accent)) 0%, hsl(var(--primary)) 100%); +} + +/* Enhanced gradients - more vibrant with multiple color stops */ +[data-gradient="enhanced"] .bg-gradient-primary { + background: linear-gradient(135deg, + hsl(var(--primary)) 0%, + hsl(var(--accent)) 50%, + hsl(var(--secondary)) 100% + ); +} + +[data-gradient="enhanced"] .bg-gradient-brand { + background: linear-gradient(135deg, + hsl(var(--accent)) 0%, + hsl(var(--primary)) 40%, + hsl(var(--secondary)) 100% + ); +} + +[data-gradient="enhanced"] .bg-gradient-accent { + background: linear-gradient(90deg, + hsl(var(--accent)) 0%, + hsl(var(--primary)) 50%, + hsl(var(--accent)) 100% + ); +} + +/* Hover glow effects - disabled when data-hover-glow="false" */ +.hover-glow, +.hover-glow-primary { + transition: box-shadow 0.3s ease; +} + +.hover-glow:hover { + box-shadow: 0 0 20px hsla(var(--accent), 0.4); +} + +.hover-glow-primary:hover { + box-shadow: 0 0 20px hsla(var(--primary), 0.4); +} + +[data-hover-glow="false"] .hover-glow:hover, +[data-hover-glow="false"] .hover-glow-primary:hover { + box-shadow: none; +} + +/* Background animation keyframes */ +@keyframes slow-gradient { + 0% { + background-position: 0% 50%; + } + 50% { + background-position: 100% 50%; + } + 100% { + background-position: 0% 50%; + } +} + +/* Animated background gradient class */ +.animate-slow-gradient { + background-size: 200% 200%; + animation: slow-gradient 15s ease infinite; +} + +/* Disable background animation when data-bg-animation="false" */ +[data-bg-animation="false"] .animate-slow-gradient { + animation: none; + background-size: 100% 100%; +} diff --git a/ccw/frontend/src/locales/en/theme.json b/ccw/frontend/src/locales/en/theme.json index c86b445d..428e6d69 100644 --- a/ccw/frontend/src/locales/en/theme.json +++ b/ccw/frontend/src/locales/en/theme.json @@ -26,5 +26,13 @@ "preview.surface": "Card", "preview.accent": "Accent", "save": "Save Custom Theme", - "reset": "Reset to Preset" + "reset": "Reset to Preset", + "gradient": { + "title": "Gradient Effects", + "off": "Off", + "standard": "Standard", + "enhanced": "Enhanced", + "hoverGlow": "Enable hover glow effects", + "bgAnimation": "Enable background gradient animation" + } } diff --git a/ccw/frontend/src/locales/zh/theme.json b/ccw/frontend/src/locales/zh/theme.json index 9909340d..d27df90c 100644 --- a/ccw/frontend/src/locales/zh/theme.json +++ b/ccw/frontend/src/locales/zh/theme.json @@ -26,5 +26,13 @@ "preview.surface": "卡片", "preview.accent": "强调色", "save": "保存自定义主题", - "reset": "重置为预设" + "reset": "重置为预设", + "gradient": { + "title": "渐变效果", + "off": "关闭", + "standard": "标准", + "enhanced": "增强", + "hoverGlow": "启用悬停光晕效果", + "bgAnimation": "启用背景渐变动画" + } } diff --git a/ccw/frontend/src/stores/appStore.ts b/ccw/frontend/src/stores/appStore.ts index 72b8470d..6f65a26c 100644 --- a/ccw/frontend/src/stores/appStore.ts +++ b/ccw/frontend/src/stores/appStore.ts @@ -5,7 +5,7 @@ import { create } from 'zustand'; import { persist, devtools } from 'zustand/middleware'; -import type { AppStore, Theme, ColorScheme, Locale, ViewMode, SessionFilter, LiteTaskType, DashboardLayouts, WidgetConfig } from '../types/store'; +import type { AppStore, Theme, ColorScheme, GradientLevel, Locale, ViewMode, SessionFilter, LiteTaskType, DashboardLayouts, WidgetConfig } from '../types/store'; import { DEFAULT_DASHBOARD_LAYOUT } from '../components/dashboard/defaultLayouts'; import { getInitialLocale, updateIntl } from '../lib/i18n'; import { getThemeId } from '../lib/theme'; @@ -41,7 +41,10 @@ const resolveTheme = (theme: Theme): 'light' | 'dark' => { const applyThemeToDocument = ( resolvedTheme: 'light' | 'dark', colorScheme: ColorScheme, - customHue: number | null + customHue: number | null, + gradientLevel: GradientLevel = 'standard', + enableHoverGlow: boolean = true, + enableBackgroundAnimation: boolean = false ): void => { if (typeof document === 'undefined') return; @@ -92,13 +95,16 @@ const applyThemeToDocument = ( // Set color scheme attribute document.documentElement.setAttribute('data-color-scheme', colorScheme); + + // Apply gradient settings + document.documentElement.setAttribute('data-gradient', gradientLevel); + document.documentElement.setAttribute('data-hover-glow', String(enableHoverGlow)); + document.documentElement.setAttribute('data-bg-animation', String(enableBackgroundAnimation)); }; // Use View Transition API for smooth transitions (progressive enhancement) - // @ts-expect-error - View Transition API not yet in TypeScript DOM types - if (document.startViewTransition) { - // @ts-expect-error - View Transition API not yet in TypeScript DOM types - document.startViewTransition(performThemeUpdate); + if (typeof document !== 'undefined' && 'startViewTransition' in document) { + (document as unknown as { startViewTransition: (callback: () => void) => void }).startViewTransition(performThemeUpdate); } else { // Fallback: apply immediately without transition performThemeUpdate(); @@ -114,6 +120,11 @@ const initialState = { customHue: null as number | null, isCustomTheme: false, + // Gradient settings + gradientLevel: 'standard' as GradientLevel, + enableHoverGlow: true, + enableBackgroundAnimation: false, + // Locale locale: getInitialLocale() as Locale, @@ -150,31 +161,31 @@ export const useAppStore = create()( set({ theme, resolvedTheme: resolved }, false, 'setTheme'); // Apply theme using helper (encapsulates DOM manipulation) - const { colorScheme, customHue } = get(); - applyThemeToDocument(resolved, colorScheme, customHue); + const { colorScheme, customHue, gradientLevel, enableHoverGlow, enableBackgroundAnimation } = get(); + applyThemeToDocument(resolved, colorScheme, customHue, gradientLevel, enableHoverGlow, enableBackgroundAnimation); }, setColorScheme: (colorScheme: ColorScheme) => { set({ colorScheme, customHue: null, isCustomTheme: false }, false, 'setColorScheme'); // Apply color scheme using helper (encapsulates DOM manipulation) - const { resolvedTheme } = get(); - applyThemeToDocument(resolvedTheme, colorScheme, null); + const { resolvedTheme, gradientLevel, enableHoverGlow, enableBackgroundAnimation } = get(); + applyThemeToDocument(resolvedTheme, colorScheme, null, gradientLevel, enableHoverGlow, enableBackgroundAnimation); }, setCustomHue: (hue: number | null) => { if (hue === null) { // Reset to preset theme - const { colorScheme, resolvedTheme } = get(); + const { colorScheme, resolvedTheme, gradientLevel, enableHoverGlow, enableBackgroundAnimation } = get(); set({ customHue: null, isCustomTheme: false }, false, 'setCustomHue'); - applyThemeToDocument(resolvedTheme, colorScheme, null); + applyThemeToDocument(resolvedTheme, colorScheme, null, gradientLevel, enableHoverGlow, enableBackgroundAnimation); return; } // Apply custom hue set({ customHue: hue, isCustomTheme: true }, false, 'setCustomHue'); - const { resolvedTheme, colorScheme } = get(); - applyThemeToDocument(resolvedTheme, colorScheme, hue); + const { resolvedTheme, colorScheme, gradientLevel, enableHoverGlow, enableBackgroundAnimation } = get(); + applyThemeToDocument(resolvedTheme, colorScheme, hue, gradientLevel, enableHoverGlow, enableBackgroundAnimation); }, toggleTheme: () => { @@ -183,6 +194,26 @@ export const useAppStore = create()( get().setTheme(newTheme); }, + // ========== Gradient Settings Actions ========== + + setGradientLevel: (level: GradientLevel) => { + set({ gradientLevel: level }, false, 'setGradientLevel'); + const { resolvedTheme, colorScheme, customHue, enableHoverGlow, enableBackgroundAnimation } = get(); + applyThemeToDocument(resolvedTheme, colorScheme, customHue, level, enableHoverGlow, enableBackgroundAnimation); + }, + + setEnableHoverGlow: (enabled: boolean) => { + set({ enableHoverGlow: enabled }, false, 'setEnableHoverGlow'); + const { resolvedTheme, colorScheme, customHue, gradientLevel, enableBackgroundAnimation } = get(); + applyThemeToDocument(resolvedTheme, colorScheme, customHue, gradientLevel, enabled, enableBackgroundAnimation); + }, + + setEnableBackgroundAnimation: (enabled: boolean) => { + set({ enableBackgroundAnimation: enabled }, false, 'setEnableBackgroundAnimation'); + const { resolvedTheme, colorScheme, customHue, gradientLevel, enableHoverGlow } = get(); + applyThemeToDocument(resolvedTheme, colorScheme, customHue, gradientLevel, enableHoverGlow, enabled); + }, + // ========== Locale Actions ========== setLocale: (locale: Locale) => { @@ -279,6 +310,9 @@ export const useAppStore = create()( theme: state.theme, colorScheme: state.colorScheme, customHue: state.customHue, + gradientLevel: state.gradientLevel, + enableHoverGlow: state.enableHoverGlow, + enableBackgroundAnimation: state.enableBackgroundAnimation, locale: state.locale, sidebarCollapsed: state.sidebarCollapsed, expandedNavGroups: state.expandedNavGroups, @@ -291,7 +325,14 @@ export const useAppStore = create()( state.resolvedTheme = resolved; state.isCustomTheme = state.customHue !== null; // Apply theme using helper (encapsulates DOM manipulation) - applyThemeToDocument(resolved, state.colorScheme, state.customHue); + applyThemeToDocument( + resolved, + state.colorScheme, + state.customHue, + state.gradientLevel ?? 'standard', + state.enableHoverGlow ?? true, + state.enableBackgroundAnimation ?? false + ); } // Apply locale on rehydration if (state) { @@ -313,7 +354,14 @@ if (typeof window !== 'undefined') { const resolved = getSystemTheme(); useAppStore.setState({ resolvedTheme: resolved }); // Apply theme using helper (encapsulates DOM manipulation) - applyThemeToDocument(resolved, state.colorScheme, state.customHue); + applyThemeToDocument( + resolved, + state.colorScheme, + state.customHue, + state.gradientLevel, + state.enableHoverGlow, + state.enableBackgroundAnimation + ); } }); } @@ -324,6 +372,9 @@ export const selectResolvedTheme = (state: AppStore) => state.resolvedTheme; export const selectColorScheme = (state: AppStore) => state.colorScheme; export const selectCustomHue = (state: AppStore) => state.customHue; export const selectIsCustomTheme = (state: AppStore) => state.isCustomTheme; +export const selectGradientLevel = (state: AppStore) => state.gradientLevel; +export const selectEnableHoverGlow = (state: AppStore) => state.enableHoverGlow; +export const selectEnableBackgroundAnimation = (state: AppStore) => state.enableBackgroundAnimation; export const selectLocale = (state: AppStore) => state.locale; export const selectSidebarOpen = (state: AppStore) => state.sidebarOpen; export const selectCurrentView = (state: AppStore) => state.currentView; diff --git a/ccw/frontend/src/types/flow.ts b/ccw/frontend/src/types/flow.ts index 7751afec..8fab0f13 100644 --- a/ccw/frontend/src/types/flow.ts +++ b/ccw/frontend/src/types/flow.ts @@ -266,3 +266,101 @@ export const NODE_TYPE_CONFIGS: Record = { handles: { inputs: 1, outputs: 1 }, }, }; + +// ========== Quick Templates ========== + +/** + * Quick template definition for common prompt patterns + */ +export interface QuickTemplate { + id: string; + label: string; + description: string; + icon: string; + color: string; + data: Partial; +} + +/** + * Predefined quick templates for common workflow patterns + * All use 'prompt-template' type with preset configurations + */ +export const QUICK_TEMPLATES: QuickTemplate[] = [ + { + id: 'analysis', + label: 'Analysis', + description: 'Code review, architecture analysis', + icon: 'Search', + color: 'bg-emerald-500', + data: { + label: 'Analyze', + instruction: 'Analyze the code for:\n1. Architecture patterns\n2. Code quality\n3. Potential issues', + tool: 'gemini', + mode: 'analysis', + }, + }, + { + id: 'implementation', + label: 'Implementation', + description: 'Write code, create files', + icon: 'Code', + color: 'bg-violet-500', + data: { + label: 'Implement', + instruction: 'Implement the following:\n\n[Describe what to implement]', + tool: 'codex', + mode: 'write', + }, + }, + { + id: 'file-operation', + label: 'File Operation', + description: 'Save, read, or transform files', + icon: 'FileOutput', + color: 'bg-amber-500', + data: { + label: 'Save Output', + instruction: 'Save {{previous_output}} to ./output/result.md\n\nFormat as markdown with summary.', + mode: 'write', + contextRefs: [], + }, + }, + { + id: 'conditional', + label: 'Conditional', + description: 'Branch based on condition', + icon: 'GitBranch', + color: 'bg-orange-500', + data: { + label: 'Decision', + instruction: 'If {{previous.status}} === "success":\n Continue to next step\nElse:\n Stop and report error', + mode: 'mainprocess', + contextRefs: [], + }, + }, + { + id: 'parallel', + label: 'Parallel Start', + description: 'Fork into parallel branches', + icon: 'GitFork', + color: 'bg-cyan-500', + data: { + label: 'Parallel Tasks', + instruction: 'Execute the following tasks in parallel:\n1. [Task A]\n2. [Task B]\n3. [Task C]', + mode: 'mainprocess', + }, + }, + { + id: 'merge', + label: 'Merge Results', + description: 'Combine parallel outputs', + icon: 'GitMerge', + color: 'bg-pink-500', + data: { + label: 'Merge', + instruction: 'Combine results from:\n- {{task_a}}\n- {{task_b}}\n- {{task_c}}\n\nGenerate unified summary.', + mode: 'analysis', + contextRefs: [], + }, + }, +]; diff --git a/ccw/frontend/src/types/store.ts b/ccw/frontend/src/types/store.ts index 1c16b339..a20f4ecc 100644 --- a/ccw/frontend/src/types/store.ts +++ b/ccw/frontend/src/types/store.ts @@ -7,6 +7,7 @@ export type Theme = 'light' | 'dark' | 'system'; export type ColorScheme = 'blue' | 'green' | 'orange' | 'purple'; +export type GradientLevel = 'off' | 'standard' | 'enhanced'; export type Locale = 'en' | 'zh'; export type ViewMode = 'sessions' | 'liteTasks' | 'project-overview' | 'sessionDetail' | 'liteTaskDetail' | 'loop-monitor' | 'issue-manager' | 'orchestrator'; export type SessionFilter = 'all' | 'active' | 'archived'; @@ -42,6 +43,11 @@ export interface AppState { customHue: number | null; // Custom hue value (0-360) for theme customization isCustomTheme: boolean; // Indicates if custom theme is active + // Gradient settings + gradientLevel: GradientLevel; // Gradient intensity: off, standard, enhanced + enableHoverGlow: boolean; // Enable hover glow effects + enableBackgroundAnimation: boolean; // Enable background gradient animation + // Locale locale: Locale; @@ -72,6 +78,11 @@ export interface AppActions { setColorScheme: (scheme: ColorScheme) => void; // New: set color scheme setCustomHue: (hue: number | null) => void; // Set custom hue for theme customization + // Gradient settings actions + setGradientLevel: (level: GradientLevel) => void; + setEnableHoverGlow: (enabled: boolean) => void; + setEnableBackgroundAnimation: (enabled: boolean) => void; + // Locale actions setLocale: (locale: Locale) => void;