mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-03-01 15:03:57 +08:00
docs: add VitePress documentation site
- Add docs directory with VitePress configuration - Add GitHub Actions workflow for docs build and deploy - Support bilingual (English/Chinese) documentation - Include search, custom theme, and responsive design
This commit is contained in:
363
docs/.vitepress/config.ts
Normal file
363
docs/.vitepress/config.ts
Normal file
@@ -0,0 +1,363 @@
|
||||
import { defineConfig } from 'vitepress'
|
||||
|
||||
const repoName = process.env.GITHUB_REPOSITORY?.split('/')[1]
|
||||
const isUserOrOrgSite = Boolean(repoName && repoName.endsWith('.github.io'))
|
||||
|
||||
const base =
|
||||
process.env.CCW_DOCS_BASE ||
|
||||
(process.env.GITHUB_ACTIONS && repoName && !isUserOrOrgSite ? `/${repoName}/` : '/')
|
||||
|
||||
export default defineConfig({
|
||||
title: 'CCW Documentation',
|
||||
description: 'Claude Code Workspace - Advanced AI-Powered Development Environment',
|
||||
lang: 'zh-CN',
|
||||
base,
|
||||
|
||||
// Ignore dead links for incomplete docs
|
||||
ignoreDeadLinks: true,
|
||||
head: [
|
||||
['link', { rel: 'icon', href: '/favicon.svg', type: 'image/svg+xml' }],
|
||||
[
|
||||
'script',
|
||||
{},
|
||||
`(() => {
|
||||
try {
|
||||
const theme = localStorage.getItem('ccw-theme') || 'blue'
|
||||
document.documentElement.setAttribute('data-theme', theme)
|
||||
|
||||
const mode = localStorage.getItem('ccw-color-mode') || 'auto'
|
||||
const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
const isDark = mode === 'dark' || (mode === 'auto' && prefersDark)
|
||||
document.documentElement.classList.toggle('dark', isDark)
|
||||
} catch {}
|
||||
})()`
|
||||
],
|
||||
['meta', { name: 'theme-color', content: '#3b82f6' }],
|
||||
['meta', { name: 'og:type', content: 'website' }],
|
||||
['meta', { name: 'og:locale', content: 'en_US' }],
|
||||
['meta', { name: 'og:locale:alternate', content: 'zh_CN' }]
|
||||
],
|
||||
|
||||
// Appearance
|
||||
appearance: false,
|
||||
|
||||
// Vite build/dev optimizations
|
||||
vite: {
|
||||
optimizeDeps: {
|
||||
include: ['flexsearch']
|
||||
},
|
||||
build: {
|
||||
target: 'es2019',
|
||||
cssCodeSplit: true
|
||||
}
|
||||
},
|
||||
|
||||
// Theme configuration
|
||||
themeConfig: {
|
||||
logo: '/logo.svg',
|
||||
|
||||
// Right-side table of contents (outline)
|
||||
outline: {
|
||||
level: [2, 3],
|
||||
label: 'On this page'
|
||||
},
|
||||
|
||||
// Navigation - 按照 Trellis 风格组织
|
||||
nav: [
|
||||
{ text: 'Guide', link: '/guide/ch01-what-is-claude-dms3' },
|
||||
{ text: 'Commands', link: '/commands/claude/' },
|
||||
{ text: 'Skills', link: '/skills/' },
|
||||
{ text: 'Features', link: '/features/spec' },
|
||||
{
|
||||
text: 'Languages',
|
||||
items: [
|
||||
{ text: '简体中文', link: '/zh/guide/ch01-what-is-claude-dms3' }
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
// Sidebar - 按照 Trellis 风格组织
|
||||
sidebar: {
|
||||
'/guide/': [
|
||||
{
|
||||
text: 'Guide',
|
||||
items: [
|
||||
{ text: 'What is Claude_dms3', link: '/guide/ch01-what-is-claude-dms3' },
|
||||
{ text: 'Getting Started', link: '/guide/ch02-getting-started' },
|
||||
{ text: 'Core Concepts', link: '/guide/ch03-core-concepts' },
|
||||
{ text: 'Workflow Basics', link: '/guide/ch04-workflow-basics' },
|
||||
{ text: 'Advanced Tips', link: '/guide/ch05-advanced-tips' },
|
||||
{ text: 'Best Practices', link: '/guide/ch06-best-practices' }
|
||||
]
|
||||
}
|
||||
],
|
||||
'/commands/': [
|
||||
{
|
||||
text: 'Claude Commands',
|
||||
collapsible: true,
|
||||
items: [
|
||||
{ text: 'Overview', link: '/commands/claude/' },
|
||||
{ text: 'Core Orchestration', link: '/commands/claude/core-orchestration' },
|
||||
{ text: 'Workflow', link: '/commands/claude/workflow' },
|
||||
{ text: 'Session', link: '/commands/claude/session' },
|
||||
{ text: 'Issue', link: '/commands/claude/issue' },
|
||||
{ text: 'Memory', link: '/commands/claude/memory' },
|
||||
{ text: 'CLI', link: '/commands/claude/cli' },
|
||||
{ text: 'UI Design', link: '/commands/claude/ui-design' }
|
||||
]
|
||||
},
|
||||
{
|
||||
text: 'Codex Prompts',
|
||||
collapsible: true,
|
||||
items: [
|
||||
{ text: 'Overview', link: '/commands/codex/' },
|
||||
{ text: 'Prep', link: '/commands/codex/prep' },
|
||||
{ text: 'Review', link: '/commands/codex/review' }
|
||||
]
|
||||
}
|
||||
],
|
||||
'/skills/': [
|
||||
{
|
||||
text: 'Claude Skills',
|
||||
collapsible: true,
|
||||
items: [
|
||||
{ text: 'Overview', link: '/skills/claude-index' },
|
||||
{ text: 'Collaboration', link: '/skills/claude-collaboration' },
|
||||
{ text: 'Workflow', link: '/skills/claude-workflow' },
|
||||
{ text: 'Memory', link: '/skills/claude-memory' },
|
||||
{ text: 'Review', link: '/skills/claude-review' },
|
||||
{ text: 'Meta', link: '/skills/claude-meta' }
|
||||
]
|
||||
},
|
||||
{
|
||||
text: 'Codex Skills',
|
||||
collapsible: true,
|
||||
items: [
|
||||
{ text: 'Overview', link: '/skills/codex-index' },
|
||||
{ text: 'Lifecycle', link: '/skills/codex-lifecycle' },
|
||||
{ text: 'Workflow', link: '/skills/codex-workflow' },
|
||||
{ text: 'Specialized', link: '/skills/codex-specialized' }
|
||||
]
|
||||
}
|
||||
],
|
||||
'/features/': [
|
||||
{
|
||||
text: 'Core Features',
|
||||
items: [
|
||||
{ text: 'Spec System', link: '/features/spec' },
|
||||
{ text: 'Memory System', link: '/features/memory' },
|
||||
{ text: 'CLI Call', link: '/features/cli' },
|
||||
{ text: 'Dashboard', link: '/features/dashboard' },
|
||||
{ text: 'CodexLens', link: '/features/codexlens' },
|
||||
{ text: 'API Settings', link: '/features/api-settings' },
|
||||
{ text: 'System Settings', link: '/features/system-settings' }
|
||||
]
|
||||
}
|
||||
],
|
||||
'/mcp/': [
|
||||
{
|
||||
text: 'MCP Tools',
|
||||
collapsible: true,
|
||||
items: [
|
||||
{ text: 'Overview', link: '/mcp/tools' }
|
||||
]
|
||||
}
|
||||
],
|
||||
'/agents/': [
|
||||
{
|
||||
text: 'Agents',
|
||||
collapsible: true,
|
||||
items: [
|
||||
{ text: 'Overview', link: '/agents/' },
|
||||
{ text: 'Built-in Agents', link: '/agents/builtin' },
|
||||
{ text: 'Custom Agents', link: '/agents/custom' }
|
||||
]
|
||||
}
|
||||
],
|
||||
'/workflows/': [
|
||||
{
|
||||
text: 'Workflow System',
|
||||
collapsible: true,
|
||||
items: [
|
||||
{ text: 'Overview', link: '/workflows/' },
|
||||
{ text: '4-Level System', link: '/workflows/4-level' },
|
||||
{ text: 'Best Practices', link: '/workflows/best-practices' }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// Social links
|
||||
socialLinks: [
|
||||
{ icon: 'github', link: 'https://github.com/catlog22/Claude-Code-Workflow' }
|
||||
],
|
||||
|
||||
// Footer
|
||||
footer: {
|
||||
message: 'Released under the MIT License.',
|
||||
copyright: 'Copyright © 2025-present CCW Contributors'
|
||||
},
|
||||
|
||||
// Edit link
|
||||
editLink: {
|
||||
pattern: 'https://github.com/catlog22/Claude-Code-Workflow/edit/main/docs/:path',
|
||||
text: 'Edit this page on GitHub'
|
||||
},
|
||||
|
||||
// Last updated
|
||||
lastUpdated: {
|
||||
text: 'Last updated',
|
||||
formatOptions: {
|
||||
dateStyle: 'full',
|
||||
timeStyle: 'short'
|
||||
}
|
||||
},
|
||||
|
||||
// Search (handled by custom FlexSearch DocSearch component)
|
||||
search: false
|
||||
},
|
||||
|
||||
// Markdown configuration
|
||||
markdown: {
|
||||
lineNumbers: true,
|
||||
theme: {
|
||||
light: 'github-light',
|
||||
dark: 'github-dark'
|
||||
},
|
||||
languages: [
|
||||
'bash',
|
||||
'powershell',
|
||||
'json',
|
||||
'yaml',
|
||||
'toml',
|
||||
'javascript',
|
||||
'typescript',
|
||||
'vue',
|
||||
'markdown'
|
||||
],
|
||||
config: (md) => {
|
||||
// Add markdown-it plugins if needed
|
||||
}
|
||||
},
|
||||
|
||||
// locales
|
||||
locales: {
|
||||
root: {
|
||||
label: 'English',
|
||||
lang: 'en-US'
|
||||
},
|
||||
zh: {
|
||||
label: '简体中文',
|
||||
lang: 'zh-CN',
|
||||
title: 'CCW 文档',
|
||||
description: 'Claude Code Workspace - 高级 AI 驱动开发环境',
|
||||
themeConfig: {
|
||||
outline: {
|
||||
level: [2, 3],
|
||||
label: '本页目录'
|
||||
},
|
||||
nav: [
|
||||
{ text: '指南', link: '/zh/guide/ch01-what-is-claude-dms3' },
|
||||
{ text: '命令', link: '/zh/commands/claude/' },
|
||||
{ text: '技能', link: '/skills/' },
|
||||
{ text: '功能', link: '/zh/features/spec' },
|
||||
{
|
||||
text: '语言',
|
||||
items: [
|
||||
{ text: 'English', link: '/guide/ch01-what-is-claude-dms3' }
|
||||
]
|
||||
}
|
||||
],
|
||||
sidebar: {
|
||||
'/zh/guide/': [
|
||||
{
|
||||
text: '指南',
|
||||
items: [
|
||||
{ text: 'Claude_dms3 是什么', link: '/zh/guide/ch01-what-is-claude-dms3' },
|
||||
{ text: '快速开始', link: '/zh/guide/ch02-getting-started' },
|
||||
{ text: '核心概念', link: '/zh/guide/ch03-core-concepts' },
|
||||
{ text: '工作流基础', link: '/zh/guide/ch04-workflow-basics' },
|
||||
{ text: '高级技巧', link: '/zh/guide/ch05-advanced-tips' },
|
||||
{ text: '最佳实践', link: '/zh/guide/ch06-best-practices' }
|
||||
]
|
||||
}
|
||||
],
|
||||
'/zh/commands/': [
|
||||
{
|
||||
text: 'Claude 命令',
|
||||
collapsible: true,
|
||||
items: [
|
||||
{ text: '概述', link: '/zh/commands/claude/' },
|
||||
{ text: '核心编排', link: '/zh/commands/claude/core-orchestration' },
|
||||
{ text: '工作流', link: '/zh/commands/claude/workflow' },
|
||||
{ text: '会话管理', link: '/zh/commands/claude/session' },
|
||||
{ text: 'Issue', link: '/zh/commands/claude/issue' },
|
||||
{ text: 'Memory', link: '/zh/commands/claude/memory' },
|
||||
{ text: 'CLI', link: '/zh/commands/claude/cli' },
|
||||
{ text: 'UI 设计', link: '/zh/commands/claude/ui-design' }
|
||||
]
|
||||
},
|
||||
{
|
||||
text: 'Codex Prompts',
|
||||
collapsible: true,
|
||||
items: [
|
||||
{ text: '概述', link: '/zh/commands/codex/' },
|
||||
{ text: 'Prep', link: '/zh/commands/codex/prep' },
|
||||
{ text: 'Review', link: '/zh/commands/codex/review' }
|
||||
]
|
||||
}
|
||||
],
|
||||
'/zh/skills/': [
|
||||
{
|
||||
text: 'Claude Skills',
|
||||
collapsible: true,
|
||||
items: [
|
||||
{ text: '概述', link: '/zh/skills/claude-index' },
|
||||
{ text: '协作', link: '/zh/skills/claude-collaboration' },
|
||||
{ text: '工作流', link: '/zh/skills/claude-workflow' },
|
||||
{ text: '记忆', link: '/zh/skills/claude-memory' },
|
||||
{ text: '审查', link: '/zh/skills/claude-review' },
|
||||
{ text: '元技能', link: '/zh/skills/claude-meta' }
|
||||
]
|
||||
},
|
||||
{
|
||||
text: 'Codex Skills',
|
||||
collapsible: true,
|
||||
items: [
|
||||
{ text: '概述', link: '/zh/skills/codex-index' },
|
||||
{ text: '生命周期', link: '/zh/skills/codex-lifecycle' },
|
||||
{ text: '工作流', link: '/zh/skills/codex-workflow' },
|
||||
{ text: '专项', link: '/zh/skills/codex-specialized' }
|
||||
]
|
||||
}
|
||||
],
|
||||
'/zh/features/': [
|
||||
{
|
||||
text: '核心功能',
|
||||
items: [
|
||||
{ text: 'Spec 规范系统', link: '/zh/features/spec' },
|
||||
{ text: 'Memory 记忆系统', link: '/zh/features/memory' },
|
||||
{ text: 'CLI 调用', link: '/zh/features/cli' },
|
||||
{ text: 'Dashboard 面板', link: '/zh/features/dashboard' },
|
||||
{ text: 'CodexLens', link: '/zh/features/codexlens' },
|
||||
{ text: 'API 设置', link: '/zh/features/api-settings' },
|
||||
{ text: '系统设置', link: '/zh/features/system-settings' }
|
||||
]
|
||||
}
|
||||
],
|
||||
'/zh/workflows/': [
|
||||
{
|
||||
text: '工作流系统',
|
||||
collapsible: true,
|
||||
items: [
|
||||
{ text: '概述', link: '/zh/workflows/' },
|
||||
{ text: '四级体系', link: '/zh/workflows/4-level' },
|
||||
{ text: '最佳实践', link: '/zh/workflows/best-practices' }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
25
docs/.vitepress/search/flexsearch.mjs
Normal file
25
docs/.vitepress/search/flexsearch.mjs
Normal file
@@ -0,0 +1,25 @@
|
||||
export const FLEXSEARCH_INDEX_VERSION = 1
|
||||
|
||||
export function flexsearchEncode(text) {
|
||||
const normalized = String(text ?? '')
|
||||
.toLowerCase()
|
||||
.normalize('NFKC')
|
||||
|
||||
const tokens = normalized.match(
|
||||
/[a-z0-9]+|[\u3040-\u30ff\u3400-\u4dbf\u4e00-\u9fff\uac00-\ud7af]/g
|
||||
)
|
||||
|
||||
return tokens ?? []
|
||||
}
|
||||
|
||||
export const FLEXSEARCH_OPTIONS = {
|
||||
tokenize: 'forward',
|
||||
resolution: 9,
|
||||
cache: 100,
|
||||
encode: flexsearchEncode
|
||||
}
|
||||
|
||||
export function createFlexSearchIndex(FlexSearch) {
|
||||
return new FlexSearch.Index(FLEXSEARCH_OPTIONS)
|
||||
}
|
||||
|
||||
216
docs/.vitepress/theme/components/AgentOrchestration.vue
Normal file
216
docs/.vitepress/theme/components/AgentOrchestration.vue
Normal file
@@ -0,0 +1,216 @@
|
||||
<template>
|
||||
<div class="agent-orchestration">
|
||||
<div class="orchestration-title">🤖 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>
|
||||
</div>
|
||||
|
||||
<!-- Flow Arrow -->
|
||||
<div class="flow-arrow">▼</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>
|
||||
</div>
|
||||
|
||||
<!-- Flow Arrow -->
|
||||
<div class="flow-arrow">▼</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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tooltip" v-if="tooltip" :class="{ visible: tooltip }">
|
||||
{{ tooltipText }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
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: 通用任务执行器'
|
||||
}
|
||||
|
||||
function showTooltip(key) {
|
||||
tooltipText.value = tooltips[key] || ''
|
||||
tooltip.value = true
|
||||
}
|
||||
|
||||
function hideTooltip() {
|
||||
tooltip.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.agent-orchestration {
|
||||
padding: 3rem 2rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 24px;
|
||||
margin: 2rem 0;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.orchestration-title {
|
||||
text-align: center;
|
||||
font-size: 1.75rem;
|
||||
font-weight: 700;
|
||||
color: var(--vp-c-text-1);
|
||||
margin-bottom: 2.5rem;
|
||||
}
|
||||
|
||||
.agent-flow {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.flow-layer {
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.layer-label {
|
||||
font-size: 0.7rem;
|
||||
font-weight: 800;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.15em;
|
||||
color: var(--vp-c-text-3);
|
||||
margin-bottom: 0.75rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.agents-row {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.agent-card {
|
||||
padding: 0.8rem 1.5rem;
|
||||
border-radius: 12px;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
position: relative;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.agent-card.cli {
|
||||
background: var(--vp-c-brand-soft);
|
||||
color: var(--vp-c-brand-1);
|
||||
border-color: rgba(59, 130, 246, 0.2);
|
||||
}
|
||||
|
||||
.agent-card.dev {
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
color: #10B981;
|
||||
border-color: rgba(16, 185, 129, 0.2);
|
||||
}
|
||||
|
||||
.agent-card.doc {
|
||||
background: rgba(139, 92, 246, 0.1);
|
||||
color: #8B5CF6;
|
||||
border-color: rgba(139, 92, 246, 0.2);
|
||||
}
|
||||
|
||||
.agent-card.ui {
|
||||
background: rgba(245, 158, 11, 0.1);
|
||||
color: #F59E0B;
|
||||
border-color: rgba(245, 158, 11, 0.2);
|
||||
}
|
||||
|
||||
.agent-card.universal {
|
||||
background: rgba(239, 68, 68, 0.1);
|
||||
color: #EF4444;
|
||||
border-color: rgba(239, 68, 68, 0.2);
|
||||
}
|
||||
|
||||
.agent-card:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.flow-arrow {
|
||||
color: var(--vp-c-divider);
|
||||
font-size: 1.25rem;
|
||||
animation: bounce 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes bounce {
|
||||
0%, 100% { transform: translateY(0); opacity: 0.5; }
|
||||
50% { transform: translateY(8px); opacity: 1; }
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-brand-1);
|
||||
color: var(--vp-c-text-1);
|
||||
padding: 0.75rem 1.25rem;
|
||||
border-radius: 10px;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
opacity: 0;
|
||||
transition: all 0.3s;
|
||||
pointer-events: none;
|
||||
box-shadow: var(--vp-shadow-md);
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.tooltip.visible {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.agent-orchestration {
|
||||
padding: 2rem 1rem;
|
||||
}
|
||||
|
||||
.agent-card {
|
||||
padding: 0.6rem 1rem;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
123
docs/.vitepress/theme/components/Breadcrumb.vue
Normal file
123
docs/.vitepress/theme/components/Breadcrumb.vue
Normal file
@@ -0,0 +1,123 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useData } from 'vitepress'
|
||||
|
||||
const { page } = useData()
|
||||
|
||||
interface BreadcrumbItem {
|
||||
text: string
|
||||
link?: string
|
||||
}
|
||||
|
||||
const breadcrumbs = computed<BreadcrumbItem[]>(() => {
|
||||
const items: BreadcrumbItem[] = [
|
||||
{ text: 'Home', link: '/' }
|
||||
]
|
||||
|
||||
const pathSegments = page.value.relativePath.split('/')
|
||||
const fileName = pathSegments.pop()?.replace(/\.md$/, '')
|
||||
|
||||
// Build breadcrumb from path
|
||||
let currentPath = ''
|
||||
for (const segment of pathSegments) {
|
||||
currentPath += `${segment}/`
|
||||
items.push({
|
||||
text: formatTitle(segment),
|
||||
link: `/${currentPath}`
|
||||
})
|
||||
}
|
||||
|
||||
// Add current page
|
||||
if (fileName && fileName !== 'index') {
|
||||
items.push({
|
||||
text: formatTitle(fileName)
|
||||
})
|
||||
}
|
||||
|
||||
return items
|
||||
})
|
||||
|
||||
const formatTitle = (str: string): string => {
|
||||
return str
|
||||
.split(/[-_]/)
|
||||
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.join(' ')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<nav v-if="breadcrumbs.length > 1" class="breadcrumb" aria-label="Breadcrumb">
|
||||
<ol class="breadcrumb-list">
|
||||
<li v-for="(item, index) in breadcrumbs" :key="index" class="breadcrumb-item">
|
||||
<router-link v-if="item.link && index < breadcrumbs.length - 1" :to="item.link" class="breadcrumb-link">
|
||||
{{ item.text }}
|
||||
</router-link>
|
||||
<span v-else class="breadcrumb-current">{{ item.text }}</span>
|
||||
<span v-if="index < breadcrumbs.length - 1" class="breadcrumb-separator">/</span>
|
||||
</li>
|
||||
</ol>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.breadcrumb {
|
||||
padding: 12px 0;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.breadcrumb-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.breadcrumb-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.breadcrumb-link {
|
||||
color: var(--vp-c-text-2);
|
||||
font-size: var(--vp-font-size-sm);
|
||||
text-decoration: none;
|
||||
transition: color var(--vp-transition-color);
|
||||
}
|
||||
|
||||
.breadcrumb-link:hover {
|
||||
color: var(--vp-c-primary);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.breadcrumb-current {
|
||||
color: var(--vp-c-text-1);
|
||||
font-size: var(--vp-font-size-sm);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.breadcrumb-separator {
|
||||
color: var(--vp-c-text-3);
|
||||
font-size: var(--vp-font-size-sm);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.breadcrumb {
|
||||
padding: 8px 0;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.breadcrumb-link,
|
||||
.breadcrumb-current,
|
||||
.breadcrumb-separator {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.breadcrumb-list {
|
||||
gap: 2px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
72
docs/.vitepress/theme/components/ColorSchemeSelector.vue
Normal file
72
docs/.vitepress/theme/components/ColorSchemeSelector.vue
Normal file
@@ -0,0 +1,72 @@
|
||||
<script setup lang="ts">
|
||||
// This component is integrated into ThemeSwitcher
|
||||
// Kept as separate component for modularity
|
||||
const emit = defineEmits<{
|
||||
(e: 'select', scheme: string): void
|
||||
}>()
|
||||
|
||||
const schemes = [
|
||||
{ id: 'blue', name: 'Blue', color: '#3b82f6' },
|
||||
{ id: 'green', name: 'Green', color: '#10b981' },
|
||||
{ id: 'orange', name: 'Orange', color: '#f59e0b' },
|
||||
{ id: 'purple', name: 'Purple', color: '#8b5cf6' }
|
||||
]
|
||||
|
||||
const selectScheme = (schemeId: string) => {
|
||||
emit('select', schemeId)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="color-scheme-selector">
|
||||
<button
|
||||
v-for="scheme in schemes"
|
||||
:key="scheme.id"
|
||||
:class="['scheme-option']"
|
||||
:style="{ '--scheme-color': scheme.color }"
|
||||
:aria-label="scheme.name"
|
||||
@click="selectScheme(scheme.id)"
|
||||
>
|
||||
<span class="scheme-indicator"></span>
|
||||
<span class="scheme-name">{{ scheme.name }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.color-scheme-selector {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.scheme-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 8px 12px;
|
||||
border: 1px solid var(--vp-c-border);
|
||||
border-radius: var(--vp-radius-md);
|
||||
background: var(--vp-c-bg);
|
||||
cursor: pointer;
|
||||
transition: all var(--vp-transition-color);
|
||||
}
|
||||
|
||||
.scheme-option:hover {
|
||||
border-color: var(--vp-c-primary);
|
||||
background: var(--vp-c-bg-soft);
|
||||
}
|
||||
|
||||
.scheme-indicator {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: var(--vp-radius-full);
|
||||
background: var(--scheme-color);
|
||||
border: 2px solid var(--vp-c-border);
|
||||
}
|
||||
|
||||
.scheme-name {
|
||||
font-size: var(--vp-font-size-sm);
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
</style>
|
||||
135
docs/.vitepress/theme/components/CopyCodeButton.vue
Normal file
135
docs/.vitepress/theme/components/CopyCodeButton.vue
Normal file
@@ -0,0 +1,135 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
|
||||
const props = defineProps<{
|
||||
code: string
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'copy'): void
|
||||
}>()
|
||||
|
||||
const copied = ref(false)
|
||||
|
||||
const copyToClipboard = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(props.code)
|
||||
copied.value = true
|
||||
emit('copy')
|
||||
setTimeout(() => {
|
||||
copied.value = false
|
||||
}, 2000)
|
||||
} catch (err) {
|
||||
console.error('Failed to copy:', err)
|
||||
}
|
||||
}
|
||||
|
||||
// Also handle Ctrl+C
|
||||
const handleKeydown = (e: KeyboardEvent) => {
|
||||
if (e.ctrlKey && e.key === 'c') {
|
||||
copyToClipboard()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button
|
||||
class="copy-code-button"
|
||||
:class="{ copied }"
|
||||
:aria-label="copied ? 'Copied!' : 'Copy code'"
|
||||
:title="copied ? 'Copied!' : 'Copy code (Ctrl+C)'"
|
||||
@click="copyToClipboard"
|
||||
@keydown="handleKeydown"
|
||||
>
|
||||
<svg v-if="!copied" class="icon copy-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"/>
|
||||
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
|
||||
</svg>
|
||||
<svg v-else class="icon check-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="20 6 9 17 4 12"/>
|
||||
</svg>
|
||||
<span v-if="copied" class="copy-feedback">Copied!</span>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.copy-code-button {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
right: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 12px;
|
||||
border: 1px solid var(--vp-c-border);
|
||||
border-radius: var(--vp-radius-md);
|
||||
background: var(--vp-c-bg);
|
||||
color: var(--vp-c-text-2);
|
||||
font-size: var(--vp-font-size-sm);
|
||||
cursor: pointer;
|
||||
opacity: 0;
|
||||
transition: all var(--vp-transition-color);
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.copy-code-button:hover {
|
||||
background: var(--vp-c-bg-soft);
|
||||
color: var(--vp-c-text-1);
|
||||
border-color: var(--vp-c-primary);
|
||||
}
|
||||
|
||||
.copy-code-button.copied {
|
||||
background: var(--vp-c-secondary-500);
|
||||
color: white;
|
||||
border-color: var(--vp-c-secondary-500);
|
||||
}
|
||||
|
||||
.copy-code-button .icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.copy-feedback {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
right: 0;
|
||||
margin-top: 4px;
|
||||
padding: 4px 8px;
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-border);
|
||||
border-radius: var(--vp-radius-md);
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
animation: fadeIn 0.2s ease;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Show button on code block hover */
|
||||
div[class*='language-']:hover .copy-code-button,
|
||||
.copy-code-button:focus {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.copy-code-button {
|
||||
opacity: 1;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.copy-feedback {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
133
docs/.vitepress/theme/components/DarkModeToggle.vue
Normal file
133
docs/.vitepress/theme/components/DarkModeToggle.vue
Normal file
@@ -0,0 +1,133 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
|
||||
type ColorMode = 'light' | 'dark' | 'auto'
|
||||
|
||||
const colorMode = ref<ColorMode>('auto')
|
||||
|
||||
const modes: { id: ColorMode; name: string; icon: string }[] = [
|
||||
{ id: 'light', name: 'Light', icon: 'sun' },
|
||||
{ id: 'dark', name: 'Dark', icon: 'moon' },
|
||||
{ id: 'auto', name: 'Auto', icon: 'computer' }
|
||||
]
|
||||
|
||||
const setMode = (mode: ColorMode) => {
|
||||
colorMode.value = mode
|
||||
localStorage.setItem('ccw-color-mode', mode)
|
||||
applyMode(mode)
|
||||
}
|
||||
|
||||
const applyMode = (mode: ColorMode) => {
|
||||
const html = document.documentElement
|
||||
|
||||
if (mode === 'auto') {
|
||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
html.classList.toggle('dark', prefersDark)
|
||||
} else {
|
||||
html.classList.toggle('dark', mode === 'dark')
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const savedMode = localStorage.getItem('ccw-color-mode') as ColorMode
|
||||
if (savedMode && modes.find(m => m.id === savedMode)) {
|
||||
setMode(savedMode)
|
||||
} else {
|
||||
setMode('auto')
|
||||
}
|
||||
|
||||
// Listen for system theme changes
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
|
||||
if (colorMode.value === 'auto') {
|
||||
applyMode('auto')
|
||||
}
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="dark-mode-toggle">
|
||||
<button
|
||||
v-for="mode in modes"
|
||||
:key="mode.id"
|
||||
:class="['mode-button', { active: colorMode === mode.id }]"
|
||||
:aria-label="`Switch to ${mode.name} mode`"
|
||||
:title="mode.name"
|
||||
@click="setMode(mode.id)"
|
||||
>
|
||||
<svg v-if="mode.icon === 'sun'" class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="5"/>
|
||||
<line x1="12" y1="1" x2="12" y2="3"/>
|
||||
<line x1="12" y1="21" x2="12" y2="23"/>
|
||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/>
|
||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/>
|
||||
<line x1="1" y1="12" x2="3" y2="12"/>
|
||||
<line x1="21" y1="12" x2="23" y2="12"/>
|
||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/>
|
||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/>
|
||||
</svg>
|
||||
<svg v-else-if="mode.icon === 'moon'" class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
|
||||
</svg>
|
||||
<svg v-else class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<rect x="2" y="3" width="20" height="14" rx="2" ry="2"/>
|
||||
<line x1="8" y1="21" x2="16" y2="21"/>
|
||||
<line x1="12" y1="17" x2="12" y2="21"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.dark-mode-toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
padding: 4px;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: var(--vp-radius-full);
|
||||
}
|
||||
|
||||
.mode-button {
|
||||
position: relative;
|
||||
width: 36px;
|
||||
height: 32px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
border-radius: var(--vp-radius-md);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--vp-c-text-2);
|
||||
transition: all var(--vp-transition-color);
|
||||
}
|
||||
|
||||
.mode-button:hover {
|
||||
color: var(--vp-c-text-1);
|
||||
background: var(--vp-c-bg-mute);
|
||||
}
|
||||
|
||||
.mode-button.active {
|
||||
background: var(--vp-c-bg);
|
||||
color: var(--vp-c-primary);
|
||||
box-shadow: var(--vp-shadow-sm);
|
||||
}
|
||||
|
||||
.mode-button .icon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.mode-button {
|
||||
width: 40px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.mode-button .icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
492
docs/.vitepress/theme/components/DocSearch.vue
Normal file
492
docs/.vitepress/theme/components/DocSearch.vue
Normal file
@@ -0,0 +1,492 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue'
|
||||
import { useData, useRouter, withBase } from 'vitepress'
|
||||
|
||||
type LocaleKey = 'root' | 'zh'
|
||||
|
||||
interface SearchDoc {
|
||||
id: number
|
||||
title: string
|
||||
url: string
|
||||
excerpt?: string
|
||||
}
|
||||
|
||||
interface SearchIndexPayload {
|
||||
version: number
|
||||
locale: LocaleKey
|
||||
index: Record<string, string>
|
||||
docs: SearchDoc[]
|
||||
}
|
||||
|
||||
const { page } = useData()
|
||||
const router = useRouter()
|
||||
|
||||
const localeKey = computed<LocaleKey>(() =>
|
||||
page.value.relativePath.startsWith('zh/') ? 'zh' : 'root'
|
||||
)
|
||||
|
||||
const isOpen = ref(false)
|
||||
const isLoading = ref(false)
|
||||
const error = ref<string | null>(null)
|
||||
|
||||
const query = ref('')
|
||||
const results = ref<SearchDoc[]>([])
|
||||
const activeIndex = ref(0)
|
||||
|
||||
const inputRef = ref<HTMLInputElement | null>(null)
|
||||
const buttonRef = ref<HTMLButtonElement | null>(null)
|
||||
|
||||
const modifierKey = ref('Ctrl')
|
||||
|
||||
const loadedIndex = shallowRef<any | null>(null)
|
||||
const loadedDocsById = shallowRef<Map<number, SearchDoc> | null>(null)
|
||||
const cache = new Map<LocaleKey, { index: any; docsById: Map<number, SearchDoc> }>()
|
||||
|
||||
const placeholder = computed(() => (localeKey.value === 'zh' ? '搜索文档' : 'Search docs'))
|
||||
const cancelText = computed(() => (localeKey.value === 'zh' ? '关闭' : 'Close'))
|
||||
const loadingText = computed(() => (localeKey.value === 'zh' ? '正在加载索引…' : 'Loading index…'))
|
||||
const hintText = computed(() => (localeKey.value === 'zh' ? '输入关键词开始搜索' : 'Type to start searching'))
|
||||
const noResultsText = computed(() => (localeKey.value === 'zh' ? '未找到结果' : 'No results'))
|
||||
|
||||
function isEditableTarget(target: EventTarget | null) {
|
||||
const el = target as HTMLElement | null
|
||||
if (!el) return false
|
||||
const tag = el.tagName?.toLowerCase()
|
||||
if (!tag) return false
|
||||
return tag === 'input' || tag === 'textarea' || tag === 'select' || el.isContentEditable
|
||||
}
|
||||
|
||||
async function loadLocaleIndex(key: LocaleKey) {
|
||||
const cached = cache.get(key)
|
||||
if (cached) {
|
||||
loadedIndex.value = cached.index
|
||||
loadedDocsById.value = cached.docsById
|
||||
return
|
||||
}
|
||||
|
||||
isLoading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
const res = await fetch(withBase(`/search-index.${key}.json`))
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`)
|
||||
const payload = (await res.json()) as SearchIndexPayload
|
||||
|
||||
if (!payload || payload.locale !== key) throw new Error('Invalid index payload')
|
||||
|
||||
const [{ default: FlexSearch }, { createFlexSearchIndex }] = await Promise.all([
|
||||
import('flexsearch'),
|
||||
import('../../search/flexsearch.mjs')
|
||||
])
|
||||
|
||||
const index = createFlexSearchIndex(FlexSearch)
|
||||
await Promise.all(Object.entries(payload.index).map(([k, v]) => index.import(k, v)))
|
||||
|
||||
const docsById = new Map<number, SearchDoc>()
|
||||
for (const doc of payload.docs) docsById.set(doc.id, doc)
|
||||
|
||||
cache.set(key, { index, docsById })
|
||||
loadedIndex.value = index
|
||||
loadedDocsById.value = docsById
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : String(e)
|
||||
loadedIndex.value = null
|
||||
loadedDocsById.value = null
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function ensureReady() {
|
||||
await loadLocaleIndex(localeKey.value)
|
||||
}
|
||||
|
||||
let searchTimer: number | undefined
|
||||
async function runSearch() {
|
||||
const q = query.value.trim()
|
||||
if (!q) {
|
||||
results.value = []
|
||||
activeIndex.value = 0
|
||||
return
|
||||
}
|
||||
|
||||
await ensureReady()
|
||||
if (!loadedIndex.value || !loadedDocsById.value) {
|
||||
results.value = []
|
||||
activeIndex.value = 0
|
||||
return
|
||||
}
|
||||
|
||||
const ids = loadedIndex.value.search(q, 12) as number[]
|
||||
const docsById = loadedDocsById.value
|
||||
results.value = ids
|
||||
.map((id) => docsById.get(id))
|
||||
.filter((d): d is SearchDoc => Boolean(d))
|
||||
activeIndex.value = 0
|
||||
}
|
||||
|
||||
function navigate(url: string) {
|
||||
router.go(withBase(url))
|
||||
close()
|
||||
}
|
||||
|
||||
async function open() {
|
||||
isOpen.value = true
|
||||
document.body.style.overflow = 'hidden'
|
||||
await ensureReady()
|
||||
await nextTick()
|
||||
inputRef.value?.focus()
|
||||
}
|
||||
|
||||
function close() {
|
||||
isOpen.value = false
|
||||
query.value = ''
|
||||
results.value = []
|
||||
activeIndex.value = 0
|
||||
error.value = null
|
||||
document.body.style.overflow = ''
|
||||
buttonRef.value?.focus()
|
||||
}
|
||||
|
||||
function onInputKeydown(e: KeyboardEvent) {
|
||||
if (e.key === 'Escape') {
|
||||
e.preventDefault()
|
||||
close()
|
||||
return
|
||||
}
|
||||
|
||||
if (e.key === 'ArrowDown') {
|
||||
e.preventDefault()
|
||||
if (results.value.length > 0) {
|
||||
activeIndex.value = (activeIndex.value + 1) % results.value.length
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (e.key === 'ArrowUp') {
|
||||
e.preventDefault()
|
||||
if (results.value.length > 0) {
|
||||
activeIndex.value =
|
||||
(activeIndex.value - 1 + results.value.length) % results.value.length
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (e.key === 'Enter') {
|
||||
const hit = results.value[activeIndex.value]
|
||||
if (hit) {
|
||||
e.preventDefault()
|
||||
navigate(hit.url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let onGlobalKeydown: ((e: KeyboardEvent) => void) | null = null
|
||||
|
||||
onMounted(() => {
|
||||
modifierKey.value = /mac/i.test(navigator.platform) ? '⌘' : 'Ctrl'
|
||||
|
||||
onGlobalKeydown = (e: KeyboardEvent) => {
|
||||
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'k') {
|
||||
e.preventDefault()
|
||||
if (!isOpen.value) open()
|
||||
return
|
||||
}
|
||||
|
||||
if (e.key === '/' && !e.ctrlKey && !e.metaKey && !e.altKey) {
|
||||
if (isEditableTarget(e.target)) return
|
||||
e.preventDefault()
|
||||
if (!isOpen.value) open()
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('keydown', onGlobalKeydown)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (onGlobalKeydown) window.removeEventListener('keydown', onGlobalKeydown)
|
||||
})
|
||||
|
||||
watch(query, () => {
|
||||
if (searchTimer) window.clearTimeout(searchTimer)
|
||||
searchTimer = window.setTimeout(() => {
|
||||
runSearch()
|
||||
}, 60)
|
||||
})
|
||||
|
||||
watch(
|
||||
() => page.value.relativePath,
|
||||
() => {
|
||||
if (isOpen.value) close()
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="DocSearch">
|
||||
<button
|
||||
ref="buttonRef"
|
||||
type="button"
|
||||
class="DocSearch-Button"
|
||||
:aria-label="placeholder"
|
||||
@click="open"
|
||||
>
|
||||
<svg class="DocSearch-Button-Icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="11" cy="11" r="7" />
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65" />
|
||||
</svg>
|
||||
<span class="DocSearch-Button-Placeholder">{{ placeholder }}</span>
|
||||
<span class="DocSearch-Button-Keys" aria-hidden="true">
|
||||
<kbd class="DocSearch-Button-Key">{{ modifierKey }}</kbd>
|
||||
<kbd class="DocSearch-Button-Key">K</kbd>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<Teleport to="body">
|
||||
<div v-if="isOpen" class="DocSearch-Modal">
|
||||
<div class="DocSearch-Overlay" @click="close" />
|
||||
|
||||
<div class="DocSearch-Container" role="dialog" aria-modal="true">
|
||||
<div class="DocSearch-SearchBar">
|
||||
<svg class="DocSearch-SearchIcon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="11" cy="11" r="7" />
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65" />
|
||||
</svg>
|
||||
<input
|
||||
ref="inputRef"
|
||||
v-model="query"
|
||||
class="DocSearch-Input"
|
||||
type="search"
|
||||
:placeholder="placeholder"
|
||||
:aria-label="placeholder"
|
||||
@keydown="onInputKeydown"
|
||||
/>
|
||||
<button type="button" class="DocSearch-Cancel" @click="close">{{ cancelText }}</button>
|
||||
</div>
|
||||
|
||||
<div class="DocSearch-Body">
|
||||
<div v-if="isLoading" class="DocSearch-Status">{{ loadingText }}</div>
|
||||
<div v-else-if="error" class="DocSearch-Status DocSearch-Status--error">{{ error }}</div>
|
||||
<div v-else-if="query.trim().length === 0" class="DocSearch-Status">{{ hintText }}</div>
|
||||
<div v-else>
|
||||
<ul v-if="results.length > 0" class="DocSearch-Results">
|
||||
<li
|
||||
v-for="(item, i) in results"
|
||||
:key="item.id"
|
||||
:class="['DocSearch-Result', { active: i === activeIndex }]"
|
||||
@mousemove="activeIndex = i"
|
||||
>
|
||||
<a
|
||||
class="DocSearch-Result-Link"
|
||||
:href="withBase(item.url)"
|
||||
@click.prevent="navigate(item.url)"
|
||||
>
|
||||
<div class="DocSearch-Result-Title">{{ item.title }}</div>
|
||||
<div v-if="item.excerpt" class="DocSearch-Result-Excerpt">
|
||||
{{ item.excerpt }}
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div v-else class="DocSearch-Status">{{ noResultsText }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Teleport>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.DocSearch {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.DocSearch-Button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
height: 36px;
|
||||
padding: 0 12px;
|
||||
border: 1px solid var(--vp-c-border);
|
||||
border-radius: var(--vp-radius-full);
|
||||
background: var(--vp-c-bg-soft);
|
||||
color: var(--vp-c-text-2);
|
||||
cursor: pointer;
|
||||
transition: all var(--vp-transition-color);
|
||||
}
|
||||
|
||||
.DocSearch-Button:hover {
|
||||
border-color: var(--vp-c-primary);
|
||||
color: var(--vp-c-text-1);
|
||||
background: var(--vp-c-bg);
|
||||
}
|
||||
|
||||
.DocSearch-Button-Icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.DocSearch-Button-Placeholder {
|
||||
font-size: var(--vp-font-size-sm);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.DocSearch-Button-Keys {
|
||||
display: inline-flex;
|
||||
gap: 4px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.DocSearch-Button-Key {
|
||||
font-family: var(--vp-font-family-mono);
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
padding: 4px 6px;
|
||||
border-radius: var(--vp-radius-sm);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg);
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.DocSearch-Modal {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: var(--vp-z-index-modal);
|
||||
}
|
||||
|
||||
.DocSearch-Overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.DocSearch-Container {
|
||||
position: relative;
|
||||
width: min(720px, calc(100vw - 32px));
|
||||
margin: 10vh auto 0;
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: var(--vp-radius-xl);
|
||||
box-shadow: var(--vp-shadow-xl);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.DocSearch-SearchBar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 12px 14px;
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg-soft);
|
||||
}
|
||||
|
||||
.DocSearch-SearchIcon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
color: var(--vp-c-text-3);
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.DocSearch-Input {
|
||||
flex: 1;
|
||||
height: 40px;
|
||||
padding: 0 12px;
|
||||
border: 1px solid var(--vp-c-border);
|
||||
border-radius: var(--vp-radius-md);
|
||||
background: var(--vp-c-bg);
|
||||
color: var(--vp-c-text-1);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.DocSearch-Input:focus-visible {
|
||||
outline: 2px solid var(--vp-c-primary);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.DocSearch-Cancel {
|
||||
height: 40px;
|
||||
padding: 0 12px;
|
||||
border: 1px solid var(--vp-c-border);
|
||||
border-radius: var(--vp-radius-md);
|
||||
background: var(--vp-c-bg);
|
||||
color: var(--vp-c-text-2);
|
||||
cursor: pointer;
|
||||
transition: all var(--vp-transition-color);
|
||||
}
|
||||
|
||||
.DocSearch-Cancel:hover {
|
||||
border-color: var(--vp-c-primary);
|
||||
color: var(--vp-c-text-1);
|
||||
background: var(--vp-c-bg-soft);
|
||||
}
|
||||
|
||||
.DocSearch-Body {
|
||||
max-height: 60vh;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.DocSearch-Status {
|
||||
padding: 18px 16px;
|
||||
color: var(--vp-c-text-2);
|
||||
font-size: var(--vp-font-size-sm);
|
||||
}
|
||||
|
||||
.DocSearch-Status--error {
|
||||
color: #ef4444;
|
||||
}
|
||||
|
||||
.DocSearch-Results {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.DocSearch-Result {
|
||||
border-radius: var(--vp-radius-lg);
|
||||
transition: background var(--vp-transition-color);
|
||||
}
|
||||
|
||||
.DocSearch-Result.active,
|
||||
.DocSearch-Result:hover {
|
||||
background: var(--vp-c-bg-soft);
|
||||
}
|
||||
|
||||
.DocSearch-Result-Link {
|
||||
display: block;
|
||||
padding: 12px 12px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.DocSearch-Result-Title {
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-1);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.DocSearch-Result-Excerpt {
|
||||
margin-top: 4px;
|
||||
color: var(--vp-c-text-3);
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.DocSearch-Button-Placeholder,
|
||||
.DocSearch-Button-Keys {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.DocSearch-Button {
|
||||
width: 40px;
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.DocSearch-Container {
|
||||
margin-top: 6vh;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
214
docs/.vitepress/theme/components/HeroAnimation.vue
Normal file
214
docs/.vitepress/theme/components/HeroAnimation.vue
Normal file
@@ -0,0 +1,214 @@
|
||||
<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">
|
||||
<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>
|
||||
</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>
|
||||
</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" />
|
||||
</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>
|
||||
</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" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
|
||||
const isVisible = ref(false)
|
||||
|
||||
onMounted(() => {
|
||||
setTimeout(() => {
|
||||
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 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'
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.hero-animation-container {
|
||||
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);
|
||||
}
|
||||
|
||||
.hero-animation-container.is-visible {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.orbit-ring {
|
||||
fill: none;
|
||||
stroke: var(--vp-c-brand-1);
|
||||
stroke-width: 0.5;
|
||||
opacity: 0.1;
|
||||
stroke-dasharray: 4 4;
|
||||
}
|
||||
|
||||
.ring-outer { animation: rotate 60s linear infinite; transform-origin: 200px 160px; }
|
||||
.ring-inner { animation: rotate 40s linear infinite reverse; transform-origin: 200px 160px; }
|
||||
|
||||
.connection-path {
|
||||
fill: none;
|
||||
stroke: var(--vp-c-brand-1);
|
||||
stroke-width: 0.8;
|
||||
opacity: 0.05;
|
||||
}
|
||||
|
||||
.data-pulse {
|
||||
fill: var(--vp-c-brand-2);
|
||||
filter: drop-shadow(0 0 4px var(--vp-c-brand-2));
|
||||
}
|
||||
|
||||
.agent-group {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.agent-circle {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.agent-halo {
|
||||
opacity: 0.2;
|
||||
animation: agent-pulse 2s ease-in-out infinite;
|
||||
transform-origin: center;
|
||||
}
|
||||
|
||||
.agent-label {
|
||||
font-size: 10px;
|
||||
fill: var(--vp-c-text-2);
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.core-bg {
|
||||
fill: var(--vp-c-bg-soft);
|
||||
stroke: var(--vp-c-brand-soft);
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.core-inner {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.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>
|
||||
184
docs/.vitepress/theme/components/PageToc.vue
Normal file
184
docs/.vitepress/theme/components/PageToc.vue
Normal file
@@ -0,0 +1,184 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useData } from 'vitepress'
|
||||
|
||||
const { page } = useData()
|
||||
|
||||
interface TocItem {
|
||||
id: string
|
||||
text: string
|
||||
level: number
|
||||
children?: TocItem[]
|
||||
}
|
||||
|
||||
const toc = computed<TocItem[]>(() => {
|
||||
return page.value.headers || []
|
||||
})
|
||||
|
||||
const activeId = ref('')
|
||||
|
||||
const onItemClick = (id: string) => {
|
||||
activeId.value = id
|
||||
const element = document.getElementById(id)
|
||||
if (element) {
|
||||
element.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// Update active heading on scroll
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
activeId.value = entry.target.id
|
||||
}
|
||||
})
|
||||
},
|
||||
{ rootMargin: '-80px 0px -80% 0px' }
|
||||
)
|
||||
|
||||
page.value.headers.forEach((header) => {
|
||||
const element = document.getElementById(header.id)
|
||||
if (element) {
|
||||
observer.observe(element)
|
||||
}
|
||||
})
|
||||
|
||||
return () => observer.disconnect()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<nav v-if="toc.length > 0" class="page-toc" aria-label="Page navigation">
|
||||
<div class="toc-header">On this page</div>
|
||||
<ul class="toc-list">
|
||||
<li
|
||||
v-for="item in toc"
|
||||
:key="item.id"
|
||||
:class="['toc-item', `toc-level-${item.level}`, { active: item.id === activeId }]"
|
||||
>
|
||||
<a
|
||||
:href="`#${item.id}`"
|
||||
class="toc-link"
|
||||
@click.prevent="onItemClick(item.id)"
|
||||
>
|
||||
{{ item.text }}
|
||||
</a>
|
||||
<ul v-if="item.children && item.children.length > 0" class="toc-list toc-sublist">
|
||||
<li
|
||||
v-for="child in item.children"
|
||||
:key="child.id"
|
||||
:class="['toc-item', `toc-level-${child.level}`, { active: child.id === activeId }]"
|
||||
>
|
||||
<a
|
||||
:href="`#${child.id}`"
|
||||
class="toc-link"
|
||||
@click.prevent="onItemClick(child.id)"
|
||||
>
|
||||
{{ child.text }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.page-toc {
|
||||
position: sticky;
|
||||
top: calc(var(--vp-nav-height) + 24px);
|
||||
max-height: calc(100vh - var(--vp-nav-height) - 48px);
|
||||
overflow-y: auto;
|
||||
padding: 16px;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: var(--vp-radius-lg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.toc-header {
|
||||
font-size: var(--vp-font-size-sm);
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-1);
|
||||
margin-bottom: 12px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.toc-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.toc-sublist {
|
||||
margin-left: 12px;
|
||||
padding-left: 12px;
|
||||
border-left: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.toc-item {
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.toc-link {
|
||||
display: block;
|
||||
padding: 4px 8px;
|
||||
color: var(--vp-c-text-2);
|
||||
font-size: var(--vp-font-size-sm);
|
||||
text-decoration: none;
|
||||
border-left: 2px solid transparent;
|
||||
transition: all var(--vp-transition-color);
|
||||
border-radius: 0 var(--vp-radius-sm) var(--vp-radius-sm) 0;
|
||||
}
|
||||
|
||||
.toc-link:hover {
|
||||
color: var(--vp-c-primary);
|
||||
background: var(--vp-c-bg-soft);
|
||||
}
|
||||
|
||||
.toc-item.active > .toc-link {
|
||||
color: var(--vp-c-primary);
|
||||
border-left-color: var(--vp-c-primary);
|
||||
background: var(--vp-c-bg-mute);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.toc-level-3 .toc-link {
|
||||
font-size: 13px;
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
||||
.toc-level-4 .toc-link {
|
||||
font-size: 12px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
/* Hide on mobile */
|
||||
@media (max-width: 1024px) {
|
||||
.page-toc {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Scrollbar styling for TOC */
|
||||
.page-toc::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
.page-toc::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.page-toc::-webkit-scrollbar-thumb {
|
||||
background: var(--vp-c-divider);
|
||||
border-radius: var(--vp-radius-full);
|
||||
}
|
||||
|
||||
.page-toc::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--vp-c-text-3);
|
||||
}
|
||||
</style>
|
||||
1090
docs/.vitepress/theme/components/ProfessionalHome.vue
Normal file
1090
docs/.vitepress/theme/components/ProfessionalHome.vue
Normal file
File diff suppressed because it is too large
Load Diff
42
docs/.vitepress/theme/components/SkipLink.vue
Normal file
42
docs/.vitepress/theme/components/SkipLink.vue
Normal file
@@ -0,0 +1,42 @@
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* Skip Link Component
|
||||
* Accessibility feature allowing keyboard users to skip to main content
|
||||
*/
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a href="#VPContent" class="skip-link">
|
||||
Skip to main content
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.skip-link {
|
||||
position: absolute;
|
||||
top: -100px;
|
||||
left: 0;
|
||||
padding: 8px 16px;
|
||||
background: var(--vp-c-primary);
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
z-index: 9999;
|
||||
transition: top 0.3s ease;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.skip-link:focus {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.skip-link:hover {
|
||||
background: var(--vp-c-primary-600);
|
||||
}
|
||||
|
||||
@media print {
|
||||
.skip-link {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
108
docs/.vitepress/theme/components/ThemeSwitcher.vue
Normal file
108
docs/.vitepress/theme/components/ThemeSwitcher.vue
Normal file
@@ -0,0 +1,108 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
|
||||
const currentTheme = ref<'blue' | 'green' | 'orange' | 'purple'>('blue')
|
||||
|
||||
const themes = [
|
||||
{ id: 'blue', name: 'Blue', color: '#3b82f6' },
|
||||
{ id: 'green', name: 'Green', color: '#10b981' },
|
||||
{ id: 'orange', name: 'Orange', color: '#f59e0b' },
|
||||
{ id: 'purple', name: 'Purple', color: '#8b5cf6' }
|
||||
]
|
||||
|
||||
const setTheme = (themeId: typeof currentTheme.value) => {
|
||||
currentTheme.value = themeId
|
||||
document.documentElement.setAttribute('data-theme', themeId)
|
||||
localStorage.setItem('ccw-theme', themeId)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const savedTheme = localStorage.getItem('ccw-theme') as typeof currentTheme.value
|
||||
if (savedTheme && themes.find(t => t.id === savedTheme)) {
|
||||
setTheme(savedTheme)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="theme-switcher">
|
||||
<div class="theme-buttons">
|
||||
<button
|
||||
v-for="theme in themes"
|
||||
:key="theme.id"
|
||||
:class="['theme-button', { active: currentTheme === theme.id }]"
|
||||
:style="{ '--theme-color': theme.color }"
|
||||
:aria-label="`Switch to ${theme.name} theme`"
|
||||
:title="theme.name"
|
||||
@click="setTheme(theme.id)"
|
||||
>
|
||||
<span class="theme-dot"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.theme-switcher {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.theme-buttons {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
padding: 4px;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: var(--vp-radius-full);
|
||||
}
|
||||
|
||||
.theme-button {
|
||||
position: relative;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
border-radius: var(--vp-radius-full);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all var(--vp-transition-color);
|
||||
}
|
||||
|
||||
.theme-button:hover {
|
||||
background: var(--vp-c-bg-mute);
|
||||
}
|
||||
|
||||
.theme-button.active {
|
||||
background: var(--vp-c-bg);
|
||||
box-shadow: var(--vp-shadow-sm);
|
||||
}
|
||||
|
||||
.theme-dot {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: var(--vp-radius-full);
|
||||
background: var(--theme-color);
|
||||
border: 2px solid transparent;
|
||||
transition: all var(--vp-transition-color);
|
||||
}
|
||||
|
||||
.theme-button.active .theme-dot {
|
||||
border-color: var(--vp-c-text-1);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.theme-button {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.theme-dot {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
202
docs/.vitepress/theme/components/WorkflowAnimation.vue
Normal file
202
docs/.vitepress/theme/components/WorkflowAnimation.vue
Normal file
@@ -0,0 +1,202 @@
|
||||
<template>
|
||||
<div class="workflow-animation">
|
||||
<div class="workflow-container">
|
||||
<div class="workflow-node coordinator">
|
||||
<div class="node-icon">🎯</div>
|
||||
<div class="node-label">Coordinator</div>
|
||||
</div>
|
||||
|
||||
<div class="workflow-paths">
|
||||
<svg class="path-svg" viewBox="0 0 400 200">
|
||||
<!-- Spec Path -->
|
||||
<path class="flow-path path-spec" d="M50,100 Q150,20 250,50" fill="none" stroke="#3B82F6" stroke-width="2"/>
|
||||
<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">
|
||||
<animateMotion dur="3.5s" repeatCount="indefinite" path="M50,100 Q150,180 250,150"/>
|
||||
</circle>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div class="workflow-nodes">
|
||||
<div class="workflow-node analyst">
|
||||
<div class="node-icon">📊</div>
|
||||
<div class="node-label">Analyst</div>
|
||||
</div>
|
||||
<div class="workflow-node writer">
|
||||
<div class="node-icon">✍️</div>
|
||||
<div class="node-label">Writer</div>
|
||||
</div>
|
||||
<div class="workflow-node executor">
|
||||
<div class="node-icon">⚡</div>
|
||||
<div class="node-label">Executor</div>
|
||||
</div>
|
||||
<div class="workflow-node tester">
|
||||
<div class="node-icon">🧪</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>
|
||||
<div class="legend-item"><span class="dot test"></span> Test Phase</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, onUnmounted } from 'vue'
|
||||
|
||||
onMounted(() => {
|
||||
// Add animation class after mount
|
||||
document.querySelector('.workflow-animation')?.classList.add('animate')
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.workflow-animation {
|
||||
padding: 2rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 24px;
|
||||
margin: 2rem 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.workflow-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
flex-wrap: wrap;
|
||||
gap: 2rem;
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
.workflow-paths {
|
||||
flex: 1.2;
|
||||
min-width: 280px;
|
||||
max-width: 450px;
|
||||
}
|
||||
|
||||
.path-svg {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.flow-path {
|
||||
stroke-dasharray: 6, 6;
|
||||
opacity: 0.3;
|
||||
animation: dash 30s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes dash {
|
||||
to {
|
||||
stroke-dashoffset: -120;
|
||||
}
|
||||
}
|
||||
|
||||
.flow-dot {
|
||||
filter: drop-shadow(0 0 4px currentColor);
|
||||
}
|
||||
|
||||
.workflow-nodes {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 1rem;
|
||||
flex: 0.8;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.workflow-node {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 1.25rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 16px;
|
||||
box-shadow: var(--vp-shadow-sm);
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.workflow-node:hover {
|
||||
transform: translateY(-4px);
|
||||
border-color: var(--vp-c-brand-1);
|
||||
box-shadow: var(--vp-shadow-md);
|
||||
}
|
||||
|
||||
.node-icon {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.node-label {
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.workflow-node.coordinator {
|
||||
grid-column: span 2;
|
||||
background: linear-gradient(135deg, var(--vp-c-brand-1), var(--vp-c-brand-2));
|
||||
border: none;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.workflow-node.coordinator .node-label {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.workflow-legend {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 2.5rem;
|
||||
margin-top: 2rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.legend-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.dot.spec { background: var(--vp-c-brand-1); }
|
||||
.dot.impl { background: var(--vp-c-secondary-500); }
|
||||
.dot.test { background: var(--vp-c-accent-400); }
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.workflow-animation {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.workflow-container {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.workflow-nodes {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
25
docs/.vitepress/theme/index.ts
Normal file
25
docs/.vitepress/theme/index.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import DefaultTheme from 'vitepress/theme'
|
||||
import ThemeSwitcher from './components/ThemeSwitcher.vue'
|
||||
import DocSearch from './components/DocSearch.vue'
|
||||
import DarkModeToggle from './components/DarkModeToggle.vue'
|
||||
import CopyCodeButton from './components/CopyCodeButton.vue'
|
||||
import Breadcrumb from './components/Breadcrumb.vue'
|
||||
import PageToc from './components/PageToc.vue'
|
||||
import Layout from './layouts/Layout.vue'
|
||||
import './styles/variables.css'
|
||||
import './styles/custom.css'
|
||||
import './styles/mobile.css'
|
||||
|
||||
export default {
|
||||
extends: DefaultTheme,
|
||||
Layout,
|
||||
enhanceApp({ app, router, siteData }) {
|
||||
// Register global components
|
||||
app.component('ThemeSwitcher', ThemeSwitcher)
|
||||
app.component('DocSearch', DocSearch)
|
||||
app.component('DarkModeToggle', DarkModeToggle)
|
||||
app.component('CopyCodeButton', CopyCodeButton)
|
||||
app.component('Breadcrumb', Breadcrumb)
|
||||
app.component('PageToc', PageToc)
|
||||
}
|
||||
}
|
||||
153
docs/.vitepress/theme/layouts/Layout.vue
Normal file
153
docs/.vitepress/theme/layouts/Layout.vue
Normal file
@@ -0,0 +1,153 @@
|
||||
<script setup lang="ts">
|
||||
import DefaultTheme from 'vitepress/theme'
|
||||
import { onBeforeUnmount, onMounted } from 'vue'
|
||||
|
||||
let mediaQuery: MediaQueryList | null = null
|
||||
let systemThemeChangeHandler: (() => void) | null = null
|
||||
let storageHandler: ((e: StorageEvent) => void) | null = null
|
||||
|
||||
function applyTheme() {
|
||||
const savedTheme = localStorage.getItem('ccw-theme') || 'blue'
|
||||
document.documentElement.setAttribute('data-theme', savedTheme)
|
||||
}
|
||||
|
||||
function applyColorMode() {
|
||||
const mode = localStorage.getItem('ccw-color-mode') || 'auto'
|
||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
const isDark = mode === 'dark' || (mode === 'auto' && prefersDark)
|
||||
document.documentElement.classList.toggle('dark', isDark)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
applyTheme()
|
||||
applyColorMode()
|
||||
|
||||
mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
|
||||
systemThemeChangeHandler = () => {
|
||||
const mode = localStorage.getItem('ccw-color-mode') || 'auto'
|
||||
if (mode === 'auto') applyColorMode()
|
||||
}
|
||||
mediaQuery.addEventListener('change', systemThemeChangeHandler)
|
||||
|
||||
storageHandler = (e: StorageEvent) => {
|
||||
if (e.key === 'ccw-theme') applyTheme()
|
||||
if (e.key === 'ccw-color-mode') applyColorMode()
|
||||
}
|
||||
window.addEventListener('storage', storageHandler)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (mediaQuery && systemThemeChangeHandler) {
|
||||
mediaQuery.removeEventListener('change', systemThemeChangeHandler)
|
||||
}
|
||||
if (storageHandler) window.removeEventListener('storage', storageHandler)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DefaultTheme.Layout>
|
||||
<template #home-hero-after>
|
||||
<div class="hero-extensions">
|
||||
<div class="hero-stats">
|
||||
<div class="stat-item">
|
||||
<div class="stat-value">27+</div>
|
||||
<div class="stat-label">Built-in Skills</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-value">10+</div>
|
||||
<div class="stat-label">Agent Types</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-value">4</div>
|
||||
<div class="stat-label">Workflow Levels</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #layout-top>
|
||||
<a href="#VPContent" class="skip-link">Skip to main content</a>
|
||||
</template>
|
||||
|
||||
<template #nav-bar-content-after>
|
||||
<div class="nav-extensions">
|
||||
<DocSearch />
|
||||
<DarkModeToggle />
|
||||
<ThemeSwitcher />
|
||||
</div>
|
||||
</template>
|
||||
</DefaultTheme.Layout>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.hero-extensions {
|
||||
margin-top: 40px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.hero-stats {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 48px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 32px;
|
||||
font-weight: 700;
|
||||
color: var(--vp-c-primary);
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 14px;
|
||||
color: var(--vp-c-text-2);
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.nav-extensions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-left: auto;
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
||||
.skip-link {
|
||||
position: absolute;
|
||||
top: -100px;
|
||||
left: 0;
|
||||
padding: 8px 16px;
|
||||
background: var(--vp-c-primary);
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
z-index: 9999;
|
||||
transition: top 0.3s;
|
||||
}
|
||||
|
||||
.skip-link:focus {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.hero-stats {
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.nav-extensions {
|
||||
gap: 8px;
|
||||
padding-left: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
352
docs/.vitepress/theme/styles/custom.css
Normal file
352
docs/.vitepress/theme/styles/custom.css
Normal file
@@ -0,0 +1,352 @@
|
||||
/**
|
||||
* VitePress Custom Styles
|
||||
* Overrides and extensions for default VitePress theme
|
||||
* Design System: ui-ux-pro-max — dark-mode-first, developer-focused
|
||||
*/
|
||||
|
||||
/* ============================================
|
||||
* Global Theme Variables
|
||||
* ============================================ */
|
||||
:root {
|
||||
--vp-c-brand: var(--vp-c-primary);
|
||||
--vp-c-brand-light: var(--vp-c-primary-300);
|
||||
--vp-c-brand-lighter: var(--vp-c-primary-200);
|
||||
--vp-c-brand-dark: var(--vp-c-primary-700);
|
||||
--vp-c-brand-darker: var(--vp-c-primary-800);
|
||||
|
||||
--vp-home-hero-name-color: var(--vp-c-primary);
|
||||
--vp-home-hero-name-background: linear-gradient(120deg, var(--vp-c-primary-500) 30%, var(--vp-c-secondary-500));
|
||||
|
||||
--vp-button-brand-bg: var(--vp-c-primary);
|
||||
--vp-button-brand-hover-bg: var(--vp-c-primary-600);
|
||||
--vp-button-brand-active-bg: var(--vp-c-primary-700);
|
||||
|
||||
--vp-custom-block-tip-bg: var(--vp-c-primary-50);
|
||||
--vp-custom-block-tip-border: var(--vp-c-primary-200);
|
||||
--vp-custom-block-tip-text: var(--vp-c-primary-700);
|
||||
|
||||
--vp-custom-block-warning-bg: var(--vp-c-accent-50);
|
||||
--vp-custom-block-warning-border: var(--vp-c-accent-200);
|
||||
--vp-custom-block-warning-text: var(--vp-c-accent-700);
|
||||
|
||||
--vp-custom-block-danger-bg: #fef2f2;
|
||||
--vp-custom-block-danger-border: #fecaca;
|
||||
--vp-custom-block-danger-text: #b91c1c;
|
||||
|
||||
/* Layout Width Adjustments */
|
||||
--vp-layout-max-width: 1600px;
|
||||
--vp-content-width: 1000px;
|
||||
--vp-sidebar-width: 272px;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--vp-custom-block-tip-bg: rgba(59, 130, 246, 0.1);
|
||||
--vp-custom-block-tip-border: rgba(59, 130, 246, 0.3);
|
||||
--vp-custom-block-tip-text: var(--vp-c-primary-300);
|
||||
|
||||
--vp-custom-block-warning-bg: rgba(217, 119, 6, 0.1);
|
||||
--vp-custom-block-warning-border: rgba(217, 119, 6, 0.3);
|
||||
--vp-custom-block-warning-text: var(--vp-c-accent-300);
|
||||
|
||||
--vp-custom-block-danger-bg: rgba(185, 28, 28, 0.1);
|
||||
--vp-custom-block-danger-border: rgba(185, 28, 28, 0.3);
|
||||
--vp-custom-block-danger-text: #fca5a5;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
* Layout Container Adjustments
|
||||
* ============================================ */
|
||||
.VPDoc .content-container {
|
||||
max-width: var(--vp-content-width);
|
||||
padding: 0 32px;
|
||||
}
|
||||
|
||||
/* Adjust sidebar and content layout */
|
||||
.VPDoc {
|
||||
padding-left: var(--vp-sidebar-width);
|
||||
}
|
||||
|
||||
/* Right side outline (TOC) adjustments */
|
||||
.VPDocOutline {
|
||||
padding-left: 24px;
|
||||
}
|
||||
|
||||
.VPDocOutline .outline-link {
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
* Home Page Override
|
||||
* ============================================ */
|
||||
.VPHome {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.VPHomeHero {
|
||||
padding: 80px 24px;
|
||||
background: linear-gradient(180deg, var(--vp-c-bg-soft) 0%, var(--vp-c-bg) 100%);
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
* Documentation Content Typography
|
||||
* ============================================ */
|
||||
.vp-doc h1 {
|
||||
font-weight: 800;
|
||||
letter-spacing: -0.02em;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.vp-doc h2 {
|
||||
font-weight: 700;
|
||||
margin-top: 3rem;
|
||||
padding-top: 2rem;
|
||||
letter-spacing: -0.01em;
|
||||
border-top: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.vp-doc h2:first-of-type {
|
||||
margin-top: 1.5rem;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.vp-doc h3 {
|
||||
font-weight: 600;
|
||||
margin-top: 2.5rem;
|
||||
}
|
||||
|
||||
.vp-doc h4 {
|
||||
font-weight: 600;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.vp-doc p {
|
||||
line-height: 1.8;
|
||||
margin: 1.25rem 0;
|
||||
}
|
||||
|
||||
.vp-doc ul,
|
||||
.vp-doc ol {
|
||||
margin: 1.25rem 0;
|
||||
padding-left: 1.5rem;
|
||||
}
|
||||
|
||||
.vp-doc li {
|
||||
line-height: 1.8;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.vp-doc li + li {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
/* Better spacing for code blocks in lists */
|
||||
.vp-doc li > code {
|
||||
margin: 0 2px;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
* Command Reference Specific Styles
|
||||
* ============================================ */
|
||||
.vp-doc h3[id^="ccw"],
|
||||
.vp-doc h3[id^="workflow"],
|
||||
.vp-doc h3[id^="issue"],
|
||||
.vp-doc h3[id^="cli"],
|
||||
.vp-doc h3[id^="memory"] {
|
||||
scroll-margin-top: 80px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Add subtle separator between command sections */
|
||||
.vp-doc hr {
|
||||
border: none;
|
||||
border-top: 1px solid var(--vp-c-divider);
|
||||
margin: 3rem 0;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
* Custom Container Blocks
|
||||
* ============================================ */
|
||||
.custom-container {
|
||||
margin: 20px 0;
|
||||
padding: 16px 20px;
|
||||
border-radius: 12px;
|
||||
border-left: 4px solid;
|
||||
}
|
||||
|
||||
.custom-container.info {
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-color: var(--vp-c-primary);
|
||||
}
|
||||
|
||||
.custom-container.success {
|
||||
background: var(--vp-c-secondary-50);
|
||||
border-color: var(--vp-c-secondary);
|
||||
}
|
||||
|
||||
.dark .custom-container.success {
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
}
|
||||
|
||||
.custom-container.tip {
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.custom-container.warning {
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.custom-container.danger {
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
* Code Block Improvements
|
||||
* ============================================ */
|
||||
.vp-code-group {
|
||||
margin: 20px 0;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.vp-code-group .tabs {
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.vp-code-group div[class*='language-'] {
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
div[class*='language-'] {
|
||||
border-radius: 12px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
div[class*='language-'] pre {
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
/* Inline code */
|
||||
.vp-doc :not(pre) > code {
|
||||
border-radius: 6px;
|
||||
padding: 2px 6px;
|
||||
font-size: 0.875em;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
* Table Styling
|
||||
* ============================================ */
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
margin: 20px 0;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
table th,
|
||||
table td {
|
||||
padding: 12px 16px;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
table th {
|
||||
background: var(--vp-c-bg-soft);
|
||||
font-weight: 600;
|
||||
font-size: 0.875rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.03em;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
table tr:hover {
|
||||
background: var(--vp-c-bg-soft);
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
* Sidebar Polish
|
||||
* ============================================ */
|
||||
.VPSidebar .group + .group {
|
||||
margin-top: 0.5rem;
|
||||
padding-top: 0.5rem;
|
||||
border-top: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
* Scrollbar Styling
|
||||
* ============================================ */
|
||||
::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--vp-c-surface-2);
|
||||
border-radius: var(--vp-radius-full);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--vp-c-surface-3);
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
* Link Improvements
|
||||
* ============================================ */
|
||||
a {
|
||||
text-decoration: none;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
* Focus States — Accessibility
|
||||
* ============================================ */
|
||||
:focus-visible {
|
||||
outline: 2px solid var(--vp-c-primary);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
* Skip Link — Accessibility
|
||||
* ============================================ */
|
||||
.skip-link {
|
||||
position: absolute;
|
||||
top: -100px;
|
||||
left: 0;
|
||||
background: var(--vp-c-bg);
|
||||
padding: 8px 16px;
|
||||
z-index: 9999;
|
||||
transition: top 0.3s;
|
||||
}
|
||||
|
||||
.skip-link:focus {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
* Print Styles
|
||||
* ============================================ */
|
||||
@media print {
|
||||
.VPNav,
|
||||
.VPSidebar,
|
||||
.skip-link {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.VPContent {
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
}
|
||||
346
docs/.vitepress/theme/styles/mobile.css
Normal file
346
docs/.vitepress/theme/styles/mobile.css
Normal file
@@ -0,0 +1,346 @@
|
||||
/**
|
||||
* Mobile-Responsive Styles
|
||||
* Breakpoints: 320px-768px (mobile), 768px-1024px (tablet), 1024px+ (desktop)
|
||||
* WCAG 2.1 AA compliant
|
||||
*/
|
||||
|
||||
/* ============================================
|
||||
* Mobile First Approach
|
||||
* ============================================ */
|
||||
|
||||
/* Base Mobile Styles (320px+) */
|
||||
@media (max-width: 768px) {
|
||||
/* Typography */
|
||||
:root {
|
||||
--vp-font-size-base: 14px;
|
||||
--vp-content-width: 100%;
|
||||
}
|
||||
|
||||
/* Container */
|
||||
.container {
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
/* Navigation */
|
||||
.VPNav {
|
||||
height: 56px;
|
||||
}
|
||||
|
||||
.VPNavBar {
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
/* Sidebar */
|
||||
.VPSidebar {
|
||||
width: 100%;
|
||||
max-width: 320px;
|
||||
}
|
||||
|
||||
/* Content */
|
||||
.VPContent {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
/* Doc content adjustments */
|
||||
.VPDoc .content-container {
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
/* Hide outline on mobile */
|
||||
.VPDocOutline {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Hero Section */
|
||||
.VPHomeHero {
|
||||
padding: 40px 16px;
|
||||
}
|
||||
|
||||
.VPHomeHero h1 {
|
||||
font-size: 28px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.VPHomeHero p {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* Code Blocks */
|
||||
div[class*='language-'] {
|
||||
margin: 12px -16px;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
div[class*='language-'] pre {
|
||||
padding: 12px 16px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* Tables - make them scrollable */
|
||||
.vp-doc table {
|
||||
display: block;
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
table {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
table th,
|
||||
table td {
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.VPButton {
|
||||
padding: 8px 16px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* Cards */
|
||||
.VPFeature {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
/* Touch-friendly tap targets (min 44x44px per WCAG) */
|
||||
button,
|
||||
a,
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
min-height: 44px;
|
||||
min-width: 44px;
|
||||
}
|
||||
|
||||
/* Search */
|
||||
.DocSearch {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Theme Switcher */
|
||||
.theme-switcher {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
/* Breadcrumbs */
|
||||
.breadcrumb {
|
||||
padding: 8px 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* Table of Contents - hidden on mobile */
|
||||
.page-toc {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Typography adjustments for mobile */
|
||||
.vp-doc h1 {
|
||||
font-size: 1.75rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.vp-doc h2 {
|
||||
font-size: 1.375rem;
|
||||
margin-top: 2rem;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
|
||||
.vp-doc h3 {
|
||||
font-size: 1.125rem;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.vp-doc p {
|
||||
line-height: 1.7;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.vp-doc ul,
|
||||
.vp-doc ol {
|
||||
margin: 1rem 0;
|
||||
padding-left: 1.25rem;
|
||||
}
|
||||
|
||||
.vp-doc li {
|
||||
margin: 0.375rem 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
* Tablet Styles (768px - 1024px)
|
||||
* ============================================ */
|
||||
@media (min-width: 768px) and (max-width: 1024px) {
|
||||
:root {
|
||||
--vp-content-width: 760px;
|
||||
--vp-sidebar-width: 240px;
|
||||
}
|
||||
|
||||
.VPContent {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.VPDoc .content-container {
|
||||
padding: 0 24px;
|
||||
max-width: var(--vp-content-width);
|
||||
}
|
||||
|
||||
.VPHomeHero {
|
||||
padding: 60px 24px;
|
||||
}
|
||||
|
||||
.VPHomeHero h1 {
|
||||
font-size: 36px;
|
||||
}
|
||||
|
||||
div[class*='language-'] {
|
||||
margin: 12px 0;
|
||||
}
|
||||
|
||||
/* Outline visible but narrower */
|
||||
.VPDocOutline {
|
||||
width: 200px;
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
||||
.VPDocOutline .outline-link {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
* Desktop Styles (1024px+)
|
||||
* ============================================ */
|
||||
@media (min-width: 1024px) {
|
||||
:root {
|
||||
--vp-layout-max-width: 1600px;
|
||||
--vp-content-width: 960px;
|
||||
--vp-sidebar-width: 272px;
|
||||
}
|
||||
|
||||
.VPContent {
|
||||
padding: 32px 48px;
|
||||
max-width: var(--vp-layout-max-width);
|
||||
}
|
||||
|
||||
.VPDoc .content-container {
|
||||
max-width: var(--vp-content-width);
|
||||
padding: 0 40px;
|
||||
}
|
||||
|
||||
/* Outline - sticky on desktop with good width */
|
||||
.VPDocOutline {
|
||||
position: sticky;
|
||||
top: calc(var(--vp-nav-height) + 24px);
|
||||
width: 256px;
|
||||
padding-left: 24px;
|
||||
max-height: calc(100vh - var(--vp-nav-height) - 48px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.VPDocOutline .outline-marker {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.VPDocOutline .outline-link {
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
padding: 4px 12px;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.VPDocOutline .outline-link:hover {
|
||||
color: var(--vp-c-primary);
|
||||
}
|
||||
|
||||
/* Two-column layout for content + TOC */
|
||||
.content-with-toc {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 280px;
|
||||
gap: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
* Large Desktop (1440px+)
|
||||
* ============================================ */
|
||||
@media (min-width: 1440px) {
|
||||
:root {
|
||||
--vp-content-width: 1040px;
|
||||
--vp-sidebar-width: 280px;
|
||||
}
|
||||
|
||||
.VPDoc .content-container {
|
||||
padding: 0 48px;
|
||||
}
|
||||
|
||||
.VPDocOutline {
|
||||
width: 280px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
* Landscape Orientation
|
||||
* ============================================ */
|
||||
@media (max-height: 500px) and (orientation: landscape) {
|
||||
.VPNav {
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.VPHomeHero {
|
||||
padding: 20px 16px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
* High DPI Displays
|
||||
* ============================================ */
|
||||
@media (-webkit-min-device-pixel-ratio: 2),
|
||||
(min-resolution: 192dpi) {
|
||||
/* Optimize images for retina displays */
|
||||
img {
|
||||
image-rendering: -webkit-optimize-contrast;
|
||||
image-rendering: crisp-edges;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
* Reduced Motion (Accessibility)
|
||||
* ============================================ */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
* Dark Mode Specific
|
||||
* ============================================ */
|
||||
@media (max-width: 768px) {
|
||||
.dark {
|
||||
--vp-c-bg: #0f172a;
|
||||
--vp-c-text-1: #f1f5f9;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
* Print Styles for Mobile
|
||||
* ============================================ */
|
||||
@media print and (max-width: 768px) {
|
||||
.VPContent {
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 14pt;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 12pt;
|
||||
}
|
||||
}
|
||||
221
docs/.vitepress/theme/styles/variables.css
Normal file
221
docs/.vitepress/theme/styles/variables.css
Normal file
@@ -0,0 +1,221 @@
|
||||
/**
|
||||
* Design Tokens for CCW Documentation
|
||||
* Based on ui-ux-pro-max design system
|
||||
* 8 themes: 4 colors × 2 modes (light/dark)
|
||||
*/
|
||||
|
||||
:root {
|
||||
/* ============================================
|
||||
* Color Scheme: Blue (Default)
|
||||
* ============================================ */
|
||||
|
||||
/* Primary Colors */
|
||||
--vp-c-primary-50: #eff6ff;
|
||||
--vp-c-primary-100: #dbeafe;
|
||||
--vp-c-primary-200: #bfdbfe;
|
||||
--vp-c-primary-300: #93c5fd;
|
||||
--vp-c-primary-400: #60a5fa;
|
||||
--vp-c-primary-500: #3b82f6;
|
||||
--vp-c-primary-600: #2563eb;
|
||||
--vp-c-primary-700: #1d4ed8;
|
||||
--vp-c-primary-800: #1e40af;
|
||||
--vp-c-primary-900: #1e3a8a;
|
||||
--vp-c-primary: var(--vp-c-primary-500);
|
||||
--vp-c-brand-1: var(--vp-c-primary-500);
|
||||
--vp-c-brand-2: var(--vp-c-secondary-500);
|
||||
--vp-c-brand-soft: rgba(59, 130, 246, 0.1);
|
||||
|
||||
/* Secondary Colors (Green) */
|
||||
--vp-c-secondary-50: #ecfdf5;
|
||||
--vp-c-secondary-100: #d1fae5;
|
||||
--vp-c-secondary-200: #a7f3d0;
|
||||
--vp-c-secondary-300: #6ee7b7;
|
||||
--vp-c-secondary-400: #34d399;
|
||||
--vp-c-secondary-500: #10b981;
|
||||
--vp-c-secondary-600: #059669;
|
||||
--vp-c-secondary-700: #047857;
|
||||
--vp-c-secondary-800: #065f46;
|
||||
--vp-c-secondary-900: #064e3b;
|
||||
--vp-c-secondary: var(--vp-c-secondary-500);
|
||||
|
||||
/* Accent Colors */
|
||||
--vp-c-accent-50: #fef3c7;
|
||||
--vp-c-accent-100: #fde68a;
|
||||
--vp-c-accent-200: #fcd34d;
|
||||
--vp-c-accent-300: #fbbf24;
|
||||
--vp-c-accent-400: #f59e0b;
|
||||
--vp-c-accent-500: #d97706;
|
||||
--vp-c-accent: var(--vp-c-accent-400);
|
||||
|
||||
/* Background Colors (Light Mode) */
|
||||
--vp-c-bg: #ffffff;
|
||||
--vp-c-bg-soft: #f9fafb;
|
||||
--vp-c-bg-mute: #f3f4f6;
|
||||
--vp-c-bg-alt: #ffffff;
|
||||
|
||||
/* Surface Colors */
|
||||
--vp-c-surface: #f9fafb;
|
||||
--vp-c-surface-1: #f3f4f6;
|
||||
--vp-c-surface-2: #e5e7eb;
|
||||
--vp-c-surface-3: #d1d5db;
|
||||
|
||||
/* Border Colors */
|
||||
--vp-c-border: #e5e7eb;
|
||||
--vp-c-border-soft: #f3f4f6;
|
||||
--vp-c-divider: #e5e7eb;
|
||||
|
||||
/* Text Colors */
|
||||
--vp-c-text-1: #111827;
|
||||
--vp-c-text-2: #374151;
|
||||
--vp-c-text-3: #6b7280;
|
||||
--vp-c-text-4: #9ca3af;
|
||||
--vp-c-text-code: #ef4444;
|
||||
|
||||
/* Typography */
|
||||
--vp-font-family-base: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
|
||||
--vp-font-family-mono: 'Fira Code', 'Cascadia Code', 'JetBrains Mono', Consolas, 'Courier New', monospace;
|
||||
|
||||
/* Font Sizes */
|
||||
--vp-font-size-base: 16px;
|
||||
--vp-font-size-sm: 14px;
|
||||
--vp-font-size-lg: 18px;
|
||||
--vp-font-size-xl: 20px;
|
||||
|
||||
/* Spacing */
|
||||
--vp-spacing-xs: 0.25rem; /* 4px */
|
||||
--vp-spacing-sm: 0.5rem; /* 8px */
|
||||
--vp-spacing-md: 1rem; /* 16px */
|
||||
--vp-spacing-lg: 1.5rem; /* 24px */
|
||||
--vp-spacing-xl: 2rem; /* 32px */
|
||||
--vp-spacing-2xl: 3rem; /* 48px */
|
||||
|
||||
/* Border Radius */
|
||||
--vp-radius-sm: 0.25rem; /* 4px */
|
||||
--vp-radius-md: 0.375rem; /* 6px */
|
||||
--vp-radius-lg: 0.5rem; /* 8px */
|
||||
--vp-radius-xl: 0.75rem; /* 12px */
|
||||
--vp-radius-full: 9999px;
|
||||
|
||||
/* Shadows */
|
||||
--vp-shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
||||
--vp-shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1);
|
||||
--vp-shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1);
|
||||
--vp-shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1);
|
||||
|
||||
/* Transitions */
|
||||
--vp-transition-color: 0.2s ease;
|
||||
--vp-transition-transform: 0.2s ease;
|
||||
--vp-transition-all: 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
|
||||
/* Z-Index */
|
||||
--vp-z-index-base: 1;
|
||||
--vp-z-index-dropdown: 10;
|
||||
--vp-z-index-sticky: 20;
|
||||
--vp-z-index-fixed: 50;
|
||||
--vp-z-index-modal: 100;
|
||||
--vp-z-index-toast: 200;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
* Dark Mode
|
||||
* ============================================ */
|
||||
.dark {
|
||||
/* Background Colors (Dark Mode) */
|
||||
--vp-c-bg: #111827;
|
||||
--vp-c-bg-soft: #1f2937;
|
||||
--vp-c-bg-mute: #374151;
|
||||
--vp-c-bg-alt: #0f172a;
|
||||
|
||||
/* Surface Colors */
|
||||
--vp-c-surface: #1f2937;
|
||||
--vp-c-surface-1: #374151;
|
||||
--vp-c-surface-2: #4b5563;
|
||||
--vp-c-surface-3: #6b7280;
|
||||
|
||||
/* Border Colors */
|
||||
--vp-c-border: #374151;
|
||||
--vp-c-border-soft: #1f2937;
|
||||
--vp-c-divider: #374151;
|
||||
|
||||
/* Text Colors */
|
||||
--vp-c-text-1: #f9fafb;
|
||||
--vp-c-text-2: #e5e7eb;
|
||||
--vp-c-text-3: #d1d5db;
|
||||
--vp-c-text-4: #9ca3af;
|
||||
--vp-c-text-code: #fca5a5;
|
||||
|
||||
/* Primary Colors (adjusted for dark mode) */
|
||||
--vp-c-primary-50: #1e3a8a;
|
||||
--vp-c-primary-100: #1e40af;
|
||||
--vp-c-primary-200: #1d4ed8;
|
||||
--vp-c-primary-300: #2563eb;
|
||||
--vp-c-primary-400: #3b82f6;
|
||||
--vp-c-primary-500: #60a5fa;
|
||||
--vp-c-primary-600: #93c5fd;
|
||||
--vp-c-primary-700: #bfdbfe;
|
||||
--vp-c-primary-800: #dbeafe;
|
||||
--vp-c-primary-900: #eff6ff;
|
||||
--vp-c-primary: var(--vp-c-primary-400);
|
||||
--vp-c-brand-1: var(--vp-c-primary-400);
|
||||
--vp-c-brand-2: var(--vp-c-secondary-400);
|
||||
--vp-c-brand-soft: rgba(96, 165, 250, 0.2);
|
||||
|
||||
/* Secondary Colors (adjusted for dark mode) */
|
||||
--vp-c-secondary-50: #064e3b;
|
||||
--vp-c-secondary-100: #065f46;
|
||||
--vp-c-secondary-200: #047857;
|
||||
--vp-c-secondary-300: #059669;
|
||||
--vp-c-secondary-400: #10b981;
|
||||
--vp-c-secondary-500: #34d399;
|
||||
--vp-c-secondary-600: #6ee7b7;
|
||||
--vp-c-secondary-700: #a7f3d0;
|
||||
--vp-c-secondary-800: #d1fae5;
|
||||
--vp-c-secondary-900: #ecfdf5;
|
||||
--vp-c-secondary: var(--vp-c-secondary-400);
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
* Color Scheme: Green
|
||||
* ============================================ */
|
||||
[data-theme="green"] {
|
||||
--vp-c-primary: var(--vp-c-secondary-500);
|
||||
}
|
||||
[data-theme="green"].dark {
|
||||
--vp-c-primary: var(--vp-c-secondary-400);
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
* Color Scheme: Orange
|
||||
* ============================================ */
|
||||
[data-theme="orange"] {
|
||||
--vp-c-primary: var(--vp-c-accent-500);
|
||||
}
|
||||
[data-theme="orange"].dark {
|
||||
--vp-c-primary: var(--vp-c-accent-400);
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
* Color Scheme: Purple
|
||||
* ============================================ */
|
||||
[data-theme="purple"] {
|
||||
--vp-c-primary-500: #8b5cf6;
|
||||
--vp-c-primary-600: #7c3aed;
|
||||
--vp-c-primary-700: #6d28d9;
|
||||
--vp-c-primary: var(--vp-c-primary-500);
|
||||
}
|
||||
[data-theme="purple"].dark {
|
||||
--vp-c-primary-400: #a78bfa;
|
||||
--vp-c-primary-500: #8b5cf6;
|
||||
--vp-c-primary: var(--vp-c-primary-400);
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
* Utility Classes
|
||||
* ============================================ */
|
||||
.text-primary { color: var(--vp-c-primary); }
|
||||
.text-secondary { color: var(--vp-c-secondary); }
|
||||
.bg-surface { background-color: var(--vp-c-surface); }
|
||||
.border-primary { border-color: var(--vp-c-primary); }
|
||||
.rounded-md { border-radius: var(--vp-radius-md); }
|
||||
.shadow-md { box-shadow: var(--vp-shadow-md); }
|
||||
.transition { transition: var(--vp-transition-all); }
|
||||
Reference in New Issue
Block a user