diff --git a/ccw/frontend/src/components/.gitignore b/ccw/frontend/src/components/.gitignore new file mode 100644 index 00000000..b4a7d405 --- /dev/null +++ b/ccw/frontend/src/components/.gitignore @@ -0,0 +1 @@ +.ace-tool/ diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index a6a5baea..b96d6526 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -1,5 +1,7 @@ import { defineConfig } from 'vitepress' import { withMermaid } from 'vitepress-plugin-mermaid' +import { transformDemoBlocks } from './theme/markdownTransform' +import path from 'path' const repoName = process.env.GITHUB_REPOSITORY?.split('/')[1] const isUserOrOrgSite = Boolean(repoName && repoName.endsWith('.github.io')) @@ -11,7 +13,7 @@ const base = export default withMermaid(defineConfig({ title: 'Claude Code Workflow Documentation', description: 'Claude Code Workspace - Advanced AI-Powered Development Environment', - lang: 'zh-CN', + lang: 'en-US', base, // Ignore dead links for incomplete docs @@ -44,12 +46,22 @@ export default withMermaid(defineConfig({ // Vite build/dev optimizations vite: { + resolve: { + alias: { + '@': path.resolve(__dirname, '../../ccw/frontend/src'), + '@/components': path.resolve(__dirname, '../../ccw/frontend/src/components'), + '@/lib': path.resolve(__dirname, '../../ccw/frontend/src/lib') + } + }, optimizeDeps: { - include: ['flexsearch'] + include: ['flexsearch', 'react', 'react-dom'] }, build: { target: 'es2019', cssCodeSplit: true + }, + ssr: { + noExternal: ['react', 'react-dom', 'class-variance-authority', 'clsx', 'tailwind-merge'] } }, @@ -68,13 +80,7 @@ export default withMermaid(defineConfig({ { 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' } - ] - } + { text: 'Features', link: '/features/spec' } ], // Sidebar - 优化导航结构,增加二级标题和归类 @@ -181,6 +187,17 @@ export default withMermaid(defineConfig({ ] } ], + '/components/': [ + { + text: 'UI Components', + collapsible: true, + items: [ + { text: 'Button', link: '/components/ui/button' }, + { text: 'Card', link: '/components/ui/card' }, + { text: 'Input', link: '/components/ui/input' } + ] + } + ], '/mcp/': [ { text: '🔗 MCP Tools', @@ -276,6 +293,14 @@ export default withMermaid(defineConfig({ ], config: (md) => { // Add markdown-it plugins if needed + // Custom demo block transform is handled by markdownTransform.ts + md.core.ruler.before('block', 'demo-blocks', (state) => { + const src = state.src + const transformed = transformDemoBlocks(src, { path: '' }) + if (transformed !== src) { + state.src = transformed + } + }) } }, @@ -299,13 +324,7 @@ export default withMermaid(defineConfig({ { text: '指南', link: '/zh/guide/ch01-what-is-claude-dms3' }, { text: '命令', link: '/zh/commands/claude/' }, { text: '技能', link: '/zh/skills/claude-index' }, - { text: '功能', link: '/zh/features/spec' }, - { - text: '语言', - items: [ - { text: 'English', link: '/guide/ch01-what-is-claude-dms3' } - ] - } + { text: '功能', link: '/zh/features/spec' } ], sidebar: { '/zh/guide/': [ @@ -424,6 +443,74 @@ export default withMermaid(defineConfig({ ] } } + }, + 'zh-CN': { + label: '简体中文', + lang: 'zh-CN', + title: 'Claude Code Workflow 文档', + description: 'Claude Code Workspace - 高级 AI 驱动开发环境', + themeConfig: { + outline: { + level: [2, 3], + label: '本页目录' + }, + nav: [ + { text: '指南', link: '/zh-CN/guide/ch01-what-is-claude-dms3' }, + { text: '命令', link: '/zh-CN/commands/claude/' }, + { text: '技能', link: '/zh-CN/skills/claude-index' }, + { text: '功能', link: '/zh-CN/features/spec' }, + { text: '组件', link: '/zh-CN/components/' } + ], + sidebar: { + '/zh-CN/guide/': [ + { + text: '📖 指南', + collapsible: false, + items: [ + { text: 'Claude Code Workflow 是什么', link: '/zh-CN/guide/ch01-what-is-claude-dms3' }, + { text: '快速开始', link: '/zh-CN/guide/ch02-getting-started' }, + { text: '核心概念', link: '/zh-CN/guide/ch03-core-concepts' }, + { text: '工作流基础', link: '/zh-CN/guide/ch04-workflow-basics' }, + { text: '高级技巧', link: '/zh-CN/guide/ch05-advanced-tips' }, + { text: '最佳实践', link: '/zh-CN/guide/ch06-best-practices' } + ] + }, + { + text: '🚀 快速入口', + collapsible: true, + items: [ + { text: '安装', link: '/zh-CN/guide/installation' }, + { text: '第一个工作流', link: '/zh-CN/guide/first-workflow' }, + { text: 'CLI 工具', link: '/zh-CN/guide/cli-tools' } + ] + } + ], + '/zh-CN/features/': [ + { + text: '⚙️ 核心功能', + collapsible: false, + items: [ + { text: 'Spec 规范系统', link: '/zh-CN/features/spec' }, + { text: 'Memory 记忆系统', link: '/zh-CN/features/memory' }, + { text: 'CLI 调用', link: '/zh-CN/features/cli' }, + { text: 'Dashboard 面板', link: '/zh-CN/features/dashboard' }, + { text: 'CodexLens', link: '/zh-CN/features/codexlens' } + ] + } + ], + '/zh-CN/components/': [ + { + text: 'UI 组件', + collapsible: true, + items: [ + { text: 'Button 按钮', link: '/zh-CN/components/ui/button' }, + { text: 'Card 卡片', link: '/zh-CN/components/ui/card' }, + { text: 'Input 输入框', link: '/zh-CN/components/ui/input' } + ] + } + ] + } + } } } })) diff --git a/docs/.vitepress/demos/README.md b/docs/.vitepress/demos/README.md new file mode 100644 index 00000000..3c6fcfaa --- /dev/null +++ b/docs/.vitepress/demos/README.md @@ -0,0 +1,64 @@ +# Demo Components + +This directory contains React components that are embedded in the documentation as interactive demos. + +## Creating a New Demo + +1. Create a new `.tsx` file in this directory (e.g., `my-demo.tsx`) +2. Export a default React component +3. Use it in markdown with `:::demo my-demo :::` + +## Demo Template + +```tsx +import React from 'react' + +/** + * Brief description of what this demo shows + */ +export default function MyDemo() { + return ( +
+ {/* Your demo content */} +
+ ) +} +``` + +## Demo Guidelines + +- **Keep it simple**: Demos should be focused and easy to understand +- **Use inline styles**: Avoid external dependencies for portability +- **Add comments**: Explain what the demo is showing +- **Test interactions**: Ensure buttons, inputs, etc. work correctly +- **Handle state**: Use React hooks (`useState`, `useEffect`) for interactive demos + +## Demo File Naming + +- Use kebab-case: `button-variants.tsx`, `card-basic.tsx` +- Group by category: + - `ui/` - UI component demos + - `shared/` - Shared component demos + - `pages/` - Page-level demos + +## Using Props + +If you need to pass custom props to a demo, use the extended markdown syntax: + +```markdown +:::demo my-demo +title: Custom Title +height: 300px +expandable: false +::: +``` + +## Available Props + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| name | string | - | Demo component name (required) | +| title | string | name | Custom demo title | +| height | string | 'auto' | Container height | +| expandable | boolean | true | Allow expand/collapse | +| showCode | boolean | true | Show code tab | diff --git a/docs/.vitepress/demos/button-variants.tsx b/docs/.vitepress/demos/button-variants.tsx new file mode 100644 index 00000000..a0df4065 --- /dev/null +++ b/docs/.vitepress/demos/button-variants.tsx @@ -0,0 +1,54 @@ +import React from 'react' +import { Button } from '@/components/ui/Button' + +/** + * Button Variants Demo + * Shows all 8 visual variants and 4 sizes of the Button component + * + * Variants include: + * - default: Primary brand color button + * - destructive: Red danger button + * - outline: Bordered light button + * - secondary: Gray secondary button + * - ghost: Transparent hover button + * - link: Link-style button + * - gradient: Brand gradient button + * - gradientPrimary: Primary gradient button + * + * Sizes include: + * - default: Standard size (h-10) + * - sm: Small size (h-9) + * - lg: Large size (h-11) + * - icon: Square icon size (h-10 w-10) + */ +export default function ButtonVariants() { + return ( +
+ {/* Variants Section */} +
+

Variants

+
+ + + + + + + + +
+
+ + {/* Sizes Section */} +
+

Sizes

+
+ + + + +
+
+
+ ) +} diff --git a/docs/.vitepress/theme/components/CodeViewer.vue b/docs/.vitepress/theme/components/CodeViewer.vue new file mode 100644 index 00000000..389d07e1 --- /dev/null +++ b/docs/.vitepress/theme/components/CodeViewer.vue @@ -0,0 +1,147 @@ + + + + + diff --git a/docs/.vitepress/theme/components/DemoContainer.vue b/docs/.vitepress/theme/components/DemoContainer.vue new file mode 100644 index 00000000..fe8d1dd7 --- /dev/null +++ b/docs/.vitepress/theme/components/DemoContainer.vue @@ -0,0 +1,270 @@ + + + + + diff --git a/docs/.vitepress/theme/components/LanguageSwitcher.vue b/docs/.vitepress/theme/components/LanguageSwitcher.vue new file mode 100644 index 00000000..0d039ff4 --- /dev/null +++ b/docs/.vitepress/theme/components/LanguageSwitcher.vue @@ -0,0 +1,186 @@ + + + + + diff --git a/docs/.vitepress/theme/components/PropsTable.vue b/docs/.vitepress/theme/components/PropsTable.vue new file mode 100644 index 00000000..3259d07c --- /dev/null +++ b/docs/.vitepress/theme/components/PropsTable.vue @@ -0,0 +1,165 @@ + + + + + diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts index 9941ff3a..99046f84 100644 --- a/docs/.vitepress/theme/index.ts +++ b/docs/.vitepress/theme/index.ts @@ -7,9 +7,16 @@ import Breadcrumb from './components/Breadcrumb.vue' import PageToc from './components/PageToc.vue' import ProfessionalHome from './components/ProfessionalHome.vue' import Layout from './layouts/Layout.vue' +// Demo system components +import DemoContainer from './components/DemoContainer.vue' +import CodeViewer from './components/CodeViewer.vue' +import PropsTable from './components/PropsTable.vue' +// Language switcher component +import LanguageSwitcher from './components/LanguageSwitcher.vue' import './styles/variables.css' import './styles/custom.css' import './styles/mobile.css' +import './styles/demo.css' export default { extends: DefaultTheme, @@ -23,5 +30,11 @@ export default { app.component('Breadcrumb', Breadcrumb) app.component('PageToc', PageToc) app.component('ProfessionalHome', ProfessionalHome) + // Register demo system components + app.component('DemoContainer', DemoContainer) + app.component('CodeViewer', CodeViewer) + app.component('PropsTable', PropsTable) + // Register language switcher component + app.component('LanguageSwitcher', LanguageSwitcher) } } diff --git a/docs/.vitepress/theme/layouts/Layout.vue b/docs/.vitepress/theme/layouts/Layout.vue index 703dd0d3..8e321fa7 100644 --- a/docs/.vitepress/theme/layouts/Layout.vue +++ b/docs/.vitepress/theme/layouts/Layout.vue @@ -3,6 +3,7 @@ import DefaultTheme from 'vitepress/theme' import { onBeforeUnmount, onMounted } from 'vue' import { useDynamicIcon } from '../composables/useDynamicIcon' import ThemeLogo from '../components/ThemeLogo.vue' +import LanguageSwitcher from '../components/LanguageSwitcher.vue' let mediaQuery: MediaQueryList | null = null let systemThemeChangeHandler: (() => void) | null = null @@ -84,6 +85,7 @@ onBeforeUnmount(() => { + diff --git a/docs/.vitepress/theme/markdownTransform.ts b/docs/.vitepress/theme/markdownTransform.ts new file mode 100644 index 00000000..f4a26510 --- /dev/null +++ b/docs/.vitepress/theme/markdownTransform.ts @@ -0,0 +1,66 @@ +import type { MarkdownTransformContext } from 'vitepress' + +const demoBlockRE = /:::\s*demo\s+(.+?)\s*:::/g + +export interface DemoBlockMeta { + name: string + file?: string + height?: string + expandable?: boolean + showCode?: boolean + title?: string +} + +export function transformDemoBlocks( + code: string, + ctx: MarkdownTransformContext +): string { + return code.replace(demoBlockRE, (match, content) => { + const meta = parseDemoBlock(content) + const demoId = `demo-${ctx.path.replace(/[^a-z0-9]/gi, '-')}-${Math.random().toString(36).slice(2, 8)}` + + const props = [ + `id="${demoId}"`, + `name="${meta.name}"`, + meta.file ? `file="${meta.file}"` : '', + meta.height ? `height="${meta.height}"` : '', + meta.expandable === false ? ':expandable="false"' : ':expandable="true"', + meta.showCode === false ? ':show-code="false"' : ':show-code="true"', + meta.title ? `title="${meta.title}"` : '' + ].filter(Boolean).join(' ') + + return `` + }) +} + +function parseDemoBlock(content: string): DemoBlockMeta { + const lines = content.trim().split('\n') + const firstLine = lines[0] || '' + + // Parse: name or # file + const [name, ...rest] = firstLine.split(/\s+/) + const file = rest.find(l => l.startsWith('#'))?.slice(1) + + return { + name: name.trim(), + file, + height: extractProp(lines, 'height'), + expandable: extractProp(lines, 'expandable') !== 'false', + showCode: extractProp(lines, 'showCode') !== 'false', + title: extractProp(lines, 'title') + } +} + +function extractProp(lines: string[], prop: string): string | undefined { + const line = lines.find(l => l.trim().startsWith(`${prop}:`)) + return line?.split(':', 2)[1]?.trim() +} + +// VitePress markdown configuration hook +export function markdownTransformSetup() { + return { + transform: (code: string, ctx: MarkdownTransformContext) => { + return transformDemoBlocks(code, ctx) + } + } +} diff --git a/docs/.vitepress/theme/styles/demo.css b/docs/.vitepress/theme/styles/demo.css new file mode 100644 index 00000000..a8410301 --- /dev/null +++ b/docs/.vitepress/theme/styles/demo.css @@ -0,0 +1,104 @@ +/* Demo system styles for VitePress */ + +/* Demo container theme-aware styling */ +.demo-container { + /* Theme-aware borders */ + border: 1px solid var(--vp-c-border); + + /* Rounded corners */ + border-radius: 8px; + + /* Subtle shadow */ + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + + /* Dark mode support */ + background: var(--vp-c-bg); +} + +.demo-preview { + /* Consistent padding */ + padding: 24px; + + /* Min height for visibility */ + min-height: 100px; + + /* Center content when small */ + display: flex; + align-items: center; + justify-content: center; +} + +/* Dark mode overrides */ +.dark .demo-container { + background: var(--vp-c-bg-soft); + border-color: var(--vp-c-divider); +} + +/* Responsive design */ +@media (max-width: 768px) { + .demo-header { + flex-direction: column; + align-items: flex-start; + gap: 8px; + } + + .demo-preview { + padding: 16px; + } + + .demo-actions { + width: 100%; + justify-content: space-between; + } +} + +/* Demo code viewer styles */ +.code-viewer { + background: var(--vp-code-bg); + border-radius: 6px; + overflow: hidden; +} + +.code-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px 16px; + background: var(--vp-code-block-bg); + border-bottom: 1px solid var(--vp-c-border); +} + +/* Props table styles */ +.props-table-container { + margin: 16px 0; + overflow-x: auto; + border: 1px solid var(--vp-c-border); + border-radius: 8px; +} + +.props-table { + width: 100%; + border-collapse: collapse; + font-size: 14px; +} + +.props-table thead { + background: var(--vp-c-bg-soft); + border-bottom: 1px solid var(--vp-c-border); +} + +/* Interactive demo feedback */ +.demo-container:has(.demo-preview:hover) { + border-color: var(--vp-c-brand); + transition: border-color 0.2s; +} + +/* Code syntax highlighting in demos */ +.demo-preview code { + font-family: var(--vp-font-family-mono); + font-size: 13px; + padding: 2px 6px; + background: var(--vp-code-bg); + border-radius: 4px; + color: var(--vp-code-color); +} diff --git a/docs/components/index.md b/docs/components/index.md new file mode 100644 index 00000000..60b806d1 --- /dev/null +++ b/docs/components/index.md @@ -0,0 +1,600 @@ +# Component Library + +## One-Liner +**A comprehensive collection of reusable UI components built with Radix UI primitives and Tailwind CSS, following shadcn/ui patterns for consistent, accessible, and customizable interfaces.** + +--- + +## Overview + +**Location**: `ccw/frontend/src/components/ui/` + +**Purpose**: Provides a consistent set of UI components for building the CCW frontend application. + +**Technology Stack**: +- **Radix UI**: Unstyled, accessible component primitives +- **Tailwind CSS**: Utility-first styling with custom theme +- **class-variance-authority (CVA)**: Type-safe variant prop management +- **Lucide React**: Consistent iconography + +--- + +## Live Demo: Component Gallery + +:::demo ComponentGallery +# component-gallery.tsx +/** + * Component Gallery Demo + * Interactive showcase of all UI components + */ +export function ComponentGallery() { + const [selectedCategory, setSelectedCategory] = React.useState('all') + const [buttonVariant, setButtonVariant] = React.useState('default') + const [switchState, setSwitchState] = React.useState(false) + const [checkboxState, setCheckboxState] = React.useState(false) + const [selectedTab, setSelectedTab] = React.useState('variants') + + const categories = [ + { id: 'all', label: 'All Components' }, + { id: 'buttons', label: 'Buttons' }, + { id: 'forms', label: 'Forms' }, + { id: 'feedback', label: 'Feedback' }, + { id: 'navigation', label: 'Navigation' }, + { id: 'overlays', label: 'Overlays' }, + ] + + const components = { + buttons: [ + { name: 'Button', variants: ['default', 'destructive', 'outline', 'secondary', 'ghost', 'link', 'gradient'] }, + ], + forms: [ + { name: 'Input', type: 'text' }, + { name: 'Textarea', type: 'textarea' }, + { name: 'Select', type: 'select' }, + { name: 'Checkbox', type: 'checkbox' }, + { name: 'Switch', type: 'switch' }, + ], + feedback: [ + { name: 'Badge', variants: ['default', 'secondary', 'success', 'warning', 'destructive'] }, + { name: 'Progress', type: 'progress' }, + { name: 'Alert', type: 'alert' }, + ], + navigation: [ + { name: 'Tabs', type: 'tabs' }, + { name: 'Breadcrumb', type: 'breadcrumb' }, + ], + overlays: [ + { name: 'Dialog', type: 'dialog' }, + { name: 'Drawer', type: 'drawer' }, + { name: 'Dropdown', type: 'dropdown' }, + ], + } + + const buttonVariants = ['default', 'destructive', 'outline', 'secondary', 'ghost', 'link'] + + return ( +
+ {/* Header */} +
+

UI Component Library

+

Interactive showcase of all available UI components

+
+ + {/* Category Filter */} +
+ {categories.map((cat) => ( + + ))} +
+ + {/* Buttons Section */} + {(selectedCategory === 'all' || selectedCategory === 'buttons') && ( +
+

Buttons

+
+ {/* Variant Selector */} +
+ +
+ {buttonVariants.map((variant) => ( + + ))} +
+
+ + {/* Button Sizes */} +
+ +
+ + + + +
+
+ + {/* All Button Variants */} +
+ +
+ + + + + + + +
+
+
+
+ )} + + {/* Forms Section */} + {(selectedCategory === 'all' || selectedCategory === 'forms') && ( +
+

Form Components

+
+ {/* Input */} +
+ + + +
+ + {/* Textarea */} +
+ +