mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-03-01 15:03:57 +08:00
Refactor code structure for improved readability and maintainability
This commit is contained in:
@@ -1,46 +1,89 @@
|
||||
<template>
|
||||
<div class="agent-orchestration">
|
||||
<div class="orchestration-title">🤖 Agent Orchestration</div>
|
||||
|
||||
<div class="orchestration-title">
|
||||
<svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="title-icon">
|
||||
<rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/>
|
||||
</svg>
|
||||
Agent Orchestration
|
||||
</div>
|
||||
|
||||
<div class="agent-flow">
|
||||
<!-- CLI Layer -->
|
||||
<div class="flow-layer cli-layer">
|
||||
<div class="layer-label">CLI Tools</div>
|
||||
<div class="agents-row">
|
||||
<div class="agent-card cli" @mouseenter="showTooltip('cli-explore')" @mouseleave="hideTooltip">🔍 Explore</div>
|
||||
<div class="agent-card cli" @mouseenter="showTooltip('cli-plan')" @mouseleave="hideTooltip">📋 Plan</div>
|
||||
<div class="agent-card cli" @mouseenter="showTooltip('cli-exec')" @mouseleave="hideTooltip">⚡ Execute</div>
|
||||
<div class="agent-card cli" @mouseenter="showTooltip('cli-discuss')" @mouseleave="hideTooltip">💬 Discuss</div>
|
||||
<div class="agent-card cli" @mouseenter="showTooltip('cli-explore')" @mouseleave="hideTooltip">
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
|
||||
Explore
|
||||
</div>
|
||||
<div class="agent-card cli" @mouseenter="showTooltip('cli-plan')" @mouseleave="hideTooltip">
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><line x1="8" y1="8" x2="16" y2="8"/><line x1="8" y1="12" x2="16" y2="12"/><line x1="8" y1="16" x2="12" y2="16"/></svg>
|
||||
Plan
|
||||
</div>
|
||||
<div class="agent-card cli" @mouseenter="showTooltip('cli-exec')" @mouseleave="hideTooltip">
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>
|
||||
Execute
|
||||
</div>
|
||||
<div class="agent-card cli" @mouseenter="showTooltip('cli-discuss')" @mouseleave="hideTooltip">
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z"/></svg>
|
||||
Discuss
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Flow Arrow -->
|
||||
<div class="flow-arrow">▼</div>
|
||||
|
||||
<div class="flow-arrow">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="12" y1="5" x2="12" y2="19"/><polyline points="19 12 12 19 5 12"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<!-- Development Layer -->
|
||||
<div class="flow-layer dev-layer">
|
||||
<div class="layer-label">Development</div>
|
||||
<div class="agents-row">
|
||||
<div class="agent-card dev" @mouseenter="showTooltip('code-dev')" @mouseleave="hideTooltip">👨💻 Code</div>
|
||||
<div class="agent-card dev" @mouseenter="showTooltip('tdd')" @mouseleave="hideTooltip">🧪 TDD</div>
|
||||
<div class="agent-card dev" @mouseenter="showTooltip('test-fix')" @mouseleave="hideTooltip">🔧 Fix</div>
|
||||
<div class="agent-card dev" @mouseenter="showTooltip('code-dev')" @mouseleave="hideTooltip">
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>
|
||||
Code
|
||||
</div>
|
||||
<div class="agent-card dev" @mouseenter="showTooltip('tdd')" @mouseleave="hideTooltip">
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 3h6M12 3v7l-4 8h8l-4-8"/><circle cx="8" cy="20" r="1"/><circle cx="16" cy="20" r="1"/></svg>
|
||||
TDD
|
||||
</div>
|
||||
<div class="agent-card dev" @mouseenter="showTooltip('test-fix')" @mouseleave="hideTooltip">
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.7 6.3a1 1 0 000 1.4l1.6 1.6a1 1 0 001.4 0l3.77-3.77a6 6 0 01-7.94 7.94l-6.91 6.91a2.12 2.12 0 01-3-3l6.91-6.91a6 6 0 017.94-7.94l-3.76 3.76z"/></svg>
|
||||
Fix
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Flow Arrow -->
|
||||
<div class="flow-arrow">▼</div>
|
||||
|
||||
<div class="flow-arrow">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="12" y1="5" x2="12" y2="19"/><polyline points="19 12 12 19 5 12"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<!-- Output Layer -->
|
||||
<div class="flow-layer output-layer">
|
||||
<div class="layer-label">Output</div>
|
||||
<div class="agents-row">
|
||||
<div class="agent-card doc" @mouseenter="showTooltip('doc-gen')" @mouseleave="hideTooltip">📄 Docs</div>
|
||||
<div class="agent-card ui" @mouseenter="showTooltip('ui-design')" @mouseleave="hideTooltip">🎨 UI</div>
|
||||
<div class="agent-card universal" @mouseenter="showTooltip('universal')" @mouseleave="hideTooltip">🌐 Universal</div>
|
||||
<div class="agent-card doc" @mouseenter="showTooltip('doc-gen')" @mouseleave="hideTooltip">
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="8" y1="13" x2="16" y2="13"/><line x1="8" y1="17" x2="14" y2="17"/></svg>
|
||||
Docs
|
||||
</div>
|
||||
<div class="agent-card ui" @mouseenter="showTooltip('ui-design')" @mouseleave="hideTooltip">
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>
|
||||
UI
|
||||
</div>
|
||||
<div class="agent-card universal" @mouseenter="showTooltip('universal')" @mouseleave="hideTooltip">
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 014 10 15.3 15.3 0 01-4 10 15.3 15.3 0 01-4-10 15.3 15.3 0 014-10z"/></svg>
|
||||
Universal
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="tooltip" v-if="tooltip" :class="{ visible: tooltip }">
|
||||
{{ tooltipText }}
|
||||
</div>
|
||||
@@ -54,16 +97,16 @@ const tooltip = ref(false)
|
||||
const tooltipText = ref('')
|
||||
|
||||
const tooltips = {
|
||||
'cli-explore': 'cli-explore-agent: 代码库探索和语义搜索',
|
||||
'cli-plan': 'cli-planning-agent: 任务规划和分解',
|
||||
'cli-exec': 'cli-execution-agent: 命令执行和结果处理',
|
||||
'cli-discuss': 'cli-discuss-agent: 多视角讨论和共识达成',
|
||||
'code-dev': 'code-developer: 代码实现和开发',
|
||||
'tdd': 'tdd-developer: 测试驱动开发',
|
||||
'test-fix': 'test-fix-agent: 测试修复循环',
|
||||
'doc-gen': 'doc-generator: 文档自动生成',
|
||||
'ui-design': 'ui-design-agent: UI设计和设计令牌',
|
||||
'universal': 'universal-executor: 通用任务执行器'
|
||||
'cli-explore': 'cli-explore-agent: Codebase exploration & semantic search',
|
||||
'cli-plan': 'cli-planning-agent: Task planning & decomposition',
|
||||
'cli-exec': 'cli-execution-agent: Command execution & result handling',
|
||||
'cli-discuss': 'cli-discuss-agent: Multi-perspective discussion & consensus',
|
||||
'code-dev': 'code-developer: Code implementation & development',
|
||||
'tdd': 'tdd-developer: Test-driven development',
|
||||
'test-fix': 'test-fix-agent: Test-fix iteration loop',
|
||||
'doc-gen': 'doc-generator: Auto documentation generation',
|
||||
'ui-design': 'ui-design-agent: UI design & design tokens',
|
||||
'universal': 'universal-executor: General-purpose task executor'
|
||||
}
|
||||
|
||||
function showTooltip(key) {
|
||||
@@ -88,6 +131,10 @@ function hideTooltip() {
|
||||
}
|
||||
|
||||
.orchestration-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.6rem;
|
||||
text-align: center;
|
||||
font-size: 1.75rem;
|
||||
font-weight: 700;
|
||||
@@ -95,6 +142,10 @@ function hideTooltip() {
|
||||
margin-bottom: 2.5rem;
|
||||
}
|
||||
|
||||
.title-icon {
|
||||
color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
.agent-flow {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -125,6 +176,9 @@ function hideTooltip() {
|
||||
}
|
||||
|
||||
.agent-card {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.8rem 1.5rem;
|
||||
border-radius: 12px;
|
||||
font-size: 0.9rem;
|
||||
@@ -171,14 +225,13 @@ function hideTooltip() {
|
||||
}
|
||||
|
||||
.flow-arrow {
|
||||
color: var(--vp-c-divider);
|
||||
font-size: 1.25rem;
|
||||
animation: bounce 2s infinite;
|
||||
color: var(--vp-c-text-4);
|
||||
animation: arrowBounce 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes bounce {
|
||||
0%, 100% { transform: translateY(0); opacity: 0.5; }
|
||||
50% { transform: translateY(8px); opacity: 1; }
|
||||
@keyframes arrowBounce {
|
||||
0%, 100% { transform: translateY(0); opacity: 0.4; }
|
||||
50% { transform: translateY(6px); opacity: 0.8; }
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
@@ -203,11 +256,19 @@ function hideTooltip() {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*, *::before, *::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.agent-orchestration {
|
||||
padding: 2rem 1rem;
|
||||
}
|
||||
|
||||
|
||||
.agent-card {
|
||||
padding: 0.6rem 1rem;
|
||||
font-size: 0.8rem;
|
||||
|
||||
@@ -1,54 +1,188 @@
|
||||
<template>
|
||||
<div class="hero-animation-container" :class="{ 'is-visible': isVisible }">
|
||||
<div class="glow-bg"></div>
|
||||
<svg viewBox="0 0 400 320" class="hero-svg" preserveAspectRatio="xMidYMid meet">
|
||||
<div class="hero-anim" :class="{ 'is-visible': isVisible }">
|
||||
<svg viewBox="0 0 360 340" class="hero-svg" preserveAspectRatio="xMidYMid meet">
|
||||
<defs>
|
||||
<filter id="glow" x="-20%" y="-20%" width="140%" height="140%">
|
||||
<feGaussianBlur stdDeviation="3.5" result="coloredBlur"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="coloredBlur"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
<linearGradient id="pathGrad" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||
<stop offset="0%" stop-color="var(--vp-c-brand-1)" stop-opacity="0" />
|
||||
<stop offset="50%" stop-color="var(--vp-c-brand-1)" stop-opacity="0.5" />
|
||||
<stop offset="100%" stop-color="var(--vp-c-brand-1)" stop-opacity="0" />
|
||||
<linearGradient id="coreGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="var(--vp-c-brand-1)"/>
|
||||
<stop offset="100%" stop-color="#8B5CF6"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="ccwBorder" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="var(--vp-c-brand-1)" stop-opacity="0.6"/>
|
||||
<stop offset="50%" stop-color="#8B5CF6" stop-opacity="0.4"/>
|
||||
<stop offset="100%" stop-color="var(--vp-c-brand-1)" stop-opacity="0.6"/>
|
||||
</linearGradient>
|
||||
<filter id="ccwShadow" x="-20%" y="-20%" width="140%" height="140%">
|
||||
<feDropShadow dx="0" dy="1" stdDeviation="3" flood-color="var(--vp-c-brand-1)" flood-opacity="0.12"/>
|
||||
</filter>
|
||||
<linearGradient id="beltFade" x1="0" y1="0" x2="1" y2="0">
|
||||
<stop offset="0%" stop-color="var(--vp-c-text-3)" stop-opacity="0"/>
|
||||
<stop offset="8%" stop-color="var(--vp-c-text-3)" stop-opacity="0.12"/>
|
||||
<stop offset="92%" stop-color="var(--vp-c-text-3)" stop-opacity="0.12"/>
|
||||
<stop offset="100%" stop-color="var(--vp-c-text-3)" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<clipPath id="beltClip">
|
||||
<rect x="10" y="211" width="340" height="14"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
||||
<!-- Connection Lines -->
|
||||
<g class="data-paths">
|
||||
<path v-for="(path, i) in paths" :key="'path-'+i" :d="path" class="connection-path" />
|
||||
<circle v-for="(path, i) in paths" :key="'dot-'+i" r="2" class="data-pulse">
|
||||
<animateMotion :dur="2 + i * 0.4 + 's'" repeatCount="indefinite" :path="path" />
|
||||
</circle>
|
||||
<!-- Background mesh -->
|
||||
<g opacity="0.025">
|
||||
<line v-for="i in 6" :key="'v'+i" :x1="i*60" y1="0" :x2="i*60" y2="340" stroke="var(--vp-c-brand-1)" stroke-width="0.5"/>
|
||||
<line v-for="i in 6" :key="'h'+i" x1="0" :y1="i*57" x2="360" :y2="i*57" stroke="var(--vp-c-brand-1)" stroke-width="0.5"/>
|
||||
</g>
|
||||
|
||||
<!-- Orbit Rings -->
|
||||
<g class="orbit-rings">
|
||||
<circle cx="200" cy="160" r="130" class="orbit-ring ring-outer" />
|
||||
<circle cx="200" cy="160" r="95" class="orbit-ring ring-inner" />
|
||||
<!-- === CCW Controller === -->
|
||||
<g transform="translate(180, 30)">
|
||||
<!-- Outer pulse rings -->
|
||||
<circle r="38" fill="none" stroke="url(#coreGrad)" stroke-width="0.5" opacity="0.08" class="ccw-ring1"/>
|
||||
<circle r="32" fill="none" stroke="url(#coreGrad)" stroke-width="0.4" opacity="0.12" class="ccw-ring2"/>
|
||||
<!-- Main badge -->
|
||||
<rect x="-56" y="-20" width="112" height="40" rx="12" class="ccw-bg" filter="url(#ccwShadow)"/>
|
||||
<rect x="-56" y="-20" width="112" height="40" rx="12" fill="none" stroke="url(#ccwBorder)" stroke-width="1.2"/>
|
||||
<!-- Logo icon (simplified favicon: blue square + white lines + green dot) -->
|
||||
<g transform="translate(-48, -13)">
|
||||
<rect width="26" height="26" rx="6" fill="var(--vp-c-brand-1)" opacity="0.12"/>
|
||||
<rect width="26" height="26" rx="6" fill="none" stroke="var(--vp-c-brand-1)" stroke-width="0.8" opacity="0.25"/>
|
||||
<line x1="6" y1="9" x2="20" y2="9" stroke="var(--vp-c-brand-1)" stroke-width="2" stroke-linecap="round" opacity="0.65"/>
|
||||
<line x1="6" y1="13" x2="17" y2="13" stroke="var(--vp-c-brand-1)" stroke-width="2" stroke-linecap="round" opacity="0.65"/>
|
||||
<line x1="6" y1="17" x2="14" y2="17" stroke="var(--vp-c-brand-1)" stroke-width="2" stroke-linecap="round" opacity="0.65"/>
|
||||
<circle cx="19" cy="17.5" r="3" fill="#22C55E" opacity="0.8"/>
|
||||
</g>
|
||||
<!-- Text — shifted right to avoid logo overlap -->
|
||||
<text x="16" y="0" text-anchor="middle" class="ccw-label">CCW</text>
|
||||
<text x="16" y="12" text-anchor="middle" class="ccw-sub">Orchestrator</text>
|
||||
<!-- Signal indicator -->
|
||||
<circle cx="50" cy="-12" r="2.5" fill="#22C55E" opacity="0.5" class="ccw-signal"/>
|
||||
<circle cx="50" cy="-12" r="1.2" fill="#22C55E" opacity="0.8"/>
|
||||
</g>
|
||||
|
||||
<!-- Agent Nodes -->
|
||||
<g v-for="(agent, i) in agents" :key="'agent-'+i" class="agent-node" :style="{ '--delay': i * 0.4 + 's' }">
|
||||
<g class="agent-group" :style="{ transform: `translate(${agent.x}px, ${agent.y}px)` }">
|
||||
<circle r="8" :fill="agent.color" class="agent-circle" filter="url(#glow)" />
|
||||
<circle r="12" :stroke="agent.color" fill="none" class="agent-halo" />
|
||||
<text y="22" text-anchor="middle" class="agent-label">{{ agent.name }}</text>
|
||||
<!-- Control lines (CCW → stations) -->
|
||||
<path d="M134,50 L134,68 L60,68 L60,90" class="ctrl-line" style="--d:0.3s"/>
|
||||
<path d="M180,50 L180,90" class="ctrl-line" style="--d:0.5s"/>
|
||||
<path d="M226,50 L226,68 L300,68 L300,90" class="ctrl-line" style="--d:0.7s"/>
|
||||
<!-- Signal pulses on control lines -->
|
||||
<circle r="1.5" fill="var(--vp-c-brand-1)" class="line-pulse">
|
||||
<animateMotion dur="2s" repeatCount="indefinite" path="M134,50 L134,68 L60,68 L60,90" begin="0.3s"/>
|
||||
</circle>
|
||||
<circle r="1.5" fill="var(--vp-c-brand-1)" class="line-pulse">
|
||||
<animateMotion dur="2s" repeatCount="indefinite" path="M180,50 L180,90" begin="0.5s"/>
|
||||
</circle>
|
||||
<circle r="1.5" fill="var(--vp-c-brand-1)" class="line-pulse">
|
||||
<animateMotion dur="2s" repeatCount="indefinite" path="M226,50 L226,68 L300,68 L300,90" begin="0.7s"/>
|
||||
</circle>
|
||||
<circle cx="60" cy="90" r="2.5" fill="#D97757" opacity="0.5"/>
|
||||
<circle cx="180" cy="90" r="2.5" fill="#10A37F" opacity="0.5"/>
|
||||
<circle cx="300" cy="90" r="2.5" fill="#4285F4" opacity="0.5"/>
|
||||
|
||||
<!-- === Station: Claude (Design) === -->
|
||||
<!-- Outer <g> for SVG positioning, inner <g> for CSS animation -->
|
||||
<g transform="translate(60, 94)">
|
||||
<g class="station" style="--sd:0.3s">
|
||||
<rect x="-40" y="0" width="80" height="66" rx="12" class="station-box" style="--sc:#D97757"/>
|
||||
<circle cy="24" r="14" fill="#D97757" opacity="0.05"/>
|
||||
<!-- Official Claude (Anthropic) icon -->
|
||||
<svg x="-11" y="8" width="22" height="22" viewBox="0 0 16 16" fill="#D97757">
|
||||
<path d="m3.127 10.604 3.135-1.76.053-.153-.053-.085H6.11l-.525-.032-1.791-.048-1.554-.065-1.505-.08-.38-.081L0 7.832l.036-.234.32-.214.455.04 1.009.069 1.513.105 1.097.064 1.626.17h.259l.036-.105-.089-.065-.068-.064-1.566-1.062-1.695-1.121-.887-.646-.48-.327-.243-.306-.104-.67.435-.48.585.04.15.04.593.456 1.267.981 1.654 1.218.242.202.097-.068.012-.049-.109-.181-.9-1.626-.96-1.655-.428-.686-.113-.411a2 2 0 0 1-.068-.484l.496-.674L4.446 0l.662.089.279.242.411.94.666 1.48 1.033 2.014.302.597.162.553.06.17h.105v-.097l.085-1.134.157-1.392.154-1.792.052-.504.25-.605.497-.327.387.186.319.456-.045.294-.19 1.23-.37 1.93-.243 1.29h.142l.161-.16.654-.868 1.097-1.372.484-.545.565-.601.363-.287h.686l.505.751-.226.775-.707.895-.585.759-.839 1.13-.524.904.048.072.125-.012 1.897-.403 1.024-.186 1.223-.21.553.258.06.263-.218.536-1.307.323-1.533.307-2.284.54-.028.02.032.04 1.029.098.44.024h1.077l2.005.15.525.346.315.424-.053.323-.807.411-3.631-.863-.872-.218h-.12v.073l.726.71 1.331 1.202 1.667 1.55.084.383-.214.302-.226-.032-1.464-1.101-.565-.497-1.28-1.077h-.084v.113l.295.432 1.557 2.34.08.718-.112.234-.404.141-.444-.08-.911-1.28-.94-1.44-.759-1.291-.093.053-.448 4.821-.21.246-.484.186-.403-.307-.214-.496.214-.98.258-1.28.21-1.016.19-1.263.112-.42-.008-.028-.092.012-.953 1.307-1.448 1.957-1.146 1.227-.274.109-.477-.247.045-.44.266-.39 1.586-2.018.956-1.25.617-.723-.004-.105h-.036l-4.212 2.736-.75.096-.324-.302.04-.496.154-.162 1.267-.871z"/>
|
||||
</svg>
|
||||
<text y="46" text-anchor="middle" class="st-name" fill="#D97757">Claude</text>
|
||||
<text y="58" text-anchor="middle" class="st-role">Design</text>
|
||||
<line x1="0" y1="66" x2="0" y2="104" stroke="#D97757" stroke-width="1.2" opacity="0.2"/>
|
||||
<polygon points="-3,104 3,104 0,110" fill="#D97757" opacity="0.25"/>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Central Core -->
|
||||
<g class="central-core" transform="translate(200, 160)">
|
||||
<circle r="40" class="core-bg" />
|
||||
<circle r="32" fill="var(--vp-c-brand-1)" filter="url(#glow)" class="core-inner" />
|
||||
<text y="8" text-anchor="middle" class="core-text">CCW</text>
|
||||
|
||||
<!-- Scanning Effect -->
|
||||
<path d="M-32 0 A32 32 0 0 1 32 0" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" class="core-scanner" />
|
||||
<!-- === Station: OpenAI (Build) === -->
|
||||
<g transform="translate(180, 94)">
|
||||
<g class="station" style="--sd:0.5s">
|
||||
<rect x="-40" y="0" width="80" height="66" rx="12" class="station-box" style="--sc:#10A37F"/>
|
||||
<circle cy="24" r="14" fill="#10A37F" opacity="0.05"/>
|
||||
<!-- Official OpenAI icon -->
|
||||
<svg x="-11" y="8" width="22" height="22" viewBox="0 0 16 16" fill="#10A37F">
|
||||
<path d="M14.949 6.547a3.94 3.94 0 0 0-.348-3.273 4.11 4.11 0 0 0-4.4-1.934A4.1 4.1 0 0 0 8.423.2 4.15 4.15 0 0 0 6.305.086a4.1 4.1 0 0 0-1.891.948 4.04 4.04 0 0 0-1.158 1.753 4.1 4.1 0 0 0-1.563.679A4 4 0 0 0 .554 4.72a3.99 3.99 0 0 0 .502 4.731 3.94 3.94 0 0 0 .346 3.274 4.11 4.11 0 0 0 4.402 1.933c.382.425.852.764 1.377.995.526.231 1.095.35 1.67.346 1.78.002 3.358-1.132 3.901-2.804a4.1 4.1 0 0 0 1.563-.68 4 4 0 0 0 1.14-1.253 3.99 3.99 0 0 0-.506-4.716m-6.097 8.406a3.05 3.05 0 0 1-1.945-.694l.096-.054 3.23-1.838a.53.53 0 0 0 .265-.455v-4.49l1.366.778q.02.011.025.035v3.722c-.003 1.653-1.361 2.992-3.037 2.996m-6.53-2.75a2.95 2.95 0 0 1-.36-2.01l.095.057L5.29 12.09a.53.53 0 0 0 .527 0l3.949-2.246v1.555a.05.05 0 0 1-.022.041L6.473 13.3c-1.454.826-3.311.335-4.15-1.098m-.85-6.94A3.02 3.02 0 0 1 3.07 3.949v3.785a.51.51 0 0 0 .262.451l3.93 2.237-1.366.779a.05.05 0 0 1-.048 0L2.585 9.342a2.98 2.98 0 0 1-1.113-4.094zm11.216 2.571L8.747 5.576l1.362-.776a.05.05 0 0 1 .048 0l3.265 1.86a3 3 0 0 1 1.173 1.207 2.96 2.96 0 0 1-.27 3.2 3.05 3.05 0 0 1-1.36.997V8.279a.52.52 0 0 0-.276-.445m1.36-2.015-.097-.057-3.226-1.855a.53.53 0 0 0-.53 0L6.249 6.153V4.598a.04.04 0 0 1 .019-.04L9.533 2.7a3.07 3.07 0 0 1 3.257.139c.474.325.843.778 1.066 1.303.223.526.289 1.103.191 1.664zM5.503 8.575 4.139 7.8a.05.05 0 0 1-.026-.037V4.049c0-.57.166-1.127.476-1.607s.752-.864 1.275-1.105a3.08 3.08 0 0 1 3.234.41l-.096.054-3.23 1.838a.53.53 0 0 0-.265.455zm.742-1.577 1.758-1 1.762 1v2l-1.755 1-1.762-1z"/>
|
||||
</svg>
|
||||
<text y="46" text-anchor="middle" class="st-name" fill="#10A37F">OpenAI</text>
|
||||
<text y="58" text-anchor="middle" class="st-role">Build</text>
|
||||
<line x1="0" y1="66" x2="0" y2="104" stroke="#10A37F" stroke-width="1.2" opacity="0.2"/>
|
||||
<polygon points="-3,104 3,104 0,110" fill="#10A37F" opacity="0.25"/>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- === Station: Gemini (Verify) === -->
|
||||
<g transform="translate(300, 94)">
|
||||
<g class="station" style="--sd:0.7s">
|
||||
<rect x="-40" y="0" width="80" height="66" rx="12" class="station-box" style="--sc:#4285F4"/>
|
||||
<circle cy="24" r="14" fill="#4285F4" opacity="0.05"/>
|
||||
<!-- Official Google Gemini icon (4-point star) -->
|
||||
<svg x="-11" y="8" width="22" height="22" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M3 12a9 9 0 0 0 9-9a9 9 0 0 0 9 9a9 9 0 0 0-9 9a9 9 0 0 0-9-9" stroke="#4285F4" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" fill="#4285F4" fill-opacity="0.15"/>
|
||||
</svg>
|
||||
<text y="46" text-anchor="middle" class="st-name" fill="#4285F4">Gemini</text>
|
||||
<text y="58" text-anchor="middle" class="st-role">Verify</text>
|
||||
<line x1="0" y1="66" x2="0" y2="104" stroke="#4285F4" stroke-width="1.2" opacity="0.2"/>
|
||||
<polygon points="-3,104 3,104 0,110" fill="#4285F4" opacity="0.25"/>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- === Conveyor Belt === -->
|
||||
<rect x="8" y="212" width="344" height="12" rx="6" fill="url(#beltFade)"/>
|
||||
<line x1="18" y1="212" x2="342" y2="212" stroke="var(--vp-c-divider)" stroke-width="0.3" opacity="0.4"/>
|
||||
<line x1="18" y1="224" x2="342" y2="224" stroke="var(--vp-c-divider)" stroke-width="0.3" opacity="0.4"/>
|
||||
<!-- Rollers -->
|
||||
<circle v-for="i in 9" :key="'r'+i" :cx="18 + i * 36" cy="218" r="2.5" class="roller"/>
|
||||
<!-- Scrolling belt marks -->
|
||||
<g clip-path="url(#beltClip)" class="belt-scroll">
|
||||
<line v-for="i in 22" :key="'bm'+i" :x1="i * 18 - 18" y1="213" :x2="i * 18 - 18" y2="223"
|
||||
stroke="var(--vp-c-text-3)" stroke-width="0.3" opacity="0.1"/>
|
||||
</g>
|
||||
|
||||
<!-- Flow direction arrows -->
|
||||
<g opacity="0.12">
|
||||
<polygon points="116,215 116,221 121,218" fill="var(--vp-c-text-2)"/>
|
||||
<polygon points="236,215 236,221 241,218" fill="var(--vp-c-text-2)"/>
|
||||
<polygon points="340,215 340,221 345,218" fill="var(--vp-c-text-2)"/>
|
||||
</g>
|
||||
|
||||
<!-- === Flow Items === -->
|
||||
<g class="flow-items">
|
||||
<g v-for="(item, i) in flowItems" :key="'fi'+i">
|
||||
<rect :width="item.w" height="7" rx="2" :fill="item.color" :opacity="item.opacity">
|
||||
<animateMotion :dur="item.dur + 's'" repeatCount="indefinite"
|
||||
path="M6,215 L354,215" :begin="item.delay + 's'"/>
|
||||
</rect>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Sparks at stations -->
|
||||
<g v-for="(sp, i) in sparks" :key="'sp'+i">
|
||||
<circle :r="sp.r" :fill="sp.color" class="spark">
|
||||
<animateMotion :dur="sp.dur + 's'" repeatCount="indefinite" :path="sp.path" :begin="sp.delay + 's'"/>
|
||||
</circle>
|
||||
</g>
|
||||
|
||||
<!-- I/O labels -->
|
||||
<text x="18" y="244" class="io-label">Tasks</text>
|
||||
<text x="342" y="244" text-anchor="end" class="io-label">Done</text>
|
||||
<!-- Output check -->
|
||||
<g transform="translate(350, 218)">
|
||||
<circle r="7" fill="#22C55E" opacity="0.06" class="check-pulse"/>
|
||||
<polyline points="-3,1 -1,3 3,-1" fill="none" stroke="#22C55E" stroke-width="1.3" stroke-linecap="round" opacity="0.6"/>
|
||||
</g>
|
||||
|
||||
<!-- Status row -->
|
||||
<g class="status-row">
|
||||
<g v-for="(s, i) in statusDots" :key="'sd'+i" :transform="`translate(${s.x}, 274)`">
|
||||
<line x1="0" y1="-20" x2="0" y2="-14" :stroke="s.color" stroke-width="0.5" opacity="0.2"/>
|
||||
<circle r="3" :fill="s.color" opacity="0.12"/>
|
||||
<circle r="1.5" :fill="s.color" opacity="0.5" class="status-blink"/>
|
||||
<text y="14" text-anchor="middle" class="status-text">{{ s.label }}</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Micro-particles -->
|
||||
<circle v-for="(p, i) in particles" :key="'mp'+i"
|
||||
:cx="p.x" :cy="p.y" :r="p.r"
|
||||
fill="var(--vp-c-brand-1)" :opacity="p.opacity"
|
||||
class="micro-dot" :style="{ '--fd': p.dur + 's', '--fdy': p.delay + 's' }"/>
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
@@ -59,156 +193,112 @@ import { ref, onMounted } from 'vue'
|
||||
const isVisible = ref(false)
|
||||
|
||||
onMounted(() => {
|
||||
setTimeout(() => {
|
||||
requestAnimationFrame(() => {
|
||||
isVisible.value = true
|
||||
}, 100)
|
||||
})
|
||||
})
|
||||
|
||||
const agents = [
|
||||
{ name: 'Analyze', x: 200, y: 35, color: '#3B82F6' },
|
||||
{ name: 'Plan', x: 315, y: 110, color: '#10B981' },
|
||||
{ name: 'Code', x: 285, y: 245, color: '#8B5CF6' },
|
||||
{ name: 'Test', x: 115, y: 245, color: '#F59E0B' },
|
||||
{ name: 'Review', x: 85, y: 110, color: '#EF4444' }
|
||||
const flowItems = [
|
||||
{ w: 12, color: 'var(--vp-c-text-3)', opacity: 0.35, dur: 7, delay: 0 },
|
||||
{ w: 10, color: '#D97757', opacity: 0.55, dur: 7, delay: 1.75 },
|
||||
{ w: 14, color: '#10A37F', opacity: 0.55, dur: 7, delay: 3.5 },
|
||||
{ w: 10, color: '#4285F4', opacity: 0.55, dur: 7, delay: 5.25 }
|
||||
]
|
||||
|
||||
const paths = [
|
||||
'M200,160 L200,35',
|
||||
'M200,160 L315,110',
|
||||
'M200,160 L285,245',
|
||||
'M200,160 L115,245',
|
||||
'M200,160 L85,110',
|
||||
'M200,35 Q260,35 315,110',
|
||||
'M315,110 Q315,180 285,245',
|
||||
'M285,245 Q200,285 115,245',
|
||||
'M115,245 Q85,180 85,110',
|
||||
'M85,110 Q85,35 200,35'
|
||||
const sparks = [
|
||||
{ r: 1.5, color: '#D97757', dur: 2, delay: 0, path: 'M60,212 Q57,202 55,192' },
|
||||
{ r: 1, color: '#D97757', dur: 2.5, delay: 0.5, path: 'M62,212 Q65,202 67,192' },
|
||||
{ r: 1.5, color: '#10A37F', dur: 2, delay: 0.8, path: 'M180,212 Q177,202 175,192' },
|
||||
{ r: 1, color: '#10A37F', dur: 2.5, delay: 1.3, path: 'M182,212 Q185,202 187,192' },
|
||||
{ r: 1.5, color: '#4285F4', dur: 2, delay: 1.6, path: 'M300,212 Q297,202 295,192' },
|
||||
{ r: 1, color: '#4285F4', dur: 2.5, delay: 2.1, path: 'M302,212 Q305,202 307,192' }
|
||||
]
|
||||
|
||||
const statusDots = [
|
||||
{ x: 60, color: '#D97757', label: 'Planning' },
|
||||
{ x: 180, color: '#10A37F', label: 'Coding' },
|
||||
{ x: 300, color: '#4285F4', label: 'Testing' }
|
||||
]
|
||||
|
||||
const particles = Array.from({ length: 10 }, () => ({
|
||||
x: 15 + Math.random() * 330,
|
||||
y: 10 + Math.random() * 320,
|
||||
r: 0.5 + Math.random() * 1,
|
||||
opacity: 0.05 + Math.random() * 0.1,
|
||||
dur: 3 + Math.random() * 4,
|
||||
delay: Math.random() * 3
|
||||
}))
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.hero-animation-container {
|
||||
.hero-anim {
|
||||
width: 100%;
|
||||
max-width: 480px;
|
||||
position: relative;
|
||||
opacity: 0;
|
||||
transform: scale(0.95);
|
||||
transition: all 1s cubic-bezier(0.2, 0.8, 0.2, 1);
|
||||
transform: scale(0.92) translateY(12px);
|
||||
transition: all 0.8s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
|
||||
.hero-animation-container.is-visible {
|
||||
.hero-anim.is-visible {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
transform: scale(1) translateY(0);
|
||||
}
|
||||
|
||||
.glow-bg {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
background: var(--vp-c-brand-1);
|
||||
filter: blur(80px);
|
||||
opacity: 0.15;
|
||||
transform: translate(-50%, -50%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.hero-svg {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
overflow: visible;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.orbit-ring {
|
||||
fill: none;
|
||||
stroke: var(--vp-c-brand-1);
|
||||
stroke-width: 0.5;
|
||||
opacity: 0.1;
|
||||
stroke-dasharray: 4 4;
|
||||
}
|
||||
/* CCW */
|
||||
.ccw-bg { fill: var(--vp-c-bg-soft); }
|
||||
.ccw-ring1 { animation: ccwRing 4s ease-in-out infinite; }
|
||||
.ccw-ring2 { animation: ccwRing 4s ease-in-out infinite 2s; }
|
||||
.ccw-label { font-size: 15px; font-weight: 800; fill: var(--vp-c-brand-1); letter-spacing: 0.1em; }
|
||||
.ccw-sub { font-size: 7px; fill: var(--vp-c-text-3); letter-spacing: 0.1em; text-transform: uppercase; font-weight: 500; }
|
||||
.ccw-signal { animation: signalPulse 2s ease-in-out infinite; }
|
||||
.line-pulse { opacity: 0.4; }
|
||||
|
||||
.ring-outer { animation: rotate 60s linear infinite; transform-origin: 200px 160px; }
|
||||
.ring-inner { animation: rotate 40s linear infinite reverse; transform-origin: 200px 160px; }
|
||||
/* Control lines */
|
||||
.ctrl-line { fill: none; stroke: var(--vp-c-text-4); stroke-width: 0.8; stroke-dasharray: 3 3; opacity: 0; animation: fadeIn 0.6s ease forwards; animation-delay: var(--d); }
|
||||
|
||||
.connection-path {
|
||||
fill: none;
|
||||
stroke: var(--vp-c-brand-1);
|
||||
stroke-width: 0.8;
|
||||
opacity: 0.05;
|
||||
}
|
||||
/* Stations - animation only uses opacity, no transform conflict */
|
||||
.station { opacity: 0; animation: stationAppear 0.5s cubic-bezier(0.16, 1, 0.3, 1) forwards; animation-delay: var(--sd); }
|
||||
.station-box { fill: var(--vp-c-bg-soft); stroke: var(--sc); stroke-width: 1.2; transition: all 0.3s ease; }
|
||||
.station:hover .station-box { stroke-width: 2; }
|
||||
.st-name { font-size: 11px; font-weight: 700; letter-spacing: 0.06em; }
|
||||
.st-role { font-size: 7.5px; fill: var(--vp-c-text-3); letter-spacing: 0.1em; text-transform: uppercase; font-weight: 500; }
|
||||
|
||||
.data-pulse {
|
||||
fill: var(--vp-c-brand-2);
|
||||
filter: drop-shadow(0 0 4px var(--vp-c-brand-2));
|
||||
}
|
||||
/* Belt */
|
||||
.roller { fill: var(--vp-c-bg-soft); stroke: var(--vp-c-divider); stroke-width: 0.5; }
|
||||
.belt-scroll { animation: beltScroll 1.2s linear infinite; }
|
||||
|
||||
.agent-group {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
/* Sparks */
|
||||
.spark { opacity: 0.5; filter: drop-shadow(0 0 2px currentColor); }
|
||||
|
||||
.agent-circle {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
/* I/O */
|
||||
.io-label { font-size: 8px; fill: var(--vp-c-text-3); letter-spacing: 0.08em; text-transform: uppercase; font-weight: 600; opacity: 0.5; }
|
||||
.check-pulse { animation: checkPulse 2.5s ease-in-out infinite; }
|
||||
|
||||
.agent-halo {
|
||||
opacity: 0.2;
|
||||
animation: agent-pulse 2s ease-in-out infinite;
|
||||
transform-origin: center;
|
||||
}
|
||||
/* Status */
|
||||
.status-blink { animation: blink 2.5s ease-in-out infinite; }
|
||||
.status-text { font-size: 7.5px; fill: var(--vp-c-text-3); letter-spacing: 0.08em; text-transform: uppercase; font-weight: 600; opacity: 0.5; }
|
||||
|
||||
.agent-label {
|
||||
font-size: 10px;
|
||||
fill: var(--vp-c-text-2);
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
opacity: 0.7;
|
||||
}
|
||||
/* Particles */
|
||||
.micro-dot { animation: microFloat var(--fd) ease-in-out infinite; animation-delay: var(--fdy); }
|
||||
|
||||
.core-bg {
|
||||
fill: var(--vp-c-bg-soft);
|
||||
stroke: var(--vp-c-brand-soft);
|
||||
stroke-width: 1;
|
||||
}
|
||||
/* Keyframes */
|
||||
@keyframes ccwRing { 0%, 100% { opacity: 0.06; } 50% { opacity: 0.18; } }
|
||||
@keyframes signalPulse { 0%, 100% { opacity: 0.3; } 50% { opacity: 0.8; } }
|
||||
@keyframes fadeIn { to { opacity: 0.25; } }
|
||||
/* Only animate opacity - SVG positioning handled by parent <g> transform attribute */
|
||||
@keyframes stationAppear { from { opacity: 0; } to { opacity: 1; } }
|
||||
@keyframes beltScroll { from { transform: translateX(0); } to { transform: translateX(-18px); } }
|
||||
@keyframes blink { 0%, 100% { opacity: 0.5; } 50% { opacity: 0.15; } }
|
||||
@keyframes checkPulse { 0%, 100% { opacity: 0.06; } 50% { opacity: 0.14; } }
|
||||
@keyframes microFloat { 0%, 100% { transform: translateY(0); opacity: 0.05; } 50% { transform: translateY(-5px); opacity: 0.14; } }
|
||||
|
||||
.core-inner {
|
||||
opacity: 0.8;
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; }
|
||||
.hero-anim.is-visible { opacity: 1; transform: none; }
|
||||
.station { opacity: 1; }
|
||||
.ctrl-line { opacity: 0.25; }
|
||||
}
|
||||
|
||||
.core-text {
|
||||
font-size: 14px;
|
||||
font-weight: 800;
|
||||
fill: white;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.core-scanner {
|
||||
animation: rotate 3s linear infinite;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
@keyframes agent-pulse {
|
||||
0%, 100% { transform: scale(1); opacity: 0.2; }
|
||||
50% { transform: scale(1.3); opacity: 0.1; }
|
||||
}
|
||||
|
||||
.agent-node {
|
||||
animation: agent-float 4s ease-in-out infinite;
|
||||
animation-delay: var(--delay);
|
||||
}
|
||||
|
||||
@keyframes agent-float {
|
||||
0%, 100% { transform: translateY(0); }
|
||||
50% { transform: translateY(-5px); }
|
||||
}
|
||||
|
||||
.hero-animation-container:hover .agent-circle {
|
||||
filter: blur(2px) brightness(1.5);
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -23,29 +23,18 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="terminal-visual">
|
||||
<div class="terminal-window">
|
||||
<div class="terminal-header">
|
||||
<div class="dots"><span></span><span></span><span></span></div>
|
||||
<div class="title">workflow.json — ccw run</div>
|
||||
</div>
|
||||
<div class="terminal-body font-mono">
|
||||
<div v-for="(line, idx) in terminalLines.slice(0, terminalStep)" :key="idx" :class="line.color" class="line">
|
||||
{{ line.text }}
|
||||
</div>
|
||||
<div v-if="terminalStep < terminalLines.length" class="cursor">_</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hero-visual">
|
||||
<HeroAnimation />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Features Grid -->
|
||||
<section class="features-section">
|
||||
<section class="features-section" ref="featuresRef">
|
||||
<div class="section-container">
|
||||
<h2 class="section-title">{{ t.featureTitle }}</h2>
|
||||
<h2 class="section-title reveal-text" :class="{ visible: featuresVisible }">{{ t.featureTitle }}</h2>
|
||||
<div class="features-grid">
|
||||
<div v-for="(f, i) in t.features" :key="i" class="feature-card">
|
||||
<div v-for="(f, i) in t.features" :key="i" class="feature-card reveal-card" :class="{ visible: featuresVisible }" :style="{ '--card-delay': i * 0.1 + 's' }">
|
||||
<div class="feature-icon-box">
|
||||
<svg v-if="i === 0" viewBox="0 0 24 24" width="28" height="28" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0110 0v4"/><circle cx="12" cy="16" r="1"/>
|
||||
@@ -68,9 +57,9 @@
|
||||
</section>
|
||||
|
||||
<!-- Pipeline Visual -->
|
||||
<section class="pipeline-section">
|
||||
<section class="pipeline-section" ref="pipelineRef">
|
||||
<div class="section-container">
|
||||
<div class="section-header">
|
||||
<div class="section-header reveal-text" :class="{ visible: pipelineVisible }">
|
||||
<h2>{{ t.pipelineTitle }}</h2>
|
||||
<p>{{ t.pipelineSubtitle }}</p>
|
||||
</div>
|
||||
@@ -147,9 +136,9 @@
|
||||
</section>
|
||||
|
||||
<!-- JSON Demo -->
|
||||
<section class="json-section">
|
||||
<section class="json-section" ref="jsonRef">
|
||||
<div class="section-container">
|
||||
<div class="json-grid">
|
||||
<div class="json-grid reveal-slide" :class="{ visible: jsonVisible }">
|
||||
<div class="json-text">
|
||||
<h2>{{ t.jsonTitle }}</h2>
|
||||
<p>{{ t.jsonSubtitle }}</p>
|
||||
@@ -172,9 +161,9 @@
|
||||
</section>
|
||||
|
||||
<!-- Quick Start Section -->
|
||||
<section class="quickstart-section">
|
||||
<section class="quickstart-section" ref="quickstartRef">
|
||||
<div class="section-container">
|
||||
<div class="quickstart-layout">
|
||||
<div class="quickstart-layout reveal-slide" :class="{ visible: quickstartVisible }">
|
||||
<div class="quickstart-info">
|
||||
<h2 class="quickstart-title">{{ t.quickStartTitle }}</h2>
|
||||
<p class="quickstart-desc">{{ t.quickStartDesc }}</p>
|
||||
@@ -225,9 +214,9 @@
|
||||
</section>
|
||||
|
||||
<!-- CTA Footer -->
|
||||
<section class="cta-section">
|
||||
<section class="cta-section" ref="ctaRef">
|
||||
<div class="section-container">
|
||||
<div class="cta-card">
|
||||
<div class="cta-card reveal-scale" :class="{ visible: ctaVisible }">
|
||||
<h2>{{ t.ctaTitle }}</h2>
|
||||
<p>{{ t.ctaDesc }}</p>
|
||||
<div class="cta-actions">
|
||||
@@ -248,6 +237,7 @@
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted, computed } from 'vue'
|
||||
import HeroAnimation from './HeroAnimation.vue'
|
||||
|
||||
const props = defineProps({
|
||||
lang: { type: String, default: 'en' }
|
||||
@@ -256,6 +246,21 @@ const props = defineProps({
|
||||
const localePath = computed(() => props.lang === 'zh' ? '/zh' : '')
|
||||
const activeTab = ref(0)
|
||||
|
||||
// --- Scroll Reveal ---
|
||||
const featuresRef = ref(null)
|
||||
const pipelineRef = ref(null)
|
||||
const jsonRef = ref(null)
|
||||
const quickstartRef = ref(null)
|
||||
const ctaRef = ref(null)
|
||||
|
||||
const featuresVisible = ref(false)
|
||||
const pipelineVisible = ref(false)
|
||||
const jsonVisible = ref(false)
|
||||
const quickstartVisible = ref(false)
|
||||
const ctaVisible = ref(false)
|
||||
|
||||
let observer = null
|
||||
|
||||
// --- Translations ---
|
||||
const content = {
|
||||
en: {
|
||||
@@ -263,7 +268,7 @@ const content = {
|
||||
heroTitle: 'JSON-Driven Multi-Agent <br/> <span class="gradient-text">Collaborative Framework</span>',
|
||||
heroSubtitle: 'Industrial-grade orchestration blending 21 specialized agents and 32 core skills with precision cadence control.',
|
||||
getStarted: 'Get Started',
|
||||
featureTitle: 'The CCW Ecosystem',
|
||||
featureTitle: 'The Claude Code Workflow Ecosystem',
|
||||
features: [
|
||||
{ title: "21 Specialized Agents", desc: "From CLI exploration to TDD implementation and UI design token management." },
|
||||
{ title: "32 Core Skills", desc: "Standalone, Team, and Workflow skills including brainstorm, spec-gen, and code-review." },
|
||||
@@ -336,7 +341,7 @@ const content = {
|
||||
heroTitle: 'JSON 驱动的多智能体 <br/> <span class="gradient-text">协同开发框架</span>',
|
||||
heroSubtitle: '融合 21 个专业代理与 32 项核心技能,通过精准节拍控制实现工业级自动化工作流处理。',
|
||||
getStarted: '快速开始',
|
||||
featureTitle: 'CCW 全局生态概览',
|
||||
featureTitle: 'Claude Code Workflow 全局生态概览',
|
||||
features: [
|
||||
{ title: "21 个专业代理", desc: "涵盖 CLI 探索、TDD 实现、UI 设计令牌管理等全流程智能体。" },
|
||||
{ title: "32 项核心技能", desc: "独立技能、团队技能与工作流技能,覆盖头脑风暴、规格生成、代码审查等场景。" },
|
||||
@@ -465,11 +470,34 @@ onMounted(() => {
|
||||
pipelineInterval = setInterval(() => {
|
||||
currentTick.value = (currentTick.value + 1) % sequence.length
|
||||
}, 3000)
|
||||
|
||||
// Scroll reveal observer
|
||||
const sectionMap = new Map([
|
||||
[featuresRef.value, featuresVisible],
|
||||
[pipelineRef.value, pipelineVisible],
|
||||
[jsonRef.value, jsonVisible],
|
||||
[quickstartRef.value, quickstartVisible],
|
||||
[ctaRef.value, ctaVisible]
|
||||
])
|
||||
|
||||
observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
const visRef = sectionMap.get(entry.target)
|
||||
if (visRef) visRef.value = true
|
||||
}
|
||||
})
|
||||
}, { threshold: 0.15, rootMargin: '0px 0px -60px 0px' })
|
||||
|
||||
sectionMap.forEach((_, el) => {
|
||||
if (el) observer.observe(el)
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
clearInterval(terminalInterval)
|
||||
clearInterval(pipelineInterval)
|
||||
if (observer) observer.disconnect()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -483,7 +511,7 @@ onUnmounted(() => {
|
||||
.section-container {
|
||||
max-width: 1152px;
|
||||
margin: 0 auto;
|
||||
padding: 0 1.5rem;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -493,7 +521,9 @@ onUnmounted(() => {
|
||||
.hero-section {
|
||||
width: 100%;
|
||||
padding: 5rem 0 4rem;
|
||||
background: radial-gradient(ellipse at 70% 20%, var(--vp-c-brand-soft) 0%, transparent 55%);
|
||||
background:
|
||||
radial-gradient(ellipse 120% 80% at 65% 30%, var(--vp-c-brand-soft) 0%, transparent 70%),
|
||||
radial-gradient(ellipse 60% 50% at 90% 60%, color-mix(in srgb, var(--vp-c-brand-soft) 40%, transparent) 0%, transparent 100%);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
@@ -502,13 +532,19 @@ onUnmounted(() => {
|
||||
max-width: 1152px;
|
||||
margin: 0 auto;
|
||||
display: grid;
|
||||
grid-template-columns: 1.1fr 0.9fr;
|
||||
gap: 3rem;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 2rem;
|
||||
align-items: center;
|
||||
padding: 0 1.5rem;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.hero-visual {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.hero-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
@@ -539,12 +575,6 @@ onUnmounted(() => {
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.gradient-text {
|
||||
background: linear-gradient(135deg, var(--vp-c-brand-1), var(--vp-c-brand-2));
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
.hero-subtitle {
|
||||
font-size: 1.1rem;
|
||||
color: var(--vp-c-text-2);
|
||||
@@ -1045,9 +1075,94 @@ onUnmounted(() => {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
* Terminal cursor blink
|
||||
* ============================================ */
|
||||
.cursor-blink {
|
||||
color: #10b981;
|
||||
font-weight: bold;
|
||||
animation: cursorBlink 1s step-end infinite;
|
||||
}
|
||||
|
||||
.term-line {
|
||||
animation: termLineReveal 0.3s ease-out both;
|
||||
animation-delay: calc(var(--line-idx, 0) * 0.05s);
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
* Scroll Reveal Animations
|
||||
* ============================================ */
|
||||
.reveal-text {
|
||||
opacity: 0;
|
||||
transform: translateY(24px);
|
||||
transition: opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1), transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
.reveal-text.visible {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.reveal-card {
|
||||
opacity: 0;
|
||||
transform: translateY(32px);
|
||||
transition: opacity 0.5s cubic-bezier(0.16, 1, 0.3, 1), transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
transition-delay: var(--card-delay, 0s);
|
||||
}
|
||||
.reveal-card.visible {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.reveal-slide {
|
||||
opacity: 0;
|
||||
transform: translateY(40px);
|
||||
transition: opacity 0.7s cubic-bezier(0.16, 1, 0.3, 1), transform 0.7s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
.reveal-slide.visible {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.reveal-scale {
|
||||
opacity: 0;
|
||||
transform: scale(0.95) translateY(20px);
|
||||
transition: opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1), transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
.reveal-scale.visible {
|
||||
opacity: 1;
|
||||
transform: scale(1) translateY(0);
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
* Gradient text shimmer
|
||||
* ============================================ */
|
||||
.gradient-text {
|
||||
background: linear-gradient(135deg, var(--vp-c-brand-1), #8B5CF6, var(--vp-c-brand-2));
|
||||
background-size: 200% 200%;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
animation: gradientShimmer 6s ease infinite;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
* Animations
|
||||
* ============================================ */
|
||||
@keyframes cursorBlink {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0; }
|
||||
}
|
||||
|
||||
@keyframes termLineReveal {
|
||||
from { opacity: 0; transform: translateX(-8px); }
|
||||
to { opacity: 1; transform: translateX(0); }
|
||||
}
|
||||
|
||||
@keyframes gradientShimmer {
|
||||
0% { background-position: 0% 50%; }
|
||||
50% { background-position: 100% 50%; }
|
||||
100% { background-position: 0% 50%; }
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% { box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.4); }
|
||||
70% { box-shadow: 0 0 0 10px rgba(59, 130, 246, 0); }
|
||||
@@ -1063,8 +1178,13 @@ onUnmounted(() => {
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*, *::before, *::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
.reveal-text, .reveal-card, .reveal-slide, .reveal-scale {
|
||||
opacity: 1 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
|
||||
@@ -2,10 +2,14 @@
|
||||
<div class="workflow-animation">
|
||||
<div class="workflow-container">
|
||||
<div class="workflow-node coordinator">
|
||||
<div class="node-icon">🎯</div>
|
||||
<div class="node-icon">
|
||||
<svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="3"/><path d="M12 1v4M12 19v4M4.22 4.22l2.83 2.83M16.95 16.95l2.83 2.83M1 12h4M19 12h4M4.22 19.78l2.83-2.83M16.95 7.05l2.83-2.83"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="node-label">Coordinator</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="workflow-paths">
|
||||
<svg class="path-svg" viewBox="0 0 400 200">
|
||||
<!-- Spec Path -->
|
||||
@@ -13,13 +17,13 @@
|
||||
<circle class="flow-dot dot-spec" r="6" fill="#3B82F6">
|
||||
<animateMotion dur="3s" repeatCount="indefinite" path="M50,100 Q150,20 250,50"/>
|
||||
</circle>
|
||||
|
||||
|
||||
<!-- Impl Path -->
|
||||
<path class="flow-path path-impl" d="M50,100 Q150,100 250,100" fill="none" stroke="#10B981" stroke-width="2"/>
|
||||
<circle class="flow-dot dot-impl" r="6" fill="#10B981">
|
||||
<animateMotion dur="2.5s" repeatCount="indefinite" path="M50,100 Q150,100 250,100"/>
|
||||
</circle>
|
||||
|
||||
|
||||
<!-- Test Path -->
|
||||
<path class="flow-path path-test" d="M50,100 Q150,180 250,150" fill="none" stroke="#F59E0B" stroke-width="2"/>
|
||||
<circle class="flow-dot dot-test" r="6" fill="#F59E0B">
|
||||
@@ -27,27 +31,44 @@
|
||||
</circle>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="workflow-nodes">
|
||||
<div class="workflow-node analyst">
|
||||
<div class="node-icon">📊</div>
|
||||
<div class="node-icon">
|
||||
<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M18 20V10M12 20V4M6 20v-6"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="node-label">Analyst</div>
|
||||
</div>
|
||||
<div class="workflow-node writer">
|
||||
<div class="node-icon">✍️</div>
|
||||
<div class="node-icon">
|
||||
<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M17 3a2.83 2.83 0 114 4L7.5 20.5 2 22l1.5-5.5L17 3z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="node-label">Writer</div>
|
||||
</div>
|
||||
<div class="workflow-node executor">
|
||||
<div class="node-icon">⚡</div>
|
||||
<div class="node-icon">
|
||||
<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="node-label">Executor</div>
|
||||
</div>
|
||||
<div class="workflow-node tester">
|
||||
<div class="node-icon">🧪</div>
|
||||
<div class="node-icon">
|
||||
<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M9 3h6M12 3v7l-4 8h8l-4-8"/>
|
||||
<circle cx="8" cy="20" r="1"/><circle cx="16" cy="20" r="1"/><circle cx="12" cy="17" r="1"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="node-label">Tester</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="workflow-legend">
|
||||
<div class="legend-item"><span class="dot spec"></span> Spec Phase</div>
|
||||
<div class="legend-item"><span class="dot impl"></span> Impl Phase</div>
|
||||
@@ -57,10 +78,9 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, onUnmounted } from 'vue'
|
||||
import { onMounted } from 'vue'
|
||||
|
||||
onMounted(() => {
|
||||
// Add animation class after mount
|
||||
document.querySelector('.workflow-animation')?.classList.add('animate')
|
||||
})
|
||||
</script>
|
||||
@@ -129,6 +149,7 @@ onMounted(() => {
|
||||
border-radius: 16px;
|
||||
box-shadow: var(--vp-shadow-sm);
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.workflow-node:hover {
|
||||
@@ -138,8 +159,14 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.node-icon {
|
||||
font-size: 2rem;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.node-label {
|
||||
@@ -155,10 +182,19 @@ onMounted(() => {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.workflow-node.coordinator .node-icon {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.workflow-node.coordinator .node-label {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.workflow-node.analyst .node-icon { color: #3B82F6; }
|
||||
.workflow-node.writer .node-icon { color: #8B5CF6; }
|
||||
.workflow-node.executor .node-icon { color: #10B981; }
|
||||
.workflow-node.tester .node-icon { color: #F59E0B; }
|
||||
|
||||
.workflow-legend {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
@@ -186,15 +222,23 @@ onMounted(() => {
|
||||
.dot.impl { background: var(--vp-c-secondary-500); }
|
||||
.dot.test { background: var(--vp-c-accent-400); }
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*, *::before, *::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.workflow-animation {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
|
||||
.workflow-container {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
|
||||
.workflow-nodes {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user