mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-03-02 15:23:19 +08:00
Add Chinese documentation for custom skills development and reference guide
- Created a new document for custom skills development (`custom.md`) detailing the structure, creation, implementation, and best practices for developing custom CCW skills. - Added an index document (`index.md`) summarizing all built-in skills, their categories, and usage examples. - Introduced a reference guide (`reference.md`) providing a quick reference for all 33 built-in CCW skills, including triggers and purposes.
This commit is contained in:
@@ -104,6 +104,17 @@ const sessionFolder = `.workflow/.lite-plan/${sessionId}`
|
|||||||
bash(`mkdir -p ${sessionFolder} && test -d ${sessionFolder} && echo "SUCCESS: ${sessionFolder}" || echo "FAILED: ${sessionFolder}"`)
|
bash(`mkdir -p ${sessionFolder} && test -d ${sessionFolder} && echo "SUCCESS: ${sessionFolder}" || echo "FAILED: ${sessionFolder}"`)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**TodoWrite (Phase 1 start)**:
|
||||||
|
```javascript
|
||||||
|
TodoWrite({ todos: [
|
||||||
|
{ content: "Phase 1: Exploration", status: "in_progress", activeForm: "Exploring codebase" },
|
||||||
|
{ content: "Phase 2: Clarification", status: "pending", activeForm: "Collecting clarifications" },
|
||||||
|
{ content: "Phase 3: Planning", status: "pending", activeForm: "Generating plan" },
|
||||||
|
{ content: "Phase 4: Confirmation", status: "pending", activeForm: "Awaiting confirmation" },
|
||||||
|
{ content: "Phase 5: Execution", status: "pending", activeForm: "Executing tasks" }
|
||||||
|
]})
|
||||||
|
```
|
||||||
|
|
||||||
**Exploration Decision Logic**:
|
**Exploration Decision Logic**:
|
||||||
```javascript
|
```javascript
|
||||||
// Check if task description already contains prior analysis context (from analyze-with-file)
|
// Check if task description already contains prior analysis context (from analyze-with-file)
|
||||||
@@ -307,6 +318,17 @@ Angles explored: ${explorationManifest.explorations.map(e => e.angle).join(', ')
|
|||||||
`)
|
`)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**TodoWrite (Phase 1 complete)**:
|
||||||
|
```javascript
|
||||||
|
TodoWrite({ todos: [
|
||||||
|
{ content: "Phase 1: Exploration", status: "completed", activeForm: "Exploring codebase" },
|
||||||
|
{ content: "Phase 2: Clarification", status: "in_progress", activeForm: "Collecting clarifications" },
|
||||||
|
{ content: "Phase 3: Planning", status: "pending", activeForm: "Generating plan" },
|
||||||
|
{ content: "Phase 4: Confirmation", status: "pending", activeForm: "Awaiting confirmation" },
|
||||||
|
{ content: "Phase 5: Execution", status: "pending", activeForm: "Executing tasks" }
|
||||||
|
]})
|
||||||
|
```
|
||||||
|
|
||||||
**Output**:
|
**Output**:
|
||||||
- `${sessionFolder}/exploration-{angle1}.json`
|
- `${sessionFolder}/exploration-{angle1}.json`
|
||||||
- `${sessionFolder}/exploration-{angle2}.json`
|
- `${sessionFolder}/exploration-{angle2}.json`
|
||||||
@@ -560,6 +582,17 @@ Note: Use files[].change (not modification_points), convergence.criteria (not ac
|
|||||||
|
|
||||||
**Output**: `${sessionFolder}/plan.json`
|
**Output**: `${sessionFolder}/plan.json`
|
||||||
|
|
||||||
|
**TodoWrite (Phase 3 complete)**:
|
||||||
|
```javascript
|
||||||
|
TodoWrite({ todos: [
|
||||||
|
{ content: "Phase 1: Exploration", status: "completed", activeForm: "Exploring codebase" },
|
||||||
|
{ content: "Phase 2: Clarification", status: "completed", activeForm: "Collecting clarifications" },
|
||||||
|
{ content: "Phase 3: Planning", status: "completed", activeForm: "Generating plan" },
|
||||||
|
{ content: "Phase 4: Confirmation", status: "in_progress", activeForm: "Awaiting confirmation" },
|
||||||
|
{ content: "Phase 5: Execution", status: "pending", activeForm: "Executing tasks" }
|
||||||
|
]})
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Phase 4: Task Confirmation & Execution Selection
|
### Phase 4: Task Confirmation & Execution Selection
|
||||||
@@ -649,6 +682,19 @@ if (autoYes) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**TodoWrite (Phase 4 confirmed)**:
|
||||||
|
```javascript
|
||||||
|
const executionLabel = userSelection.execution_method
|
||||||
|
|
||||||
|
TodoWrite({ todos: [
|
||||||
|
{ content: "Phase 1: Exploration", status: "completed", activeForm: "Exploring codebase" },
|
||||||
|
{ content: "Phase 2: Clarification", status: "completed", activeForm: "Collecting clarifications" },
|
||||||
|
{ content: "Phase 3: Planning", status: "completed", activeForm: "Generating plan" },
|
||||||
|
{ content: `Phase 4: Confirmed [${executionLabel}]`, status: "completed", activeForm: "Confirmed" },
|
||||||
|
{ content: `Phase 5: Execution [${executionLabel}]`, status: "in_progress", activeForm: `Executing [${executionLabel}]` }
|
||||||
|
]})
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Phase 5: Handoff to Execution
|
### Phase 5: Handoff to Execution
|
||||||
|
|||||||
@@ -350,9 +350,9 @@ executionCalls = createExecutionCalls(getTasks(planObject), executionMethod).map
|
|||||||
|
|
||||||
TodoWrite({
|
TodoWrite({
|
||||||
todos: executionCalls.map(c => ({
|
todos: executionCalls.map(c => ({
|
||||||
content: `${c.executionType === "parallel" ? "⚡" : "→"} ${c.id} (${c.tasks.length} tasks)`,
|
content: `${c.executionType === "parallel" ? "⚡" : "→"} ${c.id} [${c.executor}] (${c.tasks.length} tasks)`,
|
||||||
status: "pending",
|
status: "pending",
|
||||||
activeForm: `Executing ${c.id}`
|
activeForm: `Executing ${c.id} [${c.executor}]`
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -258,6 +258,19 @@ AskUserQuestion({
|
|||||||
- Need More Analysis → Phase 2 with feedback
|
- Need More Analysis → Phase 2 with feedback
|
||||||
- Cancel → Save session for resumption
|
- Cancel → Save session for resumption
|
||||||
|
|
||||||
|
**TodoWrite Update (Phase 4 Decision)**:
|
||||||
|
```javascript
|
||||||
|
const executionLabel = userSelection.execution_method // "Agent" / "Codex" / "Auto"
|
||||||
|
|
||||||
|
TodoWrite({ todos: [
|
||||||
|
{ content: "Phase 1: Context Gathering", status: "completed", activeForm: "Gathering context" },
|
||||||
|
{ content: "Phase 2: Multi-CLI Discussion", status: "completed", activeForm: "Running discussion" },
|
||||||
|
{ content: "Phase 3: Present Options", status: "completed", activeForm: "Presenting options" },
|
||||||
|
{ content: `Phase 4: User Decision [${executionLabel}]`, status: "completed", activeForm: "Decision recorded" },
|
||||||
|
{ content: `Phase 5: Plan Generation [${executionLabel}]`, status: "in_progress", activeForm: `Generating plan [${executionLabel}]` }
|
||||||
|
]})
|
||||||
|
```
|
||||||
|
|
||||||
### Phase 5: Plan Generation & Execution Handoff
|
### Phase 5: Plan Generation & Execution Handoff
|
||||||
|
|
||||||
**Step 1: Build Context-Package** (Orchestrator responsibility):
|
**Step 1: Build Context-Package** (Orchestrator responsibility):
|
||||||
|
|||||||
@@ -357,9 +357,9 @@ executionCalls = createExecutionCalls(getTasks(planObject), executionMethod).map
|
|||||||
|
|
||||||
TodoWrite({
|
TodoWrite({
|
||||||
todos: executionCalls.map(c => ({
|
todos: executionCalls.map(c => ({
|
||||||
content: `${c.executionType === "parallel" ? "⚡" : "→"} ${c.id} (${c.tasks.length} tasks)`,
|
content: `${c.executionType === "parallel" ? "⚡" : "→"} ${c.id} [${c.executor}] (${c.tasks.length} tasks)`,
|
||||||
status: "pending",
|
status: "pending",
|
||||||
activeForm: `Executing ${c.id}`
|
activeForm: `Executing ${c.id} [${c.executor}]`
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -338,13 +338,20 @@ Output:
|
|||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Executor Label** (computed after Step 4.0):
|
||||||
|
```javascript
|
||||||
|
const executorLabel = userConfig.executionMethod === 'agent' ? 'Agent'
|
||||||
|
: userConfig.executionMethod === 'hybrid' ? 'Hybrid'
|
||||||
|
: `CLI (${userConfig.preferredCliTool})`
|
||||||
|
```
|
||||||
|
|
||||||
### TodoWrite Update (Phase 4 in progress)
|
### TodoWrite Update (Phase 4 in progress)
|
||||||
|
|
||||||
```json
|
```json
|
||||||
[
|
[
|
||||||
{"content": "Phase 1: Session Discovery", "status": "completed", "activeForm": "Executing session discovery"},
|
{"content": "Phase 1: Session Discovery", "status": "completed", "activeForm": "Executing session discovery"},
|
||||||
{"content": "Phase 2: Context Gathering", "status": "completed", "activeForm": "Executing context gathering"},
|
{"content": "Phase 2: Context Gathering", "status": "completed", "activeForm": "Executing context gathering"},
|
||||||
{"content": "Phase 4: Task Generation", "status": "in_progress", "activeForm": "Executing task generation"}
|
{"content": "Phase 4: Task Generation [${executorLabel}]", "status": "in_progress", "activeForm": "Generating tasks [${executorLabel}]"}
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -354,7 +361,7 @@ Output:
|
|||||||
[
|
[
|
||||||
{"content": "Phase 1: Session Discovery", "status": "completed", "activeForm": "Executing session discovery"},
|
{"content": "Phase 1: Session Discovery", "status": "completed", "activeForm": "Executing session discovery"},
|
||||||
{"content": "Phase 2: Context Gathering", "status": "completed", "activeForm": "Executing context gathering"},
|
{"content": "Phase 2: Context Gathering", "status": "completed", "activeForm": "Executing context gathering"},
|
||||||
{"content": "Phase 4: Task Generation", "status": "completed", "activeForm": "Executing task generation"}
|
{"content": "Phase 4: Task Generation [${executorLabel}]", "status": "completed", "activeForm": "Generating tasks [${executorLabel}]"}
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -1,896 +0,0 @@
|
|||||||
---
|
|
||||||
name: workflow-wave-plan
|
|
||||||
description: CSV Wave planning and execution - explore via wave, resolve conflicts, execute from CSV with linked exploration context. Triggers on "workflow:wave-plan".
|
|
||||||
argument-hint: "<task description> [--yes|-y] [--concurrency|-c N]"
|
|
||||||
allowed-tools: Task, AskUserQuestion, Read, Write, Edit, Bash, Glob, Grep
|
|
||||||
---
|
|
||||||
|
|
||||||
# Workflow Wave Plan
|
|
||||||
|
|
||||||
CSV Wave-based planning and execution. Uses structured CSV state for both exploration and execution, with cross-phase context propagation via `context_from` linking.
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
```
|
|
||||||
Requirement
|
|
||||||
↓
|
|
||||||
┌─ Phase 1: Decompose ─────────────────────┐
|
|
||||||
│ Analyze requirement → explore.csv │
|
|
||||||
│ (1 row per exploration angle) │
|
|
||||||
└────────────────────┬──────────────────────┘
|
|
||||||
↓
|
|
||||||
┌─ Phase 2: Wave Explore ──────────────────┐
|
|
||||||
│ Wave loop: spawn Explore agents │
|
|
||||||
│ → findings/key_files → explore.csv │
|
|
||||||
└────────────────────┬──────────────────────┘
|
|
||||||
↓
|
|
||||||
┌─ Phase 3: Synthesize & Plan ─────────────┐
|
|
||||||
│ Read explore findings → cross-reference │
|
|
||||||
│ → resolve conflicts → tasks.csv │
|
|
||||||
│ (context_from links to E* explore rows) │
|
|
||||||
└────────────────────┬──────────────────────┘
|
|
||||||
↓
|
|
||||||
┌─ Phase 4: Wave Execute ──────────────────┐
|
|
||||||
│ Wave loop: build prev_context from CSV │
|
|
||||||
│ → spawn code-developer agents per wave │
|
|
||||||
│ → results → tasks.csv │
|
|
||||||
└────────────────────┬──────────────────────┘
|
|
||||||
↓
|
|
||||||
┌─ Phase 5: Aggregate ─────────────────────┐
|
|
||||||
│ results.csv + context.md + summary │
|
|
||||||
└───────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
## Context Flow
|
|
||||||
|
|
||||||
```
|
|
||||||
explore.csv tasks.csv
|
|
||||||
┌──────────┐ ┌──────────┐
|
|
||||||
│ E1: arch │──────────→│ T1: setup│ context_from: E1;E2
|
|
||||||
│ findings │ │ prev_ctx │← E1+E2 findings
|
|
||||||
├──────────┤ ├──────────┤
|
|
||||||
│ E2: deps │──────────→│ T2: impl │ context_from: E1;T1
|
|
||||||
│ findings │ │ prev_ctx │← E1+T1 findings
|
|
||||||
├──────────┤ ├──────────┤
|
|
||||||
│ E3: test │──┐ ┌───→│ T3: test │ context_from: E3;T2
|
|
||||||
│ findings │ └───┘ │ prev_ctx │← E3+T2 findings
|
|
||||||
└──────────┘ └──────────┘
|
|
||||||
|
|
||||||
Two context channels:
|
|
||||||
1. Directed: context_from → prev_context (from CSV findings)
|
|
||||||
2. Broadcast: discoveries.ndjson (append-only shared board)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## CSV Schemas
|
|
||||||
|
|
||||||
### explore.csv
|
|
||||||
|
|
||||||
| Column | Type | Set By | Description |
|
|
||||||
|--------|------|--------|-------------|
|
|
||||||
| `id` | string | Decomposer | E1, E2, ... |
|
|
||||||
| `angle` | string | Decomposer | Exploration angle name |
|
|
||||||
| `description` | string | Decomposer | What to explore from this angle |
|
|
||||||
| `focus` | string | Decomposer | Keywords and focus areas |
|
|
||||||
| `deps` | string | Decomposer | Semicolon-separated dep IDs (usually empty) |
|
|
||||||
| `wave` | integer | Wave Engine | Wave number (usually 1) |
|
|
||||||
| `status` | enum | Agent | pending / completed / failed |
|
|
||||||
| `findings` | string | Agent | Discoveries (max 800 chars) |
|
|
||||||
| `key_files` | string | Agent | Relevant files (semicolon-separated) |
|
|
||||||
| `error` | string | Agent | Error message if failed |
|
|
||||||
|
|
||||||
### tasks.csv
|
|
||||||
|
|
||||||
| Column | Type | Set By | Description |
|
|
||||||
|--------|------|--------|-------------|
|
|
||||||
| `id` | string | Planner | T1, T2, ... |
|
|
||||||
| `title` | string | Planner | Task title |
|
|
||||||
| `description` | string | Planner | Self-contained task description — what to implement |
|
|
||||||
| `test` | string | Planner | Test cases: what tests to write and how to verify (unit/integration/edge) |
|
|
||||||
| `acceptance_criteria` | string | Planner | Measurable conditions that define "done" |
|
|
||||||
| `scope` | string | Planner | Target file/directory glob — constrains agent write area, prevents cross-task file conflicts |
|
|
||||||
| `hints` | string | Planner | Implementation tips + reference files. Format: `tips text \|\| file1;file2`. Either part is optional |
|
|
||||||
| `execution_directives` | string | Planner | Execution constraints: commands to run for verification, tool restrictions |
|
|
||||||
| `deps` | string | Planner | Dependency task IDs: T1;T2 |
|
|
||||||
| `context_from` | string | Planner | Context source IDs: **E1;E2;T1** |
|
|
||||||
| `wave` | integer | Wave Engine | Wave number (computed from deps) |
|
|
||||||
| `status` | enum | Agent | pending / completed / failed / skipped |
|
|
||||||
| `findings` | string | Agent | Execution findings (max 500 chars) |
|
|
||||||
| `files_modified` | string | Agent | Files modified (semicolon-separated) |
|
|
||||||
| `tests_passed` | boolean | Agent | Whether all defined test cases passed (true/false) |
|
|
||||||
| `acceptance_met` | string | Agent | Summary of which acceptance criteria were met/unmet |
|
|
||||||
| `error` | string | Agent | Error if failed |
|
|
||||||
|
|
||||||
**context_from prefix convention**: `E*` → explore.csv lookup, `T*` → tasks.csv lookup.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Session Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
.workflow/.wave-plan/{session-id}/
|
|
||||||
├── explore.csv # Exploration state
|
|
||||||
├── tasks.csv # Execution state
|
|
||||||
├── discoveries.ndjson # Shared discovery board
|
|
||||||
├── explore-results/ # Detailed per-angle results
|
|
||||||
│ ├── E1.json
|
|
||||||
│ └── E2.json
|
|
||||||
├── task-results/ # Detailed per-task results
|
|
||||||
│ ├── T1.json
|
|
||||||
│ └── T2.json
|
|
||||||
├── results.csv # Final results export
|
|
||||||
└── context.md # Full context summary
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Session Initialization
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const getUtc8ISOString = () => new Date(Date.now() + 8 * 60 * 60 * 1000).toISOString()
|
|
||||||
|
|
||||||
// Parse flags
|
|
||||||
const AUTO_YES = $ARGUMENTS.includes('--yes') || $ARGUMENTS.includes('-y')
|
|
||||||
const concurrencyMatch = $ARGUMENTS.match(/(?:--concurrency|-c)\s+(\d+)/)
|
|
||||||
const maxConcurrency = concurrencyMatch ? parseInt(concurrencyMatch[1]) : 4
|
|
||||||
|
|
||||||
const requirement = $ARGUMENTS
|
|
||||||
.replace(/--yes|-y|--concurrency\s+\d+|-c\s+\d+/g, '')
|
|
||||||
.trim()
|
|
||||||
|
|
||||||
const slug = requirement.toLowerCase()
|
|
||||||
.replace(/[^a-z0-9\u4e00-\u9fa5]+/g, '-')
|
|
||||||
.substring(0, 40)
|
|
||||||
const dateStr = getUtc8ISOString().substring(0, 10).replace(/-/g, '')
|
|
||||||
const sessionId = `wp-${slug}-${dateStr}`
|
|
||||||
const sessionFolder = `.workflow/.wave-plan/${sessionId}`
|
|
||||||
|
|
||||||
Bash(`mkdir -p ${sessionFolder}/explore-results ${sessionFolder}/task-results`)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Phase 1: Decompose → explore.csv
|
|
||||||
|
|
||||||
### 1.1 Analyze Requirement
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const complexity = analyzeComplexity(requirement)
|
|
||||||
// Low: 1 angle | Medium: 2-3 angles | High: 3-4 angles
|
|
||||||
|
|
||||||
const ANGLE_PRESETS = {
|
|
||||||
architecture: ['architecture', 'dependencies', 'integration-points', 'modularity'],
|
|
||||||
security: ['security', 'auth-patterns', 'dataflow', 'validation'],
|
|
||||||
performance: ['performance', 'bottlenecks', 'caching', 'data-access'],
|
|
||||||
bugfix: ['error-handling', 'dataflow', 'state-management', 'edge-cases'],
|
|
||||||
feature: ['patterns', 'integration-points', 'testing', 'dependencies']
|
|
||||||
}
|
|
||||||
|
|
||||||
function selectAngles(text, count) {
|
|
||||||
let preset = 'feature'
|
|
||||||
if (/refactor|architect|restructure|modular/.test(text)) preset = 'architecture'
|
|
||||||
else if (/security|auth|permission|access/.test(text)) preset = 'security'
|
|
||||||
else if (/performance|slow|optimi|cache/.test(text)) preset = 'performance'
|
|
||||||
else if (/fix|bug|error|broken/.test(text)) preset = 'bugfix'
|
|
||||||
return ANGLE_PRESETS[preset].slice(0, count)
|
|
||||||
}
|
|
||||||
|
|
||||||
const angleCount = complexity === 'High' ? 4 : complexity === 'Medium' ? 3 : 1
|
|
||||||
const angles = selectAngles(requirement, angleCount)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 1.2 Generate explore.csv
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const header = 'id,angle,description,focus,deps,wave,status,findings,key_files,error'
|
|
||||||
const rows = angles.map((angle, i) => {
|
|
||||||
const id = `E${i + 1}`
|
|
||||||
const desc = `Explore codebase from ${angle} perspective for: ${requirement}`
|
|
||||||
return `"${id}","${angle}","${escCSV(desc)}","${angle}","",1,"pending","","",""`
|
|
||||||
})
|
|
||||||
|
|
||||||
Write(`${sessionFolder}/explore.csv`, [header, ...rows].join('\n'))
|
|
||||||
```
|
|
||||||
|
|
||||||
All exploration rows default to wave 1 (independent parallel). If angle dependencies exist, compute waves.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Phase 2: Wave Explore
|
|
||||||
|
|
||||||
Execute exploration waves using `Task(Explore)` agents.
|
|
||||||
|
|
||||||
### 2.1 Wave Loop
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const exploreCSV = parseCSV(Read(`${sessionFolder}/explore.csv`))
|
|
||||||
const maxExploreWave = Math.max(...exploreCSV.map(r => parseInt(r.wave)))
|
|
||||||
|
|
||||||
for (let wave = 1; wave <= maxExploreWave; wave++) {
|
|
||||||
const waveRows = exploreCSV.filter(r =>
|
|
||||||
parseInt(r.wave) === wave && r.status === 'pending'
|
|
||||||
)
|
|
||||||
if (waveRows.length === 0) continue
|
|
||||||
|
|
||||||
// Skip rows with failed dependencies
|
|
||||||
const validRows = waveRows.filter(r => {
|
|
||||||
if (!r.deps) return true
|
|
||||||
return r.deps.split(';').filter(Boolean).every(depId => {
|
|
||||||
const dep = exploreCSV.find(d => d.id === depId)
|
|
||||||
return dep && dep.status === 'completed'
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
waveRows.filter(r => !validRows.includes(r)).forEach(r => {
|
|
||||||
r.status = 'skipped'
|
|
||||||
r.error = 'Dependency failed/skipped'
|
|
||||||
})
|
|
||||||
|
|
||||||
// ★ Spawn ALL explore agents in SINGLE message → parallel execution
|
|
||||||
const results = validRows.map(row =>
|
|
||||||
Task({
|
|
||||||
subagent_type: "Explore",
|
|
||||||
run_in_background: false,
|
|
||||||
description: `Explore: ${row.angle}`,
|
|
||||||
prompt: buildExplorePrompt(row, requirement, sessionFolder)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
// Collect results from JSON files → update explore.csv
|
|
||||||
validRows.forEach((row, i) => {
|
|
||||||
const resultPath = `${sessionFolder}/explore-results/${row.id}.json`
|
|
||||||
if (fileExists(resultPath)) {
|
|
||||||
const result = JSON.parse(Read(resultPath))
|
|
||||||
row.status = result.status || 'completed'
|
|
||||||
row.findings = truncate(result.findings, 800)
|
|
||||||
row.key_files = Array.isArray(result.key_files)
|
|
||||||
? result.key_files.join(';')
|
|
||||||
: (result.key_files || '')
|
|
||||||
row.error = result.error || ''
|
|
||||||
} else {
|
|
||||||
// Fallback: parse from agent output text
|
|
||||||
row.status = 'completed'
|
|
||||||
row.findings = truncate(results[i], 800)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
writeCSV(`${sessionFolder}/explore.csv`, exploreCSV)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2.2 Explore Agent Prompt
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
function buildExplorePrompt(row, requirement, sessionFolder) {
|
|
||||||
return `## Exploration: ${row.angle}
|
|
||||||
|
|
||||||
**Requirement**: ${requirement}
|
|
||||||
**Focus**: ${row.focus}
|
|
||||||
|
|
||||||
### MANDATORY FIRST STEPS
|
|
||||||
1. Read shared discoveries: ${sessionFolder}/discoveries.ndjson (if exists, skip if not)
|
|
||||||
2. Read project context: .workflow/project-tech.json (if exists)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Instructions
|
|
||||||
Explore the codebase from the **${row.angle}** perspective:
|
|
||||||
1. Discover relevant files, modules, and patterns
|
|
||||||
2. Identify integration points and dependencies
|
|
||||||
3. Note constraints, risks, and conventions
|
|
||||||
4. Find existing patterns to follow
|
|
||||||
5. Share discoveries: append findings to ${sessionFolder}/discoveries.ndjson
|
|
||||||
|
|
||||||
## Output
|
|
||||||
Write findings to: ${sessionFolder}/explore-results/${row.id}.json
|
|
||||||
|
|
||||||
JSON format:
|
|
||||||
{
|
|
||||||
"status": "completed",
|
|
||||||
"findings": "Concise summary of ${row.angle} discoveries (max 800 chars)",
|
|
||||||
"key_files": ["relevant/file1.ts", "relevant/file2.ts"],
|
|
||||||
"details": {
|
|
||||||
"patterns": ["pattern descriptions"],
|
|
||||||
"integration_points": [{"file": "path", "description": "..."}],
|
|
||||||
"constraints": ["constraint descriptions"],
|
|
||||||
"recommendations": ["recommendation descriptions"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Also provide a 2-3 sentence summary.`
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Phase 3: Synthesize & Plan → tasks.csv
|
|
||||||
|
|
||||||
Read exploration findings, cross-reference, resolve conflicts, generate execution tasks.
|
|
||||||
|
|
||||||
### 3.1 Load Explore Results
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const exploreCSV = parseCSV(Read(`${sessionFolder}/explore.csv`))
|
|
||||||
const completed = exploreCSV.filter(r => r.status === 'completed')
|
|
||||||
|
|
||||||
// Load detailed result JSONs where available
|
|
||||||
const detailedResults = {}
|
|
||||||
completed.forEach(r => {
|
|
||||||
const path = `${sessionFolder}/explore-results/${r.id}.json`
|
|
||||||
if (fileExists(path)) detailedResults[r.id] = JSON.parse(Read(path))
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3.2 Conflict Resolution Protocol
|
|
||||||
|
|
||||||
Cross-reference findings across all exploration angles:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// 1. Identify common files referenced by multiple angles
|
|
||||||
const fileRefs = {}
|
|
||||||
completed.forEach(r => {
|
|
||||||
r.key_files.split(';').filter(Boolean).forEach(f => {
|
|
||||||
if (!fileRefs[f]) fileRefs[f] = []
|
|
||||||
fileRefs[f].push({ angle: r.angle, id: r.id })
|
|
||||||
})
|
|
||||||
})
|
|
||||||
const sharedFiles = Object.entries(fileRefs).filter(([_, refs]) => refs.length > 1)
|
|
||||||
|
|
||||||
// 2. Detect conflicting recommendations
|
|
||||||
// Compare recommendations from different angles for same file/module
|
|
||||||
// Flag contradictions (angle A says "refactor X" vs angle B says "extend X")
|
|
||||||
|
|
||||||
// 3. Resolution rules:
|
|
||||||
// a. Safety first — when approaches conflict, choose safer option
|
|
||||||
// b. Consistency — prefer approaches aligned with existing patterns
|
|
||||||
// c. Scope — prefer minimal-change approaches
|
|
||||||
// d. Document — note all resolved conflicts for transparency
|
|
||||||
|
|
||||||
const synthesis = {
|
|
||||||
sharedFiles,
|
|
||||||
conflicts: detectConflicts(completed, detailedResults),
|
|
||||||
resolutions: [],
|
|
||||||
allKeyFiles: [...new Set(completed.flatMap(r => r.key_files.split(';').filter(Boolean)))]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3.3 Generate tasks.csv
|
|
||||||
|
|
||||||
Decompose into execution tasks based on synthesized exploration:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Task decomposition rules:
|
|
||||||
// 1. Group by feature/module (not per-file)
|
|
||||||
// 2. Each description is self-contained (agent sees only its row + prev_context)
|
|
||||||
// 3. deps only when task B requires task A's output
|
|
||||||
// 4. context_from links relevant explore rows (E*) and predecessor tasks (T*)
|
|
||||||
// 5. Prefer parallel (minimize deps)
|
|
||||||
// 6. Use exploration findings: key_files → target files, patterns → references,
|
|
||||||
// integration_points → dependency relationships, constraints → included in description
|
|
||||||
// 7. Each task MUST include: test (how to verify), acceptance_criteria (what defines done)
|
|
||||||
// 8. scope must not overlap between tasks in the same wave
|
|
||||||
// 9. hints = implementation tips + reference files (format: tips || file1;file2)
|
|
||||||
// 10. execution_directives = commands to run for verification, tool restrictions
|
|
||||||
|
|
||||||
const tasks = []
|
|
||||||
// Claude decomposes requirement using exploration synthesis
|
|
||||||
// Example:
|
|
||||||
// tasks.push({ id: 'T1', title: 'Setup types', description: '...', test: 'Verify types compile', acceptance_criteria: 'All interfaces exported', scope: 'src/types/**', hints: 'Follow existing type patterns || src/types/index.ts', execution_directives: 'tsc --noEmit', deps: '', context_from: 'E1;E2' })
|
|
||||||
// tasks.push({ id: 'T2', title: 'Implement core', description: '...', test: 'Unit test: core logic', acceptance_criteria: 'All functions pass tests', scope: 'src/core/**', hints: 'Reuse BaseService || src/services/Base.ts', execution_directives: 'npm test -- --grep core', deps: 'T1', context_from: 'E1;E2;T1' })
|
|
||||||
// tasks.push({ id: 'T3', title: 'Add tests', description: '...', test: 'Integration test suite', acceptance_criteria: '>80% coverage', scope: 'tests/**', hints: 'Follow existing test patterns || tests/auth.test.ts', execution_directives: 'npm test', deps: 'T2', context_from: 'E3;T2' })
|
|
||||||
|
|
||||||
// Compute waves
|
|
||||||
const waves = computeWaves(tasks)
|
|
||||||
tasks.forEach(t => { t.wave = waves[t.id] })
|
|
||||||
|
|
||||||
// Write tasks.csv
|
|
||||||
const header = 'id,title,description,test,acceptance_criteria,scope,hints,execution_directives,deps,context_from,wave,status,findings,files_modified,tests_passed,acceptance_met,error'
|
|
||||||
const rows = tasks.map(t =>
|
|
||||||
[t.id, escCSV(t.title), escCSV(t.description), escCSV(t.test), escCSV(t.acceptance_criteria), escCSV(t.scope), escCSV(t.hints), escCSV(t.execution_directives), t.deps, t.context_from, t.wave, 'pending', '', '', '', '', '']
|
|
||||||
.map(v => `"${v}"`).join(',')
|
|
||||||
)
|
|
||||||
|
|
||||||
Write(`${sessionFolder}/tasks.csv`, [header, ...rows].join('\n'))
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3.4 User Confirmation
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
if (!AUTO_YES) {
|
|
||||||
const maxWave = Math.max(...tasks.map(t => t.wave))
|
|
||||||
|
|
||||||
console.log(`
|
|
||||||
## Execution Plan
|
|
||||||
|
|
||||||
Explore: ${completed.length} angles completed
|
|
||||||
Conflicts resolved: ${synthesis.conflicts.length}
|
|
||||||
Tasks: ${tasks.length} across ${maxWave} waves
|
|
||||||
|
|
||||||
${Array.from({length: maxWave}, (_, i) => i + 1).map(w => {
|
|
||||||
const wt = tasks.filter(t => t.wave === w)
|
|
||||||
return `### Wave ${w} (${wt.length} tasks, concurrent)
|
|
||||||
${wt.map(t => ` - [${t.id}] ${t.title} (from: ${t.context_from})`).join('\n')}`
|
|
||||||
}).join('\n')}
|
|
||||||
`)
|
|
||||||
|
|
||||||
AskUserQuestion({
|
|
||||||
questions: [{
|
|
||||||
question: `Proceed with ${tasks.length} tasks across ${maxWave} waves?`,
|
|
||||||
header: "Confirm",
|
|
||||||
multiSelect: false,
|
|
||||||
options: [
|
|
||||||
{ label: "Execute", description: "Proceed with wave execution" },
|
|
||||||
{ label: "Modify", description: `Edit ${sessionFolder}/tasks.csv then re-run` },
|
|
||||||
{ label: "Cancel", description: "Abort" }
|
|
||||||
]
|
|
||||||
}]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Phase 4: Wave Execute
|
|
||||||
|
|
||||||
Execute tasks from tasks.csv in wave order, with prev_context built from both explore.csv and tasks.csv.
|
|
||||||
|
|
||||||
### 4.1 Wave Loop
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const exploreCSV = parseCSV(Read(`${sessionFolder}/explore.csv`))
|
|
||||||
const failedIds = new Set()
|
|
||||||
const skippedIds = new Set()
|
|
||||||
|
|
||||||
let tasksCSV = parseCSV(Read(`${sessionFolder}/tasks.csv`))
|
|
||||||
const maxWave = Math.max(...tasksCSV.map(r => parseInt(r.wave)))
|
|
||||||
|
|
||||||
for (let wave = 1; wave <= maxWave; wave++) {
|
|
||||||
// Re-read master CSV (updated by previous wave)
|
|
||||||
tasksCSV = parseCSV(Read(`${sessionFolder}/tasks.csv`))
|
|
||||||
|
|
||||||
const waveRows = tasksCSV.filter(r =>
|
|
||||||
parseInt(r.wave) === wave && r.status === 'pending'
|
|
||||||
)
|
|
||||||
if (waveRows.length === 0) continue
|
|
||||||
|
|
||||||
// Skip on failed dependencies (cascade)
|
|
||||||
const validRows = []
|
|
||||||
for (const row of waveRows) {
|
|
||||||
const deps = (row.deps || '').split(';').filter(Boolean)
|
|
||||||
if (deps.some(d => failedIds.has(d) || skippedIds.has(d))) {
|
|
||||||
skippedIds.add(row.id)
|
|
||||||
row.status = 'skipped'
|
|
||||||
row.error = 'Dependency failed/skipped'
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
validRows.push(row)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (validRows.length === 0) {
|
|
||||||
writeCSV(`${sessionFolder}/tasks.csv`, tasksCSV)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build prev_context for each row from explore.csv + tasks.csv
|
|
||||||
validRows.forEach(row => {
|
|
||||||
row._prev_context = buildPrevContext(row.context_from, exploreCSV, tasksCSV)
|
|
||||||
})
|
|
||||||
|
|
||||||
// ★ Spawn ALL task agents in SINGLE message → parallel execution
|
|
||||||
const results = validRows.map(row =>
|
|
||||||
Task({
|
|
||||||
subagent_type: "code-developer",
|
|
||||||
run_in_background: false,
|
|
||||||
description: row.title,
|
|
||||||
prompt: buildExecutePrompt(row, requirement, sessionFolder)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
// Collect results → update tasks.csv
|
|
||||||
validRows.forEach((row, i) => {
|
|
||||||
const resultPath = `${sessionFolder}/task-results/${row.id}.json`
|
|
||||||
if (fileExists(resultPath)) {
|
|
||||||
const result = JSON.parse(Read(resultPath))
|
|
||||||
row.status = result.status || 'completed'
|
|
||||||
row.findings = truncate(result.findings, 500)
|
|
||||||
row.files_modified = Array.isArray(result.files_modified)
|
|
||||||
? result.files_modified.join(';')
|
|
||||||
: (result.files_modified || '')
|
|
||||||
row.tests_passed = String(result.tests_passed ?? '')
|
|
||||||
row.acceptance_met = result.acceptance_met || ''
|
|
||||||
row.error = result.error || ''
|
|
||||||
} else {
|
|
||||||
row.status = 'completed'
|
|
||||||
row.findings = truncate(results[i], 500)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (row.status === 'failed') failedIds.add(row.id)
|
|
||||||
delete row._prev_context // runtime-only, don't persist
|
|
||||||
})
|
|
||||||
|
|
||||||
writeCSV(`${sessionFolder}/tasks.csv`, tasksCSV)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4.2 prev_context Builder
|
|
||||||
|
|
||||||
The key function linking exploration context to execution:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
function buildPrevContext(contextFrom, exploreCSV, tasksCSV) {
|
|
||||||
if (!contextFrom) return 'No previous context available'
|
|
||||||
|
|
||||||
const ids = contextFrom.split(';').filter(Boolean)
|
|
||||||
const entries = []
|
|
||||||
|
|
||||||
ids.forEach(id => {
|
|
||||||
if (id.startsWith('E')) {
|
|
||||||
// ← Look up in explore.csv (cross-phase link)
|
|
||||||
const row = exploreCSV.find(r => r.id === id)
|
|
||||||
if (row && row.status === 'completed' && row.findings) {
|
|
||||||
entries.push(`[Explore ${row.angle}] ${row.findings}`)
|
|
||||||
if (row.key_files) entries.push(` Key files: ${row.key_files}`)
|
|
||||||
}
|
|
||||||
} else if (id.startsWith('T')) {
|
|
||||||
// ← Look up in tasks.csv (same-phase link)
|
|
||||||
const row = tasksCSV.find(r => r.id === id)
|
|
||||||
if (row && row.status === 'completed' && row.findings) {
|
|
||||||
entries.push(`[Task ${row.id}: ${row.title}] ${row.findings}`)
|
|
||||||
if (row.files_modified) entries.push(` Modified: ${row.files_modified}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return entries.length > 0 ? entries.join('\n') : 'No previous context available'
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4.3 Execute Agent Prompt
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
function buildExecutePrompt(row, requirement, sessionFolder) {
|
|
||||||
return `## Task: ${row.title}
|
|
||||||
|
|
||||||
**ID**: ${row.id}
|
|
||||||
**Goal**: ${requirement}
|
|
||||||
**Scope**: ${row.scope || 'Not specified'}
|
|
||||||
|
|
||||||
## Description
|
|
||||||
${row.description}
|
|
||||||
|
|
||||||
### Implementation Hints & Reference Files
|
|
||||||
${row.hints || 'None'}
|
|
||||||
|
|
||||||
> Format: \`tips text || file1;file2\`. Read ALL reference files (after ||) before starting. Apply tips (before ||) as guidance.
|
|
||||||
|
|
||||||
### Execution Directives
|
|
||||||
${row.execution_directives || 'None'}
|
|
||||||
|
|
||||||
> Commands to run for verification, tool restrictions, or environment requirements.
|
|
||||||
|
|
||||||
### Test Cases
|
|
||||||
${row.test || 'None specified'}
|
|
||||||
|
|
||||||
### Acceptance Criteria
|
|
||||||
${row.acceptance_criteria || 'None specified'}
|
|
||||||
|
|
||||||
## Previous Context (from exploration and predecessor tasks)
|
|
||||||
${row._prev_context}
|
|
||||||
|
|
||||||
### MANDATORY FIRST STEPS
|
|
||||||
1. Read shared discoveries: ${sessionFolder}/discoveries.ndjson (if exists, skip if not)
|
|
||||||
2. Read project context: .workflow/project-tech.json (if exists)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Execution Protocol
|
|
||||||
|
|
||||||
1. **Read references**: Parse hints — read all files listed after \`||\` to understand existing patterns
|
|
||||||
2. **Read discoveries**: Load ${sessionFolder}/discoveries.ndjson for shared exploration findings
|
|
||||||
3. **Use context**: Apply previous tasks' findings from prev_context above
|
|
||||||
4. **Stay in scope**: ONLY create/modify files within ${row.scope || 'project'} — do NOT touch files outside this boundary
|
|
||||||
5. **Apply hints**: Follow implementation tips from hints (before \`||\`)
|
|
||||||
6. **Implement**: Execute changes described in the task description
|
|
||||||
7. **Write tests**: Implement the test cases defined above
|
|
||||||
8. **Run directives**: Execute commands from execution_directives to verify your work
|
|
||||||
9. **Verify acceptance**: Ensure all acceptance criteria are met before reporting completion
|
|
||||||
10. **Share discoveries**: Append exploration findings to shared board:
|
|
||||||
\\\`\\\`\\\`bash
|
|
||||||
echo '{"ts":"<ISO>","worker":"${row.id}","type":"<type>","data":{...}}' >> ${sessionFolder}/discoveries.ndjson
|
|
||||||
\\\`\\\`\\\`
|
|
||||||
11. **Report result**: Write JSON to output file
|
|
||||||
|
|
||||||
## Output
|
|
||||||
Write results to: ${sessionFolder}/task-results/${row.id}.json
|
|
||||||
|
|
||||||
{
|
|
||||||
"status": "completed" | "failed",
|
|
||||||
"findings": "What was done (max 500 chars)",
|
|
||||||
"files_modified": ["file1.ts", "file2.ts"],
|
|
||||||
"tests_passed": true | false,
|
|
||||||
"acceptance_met": "Summary of which acceptance criteria were met/unmet",
|
|
||||||
"error": ""
|
|
||||||
}
|
|
||||||
|
|
||||||
**IMPORTANT**: Set status to "completed" ONLY if:
|
|
||||||
- All test cases pass
|
|
||||||
- All acceptance criteria are met
|
|
||||||
Otherwise set status to "failed" with details in error field.`
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Phase 5: Aggregate
|
|
||||||
|
|
||||||
### 5.1 Generate Results
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const finalTasks = parseCSV(Read(`${sessionFolder}/tasks.csv`))
|
|
||||||
const exploreCSV = parseCSV(Read(`${sessionFolder}/explore.csv`))
|
|
||||||
|
|
||||||
Bash(`cp "${sessionFolder}/tasks.csv" "${sessionFolder}/results.csv"`)
|
|
||||||
|
|
||||||
const completed = finalTasks.filter(r => r.status === 'completed')
|
|
||||||
const failed = finalTasks.filter(r => r.status === 'failed')
|
|
||||||
const skipped = finalTasks.filter(r => r.status === 'skipped')
|
|
||||||
const maxWave = Math.max(...finalTasks.map(r => parseInt(r.wave)))
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5.2 Generate context.md
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const contextMd = `# Wave Plan Results
|
|
||||||
|
|
||||||
**Requirement**: ${requirement}
|
|
||||||
**Session**: ${sessionId}
|
|
||||||
**Timestamp**: ${getUtc8ISOString()}
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
| Metric | Count |
|
|
||||||
|--------|-------|
|
|
||||||
| Explore Angles | ${exploreCSV.length} |
|
|
||||||
| Total Tasks | ${finalTasks.length} |
|
|
||||||
| Completed | ${completed.length} |
|
|
||||||
| Failed | ${failed.length} |
|
|
||||||
| Skipped | ${skipped.length} |
|
|
||||||
| Waves | ${maxWave} |
|
|
||||||
|
|
||||||
## Exploration Results
|
|
||||||
|
|
||||||
${exploreCSV.map(e => `### ${e.id}: ${e.angle} (${e.status})
|
|
||||||
${e.findings || 'N/A'}
|
|
||||||
Key files: ${e.key_files || 'none'}`).join('\n\n')}
|
|
||||||
|
|
||||||
## Task Results
|
|
||||||
|
|
||||||
${finalTasks.map(t => `### ${t.id}: ${t.title} (${t.status})
|
|
||||||
|
|
||||||
| Field | Value |
|
|
||||||
|-------|-------|
|
|
||||||
| Wave | ${t.wave} |
|
|
||||||
| Scope | ${t.scope || 'none'} |
|
|
||||||
| Dependencies | ${t.deps || 'none'} |
|
|
||||||
| Context From | ${t.context_from || 'none'} |
|
|
||||||
| Tests Passed | ${t.tests_passed || 'N/A'} |
|
|
||||||
| Acceptance Met | ${t.acceptance_met || 'N/A'} |
|
|
||||||
| Error | ${t.error || 'none'} |
|
|
||||||
|
|
||||||
**Description**: ${t.description}
|
|
||||||
|
|
||||||
**Test Cases**: ${t.test || 'N/A'}
|
|
||||||
|
|
||||||
**Acceptance Criteria**: ${t.acceptance_criteria || 'N/A'}
|
|
||||||
|
|
||||||
**Hints**: ${t.hints || 'N/A'}
|
|
||||||
|
|
||||||
**Execution Directives**: ${t.execution_directives || 'N/A'}
|
|
||||||
|
|
||||||
**Findings**: ${t.findings || 'N/A'}
|
|
||||||
|
|
||||||
**Files Modified**: ${t.files_modified || 'none'}`).join('\n\n---\n\n')}
|
|
||||||
|
|
||||||
## All Modified Files
|
|
||||||
|
|
||||||
${[...new Set(finalTasks.flatMap(t =>
|
|
||||||
(t.files_modified || '').split(';')).filter(Boolean)
|
|
||||||
)].map(f => '- ' + f).join('\n') || 'None'}
|
|
||||||
`
|
|
||||||
|
|
||||||
Write(`${sessionFolder}/context.md`, contextMd)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5.3 Summary & Next Steps
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
console.log(`
|
|
||||||
## Wave Plan Complete
|
|
||||||
|
|
||||||
Session: ${sessionFolder}
|
|
||||||
Explore: ${exploreCSV.filter(r => r.status === 'completed').length}/${exploreCSV.length} angles
|
|
||||||
Tasks: ${completed.length}/${finalTasks.length} completed, ${failed.length} failed, ${skipped.length} skipped
|
|
||||||
Waves: ${maxWave}
|
|
||||||
|
|
||||||
Files:
|
|
||||||
- explore.csv — exploration state
|
|
||||||
- tasks.csv — execution state
|
|
||||||
- results.csv — final results
|
|
||||||
- context.md — full report
|
|
||||||
- discoveries.ndjson — shared discoveries
|
|
||||||
`)
|
|
||||||
|
|
||||||
if (!AUTO_YES && failed.length > 0) {
|
|
||||||
AskUserQuestion({
|
|
||||||
questions: [{
|
|
||||||
question: `${failed.length} tasks failed. Next action?`,
|
|
||||||
header: "Next Step",
|
|
||||||
multiSelect: false,
|
|
||||||
options: [
|
|
||||||
{ label: "Retry Failed", description: "Reset failed + skipped, re-execute Phase 4" },
|
|
||||||
{ label: "View Report", description: "Display context.md" },
|
|
||||||
{ label: "Done", description: "Complete session" }
|
|
||||||
]
|
|
||||||
}]
|
|
||||||
})
|
|
||||||
// If Retry: reset failed/skipped status to pending, re-run Phase 4
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Utilities
|
|
||||||
|
|
||||||
### Wave Computation (Kahn's BFS)
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
function computeWaves(tasks) {
|
|
||||||
const inDegree = {}, adj = {}, depth = {}
|
|
||||||
tasks.forEach(t => { inDegree[t.id] = 0; adj[t.id] = []; depth[t.id] = 1 })
|
|
||||||
|
|
||||||
tasks.forEach(t => {
|
|
||||||
const deps = (t.deps || '').split(';').filter(Boolean)
|
|
||||||
deps.forEach(dep => {
|
|
||||||
if (adj[dep]) { adj[dep].push(t.id); inDegree[t.id]++ }
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const queue = Object.keys(inDegree).filter(id => inDegree[id] === 0)
|
|
||||||
queue.forEach(id => { depth[id] = 1 })
|
|
||||||
|
|
||||||
while (queue.length > 0) {
|
|
||||||
const current = queue.shift()
|
|
||||||
adj[current].forEach(next => {
|
|
||||||
depth[next] = Math.max(depth[next], depth[current] + 1)
|
|
||||||
inDegree[next]--
|
|
||||||
if (inDegree[next] === 0) queue.push(next)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Object.values(inDegree).some(d => d > 0)) {
|
|
||||||
throw new Error('Circular dependency detected')
|
|
||||||
}
|
|
||||||
|
|
||||||
return depth // { taskId: waveNumber }
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### CSV Helpers
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
function escCSV(s) { return String(s || '').replace(/"/g, '""') }
|
|
||||||
|
|
||||||
function parseCSV(content) {
|
|
||||||
const lines = content.trim().split('\n')
|
|
||||||
const header = lines[0].split(',').map(h => h.replace(/"/g, '').trim())
|
|
||||||
return lines.slice(1).filter(l => l.trim()).map(line => {
|
|
||||||
const values = parseCSVLine(line)
|
|
||||||
const row = {}
|
|
||||||
header.forEach((col, i) => { row[col] = (values[i] || '').replace(/^"|"$/g, '') })
|
|
||||||
return row
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function writeCSV(path, rows) {
|
|
||||||
if (rows.length === 0) return
|
|
||||||
// Exclude runtime-only columns (prefixed with _)
|
|
||||||
const cols = Object.keys(rows[0]).filter(k => !k.startsWith('_'))
|
|
||||||
const header = cols.join(',')
|
|
||||||
const lines = rows.map(r =>
|
|
||||||
cols.map(c => `"${escCSV(r[c])}"`).join(',')
|
|
||||||
)
|
|
||||||
Write(path, [header, ...lines].join('\n'))
|
|
||||||
}
|
|
||||||
|
|
||||||
function truncate(s, max) {
|
|
||||||
s = String(s || '')
|
|
||||||
return s.length > max ? s.substring(0, max - 3) + '...' : s
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Discovery Board Protocol
|
|
||||||
|
|
||||||
Shared `discoveries.ndjson` — append-only NDJSON accessible to all agents across all phases.
|
|
||||||
|
|
||||||
**Lifecycle**:
|
|
||||||
- Created by the first agent to write a discovery
|
|
||||||
- Carries over across all phases and waves — never cleared
|
|
||||||
- Agents append via `echo '...' >> discoveries.ndjson`
|
|
||||||
|
|
||||||
**Format**: NDJSON, each line is a self-contained JSON:
|
|
||||||
|
|
||||||
```jsonl
|
|
||||||
{"ts":"...","worker":"E1","type":"code_pattern","data":{"name":"repo-pattern","file":"src/repos/Base.ts"}}
|
|
||||||
{"ts":"...","worker":"T2","type":"integration_point","data":{"file":"src/auth/index.ts","exports":["auth"]}}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Discovery Types**:
|
|
||||||
|
|
||||||
| type | Dedup Key | Description |
|
|
||||||
|------|-----------|-------------|
|
|
||||||
| `code_pattern` | `data.name` | Reusable code pattern found |
|
|
||||||
| `integration_point` | `data.file` | Module connection point |
|
|
||||||
| `convention` | singleton | Code style conventions |
|
|
||||||
| `blocker` | `data.issue` | Blocking issue encountered |
|
|
||||||
| `tech_stack` | singleton | Project technology stack |
|
|
||||||
| `test_command` | singleton | Test commands discovered |
|
|
||||||
|
|
||||||
**Protocol Rules**:
|
|
||||||
1. Read board before own exploration → skip covered areas
|
|
||||||
2. Write discoveries immediately via `echo >>` → don't batch
|
|
||||||
3. Deduplicate — check existing entries; skip if same type + dedup key exists
|
|
||||||
4. Append-only — never modify or delete existing lines
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Error Handling
|
|
||||||
|
|
||||||
| Error | Resolution |
|
|
||||||
|-------|------------|
|
|
||||||
| Explore agent failure | Mark as failed in explore.csv, exclude from planning |
|
|
||||||
| All explores failed | Fallback: plan directly from requirement without exploration |
|
|
||||||
| Execute agent failure | Mark as failed, skip dependents (cascade) |
|
|
||||||
| Agent timeout | Mark as failed in results, continue with wave |
|
|
||||||
| Circular dependency | Abort wave computation, report cycle |
|
|
||||||
| CSV parse error | Validate CSV format before execution, show line number |
|
|
||||||
| discoveries.ndjson corrupt | Ignore malformed lines, continue with valid entries |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Core Rules
|
|
||||||
|
|
||||||
1. **Wave Order is Sacred**: Never execute wave N before wave N-1 completes
|
|
||||||
2. **CSV is Source of Truth**: Read master CSV before each wave, write after
|
|
||||||
3. **Context via CSV**: prev_context built from CSV findings, not from memory
|
|
||||||
4. **E* ↔ T* Linking**: tasks.csv `context_from` references explore.csv rows for cross-phase context
|
|
||||||
5. **Skip on Failure**: Failed dep → skip dependent (cascade)
|
|
||||||
6. **Discovery Board Append-Only**: Never clear or modify discoveries.ndjson
|
|
||||||
7. **Explore Before Execute**: Phase 2 completes before Phase 4 starts
|
|
||||||
8. **DO NOT STOP**: Continuous execution until all waves complete or remaining skipped
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
1. **Exploration Angles**: 1 for simple, 3-4 for complex; avoid redundant angles
|
|
||||||
2. **Context Linking**: Link every task to at least one explore row (E*) — exploration was done for a reason
|
|
||||||
3. **Task Granularity**: 3-10 tasks optimal; too many = overhead, too few = no parallelism
|
|
||||||
4. **Minimize Cross-Wave Deps**: More tasks in wave 1 = more parallelism
|
|
||||||
5. **Specific Descriptions**: Agent sees only its CSV row + prev_context — make description self-contained
|
|
||||||
6. **Non-Overlapping Scopes**: Same-wave tasks must not write to the same files
|
|
||||||
7. **Context From ≠ Deps**: `deps` = execution order constraint; `context_from` = information flow
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Usage Recommendations
|
|
||||||
|
|
||||||
| Scenario | Recommended Approach |
|
|
||||||
|----------|---------------------|
|
|
||||||
| Complex feature (unclear architecture) | `workflow:wave-plan` — explore first, then plan |
|
|
||||||
| Simple known-pattern task | `$csv-wave-pipeline` — skip exploration, direct execution |
|
|
||||||
| Independent parallel tasks | `$csv-wave-pipeline -c 8` — single wave, max parallelism |
|
|
||||||
| Diamond dependency (A→B,C→D) | `workflow:wave-plan` — 3 waves with context propagation |
|
|
||||||
| Unknown codebase | `workflow:wave-plan` — exploration phase is essential |
|
|
||||||
@@ -104,41 +104,131 @@ export interface ApiError {
|
|||||||
// ========== CSRF Token Handling ==========
|
// ========== CSRF Token Handling ==========
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* In-memory CSRF token storage
|
* CSRF token pool for concurrent request support
|
||||||
* The token is obtained from X-CSRF-Token response header and stored here
|
* The pool maintains multiple tokens to support parallel mutating requests
|
||||||
* because the XSRF-TOKEN cookie is HttpOnly and cannot be read by JavaScript
|
|
||||||
*/
|
*/
|
||||||
let csrfToken: string | null = null;
|
const MAX_CSRF_TOKEN_POOL_SIZE = 5;
|
||||||
|
|
||||||
|
// Token pool queue - FIFO for fair distribution
|
||||||
|
let csrfTokenQueue: string[] = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get CSRF token from memory
|
* Get a CSRF token from the pool
|
||||||
|
* @returns Token string or undefined if pool is empty
|
||||||
*/
|
*/
|
||||||
function getCsrfToken(): string | null {
|
function getCsrfTokenFromPool(): string | undefined {
|
||||||
return csrfToken;
|
return csrfTokenQueue.shift();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set CSRF token from response header
|
* Add a CSRF token to the pool with deduplication
|
||||||
|
* @param token - Token to add
|
||||||
|
*/
|
||||||
|
function addCsrfTokenToPool(token: string): void {
|
||||||
|
if (!token) return;
|
||||||
|
// Deduplication: don't add if already in pool
|
||||||
|
if (csrfTokenQueue.includes(token)) return;
|
||||||
|
// Limit pool size
|
||||||
|
if (csrfTokenQueue.length >= MAX_CSRF_TOKEN_POOL_SIZE) return;
|
||||||
|
csrfTokenQueue.push(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current pool size (for debugging)
|
||||||
|
*/
|
||||||
|
export function getCsrfPoolSize(): number {
|
||||||
|
return csrfTokenQueue.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lock for deduplicating concurrent token fetch requests
|
||||||
|
* Prevents multiple simultaneous calls to fetchTokenSynchronously
|
||||||
|
*/
|
||||||
|
let tokenFetchPromise: Promise<string> | null = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronously fetch a single token when pool is depleted
|
||||||
|
* This blocks the request until a token is available
|
||||||
|
* Uses lock mechanism to prevent concurrent fetch deduplication
|
||||||
|
*/
|
||||||
|
async function fetchTokenSynchronously(): Promise<string> {
|
||||||
|
// If a fetch is already in progress, wait for it
|
||||||
|
if (tokenFetchPromise) {
|
||||||
|
return tokenFetchPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new fetch promise and store as lock
|
||||||
|
tokenFetchPromise = (async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/csrf-token', {
|
||||||
|
credentials: 'same-origin',
|
||||||
|
});
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Failed to fetch CSRF token');
|
||||||
|
}
|
||||||
|
const data = await response.json();
|
||||||
|
const token = data.csrfToken;
|
||||||
|
if (!token) {
|
||||||
|
throw new Error('No CSRF token in response');
|
||||||
|
}
|
||||||
|
return token;
|
||||||
|
} finally {
|
||||||
|
// Release lock after completion (success or failure)
|
||||||
|
tokenFetchPromise = null;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
return tokenFetchPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set CSRF token from response header (adds to pool)
|
||||||
*/
|
*/
|
||||||
function updateCsrfToken(response: Response): void {
|
function updateCsrfToken(response: Response): void {
|
||||||
const token = response.headers.get('X-CSRF-Token');
|
const token = response.headers.get('X-CSRF-Token');
|
||||||
if (token) {
|
if (token) {
|
||||||
csrfToken = token;
|
addCsrfTokenToPool(token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize CSRF token by fetching from server
|
* Initialize CSRF token pool by fetching multiple tokens from server
|
||||||
* Should be called once on app initialization
|
* Should be called once on app initialization
|
||||||
*/
|
*/
|
||||||
export async function initializeCsrfToken(): Promise<void> {
|
export async function initializeCsrfToken(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/csrf-token', {
|
// Prefetch 5 tokens for pool
|
||||||
|
const response = await fetch(`/api/csrf-token?count=${MAX_CSRF_TOKEN_POOL_SIZE}`, {
|
||||||
credentials: 'same-origin',
|
credentials: 'same-origin',
|
||||||
});
|
});
|
||||||
updateCsrfToken(response);
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Failed to initialize CSRF token pool');
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
// Handle both single token and batch response formats
|
||||||
|
if (data.tokens && Array.isArray(data.tokens)) {
|
||||||
|
// Batch response - add all tokens to pool
|
||||||
|
for (const token of data.tokens) {
|
||||||
|
addCsrfTokenToPool(token);
|
||||||
|
}
|
||||||
|
} else if (data.csrfToken) {
|
||||||
|
// Single token response - add to pool
|
||||||
|
addCsrfTokenToPool(data.csrfToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[CSRF] Token pool initialized with ${csrfTokenQueue.length} tokens`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[CSRF] Failed to initialize CSRF token:', error);
|
console.error('[CSRF] Failed to initialize CSRF token pool:', error);
|
||||||
|
// Fallback: try to get at least one token
|
||||||
|
try {
|
||||||
|
const token = await fetchTokenSynchronously();
|
||||||
|
addCsrfTokenToPool(token);
|
||||||
|
} catch (fallbackError) {
|
||||||
|
console.error('[CSRF] Fallback token fetch failed:', fallbackError);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,7 +245,18 @@ async function fetchApi<T>(
|
|||||||
|
|
||||||
// Add CSRF token for mutating requests
|
// Add CSRF token for mutating requests
|
||||||
if (options.method && ['POST', 'PUT', 'PATCH', 'DELETE'].includes(options.method)) {
|
if (options.method && ['POST', 'PUT', 'PATCH', 'DELETE'].includes(options.method)) {
|
||||||
const token = getCsrfToken();
|
let token = getCsrfTokenFromPool();
|
||||||
|
|
||||||
|
// If pool is depleted, synchronously fetch a new token
|
||||||
|
if (!token) {
|
||||||
|
console.warn('[CSRF] Token pool depleted, fetching synchronously');
|
||||||
|
try {
|
||||||
|
token = await fetchTokenSynchronously();
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error('Failed to acquire CSRF token for request');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (token) {
|
if (token) {
|
||||||
headers.set('X-CSRF-Token', token);
|
headers.set('X-CSRF-Token', token);
|
||||||
}
|
}
|
||||||
@@ -172,7 +273,7 @@ async function fetchApi<T>(
|
|||||||
credentials: 'same-origin',
|
credentials: 'same-origin',
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update CSRF token from response header
|
// Update CSRF token from response header (adds to pool)
|
||||||
updateCsrfToken(response);
|
updateCsrfToken(response);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@@ -2963,6 +3064,18 @@ export interface ReviewDimension {
|
|||||||
findings: ReviewFinding[];
|
findings: ReviewFinding[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ReviewSummary {
|
||||||
|
phase?: string;
|
||||||
|
status?: string;
|
||||||
|
severityDistribution?: {
|
||||||
|
critical: number;
|
||||||
|
high: number;
|
||||||
|
medium: number;
|
||||||
|
low: number;
|
||||||
|
};
|
||||||
|
criticalFiles?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface ReviewSession {
|
export interface ReviewSession {
|
||||||
session_id: string;
|
session_id: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
@@ -2970,6 +3083,7 @@ export interface ReviewSession {
|
|||||||
type: 'review';
|
type: 'review';
|
||||||
phase?: string;
|
phase?: string;
|
||||||
reviewDimensions?: ReviewDimension[];
|
reviewDimensions?: ReviewDimension[];
|
||||||
|
reviewSummary?: ReviewSummary;
|
||||||
_isActive?: boolean;
|
_isActive?: boolean;
|
||||||
created_at?: string;
|
created_at?: string;
|
||||||
updated_at?: string;
|
updated_at?: string;
|
||||||
@@ -2986,6 +3100,17 @@ export interface ReviewSessionsResponse {
|
|||||||
progress?: unknown;
|
progress?: unknown;
|
||||||
}>;
|
}>;
|
||||||
};
|
};
|
||||||
|
// New: Support activeSessions with review type
|
||||||
|
activeSessions?: Array<{
|
||||||
|
session_id: string;
|
||||||
|
project?: string;
|
||||||
|
type?: string;
|
||||||
|
status?: string;
|
||||||
|
created_at?: string;
|
||||||
|
hasReview?: boolean;
|
||||||
|
reviewSummary?: ReviewSummary;
|
||||||
|
reviewDimensions?: ReviewDimension[];
|
||||||
|
}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2994,12 +3119,34 @@ export interface ReviewSessionsResponse {
|
|||||||
export async function fetchReviewSessions(): Promise<ReviewSession[]> {
|
export async function fetchReviewSessions(): Promise<ReviewSession[]> {
|
||||||
const data = await fetchApi<ReviewSessionsResponse>('/api/data');
|
const data = await fetchApi<ReviewSessionsResponse>('/api/data');
|
||||||
|
|
||||||
// If reviewSessions field exists (legacy format), use it
|
// Priority 1: Use activeSessions with type='review' or hasReview=true
|
||||||
|
if (data.activeSessions) {
|
||||||
|
const reviewSessions = data.activeSessions.filter(
|
||||||
|
session => session.type === 'review' || session.hasReview
|
||||||
|
);
|
||||||
|
if (reviewSessions.length > 0) {
|
||||||
|
return reviewSessions.map(session => ({
|
||||||
|
session_id: session.session_id,
|
||||||
|
title: session.project || session.session_id,
|
||||||
|
description: '',
|
||||||
|
type: 'review' as const,
|
||||||
|
phase: session.reviewSummary?.phase,
|
||||||
|
reviewDimensions: session.reviewDimensions || [],
|
||||||
|
reviewSummary: session.reviewSummary,
|
||||||
|
_isActive: true,
|
||||||
|
created_at: session.created_at,
|
||||||
|
updated_at: undefined,
|
||||||
|
status: session.status
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Priority 2: Legacy reviewSessions field
|
||||||
if (data.reviewSessions && data.reviewSessions.length > 0) {
|
if (data.reviewSessions && data.reviewSessions.length > 0) {
|
||||||
return data.reviewSessions;
|
return data.reviewSessions;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, transform reviewData.sessions into ReviewSession format
|
// Priority 3: Legacy reviewData.sessions format
|
||||||
if (data.reviewData?.sessions) {
|
if (data.reviewData?.sessions) {
|
||||||
return data.reviewData.sessions.map(session => ({
|
return data.reviewData.sessions.map(session => ({
|
||||||
session_id: session.session_id,
|
session_id: session.session_id,
|
||||||
|
|||||||
@@ -96,6 +96,11 @@
|
|||||||
"message": "Try adjusting your filters or search query.",
|
"message": "Try adjusting your filters or search query.",
|
||||||
"noFixProgress": "No fix progress data available"
|
"noFixProgress": "No fix progress data available"
|
||||||
},
|
},
|
||||||
|
"notExecuted": {
|
||||||
|
"title": "Review Not Yet Executed",
|
||||||
|
"message": "This review session has been created but the review process has not been started yet. No findings have been generated.",
|
||||||
|
"hint": "💡 Tip: Execute the review workflow to start analyzing code and generate findings."
|
||||||
|
},
|
||||||
"notFound": {
|
"notFound": {
|
||||||
"title": "Review Session Not Found",
|
"title": "Review Session Not Found",
|
||||||
"message": "The requested review session could not be found."
|
"message": "The requested review session could not be found."
|
||||||
|
|||||||
@@ -96,6 +96,11 @@
|
|||||||
"message": "尝试调整筛选条件或搜索查询。",
|
"message": "尝试调整筛选条件或搜索查询。",
|
||||||
"noFixProgress": "无修复进度数据"
|
"noFixProgress": "无修复进度数据"
|
||||||
},
|
},
|
||||||
|
"notExecuted": {
|
||||||
|
"title": "审查尚未执行",
|
||||||
|
"message": "此审查会话已创建,但审查流程尚未启动。尚未生成任何发现结果。",
|
||||||
|
"hint": "💡 提示:请执行审查工作流以开始分析代码并生成发现结果。"
|
||||||
|
},
|
||||||
"notFound": {
|
"notFound": {
|
||||||
"title": "未找到审查会话",
|
"title": "未找到审查会话",
|
||||||
"message": "无法找到请求的审查会话。"
|
"message": "无法找到请求的审查会话。"
|
||||||
|
|||||||
@@ -765,13 +765,32 @@ export function ReviewSessionPage() {
|
|||||||
{filteredFindings.length === 0 ? (
|
{filteredFindings.length === 0 ? (
|
||||||
<Card>
|
<Card>
|
||||||
<CardContent className="p-12 text-center">
|
<CardContent className="p-12 text-center">
|
||||||
<FileText className="h-12 w-12 text-muted-foreground mx-auto mb-4" />
|
{/* Check if review hasn't been executed yet */}
|
||||||
<h3 className="text-lg font-medium text-foreground mb-2">
|
{reviewSession?.reviewSummary?.status === 'in_progress' &&
|
||||||
{formatMessage({ id: 'reviewSession.empty.title' })}
|
(!reviewSession?.reviewDimensions || reviewSession.reviewDimensions.length === 0) ? (
|
||||||
</h3>
|
<>
|
||||||
<p className="text-sm text-muted-foreground">
|
<AlertTriangle className="h-12 w-12 text-amber-500 mx-auto mb-4" />
|
||||||
{formatMessage({ id: 'reviewSession.empty.message' })}
|
<h3 className="text-lg font-medium text-foreground mb-2">
|
||||||
</p>
|
{formatMessage({ id: 'reviewSession.notExecuted.title' })}
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm text-muted-foreground mb-4">
|
||||||
|
{formatMessage({ id: 'reviewSession.notExecuted.message' })}
|
||||||
|
</p>
|
||||||
|
<div className="text-xs text-muted-foreground bg-muted p-3 rounded-lg inline-block">
|
||||||
|
{formatMessage({ id: 'reviewSession.notExecuted.hint' })}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<FileText className="h-12 w-12 text-muted-foreground mx-auto mb-4" />
|
||||||
|
<h3 className="text-lg font-medium text-foreground mb-2">
|
||||||
|
{formatMessage({ id: 'reviewSession.empty.title' })}
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
{formatMessage({ id: 'reviewSession.empty.message' })}
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { randomBytes } from 'crypto';
|
|||||||
export interface CsrfTokenManagerOptions {
|
export interface CsrfTokenManagerOptions {
|
||||||
tokenTtlMs?: number;
|
tokenTtlMs?: number;
|
||||||
cleanupIntervalMs?: number;
|
cleanupIntervalMs?: number;
|
||||||
|
maxTokensPerSession?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
type CsrfTokenRecord = {
|
type CsrfTokenRecord = {
|
||||||
@@ -13,14 +14,20 @@ type CsrfTokenRecord = {
|
|||||||
|
|
||||||
const DEFAULT_TOKEN_TTL_MS = 15 * 60 * 1000; // 15 minutes
|
const DEFAULT_TOKEN_TTL_MS = 15 * 60 * 1000; // 15 minutes
|
||||||
const DEFAULT_CLEANUP_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes
|
const DEFAULT_CLEANUP_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes
|
||||||
|
const DEFAULT_MAX_TOKENS_PER_SESSION = 5;
|
||||||
|
|
||||||
export class CsrfTokenManager {
|
export class CsrfTokenManager {
|
||||||
private readonly tokenTtlMs: number;
|
private readonly tokenTtlMs: number;
|
||||||
private readonly records = new Map<string, CsrfTokenRecord>();
|
private readonly maxTokensPerSession: number;
|
||||||
|
// sessionId -> (token -> record) - supports multiple tokens per session
|
||||||
|
private readonly sessionTokens = new Map<string, Map<string, CsrfTokenRecord>>();
|
||||||
|
// Quick lookup: token -> sessionId for validation
|
||||||
|
private readonly tokenToSession = new Map<string, string>();
|
||||||
private readonly cleanupTimer: NodeJS.Timeout | null;
|
private readonly cleanupTimer: NodeJS.Timeout | null;
|
||||||
|
|
||||||
constructor(options: CsrfTokenManagerOptions = {}) {
|
constructor(options: CsrfTokenManagerOptions = {}) {
|
||||||
this.tokenTtlMs = options.tokenTtlMs ?? DEFAULT_TOKEN_TTL_MS;
|
this.tokenTtlMs = options.tokenTtlMs ?? DEFAULT_TOKEN_TTL_MS;
|
||||||
|
this.maxTokensPerSession = options.maxTokensPerSession ?? DEFAULT_MAX_TOKENS_PER_SESSION;
|
||||||
|
|
||||||
const cleanupIntervalMs = options.cleanupIntervalMs ?? DEFAULT_CLEANUP_INTERVAL_MS;
|
const cleanupIntervalMs = options.cleanupIntervalMs ?? DEFAULT_CLEANUP_INTERVAL_MS;
|
||||||
if (cleanupIntervalMs > 0) {
|
if (cleanupIntervalMs > 0) {
|
||||||
@@ -40,50 +47,137 @@ export class CsrfTokenManager {
|
|||||||
if (this.cleanupTimer) {
|
if (this.cleanupTimer) {
|
||||||
clearInterval(this.cleanupTimer);
|
clearInterval(this.cleanupTimer);
|
||||||
}
|
}
|
||||||
this.records.clear();
|
this.sessionTokens.clear();
|
||||||
|
this.tokenToSession.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a single CSRF token for a session
|
||||||
|
*/
|
||||||
generateToken(sessionId: string): string {
|
generateToken(sessionId: string): string {
|
||||||
const token = randomBytes(32).toString('hex');
|
const tokens = this.generateTokens(sessionId, 1);
|
||||||
this.records.set(token, {
|
return tokens[0];
|
||||||
sessionId,
|
|
||||||
expiresAtMs: Date.now() + this.tokenTtlMs,
|
|
||||||
used: false,
|
|
||||||
});
|
|
||||||
return token;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate multiple CSRF tokens for a session (pool pattern)
|
||||||
|
* @param sessionId - Session identifier
|
||||||
|
* @param count - Number of tokens to generate (max: maxTokensPerSession)
|
||||||
|
* @returns Array of generated tokens
|
||||||
|
*/
|
||||||
|
generateTokens(sessionId: string, count: number): string[] {
|
||||||
|
// Get or create session token map
|
||||||
|
let sessionMap = this.sessionTokens.get(sessionId);
|
||||||
|
if (!sessionMap) {
|
||||||
|
sessionMap = new Map();
|
||||||
|
this.sessionTokens.set(sessionId, sessionMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limit count to max tokens per session
|
||||||
|
const currentCount = sessionMap.size;
|
||||||
|
const availableSlots = Math.max(0, this.maxTokensPerSession - currentCount);
|
||||||
|
const tokensToGenerate = Math.min(count, availableSlots);
|
||||||
|
|
||||||
|
const tokens: string[] = [];
|
||||||
|
const expiresAtMs = Date.now() + this.tokenTtlMs;
|
||||||
|
|
||||||
|
for (let i = 0; i < tokensToGenerate; i++) {
|
||||||
|
const token = randomBytes(32).toString('hex');
|
||||||
|
const record: CsrfTokenRecord = {
|
||||||
|
sessionId,
|
||||||
|
expiresAtMs,
|
||||||
|
used: false,
|
||||||
|
};
|
||||||
|
sessionMap.set(token, record);
|
||||||
|
this.tokenToSession.set(token, sessionId);
|
||||||
|
tokens.push(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate a CSRF token against a session
|
||||||
|
* Marks token as used (single-use) on successful validation
|
||||||
|
*/
|
||||||
validateToken(token: string, sessionId: string): boolean {
|
validateToken(token: string, sessionId: string): boolean {
|
||||||
const record = this.records.get(token);
|
// Quick lookup: get session from token
|
||||||
|
const tokenSessionId = this.tokenToSession.get(token);
|
||||||
|
if (!tokenSessionId) return false;
|
||||||
|
if (tokenSessionId !== sessionId) return false;
|
||||||
|
|
||||||
|
// Get session's token map
|
||||||
|
const sessionMap = this.sessionTokens.get(sessionId);
|
||||||
|
if (!sessionMap) return false;
|
||||||
|
|
||||||
|
// Get token record
|
||||||
|
const record = sessionMap.get(token);
|
||||||
if (!record) return false;
|
if (!record) return false;
|
||||||
if (record.used) return false;
|
if (record.used) return false;
|
||||||
if (record.sessionId !== sessionId) return false;
|
|
||||||
|
|
||||||
|
// Check expiration
|
||||||
if (Date.now() > record.expiresAtMs) {
|
if (Date.now() > record.expiresAtMs) {
|
||||||
this.records.delete(token);
|
this.removeToken(token, sessionId);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mark as used (single-use enforcement)
|
||||||
record.used = true;
|
record.used = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a token from the pool
|
||||||
|
*/
|
||||||
|
private removeToken(token: string, sessionId: string): void {
|
||||||
|
const sessionMap = this.sessionTokens.get(sessionId);
|
||||||
|
if (sessionMap) {
|
||||||
|
sessionMap.delete(token);
|
||||||
|
// Clean up empty session maps
|
||||||
|
if (sessionMap.size === 0) {
|
||||||
|
this.sessionTokens.delete(sessionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.tokenToSession.delete(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the number of active tokens for a session
|
||||||
|
*/
|
||||||
|
getTokenCount(sessionId: string): number {
|
||||||
|
const sessionMap = this.sessionTokens.get(sessionId);
|
||||||
|
return sessionMap ? sessionMap.size : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get total number of active tokens across all sessions
|
||||||
|
*/
|
||||||
|
getActiveTokenCount(): number {
|
||||||
|
return this.tokenToSession.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clean up expired and used tokens
|
||||||
|
*/
|
||||||
cleanupExpiredTokens(nowMs: number = Date.now()): number {
|
cleanupExpiredTokens(nowMs: number = Date.now()): number {
|
||||||
let removed = 0;
|
let removed = 0;
|
||||||
|
|
||||||
for (const [token, record] of this.records.entries()) {
|
for (const [sessionId, sessionMap] of this.sessionTokens.entries()) {
|
||||||
if (record.used || nowMs > record.expiresAtMs) {
|
for (const [token, record] of sessionMap.entries()) {
|
||||||
this.records.delete(token);
|
if (record.used || nowMs > record.expiresAtMs) {
|
||||||
removed += 1;
|
sessionMap.delete(token);
|
||||||
|
this.tokenToSession.delete(token);
|
||||||
|
removed += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Clean up empty session maps
|
||||||
|
if (sessionMap.size === 0) {
|
||||||
|
this.sessionTokens.delete(sessionId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return removed;
|
return removed;
|
||||||
}
|
}
|
||||||
|
|
||||||
getActiveTokenCount(): number {
|
|
||||||
return this.records.size;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let csrfManagerInstance: CsrfTokenManager | null = null;
|
let csrfManagerInstance: CsrfTokenManager | null = null;
|
||||||
|
|||||||
@@ -79,17 +79,37 @@ function setCsrfCookie(res: ServerResponse, token: string, maxAgeSeconds: number
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function handleAuthRoutes(ctx: RouteContext): Promise<boolean> {
|
export async function handleAuthRoutes(ctx: RouteContext): Promise<boolean> {
|
||||||
const { pathname, req, res } = ctx;
|
const { pathname, req, res, url } = ctx;
|
||||||
|
|
||||||
if (pathname === '/api/csrf-token' && req.method === 'GET') {
|
if (pathname === '/api/csrf-token' && req.method === 'GET') {
|
||||||
const sessionId = getOrCreateSessionId(req, res);
|
const sessionId = getOrCreateSessionId(req, res);
|
||||||
const tokenManager = getCsrfTokenManager();
|
const tokenManager = getCsrfTokenManager();
|
||||||
const csrfToken = tokenManager.generateToken(sessionId);
|
|
||||||
|
|
||||||
res.setHeader('X-CSRF-Token', csrfToken);
|
// Check for count parameter (pool pattern)
|
||||||
setCsrfCookie(res, csrfToken, 15 * 60);
|
const countParam = url.searchParams.get('count');
|
||||||
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
const count = countParam ? Math.min(Math.max(1, parseInt(countParam, 10) || 1), 10) : 1;
|
||||||
res.end(JSON.stringify({ csrfToken }));
|
|
||||||
|
if (count === 1) {
|
||||||
|
// Single token response (existing behavior)
|
||||||
|
const csrfToken = tokenManager.generateToken(sessionId);
|
||||||
|
res.setHeader('X-CSRF-Token', csrfToken);
|
||||||
|
setCsrfCookie(res, csrfToken, 15 * 60);
|
||||||
|
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
||||||
|
res.end(JSON.stringify({ csrfToken }));
|
||||||
|
} else {
|
||||||
|
// Batch token response (pool pattern)
|
||||||
|
const tokens = tokenManager.generateTokens(sessionId, count);
|
||||||
|
const firstToken = tokens[0];
|
||||||
|
|
||||||
|
// Set header and cookie with first token for compatibility
|
||||||
|
res.setHeader('X-CSRF-Token', firstToken);
|
||||||
|
setCsrfCookie(res, firstToken, 15 * 60);
|
||||||
|
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
||||||
|
res.end(JSON.stringify({
|
||||||
|
tokens,
|
||||||
|
expiresIn: 15 * 60, // seconds
|
||||||
|
}));
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -60,5 +60,91 @@ describe('CsrfTokenManager', async () => {
|
|||||||
assert.equal(manager.validateToken(token, 'session-1'), true);
|
assert.equal(manager.validateToken(token, 'session-1'), true);
|
||||||
manager.dispose();
|
manager.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ========== Pool Pattern Tests ==========
|
||||||
|
|
||||||
|
it('generateTokens produces N unique tokens', () => {
|
||||||
|
const manager = new mod.CsrfTokenManager({ cleanupIntervalMs: 0, maxTokensPerSession: 5 });
|
||||||
|
const tokens = manager.generateTokens('session-1', 3);
|
||||||
|
|
||||||
|
assert.equal(tokens.length, 3);
|
||||||
|
// All tokens should be unique
|
||||||
|
assert.equal(new Set(tokens).size, 3);
|
||||||
|
// All tokens should be valid hex
|
||||||
|
for (const token of tokens) {
|
||||||
|
assert.match(token, /^[a-f0-9]{64}$/);
|
||||||
|
}
|
||||||
|
manager.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('generateTokens respects maxTokensPerSession limit', () => {
|
||||||
|
const manager = new mod.CsrfTokenManager({ cleanupIntervalMs: 0, maxTokensPerSession: 5 });
|
||||||
|
// First batch of 5
|
||||||
|
const tokens1 = manager.generateTokens('session-1', 5);
|
||||||
|
assert.equal(tokens1.length, 5);
|
||||||
|
|
||||||
|
// Second batch should be empty (pool full)
|
||||||
|
const tokens2 = manager.generateTokens('session-1', 3);
|
||||||
|
assert.equal(tokens2.length, 0);
|
||||||
|
|
||||||
|
manager.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getTokenCount returns correct count for session', () => {
|
||||||
|
const manager = new mod.CsrfTokenManager({ cleanupIntervalMs: 0 });
|
||||||
|
manager.generateTokens('session-1', 3);
|
||||||
|
manager.generateTokens('session-2', 2);
|
||||||
|
|
||||||
|
assert.equal(manager.getTokenCount('session-1'), 3);
|
||||||
|
assert.equal(manager.getTokenCount('session-2'), 2);
|
||||||
|
assert.equal(manager.getTokenCount('session-3'), 0);
|
||||||
|
manager.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('validateToken works with pool pattern (multiple tokens per session)', () => {
|
||||||
|
const manager = new mod.CsrfTokenManager({ cleanupIntervalMs: 0 });
|
||||||
|
const tokens = manager.generateTokens('session-1', 3);
|
||||||
|
|
||||||
|
// All tokens should be valid once
|
||||||
|
assert.equal(manager.validateToken(tokens[0], 'session-1'), true);
|
||||||
|
assert.equal(manager.validateToken(tokens[1], 'session-1'), true);
|
||||||
|
assert.equal(manager.validateToken(tokens[2], 'session-1'), true);
|
||||||
|
|
||||||
|
// All tokens should now be invalid (used)
|
||||||
|
assert.equal(manager.validateToken(tokens[0], 'session-1'), false);
|
||||||
|
assert.equal(manager.validateToken(tokens[1], 'session-1'), false);
|
||||||
|
assert.equal(manager.validateToken(tokens[2], 'session-1'), false);
|
||||||
|
|
||||||
|
manager.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cleanupExpiredTokens handles multiple sessions', () => {
|
||||||
|
const manager = new mod.CsrfTokenManager({ tokenTtlMs: 10, cleanupIntervalMs: 0 });
|
||||||
|
manager.generateTokens('session-1', 3);
|
||||||
|
manager.generateTokens('session-2', 2);
|
||||||
|
|
||||||
|
const removed = manager.cleanupExpiredTokens(Date.now() + 100);
|
||||||
|
assert.equal(removed, 5);
|
||||||
|
assert.equal(manager.getActiveTokenCount(), 0);
|
||||||
|
assert.equal(manager.getTokenCount('session-1'), 0);
|
||||||
|
assert.equal(manager.getTokenCount('session-2'), 0);
|
||||||
|
manager.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('concurrent requests can use different tokens from pool', () => {
|
||||||
|
const manager = new mod.CsrfTokenManager({ cleanupIntervalMs: 0 });
|
||||||
|
const tokens = manager.generateTokens('session-1', 5);
|
||||||
|
|
||||||
|
// Simulate 5 concurrent requests using different tokens
|
||||||
|
const results = tokens.map(token => manager.validateToken(token, 'session-1'));
|
||||||
|
|
||||||
|
// All should succeed
|
||||||
|
assert.deepEqual(results, [true, true, true, true, true]);
|
||||||
|
|
||||||
|
// Token count should still be 5 (but all marked as used)
|
||||||
|
assert.equal(manager.getTokenCount('session-1'), 5);
|
||||||
|
|
||||||
|
manager.dispose();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -80,7 +80,8 @@ export default withMermaid(defineConfig({
|
|||||||
{ text: 'Guide', link: '/guide/ch01-what-is-claude-dms3' },
|
{ text: 'Guide', link: '/guide/ch01-what-is-claude-dms3' },
|
||||||
{ text: 'Commands', link: '/commands/claude/' },
|
{ text: 'Commands', link: '/commands/claude/' },
|
||||||
{ text: 'Skills', link: '/skills/' },
|
{ text: 'Skills', link: '/skills/' },
|
||||||
{ text: 'Features', link: '/features/spec' }
|
{ text: 'Features', link: '/features/spec' },
|
||||||
|
{ text: 'Components', link: '/components/' }
|
||||||
],
|
],
|
||||||
|
|
||||||
// Sidebar - 优化导航结构,增加二级标题和归类
|
// Sidebar - 优化导航结构,增加二级标题和归类
|
||||||
@@ -134,6 +135,20 @@ export default withMermaid(defineConfig({
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
'/skills/': [
|
'/skills/': [
|
||||||
|
{
|
||||||
|
text: 'Overview',
|
||||||
|
collapsible: false,
|
||||||
|
items: [
|
||||||
|
{ text: 'Skills Guide', link: '/skills/' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '📚 Conventions',
|
||||||
|
collapsible: true,
|
||||||
|
items: [
|
||||||
|
{ text: 'Naming Conventions', link: '/skills/naming-conventions' }
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
text: '⚡ Claude Skills',
|
text: '⚡ Claude Skills',
|
||||||
collapsible: true,
|
collapsible: true,
|
||||||
@@ -192,9 +207,13 @@ export default withMermaid(defineConfig({
|
|||||||
text: 'UI Components',
|
text: 'UI Components',
|
||||||
collapsible: true,
|
collapsible: true,
|
||||||
items: [
|
items: [
|
||||||
|
{ text: 'Overview', link: '/components/index' },
|
||||||
{ text: 'Button', link: '/components/ui/button' },
|
{ text: 'Button', link: '/components/ui/button' },
|
||||||
{ text: 'Card', link: '/components/ui/card' },
|
{ text: 'Card', link: '/components/ui/card' },
|
||||||
{ text: 'Input', link: '/components/ui/input' }
|
{ text: 'Input', link: '/components/ui/input' },
|
||||||
|
{ text: 'Select', link: '/components/ui/select' },
|
||||||
|
{ text: 'Checkbox', link: '/components/ui/checkbox' },
|
||||||
|
{ text: 'Badge', link: '/components/ui/badge' }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -292,11 +311,10 @@ export default withMermaid(defineConfig({
|
|||||||
'mermaid'
|
'mermaid'
|
||||||
],
|
],
|
||||||
config: (md) => {
|
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) => {
|
md.core.ruler.before('block', 'demo-blocks', (state) => {
|
||||||
const src = state.src
|
const src = state.src
|
||||||
const transformed = transformDemoBlocks(src, { path: '' })
|
const filePath = (state as any).path || ''
|
||||||
|
const transformed = transformDemoBlocks(src, { path: filePath })
|
||||||
if (transformed !== src) {
|
if (transformed !== src) {
|
||||||
state.src = transformed
|
state.src = transformed
|
||||||
}
|
}
|
||||||
@@ -376,6 +394,20 @@ export default withMermaid(defineConfig({
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
'/zh/skills/': [
|
'/zh/skills/': [
|
||||||
|
{
|
||||||
|
text: '概述',
|
||||||
|
collapsible: false,
|
||||||
|
items: [
|
||||||
|
{ text: '技能指南', link: '/zh/skills/' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '📚 规范',
|
||||||
|
collapsible: true,
|
||||||
|
items: [
|
||||||
|
{ text: '命名规范', link: '/zh/skills/naming-conventions' }
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
text: '⚡ Claude Skills',
|
text: '⚡ Claude Skills',
|
||||||
collapsible: true,
|
collapsible: true,
|
||||||
@@ -485,6 +517,53 @@ export default withMermaid(defineConfig({
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
'/zh-CN/skills/': [
|
||||||
|
{
|
||||||
|
text: '概述',
|
||||||
|
collapsible: false,
|
||||||
|
items: [
|
||||||
|
{ text: '技能指南', link: '/zh-CN/skills/' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '📚 规范',
|
||||||
|
collapsible: true,
|
||||||
|
items: [
|
||||||
|
{ text: '命名规范', link: '/zh-CN/skills/naming-conventions' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '⚡ Claude Skills',
|
||||||
|
collapsible: true,
|
||||||
|
items: [
|
||||||
|
{ text: '概述', link: '/zh-CN/skills/claude-index' },
|
||||||
|
{ text: '协作', link: '/zh-CN/skills/claude-collaboration' },
|
||||||
|
{ text: '工作流', link: '/zh-CN/skills/claude-workflow' },
|
||||||
|
{ text: '记忆', link: '/zh-CN/skills/claude-memory' },
|
||||||
|
{ text: '审查', link: '/zh-CN/skills/claude-review' },
|
||||||
|
{ text: '元技能', link: '/zh-CN/skills/claude-meta' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '🔧 Codex Skills',
|
||||||
|
collapsible: true,
|
||||||
|
items: [
|
||||||
|
{ text: '概述', link: '/zh-CN/skills/codex-index' },
|
||||||
|
{ text: '生命周期', link: '/zh-CN/skills/codex-lifecycle' },
|
||||||
|
{ text: '工作流', link: '/zh-CN/skills/codex-workflow' },
|
||||||
|
{ text: '专项', link: '/zh-CN/skills/codex-specialized' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '🎨 自定义技能',
|
||||||
|
collapsible: true,
|
||||||
|
items: [
|
||||||
|
{ text: '概述', link: '/zh-CN/skills/custom' },
|
||||||
|
{ text: '核心技能', link: '/zh-CN/skills/core-skills' },
|
||||||
|
{ text: '参考', link: '/zh-CN/skills/reference' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
'/zh-CN/features/': [
|
'/zh-CN/features/': [
|
||||||
{
|
{
|
||||||
text: '⚙️ 核心功能',
|
text: '⚙️ 核心功能',
|
||||||
@@ -494,6 +573,8 @@ export default withMermaid(defineConfig({
|
|||||||
{ text: 'Memory 记忆系统', link: '/zh-CN/features/memory' },
|
{ text: 'Memory 记忆系统', link: '/zh-CN/features/memory' },
|
||||||
{ text: 'CLI 调用', link: '/zh-CN/features/cli' },
|
{ text: 'CLI 调用', link: '/zh-CN/features/cli' },
|
||||||
{ text: 'Dashboard 面板', link: '/zh-CN/features/dashboard' },
|
{ text: 'Dashboard 面板', link: '/zh-CN/features/dashboard' },
|
||||||
|
{ text: 'Terminal 终端监控', link: '/zh-CN/features/terminal' },
|
||||||
|
{ text: 'Queue 队列管理', link: '/zh-CN/features/queue' },
|
||||||
{ text: 'CodexLens', link: '/zh-CN/features/codexlens' }
|
{ text: 'CodexLens', link: '/zh-CN/features/codexlens' }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -503,9 +584,13 @@ export default withMermaid(defineConfig({
|
|||||||
text: 'UI 组件',
|
text: 'UI 组件',
|
||||||
collapsible: true,
|
collapsible: true,
|
||||||
items: [
|
items: [
|
||||||
|
{ text: '概述', link: '/zh-CN/components/index' },
|
||||||
{ text: 'Button 按钮', link: '/zh-CN/components/ui/button' },
|
{ text: 'Button 按钮', link: '/zh-CN/components/ui/button' },
|
||||||
{ text: 'Card 卡片', link: '/zh-CN/components/ui/card' },
|
{ text: 'Card 卡片', link: '/zh-CN/components/ui/card' },
|
||||||
{ text: 'Input 输入框', link: '/zh-CN/components/ui/input' }
|
{ text: 'Input 输入框', link: '/zh-CN/components/ui/input' },
|
||||||
|
{ text: 'Select 选择器', link: '/zh-CN/components/ui/select' },
|
||||||
|
{ text: 'Checkbox 复选框', link: '/zh-CN/components/ui/checkbox' },
|
||||||
|
{ text: 'Badge 徽标', link: '/zh-CN/components/ui/badge' }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
326
docs/.vitepress/demos/ComponentGallery.tsx
Normal file
326
docs/.vitepress/demos/ComponentGallery.tsx
Normal file
@@ -0,0 +1,326 @@
|
|||||||
|
/**
|
||||||
|
* Component Gallery Demo
|
||||||
|
* Interactive showcase of all UI components
|
||||||
|
*/
|
||||||
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
|
export default function ComponentGallery() {
|
||||||
|
const [selectedCategory, setSelectedCategory] = useState('all')
|
||||||
|
const [buttonVariant, setButtonVariant] = useState('default')
|
||||||
|
const [switchState, setSwitchState] = useState(false)
|
||||||
|
const [checkboxState, setCheckboxState] = useState(false)
|
||||||
|
const [selectedTab, setSelectedTab] = 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 buttonVariants = ['default', 'destructive', 'outline', 'secondary', 'ghost', 'link']
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-6 bg-background space-y-8">
|
||||||
|
{/* Header */}
|
||||||
|
<div>
|
||||||
|
<h1 className="text-2xl font-bold">UI Component Library</h1>
|
||||||
|
<p className="text-muted-foreground">Interactive showcase of all available UI components</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Category Filter */}
|
||||||
|
<div className="flex flex-wrap gap-2">
|
||||||
|
{categories.map((cat) => (
|
||||||
|
<button
|
||||||
|
key={cat.id}
|
||||||
|
onClick={() => setSelectedCategory(cat.id)}
|
||||||
|
className={`px-4 py-2 rounded-md text-sm transition-colors ${
|
||||||
|
selectedCategory === cat.id
|
||||||
|
? 'bg-primary text-primary-foreground'
|
||||||
|
: 'border hover:bg-accent'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{cat.label}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Buttons Section */}
|
||||||
|
{(selectedCategory === 'all' || selectedCategory === 'buttons') && (
|
||||||
|
<section className="space-y-4">
|
||||||
|
<h2 className="text-lg font-semibold">Buttons</h2>
|
||||||
|
<div className="space-y-6">
|
||||||
|
{/* Variant Selector */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
<label className="text-sm font-medium">Variant</label>
|
||||||
|
<div className="flex flex-wrap gap-2">
|
||||||
|
{buttonVariants.map((variant) => (
|
||||||
|
<button
|
||||||
|
key={variant}
|
||||||
|
onClick={() => setButtonVariant(variant)}
|
||||||
|
className={`px-4 py-2 rounded-md text-sm capitalize transition-colors ${
|
||||||
|
buttonVariant === variant
|
||||||
|
? 'bg-primary text-primary-foreground ring-2 ring-ring'
|
||||||
|
: 'border hover:bg-accent'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{variant}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Button Sizes */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
<label className="text-sm font-medium">Sizes</label>
|
||||||
|
<div className="flex items-center gap-3 flex-wrap">
|
||||||
|
<button className={`h-8 rounded-md px-3 text-sm ${buttonVariant === 'default' ? 'bg-primary text-primary-foreground' : 'border'}`}>
|
||||||
|
Small
|
||||||
|
</button>
|
||||||
|
<button className={`h-10 px-4 py-2 rounded-md text-sm ${buttonVariant === 'default' ? 'bg-primary text-primary-foreground' : 'border'}`}>
|
||||||
|
Default
|
||||||
|
</button>
|
||||||
|
<button className={`h-11 rounded-md px-8 text-sm ${buttonVariant === 'default' ? 'bg-primary text-primary-foreground' : 'border'}`}>
|
||||||
|
Large
|
||||||
|
</button>
|
||||||
|
<button className={`h-10 w-10 rounded-md flex items-center justify-center ${buttonVariant === 'default' ? 'bg-primary text-primary-foreground' : 'border'}`}>
|
||||||
|
⚙
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* All Button Variants */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
<label className="text-sm font-medium">All Variants</label>
|
||||||
|
<div className="flex flex-wrap gap-3 p-4 border rounded-lg bg-muted/20">
|
||||||
|
<button className="px-4 py-2 rounded-md text-sm bg-primary text-primary-foreground hover:opacity-90">Default</button>
|
||||||
|
<button className="px-4 py-2 rounded-md text-sm bg-destructive text-destructive-foreground hover:opacity-90">Destructive</button>
|
||||||
|
<button className="px-4 py-2 rounded-md text-sm border bg-background hover:bg-accent">Outline</button>
|
||||||
|
<button className="px-4 py-2 rounded-md text-sm bg-secondary text-secondary-foreground hover:opacity-80">Secondary</button>
|
||||||
|
<button className="px-4 py-2 rounded-md text-sm hover:bg-accent">Ghost</button>
|
||||||
|
<button className="px-4 py-2 rounded-md text-sm text-primary underline-offset-4 hover:underline">Link</button>
|
||||||
|
<button className="px-4 py-2 rounded-md text-sm bg-gradient-to-r from-blue-500 to-purple-500 text-white hover:opacity-90">Gradient</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Forms Section */}
|
||||||
|
{(selectedCategory === 'all' || selectedCategory === 'forms') && (
|
||||||
|
<section className="space-y-4">
|
||||||
|
<h2 className="text-lg font-semibold">Form Components</h2>
|
||||||
|
<div className="grid md:grid-cols-2 gap-6">
|
||||||
|
{/* Input */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
<label className="text-sm font-medium">Input</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Enter text..."
|
||||||
|
className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Error state"
|
||||||
|
className="flex h-10 w-full rounded-md border border-destructive bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-destructive"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Textarea */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
<label className="text-sm font-medium">Textarea</label>
|
||||||
|
<textarea
|
||||||
|
placeholder="Enter multi-line text..."
|
||||||
|
className="flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Checkbox */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
<label className="text-sm font-medium">Checkbox</label>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="flex items-center gap-2 text-sm cursor-pointer">
|
||||||
|
<input type="checkbox" className="h-4 w-4 rounded border border-primary" checked={checkboxState} onChange={(e) => setCheckboxState(e.target.checked)} />
|
||||||
|
<span>Accept terms and conditions</span>
|
||||||
|
</label>
|
||||||
|
<label className="flex items-center gap-2 text-sm cursor-pointer opacity-50">
|
||||||
|
<input type="checkbox" className="h-4 w-4 rounded border border-primary" />
|
||||||
|
<span>Subscribe to newsletter</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Switch */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
<label className="text-sm font-medium">Switch</label>
|
||||||
|
<div className="space-y-3">
|
||||||
|
<label className="flex items-center gap-3 cursor-pointer">
|
||||||
|
<div className="relative">
|
||||||
|
<input type="checkbox" className="sr-only peer" checked={switchState} onChange={(e) => setSwitchState(e.target.checked)} />
|
||||||
|
<div className="w-9 h-5 bg-input rounded-full peer peer-focus:ring-2 peer-focus:ring-ring peer-checked:bg-primary after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-background after:rounded-full after:h-4 after:w-4 after:transition-all peer-checked:after:translate-x-full" />
|
||||||
|
</div>
|
||||||
|
<span className="text-sm">Enable notifications {switchState ? '(on)' : '(off)'}</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Select */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
<label className="text-sm font-medium">Select</label>
|
||||||
|
<select className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring">
|
||||||
|
<option value="">Choose an option</option>
|
||||||
|
<option value="1">Option 1</option>
|
||||||
|
<option value="2">Option 2</option>
|
||||||
|
<option value="3">Option 3</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Form Actions */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
<label className="text-sm font-medium">Form Actions</label>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<button className="px-4 py-2 rounded-md text-sm bg-primary text-primary-foreground hover:opacity-90">Submit</button>
|
||||||
|
<button className="px-4 py-2 rounded-md text-sm border hover:bg-accent">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Feedback Section */}
|
||||||
|
{(selectedCategory === 'all' || selectedCategory === 'feedback') && (
|
||||||
|
<section className="space-y-4">
|
||||||
|
<h2 className="text-lg font-semibold">Feedback Components</h2>
|
||||||
|
|
||||||
|
{/* Badges */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
<label className="text-sm font-medium">Badges</label>
|
||||||
|
<div className="flex flex-wrap gap-2">
|
||||||
|
<span className="inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold bg-primary text-primary-foreground">Default</span>
|
||||||
|
<span className="inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold bg-secondary text-secondary-foreground">Secondary</span>
|
||||||
|
<span className="inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold bg-destructive text-destructive-foreground">Destructive</span>
|
||||||
|
<span className="inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold bg-success text-white">Success</span>
|
||||||
|
<span className="inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold bg-warning text-white">Warning</span>
|
||||||
|
<span className="inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold bg-info text-white">Info</span>
|
||||||
|
<span className="inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold text-foreground">Outline</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Progress */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
<label className="text-sm font-medium">Progress Bars</label>
|
||||||
|
<div className="space-y-3 max-w-md">
|
||||||
|
<div>
|
||||||
|
<div className="flex justify-between text-xs mb-1">
|
||||||
|
<span>Processing...</span>
|
||||||
|
<span>65%</span>
|
||||||
|
</div>
|
||||||
|
<div className="h-2 bg-muted rounded-full overflow-hidden">
|
||||||
|
<div className="h-full bg-primary rounded-full transition-all" style={{ width: '65%' }}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="flex justify-between text-xs mb-1">
|
||||||
|
<span>Uploading...</span>
|
||||||
|
<span>30%</span>
|
||||||
|
</div>
|
||||||
|
<div className="h-2 bg-muted rounded-full overflow-hidden">
|
||||||
|
<div className="h-full bg-blue-500 rounded-full transition-all" style={{ width: '30%' }}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Alerts */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
<label className="text-sm font-medium">Alerts</label>
|
||||||
|
<div className="space-y-3">
|
||||||
|
<div className="flex items-start gap-3 p-4 border rounded-lg bg-destructive/10 border-destructive/20 text-destructive">
|
||||||
|
<span className="text-lg">⚠</span>
|
||||||
|
<div className="flex-1">
|
||||||
|
<div className="font-medium text-sm">Error occurred</div>
|
||||||
|
<div className="text-xs mt-1 opacity-80">Something went wrong. Please try again.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-start gap-3 p-4 border rounded-lg bg-success/10 border-success/20 text-success">
|
||||||
|
<span className="text-lg">✓</span>
|
||||||
|
<div className="flex-1">
|
||||||
|
<div className="font-medium text-sm">Success!</div>
|
||||||
|
<div className="text-xs mt-1 opacity-80">Your changes have been saved.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Navigation Section */}
|
||||||
|
{(selectedCategory === 'all' || selectedCategory === 'navigation') && (
|
||||||
|
<section className="space-y-4">
|
||||||
|
<h2 className="text-lg font-semibold">Navigation Components</h2>
|
||||||
|
|
||||||
|
{/* Tabs */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
<label className="text-sm font-medium">Tabs</label>
|
||||||
|
<div className="border-b">
|
||||||
|
<div className="flex gap-4">
|
||||||
|
{['Overview', 'Documentation', 'API Reference', 'Examples'].map((tab) => (
|
||||||
|
<button
|
||||||
|
key={tab}
|
||||||
|
onClick={() => setSelectedTab(tab.toLowerCase().replace(' ', '-'))}
|
||||||
|
className={`pb-3 px-1 text-sm border-b-2 transition-colors ${
|
||||||
|
selectedTab === tab.toLowerCase().replace(' ', '-')
|
||||||
|
? 'border-primary text-primary'
|
||||||
|
: 'border-transparent text-muted-foreground hover:text-foreground'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{tab}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Breadcrumb */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
<label className="text-sm font-medium">Breadcrumb</label>
|
||||||
|
<nav className="flex items-center gap-2 text-sm text-muted-foreground">
|
||||||
|
<a href="#" className="hover:text-foreground">Home</a>
|
||||||
|
<span>/</span>
|
||||||
|
<a href="#" className="hover:text-foreground">Components</a>
|
||||||
|
<span>/</span>
|
||||||
|
<span className="text-foreground">Library</span>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Overlays Section */}
|
||||||
|
{(selectedCategory === 'all' || selectedCategory === 'overlays') && (
|
||||||
|
<section className="space-y-4">
|
||||||
|
<h2 className="text-lg font-semibold">Overlay Components</h2>
|
||||||
|
|
||||||
|
<div className="grid md:grid-cols-3 gap-4 text-sm">
|
||||||
|
<div className="p-4 border rounded-lg">
|
||||||
|
<h3 className="font-medium mb-2">Dialog</h3>
|
||||||
|
<p className="text-muted-foreground text-xs">Modal dialogs for focused user interactions.</p>
|
||||||
|
<button className="mt-3 px-3 py-1.5 text-xs bg-primary text-primary-foreground rounded">Open Dialog</button>
|
||||||
|
</div>
|
||||||
|
<div className="p-4 border rounded-lg">
|
||||||
|
<h3 className="font-medium mb-2">Drawer</h3>
|
||||||
|
<p className="text-muted-foreground text-xs">Side panels that slide in from screen edges.</p>
|
||||||
|
<button className="mt-3 px-3 py-1.5 text-xs border rounded hover:bg-accent">Open Drawer</button>
|
||||||
|
</div>
|
||||||
|
<div className="p-4 border rounded-lg">
|
||||||
|
<h3 className="font-medium mb-2">Dropdown Menu</h3>
|
||||||
|
<p className="text-muted-foreground text-xs">Context menus and action lists.</p>
|
||||||
|
<button className="mt-3 px-3 py-1.5 text-xs border rounded hover:bg-accent">▼ Open Menu</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
326
docs/.vitepress/demos/ComponentGalleryZh.tsx
Normal file
326
docs/.vitepress/demos/ComponentGalleryZh.tsx
Normal file
@@ -0,0 +1,326 @@
|
|||||||
|
/**
|
||||||
|
* 组件库展示演示 (中文版)
|
||||||
|
* 所有 UI 组件的交互式展示
|
||||||
|
*/
|
||||||
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
|
export default function ComponentGalleryZh() {
|
||||||
|
const [selectedCategory, setSelectedCategory] = useState('all')
|
||||||
|
const [buttonVariant, setButtonVariant] = useState('default')
|
||||||
|
const [switchState, setSwitchState] = useState(false)
|
||||||
|
const [checkboxState, setCheckboxState] = useState(false)
|
||||||
|
const [selectedTab, setSelectedTab] = useState('variants')
|
||||||
|
|
||||||
|
const categories = [
|
||||||
|
{ id: 'all', label: '全部组件' },
|
||||||
|
{ id: 'buttons', label: '按钮' },
|
||||||
|
{ id: 'forms', label: '表单' },
|
||||||
|
{ id: 'feedback', label: '反馈' },
|
||||||
|
{ id: 'navigation', label: '导航' },
|
||||||
|
{ id: 'overlays', label: '叠加层' },
|
||||||
|
]
|
||||||
|
|
||||||
|
const buttonVariants = ['default', 'destructive', 'outline', 'secondary', 'ghost', 'link']
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-6 bg-background space-y-8">
|
||||||
|
{/* 标题 */}
|
||||||
|
<div>
|
||||||
|
<h1 className="text-2xl font-bold">UI 组件库</h1>
|
||||||
|
<p className="text-muted-foreground">所有可用 UI 组件的交互式展示</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 分类筛选 */}
|
||||||
|
<div className="flex flex-wrap gap-2">
|
||||||
|
{categories.map((cat) => (
|
||||||
|
<button
|
||||||
|
key={cat.id}
|
||||||
|
onClick={() => setSelectedCategory(cat.id)}
|
||||||
|
className={`px-4 py-2 rounded-md text-sm transition-colors ${
|
||||||
|
selectedCategory === cat.id
|
||||||
|
? 'bg-primary text-primary-foreground'
|
||||||
|
: 'border hover:bg-accent'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{cat.label}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 按钮部分 */}
|
||||||
|
{(selectedCategory === 'all' || selectedCategory === 'buttons') && (
|
||||||
|
<section className="space-y-4">
|
||||||
|
<h2 className="text-lg font-semibold">按钮</h2>
|
||||||
|
<div className="space-y-6">
|
||||||
|
{/* 变体选择器 */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
<label className="text-sm font-medium">变体</label>
|
||||||
|
<div className="flex flex-wrap gap-2">
|
||||||
|
{buttonVariants.map((variant) => (
|
||||||
|
<button
|
||||||
|
key={variant}
|
||||||
|
onClick={() => setButtonVariant(variant)}
|
||||||
|
className={`px-4 py-2 rounded-md text-sm capitalize transition-colors ${
|
||||||
|
buttonVariant === variant
|
||||||
|
? 'bg-primary text-primary-foreground ring-2 ring-ring'
|
||||||
|
: 'border hover:bg-accent'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{variant}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 按钮尺寸 */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
<label className="text-sm font-medium">尺寸</label>
|
||||||
|
<div className="flex items-center gap-3 flex-wrap">
|
||||||
|
<button className={`h-8 rounded-md px-3 text-sm ${buttonVariant === 'default' ? 'bg-primary text-primary-foreground' : 'border'}`}>
|
||||||
|
小
|
||||||
|
</button>
|
||||||
|
<button className={`h-10 px-4 py-2 rounded-md text-sm ${buttonVariant === 'default' ? 'bg-primary text-primary-foreground' : 'border'}`}>
|
||||||
|
默认
|
||||||
|
</button>
|
||||||
|
<button className={`h-11 rounded-md px-8 text-sm ${buttonVariant === 'default' ? 'bg-primary text-primary-foreground' : 'border'}`}>
|
||||||
|
大
|
||||||
|
</button>
|
||||||
|
<button className={`h-10 w-10 rounded-md flex items-center justify-center ${buttonVariant === 'default' ? 'bg-primary text-primary-foreground' : 'border'}`}>
|
||||||
|
⚙
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 所有按钮变体 */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
<label className="text-sm font-medium">所有变体</label>
|
||||||
|
<div className="flex flex-wrap gap-3 p-4 border rounded-lg bg-muted/20">
|
||||||
|
<button className="px-4 py-2 rounded-md text-sm bg-primary text-primary-foreground hover:opacity-90">默认</button>
|
||||||
|
<button className="px-4 py-2 rounded-md text-sm bg-destructive text-destructive-foreground hover:opacity-90">危险</button>
|
||||||
|
<button className="px-4 py-2 rounded-md text-sm border bg-background hover:bg-accent">轮廓</button>
|
||||||
|
<button className="px-4 py-2 rounded-md text-sm bg-secondary text-secondary-foreground hover:opacity-80">次要</button>
|
||||||
|
<button className="px-4 py-2 rounded-md text-sm hover:bg-accent">幽灵</button>
|
||||||
|
<button className="px-4 py-2 rounded-md text-sm text-primary underline-offset-4 hover:underline">链接</button>
|
||||||
|
<button className="px-4 py-2 rounded-md text-sm bg-gradient-to-r from-blue-500 to-purple-500 text-white hover:opacity-90">渐变</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 表单部分 */}
|
||||||
|
{(selectedCategory === 'all' || selectedCategory === 'forms') && (
|
||||||
|
<section className="space-y-4">
|
||||||
|
<h2 className="text-lg font-semibold">表单组件</h2>
|
||||||
|
<div className="grid md:grid-cols-2 gap-6">
|
||||||
|
{/* 输入框 */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
<label className="text-sm font-medium">输入框</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="输入文本..."
|
||||||
|
className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="错误状态"
|
||||||
|
className="flex h-10 w-full rounded-md border border-destructive bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-destructive"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 文本域 */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
<label className="text-sm font-medium">文本域</label>
|
||||||
|
<textarea
|
||||||
|
placeholder="输入多行文本..."
|
||||||
|
className="flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 复选框 */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
<label className="text-sm font-medium">复选框</label>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="flex items-center gap-2 text-sm cursor-pointer">
|
||||||
|
<input type="checkbox" className="h-4 w-4 rounded border border-primary" checked={checkboxState} onChange={(e) => setCheckboxState(e.target.checked)} />
|
||||||
|
<span>接受条款和条件</span>
|
||||||
|
</label>
|
||||||
|
<label className="flex items-center gap-2 text-sm cursor-pointer opacity-50">
|
||||||
|
<input type="checkbox" className="h-4 w-4 rounded border border-primary" />
|
||||||
|
<span>订阅新闻通讯</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 开关 */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
<label className="text-sm font-medium">开关</label>
|
||||||
|
<div className="space-y-3">
|
||||||
|
<label className="flex items-center gap-3 cursor-pointer">
|
||||||
|
<div className="relative">
|
||||||
|
<input type="checkbox" className="sr-only peer" checked={switchState} onChange={(e) => setSwitchState(e.target.checked)} />
|
||||||
|
<div className="w-9 h-5 bg-input rounded-full peer peer-focus:ring-2 peer-focus:ring-ring peer-checked:bg-primary after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-background after:rounded-full after:h-4 after:w-4 after:transition-all peer-checked:after:translate-x-full" />
|
||||||
|
</div>
|
||||||
|
<span className="text-sm">启用通知 {switchState ? '(开)' : '(关)'}</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 选择器 */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
<label className="text-sm font-medium">选择器</label>
|
||||||
|
<select className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring">
|
||||||
|
<option value="">选择一个选项</option>
|
||||||
|
<option value="1">选项 1</option>
|
||||||
|
<option value="2">选项 2</option>
|
||||||
|
<option value="3">选项 3</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 表单操作 */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
<label className="text-sm font-medium">表单操作</label>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<button className="px-4 py-2 rounded-md text-sm bg-primary text-primary-foreground hover:opacity-90">提交</button>
|
||||||
|
<button className="px-4 py-2 rounded-md text-sm border hover:bg-accent">取消</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 反馈部分 */}
|
||||||
|
{(selectedCategory === 'all' || selectedCategory === 'feedback') && (
|
||||||
|
<section className="space-y-4">
|
||||||
|
<h2 className="text-lg font-semibold">反馈组件</h2>
|
||||||
|
|
||||||
|
{/* 徽标 */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
<label className="text-sm font-medium">徽标</label>
|
||||||
|
<div className="flex flex-wrap gap-2">
|
||||||
|
<span className="inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold bg-primary text-primary-foreground">默认</span>
|
||||||
|
<span className="inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold bg-secondary text-secondary-foreground">次要</span>
|
||||||
|
<span className="inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold bg-destructive text-destructive-foreground">危险</span>
|
||||||
|
<span className="inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold bg-success text-white">成功</span>
|
||||||
|
<span className="inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold bg-warning text-white">警告</span>
|
||||||
|
<span className="inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold bg-info text-white">信息</span>
|
||||||
|
<span className="inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold text-foreground">轮廓</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 进度条 */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
<label className="text-sm font-medium">进度条</label>
|
||||||
|
<div className="space-y-3 max-w-md">
|
||||||
|
<div>
|
||||||
|
<div className="flex justify-between text-xs mb-1">
|
||||||
|
<span>处理中...</span>
|
||||||
|
<span>65%</span>
|
||||||
|
</div>
|
||||||
|
<div className="h-2 bg-muted rounded-full overflow-hidden">
|
||||||
|
<div className="h-full bg-primary rounded-full transition-all" style={{ width: '65%' }}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="flex justify-between text-xs mb-1">
|
||||||
|
<span>上传中...</span>
|
||||||
|
<span>30%</span>
|
||||||
|
</div>
|
||||||
|
<div className="h-2 bg-muted rounded-full overflow-hidden">
|
||||||
|
<div className="h-full bg-blue-500 rounded-full transition-all" style={{ width: '30%' }}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 提示 */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
<label className="text-sm font-medium">提示</label>
|
||||||
|
<div className="space-y-3">
|
||||||
|
<div className="flex items-start gap-3 p-4 border rounded-lg bg-destructive/10 border-destructive/20 text-destructive">
|
||||||
|
<span className="text-lg">⚠</span>
|
||||||
|
<div className="flex-1">
|
||||||
|
<div className="font-medium text-sm">发生错误</div>
|
||||||
|
<div className="text-xs mt-1 opacity-80">出错了,请重试。</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-start gap-3 p-4 border rounded-lg bg-success/10 border-success/20 text-success">
|
||||||
|
<span className="text-lg">✓</span>
|
||||||
|
<div className="flex-1">
|
||||||
|
<div className="font-medium text-sm">成功!</div>
|
||||||
|
<div className="text-xs mt-1 opacity-80">您的更改已保存。</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 导航部分 */}
|
||||||
|
{(selectedCategory === 'all' || selectedCategory === 'navigation') && (
|
||||||
|
<section className="space-y-4">
|
||||||
|
<h2 className="text-lg font-semibold">导航组件</h2>
|
||||||
|
|
||||||
|
{/* 标签页 */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
<label className="text-sm font-medium">标签页</label>
|
||||||
|
<div className="border-b">
|
||||||
|
<div className="flex gap-4">
|
||||||
|
{['概览', '文档', 'API 参考', '示例'].map((tab) => (
|
||||||
|
<button
|
||||||
|
key={tab}
|
||||||
|
onClick={() => setSelectedTab(tab.toLowerCase().replace(' ', '-'))}
|
||||||
|
className={`pb-3 px-1 text-sm border-b-2 transition-colors ${
|
||||||
|
selectedTab === tab.toLowerCase().replace(' ', '-')
|
||||||
|
? 'border-primary text-primary'
|
||||||
|
: 'border-transparent text-muted-foreground hover:text-foreground'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{tab}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 面包屑 */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
<label className="text-sm font-medium">面包屑</label>
|
||||||
|
<nav className="flex items-center gap-2 text-sm text-muted-foreground">
|
||||||
|
<a href="#" className="hover:text-foreground">首页</a>
|
||||||
|
<span>/</span>
|
||||||
|
<a href="#" className="hover:text-foreground">组件</a>
|
||||||
|
<span>/</span>
|
||||||
|
<span className="text-foreground">库</span>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 叠加层部分 */}
|
||||||
|
{(selectedCategory === 'all' || selectedCategory === 'overlays') && (
|
||||||
|
<section className="space-y-4">
|
||||||
|
<h2 className="text-lg font-semibold">叠加层组件</h2>
|
||||||
|
|
||||||
|
<div className="grid md:grid-cols-3 gap-4 text-sm">
|
||||||
|
<div className="p-4 border rounded-lg">
|
||||||
|
<h3 className="font-medium mb-2">对话框</h3>
|
||||||
|
<p className="text-muted-foreground text-xs">用于专注用户交互的模态对话框。</p>
|
||||||
|
<button className="mt-3 px-3 py-1.5 text-xs bg-primary text-primary-foreground rounded">打开对话框</button>
|
||||||
|
</div>
|
||||||
|
<div className="p-4 border rounded-lg">
|
||||||
|
<h3 className="font-medium mb-2">抽屉</h3>
|
||||||
|
<p className="text-muted-foreground text-xs">从屏幕边缘滑入的侧边面板。</p>
|
||||||
|
<button className="mt-3 px-3 py-1.5 text-xs border rounded hover:bg-accent">打开抽屉</button>
|
||||||
|
</div>
|
||||||
|
<div className="p-4 border rounded-lg">
|
||||||
|
<h3 className="font-medium mb-2">下拉菜单</h3>
|
||||||
|
<p className="text-muted-foreground text-xs">上下文菜单和操作列表。</p>
|
||||||
|
<button className="mt-3 px-3 py-1.5 text-xs border rounded hover:bg-accent">▼ 打开菜单</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
137
docs/.vitepress/demos/DashboardOverview.tsx
Normal file
137
docs/.vitepress/demos/DashboardOverview.tsx
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
/**
|
||||||
|
* Dashboard Overview Demo
|
||||||
|
* Shows the main dashboard layout with widgets
|
||||||
|
*/
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
export default function DashboardOverview() {
|
||||||
|
return (
|
||||||
|
<div className="space-y-6 p-6 bg-background min-h-[600px]">
|
||||||
|
{/* Header */}
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<h1 className="text-2xl font-bold">Dashboard</h1>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Project overview and activity monitoring
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<button className="px-3 py-1.5 text-sm border rounded-md hover:bg-accent">
|
||||||
|
Refresh
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Workflow Stats Widget */}
|
||||||
|
<div className="border rounded-lg overflow-hidden">
|
||||||
|
<div className="p-4 border-b bg-muted/30">
|
||||||
|
<h2 className="font-semibold">Project Overview & Statistics</h2>
|
||||||
|
</div>
|
||||||
|
<div className="p-4">
|
||||||
|
<div className="grid grid-cols-3 gap-4">
|
||||||
|
<div className="space-y-3">
|
||||||
|
<div className="text-xs font-medium text-muted-foreground">Statistics</div>
|
||||||
|
<div className="grid grid-cols-2 gap-2">
|
||||||
|
{[
|
||||||
|
{ label: 'Active Sessions', value: '12', color: 'text-blue-500' },
|
||||||
|
{ label: 'Total Tasks', value: '48', color: 'text-green-500' },
|
||||||
|
{ label: 'Completed', value: '35', color: 'text-emerald-500' },
|
||||||
|
{ label: 'Pending', value: '8', color: 'text-amber-500' },
|
||||||
|
].map((stat, i) => (
|
||||||
|
<div key={i} className="p-2 bg-muted/50 rounded">
|
||||||
|
<div className={`text-lg font-bold ${stat.color}`}>{stat.value}</div>
|
||||||
|
<div className="text-xs text-muted-foreground truncate">{stat.label}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-3">
|
||||||
|
<div className="text-xs font-medium text-muted-foreground">Workflow Status</div>
|
||||||
|
<div className="flex items-center justify-center h-24">
|
||||||
|
<div className="relative w-20 h-20">
|
||||||
|
<svg className="w-full h-full -rotate-90" viewBox="0 0 36 36">
|
||||||
|
<circle cx="18" cy="18" r="15" fill="none" stroke="currentColor" strokeWidth="3" className="text-muted opacity-20"/>
|
||||||
|
<circle cx="18" cy="18" r="15" fill="none" stroke="currentColor" strokeWidth="3" className="text-blue-500" strokeDasharray="70 100"/>
|
||||||
|
</svg>
|
||||||
|
<div className="absolute inset-0 flex items-center justify-center text-xs font-bold">70%</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="text-xs text-center space-y-1">
|
||||||
|
<div className="flex items-center justify-center gap-1">
|
||||||
|
<div className="w-2 h-2 rounded-full bg-blue-500"/>
|
||||||
|
<span>Completed: 70%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-3">
|
||||||
|
<div className="text-xs font-medium text-muted-foreground">Recent Session</div>
|
||||||
|
<div className="p-3 bg-accent/20 rounded border">
|
||||||
|
<div className="flex items-center justify-between mb-2">
|
||||||
|
<span className="text-sm font-medium">Feature: Auth Flow</span>
|
||||||
|
<span className="text-xs px-2 py-0.5 rounded-full bg-green-500/20 text-green-600">Running</span>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1.5">
|
||||||
|
<div className="flex items-center gap-2 text-xs">
|
||||||
|
<div className="w-3 h-3 rounded bg-green-500"/>
|
||||||
|
<span>Implement login</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2 text-xs">
|
||||||
|
<div className="w-3 h-3 rounded bg-amber-500"/>
|
||||||
|
<span>Add OAuth</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2 text-xs">
|
||||||
|
<div className="w-3 h-3 rounded bg-muted"/>
|
||||||
|
<span className="text-muted-foreground">Test flow</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Recent Sessions Widget */}
|
||||||
|
<div className="border rounded-lg overflow-hidden">
|
||||||
|
<div className="border-b bg-muted/30">
|
||||||
|
<div className="flex gap-1 p-2">
|
||||||
|
{['All Tasks', 'Workflow', 'Lite Tasks'].map((tab, i) => (
|
||||||
|
<button
|
||||||
|
key={tab}
|
||||||
|
className={`px-3 py-1.5 text-xs rounded-md transition-colors ${
|
||||||
|
i === 0 ? 'bg-background text-foreground' : 'text-muted-foreground hover:bg-foreground/5'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{tab}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="p-4">
|
||||||
|
<div className="grid grid-cols-3 gap-3">
|
||||||
|
{[
|
||||||
|
{ name: 'Refactor UI Components', status: 'In Progress', progress: 65 },
|
||||||
|
{ name: 'Fix Login Bug', status: 'Pending', progress: 0 },
|
||||||
|
{ name: 'Add Dark Mode', status: 'Completed', progress: 100 },
|
||||||
|
].map((task, i) => (
|
||||||
|
<div key={i} className="p-3 bg-muted/30 rounded border cursor-pointer hover:border-primary/30">
|
||||||
|
<div className="flex items-center justify-between mb-2">
|
||||||
|
<span className="text-xs font-medium line-clamp-1">{task.name}</span>
|
||||||
|
<span className={`text-xs px-1.5 py-0.5 rounded ${
|
||||||
|
task.status === 'Completed' ? 'bg-green-500/20 text-green-600' :
|
||||||
|
task.status === 'In Progress' ? 'bg-blue-500/20 text-blue-600' :
|
||||||
|
'bg-gray-500/20 text-gray-600'
|
||||||
|
}`}>{task.status}</span>
|
||||||
|
</div>
|
||||||
|
{task.progress > 0 && task.progress < 100 && (
|
||||||
|
<div className="w-full h-1.5 bg-muted rounded-full overflow-hidden">
|
||||||
|
<div className="h-full bg-blue-500 rounded-full" style={{ width: `${task.progress}%` }}/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
82
docs/.vitepress/demos/FloatingPanelsDemo.tsx
Normal file
82
docs/.vitepress/demos/FloatingPanelsDemo.tsx
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
/**
|
||||||
|
* Floating Panels Demo
|
||||||
|
* Mutually exclusive overlay panels
|
||||||
|
*/
|
||||||
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
|
export default function FloatingPanelsDemo() {
|
||||||
|
const [activePanel, setActivePanel] = useState<string | null>(null)
|
||||||
|
|
||||||
|
const panels = [
|
||||||
|
{ id: 'issues', title: 'Issues + Queue', side: 'left', width: 400 },
|
||||||
|
{ id: 'queue', title: 'Queue', side: 'right', width: 320 },
|
||||||
|
{ id: 'inspector', title: 'Inspector', side: 'right', width: 280 },
|
||||||
|
{ id: 'execution', title: 'Execution Monitor', side: 'right', width: 300 },
|
||||||
|
{ id: 'scheduler', title: 'Scheduler', side: 'right', width: 260 },
|
||||||
|
]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative h-[500px] p-6 bg-background border rounded-lg">
|
||||||
|
<div className="flex items-center justify-between mb-4">
|
||||||
|
<h3 className="text-sm font-semibold">Floating Panels</h3>
|
||||||
|
<div className="flex gap-2 flex-wrap">
|
||||||
|
{panels.map((panel) => (
|
||||||
|
<button
|
||||||
|
key={panel.id}
|
||||||
|
onClick={() => setActivePanel(activePanel === panel.id ? null : panel.id)}
|
||||||
|
className={`px-3 py-1.5 text-xs rounded transition-colors ${
|
||||||
|
activePanel === panel.id
|
||||||
|
? 'bg-primary text-primary-foreground'
|
||||||
|
: 'border hover:bg-accent'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{panel.title}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="h-[380px] bg-muted/20 border rounded flex items-center justify-center">
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
{activePanel ? `"${panels.find((p) => p.id === activePanel)?.title}" panel is open` : 'Click a button to open a floating panel'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Floating Panel Overlay */}
|
||||||
|
{activePanel && (
|
||||||
|
<div
|
||||||
|
className={`absolute top-16 border rounded-lg shadow-lg bg-background ${
|
||||||
|
panels.find((p) => p.id === activePanel)?.side === 'left' ? 'left-6' : 'right-6'
|
||||||
|
}`}
|
||||||
|
style={{ width: panels.find((p) => p.id === activePanel)?.width }}
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between px-3 py-2 border-b">
|
||||||
|
<span className="text-sm font-medium">{panels.find((p) => p.id === activePanel)?.title}</span>
|
||||||
|
<button
|
||||||
|
onClick={() => setActivePanel(null)}
|
||||||
|
className="text-xs hover:bg-accent px-2 py-1 rounded"
|
||||||
|
>
|
||||||
|
✕
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="p-4 text-sm text-muted-foreground">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="w-2 h-2 rounded-full bg-blue-500"/>
|
||||||
|
<span>Item 1</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="w-2 h-2 rounded-full bg-green-500"/>
|
||||||
|
<span>Item 2</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="w-2 h-2 rounded-full bg-amber-500"/>
|
||||||
|
<span>Item 3</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
54
docs/.vitepress/demos/MiniStatCards.tsx
Normal file
54
docs/.vitepress/demos/MiniStatCards.tsx
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
/**
|
||||||
|
* Mini Stat Cards Demo
|
||||||
|
* Individual statistics cards with sparkline trends
|
||||||
|
*/
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
export default function MiniStatCards() {
|
||||||
|
const stats = [
|
||||||
|
{ label: 'Active Sessions', value: 12, trend: [8, 10, 9, 11, 10, 12, 12], color: 'blue' },
|
||||||
|
{ label: 'Total Tasks', value: 48, trend: [40, 42, 45, 44, 46, 47, 48], color: 'green' },
|
||||||
|
{ label: 'Completed', value: 35, trend: [25, 28, 30, 32, 33, 34, 35], color: 'emerald' },
|
||||||
|
{ label: 'Pending', value: 8, trend: [12, 10, 11, 9, 8, 7, 8], color: 'amber' },
|
||||||
|
{ label: 'Failed', value: 5, trend: [3, 4, 3, 5, 4, 5, 5], color: 'red' },
|
||||||
|
{ label: 'Today Activity', value: 23, trend: [5, 10, 15, 18, 20, 22, 23], color: 'purple' },
|
||||||
|
]
|
||||||
|
|
||||||
|
const colorMap = {
|
||||||
|
blue: 'text-blue-500 bg-blue-500/10',
|
||||||
|
green: 'text-green-500 bg-green-500/10',
|
||||||
|
emerald: 'text-emerald-500 bg-emerald-500/10',
|
||||||
|
amber: 'text-amber-500 bg-amber-500/10',
|
||||||
|
red: 'text-red-500 bg-red-500/10',
|
||||||
|
purple: 'text-purple-500 bg-purple-500/10',
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-6 bg-background">
|
||||||
|
<h3 className="text-sm font-semibold mb-4">Statistics with Sparklines</h3>
|
||||||
|
<div className="grid grid-cols-2 md:grid-cols-3 gap-4">
|
||||||
|
{stats.map((stat, i) => (
|
||||||
|
<div key={i} className="p-4 border rounded-lg bg-card">
|
||||||
|
<div className="flex items-center justify-between mb-2">
|
||||||
|
<span className="text-xs text-muted-foreground">{stat.label}</span>
|
||||||
|
<div className={`w-2 h-2 rounded-full ${colorMap[stat.color].split(' ')[1]}`}/>
|
||||||
|
</div>
|
||||||
|
<div className={`text-2xl font-bold ${colorMap[stat.color].split(' ')[0]}`}>{stat.value}</div>
|
||||||
|
<div className="mt-2 h-8 flex items-end gap-0.5">
|
||||||
|
{stat.trend.map((v, j) => (
|
||||||
|
<div
|
||||||
|
key={j}
|
||||||
|
className="flex-1 rounded-t"
|
||||||
|
style={{
|
||||||
|
height: `${(v / Math.max(...stat.trend)) * 100}%`,
|
||||||
|
backgroundColor: v === stat.value ? 'currentColor' : 'rgba(59, 130, 246, 0.3)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
66
docs/.vitepress/demos/ProjectInfoBanner.tsx
Normal file
66
docs/.vitepress/demos/ProjectInfoBanner.tsx
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* Project Info Banner Demo
|
||||||
|
* Expandable project information with tech stack
|
||||||
|
*/
|
||||||
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
|
export default function ProjectInfoBanner() {
|
||||||
|
const [expanded, setExpanded] = useState(false)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-6 bg-background">
|
||||||
|
<h3 className="text-sm font-semibold mb-4">Project Info Banner</h3>
|
||||||
|
<div className="border rounded-lg overflow-hidden">
|
||||||
|
{/* Banner Header */}
|
||||||
|
<div className="p-4 bg-muted/30 flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<h4 className="font-semibold">My Awesome Project</h4>
|
||||||
|
<p className="text-sm text-muted-foreground">A modern web application built with React</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => setExpanded(!expanded)}
|
||||||
|
className="p-2 rounded-md hover:bg-accent"
|
||||||
|
>
|
||||||
|
<svg className={`w-5 h-5 transition-transform ${expanded ? 'rotate-180' : ''}`} fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Tech Stack Badges */}
|
||||||
|
<div className="px-4 pb-3 flex flex-wrap gap-2">
|
||||||
|
{['TypeScript', 'React', 'Vite', 'Tailwind CSS', 'Zustand'].map((tech) => (
|
||||||
|
<span key={tech} className="px-2 py-1 text-xs rounded-full bg-primary/10 text-primary">
|
||||||
|
{tech}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Expanded Content */}
|
||||||
|
{expanded && (
|
||||||
|
<div className="p-4 border-t bg-muted/20 space-y-4">
|
||||||
|
<div>
|
||||||
|
<h5 className="text-xs font-semibold mb-2">Architecture</h5>
|
||||||
|
<div className="text-sm text-muted-foreground space-y-1">
|
||||||
|
<div>• Component-based UI architecture</div>
|
||||||
|
<div>• Centralized state management</div>
|
||||||
|
<div>• RESTful API integration</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h5 className="text-xs font-semibold mb-2">Key Components</h5>
|
||||||
|
<div className="grid grid-cols-2 gap-2 text-sm">
|
||||||
|
{['Session Manager', 'Dashboard', 'Task Scheduler', 'Analytics'].map((comp) => (
|
||||||
|
<div key={comp} className="flex items-center gap-2">
|
||||||
|
<div className="w-1.5 h-1.5 rounded-full bg-primary"/>
|
||||||
|
{comp}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
60
docs/.vitepress/demos/QueueItemStatusDemo.tsx
Normal file
60
docs/.vitepress/demos/QueueItemStatusDemo.tsx
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
/**
|
||||||
|
* Queue Item Status Demo
|
||||||
|
* Shows all possible queue item states
|
||||||
|
*/
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
export default function QueueItemStatusDemo() {
|
||||||
|
const itemStates = [
|
||||||
|
{ status: 'pending', issueId: 'ISSUE-101', sessionKey: null },
|
||||||
|
{ status: 'executing', issueId: 'ISSUE-102', sessionKey: 'cli-session-abc' },
|
||||||
|
{ status: 'completed', issueId: 'ISSUE-103', sessionKey: 'cli-session-def' },
|
||||||
|
{ status: 'blocked', issueId: 'ISSUE-104', sessionKey: null },
|
||||||
|
{ status: 'failed', issueId: 'ISSUE-105', sessionKey: 'cli-session-ghi' },
|
||||||
|
]
|
||||||
|
|
||||||
|
const statusConfig = {
|
||||||
|
pending: { icon: '○', color: 'text-gray-400', bg: 'bg-gray-500/10', label: 'Pending' },
|
||||||
|
executing: { icon: '▶', color: 'text-blue-500', bg: 'bg-blue-500/10', label: 'Executing' },
|
||||||
|
completed: { icon: '✓', color: 'text-green-500', bg: 'bg-green-500/10', label: 'Completed' },
|
||||||
|
blocked: { icon: '✕', color: 'text-red-500', bg: 'bg-red-500/10', label: 'Blocked' },
|
||||||
|
failed: { icon: '!', color: 'text-red-500', bg: 'bg-red-500/10', label: 'Failed' },
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-6 bg-background space-y-4">
|
||||||
|
<h3 className="text-sm font-semibold">Queue Item Status States</h3>
|
||||||
|
<div className="space-y-2">
|
||||||
|
{itemStates.map((item) => {
|
||||||
|
const config = statusConfig[item.status as keyof typeof statusConfig]
|
||||||
|
return (
|
||||||
|
<div key={item.status} className="border rounded-lg p-4 flex items-center gap-4">
|
||||||
|
<span className={`text-2xl ${config.color}`}>{config.icon}</span>
|
||||||
|
<div className="flex-1">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="font-medium">{item.issueId}</span>
|
||||||
|
<span className={`text-xs px-2 py-0.5 rounded-full ${config.bg} ${config.color}`}>
|
||||||
|
{config.label}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{item.sessionKey && (
|
||||||
|
<div className="text-sm text-muted-foreground mt-1">
|
||||||
|
Bound session: <code className="text-xs bg-muted px-1 rounded">{item.sessionKey}</code>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{item.status === 'executing' && (
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="w-24 h-2 bg-muted rounded-full overflow-hidden">
|
||||||
|
<div className="h-full bg-blue-500 animate-pulse" style={{ width: '60%' }}/>
|
||||||
|
</div>
|
||||||
|
<span className="text-xs text-muted-foreground">60%</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
164
docs/.vitepress/demos/QueueManagementDemo.tsx
Normal file
164
docs/.vitepress/demos/QueueManagementDemo.tsx
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
/**
|
||||||
|
* Queue Management Demo
|
||||||
|
* Shows scheduler controls and queue items list
|
||||||
|
*/
|
||||||
|
import React, { useState, useEffect } from 'react'
|
||||||
|
|
||||||
|
export default function QueueManagementDemo() {
|
||||||
|
const [schedulerStatus, setSchedulerStatus] = useState('idle')
|
||||||
|
const [progress, setProgress] = useState(0)
|
||||||
|
|
||||||
|
const queueItems = [
|
||||||
|
{ id: '1', status: 'completed', issueId: 'ISSUE-1', sessionKey: 'session-1' },
|
||||||
|
{ id: '2', status: 'executing', issueId: 'ISSUE-2', sessionKey: 'session-2' },
|
||||||
|
{ id: '3', status: 'pending', issueId: 'ISSUE-3', sessionKey: null },
|
||||||
|
{ id: '4', status: 'pending', issueId: 'ISSUE-4', sessionKey: null },
|
||||||
|
{ id: '5', status: 'blocked', issueId: 'ISSUE-5', sessionKey: null },
|
||||||
|
]
|
||||||
|
|
||||||
|
const statusConfig = {
|
||||||
|
idle: { label: 'Idle', color: 'bg-gray-500/20 text-gray-600 border-gray-500' },
|
||||||
|
running: { label: 'Running', color: 'bg-green-500/20 text-green-600 border-green-500' },
|
||||||
|
paused: { label: 'Paused', color: 'bg-amber-500/20 text-amber-600 border-amber-500' },
|
||||||
|
}
|
||||||
|
|
||||||
|
const itemStatusConfig = {
|
||||||
|
completed: { icon: '✓', color: 'text-green-500', label: 'Completed' },
|
||||||
|
executing: { icon: '▶', color: 'text-blue-500', label: 'Executing' },
|
||||||
|
pending: { icon: '○', color: 'text-gray-400', label: 'Pending' },
|
||||||
|
blocked: { icon: '✕', color: 'text-red-500', label: 'Blocked' },
|
||||||
|
failed: { icon: '!', color: 'text-red-500', label: 'Failed' },
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (schedulerStatus === 'running') {
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
setProgress((p) => (p >= 100 ? 0 : p + 10))
|
||||||
|
}, 500)
|
||||||
|
return () => clearInterval(interval)
|
||||||
|
}
|
||||||
|
}, [schedulerStatus])
|
||||||
|
|
||||||
|
const handleStart = () => {
|
||||||
|
if (schedulerStatus === 'idle' || schedulerStatus === 'paused') {
|
||||||
|
setSchedulerStatus('running')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlePause = () => {
|
||||||
|
if (schedulerStatus === 'running') {
|
||||||
|
setSchedulerStatus('paused')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleStop = () => {
|
||||||
|
setSchedulerStatus('idle')
|
||||||
|
setProgress(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentConfig = statusConfig[schedulerStatus as keyof typeof statusConfig]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-6 bg-background space-y-6">
|
||||||
|
{/* Header */}
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<h3 className="text-lg font-semibold">Queue Management</h3>
|
||||||
|
<p className="text-sm text-muted-foreground">Manage issue execution queue</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Scheduler Status Bar */}
|
||||||
|
<div className="border rounded-lg p-4 space-y-4">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className={`px-3 py-1 rounded text-xs font-medium border ${currentConfig.color}`}>
|
||||||
|
{currentConfig.label}
|
||||||
|
</span>
|
||||||
|
<div className="flex items-center gap-4 text-sm text-muted-foreground">
|
||||||
|
<span>{queueItems.filter((i) => i.status === 'completed').length}/{queueItems.length} items</span>
|
||||||
|
<span>2/2 concurrent</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Progress Bar */}
|
||||||
|
{schedulerStatus === 'running' && (
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="h-2 bg-muted rounded-full overflow-hidden">
|
||||||
|
<div
|
||||||
|
className="h-full bg-green-500 rounded-full transition-all"
|
||||||
|
style={{ width: `${progress}%` }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<span className="text-xs text-muted-foreground">{progress}% complete</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Scheduler Controls */}
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
{(schedulerStatus === 'idle' || schedulerStatus === 'paused') && (
|
||||||
|
<button
|
||||||
|
onClick={handleStart}
|
||||||
|
className="px-4 py-2 text-sm bg-green-500 text-white rounded hover:bg-green-600 flex items-center gap-2"
|
||||||
|
>
|
||||||
|
<span>▶</span> Start
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
{schedulerStatus === 'running' && (
|
||||||
|
<button
|
||||||
|
onClick={handlePause}
|
||||||
|
className="px-4 py-2 text-sm bg-amber-500 text-white rounded hover:bg-amber-600 flex items-center gap-2"
|
||||||
|
>
|
||||||
|
<span>⏸</span> Pause
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
{schedulerStatus !== 'idle' && (
|
||||||
|
<button
|
||||||
|
onClick={handleStop}
|
||||||
|
className="px-4 py-2 text-sm bg-red-500 text-white rounded hover:bg-red-600 flex items-center gap-2"
|
||||||
|
>
|
||||||
|
<span>⬛</span> Stop
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
<button className="px-4 py-2 text-sm border rounded hover:bg-accent">
|
||||||
|
Config
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Queue Items List */}
|
||||||
|
<div className="border rounded-lg">
|
||||||
|
<div className="px-4 py-3 border-b bg-muted/30">
|
||||||
|
<h4 className="text-sm font-semibold">Queue Items</h4>
|
||||||
|
</div>
|
||||||
|
<div className="divide-y max-h-80 overflow-auto">
|
||||||
|
{queueItems.map((item) => {
|
||||||
|
const config = itemStatusConfig[item.status as keyof typeof itemStatusConfig]
|
||||||
|
return (
|
||||||
|
<div key={item.id} className="px-4 py-3 flex items-center gap-4 hover:bg-accent/50">
|
||||||
|
<span className={`text-lg ${config.color}`}>{config.icon}</span>
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="text-sm font-medium">{item.issueId}</span>
|
||||||
|
<span className={`text-xs px-2 py-0.5 rounded-full ${config.color} bg-opacity-10`}>
|
||||||
|
{config.label}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{item.sessionKey && (
|
||||||
|
<div className="text-xs text-muted-foreground mt-1">
|
||||||
|
Session: {item.sessionKey}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{item.status === 'executing' && (
|
||||||
|
<div className="w-20 h-1.5 bg-muted rounded-full overflow-hidden">
|
||||||
|
<div className="h-full bg-blue-500 animate-pulse" style={{ width: '60%' }}/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
86
docs/.vitepress/demos/ResizablePanesDemo.tsx
Normal file
86
docs/.vitepress/demos/ResizablePanesDemo.tsx
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
/**
|
||||||
|
* Resizable Panes Demo
|
||||||
|
* Simulates the Allotment resizable split behavior
|
||||||
|
*/
|
||||||
|
import React, { useState, useEffect } from 'react'
|
||||||
|
|
||||||
|
export default function ResizablePanesDemo() {
|
||||||
|
const [leftWidth, setLeftWidth] = useState(240)
|
||||||
|
const [rightWidth, setRightWidth] = useState(280)
|
||||||
|
const [isDragging, setIsDragging] = useState<string | null>(null)
|
||||||
|
|
||||||
|
const handleDragStart = (side: string) => (e: React.MouseEvent) => {
|
||||||
|
setIsDragging(side)
|
||||||
|
e.preventDefault()
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleMouseMove = (e: MouseEvent) => {
|
||||||
|
if (isDragging === 'left') {
|
||||||
|
setLeftWidth(Math.max(180, Math.min(320, e.clientX - 24)))
|
||||||
|
} else if (isDragging === 'right') {
|
||||||
|
setRightWidth(Math.max(200, Math.min(400, window.innerWidth - e.clientX - 24)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleMouseUp = () => setIsDragging(null)
|
||||||
|
|
||||||
|
if (isDragging) {
|
||||||
|
window.addEventListener('mousemove', handleMouseMove)
|
||||||
|
window.addEventListener('mouseup', handleMouseUp)
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('mousemove', handleMouseMove)
|
||||||
|
window.removeEventListener('mouseup', handleMouseUp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [isDragging])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="h-[400px] flex bg-background border rounded-lg overflow-hidden">
|
||||||
|
{/* Left Sidebar */}
|
||||||
|
<div style={{ width: leftWidth }} className="border-r flex flex-col min-w-[180px]">
|
||||||
|
<div className="px-3 py-2 text-xs font-semibold border-b bg-muted/30">
|
||||||
|
Session Groups
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 p-2 text-sm space-y-1">
|
||||||
|
{['Active Sessions', 'Completed'].map((g) => (
|
||||||
|
<div key={g} className="px-2 py-1 hover:bg-accent rounded cursor-pointer">{g}</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Left Drag Handle */}
|
||||||
|
<div
|
||||||
|
onMouseDown={handleDragStart('left')}
|
||||||
|
className={`w-1 bg-border hover:bg-primary cursor-col-resize transition-colors ${
|
||||||
|
isDragging === 'left' ? 'bg-primary' : ''
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Main Content */}
|
||||||
|
<div className="flex-1 bg-muted/20 flex items-center justify-center">
|
||||||
|
<span className="text-sm text-muted-foreground">Terminal Grid Area</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Right Drag Handle */}
|
||||||
|
<div
|
||||||
|
onMouseDown={handleDragStart('right')}
|
||||||
|
className={`w-1 bg-border hover:bg-primary cursor-col-resize transition-colors ${
|
||||||
|
isDragging === 'right' ? 'bg-primary' : ''
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Right Sidebar */}
|
||||||
|
<div style={{ width: rightWidth }} className="border-l flex flex-col min-w-[200px]">
|
||||||
|
<div className="px-3 py-2 text-xs font-semibold border-b bg-muted/30">
|
||||||
|
Project Files
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 p-2 text-sm space-y-1">
|
||||||
|
{['src/', 'docs/', 'tests/'].map((f) => (
|
||||||
|
<div key={f} className="px-2 py-1 hover:bg-accent rounded cursor-pointer">{f}</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
110
docs/.vitepress/demos/SchedulerConfigDemo.tsx
Normal file
110
docs/.vitepress/demos/SchedulerConfigDemo.tsx
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
/**
|
||||||
|
* Scheduler Config Demo
|
||||||
|
* Interactive configuration panel
|
||||||
|
*/
|
||||||
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
|
export default function SchedulerConfigDemo() {
|
||||||
|
const [config, setConfig] = useState({
|
||||||
|
maxConcurrentSessions: 2,
|
||||||
|
sessionIdleTimeoutMs: 60000,
|
||||||
|
resumeKeySessionBindingTimeoutMs: 300000,
|
||||||
|
})
|
||||||
|
|
||||||
|
const formatMs = (ms: number) => {
|
||||||
|
if (ms >= 60000) return `${ms / 60000}m`
|
||||||
|
if (ms >= 1000) return `${ms / 1000}s`
|
||||||
|
return `${ms}ms`
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-6 bg-background space-y-6 max-w-md">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<h3 className="text-sm font-semibold">Scheduler Configuration</h3>
|
||||||
|
<button className="px-3 py-1.5 text-xs bg-primary text-primary-foreground rounded hover:opacity-90">
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-4">
|
||||||
|
{/* Max Concurrent Sessions */}
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="text-sm font-medium">Max Concurrent Sessions</label>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
min="1"
|
||||||
|
max="8"
|
||||||
|
value={config.maxConcurrentSessions}
|
||||||
|
onChange={(e) => setConfig({ ...config, maxConcurrentSessions: parseInt(e.target.value) })}
|
||||||
|
className="flex-1"
|
||||||
|
/>
|
||||||
|
<span className="text-sm font-medium w-8 text-center">{config.maxConcurrentSessions}</span>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
Maximum number of sessions to run simultaneously
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Session Idle Timeout */}
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="text-sm font-medium">Session Idle Timeout</label>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
min="10000"
|
||||||
|
max="300000"
|
||||||
|
step="10000"
|
||||||
|
value={config.sessionIdleTimeoutMs}
|
||||||
|
onChange={(e) => setConfig({ ...config, sessionIdleTimeoutMs: parseInt(e.target.value) })}
|
||||||
|
className="flex-1"
|
||||||
|
/>
|
||||||
|
<span className="text-sm font-medium w-12 text-right">{formatMs(config.sessionIdleTimeoutMs)}</span>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
Time before idle session is terminated
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Resume Key Binding Timeout */}
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="text-sm font-medium">Resume Key Binding Timeout</label>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
min="60000"
|
||||||
|
max="600000"
|
||||||
|
step="60000"
|
||||||
|
value={config.resumeKeySessionBindingTimeoutMs}
|
||||||
|
onChange={(e) => setConfig({ ...config, resumeKeySessionBindingTimeoutMs: parseInt(e.target.value) })}
|
||||||
|
className="flex-1"
|
||||||
|
/>
|
||||||
|
<span className="text-sm font-medium w-12 text-right">{formatMs(config.resumeKeySessionBindingTimeoutMs)}</span>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
Time to preserve resume key session binding
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Current Config Display */}
|
||||||
|
<div className="border rounded-lg p-4 bg-muted/30">
|
||||||
|
<h4 className="text-xs font-semibold mb-3">Current Configuration</h4>
|
||||||
|
<dl className="space-y-2 text-sm">
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<dt className="text-muted-foreground">Max Concurrent</dt>
|
||||||
|
<dd className="font-medium">{config.maxConcurrentSessions} sessions</dd>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<dt className="text-muted-foreground">Idle Timeout</dt>
|
||||||
|
<dd className="font-medium">{formatMs(config.sessionIdleTimeoutMs)}</dd>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<dt className="text-muted-foreground">Binding Timeout</dt>
|
||||||
|
<dd className="font-medium">{formatMs(config.resumeKeySessionBindingTimeoutMs)}</dd>
|
||||||
|
</div>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
108
docs/.vitepress/demos/SessionCarousel.tsx
Normal file
108
docs/.vitepress/demos/SessionCarousel.tsx
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
/**
|
||||||
|
* Session Carousel Demo
|
||||||
|
* Auto-rotating session cards with navigation
|
||||||
|
*/
|
||||||
|
import React, { useState, useEffect } from 'react'
|
||||||
|
|
||||||
|
export default function SessionCarousel() {
|
||||||
|
const [currentIndex, setCurrentIndex] = useState(0)
|
||||||
|
const sessions = [
|
||||||
|
{
|
||||||
|
name: 'Feature: User Authentication',
|
||||||
|
status: 'running',
|
||||||
|
tasks: [
|
||||||
|
{ name: 'Implement login form', status: 'completed' },
|
||||||
|
{ name: 'Add OAuth provider', status: 'in-progress' },
|
||||||
|
{ name: 'Create session management', status: 'pending' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Bug Fix: Memory Leak',
|
||||||
|
status: 'running',
|
||||||
|
tasks: [
|
||||||
|
{ name: 'Identify leak source', status: 'completed' },
|
||||||
|
{ name: 'Fix cleanup handlers', status: 'in-progress' },
|
||||||
|
{ name: 'Add unit tests', status: 'pending' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Refactor: API Layer',
|
||||||
|
status: 'planning',
|
||||||
|
tasks: [
|
||||||
|
{ name: 'Design new interface', status: 'pending' },
|
||||||
|
{ name: 'Migrate existing endpoints', status: 'pending' },
|
||||||
|
{ name: 'Update documentation', status: 'pending' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const statusColors = {
|
||||||
|
completed: 'bg-green-500',
|
||||||
|
'in-progress': 'bg-amber-500',
|
||||||
|
pending: 'bg-muted',
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const timer = setInterval(() => {
|
||||||
|
setCurrentIndex((i) => (i + 1) % sessions.length)
|
||||||
|
}, 5000)
|
||||||
|
return () => clearInterval(timer)
|
||||||
|
}, [sessions.length])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-6 bg-background">
|
||||||
|
<h3 className="text-sm font-semibold mb-4">Session Carousel (auto-rotates every 5s)</h3>
|
||||||
|
<div className="border rounded-lg p-4 bg-card">
|
||||||
|
<div className="flex items-center justify-between mb-3">
|
||||||
|
<span className="text-sm font-medium">Session {currentIndex + 1} of {sessions.length}</span>
|
||||||
|
<div className="flex gap-1">
|
||||||
|
{sessions.map((_, i) => (
|
||||||
|
<button
|
||||||
|
key={i}
|
||||||
|
onClick={() => setCurrentIndex(i)}
|
||||||
|
className={`w-2 h-2 rounded-full transition-colors ${
|
||||||
|
i === currentIndex ? 'bg-primary' : 'bg-muted-foreground/30'
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="p-4 bg-accent/20 rounded border">
|
||||||
|
<div className="flex items-center justify-between mb-3">
|
||||||
|
<span className="font-medium">{sessions[currentIndex].name}</span>
|
||||||
|
<span className={`text-xs px-2 py-1 rounded-full ${
|
||||||
|
sessions[currentIndex].status === 'running' ? 'bg-green-500/20 text-green-600' : 'bg-blue-500/20 text-blue-600'
|
||||||
|
}`}>
|
||||||
|
{sessions[currentIndex].status}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
{sessions[currentIndex].tasks.map((task, i) => (
|
||||||
|
<div key={i} className="flex items-center gap-2 text-sm">
|
||||||
|
<div className={`w-3 h-3 rounded ${statusColors[task.status]}`}/>
|
||||||
|
<span className={task.status === 'pending' ? 'text-muted-foreground' : ''}>{task.name}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-between mt-3">
|
||||||
|
<button
|
||||||
|
onClick={() => setCurrentIndex((i) => (i - 1 + sessions.length) % sessions.length)}
|
||||||
|
className="px-3 py-1.5 text-sm border rounded-md hover:bg-accent"
|
||||||
|
>
|
||||||
|
← Previous
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setCurrentIndex((i) => (i + 1) % sessions.length)}
|
||||||
|
className="px-3 py-1.5 text-sm border rounded-md hover:bg-accent"
|
||||||
|
>
|
||||||
|
Next →
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
122
docs/.vitepress/demos/TerminalDashboardOverview.tsx
Normal file
122
docs/.vitepress/demos/TerminalDashboardOverview.tsx
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
/**
|
||||||
|
* Terminal Dashboard Overview Demo
|
||||||
|
* Shows the three-column layout with resizable panes and toolbar
|
||||||
|
*/
|
||||||
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
|
export default function TerminalDashboardOverview() {
|
||||||
|
const [fileSidebarOpen, setFileSidebarOpen] = useState(true)
|
||||||
|
const [sessionSidebarOpen, setSessionSidebarOpen] = useState(true)
|
||||||
|
const [activePanel, setActivePanel] = useState<string | null>(null)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="h-[600px] flex flex-col bg-background relative">
|
||||||
|
{/* Toolbar */}
|
||||||
|
<div className="flex items-center justify-between px-3 py-2 border-b bg-muted/30">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="text-sm font-medium">Terminal Dashboard</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
{['Sessions', 'Files', 'Issues', 'Queue', 'Inspector', 'Scheduler'].map((item) => (
|
||||||
|
<button
|
||||||
|
key={item}
|
||||||
|
onClick={() => {
|
||||||
|
if (item === 'Sessions') setSessionSidebarOpen(!sessionSidebarOpen)
|
||||||
|
else if (item === 'Files') setFileSidebarOpen(!fileSidebarOpen)
|
||||||
|
else setActivePanel(activePanel === item.toLowerCase() ? null : item.toLowerCase())
|
||||||
|
}}
|
||||||
|
className={`px-2 py-1 text-xs rounded transition-colors ${
|
||||||
|
(item === 'Sessions' && sessionSidebarOpen) ||
|
||||||
|
(item === 'Files' && fileSidebarOpen) ||
|
||||||
|
activePanel === item.toLowerCase()
|
||||||
|
? 'bg-primary text-primary-foreground'
|
||||||
|
: 'hover:bg-accent'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{item}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Main Layout */}
|
||||||
|
<div className="flex-1 flex min-h-0">
|
||||||
|
{/* Session Sidebar */}
|
||||||
|
{sessionSidebarOpen && (
|
||||||
|
<div className="w-60 border-r flex flex-col">
|
||||||
|
<div className="px-3 py-2 text-xs font-semibold border-b bg-muted/30">
|
||||||
|
Session Groups
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 p-2 space-y-1 text-sm overflow-auto">
|
||||||
|
{['Active Sessions', 'Completed', 'Archived'].map((group) => (
|
||||||
|
<div key={group}>
|
||||||
|
<div className="flex items-center gap-1 px-2 py-1 rounded hover:bg-accent cursor-pointer">
|
||||||
|
<span className="text-xs">▼</span>
|
||||||
|
<span>{group}</span>
|
||||||
|
</div>
|
||||||
|
<div className="ml-4 space-y-0.5">
|
||||||
|
<div className="px-2 py-1 text-xs text-muted-foreground hover:bg-accent rounded cursor-pointer">
|
||||||
|
Session 1
|
||||||
|
</div>
|
||||||
|
<div className="px-2 py-1 text-xs text-muted-foreground hover:bg-accent rounded cursor-pointer">
|
||||||
|
Session 2
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Terminal Grid */}
|
||||||
|
<div className="flex-1 bg-muted/20 p-2">
|
||||||
|
<div className="grid grid-cols-2 grid-rows-2 gap-2 h-full">
|
||||||
|
{[1, 2, 3, 4].map((i) => (
|
||||||
|
<div key={i} className="bg-background border rounded p-3 font-mono text-xs">
|
||||||
|
<div className="text-green-500 mb-2">$ Terminal {i}</div>
|
||||||
|
<div className="text-muted-foreground">
|
||||||
|
<div>Working directory: /project</div>
|
||||||
|
<div>Type a command to begin...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* File Sidebar */}
|
||||||
|
{fileSidebarOpen && (
|
||||||
|
<div className="w-64 border-l flex flex-col">
|
||||||
|
<div className="px-3 py-2 text-xs font-semibold border-b bg-muted/30">
|
||||||
|
Project Files
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 p-2 text-sm overflow-auto">
|
||||||
|
<div className="space-y-1">
|
||||||
|
{['src', 'docs', 'tests', 'package.json', 'README.md'].map((item) => (
|
||||||
|
<div key={item} className="px-2 py-1 rounded hover:bg-accent cursor-pointer flex items-center gap-2">
|
||||||
|
<span className="text-xs text-muted-foreground">📁</span>
|
||||||
|
{item}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Floating Panel */}
|
||||||
|
{activePanel && (
|
||||||
|
<div className="absolute top-12 right-4 w-80 bg-background border rounded-lg shadow-lg">
|
||||||
|
<div className="flex items-center justify-between px-3 py-2 border-b">
|
||||||
|
<span className="text-sm font-medium capitalize">{activePanel} Panel</span>
|
||||||
|
<button onClick={() => setActivePanel(null)} className="text-xs hover:bg-accent px-2 py-1 rounded">
|
||||||
|
✕
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="p-4 text-sm text-muted-foreground">
|
||||||
|
{activePanel} content placeholder
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
48
docs/.vitepress/demos/TerminalLayoutPresets.tsx
Normal file
48
docs/.vitepress/demos/TerminalLayoutPresets.tsx
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
/**
|
||||||
|
* Terminal Layout Presets Demo
|
||||||
|
* Interactive layout preset buttons
|
||||||
|
*/
|
||||||
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
|
export default function TerminalLayoutPresets() {
|
||||||
|
const [layout, setLayout] = useState('grid-2x2')
|
||||||
|
|
||||||
|
const layouts = {
|
||||||
|
single: 'grid-cols-1 grid-rows-1',
|
||||||
|
'split-h': 'grid-cols-2 grid-rows-1',
|
||||||
|
'split-v': 'grid-cols-1 grid-rows-2',
|
||||||
|
'grid-2x2': 'grid-cols-2 grid-rows-2',
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-6 bg-background space-y-4">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<h3 className="text-sm font-semibold">Terminal Layout Presets</h3>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
{Object.keys(layouts).map((preset) => (
|
||||||
|
<button
|
||||||
|
key={preset}
|
||||||
|
onClick={() => setLayout(preset)}
|
||||||
|
className={`px-3 py-1.5 text-xs rounded transition-colors ${
|
||||||
|
layout === preset
|
||||||
|
? 'bg-primary text-primary-foreground'
|
||||||
|
: 'border hover:bg-accent'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{preset.replace('-', ' ').toUpperCase()}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={`grid gap-2 h-64 ${layouts[layout]}`}>
|
||||||
|
{Array.from({ length: layout === 'single' ? 1 : layout.includes('2x') ? 4 : 2 }).map((_, i) => (
|
||||||
|
<div key={i} className="bg-muted/20 border rounded p-4 font-mono text-xs">
|
||||||
|
<div className="text-green-500">$ Terminal {i + 1}</div>
|
||||||
|
<div className="text-muted-foreground mt-1">Ready for input...</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
57
docs/.vitepress/demos/badge-variants.tsx
Normal file
57
docs/.vitepress/demos/badge-variants.tsx
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
/**
|
||||||
|
* Badge Variants Demo
|
||||||
|
* Shows all available badge variants
|
||||||
|
*/
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
export default function BadgeVariantsDemo() {
|
||||||
|
const variants = [
|
||||||
|
{ name: 'default', class: 'bg-primary text-primary-foreground' },
|
||||||
|
{ name: 'secondary', class: 'bg-secondary text-secondary-foreground' },
|
||||||
|
{ name: 'destructive', class: 'bg-destructive text-destructive-foreground' },
|
||||||
|
{ name: 'outline', class: 'border text-foreground' },
|
||||||
|
{ name: 'success', class: 'bg-success text-white' },
|
||||||
|
{ name: 'warning', class: 'bg-warning text-white' },
|
||||||
|
{ name: 'info', class: 'bg-info text-white' },
|
||||||
|
{ name: 'review', class: 'bg-purple-500 text-white' },
|
||||||
|
{ name: 'gradient', class: 'bg-gradient-to-r from-blue-500 to-purple-500 text-white' },
|
||||||
|
]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-6 bg-background space-y-6">
|
||||||
|
<h3 className="text-sm font-semibold">Badge Variants</h3>
|
||||||
|
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="flex flex-wrap gap-3">
|
||||||
|
{variants.map((v) => (
|
||||||
|
<span
|
||||||
|
key={v.name}
|
||||||
|
className={`inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold ${v.class}`}
|
||||||
|
>
|
||||||
|
{v.name}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Usage Examples */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
<h4 className="text-sm font-medium">Usage Examples</h4>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="flex items-center gap-2 p-2 border rounded">
|
||||||
|
<span className="font-medium">Status:</span>
|
||||||
|
<span className="inline-flex items-center rounded-full bg-success px-2 py-0.5 text-xs text-white">Active</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2 p-2 border rounded">
|
||||||
|
<span className="font-medium">Priority:</span>
|
||||||
|
<span className="inline-flex items-center rounded-full bg-destructive px-2 py-0.5 text-xs text-white">High</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2 p-2 border rounded">
|
||||||
|
<span className="font-medium">Type:</span>
|
||||||
|
<span className="inline-flex items-center rounded-full bg-info px-2 py-0.5 text-xs text-white">Feature</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,52 +1,80 @@
|
|||||||
import React from 'react'
|
|
||||||
import { Button } from '@/components/ui/Button'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Button Variants Demo
|
* Button Variants Demo
|
||||||
* Shows all 8 visual variants and 4 sizes of the Button component
|
* Shows all visual variants 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() {
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
|
export default function ButtonVariantsDemo() {
|
||||||
|
const [variant, setVariant] = useState('default')
|
||||||
|
|
||||||
|
const variants = ['default', 'destructive', 'outline', 'secondary', 'ghost', 'link', 'gradient']
|
||||||
|
|
||||||
|
const getButtonClass = (v: string) => {
|
||||||
|
const base = 'px-4 py-2 rounded-md text-sm transition-colors'
|
||||||
|
switch (v) {
|
||||||
|
case 'default': return `${base} bg-primary text-primary-foreground hover:opacity-90`
|
||||||
|
case 'destructive': return `${base} bg-destructive text-destructive-foreground hover:opacity-90`
|
||||||
|
case 'outline': return `${base} border bg-background hover:bg-accent`
|
||||||
|
case 'secondary': return `${base} bg-secondary text-secondary-foreground hover:opacity-80`
|
||||||
|
case 'ghost': return `${base} hover:bg-accent`
|
||||||
|
case 'link': return `${base} text-primary underline-offset-4 hover:underline`
|
||||||
|
case 'gradient': return `${base} bg-gradient-to-r from-blue-500 to-purple-500 text-white hover:opacity-90`
|
||||||
|
default: return base
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-6 p-4">
|
<div className="p-6 bg-background space-y-6">
|
||||||
{/* Variants Section */}
|
<h3 className="text-sm font-semibold">Button Variants</h3>
|
||||||
<div>
|
|
||||||
<h3 className="text-sm font-semibold mb-3 text-muted-foreground">Variants</h3>
|
{/* Variant Selector */}
|
||||||
<div className="flex flex-wrap gap-3">
|
<div className="space-y-3">
|
||||||
<Button variant="default">Default</Button>
|
<label className="text-sm font-medium">Select Variant</label>
|
||||||
<Button variant="destructive">Destructive</Button>
|
<div className="flex flex-wrap gap-2">
|
||||||
<Button variant="outline">Outline</Button>
|
{variants.map((v) => (
|
||||||
<Button variant="secondary">Secondary</Button>
|
<button
|
||||||
<Button variant="ghost">Ghost</Button>
|
key={v}
|
||||||
<Button variant="link">Link</Button>
|
onClick={() => setVariant(v)}
|
||||||
<Button variant="gradient">Gradient</Button>
|
className={`px-3 py-1.5 text-xs rounded capitalize transition-colors ${
|
||||||
<Button variant="gradientPrimary">Gradient Primary</Button>
|
variant === v ? 'bg-primary text-primary-foreground' : 'border hover:bg-accent'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{v}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Sizes Section */}
|
{/* Preview */}
|
||||||
<div>
|
<div className="space-y-3">
|
||||||
<h3 className="text-sm font-semibold mb-3 text-muted-foreground">Sizes</h3>
|
<label className="text-sm font-medium">Preview</label>
|
||||||
<div className="flex flex-wrap items-center gap-3">
|
<div className="p-4 border rounded-lg bg-muted/20">
|
||||||
<Button size="sm">Small</Button>
|
<button className={getButtonClass(variant)}>
|
||||||
<Button size="default">Default</Button>
|
Button
|
||||||
<Button size="lg">Large</Button>
|
</button>
|
||||||
<Button size="icon">🔍</Button>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* All Variants */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
<label className="text-sm font-medium">All Variants</label>
|
||||||
|
<div className="flex flex-wrap gap-3 p-4 border rounded-lg">
|
||||||
|
{variants.map((v) => (
|
||||||
|
<button key={v} className={getButtonClass(v)}>
|
||||||
|
{v.charAt(0).toUpperCase() + v.slice(1)}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Sizes */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
<label className="text-sm font-medium">Sizes</label>
|
||||||
|
<div className="flex items-center gap-3 flex-wrap p-4 border rounded-lg">
|
||||||
|
<button className={`${getButtonClass(variant)} h-8 px-3 text-xs`}>Small</button>
|
||||||
|
<button className={`${getButtonClass(variant)} h-10 px-4`}>Default</button>
|
||||||
|
<button className={`${getButtonClass(variant)} h-11 px-8`}>Large</button>
|
||||||
|
<button className={`${getButtonClass(variant)} h-10 w-10`}>⚙</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
71
docs/.vitepress/demos/card-variants.tsx
Normal file
71
docs/.vitepress/demos/card-variants.tsx
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/**
|
||||||
|
* Card Variants Demo
|
||||||
|
* Shows different card layouts
|
||||||
|
*/
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
export default function CardVariantsDemo() {
|
||||||
|
return (
|
||||||
|
<div className="p-6 bg-background space-y-6">
|
||||||
|
<h3 className="text-sm font-semibold">Card Variants</h3>
|
||||||
|
|
||||||
|
<div className="grid md:grid-cols-2 gap-4">
|
||||||
|
{/* Basic Card */}
|
||||||
|
<div className="border rounded-lg">
|
||||||
|
<div className="p-4 border-b">
|
||||||
|
<h4 className="font-semibold">Basic Card</h4>
|
||||||
|
<p className="text-sm text-muted-foreground">With header and content</p>
|
||||||
|
</div>
|
||||||
|
<div className="p-4">
|
||||||
|
<p className="text-sm">Card content goes here.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Card with Footer */}
|
||||||
|
<div className="border rounded-lg">
|
||||||
|
<div className="p-4 border-b">
|
||||||
|
<h4 className="font-semibold">Card with Footer</h4>
|
||||||
|
</div>
|
||||||
|
<div className="p-4">
|
||||||
|
<p className="text-sm">Main content area.</p>
|
||||||
|
</div>
|
||||||
|
<div className="p-4 border-t bg-muted/20">
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<button className="px-3 py-1.5 text-xs bg-primary text-primary-foreground rounded">Action</button>
|
||||||
|
<button className="px-3 py-1.5 text-xs border rounded">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Gradient Border Card */}
|
||||||
|
<div className="border-2 border-transparent bg-gradient-to-r from-blue-500 to-purple-500 p-[2px] rounded-lg">
|
||||||
|
<div className="bg-background rounded-lg p-4">
|
||||||
|
<h4 className="font-semibold">Gradient Border</h4>
|
||||||
|
<p className="text-sm text-muted-foreground mt-1">Card with gradient border effect.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Settings Card */}
|
||||||
|
<div className="border rounded-lg">
|
||||||
|
<div className="p-4 border-b">
|
||||||
|
<h4 className="font-semibold">Settings</h4>
|
||||||
|
</div>
|
||||||
|
<div className="p-4 space-y-3">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-sm">Enable notifications</span>
|
||||||
|
<div className="w-9 h-5 bg-primary rounded-full relative">
|
||||||
|
<div className="absolute right-[2px] top-[2px] w-4 h-4 bg-white rounded-full" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-sm">Dark mode</span>
|
||||||
|
<div className="w-9 h-5 bg-muted rounded-full relative">
|
||||||
|
<div className="absolute left-[2px] top-[2px] w-4 h-4 bg-white rounded-full" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
79
docs/.vitepress/demos/checkbox-variants.tsx
Normal file
79
docs/.vitepress/demos/checkbox-variants.tsx
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
/**
|
||||||
|
* Checkbox Variants Demo
|
||||||
|
* Shows different checkbox states
|
||||||
|
*/
|
||||||
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
|
export default function CheckboxVariantsDemo() {
|
||||||
|
const [checked, setChecked] = useState({ a: true, b: false, c: true })
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-6 bg-background space-y-6">
|
||||||
|
<h3 className="text-sm font-semibold">Checkbox States</h3>
|
||||||
|
|
||||||
|
<div className="space-y-4">
|
||||||
|
{/* Checked */}
|
||||||
|
<label className="flex items-center gap-3 cursor-pointer">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={checked.a}
|
||||||
|
onChange={(e) => setChecked({ ...checked, a: e.target.checked })}
|
||||||
|
className="h-4 w-4 rounded border border-primary accent-primary"
|
||||||
|
/>
|
||||||
|
<span className="text-sm">Checked checkbox</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
{/* Unchecked */}
|
||||||
|
<label className="flex items-center gap-3 cursor-pointer">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={checked.b}
|
||||||
|
onChange={(e) => setChecked({ ...checked, b: e.target.checked })}
|
||||||
|
className="h-4 w-4 rounded border border-primary accent-primary"
|
||||||
|
/>
|
||||||
|
<span className="text-sm">Unchecked checkbox</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
{/* Disabled */}
|
||||||
|
<label className="flex items-center gap-3 cursor-not-allowed opacity-50">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
disabled
|
||||||
|
className="h-4 w-4 rounded border border-primary"
|
||||||
|
/>
|
||||||
|
<span className="text-sm">Disabled checkbox</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
{/* Disabled Checked */}
|
||||||
|
<label className="flex items-center gap-3 cursor-not-allowed opacity-50">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
disabled
|
||||||
|
defaultChecked
|
||||||
|
className="h-4 w-4 rounded border border-primary accent-primary"
|
||||||
|
/>
|
||||||
|
<span className="text-sm">Disabled checked checkbox</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Usage Example */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
<h4 className="text-sm font-medium">Usage Example</h4>
|
||||||
|
<div className="p-4 border rounded-lg space-y-2">
|
||||||
|
<label className="flex items-center gap-3 cursor-pointer">
|
||||||
|
<input type="checkbox" className="h-4 w-4 rounded border accent-primary" />
|
||||||
|
<span className="text-sm">Accept terms and conditions</span>
|
||||||
|
</label>
|
||||||
|
<label className="flex items-center gap-3 cursor-pointer">
|
||||||
|
<input type="checkbox" className="h-4 w-4 rounded border accent-primary" />
|
||||||
|
<span className="text-sm">Subscribe to newsletter</span>
|
||||||
|
</label>
|
||||||
|
<label className="flex items-center gap-3 cursor-pointer">
|
||||||
|
<input type="checkbox" className="h-4 w-4 rounded border accent-primary" />
|
||||||
|
<span className="text-sm">Remember me</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
95
docs/.vitepress/demos/input-variants.tsx
Normal file
95
docs/.vitepress/demos/input-variants.tsx
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
/**
|
||||||
|
* Input Variants Demo
|
||||||
|
* Shows all input states
|
||||||
|
*/
|
||||||
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
|
export default function InputVariantsDemo() {
|
||||||
|
const [value, setValue] = useState('')
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-6 bg-background space-y-6">
|
||||||
|
<h3 className="text-sm font-semibold">Input States</h3>
|
||||||
|
|
||||||
|
<div className="grid md:grid-cols-2 gap-6">
|
||||||
|
{/* Default */}
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="text-sm font-medium">Default</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Enter text..."
|
||||||
|
className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* With Value */}
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="text-sm font-medium">With Value</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value="John Doe"
|
||||||
|
readOnly
|
||||||
|
className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Error State */}
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="text-sm font-medium">Error State</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Invalid input"
|
||||||
|
className="flex h-10 w-full rounded-md border border-destructive bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-destructive"
|
||||||
|
/>
|
||||||
|
<p className="text-xs text-destructive">This field is required</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Disabled */}
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="text-sm font-medium">Disabled</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
disabled
|
||||||
|
placeholder="Disabled input"
|
||||||
|
className="flex h-10 w-full rounded-md border border-input bg-muted px-3 py-2 text-sm opacity-50 cursor-not-allowed"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* With Icon */}
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="text-sm font-medium">Search Input</label>
|
||||||
|
<div className="relative">
|
||||||
|
<span className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground">🔍</span>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Search..."
|
||||||
|
className="flex h-10 w-full rounded-md border border-input bg-background pl-10 pr-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Controlled */}
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="text-sm font-medium">Controlled Input</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={value}
|
||||||
|
onChange={(e) => setValue(e.target.value)}
|
||||||
|
placeholder="Type something..."
|
||||||
|
className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
|
||||||
|
/>
|
||||||
|
<p className="text-xs text-muted-foreground">Value: {value || '(empty)'}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Textarea */}
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="text-sm font-medium">Textarea</label>
|
||||||
|
<textarea
|
||||||
|
placeholder="Enter multi-line text..."
|
||||||
|
className="flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
101
docs/.vitepress/demos/select-variants.tsx
Normal file
101
docs/.vitepress/demos/select-variants.tsx
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
/**
|
||||||
|
* Select Variants Demo
|
||||||
|
* Shows different select configurations
|
||||||
|
*/
|
||||||
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
|
export default function SelectVariantsDemo() {
|
||||||
|
const [selected, setSelected] = useState('')
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-6 bg-background space-y-6">
|
||||||
|
<h3 className="text-sm font-semibold">Select Configurations</h3>
|
||||||
|
|
||||||
|
<div className="grid md:grid-cols-2 gap-6">
|
||||||
|
{/* Basic Select */}
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="text-sm font-medium">Basic Select</label>
|
||||||
|
<select className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring">
|
||||||
|
<option value="">Choose an option</option>
|
||||||
|
<option value="1">Option 1</option>
|
||||||
|
<option value="2">Option 2</option>
|
||||||
|
<option value="3">Option 3</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* With Labels */}
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="text-sm font-medium">With Option Groups</label>
|
||||||
|
<select className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring">
|
||||||
|
<optgroup label="Fruits">
|
||||||
|
<option value="apple">Apple</option>
|
||||||
|
<option value="banana">Banana</option>
|
||||||
|
<option value="orange">Orange</option>
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="Vegetables">
|
||||||
|
<option value="carrot">Carrot</option>
|
||||||
|
<option value="broccoli">Broccoli</option>
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Controlled Select */}
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="text-sm font-medium">Controlled Select</label>
|
||||||
|
<select
|
||||||
|
value={selected}
|
||||||
|
onChange={(e) => setSelected(e.target.value)}
|
||||||
|
className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
|
||||||
|
>
|
||||||
|
<option value="">Select...</option>
|
||||||
|
<option value="react">React</option>
|
||||||
|
<option value="vue">Vue</option>
|
||||||
|
<option value="angular">Angular</option>
|
||||||
|
</select>
|
||||||
|
<p className="text-xs text-muted-foreground">Selected: {selected || '(none)'}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Disabled Select */}
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="text-sm font-medium">Disabled Select</label>
|
||||||
|
<select
|
||||||
|
disabled
|
||||||
|
className="flex h-10 w-full rounded-md border border-input bg-muted px-3 py-2 text-sm opacity-50 cursor-not-allowed"
|
||||||
|
>
|
||||||
|
<option value="">Disabled select</option>
|
||||||
|
<option value="1">Option 1</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* With Separators */}
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="text-sm font-medium">With Separators</label>
|
||||||
|
<select className="flex h-10 w-full max-w-md rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring">
|
||||||
|
<option value="">Select an action</option>
|
||||||
|
<option value="edit">Edit</option>
|
||||||
|
<option value="copy">Copy</option>
|
||||||
|
<option value="move">Move</option>
|
||||||
|
{/* Visual separator via disabled option */}
|
||||||
|
<option disabled>──────────</option>
|
||||||
|
<option value="delete" className="text-destructive">Delete</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Multiple Select */}
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="text-sm font-medium">Multiple Select (Hold Ctrl/Cmd)</label>
|
||||||
|
<select
|
||||||
|
multiple
|
||||||
|
className="flex min-h-[100px] w-full max-w-md rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
|
||||||
|
>
|
||||||
|
<option value="html">HTML</option>
|
||||||
|
<option value="css">CSS</option>
|
||||||
|
<option value="js">JavaScript</option>
|
||||||
|
<option value="ts">TypeScript</option>
|
||||||
|
<option value="react">React</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -264,7 +264,7 @@ function hideTooltip() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 640px) {
|
@media (max-width: 768px) {
|
||||||
.agent-orchestration {
|
.agent-orchestration {
|
||||||
padding: 2rem 1rem;
|
padding: 2rem 1rem;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, onUnmounted, computed } from 'vue'
|
import { ref, onMounted, onUnmounted, computed } from 'vue'
|
||||||
import { createRoot } from 'react-dom/client'
|
import { createRoot } from 'react-dom/client'
|
||||||
|
import React from 'react'
|
||||||
import type { Root } from 'react-dom/client'
|
import type { Root } from 'react-dom/client'
|
||||||
import CodeViewer from './CodeViewer.vue'
|
import CodeViewer from './CodeViewer.vue'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
name: string // Demo component name
|
id: string // Demo unique ID
|
||||||
|
name: string // Demo component name
|
||||||
file?: string // Optional: explicit source file
|
file?: string // Optional: explicit source file
|
||||||
|
hasInlineCode?: boolean // Whether inline code is provided
|
||||||
|
inlineCode?: string // Base64 encoded inline code (for display)
|
||||||
|
virtualModule?: string // Virtual module path for inline demos
|
||||||
height?: string // Demo container height
|
height?: string // Demo container height
|
||||||
expandable?: boolean // Allow expand/collapse
|
expandable?: boolean // Allow expand/collapse
|
||||||
showCode?: boolean // Show code tab
|
showCode?: boolean // Show code tab
|
||||||
@@ -14,6 +19,9 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
hasInlineCode: false,
|
||||||
|
inlineCode: '',
|
||||||
|
virtualModule: '',
|
||||||
height: 'auto',
|
height: 'auto',
|
||||||
expandable: true,
|
expandable: true,
|
||||||
showCode: true,
|
showCode: true,
|
||||||
@@ -33,25 +41,64 @@ const demoTitle = computed(() => props.title || props.name)
|
|||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
try {
|
try {
|
||||||
// Dynamically import demo component
|
// Handle inline code mode with virtual module
|
||||||
const demoModule = await import(`../demos/${props.name}.tsx`)
|
if (props.hasInlineCode && props.virtualModule) {
|
||||||
const DemoComponent = demoModule.default || demoModule[props.name]
|
// Decode base64 for source code display
|
||||||
|
if (props.inlineCode) {
|
||||||
|
sourceCode.value = atob(props.inlineCode)
|
||||||
|
}
|
||||||
|
|
||||||
if (!DemoComponent) {
|
// Dynamically import the virtual module
|
||||||
throw new Error(`Demo component "${props.name}" not found`)
|
// @vite-ignore is needed for dynamic imports with variable paths
|
||||||
}
|
const inlineDemoModule = await import(/* @vite-ignore */ props.virtualModule)
|
||||||
|
const DemoComponent = inlineDemoModule.default
|
||||||
|
|
||||||
// Mount React component
|
if (!DemoComponent) {
|
||||||
if (demoRoot.value) {
|
throw new Error(`Inline demo component "${props.name}" not found in virtual module`)
|
||||||
reactRoot.value = createRoot(demoRoot.value)
|
}
|
||||||
reactRoot.value.render(DemoComponent)
|
|
||||||
|
|
||||||
// Extract source code
|
// Mount React component properly
|
||||||
try {
|
if (demoRoot.value) {
|
||||||
const rawModule = await import(`../demos/${props.name}.tsx?raw`)
|
reactRoot.value = createRoot(demoRoot.value)
|
||||||
sourceCode.value = rawModule.default || rawModule
|
reactRoot.value.render(React.createElement(DemoComponent))
|
||||||
} catch {
|
}
|
||||||
sourceCode.value = '// Source code not available'
|
} else if (props.hasInlineCode && props.inlineCode) {
|
||||||
|
// Fallback: inline code without virtual module (display only, no execution)
|
||||||
|
const decodedCode = atob(props.inlineCode)
|
||||||
|
sourceCode.value = decodedCode
|
||||||
|
|
||||||
|
if (demoRoot.value) {
|
||||||
|
// Show a message that preview is not available
|
||||||
|
const noticeEl = document.createElement('div')
|
||||||
|
noticeEl.className = 'inline-demo-notice'
|
||||||
|
noticeEl.innerHTML = `
|
||||||
|
<p><strong>Preview not available</strong></p>
|
||||||
|
<p>Inline demo "${props.name}" requires virtual module support.</p>
|
||||||
|
<p>Check the "Code" tab to see the source.</p>
|
||||||
|
`
|
||||||
|
demoRoot.value.appendChild(noticeEl)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Dynamically import demo component from file
|
||||||
|
const demoModule = await import(`../demos/${props.name}.tsx`)
|
||||||
|
const DemoComponent = demoModule.default || demoModule[props.name]
|
||||||
|
|
||||||
|
if (!DemoComponent) {
|
||||||
|
throw new Error(`Demo component "${props.name}" not found`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mount React component
|
||||||
|
if (demoRoot.value) {
|
||||||
|
reactRoot.value = createRoot(demoRoot.value)
|
||||||
|
reactRoot.value.render(DemoComponent)
|
||||||
|
|
||||||
|
// Extract source code
|
||||||
|
try {
|
||||||
|
const rawModule = await import(`../demos/${props.name}.tsx?raw`)
|
||||||
|
sourceCode.value = rawModule.default || rawModule
|
||||||
|
} catch {
|
||||||
|
sourceCode.value = '// Source code not available'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -250,6 +297,43 @@ const switchTab = (tab: 'preview' | 'code') => {
|
|||||||
height: auto !important;
|
height: auto !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Inline demo notice (fallback mode) */
|
||||||
|
:deep(.inline-demo-notice) {
|
||||||
|
padding: 20px;
|
||||||
|
background: var(--vp-c-warning-soft);
|
||||||
|
border-radius: 4px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.inline-demo-notice p) {
|
||||||
|
margin: 8px 0;
|
||||||
|
color: var(--vp-c-text-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.inline-demo-notice strong) {
|
||||||
|
color: var(--vp-c-warning-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Inline demo preview styles */
|
||||||
|
:deep(.inline-demo-preview) {
|
||||||
|
padding: 16px;
|
||||||
|
background: var(--vp-c-bg-soft);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.inline-demo-preview button) {
|
||||||
|
margin: 4px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid var(--vp-c-border);
|
||||||
|
background: var(--vp-c-bg);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.inline-demo-preview button:hover) {
|
||||||
|
background: var(--vp-c-bg-soft);
|
||||||
|
}
|
||||||
|
|
||||||
/* Responsive */
|
/* Responsive */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.demo-header {
|
.demo-header {
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref, onMounted } from 'vue'
|
import { computed, ref, onMounted, onBeforeUnmount, nextTick } from 'vue'
|
||||||
import { useData } from 'vitepress'
|
import { useData } from 'vitepress'
|
||||||
|
|
||||||
const { site, lang, page } = useData()
|
const { site, lang, page } = useData()
|
||||||
|
|
||||||
const isOpen = ref(false)
|
const isOpen = ref(false)
|
||||||
const switcherRef = ref<HTMLElement>()
|
const switcherRef = ref<HTMLElement>()
|
||||||
|
const buttonRef = ref<HTMLElement>()
|
||||||
|
const dropdownPosition = ref({ top: 0, left: 0 })
|
||||||
|
|
||||||
// Get available locales from VitePress config
|
// Get available locales from VitePress config
|
||||||
const locales = computed(() => {
|
const locales = computed(() => {
|
||||||
@@ -36,46 +38,101 @@ const getAltLink = (localeCode: string) => {
|
|||||||
|
|
||||||
const switchLanguage = (localeCode: string) => {
|
const switchLanguage = (localeCode: string) => {
|
||||||
const altLink = getAltLink(localeCode)
|
const altLink = getAltLink(localeCode)
|
||||||
|
isOpen.value = false
|
||||||
window.location.href = altLink
|
window.location.href = altLink
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close dropdown when clicking outside
|
// Calculate dropdown position
|
||||||
onMounted(() => {
|
const updatePosition = () => {
|
||||||
const handleClickOutside = (e: MouseEvent) => {
|
if (buttonRef.value) {
|
||||||
if (switcherRef.value && !switcherRef.value.contains(e.target as Node)) {
|
const rect = buttonRef.value.getBoundingClientRect()
|
||||||
isOpen.value = false
|
const isMobile = window.innerWidth <= 768
|
||||||
|
|
||||||
|
if (isMobile) {
|
||||||
|
dropdownPosition.value = {
|
||||||
|
top: rect.bottom + 8,
|
||||||
|
left: 12
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dropdownPosition.value = {
|
||||||
|
top: rect.bottom + 4,
|
||||||
|
left: rect.right - 150
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const toggleDropdown = async () => {
|
||||||
|
isOpen.value = !isOpen.value
|
||||||
|
if (isOpen.value) {
|
||||||
|
await nextTick()
|
||||||
|
updatePosition()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close dropdown when clicking outside
|
||||||
|
const handleClickOutside = (e: MouseEvent) => {
|
||||||
|
if (switcherRef.value && !switcherRef.value.contains(e.target as Node)) {
|
||||||
|
isOpen.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle scroll to close dropdown
|
||||||
|
const handleScroll = () => {
|
||||||
|
if (isOpen.value) {
|
||||||
|
isOpen.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
document.addEventListener('click', handleClickOutside)
|
document.addEventListener('click', handleClickOutside)
|
||||||
|
window.addEventListener('scroll', handleScroll, true)
|
||||||
|
window.addEventListener('resize', updatePosition)
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
document.removeEventListener('click', handleClickOutside)
|
||||||
|
window.removeEventListener('scroll', handleScroll, true)
|
||||||
|
window.removeEventListener('resize', updatePosition)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div ref="switcherRef" class="language-switcher">
|
<div ref="switcherRef" class="language-switcher">
|
||||||
<button
|
<button
|
||||||
|
ref="buttonRef"
|
||||||
class="switcher-button"
|
class="switcher-button"
|
||||||
:aria-expanded="isOpen"
|
:aria-expanded="isOpen"
|
||||||
aria-label="Switch language"
|
aria-label="Switch language"
|
||||||
@click="isOpen = !isOpen"
|
@click.stop="toggleDropdown"
|
||||||
>
|
>
|
||||||
<span class="current-locale">{{ currentLocale?.label }}</span>
|
<span class="current-locale">{{ currentLocale?.label }}</span>
|
||||||
<span class="dropdown-icon" :class="{ open: isOpen }">▼</span>
|
<span class="dropdown-icon" :class="{ open: isOpen }">▼</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<Transition name="fade">
|
<Teleport to="body">
|
||||||
<ul v-if="isOpen" class="locale-list">
|
<Transition name="fade">
|
||||||
<li v-for="locale in locales" :key="locale.code">
|
<ul
|
||||||
<button
|
v-if="isOpen"
|
||||||
class="locale-button"
|
class="locale-list"
|
||||||
:class="{ active: locale.code === lang }"
|
:style="{
|
||||||
@click="switchLanguage(locale.code)"
|
top: dropdownPosition.top + 'px',
|
||||||
>
|
left: dropdownPosition.left + 'px'
|
||||||
<span class="locale-label">{{ locale.label }}</span>
|
}"
|
||||||
<span v-if="locale.code === lang" class="check-icon">✓</span>
|
>
|
||||||
</button>
|
<li v-for="locale in locales" :key="locale.code">
|
||||||
</li>
|
<button
|
||||||
</ul>
|
class="locale-button"
|
||||||
</Transition>
|
:class="{ active: locale.code === lang }"
|
||||||
|
@click="switchLanguage(locale.code)"
|
||||||
|
>
|
||||||
|
<span class="locale-label">{{ locale.label }}</span>
|
||||||
|
<span v-if="locale.code === lang" class="check-icon">✓</span>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</Transition>
|
||||||
|
</Teleport>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -117,19 +174,19 @@ onMounted(() => {
|
|||||||
transform: rotate(180deg);
|
transform: rotate(180deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Locale list - rendered at body level via Teleport */
|
||||||
.locale-list {
|
.locale-list {
|
||||||
position: absolute;
|
position: fixed;
|
||||||
top: calc(100% + 4px);
|
|
||||||
right: 0;
|
|
||||||
min-width: 150px;
|
min-width: 150px;
|
||||||
|
max-width: calc(100vw - 24px);
|
||||||
list-style: none;
|
list-style: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 4px;
|
padding: 8px 0;
|
||||||
background: var(--vp-c-bg);
|
background: var(--vp-c-bg);
|
||||||
border: 1px solid var(--vp-c-border);
|
border: 1px solid var(--vp-c-border);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
|
||||||
z-index: 100;
|
z-index: 9999;
|
||||||
}
|
}
|
||||||
|
|
||||||
.locale-button {
|
.locale-button {
|
||||||
@@ -137,14 +194,14 @@ onMounted(() => {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 8px 12px;
|
padding: 10px 16px;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 4px;
|
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: var(--vp-c-text-1);
|
color: var(--vp-c-text-1);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background 0.2s;
|
transition: background 0.2s;
|
||||||
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.locale-button:hover {
|
.locale-button:hover {
|
||||||
@@ -173,14 +230,24 @@ onMounted(() => {
|
|||||||
.fade-enter-from,
|
.fade-enter-from,
|
||||||
.fade-leave-to {
|
.fade-leave-to {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateY(-4px);
|
transform: translateY(-8px);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Responsive */
|
/* Responsive */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.locale-list {
|
.locale-list {
|
||||||
right: auto;
|
width: calc(100vw - 24px) !important;
|
||||||
left: 0;
|
max-width: 300px !important;
|
||||||
|
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.25) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switcher-button {
|
||||||
|
padding: 8px 12px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.locale-button {
|
||||||
|
padding: 12px 16px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1275,23 +1275,199 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
* Responsive
|
* Responsive (Standardized Breakpoints)
|
||||||
|
* Mobile: < 768px | Tablet: 768px-1024px | Desktop: > 1024px
|
||||||
|
* WCAG 2.1 AA: Touch targets min 44x44px
|
||||||
* ============================================ */
|
* ============================================ */
|
||||||
@media (max-width: 1100px) {
|
|
||||||
|
/* Tablet (768px - 1024px) */
|
||||||
|
@media (max-width: 1024px) {
|
||||||
.features-grid { grid-template-columns: repeat(2, 1fr); }
|
.features-grid { grid-template-columns: repeat(2, 1fr); }
|
||||||
|
.hero-container { gap: 1.5rem; }
|
||||||
|
.hero-title { font-size: 2.25rem; }
|
||||||
|
.section-container { padding: 0 1.5rem; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 960px) {
|
/* Mobile (< 768px) */
|
||||||
.hero-container, .json-grid, .quickstart-layout { grid-template-columns: 1fr; text-align: center; }
|
@media (max-width: 768px) {
|
||||||
.hero-subtitle { margin-left: auto; margin-right: auto; }
|
/* Hero Section - add extra padding-top to clear fixed nav (56px) */
|
||||||
.hero-actions { justify-content: center; }
|
.hero-section {
|
||||||
.hero-title { font-size: 2.5rem; }
|
padding: 4.5rem 0 2rem;
|
||||||
.logic-panel { grid-template-columns: 1fr; }
|
background:
|
||||||
.features-grid { grid-template-columns: 1fr; max-width: 420px; margin: 0 auto; }
|
radial-gradient(ellipse 150% 100% at 50% 20%, var(--vp-c-brand-soft) 0%, transparent 60%),
|
||||||
.feature-card { text-align: center; }
|
var(--vp-c-bg);
|
||||||
.feature-icon-box { margin-left: auto; margin-right: auto; }
|
}
|
||||||
.quickstart-info { text-align: center; }
|
.hero-container {
|
||||||
.qs-step { flex-direction: column; align-items: center; text-align: center; }
|
grid-template-columns: 1fr;
|
||||||
.cta-actions { flex-direction: column; }
|
text-align: center;
|
||||||
|
padding: 0 12px;
|
||||||
|
gap: 2rem;
|
||||||
|
max-width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.hero-content { order: 1; }
|
||||||
|
.hero-visual { order: 2; }
|
||||||
|
.hero-title {
|
||||||
|
font-size: 1.875rem;
|
||||||
|
line-height: 1.2;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
.hero-subtitle {
|
||||||
|
font-size: 1rem;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
margin-bottom: 1.75rem;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
.hero-actions {
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
.btn-primary,
|
||||||
|
.btn-secondary {
|
||||||
|
min-height: 44px;
|
||||||
|
min-width: 44px;
|
||||||
|
padding: 0.75rem 1.25rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
.hero-visual { min-height: 200px; }
|
||||||
|
|
||||||
|
/* Section Container */
|
||||||
|
.section-container {
|
||||||
|
padding: 0 12px;
|
||||||
|
max-width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Features Grid */
|
||||||
|
.features-section { padding: 3rem 0; }
|
||||||
|
.section-title {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
.features-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
max-width: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
.feature-card {
|
||||||
|
text-align: center;
|
||||||
|
padding: 1.5rem;
|
||||||
|
min-height: auto;
|
||||||
|
}
|
||||||
|
.feature-icon-box {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
margin: 0 auto 1rem;
|
||||||
|
}
|
||||||
|
.feature-card h3 { font-size: 1.05rem; }
|
||||||
|
.feature-card p { font-size: 0.9rem; }
|
||||||
|
|
||||||
|
/* Pipeline Section */
|
||||||
|
.pipeline-section { padding: 3rem 0; }
|
||||||
|
.section-header h2 { font-size: 1.5rem; }
|
||||||
|
.section-header p { font-size: 0.9rem; }
|
||||||
|
.pipeline-card {
|
||||||
|
padding: 1.25rem;
|
||||||
|
max-width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
.pipeline-flow {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
.stage-node { flex-direction: row; justify-content: flex-start; gap: 1rem; }
|
||||||
|
.stage-icon { width: 44px; height: 44px; min-width: 44px; }
|
||||||
|
.stage-info { text-align: left; }
|
||||||
|
.stage-info h4 { margin-top: 0; font-size: 0.9rem; }
|
||||||
|
.stage-info p { font-size: 0.8rem; }
|
||||||
|
.logic-panel { grid-template-columns: 1fr; gap: 1rem; }
|
||||||
|
.law-content, .log-content { padding: 1rem; font-size: 0.8rem; min-height: 60px; }
|
||||||
|
|
||||||
|
/* JSON Section */
|
||||||
|
.json-section { padding: 0; overflow-x: hidden; }
|
||||||
|
.json-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
text-align: center;
|
||||||
|
padding: 3rem 12px;
|
||||||
|
gap: 2rem;
|
||||||
|
max-width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.json-text h2 { font-size: 1.5rem; }
|
||||||
|
.json-text p { font-size: 0.95rem; }
|
||||||
|
.json-benefits { text-align: left; }
|
||||||
|
.json-benefits li { font-size: 0.9rem; }
|
||||||
|
.json-code {
|
||||||
|
padding: 1rem;
|
||||||
|
max-width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
.json-pre { font-size: 0.75rem; }
|
||||||
|
|
||||||
|
/* Quick Start */
|
||||||
|
.quickstart-section { padding: 3rem 0; }
|
||||||
|
.quickstart-layout {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
text-align: center;
|
||||||
|
gap: 2rem;
|
||||||
|
}
|
||||||
|
.quickstart-info { order: 2; }
|
||||||
|
.quickstart-terminal { order: 1; }
|
||||||
|
.qs-step {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
.qs-step-num { width: 44px; height: 44px; min-width: 44px; }
|
||||||
|
.qs-terminal-body { min-height: 200px; padding: 1rem; }
|
||||||
|
.qs-terminal-header { padding: 0.4rem 0.6rem; }
|
||||||
|
.qs-tab { min-height: 36px; padding: 0.25rem 0.5rem; }
|
||||||
|
|
||||||
|
/* CTA Section */
|
||||||
|
.cta-section { padding: 3rem 0; }
|
||||||
|
.cta-card {
|
||||||
|
padding: 2rem 1rem;
|
||||||
|
margin: 0 12px;
|
||||||
|
max-width: calc(100% - 24px);
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.cta-card h2 { font-size: 1.5rem; }
|
||||||
|
.cta-card p { font-size: 0.95rem; margin-bottom: 1.5rem; }
|
||||||
|
.cta-actions {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.75rem;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.cta-actions .btn-primary,
|
||||||
|
.cta-actions .btn-outline,
|
||||||
|
.cta-actions .btn-secondary {
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
min-height: 48px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Small Mobile (< 480px) */
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.hero-title { font-size: 1.5rem; }
|
||||||
|
.hero-actions { flex-direction: column; width: 100%; }
|
||||||
|
.hero-actions .btn-primary,
|
||||||
|
.hero-actions .btn-secondary {
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.section-title { font-size: 1.25rem; }
|
||||||
|
.pipeline-flow { gap: 0.75rem; }
|
||||||
|
.stage-icon { width: 40px; height: 40px; border-radius: 10px; }
|
||||||
|
.json-text h2 { font-size: 1.25rem; }
|
||||||
|
.cta-card { padding: 1.5rem 1rem; }
|
||||||
|
.cta-card h2 { font-size: 1.25rem; }
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -35,10 +35,16 @@ export const STATUS_COLORS = {
|
|||||||
const STORAGE_KEY_THEME = 'ccw-theme'
|
const STORAGE_KEY_THEME = 'ccw-theme'
|
||||||
const STORAGE_KEY_COLOR_MODE = 'ccw-color-mode'
|
const STORAGE_KEY_COLOR_MODE = 'ccw-color-mode'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if running in browser environment
|
||||||
|
*/
|
||||||
|
const isBrowser = typeof window !== 'undefined' && typeof localStorage !== 'undefined'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get current theme from localStorage or default
|
* Get current theme from localStorage or default
|
||||||
*/
|
*/
|
||||||
export function getCurrentTheme(): ThemeName {
|
export function getCurrentTheme(): ThemeName {
|
||||||
|
if (!isBrowser) return 'blue'
|
||||||
const saved = localStorage.getItem(STORAGE_KEY_THEME)
|
const saved = localStorage.getItem(STORAGE_KEY_THEME)
|
||||||
if (saved && saved in THEME_COLORS) {
|
if (saved && saved in THEME_COLORS) {
|
||||||
return saved as ThemeName
|
return saved as ThemeName
|
||||||
@@ -50,6 +56,7 @@ export function getCurrentTheme(): ThemeName {
|
|||||||
* Check if dark mode is active
|
* Check if dark mode is active
|
||||||
*/
|
*/
|
||||||
export function isDarkMode(): boolean {
|
export function isDarkMode(): boolean {
|
||||||
|
if (!isBrowser) return false
|
||||||
const mode = localStorage.getItem(STORAGE_KEY_COLOR_MODE) || 'auto'
|
const mode = localStorage.getItem(STORAGE_KEY_COLOR_MODE) || 'auto'
|
||||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
|
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||||
return mode === 'dark' || (mode === 'auto' && prefersDark)
|
return mode === 'dark' || (mode === 'auto' && prefersDark)
|
||||||
|
|||||||
130
docs/.vitepress/theme/inlineDemoPlugin.ts
Normal file
130
docs/.vitepress/theme/inlineDemoPlugin.ts
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
/**
|
||||||
|
* Vite plugin for handling inline demo blocks as virtual modules.
|
||||||
|
* This allows React JSX code embedded in markdown :::demo blocks to be
|
||||||
|
* dynamically compiled and executed as proper React components.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { Plugin } from 'vite'
|
||||||
|
import type { DemoBlockMeta } from './markdownTransform'
|
||||||
|
|
||||||
|
// Global registry for inline demos (populated during markdown transform)
|
||||||
|
const inlineDemoRegistry = new Map<string, { code: string; name: string }>()
|
||||||
|
|
||||||
|
// Virtual module prefix
|
||||||
|
const VIRTUAL_PREFIX = 'virtual:inline-demo:'
|
||||||
|
const VIRTUAL_PREFIX_FULL = '\0' + VIRTUAL_PREFIX
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register an inline demo during markdown transformation.
|
||||||
|
* Returns a virtual module ID that can be imported.
|
||||||
|
*/
|
||||||
|
export function registerInlineDemo(
|
||||||
|
demoId: string,
|
||||||
|
code: string,
|
||||||
|
name: string
|
||||||
|
): string {
|
||||||
|
inlineDemoRegistry.set(demoId, { code, name })
|
||||||
|
return VIRTUAL_PREFIX + demoId
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get registered inline demo by ID
|
||||||
|
*/
|
||||||
|
export function getInlineDemo(demoId: string) {
|
||||||
|
return inlineDemoRegistry.get(demoId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear all registered demos (useful for rebuilds)
|
||||||
|
*/
|
||||||
|
export function clearInlineDemos() {
|
||||||
|
inlineDemoRegistry.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vite plugin that resolves virtual inline-demo modules.
|
||||||
|
* These modules contain the React/JSX code from markdown :::demo blocks.
|
||||||
|
*/
|
||||||
|
export function inlineDemoPlugin(): Plugin {
|
||||||
|
return {
|
||||||
|
name: 'vitepress-inline-demo-plugin',
|
||||||
|
enforce: 'pre',
|
||||||
|
|
||||||
|
resolveId(id) {
|
||||||
|
// Handle virtual module resolution
|
||||||
|
if (id.startsWith(VIRTUAL_PREFIX)) {
|
||||||
|
return VIRTUAL_PREFIX_FULL + id.slice(VIRTUAL_PREFIX.length)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
|
||||||
|
load(id) {
|
||||||
|
// Load virtual module content
|
||||||
|
if (id.startsWith(VIRTUAL_PREFIX_FULL)) {
|
||||||
|
const demoId = id.slice(VIRTUAL_PREFIX_FULL.length)
|
||||||
|
const demo = inlineDemoRegistry.get(demoId)
|
||||||
|
|
||||||
|
if (!demo) {
|
||||||
|
return `export default function MissingDemo() {
|
||||||
|
return React.createElement('div', { className: 'demo-error' },
|
||||||
|
'Demo not found: ${demoId}'
|
||||||
|
)
|
||||||
|
}`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap the inline code in an ESM module
|
||||||
|
// The code should export a React component
|
||||||
|
return `
|
||||||
|
import React from 'react';
|
||||||
|
import { useState, useEffect, useRef, useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
|
// User's inline demo code:
|
||||||
|
${demo.code}
|
||||||
|
|
||||||
|
// Auto-export fallback if no default export
|
||||||
|
export default ${demo.name} || (() => React.createElement('div', null, 'Demo component "${demo.name}" not found'));
|
||||||
|
`
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
|
||||||
|
// Handle HMR for inline demos
|
||||||
|
handleHotUpdate({ file, server }) {
|
||||||
|
// When markdown files change, clear the registry
|
||||||
|
if (file.endsWith('.md')) {
|
||||||
|
clearInlineDemos()
|
||||||
|
server.ws.send({
|
||||||
|
type: 'full-reload',
|
||||||
|
path: file
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform inline demo code to ensure it has proper exports.
|
||||||
|
* This wraps bare JSX in a function component if needed.
|
||||||
|
*/
|
||||||
|
export function wrapDemoCode(code: string, componentName: string): string {
|
||||||
|
// If code already has export, return as-is
|
||||||
|
if (code.includes('export ')) {
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
// If code is just JSX (starts with <), wrap it
|
||||||
|
const trimmedCode = code.trim()
|
||||||
|
if (trimmedCode.startsWith('<') || trimmedCode.startsWith('React.createElement')) {
|
||||||
|
return `
|
||||||
|
function ${componentName}() {
|
||||||
|
return (
|
||||||
|
${trimmedCode}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default ${componentName};
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise return as-is and hope it defines the component
|
||||||
|
return code + `\nexport default ${componentName};`
|
||||||
|
}
|
||||||
@@ -82,25 +82,61 @@ onBeforeUnmount(() => {
|
|||||||
|
|
||||||
<template #nav-bar-content-after>
|
<template #nav-bar-content-after>
|
||||||
<div class="nav-extensions">
|
<div class="nav-extensions">
|
||||||
<DocSearch />
|
<DocSearch class="nav-item-always" />
|
||||||
<DarkModeToggle />
|
<DarkModeToggle class="nav-item-desktop" />
|
||||||
<ThemeSwitcher />
|
<ThemeSwitcher class="nav-item-desktop" />
|
||||||
<LanguageSwitcher />
|
<LanguageSwitcher class="nav-item-desktop" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</DefaultTheme.Layout>
|
</DefaultTheme.Layout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
/* ============================================
|
||||||
|
* Container Query Context Definitions
|
||||||
|
* Enables component-level responsive design
|
||||||
|
* ============================================ */
|
||||||
|
|
||||||
|
/* Set container context on layout root */
|
||||||
|
:deep(.Layout) {
|
||||||
|
container-type: inline-size;
|
||||||
|
container-name: layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sidebar container context */
|
||||||
|
:deep(.VPSidebar) {
|
||||||
|
container-type: inline-size;
|
||||||
|
container-name: sidebar;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Main content container context */
|
||||||
|
:deep(.VPContent) {
|
||||||
|
container-type: inline-size;
|
||||||
|
container-name: content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Document outline container context */
|
||||||
|
:deep(.VPDocOutline) {
|
||||||
|
container-type: inline-size;
|
||||||
|
container-name: outline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Navigation container context */
|
||||||
|
:deep(.VPNav) {
|
||||||
|
container-type: inline-size;
|
||||||
|
container-name: nav;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hero section with fluid spacing */
|
||||||
.hero-extensions {
|
.hero-extensions {
|
||||||
margin-top: 40px;
|
margin-top: var(--spacing-fluid-lg);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-stats {
|
.hero-stats {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 48px;
|
gap: var(--spacing-fluid-xl);
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,23 +145,23 @@ onBeforeUnmount(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.stat-value {
|
.stat-value {
|
||||||
font-size: 32px;
|
font-size: clamp(1.5rem, 1.25rem + 1.25vw, 2rem);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: var(--vp-c-primary);
|
color: var(--vp-c-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-label {
|
.stat-label {
|
||||||
font-size: 14px;
|
font-size: clamp(0.75rem, 0.7rem + 0.25vw, 0.875rem);
|
||||||
color: var(--vp-c-text-2);
|
color: var(--vp-c-text-2);
|
||||||
margin-top: 4px;
|
margin-top: var(--spacing-fluid-xs);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-extensions {
|
.nav-extensions {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 12px;
|
gap: var(--spacing-fluid-sm);
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
padding-left: 16px;
|
padding-left: var(--spacing-fluid-sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-logo {
|
.nav-logo {
|
||||||
@@ -156,22 +192,64 @@ onBeforeUnmount(() => {
|
|||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Mobile overrides now handled by fluid spacing variables */
|
||||||
|
/* Container queries in mobile.css provide additional responsiveness */
|
||||||
|
|
||||||
|
/* Mobile-specific styles */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
|
.hero-extensions {
|
||||||
|
margin-top: 1rem;
|
||||||
|
padding: 0 12px;
|
||||||
|
max-width: 100vw;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.hero-stats {
|
.hero-stats {
|
||||||
gap: 24px;
|
gap: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-value {
|
.stat-value {
|
||||||
font-size: 24px;
|
font-size: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-label {
|
.stat-label {
|
||||||
font-size: 12px;
|
font-size: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-extensions {
|
.nav-extensions {
|
||||||
gap: 8px;
|
gap: 0.25rem;
|
||||||
padding-left: 8px;
|
padding-left: 0.25rem;
|
||||||
|
overflow: visible !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide desktop-only nav items on mobile */
|
||||||
|
.nav-item-desktop {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Keep always-visible items */
|
||||||
|
.nav-item-always {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure nav bar allows dropdown overflow */
|
||||||
|
:deep(.VPNavBar) {
|
||||||
|
overflow: visible !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.VPNavBar .content) {
|
||||||
|
overflow: visible !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fix dropdown positioning for mobile */
|
||||||
|
:deep(.VPNavBarMenuGroup .items) {
|
||||||
|
position: fixed !important;
|
||||||
|
left: 12px !important;
|
||||||
|
right: 12px !important;
|
||||||
|
top: 56px !important;
|
||||||
|
max-width: none !important;
|
||||||
|
width: auto !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
import type { MarkdownTransformContext } from 'vitepress'
|
import type { MarkdownTransformContext } from 'vitepress'
|
||||||
|
|
||||||
const demoBlockRE = /:::\s*demo\s+(.+?)\s*:::/g
|
// Multi-line demo block: :::demo Name #file ...code... :::
|
||||||
|
const demoBlockRE = /:::\s*demo\s+([^\n]+)\n([\s\S]*?)\n:::/g
|
||||||
|
|
||||||
|
// Single-line demo block: :::demo Name #file :::
|
||||||
|
const demoBlockSingleRE = /:::\s*demo\s+(\S+)\s*(#\S+)?\s*:::/g
|
||||||
|
|
||||||
export interface DemoBlockMeta {
|
export interface DemoBlockMeta {
|
||||||
name: string
|
name: string
|
||||||
file?: string
|
file?: string
|
||||||
|
code?: string
|
||||||
height?: string
|
height?: string
|
||||||
expandable?: boolean
|
expandable?: boolean
|
||||||
showCode?: boolean
|
showCode?: boolean
|
||||||
@@ -15,47 +20,71 @@ export function transformDemoBlocks(
|
|||||||
code: string,
|
code: string,
|
||||||
ctx: MarkdownTransformContext
|
ctx: MarkdownTransformContext
|
||||||
): string {
|
): string {
|
||||||
return code.replace(demoBlockRE, (match, content) => {
|
// First handle multi-line demo blocks with inline code
|
||||||
const meta = parseDemoBlock(content)
|
let result = code.replace(demoBlockRE, (match, headerLine, codeContent) => {
|
||||||
const demoId = `demo-${ctx.path.replace(/[^a-z0-9]/gi, '-')}-${Math.random().toString(36).slice(2, 8)}`
|
const meta = parseDemoHeader(headerLine)
|
||||||
|
|
||||||
|
const demoId = `demo-${ctx.path.replace(/[^a-z0-9]/gi, '-')}-${meta.name}-${Date.now().toString(36)}`
|
||||||
|
|
||||||
const props = [
|
const props = [
|
||||||
`id="${demoId}"`,
|
`id="${demoId}"`,
|
||||||
`name="${meta.name}"`,
|
`name="${meta.name}"`,
|
||||||
meta.file ? `file="${meta.file}"` : '',
|
meta.file ? `file="${meta.file}"` : '',
|
||||||
meta.height ? `height="${meta.height}"` : '',
|
meta.height ? `height="${meta.height}"` : '',
|
||||||
meta.expandable === false ? ':expandable="false"' : ':expandable="true"',
|
meta.expandable === false ? ':expandable="false"' : '',
|
||||||
meta.showCode === false ? ':show-code="false"' : ':show-code="true"',
|
meta.showCode === false ? ':show-code="false"' : '',
|
||||||
meta.title ? `title="${meta.title}"` : ''
|
meta.title ? `title="${meta.title}"` : ''
|
||||||
].filter(Boolean).join(' ')
|
].filter(Boolean).join(' ')
|
||||||
|
|
||||||
|
// Return a simple comment placeholder - the inline code will be ignored
|
||||||
|
// This avoids Vue parsing issues with JSX in markdown
|
||||||
|
return `<DemoContainer ${props} />`
|
||||||
|
})
|
||||||
|
|
||||||
|
// Then handle single-line demo blocks (file references only)
|
||||||
|
result = result.replace(demoBlockSingleRE, (match, name, fileRef) => {
|
||||||
|
const demoId = `demo-${ctx.path.replace(/[^a-z0-9]/gi, '-')}-${name}-${Date.now().toString(36)}`
|
||||||
|
|
||||||
|
const file = fileRef ? fileRef.slice(1) : undefined
|
||||||
|
|
||||||
|
const props = [
|
||||||
|
`id="${demoId}"`,
|
||||||
|
`name="${name}"`,
|
||||||
|
file ? `file="${file}"` : '',
|
||||||
|
':expandable="true"',
|
||||||
|
':show-code="true"'
|
||||||
|
].filter(Boolean).join(' ')
|
||||||
|
|
||||||
return `<DemoContainer ${props} />`
|
return `<DemoContainer ${props} />`
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseDemoBlock(content: string): DemoBlockMeta {
|
function parseDemoHeader(headerLine: string): DemoBlockMeta {
|
||||||
const lines = content.trim().split('\n')
|
const parts = headerLine.trim().split(/\s+/)
|
||||||
const firstLine = lines[0] || ''
|
const name = parts[0] || ''
|
||||||
|
const file = parts.find(p => p.startsWith('#'))?.slice(1)
|
||||||
|
|
||||||
// Parse: name or # file
|
// Extract props from remaining parts
|
||||||
const [name, ...rest] = firstLine.split(/\s+/)
|
const props: Record<string, string> = {}
|
||||||
const file = rest.find(l => l.startsWith('#'))?.slice(1)
|
for (const part of parts.slice(1)) {
|
||||||
|
if (part.includes(':')) {
|
||||||
|
const [key, value] = part.split(':', 2)
|
||||||
|
props[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: name.trim(),
|
name,
|
||||||
file,
|
file,
|
||||||
height: extractProp(lines, 'height'),
|
height: props.height,
|
||||||
expandable: extractProp(lines, 'expandable') !== 'false',
|
expandable: props.expandable !== 'false',
|
||||||
showCode: extractProp(lines, 'showCode') !== 'false',
|
showCode: props.showCode !== 'false',
|
||||||
title: extractProp(lines, 'title')
|
title: props.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
|
// VitePress markdown configuration hook
|
||||||
export function markdownTransformSetup() {
|
export function markdownTransformSetup() {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -85,11 +85,26 @@
|
|||||||
.VPContent:has(.pro-home) {
|
.VPContent:has(.pro-home) {
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
max-width: 100% !important;
|
max-width: 100vw !important;
|
||||||
|
overflow-x: hidden !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Layout:has(.pro-home) {
|
.Layout:has(.pro-home) {
|
||||||
max-width: 100% !important;
|
max-width: 100vw !important;
|
||||||
|
overflow-x: hidden !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure VPNav doesn't cause overflow */
|
||||||
|
.Layout:has(.pro-home) .VPNav {
|
||||||
|
max-width: 100vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure VPFooter doesn't cause overflow */
|
||||||
|
.Layout:has(.pro-home) .VPFooter {
|
||||||
|
max-width: 100vw;
|
||||||
|
padding-left: 1rem;
|
||||||
|
padding-right: 1rem;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ProfessionalHome component full width */
|
/* ProfessionalHome component full width */
|
||||||
@@ -98,6 +113,8 @@
|
|||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
|
overflow-x: hidden !important;
|
||||||
|
box-sizing: border-box !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ensure all sections extend to full width */
|
/* Ensure all sections extend to full width */
|
||||||
@@ -111,6 +128,8 @@
|
|||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
padding-left: 0 !important;
|
padding-left: 0 !important;
|
||||||
padding-right: 0 !important;
|
padding-right: 0 !important;
|
||||||
|
box-sizing: border-box !important;
|
||||||
|
max-width: 100vw !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.VPHomeHero {
|
.VPHomeHero {
|
||||||
|
|||||||
@@ -1,9 +1,250 @@
|
|||||||
/**
|
/**
|
||||||
* Mobile-Responsive Styles
|
* Mobile-Responsive Styles
|
||||||
* Breakpoints: 320px-768px (mobile), 768px-1024px (tablet), 1024px+ (desktop)
|
* Breakpoints: 320px-768px (mobile), 768px-1024px (tablet), 1024px+ (desktop)
|
||||||
|
* Uses CSS custom properties from variables.css: --bp-mobile, --bp-tablet, --bp-desktop
|
||||||
* WCAG 2.1 AA compliant
|
* WCAG 2.1 AA compliant
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* ============================================
|
||||||
|
* Container Query Support
|
||||||
|
* Enable component-level responsive design
|
||||||
|
* Fallback: Uses @supports to provide media query fallbacks
|
||||||
|
* ============================================ */
|
||||||
|
|
||||||
|
/* Fallback for browsers without container query support */
|
||||||
|
@supports not (container-type: inline-size) {
|
||||||
|
/* Sidebar fallback */
|
||||||
|
.VPSidebar {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 320px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.VPSidebar {
|
||||||
|
width: var(--vp-sidebar-width, 272px);
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Content fallback */
|
||||||
|
.VPContent {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.VPContent {
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1024px) {
|
||||||
|
.VPContent {
|
||||||
|
padding: 32px 48px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Outline fallback */
|
||||||
|
.VPDocOutline {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.VPDocOutline {
|
||||||
|
display: block;
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1024px) {
|
||||||
|
.VPDocOutline {
|
||||||
|
width: 256px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Container Query Rules (modern browsers) */
|
||||||
|
@supports (container-type: inline-size) {
|
||||||
|
/* Sidebar Container Queries */
|
||||||
|
@container sidebar (max-width: 480px) {
|
||||||
|
.VPSidebar .group {
|
||||||
|
padding: 12px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPSidebar .title {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container sidebar (min-width: 480px) and (max-width: 768px) {
|
||||||
|
.VPSidebar .group {
|
||||||
|
padding: 16px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPSidebar .title {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container sidebar (min-width: 768px) {
|
||||||
|
.VPSidebar .group {
|
||||||
|
padding: 16px 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPSidebar .title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Content Container Queries */
|
||||||
|
@container content (max-width: 640px) {
|
||||||
|
.VPDoc .content-container {
|
||||||
|
padding: 0 var(--spacing-fluid-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc h1 {
|
||||||
|
font-size: 1.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc h2 {
|
||||||
|
font-size: 1.375rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc pre {
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container content (min-width: 640px) and (max-width: 960px) {
|
||||||
|
.VPDoc .content-container {
|
||||||
|
padding: 0 var(--spacing-fluid-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc h1 {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc h2 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc pre {
|
||||||
|
font-size: 13px;
|
||||||
|
padding: 16px 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container content (min-width: 960px) {
|
||||||
|
.VPDoc .content-container {
|
||||||
|
padding: 0 var(--spacing-fluid-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc h1 {
|
||||||
|
font-size: 2.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc h2 {
|
||||||
|
font-size: 1.625rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc pre {
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 20px 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Outline Container Queries */
|
||||||
|
@container outline (max-width: 200px) {
|
||||||
|
.VPDocOutline .outline-link {
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 3px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPDocOutline .outline-marker {
|
||||||
|
width: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container outline (min-width: 200px) and (max-width: 280px) {
|
||||||
|
.VPDocOutline .outline-link {
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 4px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPDocOutline .outline-marker {
|
||||||
|
width: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container outline (min-width: 280px) {
|
||||||
|
.VPDocOutline .outline-link {
|
||||||
|
font-size: 13px;
|
||||||
|
padding: 4px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPDocOutline .outline-marker {
|
||||||
|
width: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Navigation Container Queries */
|
||||||
|
@container nav (max-width: 640px) {
|
||||||
|
.VPNavBar {
|
||||||
|
padding: 0 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPNavBar .nav-extensions {
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container nav (min-width: 640px) and (max-width: 960px) {
|
||||||
|
.VPNavBar {
|
||||||
|
padding: 0 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPNavBar .nav-extensions {
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container nav (min-width: 960px) {
|
||||||
|
.VPNavBar {
|
||||||
|
padding: 0 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPNavBar .nav-extensions {
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Generic Container-Responsive Utility Class */
|
||||||
|
@container (max-width: 480px) {
|
||||||
|
.container-responsive {
|
||||||
|
padding: 0 var(--spacing-fluid-xs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container (min-width: 480px) and (max-width: 768px) {
|
||||||
|
.container-responsive {
|
||||||
|
padding: 0 var(--spacing-fluid-sm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container (min-width: 768px) and (max-width: 1024px) {
|
||||||
|
.container-responsive {
|
||||||
|
padding: 0 var(--spacing-fluid-md);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container (min-width: 1024px) {
|
||||||
|
.container-responsive {
|
||||||
|
padding: 0 var(--spacing-fluid-lg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
* Mobile First Approach
|
* Mobile First Approach
|
||||||
* ============================================ */
|
* ============================================ */
|
||||||
@@ -18,32 +259,218 @@
|
|||||||
|
|
||||||
/* Container */
|
/* Container */
|
||||||
.container {
|
.container {
|
||||||
padding: 0 16px;
|
padding: 0 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Navigation */
|
/* Navigation - ensure hamburger menu is visible */
|
||||||
.VPNav {
|
.VPNav {
|
||||||
height: 56px;
|
height: 56px;
|
||||||
|
overflow: visible !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.VPNavBar {
|
.VPNavBar {
|
||||||
padding: 0 16px;
|
padding: 0 12px;
|
||||||
|
overflow: visible !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sidebar */
|
/* Navigation bar content wrapper */
|
||||||
|
.VPNavBar .content {
|
||||||
|
overflow: visible !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Show hamburger menu button on mobile */
|
||||||
|
.VPNavBar .VPNavBarHamburger {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide desktop nav links on mobile, use hamburger menu */
|
||||||
|
.VPNavBar .VPNavBarMenu {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure nav title is visible */
|
||||||
|
.VPNavBar .VPNavBarTitle {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reduce nav-extensions gap on mobile */
|
||||||
|
.VPNavBar .nav-extensions {
|
||||||
|
gap: 0.25rem;
|
||||||
|
padding-left: 0.25rem;
|
||||||
|
overflow: visible !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide non-essential nav items on mobile */
|
||||||
|
.nav-extensions .nav-item-desktop {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Style search button for mobile */
|
||||||
|
.nav-extensions .DocSearch {
|
||||||
|
min-height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure VitePress social link is hidden on mobile (uses sidebar) */
|
||||||
|
.VPNavBar .VPNavBarSocialLinks {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fix dropdown menus overflow on mobile */
|
||||||
|
.VPNavBar .VPNavBarMenuGroup {
|
||||||
|
position: relative;
|
||||||
|
overflow: visible !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPNavBar .VPNavBarMenuGroup .items {
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 0;
|
||||||
|
min-width: 180px;
|
||||||
|
max-width: calc(100vw - 24px);
|
||||||
|
max-height: 60vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
background: var(--vp-c-bg);
|
||||||
|
border: 1px solid var(--vp-c-divider);
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
||||||
|
z-index: 100;
|
||||||
|
padding: 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Language switcher dropdown fix */
|
||||||
|
.language-switcher {
|
||||||
|
position: relative !important;
|
||||||
|
overflow: visible !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-switcher .locale-list {
|
||||||
|
position: fixed !important;
|
||||||
|
top: auto !important;
|
||||||
|
left: 50% !important;
|
||||||
|
transform: translateX(-50%) !important;
|
||||||
|
right: auto !important;
|
||||||
|
min-width: 200px !important;
|
||||||
|
max-width: calc(100vw - 24px) !important;
|
||||||
|
z-index: 1000 !important;
|
||||||
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sidebar - fix display issues */
|
||||||
.VPSidebar {
|
.VPSidebar {
|
||||||
width: 100%;
|
width: 100% !important;
|
||||||
max-width: 320px;
|
max-width: 320px !important;
|
||||||
|
padding-top: 0 !important;
|
||||||
|
top: 56px !important;
|
||||||
|
height: calc(100vh - 56px) !important;
|
||||||
|
max-height: calc(100vh - 56px) !important;
|
||||||
|
overflow: visible !important;
|
||||||
|
position: fixed !important;
|
||||||
|
left: 0 !important;
|
||||||
|
z-index: 40 !important;
|
||||||
|
background: var(--vp-c-bg) !important;
|
||||||
|
transition: transform 0.25s ease !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Content */
|
/* Sidebar when open */
|
||||||
|
.VPSidebar.open,
|
||||||
|
.sidebar-open .VPSidebar {
|
||||||
|
transform: translateX(0) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sidebar when closed */
|
||||||
|
.VPSidebar:not(.open) {
|
||||||
|
transform: translateX(-100%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sidebar nav container */
|
||||||
|
.VPSidebar .VPSidebarNav {
|
||||||
|
padding: 12px 0;
|
||||||
|
height: 100%;
|
||||||
|
min-height: auto;
|
||||||
|
overflow-y: auto;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sidebar groups */
|
||||||
|
.VPSidebar .VPSidebarGroup {
|
||||||
|
padding: 8px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sidebar items */
|
||||||
|
.VPSidebar .VPSidebarItem {
|
||||||
|
padding: 6px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure sidebar links are properly sized */
|
||||||
|
.VPSidebar .link {
|
||||||
|
padding: 8px 12px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Local nav for mobile */
|
||||||
|
.VPLocalNav {
|
||||||
|
display: flex !important;
|
||||||
|
position: sticky;
|
||||||
|
top: 56px;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sidebar curtain/backdrop */
|
||||||
|
.VPSidebar curtain,
|
||||||
|
.VPSidebar .curtain {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sidebar scroll container */
|
||||||
|
.VPSidebar .sidebar-container,
|
||||||
|
.VPSidebar nav {
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure all sidebar content is visible */
|
||||||
|
.VPSidebar .group {
|
||||||
|
margin: 0;
|
||||||
|
padding: 12px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.VPSidebar .title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 4px 0;
|
||||||
|
color: var(--vp-c-text-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sidebar text styling */
|
||||||
|
.VPSidebar .text {
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.5;
|
||||||
|
padding: 6px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure nested items are visible */
|
||||||
|
.VPSidebar .items {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Backdrop for sidebar */
|
||||||
|
.VPBackdrop {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
top: 56px;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
z-index: 39;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Content - reduce padding for better space usage */
|
||||||
.VPContent {
|
.VPContent {
|
||||||
padding: 16px;
|
padding: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Doc content adjustments */
|
/* Doc content adjustments - reduce padding */
|
||||||
.VPDoc .content-container {
|
.VPDoc .content-container {
|
||||||
padding: 0 16px;
|
padding: 0 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Hide outline on mobile */
|
/* Hide outline on mobile */
|
||||||
@@ -53,7 +480,7 @@
|
|||||||
|
|
||||||
/* Hero Section */
|
/* Hero Section */
|
||||||
.VPHomeHero {
|
.VPHomeHero {
|
||||||
padding: 40px 16px;
|
padding: 40px 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.VPHomeHero h1 {
|
.VPHomeHero h1 {
|
||||||
@@ -65,14 +492,14 @@
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Code Blocks */
|
/* Code Blocks - reduce margins */
|
||||||
div[class*='language-'] {
|
div[class*='language-'] {
|
||||||
margin: 12px -16px;
|
margin: 12px -12px;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
div[class*='language-'] pre {
|
div[class*='language-'] pre {
|
||||||
padding: 12px 16px;
|
padding: 12px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,6 +746,230 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================================
|
||||||
|
* ProfessionalHome Component - Mobile Optimizations
|
||||||
|
* ============================================ */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
/* Root level overflow prevention */
|
||||||
|
html, body {
|
||||||
|
overflow-x: hidden;
|
||||||
|
max-width: 100vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* VitePress Layout container fix */
|
||||||
|
.Layout {
|
||||||
|
max-width: 100vw;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* VPContent container fix */
|
||||||
|
.VPContent {
|
||||||
|
max-width: 100vw;
|
||||||
|
overflow-x: hidden;
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hero extensions in Layout.vue - add proper padding */
|
||||||
|
.hero-extensions {
|
||||||
|
padding: 0 12px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
max-width: 100vw;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-stats {
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ProfessionalHome - Hero Section */
|
||||||
|
.pro-home .hero-section {
|
||||||
|
min-height: auto;
|
||||||
|
padding-top: 4.5rem; /* Clear fixed nav (56px) */
|
||||||
|
padding-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prevent horizontal scroll */
|
||||||
|
.pro-home {
|
||||||
|
overflow-x: hidden;
|
||||||
|
max-width: 100vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pro-home .hero-container {
|
||||||
|
max-width: 100%;
|
||||||
|
padding-left: 12px;
|
||||||
|
padding-right: 12px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fix section containers */
|
||||||
|
.pro-home .section-container {
|
||||||
|
max-width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding-left: 12px;
|
||||||
|
padding-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ProfessionalHome - Feature Cards */
|
||||||
|
.pro-home .feature-card {
|
||||||
|
border-radius: 12px;
|
||||||
|
touch-action: manipulation;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pro-home .feature-card:active {
|
||||||
|
transform: scale(0.98);
|
||||||
|
transition: transform 0.1s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ProfessionalHome - Pipeline Animation */
|
||||||
|
.pro-home .cadence-track {
|
||||||
|
margin: 1rem 0 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pro-home .tick-node {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
min-width: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ProfessionalHome - Terminal Window */
|
||||||
|
.pro-home .terminal-window,
|
||||||
|
.pro-home .qs-terminal-window {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pro-home .terminal-header,
|
||||||
|
.pro-home .qs-terminal-header {
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ProfessionalHome - Code Blocks */
|
||||||
|
.pro-home .json-code {
|
||||||
|
overflow-x: auto;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
margin: 0 -1rem;
|
||||||
|
border-radius: 0;
|
||||||
|
border-left: none;
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ProfessionalHome - Buttons touch targets */
|
||||||
|
.pro-home .btn-primary,
|
||||||
|
.pro-home .btn-secondary,
|
||||||
|
.pro-home .btn-outline,
|
||||||
|
.pro-home .btn-ghost {
|
||||||
|
min-height: 44px;
|
||||||
|
min-width: 44px;
|
||||||
|
touch-action: manipulation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ProfessionalHome - CTA Section */
|
||||||
|
.pro-home .cta-card {
|
||||||
|
margin: 0 0.5rem;
|
||||||
|
border-radius: 16px;
|
||||||
|
max-width: calc(100% - 1rem);
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ProfessionalHome - Animation adjustments */
|
||||||
|
.pro-home .reveal-text,
|
||||||
|
.pro-home .reveal-card,
|
||||||
|
.pro-home .reveal-slide {
|
||||||
|
opacity: 1;
|
||||||
|
transform: none;
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ProfessionalHome - Stage nodes in pipeline */
|
||||||
|
.pro-home .stage-node {
|
||||||
|
touch-action: manipulation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Quick Start Section - prevent overflow */
|
||||||
|
.pro-home .quickstart-section {
|
||||||
|
padding: 3rem 0;
|
||||||
|
max-width: 100vw;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pro-home .quickstart-layout {
|
||||||
|
padding: 0 12px;
|
||||||
|
max-width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pro-home .quickstart-info,
|
||||||
|
.pro-home .quickstart-terminal {
|
||||||
|
max-width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure all text content wraps properly */
|
||||||
|
.pro-home .json-text,
|
||||||
|
.pro-home .json-benefits,
|
||||||
|
.pro-home .qs-step-content {
|
||||||
|
max-width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
word-wrap: break-word;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Features section overflow fix */
|
||||||
|
.pro-home .features-section {
|
||||||
|
max-width: 100vw;
|
||||||
|
overflow-x: hidden;
|
||||||
|
padding: 3rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pipeline section overflow fix */
|
||||||
|
.pro-home .pipeline-section {
|
||||||
|
max-width: 100vw;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ProfessionalHome - Tablet Optimizations */
|
||||||
|
@media (min-width: 768px) and (max-width: 1024px) {
|
||||||
|
.pro-home .hero-section {
|
||||||
|
padding: 3rem 0 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pro-home .hero-title {
|
||||||
|
font-size: 2.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pro-home .features-grid {
|
||||||
|
gap: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pro-home .json-grid {
|
||||||
|
gap: 2rem;
|
||||||
|
padding: 3rem 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ProfessionalHome - Small Mobile (< 480px) */
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.pro-home .hero-badge {
|
||||||
|
font-size: 0.7rem;
|
||||||
|
padding: 0.2rem 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pro-home .section-title {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pro-home .pipeline-card {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure touch targets */
|
||||||
|
.pro-home .btn-primary,
|
||||||
|
.pro-home .btn-secondary {
|
||||||
|
padding: 0.875rem 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
* Dark Mode Specific
|
* Dark Mode Specific
|
||||||
* ============================================ */
|
* ============================================ */
|
||||||
|
|||||||
@@ -89,7 +89,7 @@
|
|||||||
--vp-font-size-lg: 18px;
|
--vp-font-size-lg: 18px;
|
||||||
--vp-font-size-xl: 20px;
|
--vp-font-size-xl: 20px;
|
||||||
|
|
||||||
/* Spacing */
|
/* Spacing (Fixed) */
|
||||||
--vp-spacing-xs: 0.25rem; /* 4px */
|
--vp-spacing-xs: 0.25rem; /* 4px */
|
||||||
--vp-spacing-sm: 0.5rem; /* 8px */
|
--vp-spacing-sm: 0.5rem; /* 8px */
|
||||||
--vp-spacing-md: 1rem; /* 16px */
|
--vp-spacing-md: 1rem; /* 16px */
|
||||||
@@ -97,6 +97,25 @@
|
|||||||
--vp-spacing-xl: 2rem; /* 32px */
|
--vp-spacing-xl: 2rem; /* 32px */
|
||||||
--vp-spacing-2xl: 3rem; /* 48px */
|
--vp-spacing-2xl: 3rem; /* 48px */
|
||||||
|
|
||||||
|
/* Fluid Spacing (Responsive with clamp())
|
||||||
|
* Scales smoothly between viewport widths
|
||||||
|
* Usage: padding: var(--spacing-fluid-md);
|
||||||
|
*/
|
||||||
|
--spacing-fluid-xs: clamp(0.25rem, 0.2rem + 0.25vw, 0.375rem); /* 4-6px */
|
||||||
|
--spacing-fluid-sm: clamp(0.5rem, 0.4rem + 0.5vw, 0.75rem); /* 8-12px */
|
||||||
|
--spacing-fluid-md: clamp(0.75rem, 0.6rem + 0.75vw, 1.25rem); /* 12-20px */
|
||||||
|
--spacing-fluid-lg: clamp(1rem, 0.8rem + 1vw, 1.75rem); /* 16-28px */
|
||||||
|
--spacing-fluid-xl: clamp(1.5rem, 1.2rem + 1.5vw, 2.5rem); /* 24-40px */
|
||||||
|
--spacing-fluid-2xl: clamp(2rem, 1.5rem + 2.5vw, 3.5rem); /* 32-56px */
|
||||||
|
|
||||||
|
/* Container Query Names
|
||||||
|
* Usage: container-name: var(--container-sidebar);
|
||||||
|
*/
|
||||||
|
--container-sidebar: sidebar;
|
||||||
|
--container-content: content;
|
||||||
|
--container-outline: outline;
|
||||||
|
--container-nav: nav;
|
||||||
|
|
||||||
/* Border Radius */
|
/* Border Radius */
|
||||||
--vp-radius-sm: 0.25rem; /* 4px */
|
--vp-radius-sm: 0.25rem; /* 4px */
|
||||||
--vp-radius-md: 0.375rem; /* 6px */
|
--vp-radius-md: 0.375rem; /* 6px */
|
||||||
@@ -122,6 +141,19 @@
|
|||||||
--vp-z-index-fixed: 50;
|
--vp-z-index-fixed: 50;
|
||||||
--vp-z-index-modal: 100;
|
--vp-z-index-modal: 100;
|
||||||
--vp-z-index-toast: 200;
|
--vp-z-index-toast: 200;
|
||||||
|
|
||||||
|
/* Responsive Breakpoints (VitePress standard) */
|
||||||
|
--bp-mobile: 768px; /* Mobile: < 768px */
|
||||||
|
--bp-tablet: 1024px; /* Tablet: 768px - 1024px */
|
||||||
|
--bp-desktop: 1440px; /* Desktop: > 1024px, large: > 1440px */
|
||||||
|
|
||||||
|
/* Container Query Breakpoints
|
||||||
|
* Aligned with media query breakpoints for consistency
|
||||||
|
*/
|
||||||
|
--container-bp-sm: 480px; /* Small container */
|
||||||
|
--container-bp-md: 768px; /* Medium container */
|
||||||
|
--container-bp-lg: 1024px; /* Large container */
|
||||||
|
--container-bp-xl: 1280px; /* Extra large container */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
|
|||||||
@@ -21,360 +21,7 @@
|
|||||||
|
|
||||||
## Live Demo: Component Gallery
|
## Live Demo: Component Gallery
|
||||||
|
|
||||||
:::demo ComponentGallery
|
:::demo ComponentGallery #ComponentGallery.tsx :::
|
||||||
# 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 (
|
|
||||||
<div className="p-6 bg-background space-y-8">
|
|
||||||
{/* Header */}
|
|
||||||
<div>
|
|
||||||
<h1 className="text-2xl font-bold">UI Component Library</h1>
|
|
||||||
<p className="text-muted-foreground">Interactive showcase of all available UI components</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Category Filter */}
|
|
||||||
<div className="flex flex-wrap gap-2">
|
|
||||||
{categories.map((cat) => (
|
|
||||||
<button
|
|
||||||
key={cat.id}
|
|
||||||
onClick={() => setSelectedCategory(cat.id)}
|
|
||||||
className={`px-4 py-2 rounded-md text-sm transition-colors ${
|
|
||||||
selectedCategory === cat.id
|
|
||||||
? 'bg-primary text-primary-foreground'
|
|
||||||
: 'border hover:bg-accent'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{cat.label}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Buttons Section */}
|
|
||||||
{(selectedCategory === 'all' || selectedCategory === 'buttons') && (
|
|
||||||
<section className="space-y-4">
|
|
||||||
<h2 className="text-lg font-semibold">Buttons</h2>
|
|
||||||
<div className="space-y-6">
|
|
||||||
{/* Variant Selector */}
|
|
||||||
<div className="space-y-3">
|
|
||||||
<label className="text-sm font-medium">Variant</label>
|
|
||||||
<div className="flex flex-wrap gap-2">
|
|
||||||
{buttonVariants.map((variant) => (
|
|
||||||
<button
|
|
||||||
key={variant}
|
|
||||||
onClick={() => setButtonVariant(variant)}
|
|
||||||
className={`px-4 py-2 rounded-md text-sm capitalize transition-colors ${
|
|
||||||
buttonVariant === variant
|
|
||||||
? 'bg-primary text-primary-foreground ring-2 ring-ring'
|
|
||||||
: 'border hover:bg-accent'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{variant}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Button Sizes */}
|
|
||||||
<div className="space-y-3">
|
|
||||||
<label className="text-sm font-medium">Sizes</label>
|
|
||||||
<div className="flex items-center gap-3 flex-wrap">
|
|
||||||
<button className={`h-8 rounded-md px-3 text-sm ${buttonVariant === 'default' ? 'bg-primary text-primary-foreground' : 'border'}`}>
|
|
||||||
Small
|
|
||||||
</button>
|
|
||||||
<button className={`h-10 px-4 py-2 rounded-md text-sm ${buttonVariant === 'default' ? 'bg-primary text-primary-foreground' : 'border'}`}>
|
|
||||||
Default
|
|
||||||
</button>
|
|
||||||
<button className={`h-11 rounded-md px-8 text-sm ${buttonVariant === 'default' ? 'bg-primary text-primary-foreground' : 'border'}`}>
|
|
||||||
Large
|
|
||||||
</button>
|
|
||||||
<button className={`h-10 w-10 rounded-md flex items-center justify-center ${buttonVariant === 'default' ? 'bg-primary text-primary-foreground' : 'border'}`}>
|
|
||||||
⚙
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* All Button Variants */}
|
|
||||||
<div className="space-y-3">
|
|
||||||
<label className="text-sm font-medium">All Variants</label>
|
|
||||||
<div className="flex flex-wrap gap-3 p-4 border rounded-lg bg-muted/20">
|
|
||||||
<button className="px-4 py-2 rounded-md text-sm bg-primary text-primary-foreground hover:opacity-90">Default</button>
|
|
||||||
<button className="px-4 py-2 rounded-md text-sm bg-destructive text-destructive-foreground hover:opacity-90">Destructive</button>
|
|
||||||
<button className="px-4 py-2 rounded-md text-sm border bg-background hover:bg-accent">Outline</button>
|
|
||||||
<button className="px-4 py-2 rounded-md text-sm bg-secondary text-secondary-foreground hover:opacity-80">Secondary</button>
|
|
||||||
<button className="px-4 py-2 rounded-md text-sm hover:bg-accent">Ghost</button>
|
|
||||||
<button className="px-4 py-2 rounded-md text-sm text-primary underline-offset-4 hover:underline">Link</button>
|
|
||||||
<button className="px-4 py-2 rounded-md text-sm bg-gradient-to-r from-blue-500 to-purple-500 text-white hover:opacity-90">Gradient</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Forms Section */}
|
|
||||||
{(selectedCategory === 'all' || selectedCategory === 'forms') && (
|
|
||||||
<section className="space-y-4">
|
|
||||||
<h2 className="text-lg font-semibold">Form Components</h2>
|
|
||||||
<div className="grid md:grid-cols-2 gap-6">
|
|
||||||
{/* Input */}
|
|
||||||
<div className="space-y-3">
|
|
||||||
<label className="text-sm font-medium">Input</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
placeholder="Enter text..."
|
|
||||||
className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
placeholder="Error state"
|
|
||||||
className="flex h-10 w-full rounded-md border border-destructive bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-destructive"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Textarea */}
|
|
||||||
<div className="space-y-3">
|
|
||||||
<label className="text-sm font-medium">Textarea</label>
|
|
||||||
<textarea
|
|
||||||
placeholder="Enter multi-line text..."
|
|
||||||
className="flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Checkbox */}
|
|
||||||
<div className="space-y-3">
|
|
||||||
<label className="text-sm font-medium">Checkbox</label>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<label className="flex items-center gap-2 text-sm cursor-pointer">
|
|
||||||
<input type="checkbox" className="h-4 w-4 rounded border border-primary" checked={checkboxState} onChange={(e) => setCheckboxState(e.target.checked)} />
|
|
||||||
<span>Accept terms and conditions</span>
|
|
||||||
</label>
|
|
||||||
<label className="flex items-center gap-2 text-sm cursor-pointer opacity-50">
|
|
||||||
<input type="checkbox" className="h-4 w-4 rounded border border-primary" />
|
|
||||||
<span>Subscribe to newsletter</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Switch */}
|
|
||||||
<div className="space-y-3">
|
|
||||||
<label className="text-sm font-medium">Switch</label>
|
|
||||||
<div className="space-y-3">
|
|
||||||
<label className="flex items-center gap-3 cursor-pointer">
|
|
||||||
<div className="relative">
|
|
||||||
<input type="checkbox" className="sr-only peer" checked={switchState} onChange={(e) => setSwitchState(e.target.checked)} />
|
|
||||||
<div className="w-9 h-5 bg-input rounded-full peer peer-focus:ring-2 peer-focus:ring-ring peer-checked:bg-primary after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-background after:rounded-full after:h-4 after:w-4 after:transition-all peer-checked:after:translate-x-full" />
|
|
||||||
</div>
|
|
||||||
<span className="text-sm">Enable notifications {switchState ? '(on)' : '(off)'}</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Select */}
|
|
||||||
<div className="space-y-3">
|
|
||||||
<label className="text-sm font-medium">Select</label>
|
|
||||||
<select className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring">
|
|
||||||
<option value="">Choose an option</option>
|
|
||||||
<option value="1">Option 1</option>
|
|
||||||
<option value="2">Option 2</option>
|
|
||||||
<option value="3">Option 3</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Form Actions */}
|
|
||||||
<div className="space-y-3">
|
|
||||||
<label className="text-sm font-medium">Form Actions</label>
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<button className="px-4 py-2 rounded-md text-sm bg-primary text-primary-foreground hover:opacity-90">Submit</button>
|
|
||||||
<button className="px-4 py-2 rounded-md text-sm border hover:bg-accent">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Feedback Section */}
|
|
||||||
{(selectedCategory === 'all' || selectedCategory === 'feedback') && (
|
|
||||||
<section className="space-y-4">
|
|
||||||
<h2 className="text-lg font-semibold">Feedback Components</h2>
|
|
||||||
|
|
||||||
{/* Badges */}
|
|
||||||
<div className="space-y-3">
|
|
||||||
<label className="text-sm font-medium">Badges</label>
|
|
||||||
<div className="flex flex-wrap gap-2">
|
|
||||||
<span className="inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold bg-primary text-primary-foreground">Default</span>
|
|
||||||
<span className="inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold bg-secondary text-secondary-foreground">Secondary</span>
|
|
||||||
<span className="inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold bg-destructive text-destructive-foreground">Destructive</span>
|
|
||||||
<span className="inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold bg-success text-white">Success</span>
|
|
||||||
<span className="inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold bg-warning text-white">Warning</span>
|
|
||||||
<span className="inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold bg-info text-white">Info</span>
|
|
||||||
<span className="inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold text-foreground">Outline</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Progress */}
|
|
||||||
<div className="space-y-3">
|
|
||||||
<label className="text-sm font-medium">Progress Bars</label>
|
|
||||||
<div className="space-y-3 max-w-md">
|
|
||||||
<div>
|
|
||||||
<div className="flex justify-between text-xs mb-1">
|
|
||||||
<span>Processing...</span>
|
|
||||||
<span>65%</span>
|
|
||||||
</div>
|
|
||||||
<div className="h-2 bg-muted rounded-full overflow-hidden">
|
|
||||||
<div className="h-full bg-primary rounded-full transition-all" style={{ width: '65%' }}/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="flex justify-between text-xs mb-1">
|
|
||||||
<span>Uploading...</span>
|
|
||||||
<span>30%</span>
|
|
||||||
</div>
|
|
||||||
<div className="h-2 bg-muted rounded-full overflow-hidden">
|
|
||||||
<div className="h-full bg-blue-500 rounded-full transition-all" style={{ width: '30%' }}/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Alerts */}
|
|
||||||
<div className="space-y-3">
|
|
||||||
<label className="text-sm font-medium">Alerts</label>
|
|
||||||
<div className="space-y-3">
|
|
||||||
<div className="flex items-start gap-3 p-4 border rounded-lg bg-destructive/10 border-destructive/20 text-destructive">
|
|
||||||
<span className="text-lg">⚠</span>
|
|
||||||
<div className="flex-1">
|
|
||||||
<div className="font-medium text-sm">Error occurred</div>
|
|
||||||
<div className="text-xs mt-1 opacity-80">Something went wrong. Please try again.</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-start gap-3 p-4 border rounded-lg bg-success/10 border-success/20 text-success">
|
|
||||||
<span className="text-lg">✓</span>
|
|
||||||
<div className="flex-1">
|
|
||||||
<div className="font-medium text-sm">Success!</div>
|
|
||||||
<div className="text-xs mt-1 opacity-80">Your changes have been saved.</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Navigation Section */}
|
|
||||||
{(selectedCategory === 'all' || selectedCategory === 'navigation') && (
|
|
||||||
<section className="space-y-4">
|
|
||||||
<h2 className="text-lg font-semibold">Navigation Components</h2>
|
|
||||||
|
|
||||||
{/* Tabs */}
|
|
||||||
<div className="space-y-3">
|
|
||||||
<label className="text-sm font-medium">Tabs</label>
|
|
||||||
<div className="border-b">
|
|
||||||
<div className="flex gap-4">
|
|
||||||
{['Overview', 'Documentation', 'API Reference', 'Examples'].map((tab) => (
|
|
||||||
<button
|
|
||||||
key={tab}
|
|
||||||
onClick={() => setSelectedTab(tab.toLowerCase().replace(' ', '-'))}
|
|
||||||
className={`pb-3 px-1 text-sm border-b-2 transition-colors ${
|
|
||||||
selectedTab === tab.toLowerCase().replace(' ', '-')
|
|
||||||
? 'border-primary text-primary'
|
|
||||||
: 'border-transparent text-muted-foreground hover:text-foreground'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{tab}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Breadcrumb */}
|
|
||||||
<div className="space-y-3">
|
|
||||||
<label className="text-sm font-medium">Breadcrumb</label>
|
|
||||||
<nav className="flex items-center gap-2 text-sm text-muted-foreground">
|
|
||||||
<a href="#" className="hover:text-foreground">Home</a>
|
|
||||||
<span>/</span>
|
|
||||||
<a href="#" className="hover:text-foreground">Components</a>
|
|
||||||
<span>/</span>
|
|
||||||
<span className="text-foreground">Library</span>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Overlays Section */}
|
|
||||||
{(selectedCategory === 'all' || selectedCategory === 'overlays') && (
|
|
||||||
<section className="space-y-4">
|
|
||||||
<h2 className="text-lg font-semibold">Overlay Components</h2>
|
|
||||||
|
|
||||||
<div className="grid md:grid-cols-3 gap-4 text-sm">
|
|
||||||
<div className="p-4 border rounded-lg">
|
|
||||||
<h3 className="font-medium mb-2">Dialog</h3>
|
|
||||||
<p className="text-muted-foreground text-xs">Modal dialogs for focused user interactions.</p>
|
|
||||||
<button className="mt-3 px-3 py-1.5 text-xs bg-primary text-primary-foreground rounded">Open Dialog</button>
|
|
||||||
</div>
|
|
||||||
<div className="p-4 border rounded-lg">
|
|
||||||
<h3 className="font-medium mb-2">Drawer</h3>
|
|
||||||
<p className="text-muted-foreground text-xs">Side panels that slide in from screen edges.</p>
|
|
||||||
<button className="mt-3 px-3 py-1.5 text-xs border rounded hover:bg-accent">Open Drawer</button>
|
|
||||||
</div>
|
|
||||||
<div className="p-4 border rounded-lg">
|
|
||||||
<h3 className="font-medium mb-2">Dropdown Menu</h3>
|
|
||||||
<p className="text-muted-foreground text-xs">Context menus and action lists.</p>
|
|
||||||
<button className="mt-3 px-3 py-1.5 text-xs border rounded hover:bg-accent">▼ Open Menu</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
:::
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -55,144 +55,7 @@
|
|||||||
|
|
||||||
## Live Demo
|
## Live Demo
|
||||||
|
|
||||||
:::demo DashboardOverview
|
:::demo DashboardOverview #DashboardOverview.tsx :::
|
||||||
# dashboard-overview.tsx
|
|
||||||
/**
|
|
||||||
* Dashboard Overview Demo
|
|
||||||
* Shows the main dashboard layout with widgets
|
|
||||||
*/
|
|
||||||
export function DashboardOverview() {
|
|
||||||
return (
|
|
||||||
<div className="space-y-6 p-6 bg-background min-h-[600px]">
|
|
||||||
{/* Header */}
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div>
|
|
||||||
<h1 className="text-2xl font-bold">Dashboard</h1>
|
|
||||||
<p className="text-sm text-muted-foreground">
|
|
||||||
Project overview and activity monitoring
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<button className="px-3 py-1.5 text-sm border rounded-md hover:bg-accent">
|
|
||||||
Refresh
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Workflow Stats Widget */}
|
|
||||||
<div className="border rounded-lg overflow-hidden">
|
|
||||||
<div className="p-4 border-b bg-muted/30">
|
|
||||||
<h2 className="font-semibold">Project Overview & Statistics</h2>
|
|
||||||
</div>
|
|
||||||
<div className="p-4">
|
|
||||||
<div className="grid grid-cols-3 gap-4">
|
|
||||||
<div className="space-y-3">
|
|
||||||
<div className="text-xs font-medium text-muted-foreground">Statistics</div>
|
|
||||||
<div className="grid grid-cols-2 gap-2">
|
|
||||||
{[
|
|
||||||
{ label: 'Active Sessions', value: '12', color: 'text-blue-500' },
|
|
||||||
{ label: 'Total Tasks', value: '48', color: 'text-green-500' },
|
|
||||||
{ label: 'Completed', value: '35', color: 'text-emerald-500' },
|
|
||||||
{ label: 'Pending', value: '8', color: 'text-amber-500' },
|
|
||||||
].map((stat, i) => (
|
|
||||||
<div key={i} className="p-2 bg-muted/50 rounded">
|
|
||||||
<div className={`text-lg font-bold ${stat.color}`}>{stat.value}</div>
|
|
||||||
<div className="text-xs text-muted-foreground truncate">{stat.label}</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-3">
|
|
||||||
<div className="text-xs font-medium text-muted-foreground">Workflow Status</div>
|
|
||||||
<div className="flex items-center justify-center h-24">
|
|
||||||
<div className="relative w-20 h-20">
|
|
||||||
<svg className="w-full h-full -rotate-90" viewBox="0 0 36 36">
|
|
||||||
<circle cx="18" cy="18" r="15" fill="none" stroke="currentColor" strokeWidth="3" className="text-muted opacity-20"/>
|
|
||||||
<circle cx="18" cy="18" r="15" fill="none" stroke="currentColor" strokeWidth="3" className="text-blue-500" strokeDasharray="70 100"/>
|
|
||||||
</svg>
|
|
||||||
<div className="absolute inset-0 flex items-center justify-center text-xs font-bold">70%</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="text-xs text-center space-y-1">
|
|
||||||
<div className="flex items-center justify-center gap-1">
|
|
||||||
<div className="w-2 h-2 rounded-full bg-blue-500"/>
|
|
||||||
<span>Completed: 70%</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-3">
|
|
||||||
<div className="text-xs font-medium text-muted-foreground">Recent Session</div>
|
|
||||||
<div className="p-3 bg-accent/20 rounded border">
|
|
||||||
<div className="flex items-center justify-between mb-2">
|
|
||||||
<span className="text-sm font-medium">Feature: Auth Flow</span>
|
|
||||||
<span className="text-xs px-2 py-0.5 rounded-full bg-green-500/20 text-green-600">Running</span>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-1.5">
|
|
||||||
<div className="flex items-center gap-2 text-xs">
|
|
||||||
<div className="w-3 h-3 rounded bg-green-500"/>
|
|
||||||
<span>Implement login</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2 text-xs">
|
|
||||||
<div className="w-3 h-3 rounded bg-amber-500"/>
|
|
||||||
<span>Add OAuth</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2 text-xs">
|
|
||||||
<div className="w-3 h-3 rounded bg-muted"/>
|
|
||||||
<span className="text-muted-foreground">Test flow</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Recent Sessions Widget */}
|
|
||||||
<div className="border rounded-lg overflow-hidden">
|
|
||||||
<div className="border-b bg-muted/30">
|
|
||||||
<div className="flex gap-1 p-2">
|
|
||||||
{['All Tasks', 'Workflow', 'Lite Tasks'].map((tab, i) => (
|
|
||||||
<button
|
|
||||||
key={tab}
|
|
||||||
className={`px-3 py-1.5 text-xs rounded-md transition-colors ${
|
|
||||||
i === 0 ? 'bg-background text-foreground' : 'text-muted-foreground hover:bg-foreground/5'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{tab}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="p-4">
|
|
||||||
<div className="grid grid-cols-3 gap-3">
|
|
||||||
{[
|
|
||||||
{ name: 'Refactor UI Components', status: 'In Progress', progress: 65 },
|
|
||||||
{ name: 'Fix Login Bug', status: 'Pending', progress: 0 },
|
|
||||||
{ name: 'Add Dark Mode', status: 'Completed', progress: 100 },
|
|
||||||
].map((task, i) => (
|
|
||||||
<div key={i} className="p-3 bg-muted/30 rounded border cursor-pointer hover:border-primary/30">
|
|
||||||
<div className="flex items-center justify-between mb-2">
|
|
||||||
<span className="text-xs font-medium line-clamp-1">{task.name}</span>
|
|
||||||
<span className={`text-xs px-1.5 py-0.5 rounded ${
|
|
||||||
task.status === 'Completed' ? 'bg-green-500/20 text-green-600' :
|
|
||||||
task.status === 'In Progress' ? 'bg-blue-500/20 text-blue-600' :
|
|
||||||
'bg-gray-500/20 text-gray-600'
|
|
||||||
}`}>{task.status}</span>
|
|
||||||
</div>
|
|
||||||
{task.progress > 0 && task.progress < 100 && (
|
|
||||||
<div className="w-full h-1.5 bg-muted rounded-full overflow-hidden">
|
|
||||||
<div className="h-full bg-blue-500 rounded-full" style={{ width: `${task.progress}%` }}/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
:::
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -339,243 +202,15 @@ function ActivityFeed() {
|
|||||||
|
|
||||||
### Statistics Cards Demo
|
### Statistics Cards Demo
|
||||||
|
|
||||||
:::demo MiniStatCards
|
:::demo MiniStatCards #MiniStatCards.tsx :::
|
||||||
# mini-stat-cards.tsx
|
|
||||||
/**
|
|
||||||
* Mini Stat Cards Demo
|
|
||||||
* Individual statistics cards with sparkline trends
|
|
||||||
*/
|
|
||||||
export function MiniStatCards() {
|
|
||||||
const stats = [
|
|
||||||
{ label: 'Active Sessions', value: 12, trend: [8, 10, 9, 11, 10, 12, 12], color: 'blue' },
|
|
||||||
{ label: 'Total Tasks', value: 48, trend: [40, 42, 45, 44, 46, 47, 48], color: 'green' },
|
|
||||||
{ label: 'Completed', value: 35, trend: [25, 28, 30, 32, 33, 34, 35], color: 'emerald' },
|
|
||||||
{ label: 'Pending', value: 8, trend: [12, 10, 11, 9, 8, 7, 8], color: 'amber' },
|
|
||||||
{ label: 'Failed', value: 5, trend: [3, 4, 3, 5, 4, 5, 5], color: 'red' },
|
|
||||||
{ label: 'Today Activity', value: 23, trend: [5, 10, 15, 18, 20, 22, 23], color: 'purple' },
|
|
||||||
]
|
|
||||||
|
|
||||||
const colorMap = {
|
|
||||||
blue: 'text-blue-500 bg-blue-500/10',
|
|
||||||
green: 'text-green-500 bg-green-500/10',
|
|
||||||
emerald: 'text-emerald-500 bg-emerald-500/10',
|
|
||||||
amber: 'text-amber-500 bg-amber-500/10',
|
|
||||||
red: 'text-red-500 bg-red-500/10',
|
|
||||||
purple: 'text-purple-500 bg-purple-500/10',
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="p-6 bg-background">
|
|
||||||
<h3 className="text-sm font-semibold mb-4">Statistics with Sparklines</h3>
|
|
||||||
<div className="grid grid-cols-2 md:grid-cols-3 gap-4">
|
|
||||||
{stats.map((stat, i) => (
|
|
||||||
<div key={i} className="p-4 border rounded-lg bg-card">
|
|
||||||
<div className="flex items-center justify-between mb-2">
|
|
||||||
<span className="text-xs text-muted-foreground">{stat.label}</span>
|
|
||||||
<div className={`w-2 h-2 rounded-full ${colorMap[stat.color].split(' ')[1]}`}/>
|
|
||||||
</div>
|
|
||||||
<div className={`text-2xl font-bold ${colorMap[stat.color].split(' ')[0]}`}>{stat.value}</div>
|
|
||||||
<div className="mt-2 h-8 flex items-end gap-0.5">
|
|
||||||
{stat.trend.map((v, j) => (
|
|
||||||
<div
|
|
||||||
key={j}
|
|
||||||
className="flex-1 rounded-t"
|
|
||||||
style={{
|
|
||||||
height: `${(v / Math.max(...stat.trend)) * 100}%`,
|
|
||||||
backgroundColor: v === stat.value ? 'currentColor' : 'rgba(59, 130, 246, 0.3)',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
:::
|
|
||||||
|
|
||||||
### Project Info Banner Demo
|
### Project Info Banner Demo
|
||||||
|
|
||||||
:::demo ProjectInfoBanner
|
:::demo ProjectInfoBanner #ProjectInfoBanner.tsx :::
|
||||||
# project-info-banner.tsx
|
|
||||||
/**
|
|
||||||
* Project Info Banner Demo
|
|
||||||
* Expandable project information with tech stack
|
|
||||||
*/
|
|
||||||
export function ProjectInfoBanner() {
|
|
||||||
const [expanded, setExpanded] = React.useState(false)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="p-6 bg-background">
|
|
||||||
<h3 className="text-sm font-semibold mb-4">Project Info Banner</h3>
|
|
||||||
<div className="border rounded-lg overflow-hidden">
|
|
||||||
{/* Banner Header */}
|
|
||||||
<div className="p-4 bg-muted/30 flex items-center justify-between">
|
|
||||||
<div>
|
|
||||||
<h4 className="font-semibold">My Awesome Project</h4>
|
|
||||||
<p className="text-sm text-muted-foreground">A modern web application built with React</p>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
onClick={() => setExpanded(!expanded)}
|
|
||||||
className="p-2 rounded-md hover:bg-accent"
|
|
||||||
>
|
|
||||||
<svg className={`w-5 h-5 transition-transform ${expanded ? 'rotate-180' : ''}`} fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Tech Stack Badges */}
|
|
||||||
<div className="px-4 pb-3 flex flex-wrap gap-2">
|
|
||||||
{['TypeScript', 'React', 'Vite', 'Tailwind CSS', 'Zustand'].map((tech) => (
|
|
||||||
<span key={tech} className="px-2 py-1 text-xs rounded-full bg-primary/10 text-primary">
|
|
||||||
{tech}
|
|
||||||
</span>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Expanded Content */}
|
|
||||||
{expanded && (
|
|
||||||
<div className="p-4 border-t bg-muted/20 space-y-4">
|
|
||||||
<div>
|
|
||||||
<h5 className="text-xs font-semibold mb-2">Architecture</h5>
|
|
||||||
<div className="text-sm text-muted-foreground space-y-1">
|
|
||||||
<div>• Component-based UI architecture</div>
|
|
||||||
<div>• Centralized state management</div>
|
|
||||||
<div>• RESTful API integration</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h5 className="text-xs font-semibold mb-2">Key Components</h5>
|
|
||||||
<div className="grid grid-cols-2 gap-2 text-sm">
|
|
||||||
{['Session Manager', 'Dashboard', 'Task Scheduler', 'Analytics'].map((comp) => (
|
|
||||||
<div key={comp} className="flex items-center gap-2">
|
|
||||||
<div className="w-1.5 h-1.5 rounded-full bg-primary"/>
|
|
||||||
{comp}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
:::
|
|
||||||
|
|
||||||
### Session Carousel Demo
|
### Session Carousel Demo
|
||||||
|
|
||||||
:::demo SessionCarousel
|
:::demo SessionCarousel #SessionCarousel.tsx :::
|
||||||
# session-carousel.tsx
|
|
||||||
/**
|
|
||||||
* Session Carousel Demo
|
|
||||||
* Auto-rotating session cards with navigation
|
|
||||||
*/
|
|
||||||
export function SessionCarousel() {
|
|
||||||
const [currentIndex, setCurrentIndex] = React.useState(0)
|
|
||||||
const sessions = [
|
|
||||||
{
|
|
||||||
name: 'Feature: User Authentication',
|
|
||||||
status: 'running',
|
|
||||||
tasks: [
|
|
||||||
{ name: 'Implement login form', status: 'completed' },
|
|
||||||
{ name: 'Add OAuth provider', status: 'in-progress' },
|
|
||||||
{ name: 'Create session management', status: 'pending' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Bug Fix: Memory Leak',
|
|
||||||
status: 'running',
|
|
||||||
tasks: [
|
|
||||||
{ name: 'Identify leak source', status: 'completed' },
|
|
||||||
{ name: 'Fix cleanup handlers', status: 'in-progress' },
|
|
||||||
{ name: 'Add unit tests', status: 'pending' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Refactor: API Layer',
|
|
||||||
status: 'planning',
|
|
||||||
tasks: [
|
|
||||||
{ name: 'Design new interface', status: 'pending' },
|
|
||||||
{ name: 'Migrate existing endpoints', status: 'pending' },
|
|
||||||
{ name: 'Update documentation', status: 'pending' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
const statusColors = {
|
|
||||||
completed: 'bg-green-500',
|
|
||||||
'in-progress': 'bg-amber-500',
|
|
||||||
pending: 'bg-muted',
|
|
||||||
}
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
const timer = setInterval(() => {
|
|
||||||
setCurrentIndex((i) => (i + 1) % sessions.length)
|
|
||||||
}, 5000)
|
|
||||||
return () => clearInterval(timer)
|
|
||||||
}, [sessions.length])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="p-6 bg-background">
|
|
||||||
<h3 className="text-sm font-semibold mb-4">Session Carousel (auto-rotates every 5s)</h3>
|
|
||||||
<div className="border rounded-lg p-4 bg-card">
|
|
||||||
<div className="flex items-center justify-between mb-3">
|
|
||||||
<span className="text-sm font-medium">Session {currentIndex + 1} of {sessions.length}</span>
|
|
||||||
<div className="flex gap-1">
|
|
||||||
{sessions.map((_, i) => (
|
|
||||||
<button
|
|
||||||
key={i}
|
|
||||||
onClick={() => setCurrentIndex(i)}
|
|
||||||
className={`w-2 h-2 rounded-full transition-colors ${
|
|
||||||
i === currentIndex ? 'bg-primary' : 'bg-muted-foreground/30'
|
|
||||||
}`}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="p-4 bg-accent/20 rounded border">
|
|
||||||
<div className="flex items-center justify-between mb-3">
|
|
||||||
<span className="font-medium">{sessions[currentIndex].name}</span>
|
|
||||||
<span className={`text-xs px-2 py-1 rounded-full ${
|
|
||||||
sessions[currentIndex].status === 'running' ? 'bg-green-500/20 text-green-600' : 'bg-blue-500/20 text-blue-600'
|
|
||||||
}`}>
|
|
||||||
{sessions[currentIndex].status}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
{sessions[currentIndex].tasks.map((task, i) => (
|
|
||||||
<div key={i} className="flex items-center gap-2 text-sm">
|
|
||||||
<div className={`w-3 h-3 rounded ${statusColors[task.status]}`}/>
|
|
||||||
<span className={task.status === 'pending' ? 'text-muted-foreground' : ''}>{task.name}</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex justify-between mt-3">
|
|
||||||
<button
|
|
||||||
onClick={() => setCurrentIndex((i) => (i - 1 + sessions.length) % sessions.length)}
|
|
||||||
className="px-3 py-1.5 text-sm border rounded-md hover:bg-accent"
|
|
||||||
>
|
|
||||||
← Previous
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => setCurrentIndex((i) => (i + 1) % sessions.length)}
|
|
||||||
className="px-3 py-1.5 text-sm border rounded-md hover:bg-accent"
|
|
||||||
>
|
|
||||||
Next →
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
:::
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ The V2 Pipeline tab monitors background jobs for:
|
|||||||
- **Local state**:
|
- **Local state**:
|
||||||
- `activeTab`: TabValue
|
- `activeTab`: TabValue
|
||||||
- `searchQuery`: string
|
- `searchQuery`: string
|
||||||
- `selectedMemories`: Set<string>
|
- `selectedMemories`: Set<string>
|
||||||
- `filters`: { sourceType?: string; tags?: string[] }
|
- `filters`: { sourceType?: string; tags?: string[] }
|
||||||
- `dialogStates`: create, edit, delete
|
- `dialogStates`: create, edit, delete
|
||||||
|
|
||||||
|
|||||||
@@ -62,172 +62,7 @@
|
|||||||
|
|
||||||
## Live Demo
|
## Live Demo
|
||||||
|
|
||||||
:::demo QueueManagementDemo
|
:::demo QueueManagementDemo #QueueManagementDemo.tsx :::
|
||||||
# queue-management-demo.tsx
|
|
||||||
/**
|
|
||||||
* Queue Management Demo
|
|
||||||
* Shows scheduler controls and queue items list
|
|
||||||
*/
|
|
||||||
export function QueueManagementDemo() {
|
|
||||||
const [schedulerStatus, setSchedulerStatus] = React.useState('idle')
|
|
||||||
const [progress, setProgress] = React.useState(0)
|
|
||||||
|
|
||||||
const queueItems = [
|
|
||||||
{ id: '1', status: 'completed', issueId: 'ISSUE-1', sessionKey: 'session-1' },
|
|
||||||
{ id: '2', status: 'executing', issueId: 'ISSUE-2', sessionKey: 'session-2' },
|
|
||||||
{ id: '3', status: 'pending', issueId: 'ISSUE-3', sessionKey: null },
|
|
||||||
{ id: '4', status: 'pending', issueId: 'ISSUE-4', sessionKey: null },
|
|
||||||
{ id: '5', status: 'blocked', issueId: 'ISSUE-5', sessionKey: null },
|
|
||||||
]
|
|
||||||
|
|
||||||
const statusConfig = {
|
|
||||||
idle: { label: 'Idle', color: 'bg-gray-500/20 text-gray-600 border-gray-500' },
|
|
||||||
running: { label: 'Running', color: 'bg-green-500/20 text-green-600 border-green-500' },
|
|
||||||
paused: { label: 'Paused', color: 'bg-amber-500/20 text-amber-600 border-amber-500' },
|
|
||||||
}
|
|
||||||
|
|
||||||
const itemStatusConfig = {
|
|
||||||
completed: { icon: '✓', color: 'text-green-500', label: 'Completed' },
|
|
||||||
executing: { icon: '▶', color: 'text-blue-500', label: 'Executing' },
|
|
||||||
pending: { icon: '○', color: 'text-gray-400', label: 'Pending' },
|
|
||||||
blocked: { icon: '✕', color: 'text-red-500', label: 'Blocked' },
|
|
||||||
failed: { icon: '!', color: 'text-red-500', label: 'Failed' },
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simulate progress when running
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (schedulerStatus === 'running') {
|
|
||||||
const interval = setInterval(() => {
|
|
||||||
setProgress((p) => (p >= 100 ? 0 : p + 10))
|
|
||||||
}, 500)
|
|
||||||
return () => clearInterval(interval)
|
|
||||||
}
|
|
||||||
}, [schedulerStatus])
|
|
||||||
|
|
||||||
const handleStart = () => {
|
|
||||||
if (schedulerStatus === 'idle' || schedulerStatus === 'paused') {
|
|
||||||
setSchedulerStatus('running')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handlePause = () => {
|
|
||||||
if (schedulerStatus === 'running') {
|
|
||||||
setSchedulerStatus('paused')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleStop = () => {
|
|
||||||
setSchedulerStatus('idle')
|
|
||||||
setProgress(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentConfig = statusConfig[schedulerStatus]
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="p-6 bg-background space-y-6">
|
|
||||||
{/* Header */}
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div>
|
|
||||||
<h3 className="text-lg font-semibold">Queue Management</h3>
|
|
||||||
<p className="text-sm text-muted-foreground">Manage issue execution queue</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Scheduler Status Bar */}
|
|
||||||
<div className="border rounded-lg p-4 space-y-4">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<span className={`px-3 py-1 rounded text-xs font-medium border ${currentConfig.color}`}>
|
|
||||||
{currentConfig.label}
|
|
||||||
</span>
|
|
||||||
<div className="flex items-center gap-4 text-sm text-muted-foreground">
|
|
||||||
<span>{queueItems.filter((i) => i.status === 'completed').length}/{queueItems.length} items</span>
|
|
||||||
<span>2/2 concurrent</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Progress Bar */}
|
|
||||||
{schedulerStatus === 'running' && (
|
|
||||||
<div className="space-y-1">
|
|
||||||
<div className="h-2 bg-muted rounded-full overflow-hidden">
|
|
||||||
<div
|
|
||||||
className="h-full bg-green-500 rounded-full transition-all"
|
|
||||||
style={{ width: `${progress}%` }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<span className="text-xs text-muted-foreground">{progress}% complete</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Scheduler Controls */}
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
{(schedulerStatus === 'idle' || schedulerStatus === 'paused') && (
|
|
||||||
<button
|
|
||||||
onClick={handleStart}
|
|
||||||
className="px-4 py-2 text-sm bg-green-500 text-white rounded hover:bg-green-600 flex items-center gap-2"
|
|
||||||
>
|
|
||||||
<span>▶</span> Start
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
{schedulerStatus === 'running' && (
|
|
||||||
<button
|
|
||||||
onClick={handlePause}
|
|
||||||
className="px-4 py-2 text-sm bg-amber-500 text-white rounded hover:bg-amber-600 flex items-center gap-2"
|
|
||||||
>
|
|
||||||
<span>⏸</span> Pause
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
{schedulerStatus !== 'idle' && (
|
|
||||||
<button
|
|
||||||
onClick={handleStop}
|
|
||||||
className="px-4 py-2 text-sm bg-red-500 text-white rounded hover:bg-red-600 flex items-center gap-2"
|
|
||||||
>
|
|
||||||
<span>⬛</span> Stop
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
<button className="px-4 py-2 text-sm border rounded hover:bg-accent">
|
|
||||||
Config
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Queue Items List */}
|
|
||||||
<div className="border rounded-lg">
|
|
||||||
<div className="px-4 py-3 border-b bg-muted/30">
|
|
||||||
<h4 className="text-sm font-semibold">Queue Items</h4>
|
|
||||||
</div>
|
|
||||||
<div className="divide-y max-h-80 overflow-auto">
|
|
||||||
{queueItems.map((item) => {
|
|
||||||
const config = itemStatusConfig[item.status]
|
|
||||||
return (
|
|
||||||
<div key={item.id} className="px-4 py-3 flex items-center gap-4 hover:bg-accent/50">
|
|
||||||
<span className={`text-lg ${config.color}`}>{config.icon}</span>
|
|
||||||
<div className="flex-1 min-w-0">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<span className="text-sm font-medium">{item.issueId}</span>
|
|
||||||
<span className={`text-xs px-2 py-0.5 rounded-full ${config.color} bg-opacity-10`}>
|
|
||||||
{config.label}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{item.sessionKey && (
|
|
||||||
<div className="text-xs text-muted-foreground mt-1">
|
|
||||||
Session: {item.sessionKey}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{item.status === 'executing' && (
|
|
||||||
<div className="w-20 h-1.5 bg-muted rounded-full overflow-hidden">
|
|
||||||
<div className="h-full bg-blue-500 animate-pulse" style={{ width: '60%' }}/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
:::
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -404,181 +239,11 @@ function QueueActions() {
|
|||||||
|
|
||||||
### Queue Item Status Demo
|
### Queue Item Status Demo
|
||||||
|
|
||||||
:::demo QueueItemStatusDemo
|
:::demo QueueItemStatusDemo #QueueItemStatusDemo.tsx :::
|
||||||
# queue-item-status-demo.tsx
|
|
||||||
/**
|
|
||||||
* Queue Item Status Demo
|
|
||||||
* Shows all possible queue item states
|
|
||||||
*/
|
|
||||||
export function QueueItemStatusDemo() {
|
|
||||||
const itemStates = [
|
|
||||||
{ status: 'pending', issueId: 'ISSUE-101', sessionKey: null },
|
|
||||||
{ status: 'executing', issueId: 'ISSUE-102', sessionKey: 'cli-session-abc' },
|
|
||||||
{ status: 'completed', issueId: 'ISSUE-103', sessionKey: 'cli-session-def' },
|
|
||||||
{ status: 'blocked', issueId: 'ISSUE-104', sessionKey: null },
|
|
||||||
{ status: 'failed', issueId: 'ISSUE-105', sessionKey: 'cli-session-ghi' },
|
|
||||||
]
|
|
||||||
|
|
||||||
const statusConfig = {
|
|
||||||
pending: { icon: '○', color: 'text-gray-400', bg: 'bg-gray-500/10', label: 'Pending' },
|
|
||||||
executing: { icon: '▶', color: 'text-blue-500', bg: 'bg-blue-500/10', label: 'Executing' },
|
|
||||||
completed: { icon: '✓', color: 'text-green-500', bg: 'bg-green-500/10', label: 'Completed' },
|
|
||||||
blocked: { icon: '✕', color: 'text-red-500', bg: 'bg-red-500/10', label: 'Blocked' },
|
|
||||||
failed: { icon: '!', color: 'text-red-500', bg: 'bg-red-500/10', label: 'Failed' },
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="p-6 bg-background space-y-4">
|
|
||||||
<h3 className="text-sm font-semibold">Queue Item Status States</h3>
|
|
||||||
<div className="space-y-2">
|
|
||||||
{itemStates.map((item) => {
|
|
||||||
const config = statusConfig[item.status]
|
|
||||||
return (
|
|
||||||
<div key={item.status} className="border rounded-lg p-4 flex items-center gap-4">
|
|
||||||
<span className={`text-2xl ${config.color}`}>{config.icon}</span>
|
|
||||||
<div className="flex-1">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<span className="font-medium">{item.issueId}</span>
|
|
||||||
<span className={`text-xs px-2 py-0.5 rounded-full ${config.bg} ${config.color}`}>
|
|
||||||
{config.label}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{item.sessionKey && (
|
|
||||||
<div className="text-sm text-muted-foreground mt-1">
|
|
||||||
Bound session: <code className="text-xs bg-muted px-1 rounded">{item.sessionKey}</code>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{item.status === 'executing' && (
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<div className="w-24 h-2 bg-muted rounded-full overflow-hidden">
|
|
||||||
<div className="h-full bg-blue-500 animate-pulse" style={{ width: '60%' }}/>
|
|
||||||
</div>
|
|
||||||
<span className="text-xs text-muted-foreground">60%</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
:::
|
|
||||||
|
|
||||||
### Scheduler Config Demo
|
### Scheduler Config Demo
|
||||||
|
|
||||||
:::demo SchedulerConfigDemo
|
:::demo SchedulerConfigDemo #SchedulerConfigDemo.tsx :::
|
||||||
# scheduler-config-demo.tsx
|
|
||||||
/**
|
|
||||||
* Scheduler Config Demo
|
|
||||||
* Interactive configuration panel
|
|
||||||
*/
|
|
||||||
export function SchedulerConfigDemo() {
|
|
||||||
const [config, setConfig] = React.useState({
|
|
||||||
maxConcurrentSessions: 2,
|
|
||||||
sessionIdleTimeoutMs: 60000,
|
|
||||||
resumeKeySessionBindingTimeoutMs: 300000,
|
|
||||||
})
|
|
||||||
|
|
||||||
const formatMs = (ms) => {
|
|
||||||
if (ms >= 60000) return `${ms / 60000}m`
|
|
||||||
if (ms >= 1000) return `${ms / 1000}s`
|
|
||||||
return `${ms}ms`
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="p-6 bg-background space-y-6 max-w-md">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<h3 className="text-sm font-semibold">Scheduler Configuration</h3>
|
|
||||||
<button className="px-3 py-1.5 text-xs bg-primary text-primary-foreground rounded hover:opacity-90">
|
|
||||||
Save
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-4">
|
|
||||||
{/* Max Concurrent Sessions */}
|
|
||||||
<div className="space-y-2">
|
|
||||||
<label className="text-sm font-medium">Max Concurrent Sessions</label>
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<input
|
|
||||||
type="range"
|
|
||||||
min="1"
|
|
||||||
max="8"
|
|
||||||
value={config.maxConcurrentSessions}
|
|
||||||
onChange={(e) => setConfig({ ...config, maxConcurrentSessions: parseInt(e.target.value) })}
|
|
||||||
className="flex-1"
|
|
||||||
/>
|
|
||||||
<span className="text-sm font-medium w-8 text-center">{config.maxConcurrentSessions}</span>
|
|
||||||
</div>
|
|
||||||
<p className="text-xs text-muted-foreground">
|
|
||||||
Maximum number of sessions to run simultaneously
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Session Idle Timeout */}
|
|
||||||
<div className="space-y-2">
|
|
||||||
<label className="text-sm font-medium">Session Idle Timeout</label>
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<input
|
|
||||||
type="range"
|
|
||||||
min="10000"
|
|
||||||
max="300000"
|
|
||||||
step="10000"
|
|
||||||
value={config.sessionIdleTimeoutMs}
|
|
||||||
onChange={(e) => setConfig({ ...config, sessionIdleTimeoutMs: parseInt(e.target.value) })}
|
|
||||||
className="flex-1"
|
|
||||||
/>
|
|
||||||
<span className="text-sm font-medium w-12 text-right">{formatMs(config.sessionIdleTimeoutMs)}</span>
|
|
||||||
</div>
|
|
||||||
<p className="text-xs text-muted-foreground">
|
|
||||||
Time before idle session is terminated
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Resume Key Binding Timeout */}
|
|
||||||
<div className="space-y-2">
|
|
||||||
<label className="text-sm font-medium">Resume Key Binding Timeout</label>
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<input
|
|
||||||
type="range"
|
|
||||||
min="60000"
|
|
||||||
max="600000"
|
|
||||||
step="60000"
|
|
||||||
value={config.resumeKeySessionBindingTimeoutMs}
|
|
||||||
onChange={(e) => setConfig({ ...config, resumeKeySessionBindingTimeoutMs: parseInt(e.target.value) })}
|
|
||||||
className="flex-1"
|
|
||||||
/>
|
|
||||||
<span className="text-sm font-medium w-12 text-right">{formatMs(config.resumeKeySessionBindingTimeoutMs)}</span>
|
|
||||||
</div>
|
|
||||||
<p className="text-xs text-muted-foreground">
|
|
||||||
Time to preserve resume key session binding
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Current Config Display */}
|
|
||||||
<div className="border rounded-lg p-4 bg-muted/30">
|
|
||||||
<h4 className="text-xs font-semibold mb-3">Current Configuration</h4>
|
|
||||||
<dl className="space-y-2 text-sm">
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<dt className="text-muted-foreground">Max Concurrent</dt>
|
|
||||||
<dd className="font-medium">{config.maxConcurrentSessions} sessions</dd>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<dt className="text-muted-foreground">Idle Timeout</dt>
|
|
||||||
<dd className="font-medium">{formatMs(config.sessionIdleTimeoutMs)}</dd>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<dt className="text-muted-foreground">Binding Timeout</dt>
|
|
||||||
<dd className="font-medium">{formatMs(config.resumeKeySessionBindingTimeoutMs)}</dd>
|
|
||||||
</div>
|
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
:::
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -48,129 +48,7 @@
|
|||||||
|
|
||||||
## Live Demo
|
## Live Demo
|
||||||
|
|
||||||
:::demo TerminalDashboardOverview
|
:::demo TerminalDashboardOverview #TerminalDashboardOverview.tsx :::
|
||||||
# terminal-dashboard-overview.tsx
|
|
||||||
/**
|
|
||||||
* Terminal Dashboard Overview Demo
|
|
||||||
* Shows the three-column layout with resizable panes and toolbar
|
|
||||||
*/
|
|
||||||
export function TerminalDashboardOverview() {
|
|
||||||
const [fileSidebarOpen, setFileSidebarOpen] = React.useState(true)
|
|
||||||
const [sessionSidebarOpen, setSessionSidebarOpen] = React.useState(true)
|
|
||||||
const [activePanel, setActivePanel] = React.useState(null)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="h-[600px] flex flex-col bg-background">
|
|
||||||
{/* Toolbar */}
|
|
||||||
<div className="flex items-center justify-between px-3 py-2 border-b bg-muted/30">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<span className="text-sm font-medium">Terminal Dashboard</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-1">
|
|
||||||
{['Sessions', 'Files', 'Issues', 'Queue', 'Inspector', 'Scheduler'].map((item) => (
|
|
||||||
<button
|
|
||||||
key={item}
|
|
||||||
onClick={() => {
|
|
||||||
if (item === 'Sessions') setSessionSidebarOpen(!sessionSidebarOpen)
|
|
||||||
else if (item === 'Files') setFileSidebarOpen(!fileSidebarOpen)
|
|
||||||
else setActivePanel(activePanel === item.toLowerCase() ? null : item.toLowerCase())
|
|
||||||
}}
|
|
||||||
className={`px-2 py-1 text-xs rounded transition-colors ${
|
|
||||||
(item === 'Sessions' && sessionSidebarOpen) ||
|
|
||||||
(item === 'Files' && fileSidebarOpen) ||
|
|
||||||
activePanel === item.toLowerCase()
|
|
||||||
? 'bg-primary text-primary-foreground'
|
|
||||||
: 'hover:bg-accent'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{item}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Main Layout */}
|
|
||||||
<div className="flex-1 flex min-h-0">
|
|
||||||
{/* Session Sidebar */}
|
|
||||||
{sessionSidebarOpen && (
|
|
||||||
<div className="w-60 border-r flex flex-col">
|
|
||||||
<div className="px-3 py-2 text-xs font-semibold border-b bg-muted/30">
|
|
||||||
Session Groups
|
|
||||||
</div>
|
|
||||||
<div className="flex-1 p-2 space-y-1 text-sm overflow-auto">
|
|
||||||
{['Active Sessions', 'Completed', 'Archived'].map((group) => (
|
|
||||||
<div key={group}>
|
|
||||||
<div className="flex items-center gap-1 px-2 py-1 rounded hover:bg-accent cursor-pointer">
|
|
||||||
<span className="text-xs">▼</span>
|
|
||||||
<span>{group}</span>
|
|
||||||
</div>
|
|
||||||
<div className="ml-4 space-y-0.5">
|
|
||||||
<div className="px-2 py-1 text-xs text-muted-foreground hover:bg-accent rounded cursor-pointer">
|
|
||||||
Session 1
|
|
||||||
</div>
|
|
||||||
<div className="px-2 py-1 text-xs text-muted-foreground hover:bg-accent rounded cursor-pointer">
|
|
||||||
Session 2
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Terminal Grid */}
|
|
||||||
<div className="flex-1 bg-muted/20 p-2">
|
|
||||||
<div className="grid grid-cols-2 grid-rows-2 gap-2 h-full">
|
|
||||||
{[1, 2, 3, 4].map((i) => (
|
|
||||||
<div key={i} className="bg-background border rounded p-3 font-mono text-xs">
|
|
||||||
<div className="text-green-500 mb-2">$ Terminal {i}</div>
|
|
||||||
<div className="text-muted-foreground">
|
|
||||||
<div>Working directory: /project</div>
|
|
||||||
<div>Type a command to begin...</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* File Sidebar */}
|
|
||||||
{fileSidebarOpen && (
|
|
||||||
<div className="w-64 border-l flex flex-col">
|
|
||||||
<div className="px-3 py-2 text-xs font-semibold border-b bg-muted/30">
|
|
||||||
Project Files
|
|
||||||
</div>
|
|
||||||
<div className="flex-1 p-2 text-sm overflow-auto">
|
|
||||||
<div className="space-y-1">
|
|
||||||
{['src', 'docs', 'tests', 'package.json', 'README.md'].map((item) => (
|
|
||||||
<div key={item} className="px-2 py-1 rounded hover:bg-accent cursor-pointer flex items-center gap-2">
|
|
||||||
<span className="text-xs text-muted-foreground">📁</span>
|
|
||||||
{item}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Floating Panel */}
|
|
||||||
{activePanel && (
|
|
||||||
<div className="absolute top-12 right-4 w-80 bg-background border rounded-lg shadow-lg">
|
|
||||||
<div className="flex items-center justify-between px-3 py-2 border-b">
|
|
||||||
<span className="text-sm font-medium capitalize">{activePanel} Panel</span>
|
|
||||||
<button onClick={() => setActivePanel(null)} className="text-xs hover:bg-accent px-2 py-1 rounded">
|
|
||||||
✕
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="p-4 text-sm text-muted-foreground">
|
|
||||||
{activePanel} content placeholder
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
:::
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -343,231 +221,15 @@ function usePanelToggle() {
|
|||||||
|
|
||||||
### Layout Presets Demo
|
### Layout Presets Demo
|
||||||
|
|
||||||
:::demo TerminalLayoutPresets
|
:::demo TerminalLayoutPresets #TerminalLayoutPresets.tsx :::
|
||||||
# terminal-layout-presets.tsx
|
|
||||||
/**
|
|
||||||
* Terminal Layout Presets Demo
|
|
||||||
* Interactive layout preset buttons
|
|
||||||
*/
|
|
||||||
export function TerminalLayoutPresets() {
|
|
||||||
const [layout, setLayout] = React.useState('grid-2x2')
|
|
||||||
|
|
||||||
const layouts = {
|
|
||||||
single: 'grid-cols-1 grid-rows-1',
|
|
||||||
'split-h': 'grid-cols-2 grid-rows-1',
|
|
||||||
'split-v': 'grid-cols-1 grid-rows-2',
|
|
||||||
'grid-2x2': 'grid-cols-2 grid-rows-2',
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="p-6 bg-background space-y-4">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<h3 className="text-sm font-semibold">Terminal Layout Presets</h3>
|
|
||||||
<div className="flex gap-2">
|
|
||||||
{Object.keys(layouts).map((preset) => (
|
|
||||||
<button
|
|
||||||
key={preset}
|
|
||||||
onClick={() => setLayout(preset)}
|
|
||||||
className={`px-3 py-1.5 text-xs rounded transition-colors ${
|
|
||||||
layout === preset
|
|
||||||
? 'bg-primary text-primary-foreground'
|
|
||||||
: 'border hover:bg-accent'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{preset.replace('-', ' ').toUpperCase()}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={`grid gap-2 h-64 ${layouts[layout]}`}>
|
|
||||||
{Array.from({ length: layout === 'single' ? 1 : layout.includes('2x') ? 4 : 2 }).map((_, i) => (
|
|
||||||
<div key={i} className="bg-muted/20 border rounded p-4 font-mono text-xs">
|
|
||||||
<div className="text-green-500">$ Terminal {i + 1}</div>
|
|
||||||
<div className="text-muted-foreground mt-1">Ready for input...</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
:::
|
|
||||||
|
|
||||||
### Floating Panels Demo
|
### Floating Panels Demo
|
||||||
|
|
||||||
:::demo FloatingPanelsDemo
|
:::demo FloatingPanelsDemo #FloatingPanelsDemo.tsx :::
|
||||||
# floating-panels-demo.tsx
|
|
||||||
/**
|
|
||||||
* Floating Panels Demo
|
|
||||||
* Mutually exclusive overlay panels
|
|
||||||
*/
|
|
||||||
export function FloatingPanelsDemo() {
|
|
||||||
const [activePanel, setActivePanel] = React.useState(null)
|
|
||||||
|
|
||||||
const panels = [
|
|
||||||
{ id: 'issues', title: 'Issues + Queue', side: 'left', width: 700 },
|
|
||||||
{ id: 'queue', title: 'Queue', side: 'right', width: 400 },
|
|
||||||
{ id: 'inspector', title: 'Inspector', side: 'right', width: 360 },
|
|
||||||
{ id: 'execution', title: 'Execution Monitor', side: 'right', width: 380 },
|
|
||||||
{ id: 'scheduler', title: 'Scheduler', side: 'right', width: 340 },
|
|
||||||
]
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="relative h-[500px] p-6 bg-background border rounded-lg">
|
|
||||||
<div className="flex items-center justify-between mb-4">
|
|
||||||
<h3 className="text-sm font-semibold">Floating Panels</h3>
|
|
||||||
<div className="flex gap-2">
|
|
||||||
{panels.map((panel) => (
|
|
||||||
<button
|
|
||||||
key={panel.id}
|
|
||||||
onClick={() => setActivePanel(activePanel === panel.id ? null : panel.id)}
|
|
||||||
className={`px-3 py-1.5 text-xs rounded transition-colors ${
|
|
||||||
activePanel === panel.id
|
|
||||||
? 'bg-primary text-primary-foreground'
|
|
||||||
: 'border hover:bg-accent'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{panel.title}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="h-[380px] bg-muted/20 border rounded flex items-center justify-center">
|
|
||||||
<p className="text-sm text-muted-foreground">
|
|
||||||
{activePanel ? `"${panels.find((p) => p.id === activePanel)?.title}" panel is open` : 'Click a button to open a floating panel'}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Floating Panel Overlay */}
|
|
||||||
{activePanel && (
|
|
||||||
<div
|
|
||||||
className={`absolute top-16 border rounded-lg shadow-lg bg-background ${
|
|
||||||
panels.find((p) => p.id === activePanel)?.side === 'left' ? 'left-6' : 'right-6'
|
|
||||||
}`}
|
|
||||||
style={{ width: panels.find((p) => p.id === activePanel)?.width }}
|
|
||||||
>
|
|
||||||
<div className="flex items-center justify-between px-3 py-2 border-b">
|
|
||||||
<span className="text-sm font-medium">{panels.find((p) => p.id === activePanel)?.title}</span>
|
|
||||||
<button
|
|
||||||
onClick={() => setActivePanel(null)}
|
|
||||||
className="text-xs hover:bg-accent px-2 py-1 rounded"
|
|
||||||
>
|
|
||||||
✕
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="p-4 text-sm text-muted-foreground">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<div className="w-2 h-2 rounded-full bg-blue-500"/>
|
|
||||||
<span>Item 1</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<div className="w-2 h-2 rounded-full bg-green-500"/>
|
|
||||||
<span>Item 2</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<div className="w-2 h-2 rounded-full bg-amber-500"/>
|
|
||||||
<span>Item 3</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
:::
|
|
||||||
|
|
||||||
### Resizable Panes Demo
|
### Resizable Panes Demo
|
||||||
|
|
||||||
:::demo ResizablePanesDemo
|
:::demo ResizablePanesDemo #ResizablePanesDemo.tsx :::
|
||||||
# resizable-panes-demo.tsx
|
|
||||||
/**
|
|
||||||
* Resizable Panes Demo
|
|
||||||
* Simulates the Allotment resizable split behavior
|
|
||||||
*/
|
|
||||||
export function ResizablePanesDemo() {
|
|
||||||
const [leftWidth, setLeftWidth] = React.useState(240)
|
|
||||||
const [rightWidth, setRightWidth] = React.useState(280)
|
|
||||||
const [isDragging, setIsDragging] = React.useState(null)
|
|
||||||
|
|
||||||
const handleDragStart = (side) => (e) => {
|
|
||||||
setIsDragging(side)
|
|
||||||
e.preventDefault()
|
|
||||||
}
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
const handleMouseMove = (e) => {
|
|
||||||
if (isDragging === 'left') {
|
|
||||||
setLeftWidth(Math.max(180, Math.min(320, e.clientX)))
|
|
||||||
} else if (isDragging === 'right') {
|
|
||||||
setRightWidth(Math.max(200, Math.min(400, window.innerWidth - e.clientX)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleMouseUp = () => setIsDragging(null)
|
|
||||||
|
|
||||||
if (isDragging) {
|
|
||||||
window.addEventListener('mousemove', handleMouseMove)
|
|
||||||
window.addEventListener('mouseup', handleMouseUp)
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener('mousemove', handleMouseMove)
|
|
||||||
window.removeEventListener('mouseup', handleMouseUp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [isDragging])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="h-[400px] flex bg-background border rounded-lg overflow-hidden">
|
|
||||||
{/* Left Sidebar */}
|
|
||||||
<div style={{ width: leftWidth }} className="border-r flex flex-col">
|
|
||||||
<div className="px-3 py-2 text-xs font-semibold border-b bg-muted/30">
|
|
||||||
Session Groups
|
|
||||||
</div>
|
|
||||||
<div className="flex-1 p-2 text-sm space-y-1">
|
|
||||||
{['Active Sessions', 'Completed'].map((g) => (
|
|
||||||
<div key={g} className="px-2 py-1 hover:bg-accent rounded cursor-pointer">{g}</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Left Drag Handle */}
|
|
||||||
<div
|
|
||||||
onMouseDown={handleDragStart('left')}
|
|
||||||
className={`w-1 bg-border hover:bg-primary cursor-col-resize transition-colors ${
|
|
||||||
isDragging === 'left' ? 'bg-primary' : ''
|
|
||||||
}`}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Main Content */}
|
|
||||||
<div className="flex-1 bg-muted/20 flex items-center justify-center">
|
|
||||||
<span className="text-sm text-muted-foreground">Terminal Grid Area</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Right Drag Handle */}
|
|
||||||
<div
|
|
||||||
onMouseDown={handleDragStart('right')}
|
|
||||||
className={`w-1 bg-border hover:bg-primary cursor-col-resize transition-colors ${
|
|
||||||
isDragging === 'right' ? 'bg-primary' : ''
|
|
||||||
}`}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Right Sidebar */}
|
|
||||||
<div style={{ width: rightWidth }} className="border-l flex flex-col">
|
|
||||||
<div className="px-3 py-2 text-xs font-semibold border-b bg-muted/30">
|
|
||||||
Project Files
|
|
||||||
</div>
|
|
||||||
<div className="flex-1 p-2 text-sm space-y-1">
|
|
||||||
{['src/', 'docs/', 'tests/'].map((f) => (
|
|
||||||
<div key={f} className="px-2 py-1 hover:bg-accent rounded cursor-pointer">{f}</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
:::
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
2180
docs/public/icon-concepts.html
Normal file
2180
docs/public/icon-concepts.html
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,8 @@
|
|||||||
|
---
|
||||||
|
适用CLI: claude
|
||||||
|
分类: team
|
||||||
|
---
|
||||||
|
|
||||||
# Claude Skills - Team Collaboration
|
# Claude Skills - Team Collaboration
|
||||||
|
|
||||||
## One-Liner
|
## One-Liner
|
||||||
@@ -18,7 +23,7 @@
|
|||||||
| Skill | Function | Trigger |
|
| Skill | Function | Trigger |
|
||||||
|-------|----------|---------|
|
|-------|----------|---------|
|
||||||
| `team-coordinate` | Universal team coordinator (dynamic role generation) | `/team-coordinate` |
|
| `team-coordinate` | Universal team coordinator (dynamic role generation) | `/team-coordinate` |
|
||||||
| `team-lifecycle-v5` | Full lifecycle team (spec→impl→test→review) | `/team-lifecycle` |
|
| `team-lifecycle` | Full lifecycle team (spec→impl→test→review) | `/team-lifecycle` |
|
||||||
| `team-planex` | Plan-execute pipeline (plan while executing) | `/team-planex` |
|
| `team-planex` | Plan-execute pipeline (plan while executing) | `/team-planex` |
|
||||||
| `team-review` | Code review team (scan→review→fix) | `/team-review` |
|
| `team-review` | Code review team (scan→review→fix) | `/team-review` |
|
||||||
| `team-testing` | Testing team (strategy→generate→execute→analyze) | `/team-testing` |
|
| `team-testing` | Testing team (strategy→generate→execute→analyze) | `/team-testing` |
|
||||||
@@ -68,7 +73,7 @@ Task Analysis → Generate Roles → Initialize Session → Create Task Chain
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### team-lifecycle-v5
|
### team-lifecycle
|
||||||
|
|
||||||
**One-Liner**: Full lifecycle team — Complete pipeline from specification to implementation to testing to review
|
**One-Liner**: Full lifecycle team — Complete pipeline from specification to implementation to testing to review
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
---
|
||||||
|
适用CLI: claude
|
||||||
|
分类: specialized
|
||||||
|
---
|
||||||
|
|
||||||
# Claude Skills Overview
|
# Claude Skills Overview
|
||||||
|
|
||||||
## One-Liner
|
## One-Liner
|
||||||
@@ -50,7 +55,7 @@ Claude Code Workflow supports two team architecture models:
|
|||||||
- All worker roles are dynamically generated at runtime
|
- All worker roles are dynamically generated at runtime
|
||||||
- Supports dynamic teams for any task type
|
- Supports dynamic teams for any task type
|
||||||
|
|
||||||
2. **team-lifecycle-v5** (Full Lifecycle Team)
|
2. **team-lifecycle** (Full Lifecycle Team)
|
||||||
- Based on team-worker agent architecture
|
- Based on team-worker agent architecture
|
||||||
- All workers share the same agent definition
|
- All workers share the same agent definition
|
||||||
- Role-specific Phase 2-4 loaded from markdown specs
|
- Role-specific Phase 2-4 loaded from markdown specs
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
|
---
|
||||||
|
适用CLI: claude
|
||||||
|
分类: specialized
|
||||||
|
---
|
||||||
|
|
||||||
# Core Skills
|
# Core Skills
|
||||||
|
|
||||||
CCW includes **32 built-in skills** organized across 3 categories, with **15 workflow combinations** for common development scenarios.
|
CCW includes **33 built-in skills** organized across 3 categories, with **15 workflow combinations** for common development scenarios.
|
||||||
|
|
||||||
## Categories Overview
|
## Categories Overview
|
||||||
|
|
||||||
| Category | Count | Description |
|
| Category | Count | Description |
|
||||||
|----------|-------|-------------|
|
|----------|-------|-------------|
|
||||||
| [Standalone](#standalone-skills) | 11 | Single-purpose skills for specific tasks |
|
| [Standalone](#standalone-skills) | 12 | Single-purpose skills for specific tasks |
|
||||||
| [Team](#team-skills) | 14 | Multi-agent collaborative skills |
|
| [Team](#team-skills) | 14 | Multi-agent collaborative skills |
|
||||||
| [Workflow](#workflow-skills) | 7 | Planning and execution pipeline skills |
|
| [Workflow](#workflow-skills) | 7 | Planning and execution pipeline skills |
|
||||||
|
|
||||||
@@ -255,15 +260,38 @@ Skill(skill="software-manual")
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### command-generator
|
||||||
|
|
||||||
|
**Purpose**: Command file generation meta-skill
|
||||||
|
|
||||||
|
**Triggers**: `generate command`
|
||||||
|
|
||||||
|
**Description**: Generates CCW command files with standardized structure.
|
||||||
|
|
||||||
|
**Phases**:
|
||||||
|
1. Requirements Gathering
|
||||||
|
2. Command File Generation
|
||||||
|
3. Validation
|
||||||
|
|
||||||
|
**Outputs**: Command definition files
|
||||||
|
|
||||||
|
```bash
|
||||||
|
Skill(skill="command-generator")
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Team Skills
|
## Team Skills
|
||||||
|
|
||||||
### team-lifecycle-v4
|
### team-lifecycle-v4 [已废弃]
|
||||||
|
|
||||||
**Purpose**: Unified team skill for full lifecycle - spec/impl/test
|
**Purpose**: Unified team skill for full lifecycle - spec/impl/test
|
||||||
|
|
||||||
**Triggers**: `team lifecycle`
|
**Triggers**: `team lifecycle`
|
||||||
|
|
||||||
**Description**: Optimized cadence with inline discuss subagent and shared explore.
|
**Description**: **[已废弃]** 请使用 `team-lifecycle` (v5)。v4 保留用于向后兼容。
|
||||||
|
|
||||||
|
**Status**: Legacy - 已被 team-lifecycle (v5) 取代
|
||||||
|
|
||||||
**Roles**:
|
**Roles**:
|
||||||
|
|
||||||
@@ -677,7 +705,7 @@ Skill(skill="workflow-lite-plan")
|
|||||||
|
|
||||||
**Purpose**: Multi-CLI collaborative planning and execution skill
|
**Purpose**: Multi-CLI collaborative planning and execution skill
|
||||||
|
|
||||||
**Triggers**: `workflow-multi-cli-plan`, `workflow:lite-execute`
|
**Triggers**: `workflow-multi-cli-plan`, `workflow:multi-cli-plan`
|
||||||
|
|
||||||
**Description**: Route to multi-cli-plan or lite-execute with prompt enhancement.
|
**Description**: Route to multi-cli-plan or lite-execute with prompt enhancement.
|
||||||
|
|
||||||
@@ -812,7 +840,7 @@ Skill(skill="workflow-execute")
|
|||||||
Skill(skill="review-cycle")
|
Skill(skill="review-cycle")
|
||||||
```
|
```
|
||||||
|
|
||||||
**Team Alternative**: `team-lifecycle-v4`
|
**Team Alternative**: `team-lifecycle`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -1013,13 +1041,13 @@ Skill(skill="skill-tuning")
|
|||||||
| Meta Skills | skill-generator, skill-tuning, workflow-skill-designer |
|
| Meta Skills | skill-generator, skill-tuning, workflow-skill-designer |
|
||||||
| Orchestrators | workflow-plan, workflow-lite-plan, workflow-multi-cli-plan |
|
| Orchestrators | workflow-plan, workflow-lite-plan, workflow-multi-cli-plan |
|
||||||
| Executors | workflow-execute |
|
| Executors | workflow-execute |
|
||||||
| Team Leads | team-lifecycle-v4, team-lifecycle-v3 |
|
| Team Leads | team-lifecycle (v5) |
|
||||||
|
|
||||||
### Integrations
|
### Integrations
|
||||||
|
|
||||||
| Integration | Skills |
|
| Integration | Skills |
|
||||||
|-------------|--------|
|
|-------------|--------|
|
||||||
| ui-ux-pro-max | team-uidesign, team-frontend, team-lifecycle-v4 |
|
| ui-ux-pro-max | team-uidesign, team-frontend, team-lifecycle |
|
||||||
| ACE Context | workflow-multi-cli-plan |
|
| ACE Context | workflow-multi-cli-plan |
|
||||||
| Chrome MCP | software-manual |
|
| Chrome MCP | software-manual |
|
||||||
|
|
||||||
@@ -1055,18 +1083,27 @@ Quick reference for skill triggers:
|
|||||||
| `brainstorm`, `头脑风暴` | brainstorm |
|
| `brainstorm`, `头脑风暴` | brainstorm |
|
||||||
| `review code`, `code review`, `审查代码` | review-code |
|
| `review code`, `code review`, `审查代码` | review-code |
|
||||||
| `manage issue` | issue-manage |
|
| `manage issue` | issue-manage |
|
||||||
| `workflow-plan` | workflow-plan |
|
| `workflow-plan`, `workflow-plan-verify`, `workflow:replan` | workflow-plan |
|
||||||
| `workflow-execute` | workflow-execute |
|
| `workflow-execute` | workflow-execute |
|
||||||
| `workflow-lite-plan` | workflow-lite-plan |
|
| `workflow-lite-plan` | workflow-lite-plan |
|
||||||
| `workflow-multi-cli-plan` | workflow-multi-cli-plan |
|
| `workflow-multi-cli-plan`, `workflow:multi-cli-plan` | workflow-multi-cli-plan |
|
||||||
| `workflow-tdd-plan` | workflow-tdd-plan |
|
| `workflow-tdd-plan` | workflow-tdd-plan |
|
||||||
| `workflow-test-fix` | workflow-test-fix |
|
| `workflow-test-fix`, `test fix workflow` | workflow-test-fix |
|
||||||
| `team lifecycle` | team-lifecycle-v4 |
|
| `design workflow skill`, `create workflow skill`, `workflow skill designer` | workflow-skill-designer |
|
||||||
|
| `generate command` | command-generator |
|
||||||
|
| `team lifecycle` | team-lifecycle (v5) |
|
||||||
| `team brainstorm` | team-brainstorm |
|
| `team brainstorm` | team-brainstorm |
|
||||||
| `team frontend` | team-frontend |
|
| `team frontend` | team-frontend |
|
||||||
| `team issue` | team-issue |
|
| `team issue` | team-issue |
|
||||||
| `team qa` | team-quality-assurance |
|
| `team qa`, `team quality-assurance` | team-quality-assurance |
|
||||||
| `tech debt cleanup`, `技术债务` | team-tech-debt |
|
| `tech debt cleanup`, `team tech-debt`, `技术债务` | team-tech-debt |
|
||||||
|
| `team ultra-analyze`, `team analyze` | team-ultra-analyze |
|
||||||
|
| `memory capture`, `compact session`, `记录`, `压缩会话` | memory-capture |
|
||||||
|
| `memory manage`, `update claude`, `update memory`, `generate docs`, `更新记忆`, `生成文档` | memory-manage |
|
||||||
|
| `generate spec`, `create specification`, `spec generator`, `workflow:spec` | spec-generator |
|
||||||
|
| `create skill`, `new skill` | skill-generator |
|
||||||
|
| `skill tuning`, `tune skill`, `skill diagnosis` | skill-tuning |
|
||||||
|
| `software manual`, `user guide`, `generate manual`, `create docs` | software-manual |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Skills Library
|
# Skills Library
|
||||||
|
|
||||||
Complete reference for all **32 CCW built-in skills** across 3 categories, plus custom skill development.
|
Complete reference for all **33 CCW built-in skills** across 3 categories, plus custom skill development.
|
||||||
|
|
||||||
## What are Skills?
|
## What are Skills?
|
||||||
|
|
||||||
@@ -10,7 +10,7 @@ Skills are reusable, domain-specific capabilities that CCW can execute. Each ski
|
|||||||
|
|
||||||
| Category | Count | Description |
|
| Category | Count | Description |
|
||||||
|----------|-------|-------------|
|
|----------|-------|-------------|
|
||||||
| [Standalone](./core-skills.md#standalone-skills) | 11 | Single-purpose skills for specific tasks |
|
| [Standalone](./core-skills.md#standalone-skills) | 12 | Single-purpose skills for specific tasks |
|
||||||
| [Team](./core-skills.md#team-skills) | 14 | Multi-agent collaborative skills |
|
| [Team](./core-skills.md#team-skills) | 14 | Multi-agent collaborative skills |
|
||||||
| [Workflow](./core-skills.md#workflow-skills) | 7 | Planning and execution pipeline skills |
|
| [Workflow](./core-skills.md#workflow-skills) | 7 | Planning and execution pipeline skills |
|
||||||
|
|
||||||
@@ -23,14 +23,15 @@ Skills are reusable, domain-specific capabilities that CCW can execute. Each ski
|
|||||||
| [brainstorm](./core-skills.md#brainstorm) | `brainstorm`, `头脑风暴` | Unified brainstorming with dual-mode operation |
|
| [brainstorm](./core-skills.md#brainstorm) | `brainstorm`, `头脑风暴` | Unified brainstorming with dual-mode operation |
|
||||||
| [ccw-help](./core-skills.md#ccw-help) | `ccw-help`, `ccw-issue` | Command help system |
|
| [ccw-help](./core-skills.md#ccw-help) | `ccw-help`, `ccw-issue` | Command help system |
|
||||||
| [memory-capture](./core-skills.md#memory-capture) | `memory capture`, `compact session` | Session compact or quick tips |
|
| [memory-capture](./core-skills.md#memory-capture) | `memory capture`, `compact session` | Session compact or quick tips |
|
||||||
| [memory-manage](./core-skills.md#memory-manage) | `memory manage`, `update claude` | CLAUDE.md updates and docs generation |
|
| [memory-manage](./core-skills.md#memory-manage) | `memory manage`, `update claude`, `更新记忆` | CLAUDE.md updates and docs generation |
|
||||||
| [issue-manage](./core-skills.md#issue-manage) | `manage issue`, `list issues` | Interactive issue management |
|
| [issue-manage](./core-skills.md#issue-manage) | `manage issue`, `list issues` | Interactive issue management |
|
||||||
| [review-code](./core-skills.md#review-code) | `review code`, `code review` | 6-dimensional code review |
|
| [review-code](./core-skills.md#review-code) | `review code`, `code review` | 6-dimensional code review |
|
||||||
| [review-cycle](./core-skills.md#review-cycle) | `workflow:review-cycle` | Review with automated fix |
|
| [review-cycle](./core-skills.md#review-cycle) | `workflow:review-cycle` | Review with automated fix |
|
||||||
| [skill-generator](./core-skills.md#skill-generator) | `create skill`, `new skill` | Meta-skill for creating skills |
|
| [skill-generator](./core-skills.md#skill-generator) | `create skill`, `new skill` | Meta-skill for creating skills |
|
||||||
| [skill-tuning](./core-skills.md#skill-tuning) | `skill tuning`, `tune skill` | Skill diagnosis and optimization |
|
| [skill-tuning](./core-skills.md#skill-tuning) | `skill tuning`, `tune skill` | Skill diagnosis and optimization |
|
||||||
| [spec-generator](./core-skills.md#spec-generator) | `generate spec`, `spec generator` | 6-phase specification generation |
|
| [spec-generator](./core-skills.md#spec-generator) | `generate spec`, `create specification`, `spec generator` | 6-phase specification generation |
|
||||||
| [software-manual](./core-skills.md#software-manual) | `software manual`, `user guide` | Interactive HTML documentation |
|
| [software-manual](./core-skills.md#software-manual) | `software manual`, `user guide` | Interactive HTML documentation |
|
||||||
|
| [command-generator](./core-skills.md#command-generator) | `generate command` | Command file generation |
|
||||||
|
|
||||||
### Team Skills
|
### Team Skills
|
||||||
|
|
||||||
@@ -42,25 +43,25 @@ Skills are reusable, domain-specific capabilities that CCW can execute. Each ski
|
|||||||
| [team-issue](./core-skills.md#team-issue) | `team issue` | 6 | Issue resolution pipeline |
|
| [team-issue](./core-skills.md#team-issue) | `team issue` | 6 | Issue resolution pipeline |
|
||||||
| [team-iterdev](./core-skills.md#team-iterdev) | `team iterdev` | 5 | Generator-critic loop |
|
| [team-iterdev](./core-skills.md#team-iterdev) | `team iterdev` | 5 | Generator-critic loop |
|
||||||
| [team-planex](./core-skills.md#team-planex) | `team planex` | 3 | Plan-and-execute pipeline |
|
| [team-planex](./core-skills.md#team-planex) | `team planex` | 3 | Plan-and-execute pipeline |
|
||||||
| [team-quality-assurance](./core-skills.md#team-quality-assurance) | `team qa` | 6 | QA testing workflow |
|
| [team-quality-assurance](./core-skills.md#team-quality-assurance) | `team qa`, `team quality-assurance` | 6 | QA testing workflow |
|
||||||
| [team-review](./core-skills.md#team-review) | `team-review` | 4 | Code scanning and fix |
|
| [team-review](./core-skills.md#team-review) | `team-review` | 4 | Code scanning and fix |
|
||||||
| [team-roadmap-dev](./core-skills.md#team-roadmap-dev) | `team roadmap-dev` | 4 | Roadmap-driven development |
|
| [team-roadmap-dev](./core-skills.md#team-roadmap-dev) | `team roadmap-dev` | 4 | Roadmap-driven development |
|
||||||
| [team-tech-debt](./core-skills.md#team-tech-debt) | `tech debt cleanup` | 6 | Tech debt identification |
|
| [team-tech-debt](./core-skills.md#team-tech-debt) | `tech debt cleanup`, `技术债务` | 6 | Tech debt identification |
|
||||||
| [team-testing](./core-skills.md#team-testing) | `team testing` | 5 | Progressive test coverage |
|
| [team-testing](./core-skills.md#team-testing) | `team testing` | 5 | Progressive test coverage |
|
||||||
| [team-uidesign](./core-skills.md#team-uidesign) | `team uidesign` | 4 | UI design with tokens |
|
| [team-uidesign](./core-skills.md#team-uidesign) | `team uidesign` | 4 | UI design with tokens |
|
||||||
| [team-ultra-analyze](./core-skills.md#team-ultra-analyze) | `team analyze` | 5 | Deep collaborative analysis |
|
| [team-ultra-analyze](./core-skills.md#team-ultra-analyze) | `team ultra-analyze`, `team analyze` | 5 | Deep collaborative analysis |
|
||||||
|
|
||||||
### Workflow Skills
|
### Workflow Skills
|
||||||
|
|
||||||
| Skill | Triggers | Description |
|
| Skill | Triggers | Description |
|
||||||
|-------|----------|-------------|
|
|-------|----------|-------------|
|
||||||
| [workflow-plan](./core-skills.md#workflow-plan) | `workflow-plan` | 4-phase planning with verification |
|
| [workflow-plan](./core-skills.md#workflow-plan) | `workflow-plan`, `workflow-plan-verify`, `workflow:replan` | 4-phase planning with verification |
|
||||||
| [workflow-lite-plan](./core-skills.md#workflow-lite-plan) | `workflow-lite-plan` | Lightweight planning |
|
| [workflow-lite-plan](./core-skills.md#workflow-lite-plan) | `workflow-lite-plan` | Lightweight planning |
|
||||||
| [workflow-multi-cli-plan](./core-skills.md#workflow-multi-cli-plan) | `workflow-multi-cli-plan` | Multi-CLI collaborative planning |
|
| [workflow-multi-cli-plan](./core-skills.md#workflow-multi-cli-plan) | `workflow-multi-cli-plan`, `workflow:multi-cli-plan` | Multi-CLI collaborative planning |
|
||||||
| [workflow-execute](./core-skills.md#workflow-execute) | `workflow-execute` | Task execution coordination |
|
| [workflow-execute](./core-skills.md#workflow-execute) | `workflow-execute` | Task execution coordination |
|
||||||
| [workflow-tdd-plan](./core-skills.md#workflow-tdd-plan) | `workflow-tdd-plan` | TDD with Red-Green-Refactor |
|
| [workflow-tdd-plan](./core-skills.md#workflow-tdd-plan) | `workflow-tdd-plan` | TDD with Red-Green-Refactor |
|
||||||
| [workflow-test-fix](./core-skills.md#workflow-test-fix) | `workflow-test-fix` | Test-fix pipeline |
|
| [workflow-test-fix](./core-skills.md#workflow-test-fix) | `workflow-test-fix`, `test fix workflow` | Test-fix pipeline |
|
||||||
| [workflow-skill-designer](./core-skills.md#workflow-skill-designer) | `design workflow skill` | Meta-skill for workflow creation |
|
| [workflow-skill-designer](./core-skills.md#workflow-skill-designer) | `design workflow skill`, `create workflow skill` | Meta-skill for workflow creation |
|
||||||
|
|
||||||
## Workflow Combinations
|
## Workflow Combinations
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
|
---
|
||||||
|
适用CLI: claude
|
||||||
|
分类: specialized
|
||||||
|
---
|
||||||
|
|
||||||
# Skills Reference
|
# Skills Reference
|
||||||
|
|
||||||
Quick reference guide for all **32 CCW built-in skills**.
|
Quick reference guide for all **33 CCW built-in skills**.
|
||||||
|
|
||||||
## Core Skills
|
## Core Skills
|
||||||
|
|
||||||
@@ -10,8 +15,8 @@ Quick reference guide for all **32 CCW built-in skills**.
|
|||||||
| **review-code** | `review code`, `code review`, `审查代码` | Multi-dimensional code review (6 dimensions) |
|
| **review-code** | `review code`, `code review`, `审查代码` | Multi-dimensional code review (6 dimensions) |
|
||||||
| **review-cycle** | `workflow:review-cycle` | Code review + automated fix orchestration |
|
| **review-cycle** | `workflow:review-cycle` | Code review + automated fix orchestration |
|
||||||
| **memory-capture** | `memory capture`, `compact session` | Session compact or quick tips capture |
|
| **memory-capture** | `memory capture`, `compact session` | Session compact or quick tips capture |
|
||||||
| **memory-manage** | `memory manage`, `update claude`, `更新记忆` | CLAUDE.md updates and documentation generation |
|
| **memory-manage** | `memory manage`, `update claude`, `update memory`, `generate docs`, `更新记忆`, `生成文档` | CLAUDE.md updates and documentation generation |
|
||||||
| **spec-generator** | `generate spec`, `create specification` | 6-phase specification generator (brief → PRD → architecture → epics) |
|
| **spec-generator** | `generate spec`, `create specification`, `spec generator`, `workflow:spec` | 6-phase specification generator (brief → PRD → architecture → epics) |
|
||||||
| **skill-generator** | `create skill`, `new skill` | Meta-skill for creating new Claude Code skills |
|
| **skill-generator** | `create skill`, `new skill` | Meta-skill for creating new Claude Code skills |
|
||||||
| **skill-tuning** | `skill tuning`, `tune skill` | Universal skill diagnosis and optimization tool |
|
| **skill-tuning** | `skill tuning`, `tune skill` | Universal skill diagnosis and optimization tool |
|
||||||
| **issue-manage** | `manage issue`, `list issues` | Interactive issue management (CRUD operations) |
|
| **issue-manage** | `manage issue`, `list issues` | Interactive issue management (CRUD operations) |
|
||||||
@@ -24,18 +29,17 @@ Quick reference guide for all **32 CCW built-in skills**.
|
|||||||
|-------|---------|---------|
|
|-------|---------|---------|
|
||||||
| **workflow-plan** | `workflow-plan`, `workflow-plan-verify`, `workflow:replan` | 4-phase planning workflow with verification and interactive replanning |
|
| **workflow-plan** | `workflow-plan`, `workflow-plan-verify`, `workflow:replan` | 4-phase planning workflow with verification and interactive replanning |
|
||||||
| **workflow-lite-plan** | `workflow-lite-plan`, `workflow:lite-execute` | Lightweight planning and execution skill |
|
| **workflow-lite-plan** | `workflow-lite-plan`, `workflow:lite-execute` | Lightweight planning and execution skill |
|
||||||
| **workflow-multi-cli-plan** | `workflow-multi-cli-plan` | Multi-CLI collaborative planning with ACE context engine |
|
| **workflow-multi-cli-plan** | `workflow-multi-cli-plan`, `workflow:multi-cli-plan` | Multi-CLI collaborative planning with ACE context engine |
|
||||||
| **workflow-execute** | `workflow-execute` | Coordinate agent execution for workflow tasks |
|
| **workflow-execute** | `workflow-execute` | Coordinate agent execution for workflow tasks |
|
||||||
| **workflow-tdd-plan** | `workflow-tdd-plan`, `workflow-tdd-verify` | TDD workflow with Red-Green-Refactor task chain |
|
| **workflow-tdd-plan** | `workflow-tdd-plan`, `workflow-tdd-verify` | TDD workflow with Red-Green-Refactor task chain |
|
||||||
| **workflow-test-fix** | `workflow-test-fix`, `workflow-test-fix` | Unified test-fix pipeline with adaptive strategy |
|
| **workflow-test-fix** | `workflow-test-fix`, `test fix workflow` | Unified test-fix pipeline with adaptive strategy |
|
||||||
| **workflow-skill-designer** | `design workflow skill`, `create workflow skill` | Meta-skill for designing orchestrator+phases structured workflow skills |
|
| **workflow-skill-designer** | `design workflow skill`, `create workflow skill`, `workflow skill designer` | Meta-skill for designing orchestrator+phases structured workflow skills |
|
||||||
|
|
||||||
## Team Skills
|
## Team Skills
|
||||||
|
|
||||||
| Skill | Trigger | Roles | Purpose |
|
| Skill | Trigger | Roles | Purpose |
|
||||||
|-------|---------|-------|---------|
|
|-------|---------|-------|---------|
|
||||||
| **team-lifecycle-v4** | `team lifecycle` | 8 | Full spec/impl/test lifecycle team |
|
| **team-lifecycle** | `team lifecycle` | variable | Full spec/impl/test lifecycle team (v5, team-worker architecture) |
|
||||||
| **team-lifecycle-v5** | `team lifecycle v5` | variable | Latest lifecycle team (team-worker architecture) |
|
|
||||||
| **team-coordinate** | `team coordinate` | variable | Generic team coordination (legacy) |
|
| **team-coordinate** | `team coordinate` | variable | Generic team coordination (legacy) |
|
||||||
| **team-coordinate-v2** | - | variable | team-worker architecture coordination |
|
| **team-coordinate-v2** | - | variable | team-worker architecture coordination |
|
||||||
| **team-executor** | `team executor` | variable | Lightweight session execution |
|
| **team-executor** | `team executor` | variable | Lightweight session execution |
|
||||||
@@ -50,7 +54,7 @@ Quick reference guide for all **32 CCW built-in skills**.
|
|||||||
| **team-frontend** | `team frontend` | 6 | Frontend development with UI/UX integration |
|
| **team-frontend** | `team frontend` | 6 | Frontend development with UI/UX integration |
|
||||||
| **team-review** | `team-review` | 4 | Code scanning and automated fix |
|
| **team-review** | `team-review` | 4 | Code scanning and automated fix |
|
||||||
| **team-roadmap-dev** | `team roadmap-dev` | 4 | Roadmap-driven development |
|
| **team-roadmap-dev** | `team roadmap-dev` | 4 | Roadmap-driven development |
|
||||||
| **team-tech-debt** | `tech debt cleanup`, `技术债务` | 6 | Tech debt identification and cleanup |
|
| **team-tech-debt** | `tech debt cleanup`, `team tech-debt`, `技术债务` | 6 | Tech debt identification and cleanup |
|
||||||
| **team-ultra-analyze** | `team ultra-analyze`, `team analyze` | 5 | Deep collaborative analysis |
|
| **team-ultra-analyze** | `team ultra-analyze`, `team analyze` | 5 | Deep collaborative analysis |
|
||||||
|
|
||||||
## Command Generation Skills
|
## Command Generation Skills
|
||||||
@@ -63,11 +67,11 @@ Quick reference guide for all **32 CCW built-in skills**.
|
|||||||
|
|
||||||
| Category | Count | Description |
|
| Category | Count | Description |
|
||||||
|----------|-------|-------------|
|
|----------|-------|-------------|
|
||||||
| Core Skills | 11 | Single-purpose skills for specific tasks |
|
| Core Skills | 12 | Single-purpose skills for specific tasks |
|
||||||
| Workflow Skills | 7 | Planning and execution pipeline skills |
|
| Workflow Skills | 7 | Planning and execution pipeline skills |
|
||||||
| Team Skills | 17+ | Multi-agent collaborative skills |
|
| Team Skills | 17+ | Multi-agent collaborative skills |
|
||||||
| Command Gen Skills | 1 | Command file generation |
|
| Command Gen Skills | 1 | Command file generation |
|
||||||
| **Total** | **36+** | |
|
| **Total** | **37+** | |
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
@@ -75,7 +79,7 @@ Quick reference guide for all **32 CCW built-in skills**.
|
|||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
Skill(skill="brainstorm")
|
Skill(skill="brainstorm")
|
||||||
Skill(skill="team-lifecycle-v4", args="Build user authentication system")
|
Skill(skill="team-lifecycle", args="Build user authentication system")
|
||||||
Skill(skill="workflow-plan", args="--mode verify")
|
Skill(skill="workflow-plan", args="--mode verify")
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -100,11 +104,12 @@ team lifecycle
|
|||||||
| `workflow:review-cycle` | review-cycle |
|
| `workflow:review-cycle` | review-cycle |
|
||||||
| `workflow-plan` | workflow-plan |
|
| `workflow-plan` | workflow-plan |
|
||||||
| `workflow-lite-plan` | workflow-lite-plan |
|
| `workflow-lite-plan` | workflow-lite-plan |
|
||||||
| `workflow-multi-cli-plan` | workflow-multi-cli-plan |
|
| `workflow-multi-cli-plan`, `workflow:multi-cli-plan` | workflow-multi-cli-plan |
|
||||||
| `workflow-execute` | workflow-execute |
|
| `workflow-execute` | workflow-execute |
|
||||||
| `workflow-tdd-plan` | workflow-tdd-plan |
|
| `workflow-tdd-plan` | workflow-tdd-plan |
|
||||||
| `workflow-test-fix` | workflow-test-fix |
|
| `workflow-test-fix`, `test fix workflow` | workflow-test-fix |
|
||||||
| `team lifecycle` | team-lifecycle-v4 (or v5) |
|
| `design workflow skill`, `create workflow skill`, `workflow skill designer` | workflow-skill-designer |
|
||||||
|
| `team lifecycle` | team-lifecycle (v5) |
|
||||||
| `team planex` | team-planex |
|
| `team planex` | team-planex |
|
||||||
| `team iterdev` | team-iterdev |
|
| `team iterdev` | team-iterdev |
|
||||||
| `team issue` | team-issue |
|
| `team issue` | team-issue |
|
||||||
@@ -115,15 +120,16 @@ team lifecycle
|
|||||||
| `team frontend` | team-frontend |
|
| `team frontend` | team-frontend |
|
||||||
| `team-review` | team-review |
|
| `team-review` | team-review |
|
||||||
| `team roadmap-dev` | team-roadmap-dev |
|
| `team roadmap-dev` | team-roadmap-dev |
|
||||||
| `tech debt cleanup`, `技术债务` | team-tech-debt |
|
| `tech debt cleanup`, `team tech-debt`, `技术债务` | team-tech-debt |
|
||||||
| `team analyze` | team-ultra-analyze |
|
| `team ultra-analyze`, `team analyze` | team-ultra-analyze |
|
||||||
| `memory capture`, `compact session`, `记录`, `压缩会话` | memory-capture |
|
| `memory capture`, `compact session`, `记录`, `压缩会话` | memory-capture |
|
||||||
| `memory manage`, `update claude`, `更新记忆`, `生成文档` | memory-manage |
|
| `memory manage`, `update claude`, `update memory`, `generate docs`, `更新记忆`, `生成文档` | memory-manage |
|
||||||
| `generate spec`, `create specification`, `spec generator` | spec-generator |
|
| `generate spec`, `create specification`, `spec generator`, `workflow:spec` | spec-generator |
|
||||||
| `create skill`, `new skill` | skill-generator |
|
| `create skill`, `new skill` | skill-generator |
|
||||||
| `skill tuning`, `tune skill`, `skill diagnosis` | skill-tuning |
|
| `skill tuning`, `tune skill`, `skill diagnosis` | skill-tuning |
|
||||||
| `manage issue`, `list issues`, `edit issue` | issue-manage |
|
| `manage issue`, `list issues`, `edit issue` | issue-manage |
|
||||||
| `software manual`, `user guide`, `generate manual` | software-manual |
|
| `software manual`, `user guide`, `generate manual`, `create docs` | software-manual |
|
||||||
|
| `generate command` | command-generator |
|
||||||
|
|
||||||
## Team Skill Architecture
|
## Team Skill Architecture
|
||||||
|
|
||||||
@@ -138,7 +144,7 @@ team lifecycle
|
|||||||
|
|
||||||
### v5 Team Worker Roles
|
### v5 Team Worker Roles
|
||||||
|
|
||||||
The latest team-lifecycle-v5 uses the team-worker agent with dynamic role assignment:
|
The latest team-lifecycle (v5) uses the team-worker agent with dynamic role assignment:
|
||||||
|
|
||||||
| Role | Prefix | Phase |
|
| Role | Prefix | Phase |
|
||||||
|------|--------|-------|
|
|------|--------|-------|
|
||||||
@@ -153,12 +159,12 @@ The latest team-lifecycle-v5 uses the team-worker agent with dynamic role assign
|
|||||||
|
|
||||||
| Pattern | Skills Using It |
|
| Pattern | Skills Using It |
|
||||||
|---------|----------------|
|
|---------|----------------|
|
||||||
| Orchestrator + Workers | team-lifecycle-v4, team-testing, team-quality-assurance |
|
| Orchestrator + Workers | team-lifecycle, team-testing, team-quality-assurance |
|
||||||
| Generator-Critic Loop | team-iterdev |
|
| Generator-Critic Loop | team-iterdev |
|
||||||
| Wave Pipeline | team-planex |
|
| Wave Pipeline | team-planex |
|
||||||
| Red-Green-Refactor | workflow-tdd-plan |
|
| Red-Green-Refactor | workflow-tdd-plan |
|
||||||
| Pure Orchestrator | workflow-plan, workflow-lite-plan |
|
| Pure Orchestrator | workflow-plan, workflow-lite-plan |
|
||||||
| Progressive Phase Loading | workflow-plan, workflow-tdd-plan, team-lifecycle-v5 |
|
| Progressive Phase Loading | workflow-plan, workflow-tdd-plan, team-lifecycle |
|
||||||
|
|
||||||
::: info See Also
|
::: info See Also
|
||||||
- [Core Skills Detail](./core-skills.md) - Detailed skill documentation
|
- [Core Skills Detail](./core-skills.md) - Detailed skill documentation
|
||||||
|
|||||||
248
docs/skills/specs/document-standards.md
Normal file
248
docs/skills/specs/document-standards.md
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
# Document Standards
|
||||||
|
|
||||||
|
> 本文档定义 CCW Skills 生态系统中所有文档的格式规范、frontmatter 要求、命名约定和结构标准。
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
本规范确保 CCW Skills 文档的一致性、可维护性和可扩展性。所有文档(包括 Skill 文档、规格文档、模板文档)都应遵循此规范。
|
||||||
|
|
||||||
|
## Frontmatter 标准
|
||||||
|
|
||||||
|
### 必需字段
|
||||||
|
|
||||||
|
所有文档必须包含以下 frontmatter:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
title: "文档标题"
|
||||||
|
description: "简短描述(一句话)"
|
||||||
|
version: "1.0.0"
|
||||||
|
last_updated: "YYYY-MM-DD"
|
||||||
|
tags: ["tag1", "tag2"]
|
||||||
|
category: "specification|template|reference"
|
||||||
|
---
|
||||||
|
```
|
||||||
|
|
||||||
|
### 可选字段
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
author: "作者名称"
|
||||||
|
status: "draft|stable|deprecated"
|
||||||
|
related_docs:
|
||||||
|
- "path/to/related-doc.md"
|
||||||
|
- "path/to/another-doc.md"
|
||||||
|
cli_support:
|
||||||
|
- "claude"
|
||||||
|
- "codex"
|
||||||
|
- "both"
|
||||||
|
---
|
||||||
|
```
|
||||||
|
|
||||||
|
## 文件命名约定
|
||||||
|
|
||||||
|
### 规格文档 (specs/)
|
||||||
|
|
||||||
|
| 模式 | 示例 | 用途 |
|
||||||
|
|------|------|------|
|
||||||
|
| `{domain}-standards.md` | `quality-standards.md` | 质量标准 |
|
||||||
|
| `{domain}-gates.md` | `quality-gates.md` | 质量门禁 |
|
||||||
|
| `{domain}-dimensions.md` | `review-dimensions.md` | 审查维度 |
|
||||||
|
| `{domain}-classification.md` | `issue-classification.md` | 分类标准 |
|
||||||
|
| `{domain}-spec.md` | `reference-docs-spec.md` | 参考规范 |
|
||||||
|
|
||||||
|
### 模板文档 (templates/)
|
||||||
|
|
||||||
|
| 模式 | 示例 | 用途 |
|
||||||
|
|------|------|------|
|
||||||
|
| `{document-type}.md` | `product-brief.md` | 产品简介模板 |
|
||||||
|
| `{workflow-type}.md` | `sequential-phase.md` | 工作流阶段模板 |
|
||||||
|
| `{output-type}.md` | `review-report.md` | 输出报告模板 |
|
||||||
|
|
||||||
|
### Skill 文档
|
||||||
|
|
||||||
|
| 模式 | 示例 | 用途 |
|
||||||
|
|------|------|------|
|
||||||
|
| `claude-{category}.md` | `claude-meta.md` | Claude 专属技能 |
|
||||||
|
| `codex-{category}.md` | `codex-workflow.md` | Codex 专属技能 |
|
||||||
|
| `{concept}.md` | `core-skills.md` | 通用概念文档 |
|
||||||
|
|
||||||
|
## 文档结构标准
|
||||||
|
|
||||||
|
### 标准章节结构
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# 标题
|
||||||
|
|
||||||
|
> 概述/简介(一句话说明)
|
||||||
|
|
||||||
|
## One-Liner
|
||||||
|
|
||||||
|
**简短描述** — 详细说明
|
||||||
|
|
||||||
|
## Pain Points Solved
|
||||||
|
|
||||||
|
| Pain Point | Current State | Solution |
|
||||||
|
|------------|---------------|----------|
|
||||||
|
| 问题 | 当前状态 | 解决方案 |
|
||||||
|
|
||||||
|
## Skills List / 功能列表
|
||||||
|
|
||||||
|
| Skill | Function | Trigger |
|
||||||
|
|-------|----------|---------|
|
||||||
|
| 技能名 | 功能描述 | 触发命令 |
|
||||||
|
|
||||||
|
## Skills Details / 详细说明
|
||||||
|
|
||||||
|
### skill-name
|
||||||
|
|
||||||
|
**One-Liner**: 简短描述
|
||||||
|
|
||||||
|
**Trigger**:
|
||||||
|
```shell
|
||||||
|
/example command
|
||||||
|
```
|
||||||
|
|
||||||
|
**Features**:
|
||||||
|
- 特性1
|
||||||
|
- 特性2
|
||||||
|
|
||||||
|
**Architecture Overview**:
|
||||||
|
```plaintext
|
||||||
|
架构图或流程图
|
||||||
|
```
|
||||||
|
|
||||||
|
## Related Commands / 相关命令
|
||||||
|
|
||||||
|
## Best Practices / 最佳实践
|
||||||
|
|
||||||
|
## Usage Examples / 使用示例
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 示例命令
|
||||||
|
/example usage
|
||||||
|
```
|
||||||
|
```
|
||||||
|
|
||||||
|
### 规格文档结构
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# {规范名称}
|
||||||
|
|
||||||
|
> 本文档定义 {用途}
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
简要说明规范的目的和范围。
|
||||||
|
|
||||||
|
## 规范内容
|
||||||
|
|
||||||
|
### {分类1}
|
||||||
|
|
||||||
|
| 项目 | 标准 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| 项目1 | 标准描述 | 详细说明 |
|
||||||
|
|
||||||
|
### {分类2}
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
## 参考
|
||||||
|
|
||||||
|
- [相关文档链接](relative/path.md)
|
||||||
|
- [外部参考](https://example.com)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 模板文档结构
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# {模板名称}
|
||||||
|
|
||||||
|
> 用途: {描述}
|
||||||
|
|
||||||
|
## 模板
|
||||||
|
|
||||||
|
\`\`\`markdown
|
||||||
|
{模板内容}
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
## 使用说明
|
||||||
|
|
||||||
|
1. 步骤1
|
||||||
|
2. 步骤2
|
||||||
|
3. 步骤3
|
||||||
|
|
||||||
|
## 示例
|
||||||
|
|
||||||
|
### 示例标题
|
||||||
|
|
||||||
|
\`\`\`markdown
|
||||||
|
{示例填充内容}
|
||||||
|
\`\`\`
|
||||||
|
```
|
||||||
|
|
||||||
|
## 格式规范
|
||||||
|
|
||||||
|
### 标题层级
|
||||||
|
|
||||||
|
| 级别 | 用途 | 示例 |
|
||||||
|
|------|------|------|
|
||||||
|
| H1 (#) | 文档标题,每文档仅一次 | `# Document Title` |
|
||||||
|
| H2 (##) | 主要章节 | `## Overview` |
|
||||||
|
| H3 (###) | 子章节 | `### Section Name` |
|
||||||
|
| H4 (####) | 细节说明 | `#### Details` |
|
||||||
|
|
||||||
|
### 代码块
|
||||||
|
|
||||||
|
| 语言 | 用途 |
|
||||||
|
|------|------|
|
||||||
|
| `shell` / `bash` | 命令行示例 |
|
||||||
|
| `markdown` | Markdown 模板 |
|
||||||
|
| `plaintext` | 架构图/流程图 |
|
||||||
|
| `typescript` / `python` | 代码示例 |
|
||||||
|
|
||||||
|
### 表格
|
||||||
|
|
||||||
|
- 列对齐: 默认左对齐
|
||||||
|
- 表头: 必须包含
|
||||||
|
- 说明列: 必要时添加
|
||||||
|
|
||||||
|
### 引用块
|
||||||
|
|
||||||
|
| 类型 | 语法 | 用途 |
|
||||||
|
|------|------|------|
|
||||||
|
| 警告 | `> **⚠️ Warning**: ` | 重要提醒 |
|
||||||
|
| 注意 | `> **Note**: ` | 补充说明 |
|
||||||
|
| 不可跳过 | `> **Do not skip**: ` | 必读内容 |
|
||||||
|
| 概述 | `> 简短描述` | 章节概述 |
|
||||||
|
|
||||||
|
## 版本控制
|
||||||
|
|
||||||
|
### 版本号格式
|
||||||
|
|
||||||
|
```
|
||||||
|
major.minor.patch
|
||||||
|
```
|
||||||
|
|
||||||
|
- **major**: 重大结构变更
|
||||||
|
- **minor**: 新增章节或字段
|
||||||
|
- **patch**: 小修小补
|
||||||
|
|
||||||
|
### 更新日志
|
||||||
|
|
||||||
|
在文档末尾添加变更记录:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## Changelog
|
||||||
|
|
||||||
|
| Version | Date | Changes |
|
||||||
|
|---------|------|---------|
|
||||||
|
| 1.0.0 | 2026-03-01 | Initial version |
|
||||||
|
| 1.1.0 | 2026-03-15 | Added new section |
|
||||||
|
```
|
||||||
|
|
||||||
|
## 参考
|
||||||
|
|
||||||
|
- [CCW Skills Design Spec](../_shared/SKILL-DESIGN-SPEC.md)
|
||||||
|
- [Markdown Guide](https://www.markdownguide.org/)
|
||||||
|
- [VitePress Documentation](https://vitepress.dev/)
|
||||||
200
docs/skills/specs/issue-classification.md
Normal file
200
docs/skills/specs/issue-classification.md
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
# Issue Classification
|
||||||
|
|
||||||
|
> 本文档定义代码审查中发现的问题分类标准和严重性等级。
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
问题分类确保审查报告的一致性和可操作性。所有问题应按严重性等级标记,并提供明确的修复建议。
|
||||||
|
|
||||||
|
## 严重性等级
|
||||||
|
|
||||||
|
| 等级 | 前缀 | 描述 | 修复优先级 | 必须修复 |
|
||||||
|
|------|------|------|-----------|---------|
|
||||||
|
| **Critical** | [C] | 阻塞性问题,必须立即修复 | P0 - 立即 | 是,合并前必须修复 |
|
||||||
|
| **High** | [H] | 重要问题,需要修复 | P1 - 高 | 是,应该尽快修复 |
|
||||||
|
| **Medium** | [M] | 建议改进 | P2 - 中 | 建议,有时间应修复 |
|
||||||
|
| **Low** | [L] | 可选优化 | P3 - 低 | 可选,最好修复 |
|
||||||
|
| **Info** | [I] | 信息性建议 | P4 - 建议 | 无,仅供参考 |
|
||||||
|
|
||||||
|
## 分类标准
|
||||||
|
|
||||||
|
### Critical (严重)
|
||||||
|
|
||||||
|
**定义**: 阻止代码正常工作或造成严重后果的问题。
|
||||||
|
|
||||||
|
**典型问题**:
|
||||||
|
- 安全漏洞(SQL 注入、XSS、硬编码密钥)
|
||||||
|
- 数据丢失风险(未处理错误导致状态不一致)
|
||||||
|
- 崩溃风险(空指针、未捕获异常)
|
||||||
|
- 业务逻辑错误(核心功能不正确)
|
||||||
|
|
||||||
|
**示例**:
|
||||||
|
```typescript
|
||||||
|
// [C] SQL Injection Risk
|
||||||
|
const query = `SELECT * FROM users WHERE id='${userId}'`;
|
||||||
|
// 用户输入直接拼接 SQL,可被注入攻击
|
||||||
|
|
||||||
|
// [C] Null Pointer Crash
|
||||||
|
return user.profile.name; // user 可能为 null
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### High (高)
|
||||||
|
|
||||||
|
**定义**: 影响代码质量或可维护性的重要问题。
|
||||||
|
|
||||||
|
**典型问题**:
|
||||||
|
- 性能问题(明显的低效算法)
|
||||||
|
- 错误处理缺失(网络请求、文件操作)
|
||||||
|
- 类型安全问题(过度使用 any)
|
||||||
|
- 资源泄漏(未关闭连接、未释放内存)
|
||||||
|
|
||||||
|
**示例**:
|
||||||
|
```typescript
|
||||||
|
// [H] Unhandled Promise
|
||||||
|
fetch(url).then(res => res.json()); // 没有错误处理
|
||||||
|
|
||||||
|
// [H] Memory Leak
|
||||||
|
const listeners = [];
|
||||||
|
listeners.push(callback); // 永不移除
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Medium (中)
|
||||||
|
|
||||||
|
**定义**: 改进空间,不影响当前功能。
|
||||||
|
|
||||||
|
**典型问题**:
|
||||||
|
- 代码重复(相同逻辑出现多次)
|
||||||
|
- 命名不清晰(变量名含糊)
|
||||||
|
- 函数过长(超过 50 行)
|
||||||
|
- 缺少文档(复杂逻辑没有注释)
|
||||||
|
|
||||||
|
**示例**:
|
||||||
|
```typescript
|
||||||
|
// [M] Code Duplication
|
||||||
|
if (user.role === 'admin') { /* 20 lines */ }
|
||||||
|
if (user.role === 'moderator') { /* similar 20 lines */ }
|
||||||
|
|
||||||
|
// [M] Unclear Naming
|
||||||
|
const d = data[0]; // "d" 含义不明
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Low (低)
|
||||||
|
|
||||||
|
**定义**: 小改进,代码风格问题。
|
||||||
|
|
||||||
|
**典型问题**:
|
||||||
|
- 格式不一致(缩进、空行)
|
||||||
|
- 未使用的变量/导入
|
||||||
|
- 魔法数字(直接使用的常量)
|
||||||
|
- 注释风格不统一
|
||||||
|
|
||||||
|
**示例**:
|
||||||
|
```typescript
|
||||||
|
// [L] Unused Import
|
||||||
|
import { unused } from './lib';
|
||||||
|
|
||||||
|
// [L] Magic Number
|
||||||
|
if (count > 42) { /* ... */ } // 42 是什么?
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Info (信息)
|
||||||
|
|
||||||
|
**定义**: 提示性建议,非问题。
|
||||||
|
|
||||||
|
**典型问题**:
|
||||||
|
- 可以使用更简洁的语法
|
||||||
|
- 推荐使用的新特性
|
||||||
|
- 性能优化建议(非关键)
|
||||||
|
|
||||||
|
**示例**:
|
||||||
|
```typescript
|
||||||
|
// [I] Could use optional chaining
|
||||||
|
user && user.profile && user.profile.name;
|
||||||
|
// 可简化为: user?.profile?.name
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 问题格式模板
|
||||||
|
|
||||||
|
每个问题应包含以下信息:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### [等级] 问题标题
|
||||||
|
|
||||||
|
**位置**: `文件路径:行号`
|
||||||
|
|
||||||
|
**问题**: 问题描述(1-2 句话)
|
||||||
|
|
||||||
|
**严重性**: {Critical|High|Medium|Low|Info} - 修复必要性说明
|
||||||
|
|
||||||
|
**推荐修复**:
|
||||||
|
```typescript
|
||||||
|
// 修复前代码
|
||||||
|
// ↓
|
||||||
|
// 修复后代码
|
||||||
|
```
|
||||||
|
|
||||||
|
**参考**: [相关文档链接](relative/path.md)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 按维度分类
|
||||||
|
|
||||||
|
| 维度 | 常见 Critical | 常见 High | 常见 Medium |
|
||||||
|
|------|---------------|-----------|-------------|
|
||||||
|
| **Correctness** | 空指针、类型错误 | 边界条件 | 命名、格式 |
|
||||||
|
| **Readability** | - | 长函数、深嵌套 | 注释、命名 |
|
||||||
|
| **Performance** | O(n²) 以上 | I/O 在循环 | 缓存优化 |
|
||||||
|
| **Security** | 注入、密钥泄露 | 权限检查 | - |
|
||||||
|
| **Testing** | 关键路径无测试 | 边界测试 | 覆盖率 |
|
||||||
|
| **Architecture** | 循环依赖 | 违反 SOLID | 设计模式 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 修复优先级
|
||||||
|
|
||||||
|
### P0 - 立即修复 (Critical)
|
||||||
|
|
||||||
|
- 阻塞合并
|
||||||
|
- 安全风险
|
||||||
|
- 数据风险
|
||||||
|
|
||||||
|
### P1 - 高优先级 (High)
|
||||||
|
|
||||||
|
- 本迭代内修复
|
||||||
|
- 影响用户体验
|
||||||
|
- 技术债务积累
|
||||||
|
|
||||||
|
### P2 - 中优先级 (Medium)
|
||||||
|
|
||||||
|
- 下迭代修复
|
||||||
|
- 代码质量改进
|
||||||
|
- 可维护性提升
|
||||||
|
|
||||||
|
### P3 - 低优先级 (Low)
|
||||||
|
|
||||||
|
- 有空时修复
|
||||||
|
- 代码风格统一
|
||||||
|
- 优化改进
|
||||||
|
|
||||||
|
### P4 - 建议 (Info)
|
||||||
|
|
||||||
|
- 可选修复
|
||||||
|
- 学习参考
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 参考
|
||||||
|
|
||||||
|
- [Review Dimensions](review-dimensions.md)
|
||||||
|
- [Quality Standards](quality-standards.md)
|
||||||
157
docs/skills/specs/quality-gates.md
Normal file
157
docs/skills/specs/quality-gates.md
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
# Quality Gates
|
||||||
|
|
||||||
|
> 本文档定义 CCW Skills 各阶段的质量门禁标准和评分规则。
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
质量门禁确保每个阶段完成前达到最低质量标准。门禁标准涵盖内容完整性、格式规范性、可执行性等方面。
|
||||||
|
|
||||||
|
## 质量门禁矩阵
|
||||||
|
|
||||||
|
### Phase 1: Discovery
|
||||||
|
|
||||||
|
| 检查项 | 标准 | 权重 | 通过条件 |
|
||||||
|
|--------|------|------|----------|
|
||||||
|
| 输出文件存在 | `discovery-context.json` | 20% | 文件存在且格式正确 |
|
||||||
|
| 代码库覆盖 | 至少扫描 3 个目录 | 20% | 扫描目录 ≥ 3 |
|
||||||
|
| 关键发现 | 识别关键模式/问题 | 30% | 发现项 ≥ 5 |
|
||||||
|
| 技术栈识别 | 正确识别语言/框架 | 15% | 识别准确 |
|
||||||
|
| 可执行性 | 下一步可基于发现执行 | 15% | 可行动建议 ≥ 3 |
|
||||||
|
|
||||||
|
**最低通过分数**: 70/100
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 2: Product Brief
|
||||||
|
|
||||||
|
| 检查项 | 标准 | 权重 | 通过条件 |
|
||||||
|
|--------|------|------|----------|
|
||||||
|
| 输出文件存在 | `product-brief.md` | 15% | 文件存在 |
|
||||||
|
| 问题陈述清晰度 | Pain Point 明确 | 20% | 至少 3 个 Pain Point |
|
||||||
|
| 目标用户定义 | Target Audience 具体 | 15% | 用户画像清晰 |
|
||||||
|
| MoSCoW 分析 | 优先级分类完整 | 20% | Must/Should/Could 完整 |
|
||||||
|
| 可行性评估 | 技术可行性讨论 | 15% | 评估合理 |
|
||||||
|
| 格式规范 | 符合模板格式 | 15% | 符合模板 |
|
||||||
|
|
||||||
|
**最低通过分数**: 75/100
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 3: Requirements (PRD)
|
||||||
|
|
||||||
|
| 检查项 | 标准 | 权重 | 通过条件 |
|
||||||
|
|--------|------|------|----------|
|
||||||
|
| 输出文件存在 | `requirements/` 目录 | 10% | 目录结构完整 |
|
||||||
|
| 索引文件 | `_index.md` 完整 | 15% | 包含摘要、MoSCoW、追溯 |
|
||||||
|
| 功能需求 | `REQ-*.md` 数量 | 20% | 需求 ≥ 3 个 |
|
||||||
|
| 需求完整性 | Who/What/Why/How | 20% | 所有字段完整 |
|
||||||
|
| 非功能需求 | `NFR-*.md` 数量 | 15% | 至少 1 个 |
|
||||||
|
| 追溯性 | 从 Brief 到需求的链接 | 10% | 链接有效 |
|
||||||
|
| 格式规范 | 符合模板格式 | 10% | 符合模板 |
|
||||||
|
|
||||||
|
**最低通过分数**: 75/100
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 4: Architecture
|
||||||
|
|
||||||
|
| 检查项 | 标准 | 权重 | 通过条件 |
|
||||||
|
|--------|------|------|----------|
|
||||||
|
| 输出文件存在 | `architecture/` 目录 | 10% | 目录结构完整 |
|
||||||
|
| 索引文件 | `_index.md` 完整 | 15% | 包含组件、技术栈 |
|
||||||
|
| ADR 数量 | `ADR-*.md` 数量 | 20% | 决策记录 ≥ 2 |
|
||||||
|
| 决策质量 | Context/Decision/Consequence | 20% | 结构完整 |
|
||||||
|
| 技术栈选择 | 有理由支持 | 15% | 评估合理 |
|
||||||
|
| 追溯性 | 从 PRD 到架构的链接 | 10% | 链接有效 |
|
||||||
|
| 格式规范 | 符合模板格式 | 10% | 符合模板 |
|
||||||
|
|
||||||
|
**最低通过分数**: 75/100
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 5: Epics
|
||||||
|
|
||||||
|
| 检查项 | 标准 | 权重 | 通过条件 |
|
||||||
|
|--------|------|------|----------|
|
||||||
|
| 输出文件存在 | `epics/` 目录 | 10% | 目录结构完整 |
|
||||||
|
| 索引文件 | `_index.md` 完整 | 15% | 包含 Epic 表、依赖图 |
|
||||||
|
| Epic 数量 | `EPIC-*.md` 数量 | 20% | Epic ≥ 2 |
|
||||||
|
| Story 质量 | User Story 格式 | 20% | 作为...我想要...以便... |
|
||||||
|
| 依赖关系 | Epic 间依赖清晰 | 15% | 依赖图或说明 |
|
||||||
|
| MVP 范围 | MVP 定义明确 | 10% | MVP Epic 标记 |
|
||||||
|
| 追溯性 | 从架构到 Epic 的链接 | 10% | 链接有效 |
|
||||||
|
|
||||||
|
**最低通过分数**: 75/100
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 6: Readiness Check
|
||||||
|
|
||||||
|
| 检查项 | 标准 | 权重 | 通过条件 |
|
||||||
|
|--------|------|------|----------|
|
||||||
|
| 就绪报告 | `readiness-report.md` | 30% | 文件存在且完整 |
|
||||||
|
| 摘要文档 | `spec-summary.md` | 20% | 一页摘要 |
|
||||||
|
| 质量评分 | 所有阶段通过 | 30% | 所有阶段 ≥ 最低分 |
|
||||||
|
| 移交选项 | Handoff 选项清晰 | 10% | 选项明确 |
|
||||||
|
| 格式规范 | 符合模板格式 | 10% | 符合模板 |
|
||||||
|
|
||||||
|
**最低通过分数**: 80/100
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 质量评分计算
|
||||||
|
|
||||||
|
### 自动检查项
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 文件存在性
|
||||||
|
if [ -f "$expected_file" ]; then score=$((score + weight)); fi
|
||||||
|
|
||||||
|
# 格式验证
|
||||||
|
if markdownlint "$file"; then score=$((score + weight)); fi
|
||||||
|
|
||||||
|
# 链接有效性
|
||||||
|
if check-links "$file"; then score=$((score + weight)); fi
|
||||||
|
```
|
||||||
|
|
||||||
|
### 手动评审项
|
||||||
|
|
||||||
|
- 内容质量
|
||||||
|
- 逻辑连贯性
|
||||||
|
- 可执行性
|
||||||
|
- 实用性
|
||||||
|
|
||||||
|
### 综合评分
|
||||||
|
|
||||||
|
```
|
||||||
|
最终得分 = 自动检查得分 × 60% + 手动评审得分 × 40%
|
||||||
|
```
|
||||||
|
|
||||||
|
## 质量等级
|
||||||
|
|
||||||
|
| 等级 | 分数范围 | 描述 |
|
||||||
|
|------|----------|------|
|
||||||
|
| **A - 优秀** | 90-100 | 超出标准,可直接移交 |
|
||||||
|
| **B - 良好** | 80-89 | 满足标准,可移交 |
|
||||||
|
| **C - 及格** | 70-79 | 基本达标,需要改进 |
|
||||||
|
| **D - 不及格** | < 70 | 未达标,需要返工 |
|
||||||
|
|
||||||
|
## 不合格处理
|
||||||
|
|
||||||
|
### 返工流程
|
||||||
|
|
||||||
|
1. **识别问题**: 在就绪报告中列出不合格项
|
||||||
|
2. **修复**: 返回对应阶段进行修复
|
||||||
|
3. **重新评估**: 重新执行质量检查
|
||||||
|
4. **记录**: 记录修复过程和结果
|
||||||
|
|
||||||
|
### 豁免条件
|
||||||
|
|
||||||
|
- 探索性项目(POC)
|
||||||
|
- 时间受限的快速原型
|
||||||
|
- 明确标注为"草稿"的输出
|
||||||
|
|
||||||
|
## 参考
|
||||||
|
|
||||||
|
- [Document Standards](document-standards.md)
|
||||||
|
- [Quality Standards](quality-standards.md)
|
||||||
190
docs/skills/specs/quality-standards.md
Normal file
190
docs/skills/specs/quality-standards.md
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
# Quality Standards
|
||||||
|
|
||||||
|
> 本文档定义 CCW Skills 审查的质量标准和评分规则。
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
质量标准确保审查过程的规范性和审查结果的一致性。审查者应遵循此标准进行审查。
|
||||||
|
|
||||||
|
## 审查者资格
|
||||||
|
|
||||||
|
### 必需能力
|
||||||
|
|
||||||
|
| 能力 | 要求 |
|
||||||
|
|------|------|
|
||||||
|
| 代码理解 | 能够理解代码逻辑和设计意图 |
|
||||||
|
| 标准掌握 | 熟悉本质量标准和相关规范 |
|
||||||
|
| 语言能力 | 代码编写语言的熟练度 |
|
||||||
|
| 架构理解 | 理解系统架构和设计模式 |
|
||||||
|
|
||||||
|
### 培训要求
|
||||||
|
|
||||||
|
- 新审查者必须完成审查培训
|
||||||
|
- 每季度参加标准更新培训
|
||||||
|
- 参与审查校准会议
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 审查过程标准
|
||||||
|
|
||||||
|
### 1. 准备阶段
|
||||||
|
|
||||||
|
- ✅ 阅读 `specs/review-dimensions.md`
|
||||||
|
- ✅ 阅读 `specs/issue-classification.md`
|
||||||
|
- ✅ 了解被审查代码的上下文
|
||||||
|
|
||||||
|
### 2. 快速扫描阶段
|
||||||
|
|
||||||
|
- 扫描所有文件,识别高风险区域
|
||||||
|
- 标记明显的问题(Critical/High)
|
||||||
|
- 评估整体代码质量
|
||||||
|
|
||||||
|
**时间限制**: 5-10 分钟
|
||||||
|
|
||||||
|
### 3. 深度审查阶段
|
||||||
|
|
||||||
|
- 逐个维度进行审查
|
||||||
|
- 记录发现的问题
|
||||||
|
- 提供具体的修复建议
|
||||||
|
|
||||||
|
**时间限制**: 20-40 分钟
|
||||||
|
|
||||||
|
### 4. 报告生成阶段
|
||||||
|
|
||||||
|
- 汇总所有发现
|
||||||
|
- 生成结构化报告
|
||||||
|
- 提供修复优先级
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 审查质量评分
|
||||||
|
|
||||||
|
### 审查完整性
|
||||||
|
|
||||||
|
| 检查项 | 权重 | 标准 |
|
||||||
|
|--------|------|------|
|
||||||
|
| 覆盖所有维度 | 30% | 6 个维度全部审查 |
|
||||||
|
| 高风险区域识别 | 20% | 快速扫描识别关键问题 |
|
||||||
|
| 问题记录详细 | 25% | 每个问题包含位置、描述、建议 |
|
||||||
|
| 报告格式规范 | 15% | 符合报告模板 |
|
||||||
|
| 修复建议可执行 | 10% | 建议具体可操作 |
|
||||||
|
|
||||||
|
**最低要求**: 70/100
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 问题发现率标准
|
||||||
|
|
||||||
|
### 期望发现率
|
||||||
|
|
||||||
|
| 严重性 | 期望发现率 | 说明 |
|
||||||
|
|--------|-----------|------|
|
||||||
|
| Critical | 100% | 所有 Critical 问题必须发现 |
|
||||||
|
| High | ≥ 90% | 大部分 High 问题应发现 |
|
||||||
|
| Medium | ≥ 60% | 主要 Medium 问题应发现 |
|
||||||
|
| Low | ≥ 30% | 部分 Low 问题应发现 |
|
||||||
|
|
||||||
|
### 质量验证
|
||||||
|
|
||||||
|
通过以下方式验证审查质量:
|
||||||
|
|
||||||
|
1. **交叉审查**: 重要 PR 由多人审查
|
||||||
|
2. **审查回顾**: 定期回顾审查质量
|
||||||
|
3. **问题追踪**: 审查遗漏问题的跟踪
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 审查效率标准
|
||||||
|
|
||||||
|
### 时间标准
|
||||||
|
|
||||||
|
| 代码规模 | 预期时间 |
|
||||||
|
|----------|----------|
|
||||||
|
| 小型 (< 200 行) | 15-20 分钟 |
|
||||||
|
| 中型 (200-500 行) | 30-40 分钟 |
|
||||||
|
| 大型 (500-1000 行) | 60-80 分钟 |
|
||||||
|
| 超大 (> 1000 行) | 分批审查 |
|
||||||
|
|
||||||
|
### 响应时间
|
||||||
|
|
||||||
|
| 优先级 | 响应时间 |
|
||||||
|
|--------|----------|
|
||||||
|
| 紧急 (P0) | 2 小时内 |
|
||||||
|
| 高 (P1) | 1 个工作日内 |
|
||||||
|
| 正常 (P2) | 2 个工作日内 |
|
||||||
|
| 低 (P3) | 1 周内 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 审查行为规范
|
||||||
|
|
||||||
|
### 应该做 ✅
|
||||||
|
|
||||||
|
- 提供建设性反馈
|
||||||
|
- 解释问题原因
|
||||||
|
- 给出具体修复建议
|
||||||
|
- 认可好的代码实践
|
||||||
|
- 保持礼貌和专业
|
||||||
|
|
||||||
|
### 不应该做 ❌
|
||||||
|
|
||||||
|
- 攻击性语言
|
||||||
|
- 模糊的批评
|
||||||
|
- 只提问题不提建议
|
||||||
|
- 忽视上下文
|
||||||
|
- 延迟审查无反馈
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 报告质量检查清单
|
||||||
|
|
||||||
|
### 完整性检查
|
||||||
|
|
||||||
|
- [ ] 所有 6 个维度已审查
|
||||||
|
- [ ] Critical/High 问题已记录
|
||||||
|
- [ ] 每个问题包含位置信息
|
||||||
|
- [ ] 每个问题有修复建议
|
||||||
|
- [ ] 报告格式符合模板
|
||||||
|
|
||||||
|
### 准确性检查
|
||||||
|
|
||||||
|
- [ ] 问题严重性分类正确
|
||||||
|
- [ ] 问题描述准确无误
|
||||||
|
- [ ] 修复建议可行有效
|
||||||
|
- [ ] 代码示例正确
|
||||||
|
|
||||||
|
### 可行性检查
|
||||||
|
|
||||||
|
- [ ] 修复建议可执行
|
||||||
|
- [ ] 优先级排序合理
|
||||||
|
- [ ] 工作量评估合理
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 审查者绩效
|
||||||
|
|
||||||
|
### KPI 指标
|
||||||
|
|
||||||
|
| 指标 | 目标值 |
|
||||||
|
|------|--------|
|
||||||
|
| 审查响应时间 | < 2 个工作日 |
|
||||||
|
| 问题发现率 | High 级别 ≥ 90% |
|
||||||
|
| 报告质量评分 | ≥ 80/100 |
|
||||||
|
| 修复建议采纳率 | ≥ 70% |
|
||||||
|
|
||||||
|
### 改进计划
|
||||||
|
|
||||||
|
未达标的审查者应:
|
||||||
|
|
||||||
|
1. 参加额外培训
|
||||||
|
2. 接受导师指导
|
||||||
|
3. 参与审查校准
|
||||||
|
4. 定期自我评估
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 参考
|
||||||
|
|
||||||
|
- [Review Dimensions](review-dimensions.md)
|
||||||
|
- [Issue Classification](issue-classification.md)
|
||||||
|
- [Document Standards](document-standards.md)
|
||||||
348
docs/skills/specs/reference-docs-spec.md
Normal file
348
docs/skills/specs/reference-docs-spec.md
Normal file
@@ -0,0 +1,348 @@
|
|||||||
|
# Reference Docs Spec
|
||||||
|
|
||||||
|
> 本文档定义 Skill 参考文档的生成规范和结构要求。
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
参考文档是 Skill 的重要组成部分,为使用者和维护者提供清晰的指导。本规范确保所有 Skill 生成规范一致的参考文档。
|
||||||
|
|
||||||
|
## 参考文档类型
|
||||||
|
|
||||||
|
| 类型 | 用途 | 位置 |
|
||||||
|
|------|------|------|
|
||||||
|
| SKILL.md | 入口文件,总览和快速开始 | 技能根目录 |
|
||||||
|
| README.md | 详细文档,面向使用者 | 技能根目录 |
|
||||||
|
| Phase 文档 | 各阶段执行说明 | phases/ 目录 |
|
||||||
|
| 规格文档 | 领域规范和质量标准 | specs/ 目录 |
|
||||||
|
| 模板文档 | 输出模板和格式 | templates/ 目录 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SKILL.md 规范
|
||||||
|
|
||||||
|
### 必需章节
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# {Skill Name}
|
||||||
|
|
||||||
|
> One-liner 描述
|
||||||
|
|
||||||
|
## One-Liner
|
||||||
|
|
||||||
|
**简短描述** — 详细说明
|
||||||
|
|
||||||
|
## Pain Points Solved
|
||||||
|
|
||||||
|
| Pain Point | Current State | Solution |
|
||||||
|
|------------|---------------|----------|
|
||||||
|
| 问题1 | 当前状态 | 解决方案 |
|
||||||
|
| 问题2 | 当前状态 | 解决方案 |
|
||||||
|
|
||||||
|
## Skills List / 功能列表
|
||||||
|
|
||||||
|
| Skill | Function | Trigger |
|
||||||
|
|-------|----------|---------|
|
||||||
|
| 子技能1 | 功能描述 | 触发命令 |
|
||||||
|
| 子技能2 | 功能描述 | 触发命令 |
|
||||||
|
|
||||||
|
## Skills Details / 详细说明
|
||||||
|
|
||||||
|
### 子技能名
|
||||||
|
|
||||||
|
**One-Liner**: 一句话描述
|
||||||
|
|
||||||
|
**Trigger**:
|
||||||
|
```shell
|
||||||
|
/trigger-command <args>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Features**:
|
||||||
|
- 特性1
|
||||||
|
- 特性2
|
||||||
|
|
||||||
|
**Architecture Overview**:
|
||||||
|
```plaintext
|
||||||
|
架构图/流程图
|
||||||
|
```
|
||||||
|
|
||||||
|
**Execution Flow**:
|
||||||
|
```plaintext
|
||||||
|
Phase 1: 描述
|
||||||
|
→ Action 1
|
||||||
|
→ Action 2
|
||||||
|
|
||||||
|
Phase 2: 描述
|
||||||
|
→ Action 1
|
||||||
|
→ Action 2
|
||||||
|
```
|
||||||
|
|
||||||
|
**Output Structure**:
|
||||||
|
```plaintext
|
||||||
|
输出目录结构
|
||||||
|
```
|
||||||
|
|
||||||
|
## Related Commands / 相关命令
|
||||||
|
|
||||||
|
- [相关命令链接](path)
|
||||||
|
|
||||||
|
## Best Practices / 最佳实践
|
||||||
|
|
||||||
|
1. 实践1
|
||||||
|
2. 实践2
|
||||||
|
|
||||||
|
## Usage Examples / 使用示例
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 示例1
|
||||||
|
/command args
|
||||||
|
|
||||||
|
# 示例2
|
||||||
|
/command --flag args
|
||||||
|
```
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## README.md 规范
|
||||||
|
|
||||||
|
### 结构要求
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# {Skill Name}
|
||||||
|
|
||||||
|
详细标题和徽章
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
详细描述技能的用途和价值
|
||||||
|
|
||||||
|
## 功能特性
|
||||||
|
|
||||||
|
- 特性1
|
||||||
|
- 特性2
|
||||||
|
|
||||||
|
## 安装配置
|
||||||
|
|
||||||
|
配置步骤
|
||||||
|
|
||||||
|
## 使用指南
|
||||||
|
|
||||||
|
详细使用说明
|
||||||
|
|
||||||
|
## 输出说明
|
||||||
|
|
||||||
|
输出内容和格式
|
||||||
|
|
||||||
|
## 故障排除
|
||||||
|
|
||||||
|
常见问题和解决方案
|
||||||
|
|
||||||
|
## 贡献指南
|
||||||
|
|
||||||
|
如何贡献
|
||||||
|
|
||||||
|
## 许可证
|
||||||
|
|
||||||
|
许可证信息
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 文档规范
|
||||||
|
|
||||||
|
### Phase 文件命名
|
||||||
|
|
||||||
|
```
|
||||||
|
phases/
|
||||||
|
├── _orchestrator.md # 编排器(可选)
|
||||||
|
├── workflow.json # 工作流定义(可选)
|
||||||
|
├── 01-{phase-name}.md # Phase 1
|
||||||
|
├── 02-{phase-name}.md # Phase 2
|
||||||
|
└── 03-{phase-name}.md # Phase 3
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 内容结构
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Phase {N}: {Phase Name}
|
||||||
|
|
||||||
|
## 目标
|
||||||
|
|
||||||
|
简要说明本阶段目标
|
||||||
|
|
||||||
|
## 前置条件
|
||||||
|
|
||||||
|
- 条件1
|
||||||
|
- 条件2
|
||||||
|
|
||||||
|
## 执行步骤
|
||||||
|
|
||||||
|
### 步骤 1: {步骤名称}
|
||||||
|
|
||||||
|
**操作**: 具体操作描述
|
||||||
|
|
||||||
|
**输入**: 输入说明
|
||||||
|
|
||||||
|
**输出**: 输出说明
|
||||||
|
|
||||||
|
**验证**: 如何验证完成
|
||||||
|
|
||||||
|
### 步骤 2: ...
|
||||||
|
|
||||||
|
## 输出
|
||||||
|
|
||||||
|
本阶段产生的输出文件和数据
|
||||||
|
|
||||||
|
## 质量检查
|
||||||
|
|
||||||
|
- [ ] 检查项1
|
||||||
|
- [ ] 检查项2
|
||||||
|
|
||||||
|
## 阶段完成标准
|
||||||
|
|
||||||
|
满足以下条件视为阶段完成:
|
||||||
|
1. 标准1
|
||||||
|
2. 标准2
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Orchestrator 文档规范
|
||||||
|
|
||||||
|
### 编排器结构
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Orchestrator
|
||||||
|
|
||||||
|
## 工作流类型
|
||||||
|
|
||||||
|
{Sequential|Autonomous}
|
||||||
|
|
||||||
|
## 状态定义
|
||||||
|
|
||||||
|
| 状态 | 描述 | 触发条件 |
|
||||||
|
|------|------|---------|
|
||||||
|
| state1 | 描述 | 触发条件 |
|
||||||
|
| state2 | 描述 | 触发条件 |
|
||||||
|
|
||||||
|
## 动作定义
|
||||||
|
|
||||||
|
| 动作 | 描述 | 前置状态 | 后置状态 |
|
||||||
|
|------|------|---------|---------|
|
||||||
|
| action1 | 描述 | state1 | state2 |
|
||||||
|
| action2 | 描述 | state2 | state3 |
|
||||||
|
|
||||||
|
## 路由逻辑
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 伪代码描述状态转换
|
||||||
|
if (condition) {
|
||||||
|
return nextAction;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 错误处理
|
||||||
|
|
||||||
|
- 错误情况1 → 处理方式
|
||||||
|
- 错误情况2 → 处理方式
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Action 文档规范
|
||||||
|
|
||||||
|
### Action 结构
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# {Action Name}
|
||||||
|
|
||||||
|
## 描述
|
||||||
|
|
||||||
|
详细描述动作的功能
|
||||||
|
|
||||||
|
## 输入参数
|
||||||
|
|
||||||
|
| 参数 | 类型 | 描述 | 必需 |
|
||||||
|
|------|------|------|------|
|
||||||
|
| param1 | type | 描述 | 是 |
|
||||||
|
| param2 | type | 描述 | 否 |
|
||||||
|
|
||||||
|
## 输出
|
||||||
|
|
||||||
|
| 字段 | 类型 | 描述 |
|
||||||
|
|------|------|------|
|
||||||
|
| field1 | type | 描述 |
|
||||||
|
| field2 | type | 描述 |
|
||||||
|
|
||||||
|
## 执行逻辑
|
||||||
|
|
||||||
|
1. 步骤1
|
||||||
|
2. 步骤2
|
||||||
|
3. 步骤3
|
||||||
|
|
||||||
|
## 错误处理
|
||||||
|
|
||||||
|
| 错误 | 处理 |
|
||||||
|
|------|------|
|
||||||
|
| error1 | 处理方式 |
|
||||||
|
|
||||||
|
## 示例
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 示例代码
|
||||||
|
```
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 文档生成模板
|
||||||
|
|
||||||
|
### 使用模板的好处
|
||||||
|
|
||||||
|
- **一致性**: 所有 Skill 文档格式统一
|
||||||
|
- **完整性**: 确保所有必需章节都包含
|
||||||
|
- **可维护性**: 模板更新自动同步到所有文档
|
||||||
|
|
||||||
|
### 模板文件位置
|
||||||
|
|
||||||
|
```
|
||||||
|
docs/skills/templates/
|
||||||
|
├── skill-md.md # SKILL.md 模板
|
||||||
|
├── sequential-phase.md # 顺序阶段模板
|
||||||
|
├── autonomous-orchestrator.md # 自主编排模板
|
||||||
|
├── autonomous-action.md # 自主行动模板
|
||||||
|
└── review-report.md # 审查报告模板
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 文档质量检查
|
||||||
|
|
||||||
|
### 自动检查
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 使用 markdownlint
|
||||||
|
markdownlint **/*.md
|
||||||
|
|
||||||
|
# 检查链接
|
||||||
|
markdown-link-check **/*.md
|
||||||
|
|
||||||
|
# 检查拼写
|
||||||
|
cspell **/*.md
|
||||||
|
```
|
||||||
|
|
||||||
|
### 手动检查清单
|
||||||
|
|
||||||
|
- [ ] 所有章节完整
|
||||||
|
- [ ] 代码示例正确
|
||||||
|
- [ ] 链接有效
|
||||||
|
- [ ] 格式一致
|
||||||
|
- [ ] 语法正确
|
||||||
|
- [ ] 无拼写错误
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 参考
|
||||||
|
|
||||||
|
- [Document Standards](document-standards.md)
|
||||||
|
- [SKILL-DESIGN-SPEC](../_shared/SKILL-DESIGN-SPEC.md)
|
||||||
182
docs/skills/specs/review-dimensions.md
Normal file
182
docs/skills/specs/review-dimensions.md
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
# Review Dimensions
|
||||||
|
|
||||||
|
> 本文档定义代码审查的 6 个维度及每个维度的检查点。
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
代码审查分为 6 个维度,每个维度包含多个检查点。审查时应对每个维度进行评分和记录。
|
||||||
|
|
||||||
|
## 审查维度
|
||||||
|
|
||||||
|
### 1. Correctness (正确性)
|
||||||
|
|
||||||
|
**关注点**: 代码逻辑是否正确,边界条件是否处理,错误处理是否完善。
|
||||||
|
|
||||||
|
| 检查点 | 描述 | 严重性 |
|
||||||
|
|--------|------|--------|
|
||||||
|
| 边界条件 | 空、null、undefined、数组越界 | Critical |
|
||||||
|
| 错误处理 | try-catch、错误传播、用户反馈 | High |
|
||||||
|
| 类型安全 | 类型转换、类型断言、any 使用 | High |
|
||||||
|
| 数据验证 | 输入验证、输出校验 | High |
|
||||||
|
| 并发安全 | 竞态条件、死锁风险 | Medium |
|
||||||
|
|
||||||
|
**评分标准**:
|
||||||
|
- **9-10**: 无明显问题,处理完善
|
||||||
|
- **7-8**: 有小问题但不影响功能
|
||||||
|
- **5-6**: 存在潜在问题,需要修复
|
||||||
|
- **< 5**: 严重问题,必须修复
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. Readability (可读性)
|
||||||
|
|
||||||
|
**关注点**: 代码是否易于理解,命名是否清晰,注释是否恰当。
|
||||||
|
|
||||||
|
| 检查点 | 描述 | 严重性 |
|
||||||
|
|--------|------|--------|
|
||||||
|
| 命名规范 | 变量、函数、类名语义化 | Medium |
|
||||||
|
| 函数长度 | 单一职责,长度适中 | Medium |
|
||||||
|
| 复杂度 | 圈复杂度、嵌套层级 | Medium |
|
||||||
|
| 注释质量 | 解释"为什么"而非"是什么" | Low |
|
||||||
|
| 代码格式 | 缩进、空行、对齐 | Low |
|
||||||
|
|
||||||
|
**评分标准**:
|
||||||
|
- **9-10**: 代码如散文般清晰
|
||||||
|
- **7-8**: 大部分清晰,少量困惑点
|
||||||
|
- **5-6**: 需要额外时间理解
|
||||||
|
- **< 5**: 代码难以理解
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. Performance (性能)
|
||||||
|
|
||||||
|
**关注点**: 算法效率、资源使用、I/O 优化。
|
||||||
|
|
||||||
|
| 检查点 | 描述 | 严重性 |
|
||||||
|
|--------|------|--------|
|
||||||
|
| 算法复杂度 | 时间/空间复杂度合理 | High |
|
||||||
|
| 数据结构选择 | 使用合适的数据结构 | High |
|
||||||
|
| I/O 优化 | 减少网络/磁盘访问 | Medium |
|
||||||
|
| 缓存策略 | 适当使用缓存 | Medium |
|
||||||
|
| 资源释放 | 内存泄漏、连接关闭 | High |
|
||||||
|
|
||||||
|
**评分标准**:
|
||||||
|
- **9-10**: 性能优化充分
|
||||||
|
- **7-8**: 性能可接受,有小优化空间
|
||||||
|
- **5-6**: 存在明显性能问题
|
||||||
|
- **< 5**: 严重性能问题
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4. Security (安全)
|
||||||
|
|
||||||
|
**关注点**: 注入风险、敏感数据、权限控制。
|
||||||
|
|
||||||
|
| 检查点 | 描述 | 严重性 |
|
||||||
|
|--------|------|--------|
|
||||||
|
| 注入风险 | SQL、命令、XSS 注入 | Critical |
|
||||||
|
| 敏感数据 | 密码、密钥、token 处理 | Critical |
|
||||||
|
| 权限控制 | 认证、授权检查 | High |
|
||||||
|
| 数据验证 | 输入净化、输出编码 | High |
|
||||||
|
| 依赖安全 | 已知漏洞的依赖 | Medium |
|
||||||
|
|
||||||
|
**评分标准**:
|
||||||
|
- **9-10**: 无明显安全问题
|
||||||
|
- **7-8**: 低风险问题
|
||||||
|
- **5-6**: 中等风险,需要修复
|
||||||
|
- **< 5**: 高风险问题,必须修复
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 5. Testing (测试)
|
||||||
|
|
||||||
|
**关注点**: 测试覆盖度、测试质量、测试可维护性。
|
||||||
|
|
||||||
|
| 检查点 | 描述 | 严重性 |
|
||||||
|
|--------|------|--------|
|
||||||
|
| 覆盖度 | 单元测试、集成测试覆盖 | High |
|
||||||
|
| 边界测试 | 边界条件、异常情况测试 | Medium |
|
||||||
|
| 断言质量 | 断言有意义、不脆弱 | Medium |
|
||||||
|
| 测试可读性 | 测试意图清晰 | Low |
|
||||||
|
| Mock/Stub | 适当使用测试替身 | Medium |
|
||||||
|
|
||||||
|
**评分标准**:
|
||||||
|
- **9-10**: 测试充分,质量高
|
||||||
|
- **7-8**: 测试覆盖主要场景
|
||||||
|
- **5-6**: 测试不足,需要补充
|
||||||
|
- **< 5**: 缺少测试
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 6. Architecture (架构)
|
||||||
|
|
||||||
|
**关注点**: 设计模式、分层结构、依赖管理。
|
||||||
|
|
||||||
|
| 检查点 | 描述 | 严重性 |
|
||||||
|
|--------|------|--------|
|
||||||
|
| 设计模式 | 适当使用设计模式 | Medium |
|
||||||
|
| 模块化 | 高内聚、低耦合 | High |
|
||||||
|
| 依赖方向 | 依赖倒置、循环依赖 | High |
|
||||||
|
| 接口设计 | 接口稳定、扩展点明确 | Medium |
|
||||||
|
| 一致性 | 与现有架构风格一致 | Medium |
|
||||||
|
|
||||||
|
**评分标准**:
|
||||||
|
- **9-10**: 架构清晰,易于扩展
|
||||||
|
- **7-8**: 架构合理,小问题
|
||||||
|
- **5-6**: 架构问题,需要重构
|
||||||
|
- **< 5**: 架构混乱
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 审查报告格式
|
||||||
|
|
||||||
|
### 单维度报告模板
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### Correctness: 7/10
|
||||||
|
|
||||||
|
**发现的问题**:
|
||||||
|
|
||||||
|
#### [H] Null pointer risk
|
||||||
|
- **位置**: `src/auth.ts:45`
|
||||||
|
- **问题**: 用户对象可能为 null,直接访问属性会崩溃
|
||||||
|
- **建议**:
|
||||||
|
```typescript
|
||||||
|
// 添加空检查
|
||||||
|
if (user) {
|
||||||
|
return user.name;
|
||||||
|
}
|
||||||
|
return 'Anonymous';
|
||||||
|
```
|
||||||
|
|
||||||
|
#### [M] Missing error handling
|
||||||
|
- **位置**: `src/api.ts:78`
|
||||||
|
- **问题**: 网络请求没有错误处理
|
||||||
|
- **建议**: 添加 try-catch
|
||||||
|
|
||||||
|
**优点**:
|
||||||
|
- 边界条件检查完善
|
||||||
|
- 类型使用恰当
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 快速扫描检查点
|
||||||
|
|
||||||
|
快速扫描阶段只检查高优先级问题:
|
||||||
|
|
||||||
|
| 维度 | 高优先级检查点 |
|
||||||
|
|------|----------------|
|
||||||
|
| Correctness | 空指针、类型断言、未处理 Promise |
|
||||||
|
| Readability | 过长函数 (>50行)、过深嵌套 (>4层) |
|
||||||
|
| Performance | O(n²) 以上算法、循环中 I/O |
|
||||||
|
| Security | SQL 注入、XSS、硬编码密钥 |
|
||||||
|
| Testing | 关键路径缺少测试 |
|
||||||
|
| Architecture | 循环依赖、上帝对象 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 参考
|
||||||
|
|
||||||
|
- [Issue Classification](issue-classification.md)
|
||||||
|
- [Quality Standards](quality-standards.md)
|
||||||
245
docs/skills/templates/architecture-doc.md
Normal file
245
docs/skills/templates/architecture-doc.md
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
# Architecture Doc Template
|
||||||
|
|
||||||
|
> 用途: 架构文档模板,用于 spec-generator Phase 4 输出
|
||||||
|
|
||||||
|
## 模板
|
||||||
|
|
||||||
|
### _index.md 模板
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Architecture - Index
|
||||||
|
|
||||||
|
> **Product**: {Product Name}
|
||||||
|
> **Generated**: {YYYY-MM-DD}
|
||||||
|
> **Session**: {session-id}
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
架构概述(2-3 段)
|
||||||
|
|
||||||
|
## Architecture Principles
|
||||||
|
|
||||||
|
1. **Principle 1**: Description
|
||||||
|
2. **Principle 2**: Description
|
||||||
|
3. **Principle 3**: Description
|
||||||
|
|
||||||
|
## System Architecture
|
||||||
|
|
||||||
|
### High-Level Architecture
|
||||||
|
|
||||||
|
```plaintext
|
||||||
|
┌─────────────────────────────────────────────────┐
|
||||||
|
│ Frontend │
|
||||||
|
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||||||
|
│ │ Web UI │ │ Mobile │ │ API │ │
|
||||||
|
│ └──────────┘ └──────────┘ └──────────┘ │
|
||||||
|
└─────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────┐
|
||||||
|
│ Backend Layer │
|
||||||
|
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||||||
|
│ │ Gateway │ │ Services │ │ Events │ │
|
||||||
|
│ └──────────┘ └──────────┘ └──────────┘ │
|
||||||
|
└─────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────┐
|
||||||
|
│ Data Layer │
|
||||||
|
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||||||
|
│ | Database │ │ Cache │ │ Storage │ │
|
||||||
|
│ └──────────┘ └──────────┘ └──────────┘ │
|
||||||
|
└─────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Components
|
||||||
|
|
||||||
|
| Component | Responsibility | Technology |
|
||||||
|
|-----------|----------------|------------|
|
||||||
|
| Component 1 | 描述 | 技术栈 |
|
||||||
|
| Component 2 | 描述 | 技术栈 |
|
||||||
|
| Component 3 | 描述 | 技术栈 |
|
||||||
|
|
||||||
|
## Technology Stack
|
||||||
|
|
||||||
|
### Frontend
|
||||||
|
|
||||||
|
| Layer | Technology | Rationale |
|
||||||
|
|-------|-----------|-----------|
|
||||||
|
| Framework | {framework} | 选择理由 |
|
||||||
|
| State | {state lib} | 选择理由 |
|
||||||
|
| UI | {ui lib} | 选择理由 |
|
||||||
|
|
||||||
|
### Backend
|
||||||
|
|
||||||
|
| Layer | Technology | Rationale |
|
||||||
|
|-------|-----------|-----------|
|
||||||
|
| Runtime | {runtime} | 选择理由 |
|
||||||
|
| Framework | {framework} | 选择理由 |
|
||||||
|
| Database | {database} | 选择理由 |
|
||||||
|
|
||||||
|
## Architecture Decisions
|
||||||
|
|
||||||
|
详见各 ADR 文档:
|
||||||
|
- [ADR-001-{title}](./ADR-001-{slug}.md)
|
||||||
|
- [ADR-002-{title}](./ADR-002-{slug}.md)
|
||||||
|
|
||||||
|
## Requirements Traceability
|
||||||
|
|
||||||
|
| Requirement | ADR | Component |
|
||||||
|
|-------------|-----|-----------|
|
||||||
|
| REQ-001 | ADR-001 | Service A |
|
||||||
|
| REQ-002 | ADR-002 | Service B |
|
||||||
|
|
||||||
|
## Change Log
|
||||||
|
|
||||||
|
| Version | Date | Changes |
|
||||||
|
|---------|------|---------|
|
||||||
|
| 1.0.0 | YYYY-MM-DD | Initial version |
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ADR-*.md 模板
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# ADR-{NNN}: {Decision Title}
|
||||||
|
|
||||||
|
> **Status**: {Accepted|Deprecated|Superseded}
|
||||||
|
> **Date**: {YYYY-MM-DD}
|
||||||
|
> **Decision Type**: {Technical|Process|Architecture}
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
描述背景和问题:
|
||||||
|
- 当前状况
|
||||||
|
- 需要解决的问题
|
||||||
|
- 约束条件
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
**决策内容**: 一句话概括
|
||||||
|
|
||||||
|
详细说明决策内容
|
||||||
|
|
||||||
|
### Options Considered
|
||||||
|
|
||||||
|
| Option | Pros | Cons | Selected |
|
||||||
|
|--------|------|------|----------|
|
||||||
|
| Option 1 | 优点 | 缺点 | No |
|
||||||
|
| Option 2 | 优点 | 缺点 | **Yes** |
|
||||||
|
| Option 3 | 优点 | 缺点 | No |
|
||||||
|
|
||||||
|
### Rationale
|
||||||
|
|
||||||
|
选择此方案的原因:
|
||||||
|
1. Reason 1
|
||||||
|
2. Reason 2
|
||||||
|
3. Reason 3
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
### Positive
|
||||||
|
|
||||||
|
- Benefit 1
|
||||||
|
- Benefit 2
|
||||||
|
|
||||||
|
### Negative
|
||||||
|
|
||||||
|
- Risk 1
|
||||||
|
- Risk 2
|
||||||
|
|
||||||
|
### Mitigation
|
||||||
|
|
||||||
|
如何缓解负面影响
|
||||||
|
|
||||||
|
## Related Requirements
|
||||||
|
|
||||||
|
| Requirement | Relation |
|
||||||
|
|-------------|----------|
|
||||||
|
| REQ-001 | Enables |
|
||||||
|
| NFR-PERF-001 | Addresses |
|
||||||
|
|
||||||
|
## Alternatives Considered
|
||||||
|
|
||||||
|
1. Alternative 1: Description - Rejected because...
|
||||||
|
2. Alternative 2: Description - Rejected because...
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [Related Doc](path)
|
||||||
|
- [External Reference](url)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用说明
|
||||||
|
|
||||||
|
1. **触发**: spec-generator Phase 4
|
||||||
|
2. **输入**: Phase 3 Requirements (PRD)
|
||||||
|
3. **输出**: architecture/ 目录,包含 _index.md 和所有 ADR 文件
|
||||||
|
4. **验证**: 确保追溯链接有效
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 示例
|
||||||
|
|
||||||
|
### 简化示例 - ADR-001.md
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# ADR-001: Use Operational Transformation for Real-Time Sync
|
||||||
|
|
||||||
|
> **Status**: Accepted
|
||||||
|
> **Date**: 2026-03-01
|
||||||
|
> **Decision Type**: Technical
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
实时协作平台需要支持多用户同时编辑同一文档。
|
||||||
|
- 简单的"最后写入胜出"策略会导致数据丢失
|
||||||
|
- 需要自动解决编辑冲突
|
||||||
|
- 网络延迟和离线编辑需要支持
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
**决策内容**: 使用 Operational Transformation (OT) 算法实现实时同步
|
||||||
|
|
||||||
|
OT 通过转换操作序列来解决冲突,确保最终一致性。
|
||||||
|
|
||||||
|
### Options Considered
|
||||||
|
|
||||||
|
| Option | Pros | Cons | Selected |
|
||||||
|
|--------|------|------|----------|
|
||||||
|
| Last Write Wins | 简单 | 数据丢失 | No |
|
||||||
|
| OT (Yjs) | 成熟方案 | 复杂度高 | **Yes** |
|
||||||
|
| CRDT | 理论优雅 | 性能开销 | No |
|
||||||
|
|
||||||
|
### Rationale
|
||||||
|
|
||||||
|
1. **成熟度**: Yjs 是成熟的 OT 实现,被广泛应用
|
||||||
|
2. **性能**: OT 比 CRDT 性能更好,适合文档编辑场景
|
||||||
|
3. **社区**: 活跃的社区支持和丰富的集成
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
### Positive
|
||||||
|
|
||||||
|
- 自动解决冲突,无需用户干预
|
||||||
|
- 支持离线编辑,重连后自动同步
|
||||||
|
- 低延迟,良好的用户体验
|
||||||
|
|
||||||
|
### Negative
|
||||||
|
|
||||||
|
- 实现复杂,学习曲线陡峭
|
||||||
|
- 需要维护操作历史,内存开销
|
||||||
|
|
||||||
|
### Mitigation
|
||||||
|
|
||||||
|
- 使用成熟的 Yjs 库,降低实现复杂度
|
||||||
|
- 实现历史压缩策略,控制内存使用
|
||||||
|
|
||||||
|
## Related Requirements
|
||||||
|
|
||||||
|
| Requirement | Relation |
|
||||||
|
|-------------|----------|
|
||||||
|
| REQ-001 | Enables |
|
||||||
|
| NFR-PERF-001 | Addresses |
|
||||||
|
```
|
||||||
260
docs/skills/templates/autonomous-action.md
Normal file
260
docs/skills/templates/autonomous-action.md
Normal file
@@ -0,0 +1,260 @@
|
|||||||
|
# Autonomous Action Template
|
||||||
|
|
||||||
|
> 用途: 自主行动模板,用于 Autonomous 类型 Skill 的具体行动
|
||||||
|
|
||||||
|
## 模板
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Action: {Action Name}
|
||||||
|
|
||||||
|
> **Skill**: {Skill Name}
|
||||||
|
> **Action Type**: {Analysis|Execution|Verification}
|
||||||
|
> **Estimated Duration**: {X minutes}
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
详细描述这个行动的目的和功能
|
||||||
|
|
||||||
|
## Trigger Conditions
|
||||||
|
|
||||||
|
| Condition | Description | Check Method |
|
||||||
|
|-----------|-------------|--------------|
|
||||||
|
| condition1 | 描述 | 如何检查 |
|
||||||
|
| condition2 | 描述 | 如何检查 |
|
||||||
|
|
||||||
|
## Input Parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Required | Description | Default |
|
||||||
|
|-----------|------|----------|-------------|---------|
|
||||||
|
| param1 | string | Yes | 参数描述 | - |
|
||||||
|
| param2 | number | No | 参数描述 | 42 |
|
||||||
|
| param3 | array | No | 参数描述 | [] |
|
||||||
|
|
||||||
|
## Output
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| result1 | string | 结果描述 |
|
||||||
|
| result2 | object | 结果描述 |
|
||||||
|
| success | boolean | 行动是否成功 |
|
||||||
|
|
||||||
|
## Execution Logic
|
||||||
|
|
||||||
|
### Step 1: {Step Name}
|
||||||
|
|
||||||
|
**Description**: 步骤描述
|
||||||
|
|
||||||
|
**Implementation**:
|
||||||
|
```typescript
|
||||||
|
// 示例代码
|
||||||
|
const step1Result = await doSomething(input);
|
||||||
|
if (!step1Result) {
|
||||||
|
throw new Error('Step 1 failed');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Validation**: 如何验证步骤成功
|
||||||
|
|
||||||
|
### Step 2: {Step Name}
|
||||||
|
|
||||||
|
**Description**: 步骤描述
|
||||||
|
|
||||||
|
**Implementation**:
|
||||||
|
```typescript
|
||||||
|
// 示例代码
|
||||||
|
const step2Result = await process(step1Result);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Validation**: 如何验证步骤成功
|
||||||
|
|
||||||
|
### Step 3: {Step Name}
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
| Error | Cause | Handling | Retry |
|
||||||
|
|-------|-------|----------|-------|
|
||||||
|
| ErrorType1 | 原因描述 | 处理方式 | Yes/No |
|
||||||
|
| ErrorType2 | 原因描述 | 处理方式 | Yes/No |
|
||||||
|
|
||||||
|
**Error Recovery Logic**:
|
||||||
|
```typescript
|
||||||
|
try {
|
||||||
|
return await execute();
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof RecoverableError) {
|
||||||
|
return await recover(error);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Side Effects
|
||||||
|
|
||||||
|
| Effect | Description | Mitigation |
|
||||||
|
|--------|-------------|------------|
|
||||||
|
| Side effect 1 | 描述 | 缓解措施 |
|
||||||
|
| Side effect 2 | 描述 | 缓解措施 |
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
| Dependency | Type | Version | Description |
|
||||||
|
|------------|------|---------|-------------|
|
||||||
|
| dep1 | internal | - | 内部依赖描述 |
|
||||||
|
| dep2 | external | ^1.0.0 | 外部依赖描述 |
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
### Test Cases
|
||||||
|
|
||||||
|
| Case | Input | Expected Output |
|
||||||
|
|------|-------|-----------------|
|
||||||
|
| Normal case | `{param: "value"}` | `{result: "expected"}` |
|
||||||
|
| Edge case | `{param: null}` | `{result: "default"}` |
|
||||||
|
| Error case | `{param: "invalid"}` | Throws Error |
|
||||||
|
|
||||||
|
### Test Implementation
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
describe('ActionName', () => {
|
||||||
|
it('should handle normal case', async () => {
|
||||||
|
const result = await actionName({ param: 'value' });
|
||||||
|
expect(result).toEqual({ result: 'expected' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle edge case', async () => {
|
||||||
|
const result = await actionName({ param: null });
|
||||||
|
expect(result).toEqual({ result: 'default' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw on invalid input', async () => {
|
||||||
|
await expect(actionName({ param: 'invalid' }))
|
||||||
|
.rejects.toThrow(Error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Metrics
|
||||||
|
|
||||||
|
| Metric | Type | Unit |
|
||||||
|
|--------|------|------|
|
||||||
|
| duration | histogram | milliseconds |
|
||||||
|
| success_rate | gauge | percentage |
|
||||||
|
| error_count | counter | count |
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 基本使用
|
||||||
|
const result = await actionName({
|
||||||
|
param1: 'value1',
|
||||||
|
param2: 100
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(result);
|
||||||
|
|
||||||
|
// 带重试
|
||||||
|
const result = await actionName.withRetry({
|
||||||
|
param1: 'value1'
|
||||||
|
}, { maxRetries: 3 });
|
||||||
|
|
||||||
|
// 带回调
|
||||||
|
await actionName.withCallback({
|
||||||
|
param1: 'value1'
|
||||||
|
}, {
|
||||||
|
onProgress: (progress) => console.log(progress),
|
||||||
|
onComplete: (result) => console.log('Done', result)
|
||||||
|
});
|
||||||
|
```
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用说明
|
||||||
|
|
||||||
|
1. **触发**: skill-generator Phase 3 (Autonomous 模式)
|
||||||
|
2. **输入**: Phase 2 skill-config.json
|
||||||
|
3. **输出**: actions/{action-name}.md
|
||||||
|
4. **命名**: 使用动词-名词格式 (如 collect-context.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 示例
|
||||||
|
|
||||||
|
### 简化示例
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Action: quick-scan
|
||||||
|
|
||||||
|
> **Skill**: review-code
|
||||||
|
> **Action Type**: Analysis
|
||||||
|
> **Estimated Duration**: 5 minutes
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
快速扫描目标代码,识别高风险区域,为深度审查提供焦点
|
||||||
|
|
||||||
|
## Trigger Conditions
|
||||||
|
|
||||||
|
| Condition | Description | Check Method |
|
||||||
|
|-----------|-------------|--------------|
|
||||||
|
| context_collected | 上下文收集完成 | state.context != null |
|
||||||
|
| target_exists | 目标路径有效 | file exists check |
|
||||||
|
|
||||||
|
## Input Parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Required | Description | Default |
|
||||||
|
|-----------|------|----------|-------------|---------|
|
||||||
|
| files | array | Yes | 要扫描的文件列表 | - |
|
||||||
|
| dimensions | array | No | 要检查的维度 | ["all"] |
|
||||||
|
| risk_threshold | number | No | 风险阈值 | 0.5 |
|
||||||
|
|
||||||
|
## Output
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| risk_areas | array | 高风险区域列表 |
|
||||||
|
| scan_summary | object | 扫描摘要 |
|
||||||
|
| recommendations | array | 建议的审查重点 |
|
||||||
|
|
||||||
|
## Execution Logic
|
||||||
|
|
||||||
|
### Step 1: 按文件类型分组
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const groups = groupBy(files, f => f.extension);
|
||||||
|
// ts/tsx -> TypeScript review rules
|
||||||
|
// py -> Python review rules
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2: 应用快速检查规则
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const risks = [];
|
||||||
|
for (const file of files) {
|
||||||
|
const fileRisks = applyQuickChecks(file);
|
||||||
|
risks.push(...fileRisks);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: 聚合和排序
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const aggregated = aggregateByLocation(risks);
|
||||||
|
const sorted = sortBySeverity(aggregated);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
| Error | Cause | Handling | Retry |
|
||||||
|
|-------|-------|----------|-------|
|
||||||
|
| FileAccessError | 文件无法读取 | 跳过文件,记录警告 | No |
|
||||||
|
| ParseError | 代码解析失败 | 标记为需要人工审查 | No |
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
| Case | Input | Expected Output |
|
||||||
|
|------|-------|-----------------|
|
||||||
|
| Normal | 10 files | risk_areas non-empty |
|
||||||
|
| Empty | 0 files | risk_areas empty |
|
||||||
|
| ParseError | Invalid code | Area marked for manual review |
|
||||||
|
```
|
||||||
311
docs/skills/templates/autonomous-orchestrator.md
Normal file
311
docs/skills/templates/autonomous-orchestrator.md
Normal file
@@ -0,0 +1,311 @@
|
|||||||
|
# Autonomous Orchestrator Template
|
||||||
|
|
||||||
|
> 用途: 自主编排器模板,用于 Autonomous 类型 Skill
|
||||||
|
|
||||||
|
## 模板
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Orchestrator
|
||||||
|
|
||||||
|
> **Skill**: {Skill Name}
|
||||||
|
> **Type**: Autonomous (State-Driven)
|
||||||
|
> **Version**: 1.0.0
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
本编排器使用状态驱动的决策引擎,根据当前状态自动选择下一步行动。
|
||||||
|
|
||||||
|
## State Machine
|
||||||
|
|
||||||
|
### State Definition
|
||||||
|
|
||||||
|
| State | Description | Entry Condition | Exit Actions |
|
||||||
|
|-------|-------------|-----------------|--------------|
|
||||||
|
| `initialized` | 初始状态,等待目标 | Skill 启动 | 转到 `collecting_context` |
|
||||||
|
| `collecting_context` | 收集上下文信息 | 进入状态 | 收集完成后转到 `analyzing` |
|
||||||
|
| `analyzing` | 分析目标内容 | 上下文就绪 | 分析完成后转到 `planning` 或 `executing` |
|
||||||
|
| `planning` | 制定执行计划 | 需要复杂规划 | 计划完成后转到 `executing` |
|
||||||
|
| `executing` | 执行具体行动 | 计划就绪 | 执行完成后转到 `verifying` |
|
||||||
|
| `verifying` | 验证执行结果 | 执行完成 | 验证通过转到 `completed`,否则转到 `analyzing` |
|
||||||
|
| `completed` | 任务完成 | 所有目标达成 | 结束 |
|
||||||
|
| `error` | 错误状态 | 发生错误 | 根据错误类型恢复或终止 |
|
||||||
|
|
||||||
|
### State Transitions
|
||||||
|
|
||||||
|
```plaintext
|
||||||
|
┌─────────────────┐
|
||||||
|
│ initialized │
|
||||||
|
└────────┬────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────┐
|
||||||
|
│collecting_context│
|
||||||
|
└────────┬────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────┐
|
||||||
|
│ analyzing │◄─────────┐
|
||||||
|
└────────┬────────┘ │
|
||||||
|
│ │
|
||||||
|
┌────────┴────────┐ │
|
||||||
|
│ │ │
|
||||||
|
▼ ▼ │
|
||||||
|
┌──────────┐ ┌──────────┐ │
|
||||||
|
│ planning │ │executing │────┘
|
||||||
|
└────┬─────┘ └────┬─────┘
|
||||||
|
│ │
|
||||||
|
│ ┌────────┴────────┐
|
||||||
|
│ │ │
|
||||||
|
▼ ▼ ▼
|
||||||
|
┌──────────────────────────────┐
|
||||||
|
│ verifying │
|
||||||
|
└───────────┬──────────────────┘
|
||||||
|
│
|
||||||
|
┌─────────┴─────────┐
|
||||||
|
│ │
|
||||||
|
▼ ▼
|
||||||
|
┌──────────┐ ┌──────────┐
|
||||||
|
│completed │ │ error │
|
||||||
|
└──────────┘ └──────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Action Registry
|
||||||
|
|
||||||
|
### Available Actions
|
||||||
|
|
||||||
|
| Action | From States | To States | Description |
|
||||||
|
|--------|------------|-----------|-------------|
|
||||||
|
| `collect_context` | `initialized`, `error` | `analyzing` | 收集必要的上下文信息 |
|
||||||
|
| `analyze_target` | `collecting_context` | `planning` or `executing` | 分析目标,决定需要规划还是直接执行 |
|
||||||
|
| `create_plan` | `analyzing` | `executing` | 创建详细执行计划 |
|
||||||
|
| `execute_action` | `planning`, `analyzing` | `verifying` | 执行具体行动 |
|
||||||
|
| `verify_result` | `executing` | `completed` or `analyzing` | 验证执行结果 |
|
||||||
|
| `handle_error` | Any | `error` or `analyzing` | 处理错误情况 |
|
||||||
|
| `complete` | `verifying` | `completed` | 标记任务完成 |
|
||||||
|
|
||||||
|
## Routing Logic
|
||||||
|
|
||||||
|
### Decision Tree
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 伪代码:状态路由决策
|
||||||
|
function route(currentState: State, context: Context): Action {
|
||||||
|
switch (currentState) {
|
||||||
|
case 'initialized':
|
||||||
|
return Action.COLLECT_CONTEXT;
|
||||||
|
|
||||||
|
case 'collecting_context':
|
||||||
|
if (context.hasEnoughData()) {
|
||||||
|
return Action.ANALYZE_TARGET;
|
||||||
|
}
|
||||||
|
return Action.COLLECT_CONTEXT; // 继续收集
|
||||||
|
|
||||||
|
case 'analyzing':
|
||||||
|
if (context.needsPlanning()) {
|
||||||
|
return Action.CREATE_PLAN;
|
||||||
|
}
|
||||||
|
return Action.EXECUTE_ACTION;
|
||||||
|
|
||||||
|
case 'planning':
|
||||||
|
return Action.EXECUTE_ACTION;
|
||||||
|
|
||||||
|
case 'executing':
|
||||||
|
return Action.VERIFY_RESULT;
|
||||||
|
|
||||||
|
case 'verifying':
|
||||||
|
if (context.isResultValid()) {
|
||||||
|
return Action.COMPLETE;
|
||||||
|
}
|
||||||
|
return Action.ANALYZE_TARGET; // 重新分析
|
||||||
|
|
||||||
|
case 'error':
|
||||||
|
if (context.isRecoverable()) {
|
||||||
|
return Action.ANALYZE_TARGET;
|
||||||
|
}
|
||||||
|
throw new UnrecoverableError(context.error);
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new UnknownStateError(currentState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Condition Evaluation
|
||||||
|
|
||||||
|
| Condition | Evaluation | Result |
|
||||||
|
|-----------|------------|--------|
|
||||||
|
| `hasEnoughData()` | context.files.length > 0 | Boolean |
|
||||||
|
| `needsPlanning()` | context.complexity > threshold | Boolean |
|
||||||
|
| `isResultValid()` | validation.checks passed | Boolean |
|
||||||
|
| `isRecoverable()` | error.type in recoverableErrors | Boolean |
|
||||||
|
|
||||||
|
## State Persistence
|
||||||
|
|
||||||
|
### State File Structure
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"state": "analyzing",
|
||||||
|
"history": [
|
||||||
|
{ "state": "initialized", "timestamp": "2026-03-01T10:00:00Z" },
|
||||||
|
{ "state": "collecting_context", "timestamp": "2026-03-01T10:00:05Z" },
|
||||||
|
{ "state": "analyzing", "timestamp": "2026-03-01T10:00:15Z" }
|
||||||
|
],
|
||||||
|
"context": {
|
||||||
|
"target": "...",
|
||||||
|
"files": [],
|
||||||
|
"findings": []
|
||||||
|
},
|
||||||
|
"metrics": {
|
||||||
|
"actionsExecuted": 3,
|
||||||
|
"errors": 0,
|
||||||
|
"startTime": "2026-03-01T10:00:00Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Save/Restore
|
||||||
|
|
||||||
|
- **Save**: 每次状态转换后保存
|
||||||
|
- **Restore**: Skill 重启时从文件恢复
|
||||||
|
- **Reset**: 新任务开始时重置状态
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
### Error Classification
|
||||||
|
|
||||||
|
| Error Type | Severity | Recovery |
|
||||||
|
|------------|----------|----------|
|
||||||
|
| `ContextError` | Medium | 重新收集上下文 |
|
||||||
|
| `ValidationError` | High | 重新分析并调整 |
|
||||||
|
| `ExecutionError` | High | 尝试替代行动 |
|
||||||
|
| `FatalError` | Critical | 终止并报告 |
|
||||||
|
|
||||||
|
### Error Recovery Strategies
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function handleError(error: Error, state: State): RecoveryAction {
|
||||||
|
if (error instanceof ContextError) {
|
||||||
|
return RecoveryAction.RETRY_WITH_ALTERNATIVE_SOURCE;
|
||||||
|
}
|
||||||
|
if (error instanceof ValidationError) {
|
||||||
|
return RecoveryAction.ADJUST_AND_RETRY;
|
||||||
|
}
|
||||||
|
if (error instanceof ExecutionError) {
|
||||||
|
return RecoveryAction.TRY_ALTERNATIVE_ACTION;
|
||||||
|
}
|
||||||
|
return RecoveryAction.ABORT;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Metrics and Observability
|
||||||
|
|
||||||
|
### Tracked Metrics
|
||||||
|
|
||||||
|
| Metric | Type | Description |
|
||||||
|
|--------|------|-------------|
|
||||||
|
| `state_transitions` | Counter | 状态转换次数 |
|
||||||
|
| `action_duration` | Histogram | 每个行动的执行时间 |
|
||||||
|
| `error_count` | Counter | 错误发生次数 |
|
||||||
|
| `completion_rate` | Gauge | 任务完成率 |
|
||||||
|
|
||||||
|
### Logging
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 每次状态转换记录日志
|
||||||
|
log.info('State transition', {
|
||||||
|
from: oldState,
|
||||||
|
to: newState,
|
||||||
|
action: executedAction,
|
||||||
|
timestamp: now()
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
### Test Scenarios
|
||||||
|
|
||||||
|
1. **Happy Path**: 正常流程从初始化到完成
|
||||||
|
2. **Error Recovery**: 错误发生后正确恢复
|
||||||
|
3. **State Persistence**: 状态正确保存和恢复
|
||||||
|
4. **Edge Cases**: 边界条件处理
|
||||||
|
|
||||||
|
### Test Example
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
describe('Orchestrator', () => {
|
||||||
|
it('should complete happy path', async () => {
|
||||||
|
const orchestrator = new Orchestrator();
|
||||||
|
await orchestrator.run({ target: 'test' });
|
||||||
|
expect(orchestrator.state).toBe('completed');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should recover from errors', async () => {
|
||||||
|
const orchestrator = new Orchestrator();
|
||||||
|
// 模拟错误后恢复
|
||||||
|
await expect(orchestrator.run()).toEventuallyComplete();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用说明
|
||||||
|
|
||||||
|
1. **触发**: skill-generator Phase 3 (Autonomous 模式)
|
||||||
|
2. **输入**: Phase 2 skill-config.json
|
||||||
|
3. **输出**: phases/_orchestrator.md
|
||||||
|
4. **验证**: 确保状态转换逻辑完整
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 示例
|
||||||
|
|
||||||
|
### 简化示例
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Orchestrator
|
||||||
|
|
||||||
|
> **Skill**: review-code
|
||||||
|
> **Type**: Autonomous (State-Driven)
|
||||||
|
> **Version**: 1.0.0
|
||||||
|
|
||||||
|
## State Transitions
|
||||||
|
|
||||||
|
```plaintext
|
||||||
|
initialized → collecting_context → analyzing
|
||||||
|
↓
|
||||||
|
┌───────┴────────┐
|
||||||
|
↓ ↓
|
||||||
|
quick_scan deep_review
|
||||||
|
↓ ↓
|
||||||
|
└───────┬────────┘
|
||||||
|
↓
|
||||||
|
generating_report
|
||||||
|
↓
|
||||||
|
completed
|
||||||
|
```
|
||||||
|
|
||||||
|
## Action Registry
|
||||||
|
|
||||||
|
| Action | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| `collect_context` | 收集目标文件信息 |
|
||||||
|
| `quick_scan` | 快速扫描识别高风险区域 |
|
||||||
|
| `deep_review` | 按 6 维度深度审查 |
|
||||||
|
| `generate_report` | 生成审查报告 |
|
||||||
|
|
||||||
|
## Routing Logic
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function route(state, context) {
|
||||||
|
if (state === 'initialized') return 'collect_context';
|
||||||
|
if (state === 'collecting_context') return 'quick_scan';
|
||||||
|
if (state === 'analyzing') {
|
||||||
|
if (context.hasHighRiskAreas()) return 'deep_review';
|
||||||
|
return 'generate_report';
|
||||||
|
}
|
||||||
|
if (state === 'deep_review') return 'generate_report';
|
||||||
|
return 'complete';
|
||||||
|
}
|
||||||
|
```
|
||||||
|
```
|
||||||
311
docs/skills/templates/epics-template.md
Normal file
311
docs/skills/templates/epics-template.md
Normal file
@@ -0,0 +1,311 @@
|
|||||||
|
# Epics Template
|
||||||
|
|
||||||
|
> 用途: Epic 和 Story 文档模板,用于 spec-generator Phase 5 输出
|
||||||
|
|
||||||
|
## 模板
|
||||||
|
|
||||||
|
### _index.md 模板
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Epics - Index
|
||||||
|
|
||||||
|
> **Product**: {Product Name}
|
||||||
|
> **Generated**: {YYYY-MM-DD}
|
||||||
|
> **Session**: {session-id}
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Epic 概述(2-3 段)
|
||||||
|
|
||||||
|
## Epic Summary
|
||||||
|
|
||||||
|
| Epic | Title | Stories | Status | Priority |
|
||||||
|
|------|-------|---------|--------|----------|
|
||||||
|
| EPIC-001 | Epic 标题 | N stories | {Draft|Ready|In Progress} | {P0|P1|P2} |
|
||||||
|
| EPIC-002 | Epic 标题 | N stories | {Draft|Ready|In Progress} | {P0|P1|P2} |
|
||||||
|
|
||||||
|
## Dependency Graph
|
||||||
|
|
||||||
|
```plaintext
|
||||||
|
EPIC-001 (Foundation)
|
||||||
|
│
|
||||||
|
├── EPIC-002 (Feature A)
|
||||||
|
│ │
|
||||||
|
│ └── EPIC-004 (Enhancement)
|
||||||
|
│
|
||||||
|
└── EPIC-003 (Feature B)
|
||||||
|
```
|
||||||
|
|
||||||
|
## MVP Scope
|
||||||
|
|
||||||
|
以下 Epic 属于 MVP:
|
||||||
|
- [ ] EPIC-001: {Epic title}
|
||||||
|
- [ ] EPIC-002: {Epic title}
|
||||||
|
- [ ] EPIC-003: {Epic title}
|
||||||
|
|
||||||
|
**Estimated MVP Duration**: {X weeks}
|
||||||
|
|
||||||
|
## Requirements Traceability
|
||||||
|
|
||||||
|
| Requirement | Epic(s) | Story Count |
|
||||||
|
|-------------|---------|-------------|
|
||||||
|
| REQ-001 | EPIC-001 | 3 |
|
||||||
|
| REQ-002 | EPIC-001, EPIC-002 | 5 |
|
||||||
|
|
||||||
|
## Architecture Traceability
|
||||||
|
|
||||||
|
| ADR | Epic(s) | Component |
|
||||||
|
|-----|---------|-----------|
|
||||||
|
| ADR-001 | EPIC-001 | Service A |
|
||||||
|
| ADR-002 | EPIC-002 | Service B |
|
||||||
|
|
||||||
|
## Epics
|
||||||
|
|
||||||
|
详见各 Epic 文档:
|
||||||
|
- [EPIC-001-{title}](./EPIC-001-{slug}.md)
|
||||||
|
- [EPIC-002-{title}](./EPIC-002-{slug}.md)
|
||||||
|
|
||||||
|
## Change Log
|
||||||
|
|
||||||
|
| Version | Date | Changes |
|
||||||
|
|---------|------|---------|
|
||||||
|
| 1.0.0 | YYYY-MM-DD | Initial version |
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### EPIC-*.md 模板
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# EPIC-{NNN}: {Epic Title}
|
||||||
|
|
||||||
|
> **Priority**: {P0|P1|P2|P3}
|
||||||
|
> **Status**: {Draft|Ready|In Progress|Done}
|
||||||
|
> **MVP**: {Yes|No}
|
||||||
|
> **Created**: {YYYY-MM-DD}
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
详细描述 Epic 的目标和范围
|
||||||
|
|
||||||
|
## Business Value
|
||||||
|
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| 业务价值1 | 描述 |
|
||||||
|
| 业务价值2 | 描述 |
|
||||||
|
|
||||||
|
## Related Requirements
|
||||||
|
|
||||||
|
| Requirement | Relation |
|
||||||
|
|-------------|----------|
|
||||||
|
| REQ-001 | Addresses |
|
||||||
|
| REQ-002 | Partially Addresses |
|
||||||
|
|
||||||
|
## Stories
|
||||||
|
|
||||||
|
| Story | Title | Points | Status |
|
||||||
|
|-------|-------|--------|--------|
|
||||||
|
| STORY-001 | Story 标题 | {points} | {Todo|In Progress|Done} |
|
||||||
|
| STORY-002 | Story 标题 | {points} | {Todo|In Progress|Done} |
|
||||||
|
|
||||||
|
### STORY-001: {Story Title}
|
||||||
|
|
||||||
|
**As a** {user type},
|
||||||
|
**I want** {action},
|
||||||
|
**So that** {benefit}.
|
||||||
|
|
||||||
|
**Acceptance Criteria**:
|
||||||
|
- [ ] Criterion 1
|
||||||
|
- [ ] Criterion 2
|
||||||
|
- [ ] Criterion 3
|
||||||
|
|
||||||
|
**Technical Notes**:
|
||||||
|
- Notes for developers
|
||||||
|
- API endpoints, data models, etc.
|
||||||
|
|
||||||
|
**Dependencies**:
|
||||||
|
- STORY-002 (must complete first)
|
||||||
|
- External dependency description
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### STORY-002: {Story Title}
|
||||||
|
|
||||||
|
... (same structure)
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
| Depends On | Type | Epic |
|
||||||
|
|------------|------|------|
|
||||||
|
| EPIC-XXX | Finish-to-Start | {epic title} |
|
||||||
|
| External API | External | {description} |
|
||||||
|
|
||||||
|
## Estimate
|
||||||
|
|
||||||
|
| Category | Estimate |
|
||||||
|
|----------|----------|
|
||||||
|
| Development | {X days} |
|
||||||
|
| Testing | {X days} |
|
||||||
|
| Documentation | {X days} |
|
||||||
|
| **Total** | **{X days}** |
|
||||||
|
|
||||||
|
## Definition of Done
|
||||||
|
|
||||||
|
- [ ] All stories completed and tested
|
||||||
|
- [ ] Code reviewed
|
||||||
|
- [ ] Documentation updated
|
||||||
|
- [ ] Acceptance criteria met
|
||||||
|
- [ ] No known bugs
|
||||||
|
|
||||||
|
## Risks
|
||||||
|
|
||||||
|
| Risk | Impact | Mitigation |
|
||||||
|
|------|--------|------------|
|
||||||
|
| Risk 1 | High/Low | 缓解措施 |
|
||||||
|
| Risk 2 | High/Low | 缓解措施 |
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用说明
|
||||||
|
|
||||||
|
1. **触发**: spec-generator Phase 5
|
||||||
|
2. **输入**: Phase 4 Architecture
|
||||||
|
3. **输出**: epics/ 目录,包含 _index.md 和所有 Epic 文件
|
||||||
|
4. **验证**: 确保追溯链接有效
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 示例
|
||||||
|
|
||||||
|
### 简化示例 - _index.md
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Epics - Index
|
||||||
|
|
||||||
|
> **Product**: Real-Time Collaboration Platform
|
||||||
|
> **Generated**: 2026-03-01
|
||||||
|
> **Session**: SPEC-rtc-platform-2026-03-01
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
本文档定义实时协作平台的 Epic 和 Story 分解。
|
||||||
|
|
||||||
|
## Epic Summary
|
||||||
|
|
||||||
|
| Epic | Title | Stories | Status | Priority |
|
||||||
|
|------|-------|---------|--------|----------|
|
||||||
|
| EPIC-001 | Core Sync Engine | 4 | Ready | P0 |
|
||||||
|
| EPIC-002 | User Interface | 3 | Ready | P1 |
|
||||||
|
| EPIC-003 | Conflict Resolution | 2 | Draft | P0 |
|
||||||
|
|
||||||
|
## Dependency Graph
|
||||||
|
|
||||||
|
```plaintext
|
||||||
|
EPIC-001 (Core Sync Engine)
|
||||||
|
│
|
||||||
|
├── EPIC-002 (User Interface)
|
||||||
|
│
|
||||||
|
└── EPIC-003 (Conflict Resolution)
|
||||||
|
```
|
||||||
|
|
||||||
|
## MVP Scope
|
||||||
|
|
||||||
|
以下 Epic 属于 MVP:
|
||||||
|
- [ ] EPIC-001: Core Sync Engine
|
||||||
|
- [ ] EPIC-002: User Interface
|
||||||
|
|
||||||
|
**Estimated MVP Duration**: 6 weeks
|
||||||
|
```
|
||||||
|
|
||||||
|
### 简化示例 - EPIC-001.md
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# EPIC-001: Core Sync Engine
|
||||||
|
|
||||||
|
> **Priority**: P0
|
||||||
|
> **Status**: Ready
|
||||||
|
> **MVP**: Yes
|
||||||
|
> **Created**: 2026-03-01
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
实现基于 OT 算法的实时文档同步引擎,支持多用户并发编辑。
|
||||||
|
|
||||||
|
## Business Value
|
||||||
|
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| 无缝协作 | 用户可同时编辑而无需担心冲突 |
|
||||||
|
| 数据安全 | 自动保存,防止数据丢失 |
|
||||||
|
|
||||||
|
## Related Requirements
|
||||||
|
|
||||||
|
| Requirement | Relation |
|
||||||
|
|-------------|----------|
|
||||||
|
| REQ-001 | Addresses |
|
||||||
|
| NFR-PERF-001 | Must satisfy |
|
||||||
|
|
||||||
|
## Stories
|
||||||
|
|
||||||
|
| Story | Title | Points | Status |
|
||||||
|
|-------|-------|--------|--------|
|
||||||
|
| STORY-001 | WebSocket Connection | 5 | Todo |
|
||||||
|
| STORY-002 | Operation Transformer | 8 | Todo |
|
||||||
|
| STORY-003 | State Persistence | 3 | Todo |
|
||||||
|
| STORY-004 | Conflict Resolution | 5 | Todo |
|
||||||
|
|
||||||
|
### STORY-001: WebSocket Connection
|
||||||
|
|
||||||
|
**As a** system,
|
||||||
|
**I want** to establish WebSocket connections,
|
||||||
|
**So that** clients can receive real-time updates.
|
||||||
|
|
||||||
|
**Acceptance Criteria**:
|
||||||
|
- [ ] Server accepts WebSocket connections
|
||||||
|
- [ ] Connection authentication implemented
|
||||||
|
- [ ] Reconnection logic handles network issues
|
||||||
|
|
||||||
|
**Technical Notes**:
|
||||||
|
- Use ws library for Node.js
|
||||||
|
- Implement JWT-based authentication
|
||||||
|
- Store active connections in memory
|
||||||
|
|
||||||
|
**Dependencies**:
|
||||||
|
- None
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### STORY-002: Operation Transformer
|
||||||
|
|
||||||
|
**As a** system,
|
||||||
|
**I want** to transform concurrent operations,
|
||||||
|
**So that** conflicts are automatically resolved.
|
||||||
|
|
||||||
|
**Acceptance Criteria**:
|
||||||
|
- [ ] OT transform function implemented
|
||||||
|
- [ ] Handles insert and delete operations
|
||||||
|
- [ ] Maintains document consistency
|
||||||
|
|
||||||
|
**Technical Notes**:
|
||||||
|
- Use Yjs library for OT implementation
|
||||||
|
- Implement transform() function per OT spec
|
||||||
|
|
||||||
|
**Dependencies**:
|
||||||
|
- STORY-001 must complete first
|
||||||
|
|
||||||
|
## Estimate
|
||||||
|
|
||||||
|
| Category | Estimate |
|
||||||
|
|----------|----------|
|
||||||
|
| Development | 10 days |
|
||||||
|
| Testing | 5 days |
|
||||||
|
| Documentation | 2 days |
|
||||||
|
| **Total** | **17 days** |
|
||||||
|
|
||||||
|
## Definition of Done
|
||||||
|
|
||||||
|
- [ ] All stories completed and tested
|
||||||
|
- [ ] Unit tests coverage > 80%
|
||||||
|
- [ ] API documentation complete
|
||||||
|
- [ ] Performance benchmarks meet NFR-PERF-001
|
||||||
|
```
|
||||||
295
docs/skills/templates/issue-template.md
Normal file
295
docs/skills/templates/issue-template.md
Normal file
@@ -0,0 +1,295 @@
|
|||||||
|
# Issue Template
|
||||||
|
|
||||||
|
> 用途: Issue 记录模板,用于代码审查和问题追踪
|
||||||
|
|
||||||
|
## 模板
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### [{Severity}] {Issue Title}
|
||||||
|
|
||||||
|
**Location**: `{file-path}:{line}`
|
||||||
|
|
||||||
|
**Category**: {Correctness|Readability|Performance|Security|Testing|Architecture}
|
||||||
|
**Dimension**: {Dimension Name}
|
||||||
|
|
||||||
|
#### Issue Description
|
||||||
|
|
||||||
|
{Detailed description of the issue, 1-3 sentences}
|
||||||
|
|
||||||
|
#### Current Code
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// {file-path}:{line}
|
||||||
|
{current code snippet}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Severity
|
||||||
|
|
||||||
|
{Critical|High|Medium|Low|Info} - {为什么是这个严重性的理由}
|
||||||
|
|
||||||
|
#### Recommendation
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Suggested fix
|
||||||
|
{fixed code snippet}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Explanation**: {解释为什么这样修复}
|
||||||
|
|
||||||
|
#### Impact
|
||||||
|
|
||||||
|
- **Breaks**: {什么功能会受影响}
|
||||||
|
- **Risk**: {风险等级}
|
||||||
|
- **Users Affected**: {受影响的用户范围}
|
||||||
|
|
||||||
|
#### Effort
|
||||||
|
|
||||||
|
- **Complexity**: {Low|Medium|High}
|
||||||
|
- **Estimated Time**: {X hours/days}
|
||||||
|
- **Files to Change**: {N files}
|
||||||
|
|
||||||
|
#### Related
|
||||||
|
|
||||||
|
- **Requirement**: {REQ-XXX} (if applicable)
|
||||||
|
- **ADR**: {ADR-XXX} (if applicable)
|
||||||
|
- **Similar Issues**: {link to similar issues}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Tags**: {tag1}, {tag2}, {tag3}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用说明
|
||||||
|
|
||||||
|
1. **触发**: 任何问题记录场景
|
||||||
|
2. **输入**: 问题发现时的上下文
|
||||||
|
3. **输出**: 结构化 issue 记录
|
||||||
|
4. **位置**: 可在审查报告、Issue 追踪系统等使用
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 变体
|
||||||
|
|
||||||
|
### 简化变体 (用于快速记录)
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### [{Severity}] {Title}
|
||||||
|
|
||||||
|
**Location**: `{file}:{line}`
|
||||||
|
**Category**: {category}
|
||||||
|
|
||||||
|
{Brief description}
|
||||||
|
|
||||||
|
**Fix**:
|
||||||
|
```typescript
|
||||||
|
// Before
|
||||||
|
{code}
|
||||||
|
|
||||||
|
// After
|
||||||
|
{fix}
|
||||||
|
```
|
||||||
|
```
|
||||||
|
|
||||||
|
### 安全 Issue 变体
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### [{Severity}] Security: {Title}
|
||||||
|
|
||||||
|
**Location**: `{file}:{line}`
|
||||||
|
**CVSS**: {score}
|
||||||
|
**CWE**: {CWE-ID}
|
||||||
|
|
||||||
|
**Vulnerability**: {漏洞描述}
|
||||||
|
|
||||||
|
**Exploit Scenario**: {攻击场景}
|
||||||
|
|
||||||
|
**Mitigation**:
|
||||||
|
```typescript
|
||||||
|
{修复代码}
|
||||||
|
```
|
||||||
|
|
||||||
|
**References**:
|
||||||
|
- {OWASP link}
|
||||||
|
- {CVE link}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 性能 Issue 变体
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### [{Severity}] Performance: {Title}
|
||||||
|
|
||||||
|
**Location**: `{file}:{line}`
|
||||||
|
**Complexity**: {O(n) / O(n²) / etc.}
|
||||||
|
|
||||||
|
**Current Performance**: {当前性能指标}
|
||||||
|
**Target Performance**: {目标性能指标}
|
||||||
|
|
||||||
|
**Bottleneck**: {瓶颈描述}
|
||||||
|
|
||||||
|
**Optimization**:
|
||||||
|
```typescript
|
||||||
|
{优化代码}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected Improvement**: {预期改进}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 示例
|
||||||
|
|
||||||
|
### 完整示例
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### [C] SQL Injection Vulnerability
|
||||||
|
|
||||||
|
**Location**: `src/auth/login.ts:45`
|
||||||
|
**Category**: Security
|
||||||
|
**Dimension**: Security
|
||||||
|
|
||||||
|
#### Issue Description
|
||||||
|
|
||||||
|
User input is directly concatenated into SQL query without sanitization,
|
||||||
|
allowing attackers to inject arbitrary SQL commands.
|
||||||
|
|
||||||
|
#### Current Code
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// src/auth/login.ts:45
|
||||||
|
const userId = req.params.id;
|
||||||
|
const query = `SELECT * FROM users WHERE id='${userId}'`;
|
||||||
|
const result = await db.query(query);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Severity
|
||||||
|
|
||||||
|
Critical - Allows unauthorized data access and potential data breach
|
||||||
|
|
||||||
|
#### Recommendation
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Use parameterized query
|
||||||
|
const userId = req.params.id;
|
||||||
|
const query = 'SELECT * FROM users WHERE id = ?';
|
||||||
|
const result = await db.query(query, [userId]);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Explanation**: Parameterized queries prevent SQL injection by separating
|
||||||
|
SQL logic from data. The database driver properly escapes the parameter.
|
||||||
|
|
||||||
|
#### Impact
|
||||||
|
|
||||||
|
- **Breaks**: User authentication, data integrity
|
||||||
|
- **Risk**: Data breach, unauthorized access
|
||||||
|
- **Users Affected**: All users
|
||||||
|
|
||||||
|
#### Effort
|
||||||
|
|
||||||
|
- **Complexity**: Low
|
||||||
|
- **Estimated Time**: 1 hour
|
||||||
|
- **Files to Change**: 3 files (all query locations)
|
||||||
|
|
||||||
|
#### Related
|
||||||
|
|
||||||
|
- **Requirement**: NFR-SEC-001
|
||||||
|
- **ADR**: ADR-002 (Security Standards)
|
||||||
|
- **Similar Issues**: None in this codebase
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Tags**: security, sql-injection, critical, authentication
|
||||||
|
```
|
||||||
|
|
||||||
|
### 简化示例
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### [M] Long Function
|
||||||
|
|
||||||
|
**Location**: `src/utils/data.ts:123`
|
||||||
|
**Category**: Readability
|
||||||
|
|
||||||
|
Function `processUserData` is 120 lines long, handles too many responsibilities.
|
||||||
|
|
||||||
|
**Fix**:
|
||||||
|
```typescript
|
||||||
|
// Before: One big function
|
||||||
|
function processUserData(user) {
|
||||||
|
// 120 lines...
|
||||||
|
}
|
||||||
|
|
||||||
|
// After: Split into smaller functions
|
||||||
|
function processUserData(user) {
|
||||||
|
validateUser(user);
|
||||||
|
enrichUserData(user);
|
||||||
|
saveUser(user);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
```
|
||||||
|
|
||||||
|
### 安全 Issue 示例
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### [C] Hardcoded API Key
|
||||||
|
|
||||||
|
**Location**: `src/config/api.ts:10`
|
||||||
|
**CVSS**: 7.5 (High)
|
||||||
|
**CWE**: 798
|
||||||
|
|
||||||
|
**Vulnerability**: API key is hardcoded in source code and will be exposed
|
||||||
|
in version control.
|
||||||
|
|
||||||
|
**Exploit Scenario**: Anyone with repository access can extract the API key
|
||||||
|
and make unauthorized API calls.
|
||||||
|
|
||||||
|
**Mitigation**:
|
||||||
|
```typescript
|
||||||
|
// Before
|
||||||
|
const API_KEY = 'sk-1234567890abcdef';
|
||||||
|
|
||||||
|
// After
|
||||||
|
const API_KEY = process.env.API_KEY || throw new Error('API_KEY required');
|
||||||
|
```
|
||||||
|
|
||||||
|
**References**:
|
||||||
|
- OWASP: https://owasp.org/www-community/vulnerabilities/Use_of_hard-coded_cryptographic_key
|
||||||
|
- CWE-798: https://cwe.mitre.org/data/definitions/798.html
|
||||||
|
```
|
||||||
|
|
||||||
|
### 性能 Issue 示例
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### [H] Nested Loop Performance
|
||||||
|
|
||||||
|
**Location**: `src/processing/analyzer.ts:67`
|
||||||
|
**Complexity**: O(n²)
|
||||||
|
|
||||||
|
**Current Performance**: Processing 10k items takes ~5 seconds
|
||||||
|
**Target Performance**: Should be < 1 second
|
||||||
|
|
||||||
|
**Bottleneck**: Nested loop comparing every item with every other item.
|
||||||
|
|
||||||
|
**Optimization**:
|
||||||
|
```typescript
|
||||||
|
// Before: O(n²)
|
||||||
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
for (let j = i + 1; j < items.length; j++) {
|
||||||
|
if (compare(items[i], items[j])) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// After: O(n) using Map
|
||||||
|
const map = new Map();
|
||||||
|
for (const item of items) {
|
||||||
|
const key = item.category;
|
||||||
|
if (!map.has(key)) {
|
||||||
|
map.set(key, []);
|
||||||
|
}
|
||||||
|
map.get(key).push(item);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected Improvement**: ~100x faster for large datasets
|
||||||
|
```
|
||||||
|
```
|
||||||
160
docs/skills/templates/product-brief.md
Normal file
160
docs/skills/templates/product-brief.md
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
# Product Brief Template
|
||||||
|
|
||||||
|
> 用途: 产品简介文档模板,用于 spec-generator Phase 2 输出
|
||||||
|
|
||||||
|
## 模板
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# {Product Name} - Product Brief
|
||||||
|
|
||||||
|
> **Generated**: {YYYY-MM-DD}
|
||||||
|
> **Source**: spec-generator Phase 2
|
||||||
|
> **Session**: {session-id}
|
||||||
|
|
||||||
|
## One-Liner
|
||||||
|
|
||||||
|
**简短描述** — 详细说明
|
||||||
|
|
||||||
|
## Problem Statement
|
||||||
|
|
||||||
|
### Pain Points
|
||||||
|
|
||||||
|
| Pain Point | Current State | Impact | Solution |
|
||||||
|
|------------|---------------|--------|----------|
|
||||||
|
| Pain Point 1 | 当前问题描述 | 影响程度 | 解决方案概述 |
|
||||||
|
| Pain Point 2 | 当前问题描述 | 影响程度 | 解决方案概述 |
|
||||||
|
| Pain Point 3 | 当前问题描述 | 影响程度 | 解决方案概述 |
|
||||||
|
|
||||||
|
## Target Audience
|
||||||
|
|
||||||
|
### Primary Users
|
||||||
|
|
||||||
|
| User Type | Description | Key Needs |
|
||||||
|
|-----------|-------------|-----------|
|
||||||
|
| 用户类型1 | 描述 | 核心需求 |
|
||||||
|
| 用户类型2 | 描述 | 核心需求 |
|
||||||
|
|
||||||
|
### Secondary Users
|
||||||
|
|
||||||
|
| User Type | Description | Key Needs |
|
||||||
|
|-----------|-------------|-----------|
|
||||||
|
| 用户类型 | 描述 | 核心需求 |
|
||||||
|
|
||||||
|
## Solution Overview
|
||||||
|
|
||||||
|
### Core Value Proposition
|
||||||
|
|
||||||
|
一句话描述核心价值主张
|
||||||
|
|
||||||
|
### Key Features
|
||||||
|
|
||||||
|
| Feature | Description | Priority |
|
||||||
|
|---------|-------------|----------|
|
||||||
|
| Feature 1 | 功能描述 | Must/Should/Could |
|
||||||
|
| Feature 2 | 功能描述 | Must/Should/Could |
|
||||||
|
| Feature 3 | 功能描述 | Must/Should/Could |
|
||||||
|
|
||||||
|
## MoSCoW Analysis
|
||||||
|
|
||||||
|
| Category | Features | Rationale |
|
||||||
|
|----------|----------|-----------|
|
||||||
|
| **Must Have** | Feature 1, Feature 2 | 核心功能,必须实现 |
|
||||||
|
| **Should Have** | Feature 3, Feature 4 | 重要功能,尽快实现 |
|
||||||
|
| **Could Have** | Feature 5 | 有价值的功能,时间允许时实现 |
|
||||||
|
| **Won't Have** | Feature 6 | 明确不实现的功能 |
|
||||||
|
|
||||||
|
## Success Metrics
|
||||||
|
|
||||||
|
| Metric | Target | Measurement Method |
|
||||||
|
|--------|--------|-------------------|
|
||||||
|
| 指标1 | 目标值 | 测量方法 |
|
||||||
|
| 指标2 | 目标值 | 测量方法 |
|
||||||
|
| 指标3 | 目标值 | 测量方法 |
|
||||||
|
|
||||||
|
## Feasibility Assessment
|
||||||
|
|
||||||
|
### Technical Feasibility
|
||||||
|
|
||||||
|
- **技术栈**: {技术栈选择}
|
||||||
|
- **复杂度**: {高/中/低}
|
||||||
|
- **风险**: {主要技术风险}
|
||||||
|
|
||||||
|
### Resource Requirements
|
||||||
|
|
||||||
|
| Resource | Estimate | Notes |
|
||||||
|
|----------|----------|-------|
|
||||||
|
| 开发时间 | {estimate} | |
|
||||||
|
| 测试时间 | {estimate} | |
|
||||||
|
| 其他资源 | {estimate} | |
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
1. [ ] Review and approve Product Brief
|
||||||
|
2. [ ] Proceed to Requirements (PRD) phase
|
||||||
|
3. [ ] Conduct technical feasibility study
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [Discovery Context](../discovery-context.json)
|
||||||
|
- [Session Config](../spec-config.json)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用说明
|
||||||
|
|
||||||
|
1. **触发**: spec-generator Phase 2
|
||||||
|
2. **输入**: Phase 1 Discovery 结果 + 多 CLI 分析结果
|
||||||
|
3. **输出**: product-brief.md
|
||||||
|
4. **验证**: 确保所有必需字段填充完整
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 示例
|
||||||
|
|
||||||
|
### 简化示例
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Real-Time Collaboration Platform - Product Brief
|
||||||
|
|
||||||
|
> **Generated**: 2026-03-01
|
||||||
|
> **Session**: SPEC-rtc-platform-2026-03-01
|
||||||
|
|
||||||
|
## One-Liner
|
||||||
|
|
||||||
|
**实时协作平台** — 让团队成员能够同时编辑文档并进行实时沟通
|
||||||
|
|
||||||
|
## Problem Statement
|
||||||
|
|
||||||
|
### Pain Points
|
||||||
|
|
||||||
|
| Pain Point | Current State | Impact | Solution |
|
||||||
|
|------------|---------------|--------|----------|
|
||||||
|
| 版本冲突 | 多人编辑同一文件时产生冲突 | 降低效率 | OT 算法自动合并 |
|
||||||
|
| 沟通割裂 | 编辑和沟通在不同工具间切换 | 上下文丢失 | 内嵌即时通讯 |
|
||||||
|
| 状态不明 | 不知道谁在编辑什么 | 协作混乱 | 实时光标显示 |
|
||||||
|
|
||||||
|
## Target Audience
|
||||||
|
|
||||||
|
### Primary Users
|
||||||
|
|
||||||
|
| User Type | Description | Key Needs |
|
||||||
|
|-----------|-------------|-----------|
|
||||||
|
| 内容团队 | 需要协作编辑文档的团队 | 实时同步、冲突解决 |
|
||||||
|
| 开发团队 | 需要协作编写代码的团队 | 代码合并、审查 |
|
||||||
|
|
||||||
|
## MoSCoW Analysis
|
||||||
|
|
||||||
|
| Category | Features | Rationale |
|
||||||
|
|----------|----------|-----------|
|
||||||
|
| **Must Have** | 实时编辑、冲突解决、用户在线状态 | 核心功能 |
|
||||||
|
| **Should Have** | 评论系统、版本历史、权限管理 | 重要功能 |
|
||||||
|
| **Could Have** | AI 辅助编辑、模板系统 | 增值功能 |
|
||||||
|
| **Won't Have** | 视频通话(使用现有工具) | 非核心 |
|
||||||
|
|
||||||
|
## Success Metrics
|
||||||
|
|
||||||
|
| Metric | Target | Measurement Method |
|
||||||
|
|--------|--------|-------------------|
|
||||||
|
| 冲突自动解决率 | >95% | 系统日志统计 |
|
||||||
|
| 实时延迟 | <200ms | 性能监控 |
|
||||||
|
| 用户活跃度 | 日活 >1000 | 分析平台 |
|
||||||
|
```
|
||||||
262
docs/skills/templates/requirements-prd.md
Normal file
262
docs/skills/templates/requirements-prd.md
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
# Requirements PRD Template
|
||||||
|
|
||||||
|
> 用途: 产品需求文档模板,用于 spec-generator Phase 3 输出
|
||||||
|
|
||||||
|
## 模板
|
||||||
|
|
||||||
|
### _index.md 模板
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Requirements (PRD) - Index
|
||||||
|
|
||||||
|
> **Product**: {Product Name}
|
||||||
|
> **Generated**: {YYYY-MM-DD}
|
||||||
|
> **Session**: {session-id}
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
产品需求概述(2-3 段)
|
||||||
|
|
||||||
|
## Requirements Summary
|
||||||
|
|
||||||
|
| Category | Count | Status |
|
||||||
|
|----------|-------|--------|
|
||||||
|
| Functional Requirements | {N} | |
|
||||||
|
| Non-Functional Requirements | {N} | |
|
||||||
|
|
||||||
|
## MoSCoW Summary
|
||||||
|
|
||||||
|
| Category | Count | Items |
|
||||||
|
|----------|-------|-------|
|
||||||
|
| Must | {N} | REQ-001, REQ-002, ... |
|
||||||
|
| Should | {N} | REQ-003, REQ-004, ... |
|
||||||
|
| Could | {N} | REQ-005, ... |
|
||||||
|
|
||||||
|
## Requirements Traceability
|
||||||
|
|
||||||
|
### From Product Brief
|
||||||
|
|
||||||
|
| Brief Feature | Requirement(s) |
|
||||||
|
|---------------|----------------|
|
||||||
|
| Feature 1 | REQ-001, REQ-002 |
|
||||||
|
| Feature 2 | REQ-003 |
|
||||||
|
|
||||||
|
### To Architecture
|
||||||
|
|
||||||
|
| Requirement | ADR |
|
||||||
|
|-------------|-----|
|
||||||
|
| REQ-001 | ADR-001 |
|
||||||
|
| REQ-002 | ADR-002 |
|
||||||
|
|
||||||
|
## Functional Requirements
|
||||||
|
|
||||||
|
详见各需求文档:
|
||||||
|
- [REQ-001-{title}](./REQ-001-{slug}.md)
|
||||||
|
- [REQ-002-{title}](./REQ-002-{slug}.md)
|
||||||
|
|
||||||
|
## Non-Functional Requirements
|
||||||
|
|
||||||
|
详见各 NFR 文档:
|
||||||
|
- [NFR-PERF-001](./NFR-PERF-001.md)
|
||||||
|
- [NFR-SEC-001](./NFR-SEC-001.md)
|
||||||
|
|
||||||
|
## Change Log
|
||||||
|
|
||||||
|
| Version | Date | Changes |
|
||||||
|
|---------|------|---------|
|
||||||
|
| 1.0.0 | YYYY-MM-DD | Initial version |
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### REQ-*.md 模板
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# REQ-{NNN}: {Requirement Title}
|
||||||
|
|
||||||
|
> **Type**: Functional Requirement
|
||||||
|
> **Priority**: {Must|Should|Could}
|
||||||
|
> **Status**: {Draft|Approved|Implemented}
|
||||||
|
> **Created**: {YYYY-MM-DD}
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
详细描述需求内容
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
**As a** {user type},
|
||||||
|
**I want** {action/feature},
|
||||||
|
**So that** {benefit/value}.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] Criterion 1
|
||||||
|
- [ ] Criterion 2
|
||||||
|
- [ ] Criterion 3
|
||||||
|
|
||||||
|
## Functional Specifications
|
||||||
|
|
||||||
|
### Input
|
||||||
|
|
||||||
|
| Input | Type | Description | Validation |
|
||||||
|
|-------|------|-------------|------------|
|
||||||
|
| input1 | type | 描述 | 验证规则 |
|
||||||
|
|
||||||
|
### Output
|
||||||
|
|
||||||
|
| Output | Type | Description |
|
||||||
|
|--------|------|-------------|
|
||||||
|
| output1 | type | 描述 |
|
||||||
|
|
||||||
|
### Business Rules
|
||||||
|
|
||||||
|
1. Rule 1
|
||||||
|
2. Rule 2
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
| Dependency | Type | Status |
|
||||||
|
|------------|------|--------|
|
||||||
|
| REQ-XXX | Functional | |
|
||||||
|
| NFR-XXX | Non-Functional | |
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [Product Brief](../product-brief.md) - Related Feature
|
||||||
|
- [ADR-XXX](../architecture/ADR-XXX.md) - Technical Decision
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### NFR-*.md 模板
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# NFR-{TYPE}-{NNN}: {Non-Functional Requirement Title}
|
||||||
|
|
||||||
|
> **Type**: {Performance|Security|Scalability|Reliability|Usability}
|
||||||
|
> **Priority**: {Must|Should|Could}
|
||||||
|
> **Status**: {Draft|Approved}
|
||||||
|
> **Created**: {YYYY-MM-DD}
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
详细描述非功能需求
|
||||||
|
|
||||||
|
## Metrics
|
||||||
|
|
||||||
|
| Metric | Target | Measurement Method |
|
||||||
|
|--------|--------|-------------------|
|
||||||
|
| metric1 | value | 测量方法 |
|
||||||
|
| metric2 | value | 测量方法 |
|
||||||
|
|
||||||
|
## Specifications
|
||||||
|
|
||||||
|
### {Type} Requirements
|
||||||
|
|
||||||
|
1. Requirement 1
|
||||||
|
2. Requirement 2
|
||||||
|
|
||||||
|
### Testing Strategy
|
||||||
|
|
||||||
|
- Test 1: Description
|
||||||
|
- Test 2: Description
|
||||||
|
|
||||||
|
## Related Requirements
|
||||||
|
|
||||||
|
| Requirement | Relation |
|
||||||
|
|-------------|----------|
|
||||||
|
| REQ-001 | Impacts |
|
||||||
|
| REQ-002 | Enables |
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用说明
|
||||||
|
|
||||||
|
1. **触发**: spec-generator Phase 3
|
||||||
|
2. **输入**: Phase 2 Product Brief
|
||||||
|
3. **输出**: requirements/ 目录,包含 _index.md 和所有需求文件
|
||||||
|
4. **验证**: 确保追溯链接有效
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 示例
|
||||||
|
|
||||||
|
### 简化示例 - _index.md
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Requirements (PRD) - Index
|
||||||
|
|
||||||
|
> **Product**: Real-Time Collaboration Platform
|
||||||
|
> **Generated**: 2026-03-01
|
||||||
|
> **Session**: SPEC-rtc-platform-2026-03-01
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
本文档包含实时协作平台的完整需求规格说明。
|
||||||
|
|
||||||
|
## Requirements Summary
|
||||||
|
|
||||||
|
| Category | Count | Status |
|
||||||
|
|----------|-------|--------|
|
||||||
|
| Functional Requirements | 8 | Draft |
|
||||||
|
| Non-Functional Requirements | 3 | Draft |
|
||||||
|
|
||||||
|
## MoSCoW Summary
|
||||||
|
|
||||||
|
| Category | Count | Items |
|
||||||
|
|----------|-------|-------|
|
||||||
|
| Must | 4 | REQ-001, REQ-002, REQ-003, REQ-004 |
|
||||||
|
| Should | 3 | REQ-005, REQ-006, REQ-007 |
|
||||||
|
| Could | 1 | REQ-008 |
|
||||||
|
```
|
||||||
|
|
||||||
|
### 简化示例 - REQ-001.md
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# REQ-001: Real-Time Document Sync
|
||||||
|
|
||||||
|
> **Type**: Functional Requirement
|
||||||
|
> **Priority**: Must
|
||||||
|
> **Status**: Draft
|
||||||
|
> **Created**: 2026-03-01
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
用户编辑文档时,更改应实时同步到所有协作者
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
**As a** content editor,
|
||||||
|
**I want** to see others' changes in real-time,
|
||||||
|
**So that** we can collaborate without conflicts.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] Changes sync within 200ms
|
||||||
|
- [ ] Multiple users can edit simultaneously
|
||||||
|
- [ ] Conflicts are auto-resolved using OT algorithm
|
||||||
|
- [ ] User cursors are visible to others
|
||||||
|
|
||||||
|
## Functional Specifications
|
||||||
|
|
||||||
|
### Input
|
||||||
|
|
||||||
|
| Input | Type | Description | Validation |
|
||||||
|
|-------|------|-------------|------------|
|
||||||
|
| document_id | string | Document identifier | Required, valid UUID |
|
||||||
|
| operations | array | OT operations | Required, non-empty |
|
||||||
|
|
||||||
|
### Output
|
||||||
|
|
||||||
|
| Output | Type | Description |
|
||||||
|
|--------|------|-------------|
|
||||||
|
| status | string | "synced" or "conflict" |
|
||||||
|
| merged_ops | array | Merged operations |
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
| Dependency | Type | Status |
|
||||||
|
|------------|------|--------|
|
||||||
|
| NFR-PERF-001 | Performance | |
|
||||||
|
| ADR-001 | OT Algorithm Choice | |
|
||||||
|
```
|
||||||
382
docs/skills/templates/review-report.md
Normal file
382
docs/skills/templates/review-report.md
Normal file
@@ -0,0 +1,382 @@
|
|||||||
|
# Review Report Template
|
||||||
|
|
||||||
|
> 用途: 代码审查报告模板,用于 review-code 输出
|
||||||
|
|
||||||
|
## 模板
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Code Review Report
|
||||||
|
|
||||||
|
> **Target**: {target-path}
|
||||||
|
> **Generated**: {YYYY-MM-DD HH:MM}
|
||||||
|
> **Reviewer**: {skill-name}
|
||||||
|
> **Session**: {session-id}
|
||||||
|
|
||||||
|
## Executive Summary
|
||||||
|
|
||||||
|
| Metric | Value |
|
||||||
|
|--------|-------|
|
||||||
|
| **Overall Score** | {X/100} |
|
||||||
|
| Files Reviewed | {N} |
|
||||||
|
| Total Issues | {N} |
|
||||||
|
| Critical | {N} |
|
||||||
|
| High | {N} |
|
||||||
|
| Medium | {N} |
|
||||||
|
| Low | {N} |
|
||||||
|
|
||||||
|
### Quality Grade
|
||||||
|
|
||||||
|
{A/B/C/D}
|
||||||
|
|
||||||
|
**Rationale**: 简要说明评分理由
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Dimensions Summary
|
||||||
|
|
||||||
|
| Dimension | Score | Issues | Top Issues |
|
||||||
|
|-----------|-------|--------|------------|
|
||||||
|
| Correctness | {X/10} | {N} | [C] Issue 1, [H] Issue 2 |
|
||||||
|
| Readability | {X/10} | {N} | [M] Issue 3 |
|
||||||
|
| Performance | {X/10} | {N} | [H] Issue 4 |
|
||||||
|
| Security | {X/10} | {N} | [C] Issue 5 |
|
||||||
|
| Testing | {X/10} | {N} | [L] Issue 6 |
|
||||||
|
| Architecture | {X/10} | {N} | [M] Issue 7 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Risk Areas Identified
|
||||||
|
|
||||||
|
| Area | Risk Level | Files | Issues |
|
||||||
|
|------|------------|-------|--------|
|
||||||
|
| {area1} | {High/Medium/Low} | {file list} | {N} issues |
|
||||||
|
| {area2} | {High/Medium/Low} | {file list} | {N} issues |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Detailed Findings
|
||||||
|
|
||||||
|
### Correctness: {X/10}
|
||||||
|
|
||||||
|
**Summary**: 简要总结正确性方面的发现
|
||||||
|
|
||||||
|
#### [C] {Issue Title}
|
||||||
|
|
||||||
|
**Location**: `{file-path}:{line}`
|
||||||
|
|
||||||
|
**Issue**: 问题描述(1-2 句话)
|
||||||
|
|
||||||
|
**Severity**: Critical - 必须修复
|
||||||
|
|
||||||
|
**Recommendation**:
|
||||||
|
```typescript
|
||||||
|
// Before (problematic)
|
||||||
|
const code = "problematic code";
|
||||||
|
|
||||||
|
// After (fixed)
|
||||||
|
const code = "fixed code";
|
||||||
|
```
|
||||||
|
|
||||||
|
**Reference**: [specs/review-dimensions.md](specs/review-dimensions.md) - Correctness section
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### [H] {Issue Title}
|
||||||
|
|
||||||
|
**Location**: `{file-path}:{line}`
|
||||||
|
|
||||||
|
**Issue**: 问题描述
|
||||||
|
|
||||||
|
**Severity**: High - 应该修复
|
||||||
|
|
||||||
|
**Recommendation**:
|
||||||
|
```typescript
|
||||||
|
// Fix suggestion
|
||||||
|
const fixedCode = "fixed code";
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Readability: {X/10}
|
||||||
|
|
||||||
|
**Summary**: 简要总结可读性方面的发现
|
||||||
|
|
||||||
|
#### [M] {Issue Title}
|
||||||
|
|
||||||
|
**Location**: `{file-path}:{line}`
|
||||||
|
|
||||||
|
**Issue**: 问题描述
|
||||||
|
|
||||||
|
**Severity**: Medium - 建议改进
|
||||||
|
|
||||||
|
**Recommendation**:
|
||||||
|
```typescript
|
||||||
|
// Suggestion
|
||||||
|
const betterCode = "more readable code";
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Performance: {X/10}
|
||||||
|
|
||||||
|
**Summary**: 简要总结性能方面的发现
|
||||||
|
|
||||||
|
#### [H] {Issue Title}
|
||||||
|
|
||||||
|
**Location**: `{file-path}:{line}`
|
||||||
|
|
||||||
|
**Issue**: 问题描述
|
||||||
|
|
||||||
|
**Severity**: High - 影响性能
|
||||||
|
|
||||||
|
**Recommendation**:
|
||||||
|
```typescript
|
||||||
|
// Optimization
|
||||||
|
const optimizedCode = "optimized code";
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Security: {X/10}
|
||||||
|
|
||||||
|
**Summary**: 简要总结安全方面的发现
|
||||||
|
|
||||||
|
#### [C] {Issue Title}
|
||||||
|
|
||||||
|
**Location**: `{file-path}:{line}`
|
||||||
|
|
||||||
|
**Issue**: 问题描述
|
||||||
|
|
||||||
|
**Severity**: Critical - 安全风险
|
||||||
|
|
||||||
|
**Recommendation**:
|
||||||
|
```typescript
|
||||||
|
// Security fix
|
||||||
|
const secureCode = "secure code";
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Testing: {X/10}
|
||||||
|
|
||||||
|
**Summary**: 简要总结测试方面的发现
|
||||||
|
|
||||||
|
#### [L] {Issue Title}
|
||||||
|
|
||||||
|
**Location**: `{file-path}:{line}`
|
||||||
|
|
||||||
|
**Issue**: 问题描述
|
||||||
|
|
||||||
|
**Severity**: Low - 建议添加测试
|
||||||
|
|
||||||
|
**Recommendation**:
|
||||||
|
```typescript
|
||||||
|
// Test example
|
||||||
|
describe('Function', () => {
|
||||||
|
it('should handle edge case', () => {
|
||||||
|
// test code
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Architecture: {X/10}
|
||||||
|
|
||||||
|
**Summary**: 简要总结架构方面的发现
|
||||||
|
|
||||||
|
#### [M] {Issue Title}
|
||||||
|
|
||||||
|
**Location**: `{file-path}:{line}`
|
||||||
|
|
||||||
|
**Issue**: 问题描述
|
||||||
|
|
||||||
|
**Severity**: Medium - 架构改进建议
|
||||||
|
|
||||||
|
**Recommendation**:
|
||||||
|
```typescript
|
||||||
|
// Architecture suggestion
|
||||||
|
// Consider using {pattern} instead
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Recommendations
|
||||||
|
|
||||||
|
### Priority Actions (Do First)
|
||||||
|
|
||||||
|
1. **[Critical] Fix security vulnerability in {file}:{line}**
|
||||||
|
- Action: 修复 SQL 注入风险
|
||||||
|
- Estimate: 1 hour
|
||||||
|
|
||||||
|
2. **[Critical] Handle null pointer in {file}:{line}**
|
||||||
|
- Action: 添加空检查
|
||||||
|
- Estimate: 30 minutes
|
||||||
|
|
||||||
|
### High Priority (Do Soon)
|
||||||
|
|
||||||
|
3. **[High] Optimize performance bottleneck in {file}:{line}**
|
||||||
|
- Action: 重构算法
|
||||||
|
- Estimate: 2 hours
|
||||||
|
|
||||||
|
### Medium Priority (Do When Possible)
|
||||||
|
|
||||||
|
4. **[Medium] Improve code readability in {file}:{line}**
|
||||||
|
- Action: 重构函数
|
||||||
|
- Estimate: 1 hour
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Appendix
|
||||||
|
|
||||||
|
### Files Reviewed
|
||||||
|
|
||||||
|
| File | Lines | Issues | Score |
|
||||||
|
|------|-------|--------|-------|
|
||||||
|
| {file1} | {N} | {N} | {X/10} |
|
||||||
|
| {file2} | {N} | {N} | {X/10} |
|
||||||
|
| {file3} | {N} | {N} | {X/10} |
|
||||||
|
|
||||||
|
### Issue Distribution
|
||||||
|
|
||||||
|
```
|
||||||
|
Critical: ████ 4
|
||||||
|
High: ████████ 8
|
||||||
|
Medium: ████████████ 12
|
||||||
|
Low: ██████ 6
|
||||||
|
```
|
||||||
|
|
||||||
|
### Review Metadata
|
||||||
|
|
||||||
|
| Key | Value |
|
||||||
|
|-----|-------|
|
||||||
|
| Review Duration | {X minutes} |
|
||||||
|
| Review Method | {Quick Scan + Deep Review} |
|
||||||
|
| Dimensions Covered | {All / Specific} |
|
||||||
|
| Review Configuration | {config details} |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
1. **Review this report**: 确认所有问题理解正确
|
||||||
|
2. **Fix Critical issues**: 优先修复高风险问题
|
||||||
|
3. **Run review-cycle**: 使用 `/review-cycle` 自动修复和验证
|
||||||
|
4. **Re-review**: 修复后重新审查确认
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Generated by**: {skill-name} v{version}
|
||||||
|
**Review Standards**: [specs/review-dimensions.md](specs/review-dimensions.md)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用说明
|
||||||
|
|
||||||
|
1. **触发**: review-code Phase 4
|
||||||
|
2. **输入**: Phase 3 的 findings 数据
|
||||||
|
3. **输出**: review-report.md
|
||||||
|
4. **格式**: Markdown,支持 GitHub/GitLab 渲染
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 示例
|
||||||
|
|
||||||
|
### 简化示例
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Code Review Report
|
||||||
|
|
||||||
|
> **Target**: src/auth/**
|
||||||
|
> **Generated**: 2026-03-01 10:30
|
||||||
|
> **Reviewer**: review-code
|
||||||
|
|
||||||
|
## Executive Summary
|
||||||
|
|
||||||
|
| Metric | Value |
|
||||||
|
|--------|-------|
|
||||||
|
| **Overall Score** | 65/100 |
|
||||||
|
| Files Reviewed | 5 |
|
||||||
|
| Total Issues | 15 |
|
||||||
|
| Critical | 2 |
|
||||||
|
| High | 4 |
|
||||||
|
| Medium | 6 |
|
||||||
|
| Low | 3 |
|
||||||
|
|
||||||
|
### Quality Grade
|
||||||
|
|
||||||
|
**C - Needs Improvement**
|
||||||
|
|
||||||
|
存在 2 个严重安全问题需要立即修复
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Dimensions Summary
|
||||||
|
|
||||||
|
| Dimension | Score | Issues |
|
||||||
|
|-----------|-------|--------|
|
||||||
|
| Correctness | 6/10 | 3 |
|
||||||
|
| Readability | 7/10 | 2 |
|
||||||
|
| Performance | 7/10 | 2 |
|
||||||
|
| Security | 4/10 | 4 |
|
||||||
|
| Testing | 5/10 | 2 |
|
||||||
|
| Architecture | 6/10 | 2 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Detailed Findings
|
||||||
|
|
||||||
|
### Security: 4/10
|
||||||
|
|
||||||
|
#### [C] SQL Injection Risk
|
||||||
|
|
||||||
|
**Location**: `src/auth/login.ts:45`
|
||||||
|
|
||||||
|
**Issue**: 用户输入直接拼接 SQL,可被注入攻击
|
||||||
|
|
||||||
|
**Severity**: Critical - 必须修复
|
||||||
|
|
||||||
|
**Recommendation**:
|
||||||
|
```typescript
|
||||||
|
// Before (vulnerable)
|
||||||
|
const query = `SELECT * FROM users WHERE id='${userId}'`;
|
||||||
|
|
||||||
|
// After (safe)
|
||||||
|
const query = 'SELECT * FROM users WHERE id = ?';
|
||||||
|
await db.query(query, [userId]);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Correctness: 6/10
|
||||||
|
|
||||||
|
#### [H] Null Pointer Risk
|
||||||
|
|
||||||
|
**Location**: `src/auth/user.ts:23`
|
||||||
|
|
||||||
|
**Issue**: user 对象可能为 null
|
||||||
|
|
||||||
|
**Severity**: High - 可能导致崩溃
|
||||||
|
|
||||||
|
**Recommendation**:
|
||||||
|
```typescript
|
||||||
|
// Add null check
|
||||||
|
if (user?.profile) {
|
||||||
|
return user.profile.name;
|
||||||
|
}
|
||||||
|
return 'Anonymous';
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Recommendations
|
||||||
|
|
||||||
|
### Priority Actions
|
||||||
|
|
||||||
|
1. **[Critical] Fix SQL injection in login.ts:45**
|
||||||
|
- Use parameterized queries
|
||||||
|
- Estimate: 1 hour
|
||||||
|
|
||||||
|
2. **[Critical] Add null check in user.ts:23**
|
||||||
|
- Add optional chaining
|
||||||
|
- Estimate: 15 minutes
|
||||||
|
```
|
||||||
218
docs/skills/templates/sequential-phase.md
Normal file
218
docs/skills/templates/sequential-phase.md
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
# Sequential Phase Template
|
||||||
|
|
||||||
|
> 用途: 顺序执行阶段的模板,用于 Sequential 类型 Skill
|
||||||
|
|
||||||
|
## 模板
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Phase {N}: {Phase Name}
|
||||||
|
|
||||||
|
> **Skill**: {Skill Name}
|
||||||
|
> **Phase Number**: {N}
|
||||||
|
> **Execution Mode**: Sequential
|
||||||
|
> **Estimated Duration**: {X minutes}
|
||||||
|
|
||||||
|
## Objective
|
||||||
|
|
||||||
|
本阶段的目标描述(1-2 句话)
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
在执行本阶段之前,必须满足以下条件:
|
||||||
|
|
||||||
|
- [ ] Prerequisite 1
|
||||||
|
- [ ] Prerequisite 2
|
||||||
|
- [ ] Prerequisite 3
|
||||||
|
|
||||||
|
## Input Data
|
||||||
|
|
||||||
|
| Data | Source | Format | Required |
|
||||||
|
|------|--------|--------|----------|
|
||||||
|
| data1 | {source} | {format} | Yes |
|
||||||
|
| data2 | {source} | {format} | Yes |
|
||||||
|
| data3 | {source} | {format} | No |
|
||||||
|
|
||||||
|
## Execution Steps
|
||||||
|
|
||||||
|
### Step 1: {Step Name}
|
||||||
|
|
||||||
|
**Description**: 详细描述步骤内容
|
||||||
|
|
||||||
|
**Action**: 具体要执行的操作
|
||||||
|
|
||||||
|
**Validation**: 如何验证步骤完成
|
||||||
|
|
||||||
|
**Output**: 产出什么数据或文件
|
||||||
|
|
||||||
|
### Step 2: {Step Name}
|
||||||
|
|
||||||
|
**Description**: 详细描述步骤内容
|
||||||
|
|
||||||
|
**Action**: 具体要执行的操作
|
||||||
|
|
||||||
|
**Validation**: 如何验证步骤完成
|
||||||
|
|
||||||
|
**Output**: 产出什么数据或文件
|
||||||
|
|
||||||
|
### Step 3: {Step Name}
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
## Output
|
||||||
|
|
||||||
|
本阶段将产生以下输出:
|
||||||
|
|
||||||
|
| Output | Format | Location | Description |
|
||||||
|
|--------|--------|----------|-------------|
|
||||||
|
| output1 | {format} | {path} | 描述 |
|
||||||
|
| output2 | {format} | {path} | 描述 |
|
||||||
|
|
||||||
|
## Quality Checks
|
||||||
|
|
||||||
|
执行完成后,检查以下项目:
|
||||||
|
|
||||||
|
- [ ] 所有步骤已执行
|
||||||
|
- [ ] 输出文件已创建
|
||||||
|
- [ ] 输出格式正确
|
||||||
|
- [ ] 数据完整性验证
|
||||||
|
- [ ] 无错误或警告
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
| Error | Cause | Resolution |
|
||||||
|
|-------|-------|------------|
|
||||||
|
| Error 1 | 原因描述 | 解决方案 |
|
||||||
|
| Error 2 | 原因描述 | 解决方案 |
|
||||||
|
|
||||||
|
## Next Phase
|
||||||
|
|
||||||
|
完成本阶段后,进入 **Phase {N+1}: {Next Phase Name}**
|
||||||
|
|
||||||
|
**传递数据**:
|
||||||
|
- data1 → Next phase input 1
|
||||||
|
- data2 → Next phase input 2
|
||||||
|
|
||||||
|
## Completion Criteria
|
||||||
|
|
||||||
|
本阶段被视为完成,当:
|
||||||
|
|
||||||
|
1. 所有执行步骤已完成
|
||||||
|
2. 所有质量检查已通过
|
||||||
|
3. 输出文件已生成并验证
|
||||||
|
4. 无未处理的错误
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用说明
|
||||||
|
|
||||||
|
1. **触发**: skill-generator Phase 3 (Sequential 模式)
|
||||||
|
2. **输入**: Phase 2 生成的目录结构
|
||||||
|
3. **输出**: phases/NN-{phase-name}.md
|
||||||
|
4. **命名**: 使用数字前缀排序 (01-, 02-, 03-)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 示例
|
||||||
|
|
||||||
|
### 简化示例
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Phase 1: Context Collection
|
||||||
|
|
||||||
|
> **Skill**: review-code
|
||||||
|
> **Phase Number**: 1
|
||||||
|
> **Execution Mode**: Sequential
|
||||||
|
> **Estimated Duration**: 5 minutes
|
||||||
|
|
||||||
|
## Objective
|
||||||
|
|
||||||
|
收集目标代码的上下文信息,识别技术栈和代码结构
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- [ ] Phase 0: 规范学习已完成
|
||||||
|
- [ ] 目标路径已提供
|
||||||
|
- [ ] specs/ 文档已阅读
|
||||||
|
|
||||||
|
## Input Data
|
||||||
|
|
||||||
|
| Data | Source | Format | Required |
|
||||||
|
|------|--------|--------|----------|
|
||||||
|
| target_path | User input | string | Yes |
|
||||||
|
| review_dimensions | specs/review-dimensions.md | list | Yes |
|
||||||
|
|
||||||
|
## Execution Steps
|
||||||
|
|
||||||
|
### Step 1: 扫描目标目录
|
||||||
|
|
||||||
|
**Description**: 使用 Glob 或 ACE 搜索工具扫描目标路径
|
||||||
|
|
||||||
|
**Action**:
|
||||||
|
```bash
|
||||||
|
# 扫描所有源文件
|
||||||
|
Glob(pattern="{target}/**/*.ts")
|
||||||
|
Glob(pattern="{target}/**/*.tsx")
|
||||||
|
```
|
||||||
|
|
||||||
|
**Validation**: 至少找到 1 个文件
|
||||||
|
|
||||||
|
**Output**: file_list.json
|
||||||
|
|
||||||
|
### Step 2: 识别技术栈
|
||||||
|
|
||||||
|
**Description**: 分析配置文件和依赖,识别技术栈
|
||||||
|
|
||||||
|
**Action**:
|
||||||
|
- 读取 package.json
|
||||||
|
- 读取 tsconfig.json
|
||||||
|
- 分析导入语句
|
||||||
|
|
||||||
|
**Validation**: 识别出主要语言和框架
|
||||||
|
|
||||||
|
**Output**: tech_stack.json
|
||||||
|
|
||||||
|
### Step 3: 构建代码模型
|
||||||
|
|
||||||
|
**Description**: 构建代码的依赖关系图
|
||||||
|
|
||||||
|
**Action**: 使用 ACE 工具分析代码关系
|
||||||
|
|
||||||
|
**Validation**: 依赖图构建完成
|
||||||
|
|
||||||
|
**Output**: code_model.json
|
||||||
|
|
||||||
|
## Output
|
||||||
|
|
||||||
|
| Output | Format | Location | Description |
|
||||||
|
|--------|--------|----------|-------------|
|
||||||
|
| file_list | JSON | state/context/files.json | 所有扫描到的文件 |
|
||||||
|
| tech_stack | JSON | state/context/tech-stack.json | 技术栈信息 |
|
||||||
|
| code_model | JSON | state/context/code-model.json | 代码关系图 |
|
||||||
|
|
||||||
|
## Quality Checks
|
||||||
|
|
||||||
|
- [ ] 文件列表非空
|
||||||
|
- [ ] 技术栈识别准确
|
||||||
|
- [ ] 代码模型无循环依赖警告
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
| Error | Cause | Resolution |
|
||||||
|
|-------|-------|------------|
|
||||||
|
| No files found | 路径错误或路径为空 | 提示用户检查路径 |
|
||||||
|
| Unknown tech | 无法识别技术栈 | 使用默认配置 |
|
||||||
|
|
||||||
|
## Next Phase
|
||||||
|
|
||||||
|
完成本阶段后,进入 **Phase 2: Quick Scan**
|
||||||
|
|
||||||
|
**传递数据**:
|
||||||
|
- file_list → Quick Scan 输入
|
||||||
|
- tech_stack → Quick Scan 输入
|
||||||
|
|
||||||
|
## Completion Criteria
|
||||||
|
|
||||||
|
1. 所有文件已扫描
|
||||||
|
2. 技术栈已识别
|
||||||
|
3. 代码模型已构建
|
||||||
|
4. state/context/ 目录已创建
|
||||||
|
```
|
||||||
274
docs/skills/templates/skill-md.md
Normal file
274
docs/skills/templates/skill-md.md
Normal file
@@ -0,0 +1,274 @@
|
|||||||
|
# SKILL.md Template
|
||||||
|
|
||||||
|
> 用途: Skill 入口文档模板 (SKILL.md),用于 skill-generator
|
||||||
|
|
||||||
|
## 模板
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# {Skill Name}
|
||||||
|
|
||||||
|
> **Type**: {Sequential|Autonomous}
|
||||||
|
> **Version**: {1.0.0}
|
||||||
|
> **Status**: {Stable|Experimental}
|
||||||
|
> **Category**: {Team|Workflow|Standalone|Specialized}
|
||||||
|
|
||||||
|
## One-Liner
|
||||||
|
|
||||||
|
**简短描述** — 详细说明
|
||||||
|
|
||||||
|
## Pain Points Solved
|
||||||
|
|
||||||
|
| Pain Point | Current State | {Skill Name} Solution |
|
||||||
|
|------------|---------------|----------------------|
|
||||||
|
| Pain Point 1 | 当前问题描述 | 解决方案描述 |
|
||||||
|
| Pain Point 2 | 当前问题描述 | 解决方案描述 |
|
||||||
|
| Pain Point 3 | 当前问题描述 | 解决方案描述 |
|
||||||
|
|
||||||
|
## Skills List / 功能列表
|
||||||
|
|
||||||
|
| Skill / Phase | Function | Trigger |
|
||||||
|
|---------------|----------|---------|
|
||||||
|
| {sub-skill-1} | 功能描述 | 触发命令 |
|
||||||
|
| {sub-skill-2} | 功能描述 | 触发命令 |
|
||||||
|
|
||||||
|
## Skills Details / 详细说明
|
||||||
|
|
||||||
|
### {Sub-Skill Name}
|
||||||
|
|
||||||
|
**One-Liner**: 一句话描述
|
||||||
|
|
||||||
|
**Trigger**:
|
||||||
|
```shell
|
||||||
|
/trigger-command <args>
|
||||||
|
/trigger-command --flag <value>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Features**:
|
||||||
|
- Feature 1
|
||||||
|
- Feature 2
|
||||||
|
- Feature 3
|
||||||
|
|
||||||
|
**Architecture Overview**:
|
||||||
|
```plaintext
|
||||||
|
┌─────────────────────────────────────────────────┐
|
||||||
|
│ {Skill Name} Architecture │
|
||||||
|
│ │
|
||||||
|
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||||||
|
│ │ Phase 1 │ -> │ Phase 2 │ -> │ Phase 3 │ │
|
||||||
|
│ │ {Desc} │ │ {Desc} │ │ {Desc} │ │
|
||||||
|
│ └──────────┘ └──────────┘ └──────────┘ │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**⚠️ Mandatory Prerequisites**:
|
||||||
|
|
||||||
|
> **Do not skip**: Before executing any operations, you **must** completely read the following documents.
|
||||||
|
|
||||||
|
**Specification Documents** (required):
|
||||||
|
| Document | Purpose | Priority |
|
||||||
|
|----------|---------|----------|
|
||||||
|
| [specs/{doc-name}.md](specs/{doc-name}.md) | Description | **P0 - Highest** |
|
||||||
|
|
||||||
|
**Template Files** (read before generation):
|
||||||
|
| Document | Purpose |
|
||||||
|
|----------|---------|
|
||||||
|
| [templates/{template}.md](templates/{template}.md) | Template description |
|
||||||
|
|
||||||
|
**Execution Flow**:
|
||||||
|
```plaintext
|
||||||
|
Phase 1: {Phase Name}
|
||||||
|
-> Action 1: Description
|
||||||
|
-> Action 2: Description
|
||||||
|
-> Output: {output description}
|
||||||
|
|
||||||
|
Phase 2: {Phase Name}
|
||||||
|
-> Action 1: Description
|
||||||
|
-> Action 2: Description
|
||||||
|
-> Output: {output description}
|
||||||
|
|
||||||
|
Phase 3: {Phase Name}
|
||||||
|
-> Action 1: Description
|
||||||
|
-> Output: {output description}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Output Structure**:
|
||||||
|
```plaintext
|
||||||
|
{output-directory}/
|
||||||
|
├── file1.md # Description
|
||||||
|
├── file2.md # Description
|
||||||
|
└── subdirectory/ # Description
|
||||||
|
├── file3.md
|
||||||
|
└── file4.md
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### {Another Sub-Skill}
|
||||||
|
|
||||||
|
**One-Liner**: 一句话描述
|
||||||
|
|
||||||
|
**Trigger**:
|
||||||
|
```shell
|
||||||
|
/trigger-command <args>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Features**:
|
||||||
|
- Feature 1
|
||||||
|
- Feature 2
|
||||||
|
|
||||||
|
**Use Cases**:
|
||||||
|
- Use case 1
|
||||||
|
- Use case 2
|
||||||
|
|
||||||
|
## Related Commands
|
||||||
|
|
||||||
|
- [Related Command 1](../commands/claude/{command}.md)
|
||||||
|
- [Related Command 2](../commands/claude/{command}.md)
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
1. **Practice 1**: Description
|
||||||
|
2. **Practice 2**: Description
|
||||||
|
3. **Practice 3**: Description
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Example 1: Description
|
||||||
|
/trigger-command <args>
|
||||||
|
|
||||||
|
# Example 2: Description
|
||||||
|
/trigger-command --flag <args>
|
||||||
|
|
||||||
|
# Example 3: Description
|
||||||
|
/trigger-command sub-command <args>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Output Example
|
||||||
|
|
||||||
|
```
|
||||||
|
### Example Output Title
|
||||||
|
|
||||||
|
**Location**: `file/path:line`
|
||||||
|
|
||||||
|
**Issue**: Description of the finding
|
||||||
|
|
||||||
|
**Severity**: {Critical|High|Medium|Low}
|
||||||
|
|
||||||
|
**Recommendation**:
|
||||||
|
```typescript
|
||||||
|
// Before (problematic)
|
||||||
|
const code = "problematic code";
|
||||||
|
|
||||||
|
// After (fixed)
|
||||||
|
const code = "fixed code";
|
||||||
|
```
|
||||||
|
|
||||||
|
**Reference**: [specs/reference.md](specs/reference.md) - Section name
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
| Issue | Solution |
|
||||||
|
|-------|----------|
|
||||||
|
| Problem 1 | Solution 1 |
|
||||||
|
| Problem 2 | Solution 2 |
|
||||||
|
| Problem 3 | Solution 3 |
|
||||||
|
|
||||||
|
## Version History
|
||||||
|
|
||||||
|
| Version | Date | Changes |
|
||||||
|
|---------|------|---------|
|
||||||
|
| 1.0.0 | YYYY-MM-DD | Initial release |
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用说明
|
||||||
|
|
||||||
|
1. **触发**: skill-generator Phase 2
|
||||||
|
2. **输入**: Phase 1 skill-config.json
|
||||||
|
3. **输出**: SKILL.md
|
||||||
|
4. **验证**: 确保所有链接有效,所有章节完整
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 示例
|
||||||
|
|
||||||
|
### 简化示例
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Review Code
|
||||||
|
|
||||||
|
> **Type**: Autonomous
|
||||||
|
> **Version**: 1.0.0
|
||||||
|
> **Status**: Stable
|
||||||
|
> **Category**: Specialized
|
||||||
|
|
||||||
|
## One-Liner
|
||||||
|
|
||||||
|
**多维度代码审查** — 通过 6 个维度自动分析代码质量并提供修复建议
|
||||||
|
|
||||||
|
## Pain Points Solved
|
||||||
|
|
||||||
|
| Pain Point | Current State | Review Code Solution |
|
||||||
|
|------------|---------------|----------------------|
|
||||||
|
| 审查维度不全 | 手动审查容易遗漏 | 6 维度自动审查 |
|
||||||
|
| 问题分类混乱 | 难以区分严重性 | 结构化问题分类 |
|
||||||
|
| 修复建议模糊 | 缺乏具体方案 | 可执行的修复建议 |
|
||||||
|
|
||||||
|
## Skills List
|
||||||
|
|
||||||
|
| Skill | Function | Trigger |
|
||||||
|
|-------|----------|---------|
|
||||||
|
| review-code | 6 维度代码审查 | `/review-code <target>` |
|
||||||
|
| review-cycle | 审查和修复循环 | `/review-cycle <target>` |
|
||||||
|
|
||||||
|
## Skills Details
|
||||||
|
|
||||||
|
### review-code
|
||||||
|
|
||||||
|
**One-Liner**: 6 维度代码审查
|
||||||
|
|
||||||
|
**Trigger**:
|
||||||
|
```shell
|
||||||
|
/review-code src/**
|
||||||
|
/review-code --dimensions=sec,perf src/
|
||||||
|
```
|
||||||
|
|
||||||
|
**Features**:
|
||||||
|
- 6 维度审查:正确性、可读性、性能、安全、测试、架构
|
||||||
|
- 快速扫描识别高风险区域
|
||||||
|
- 深度审查聚焦关键问题
|
||||||
|
- 结构化报告分级输出
|
||||||
|
|
||||||
|
**Execution Flow**:
|
||||||
|
```plaintext
|
||||||
|
Phase 0: Specification Study (Mandatory)
|
||||||
|
-> Read specs/review-dimensions.md
|
||||||
|
-> Read specs/issue-classification.md
|
||||||
|
|
||||||
|
Phase 1: Collect Context
|
||||||
|
-> Scan target files
|
||||||
|
-> Identify tech stack
|
||||||
|
-> Output: state.context
|
||||||
|
|
||||||
|
Phase 2: Quick Scan
|
||||||
|
-> Identify high-risk areas
|
||||||
|
-> Output: state.risk_areas
|
||||||
|
|
||||||
|
Phase 3: Deep Review (per dimension)
|
||||||
|
-> Analyze each dimension
|
||||||
|
-> Record findings
|
||||||
|
-> Output: state.findings[]
|
||||||
|
|
||||||
|
Phase 4: Generate Report
|
||||||
|
-> Aggregate findings
|
||||||
|
-> Generate review-report.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
1. **完整阅读规范**: 执行前必须阅读 specs/ 文档
|
||||||
|
2. **快速扫描先行**: 先识别高风险,再深度审查
|
||||||
|
3. **结构化报告**: 使用生成的报告作为修复指南
|
||||||
|
```
|
||||||
@@ -21,360 +21,8 @@
|
|||||||
|
|
||||||
## 实时演示:组件库展示
|
## 实时演示:组件库展示
|
||||||
|
|
||||||
:::demo ComponentGallery
|
|
||||||
# component-gallery.tsx
|
|
||||||
/**
|
|
||||||
* 组件库展示演示
|
|
||||||
* 所有 UI 组件的交互式展示
|
|
||||||
*/
|
|
||||||
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 = [
|
:::demo ComponentGallery #ComponentGalleryZh.tsx :::
|
||||||
{ id: 'all', label: '全部组件' },
|
|
||||||
{ id: 'buttons', label: '按钮' },
|
|
||||||
{ id: 'forms', label: '表单' },
|
|
||||||
{ id: 'feedback', label: '反馈' },
|
|
||||||
{ id: 'navigation', label: '导航' },
|
|
||||||
{ id: 'overlays', label: '叠加层' },
|
|
||||||
]
|
|
||||||
|
|
||||||
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 (
|
|
||||||
<div className="p-6 bg-background space-y-8">
|
|
||||||
{/* 标题 */}
|
|
||||||
<div>
|
|
||||||
<h1 className="text-2xl font-bold">UI 组件库</h1>
|
|
||||||
<p className="text-muted-foreground">所有可用 UI 组件的交互式展示</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 分类过滤 */}
|
|
||||||
<div className="flex flex-wrap gap-2">
|
|
||||||
{categories.map((cat) => (
|
|
||||||
<button
|
|
||||||
key={cat.id}
|
|
||||||
onClick={() => setSelectedCategory(cat.id)}
|
|
||||||
className={`px-4 py-2 rounded-md text-sm transition-colors ${
|
|
||||||
selectedCategory === cat.id
|
|
||||||
? 'bg-primary text-primary-foreground'
|
|
||||||
: 'border hover:bg-accent'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{cat.label}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 按钮部分 */}
|
|
||||||
{(selectedCategory === 'all' || selectedCategory === 'buttons') && (
|
|
||||||
<section className="space-y-4">
|
|
||||||
<h2 className="text-lg font-semibold">按钮</h2>
|
|
||||||
<div className="space-y-6">
|
|
||||||
{/* 变体选择器 */}
|
|
||||||
<div className="space-y-3">
|
|
||||||
<label className="text-sm font-medium">变体</label>
|
|
||||||
<div className="flex flex-wrap gap-2">
|
|
||||||
{buttonVariants.map((variant) => (
|
|
||||||
<button
|
|
||||||
key={variant}
|
|
||||||
onClick={() => setButtonVariant(variant)}
|
|
||||||
className={`px-4 py-2 rounded-md text-sm capitalize transition-colors ${
|
|
||||||
buttonVariant === variant
|
|
||||||
? 'bg-primary text-primary-foreground ring-2 ring-ring'
|
|
||||||
: 'border hover:bg-accent'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{variant}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 按钮尺寸 */}
|
|
||||||
<div className="space-y-3">
|
|
||||||
<label className="text-sm font-medium">尺寸</label>
|
|
||||||
<div className="flex items-center gap-3 flex-wrap">
|
|
||||||
<button className={`h-8 rounded-md px-3 text-sm ${buttonVariant === 'default' ? 'bg-primary text-primary-foreground' : 'border'}`}>
|
|
||||||
小
|
|
||||||
</button>
|
|
||||||
<button className={`h-10 px-4 py-2 rounded-md text-sm ${buttonVariant === 'default' ? 'bg-primary text-primary-foreground' : 'border'}`}>
|
|
||||||
默认
|
|
||||||
</button>
|
|
||||||
<button className={`h-11 rounded-md px-8 text-sm ${buttonVariant === 'default' ? 'bg-primary text-primary-foreground' : 'border'}`}>
|
|
||||||
大
|
|
||||||
</button>
|
|
||||||
<button className={`h-10 w-10 rounded-md flex items-center justify-center ${buttonVariant === 'default' ? 'bg-primary text-primary-foreground' : 'border'}`}>
|
|
||||||
⚙
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 所有按钮变体 */}
|
|
||||||
<div className="space-y-3">
|
|
||||||
<label className="text-sm font-medium">所有变体</label>
|
|
||||||
<div className="flex flex-wrap gap-3 p-4 border rounded-lg bg-muted/20">
|
|
||||||
<button className="px-4 py-2 rounded-md text-sm bg-primary text-primary-foreground hover:opacity-90">默认</button>
|
|
||||||
<button className="px-4 py-2 rounded-md text-sm bg-destructive text-destructive-foreground hover:opacity-90">危险</button>
|
|
||||||
<button className="px-4 py-2 rounded-md text-sm border bg-background hover:bg-accent">轮廓</button>
|
|
||||||
<button className="px-4 py-2 rounded-md text-sm bg-secondary text-secondary-foreground hover:opacity-80">次要</button>
|
|
||||||
<button className="px-4 py-2 rounded-md text-sm hover:bg-accent">幽灵</button>
|
|
||||||
<button className="px-4 py-2 rounded-md text-sm text-primary underline-offset-4 hover:underline">链接</button>
|
|
||||||
<button className="px-4 py-2 rounded-md text-sm bg-gradient-to-r from-blue-500 to-purple-500 text-white hover:opacity-90">渐变</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* 表单部分 */}
|
|
||||||
{(selectedCategory === 'all' || selectedCategory === 'forms') && (
|
|
||||||
<section className="space-y-4">
|
|
||||||
<h2 className="text-lg font-semibold">表单组件</h2>
|
|
||||||
<div className="grid md:grid-cols-2 gap-6">
|
|
||||||
{/* 输入框 */}
|
|
||||||
<div className="space-y-3">
|
|
||||||
<label className="text-sm font-medium">输入框</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
placeholder="输入文本..."
|
|
||||||
className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
placeholder="错误状态"
|
|
||||||
className="flex h-10 w-full rounded-md border border-destructive bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-destructive"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 文本区域 */}
|
|
||||||
<div className="space-y-3">
|
|
||||||
<label className="text-sm font-medium">文本区域</label>
|
|
||||||
<textarea
|
|
||||||
placeholder="输入多行文本..."
|
|
||||||
className="flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 复选框 */}
|
|
||||||
<div className="space-y-3">
|
|
||||||
<label className="text-sm font-medium">复选框</label>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<label className="flex items-center gap-2 text-sm cursor-pointer">
|
|
||||||
<input type="checkbox" className="h-4 w-4 rounded border border-primary" checked={checkboxState} onChange={(e) => setCheckboxState(e.target.checked)} />
|
|
||||||
<span>接受条款和条件</span>
|
|
||||||
</label>
|
|
||||||
<label className="flex items-center gap-2 text-sm cursor-pointer opacity-50">
|
|
||||||
<input type="checkbox" className="h-4 w-4 rounded border border-primary" />
|
|
||||||
<span>订阅新闻通讯</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 开关 */}
|
|
||||||
<div className="space-y-3">
|
|
||||||
<label className="text-sm font-medium">开关</label>
|
|
||||||
<div className="space-y-3">
|
|
||||||
<label className="flex items-center gap-3 cursor-pointer">
|
|
||||||
<div className="relative">
|
|
||||||
<input type="checkbox" className="sr-only peer" checked={switchState} onChange={(e) => setSwitchState(e.target.checked)} />
|
|
||||||
<div className="w-9 h-5 bg-input rounded-full peer peer-focus:ring-2 peer-focus:ring-ring peer-checked:bg-primary after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-background after:rounded-full after:h-4 after:w-4 after:transition-all peer-checked:after:translate-x-full" />
|
|
||||||
</div>
|
|
||||||
<span className="text-sm">启用通知 {switchState ? '(开)' : '(关)'}</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 下拉选择 */}
|
|
||||||
<div className="space-y-3">
|
|
||||||
<label className="text-sm font-medium">下拉选择</label>
|
|
||||||
<select className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring">
|
|
||||||
<option value="">选择一个选项</option>
|
|
||||||
<option value="1">选项 1</option>
|
|
||||||
<option value="2">选项 2</option>
|
|
||||||
<option value="3">选项 3</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 表单操作 */}
|
|
||||||
<div className="space-y-3">
|
|
||||||
<label className="text-sm font-medium">表单操作</label>
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<button className="px-4 py-2 rounded-md text-sm bg-primary text-primary-foreground hover:opacity-90">提交</button>
|
|
||||||
<button className="px-4 py-2 rounded-md text-sm border hover:bg-accent">取消</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* 反馈部分 */}
|
|
||||||
{(selectedCategory === 'all' || selectedCategory === 'feedback') && (
|
|
||||||
<section className="space-y-4">
|
|
||||||
<h2 className="text-lg font-semibold">反馈组件</h2>
|
|
||||||
|
|
||||||
{/* 徽章 */}
|
|
||||||
<div className="space-y-3">
|
|
||||||
<label className="text-sm font-medium">徽章</label>
|
|
||||||
<div className="flex flex-wrap gap-2">
|
|
||||||
<span className="inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold bg-primary text-primary-foreground">默认</span>
|
|
||||||
<span className="inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold bg-secondary text-secondary-foreground">次要</span>
|
|
||||||
<span className="inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold bg-destructive text-destructive-foreground">危险</span>
|
|
||||||
<span className="inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold bg-success text-white">成功</span>
|
|
||||||
<span className="inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold bg-warning text-white">警告</span>
|
|
||||||
<span className="inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold bg-info text-white">信息</span>
|
|
||||||
<span className="inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold text-foreground">轮廓</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 进度条 */}
|
|
||||||
<div className="space-y-3">
|
|
||||||
<label className="text-sm font-medium">进度条</label>
|
|
||||||
<div className="space-y-3 max-w-md">
|
|
||||||
<div>
|
|
||||||
<div className="flex justify-between text-xs mb-1">
|
|
||||||
<span>处理中...</span>
|
|
||||||
<span>65%</span>
|
|
||||||
</div>
|
|
||||||
<div className="h-2 bg-muted rounded-full overflow-hidden">
|
|
||||||
<div className="h-full bg-primary rounded-full transition-all" style={{ width: '65%' }}/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="flex justify-between text-xs mb-1">
|
|
||||||
<span>上传中...</span>
|
|
||||||
<span>30%</span>
|
|
||||||
</div>
|
|
||||||
<div className="h-2 bg-muted rounded-full overflow-hidden">
|
|
||||||
<div className="h-full bg-blue-500 rounded-full transition-all" style={{ width: '30%' }}/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 警告 */}
|
|
||||||
<div className="space-y-3">
|
|
||||||
<label className="text-sm font-medium">警告提示</label>
|
|
||||||
<div className="space-y-3">
|
|
||||||
<div className="flex items-start gap-3 p-4 border rounded-lg bg-destructive/10 border-destructive/20 text-destructive">
|
|
||||||
<span className="text-lg">⚠</span>
|
|
||||||
<div className="flex-1">
|
|
||||||
<div className="font-medium text-sm">发生错误</div>
|
|
||||||
<div className="text-xs mt-1 opacity-80">出现了一些问题,请重试。</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-start gap-3 p-4 border rounded-lg bg-success/10 border-success/20 text-success">
|
|
||||||
<span className="text-lg">✓</span>
|
|
||||||
<div className="flex-1">
|
|
||||||
<div className="font-medium text-sm">成功!</div>
|
|
||||||
<div className="text-xs mt-1 opacity-80">您的更改已保存。</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* 导航部分 */}
|
|
||||||
{(selectedCategory === 'all' || selectedCategory === 'navigation') && (
|
|
||||||
<section className="space-y-4">
|
|
||||||
<h2 className="text-lg font-semibold">导航组件</h2>
|
|
||||||
|
|
||||||
{/* 标签页 */}
|
|
||||||
<div className="space-y-3">
|
|
||||||
<label className="text-sm font-medium">标签页</label>
|
|
||||||
<div className="border-b">
|
|
||||||
<div className="flex gap-4">
|
|
||||||
{['概览', '文档', 'API 参考', '示例'].map((tab) => (
|
|
||||||
<button
|
|
||||||
key={tab}
|
|
||||||
onClick={() => setSelectedTab(tab.toLowerCase().replace(' ', '-'))}
|
|
||||||
className={`pb-3 px-1 text-sm border-b-2 transition-colors ${
|
|
||||||
selectedTab === tab.toLowerCase().replace(' ', '-')
|
|
||||||
? 'border-primary text-primary'
|
|
||||||
: 'border-transparent text-muted-foreground hover:text-foreground'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{tab}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 面包屑 */}
|
|
||||||
<div className="space-y-3">
|
|
||||||
<label className="text-sm font-medium">面包屑</label>
|
|
||||||
<nav className="flex items-center gap-2 text-sm text-muted-foreground">
|
|
||||||
<a href="#" className="hover:text-foreground">首页</a>
|
|
||||||
<span>/</span>
|
|
||||||
<a href="#" className="hover:text-foreground">组件</a>
|
|
||||||
<span>/</span>
|
|
||||||
<span className="text-foreground">库</span>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* 叠加层部分 */}
|
|
||||||
{(selectedCategory === 'all' || selectedCategory === 'overlays') && (
|
|
||||||
<section className="space-y-4">
|
|
||||||
<h2 className="text-lg font-semibold">叠加层组件</h2>
|
|
||||||
|
|
||||||
<div className="grid md:grid-cols-3 gap-4 text-sm">
|
|
||||||
<div className="p-4 border rounded-lg">
|
|
||||||
<h3 className="font-medium mb-2">对话框</h3>
|
|
||||||
<p className="text-muted-foreground text-xs">用于焦点用户交互的模态对话框。</p>
|
|
||||||
<button className="mt-3 px-3 py-1.5 text-xs bg-primary text-primary-foreground rounded">打开对话框</button>
|
|
||||||
</div>
|
|
||||||
<div className="p-4 border rounded-lg">
|
|
||||||
<h3 className="font-medium mb-2">抽屉</h3>
|
|
||||||
<p className="text-muted-foreground text-xs">从屏幕边缘滑入的侧边面板。</p>
|
|
||||||
<button className="mt-3 px-3 py-1.5 text-xs border rounded hover:bg-accent">打开抽屉</button>
|
|
||||||
</div>
|
|
||||||
<div className="p-4 border rounded-lg">
|
|
||||||
<h3 className="font-medium mb-2">下拉菜单</h3>
|
|
||||||
<p className="text-muted-foreground text-xs">上下文菜单和操作列表。</p>
|
|
||||||
<button className="mt-3 px-3 py-1.5 text-xs border rounded hover:bg-accent">▼ 打开菜单</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
:::
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -55,144 +55,7 @@
|
|||||||
|
|
||||||
## 实时演示
|
## 实时演示
|
||||||
|
|
||||||
:::demo DashboardOverview
|
:::demo DashboardOverview #DashboardOverview.tsx :::
|
||||||
# dashboard-overview.tsx
|
|
||||||
/**
|
|
||||||
* 仪表板概览演示
|
|
||||||
* 显示带有小部件的主仪表板布局
|
|
||||||
*/
|
|
||||||
export function DashboardOverview() {
|
|
||||||
return (
|
|
||||||
<div className="space-y-6 p-6 bg-background min-h-[600px]">
|
|
||||||
{/* 头部 */}
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div>
|
|
||||||
<h1 className="text-2xl font-bold">仪表板</h1>
|
|
||||||
<p className="text-sm text-muted-foreground">
|
|
||||||
项目概览和活动监控
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<button className="px-3 py-1.5 text-sm border rounded-md hover:bg-accent">
|
|
||||||
刷新
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 工作流统计小部件 */}
|
|
||||||
<div className="border rounded-lg overflow-hidden">
|
|
||||||
<div className="p-4 border-b bg-muted/30">
|
|
||||||
<h2 className="font-semibold">项目概览与统计</h2>
|
|
||||||
</div>
|
|
||||||
<div className="p-4">
|
|
||||||
<div className="grid grid-cols-3 gap-4">
|
|
||||||
<div className="space-y-3">
|
|
||||||
<div className="text-xs font-medium text-muted-foreground">统计数据</div>
|
|
||||||
<div className="grid grid-cols-2 gap-2">
|
|
||||||
{[
|
|
||||||
{ label: '活动会话', value: '12', color: 'text-blue-500' },
|
|
||||||
{ label: '总任务', value: '48', color: 'text-green-500' },
|
|
||||||
{ label: '已完成', value: '35', color: 'text-emerald-500' },
|
|
||||||
{ label: '待处理', value: '8', color: 'text-amber-500' },
|
|
||||||
].map((stat, i) => (
|
|
||||||
<div key={i} className="p-2 bg-muted/50 rounded">
|
|
||||||
<div className={`text-lg font-bold ${stat.color}`}>{stat.value}</div>
|
|
||||||
<div className="text-xs text-muted-foreground truncate">{stat.label}</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-3">
|
|
||||||
<div className="text-xs font-medium text-muted-foreground">工作流状态</div>
|
|
||||||
<div className="flex items-center justify-center h-24">
|
|
||||||
<div className="relative w-20 h-20">
|
|
||||||
<svg className="w-full h-full -rotate-90" viewBox="0 0 36 36">
|
|
||||||
<circle cx="18" cy="18" r="15" fill="none" stroke="currentColor" strokeWidth="3" className="text-muted opacity-20"/>
|
|
||||||
<circle cx="18" cy="18" r="15" fill="none" stroke="currentColor" strokeWidth="3" className="text-blue-500" strokeDasharray="70 100"/>
|
|
||||||
</svg>
|
|
||||||
<div className="absolute inset-0 flex items-center justify-center text-xs font-bold">70%</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="text-xs text-center space-y-1">
|
|
||||||
<div className="flex items-center justify-center gap-1">
|
|
||||||
<div className="w-2 h-2 rounded-full bg-blue-500"/>
|
|
||||||
<span>已完成:70%</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-3">
|
|
||||||
<div className="text-xs font-medium text-muted-foreground">最近会话</div>
|
|
||||||
<div className="p-3 bg-accent/20 rounded border">
|
|
||||||
<div className="flex items-center justify-between mb-2">
|
|
||||||
<span className="text-sm font-medium">功能:身份验证流程</span>
|
|
||||||
<span className="text-xs px-2 py-0.5 rounded-full bg-green-500/20 text-green-600">运行中</span>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-1.5">
|
|
||||||
<div className="flex items-center gap-2 text-xs">
|
|
||||||
<div className="w-3 h-3 rounded bg-green-500"/>
|
|
||||||
<span>实现登录表单</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2 text-xs">
|
|
||||||
<div className="w-3 h-3 rounded bg-amber-500"/>
|
|
||||||
<span>添加 OAuth 提供商</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2 text-xs">
|
|
||||||
<div className="w-3 h-3 rounded bg-muted"/>
|
|
||||||
<span className="text-muted-foreground">测试流程</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 最近会话小部件 */}
|
|
||||||
<div className="border rounded-lg overflow-hidden">
|
|
||||||
<div className="border-b bg-muted/30">
|
|
||||||
<div className="flex gap-1 p-2">
|
|
||||||
{['所有任务', '工作流', '轻量任务'].map((tab, i) => (
|
|
||||||
<button
|
|
||||||
key={tab}
|
|
||||||
className={`px-3 py-1.5 text-xs rounded-md transition-colors ${
|
|
||||||
i === 0 ? 'bg-background text-foreground' : 'text-muted-foreground hover:bg-foreground/5'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{tab}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="p-4">
|
|
||||||
<div className="grid grid-cols-3 gap-3">
|
|
||||||
{[
|
|
||||||
{ name: '重构 UI 组件', status: '进行中', progress: 65 },
|
|
||||||
{ name: '修复登录 Bug', status: '待处理', progress: 0 },
|
|
||||||
{ name: '添加深色模式', status: '已完成', progress: 100 },
|
|
||||||
].map((task, i) => (
|
|
||||||
<div key={i} className="p-3 bg-muted/30 rounded border cursor-pointer hover:border-primary/30">
|
|
||||||
<div className="flex items-center justify-between mb-2">
|
|
||||||
<span className="text-xs font-medium line-clamp-1">{task.name}</span>
|
|
||||||
<span className={`text-xs px-1.5 py-0.5 rounded ${
|
|
||||||
task.status === '已完成' ? 'bg-green-500/20 text-green-600' :
|
|
||||||
task.status === '进行中' ? 'bg-blue-500/20 text-blue-600' :
|
|
||||||
'bg-gray-500/20 text-gray-600'
|
|
||||||
}`}>{task.status}</span>
|
|
||||||
</div>
|
|
||||||
{task.progress > 0 && task.progress < 100 && (
|
|
||||||
<div className="w-full h-1.5 bg-muted rounded-full overflow-hidden">
|
|
||||||
<div className="h-full bg-blue-500 rounded-full" style={{ width: `${task.progress}%` }}/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
:::
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -339,243 +202,15 @@ function ActivityFeed() {
|
|||||||
|
|
||||||
### 统计卡片演示
|
### 统计卡片演示
|
||||||
|
|
||||||
:::demo MiniStatCards
|
:::demo MiniStatCards #MiniStatCards.tsx :::
|
||||||
# mini-stat-cards.tsx
|
|
||||||
/**
|
|
||||||
* 迷你统计卡片演示
|
|
||||||
* 带有迷你趋势图的独立统计卡片
|
|
||||||
*/
|
|
||||||
export function MiniStatCards() {
|
|
||||||
const stats = [
|
|
||||||
{ label: '活动会话', value: 12, trend: [8, 10, 9, 11, 10, 12, 12], color: 'blue' },
|
|
||||||
{ label: '总任务', value: 48, trend: [40, 42, 45, 44, 46, 47, 48], color: 'green' },
|
|
||||||
{ label: '已完成', value: 35, trend: [25, 28, 30, 32, 33, 34, 35], color: 'emerald' },
|
|
||||||
{ label: '待处理', value: 8, trend: [12, 10, 11, 9, 8, 7, 8], color: 'amber' },
|
|
||||||
{ label: '失败', value: 5, trend: [3, 4, 3, 5, 4, 5, 5], color: 'red' },
|
|
||||||
{ label: '今日活动', value: 23, trend: [5, 10, 15, 18, 20, 22, 23], color: 'purple' },
|
|
||||||
]
|
|
||||||
|
|
||||||
const colorMap = {
|
|
||||||
blue: 'text-blue-500 bg-blue-500/10',
|
|
||||||
green: 'text-green-500 bg-green-500/10',
|
|
||||||
emerald: 'text-emerald-500 bg-emerald-500/10',
|
|
||||||
amber: 'text-amber-500 bg-amber-500/10',
|
|
||||||
red: 'text-red-500 bg-red-500/10',
|
|
||||||
purple: 'text-purple-500 bg-purple-500/10',
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="p-6 bg-background">
|
|
||||||
<h3 className="text-sm font-semibold mb-4">带迷你趋势图的统计</h3>
|
|
||||||
<div className="grid grid-cols-2 md:grid-cols-3 gap-4">
|
|
||||||
{stats.map((stat, i) => (
|
|
||||||
<div key={i} className="p-4 border rounded-lg bg-card">
|
|
||||||
<div className="flex items-center justify-between mb-2">
|
|
||||||
<span className="text-xs text-muted-foreground">{stat.label}</span>
|
|
||||||
<div className={`w-2 h-2 rounded-full ${colorMap[stat.color].split(' ')[1]}`}/>
|
|
||||||
</div>
|
|
||||||
<div className={`text-2xl font-bold ${colorMap[stat.color].split(' ')[0]}`}>{stat.value}</div>
|
|
||||||
<div className="mt-2 h-8 flex items-end gap-0.5">
|
|
||||||
{stat.trend.map((v, j) => (
|
|
||||||
<div
|
|
||||||
key={j}
|
|
||||||
className="flex-1 rounded-t"
|
|
||||||
style={{
|
|
||||||
height: `${(v / Math.max(...stat.trend)) * 100}%`,
|
|
||||||
backgroundColor: v === stat.value ? 'currentColor' : 'rgba(59, 130, 246, 0.3)',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
:::
|
|
||||||
|
|
||||||
### 项目信息横幅演示
|
### 项目信息横幅演示
|
||||||
|
|
||||||
:::demo ProjectInfoBanner
|
:::demo ProjectInfoBanner #ProjectInfoBanner.tsx :::
|
||||||
# project-info-banner.tsx
|
|
||||||
/**
|
|
||||||
* 项目信息横幅演示
|
|
||||||
* 带有技术栈的可展开项目信息
|
|
||||||
*/
|
|
||||||
export function ProjectInfoBanner() {
|
|
||||||
const [expanded, setExpanded] = React.useState(false)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="p-6 bg-background">
|
|
||||||
<h3 className="text-sm font-semibold mb-4">项目信息横幅</h3>
|
|
||||||
<div className="border rounded-lg overflow-hidden">
|
|
||||||
{/* 横幅头部 */}
|
|
||||||
<div className="p-4 bg-muted/30 flex items-center justify-between">
|
|
||||||
<div>
|
|
||||||
<h4 className="font-semibold">我的项目</h4>
|
|
||||||
<p className="text-sm text-muted-foreground">使用 React 构建的现代化 Web 应用</p>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
onClick={() => setExpanded(!expanded)}
|
|
||||||
className="p-2 rounded-md hover:bg-accent"
|
|
||||||
>
|
|
||||||
<svg className={`w-5 h-5 transition-transform ${expanded ? 'rotate-180' : ''}`} fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 技术栈徽章 */}
|
|
||||||
<div className="px-4 pb-3 flex flex-wrap gap-2">
|
|
||||||
{['TypeScript', 'React', 'Vite', 'Tailwind CSS', 'Zustand'].map((tech) => (
|
|
||||||
<span key={tech} className="px-2 py-1 text-xs rounded-full bg-primary/10 text-primary">
|
|
||||||
{tech}
|
|
||||||
</span>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 展开内容 */}
|
|
||||||
{expanded && (
|
|
||||||
<div className="p-4 border-t bg-muted/20 space-y-4">
|
|
||||||
<div>
|
|
||||||
<h5 className="text-xs font-semibold mb-2">架构</h5>
|
|
||||||
<div className="text-sm text-muted-foreground space-y-1">
|
|
||||||
<div>• 基于组件的 UI 架构</div>
|
|
||||||
<div>• 集中式状态管理</div>
|
|
||||||
<div>• RESTful API 集成</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h5 className="text-xs font-semibold mb-2">关键组件</h5>
|
|
||||||
<div className="grid grid-cols-2 gap-2 text-sm">
|
|
||||||
{['会话管理器', '仪表板', '任务调度器', '分析'].map((comp) => (
|
|
||||||
<div key={comp} className="flex items-center gap-2">
|
|
||||||
<div className="w-1.5 h-1.5 rounded-full bg-primary"/>
|
|
||||||
{comp}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
:::
|
|
||||||
|
|
||||||
### 会话轮播演示
|
### 会话轮播演示
|
||||||
|
|
||||||
:::demo SessionCarousel
|
:::demo SessionCarousel #SessionCarousel.tsx :::
|
||||||
# session-carousel.tsx
|
|
||||||
/**
|
|
||||||
* 会话轮播演示
|
|
||||||
* 带有导航的自动轮播会话卡片
|
|
||||||
*/
|
|
||||||
export function SessionCarousel() {
|
|
||||||
const [currentIndex, setCurrentIndex] = React.useState(0)
|
|
||||||
const sessions = [
|
|
||||||
{
|
|
||||||
name: '功能:用户身份验证',
|
|
||||||
status: 'running',
|
|
||||||
tasks: [
|
|
||||||
{ name: '实现登录表单', status: 'completed' },
|
|
||||||
{ name: '添加 OAuth 提供商', status: 'in-progress' },
|
|
||||||
{ name: '创建会话管理', status: 'pending' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Bug 修复:内存泄漏',
|
|
||||||
status: 'running',
|
|
||||||
tasks: [
|
|
||||||
{ name: '识别泄漏源', status: 'completed' },
|
|
||||||
{ name: '修复清理处理器', status: 'in-progress' },
|
|
||||||
{ name: '添加单元测试', status: 'pending' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '重构:API 层',
|
|
||||||
status: 'planning',
|
|
||||||
tasks: [
|
|
||||||
{ name: '设计新接口', status: 'pending' },
|
|
||||||
{ name: '迁移现有端点', status: 'pending' },
|
|
||||||
{ name: '更新文档', status: 'pending' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
const statusColors = {
|
|
||||||
completed: 'bg-green-500',
|
|
||||||
'in-progress': 'bg-amber-500',
|
|
||||||
pending: 'bg-muted',
|
|
||||||
}
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
const timer = setInterval(() => {
|
|
||||||
setCurrentIndex((i) => (i + 1) % sessions.length)
|
|
||||||
}, 5000)
|
|
||||||
return () => clearInterval(timer)
|
|
||||||
}, [sessions.length])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="p-6 bg-background">
|
|
||||||
<h3 className="text-sm font-semibold mb-4">会话轮播(每 5 秒自动轮播)</h3>
|
|
||||||
<div className="border rounded-lg p-4 bg-card">
|
|
||||||
<div className="flex items-center justify-between mb-3">
|
|
||||||
<span className="text-sm font-medium">会话 {currentIndex + 1} / {sessions.length}</span>
|
|
||||||
<div className="flex gap-1">
|
|
||||||
{sessions.map((_, i) => (
|
|
||||||
<button
|
|
||||||
key={i}
|
|
||||||
onClick={() => setCurrentIndex(i)}
|
|
||||||
className={`w-2 h-2 rounded-full transition-colors ${
|
|
||||||
i === currentIndex ? 'bg-primary' : 'bg-muted-foreground/30'
|
|
||||||
}`}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="p-4 bg-accent/20 rounded border">
|
|
||||||
<div className="flex items-center justify-between mb-3">
|
|
||||||
<span className="font-medium">{sessions[currentIndex].name}</span>
|
|
||||||
<span className={`text-xs px-2 py-1 rounded-full ${
|
|
||||||
sessions[currentIndex].status === 'running' ? 'bg-green-500/20 text-green-600' : 'bg-blue-500/20 text-blue-600'
|
|
||||||
}`}>
|
|
||||||
{sessions[currentIndex].status === 'running' ? '运行中' : sessions[currentIndex].status === 'planning' ? '计划中' : sessions[currentIndex].status}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
{sessions[currentIndex].tasks.map((task, i) => (
|
|
||||||
<div key={i} className="flex items-center gap-2 text-sm">
|
|
||||||
<div className={`w-3 h-3 rounded ${statusColors[task.status]}`}/>
|
|
||||||
<span className={task.status === 'pending' ? 'text-muted-foreground' : ''}>{task.name}</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex justify-between mt-3">
|
|
||||||
<button
|
|
||||||
onClick={() => setCurrentIndex((i) => (i - 1 + sessions.length) % sessions.length)}
|
|
||||||
className="px-3 py-1.5 text-sm border rounded-md hover:bg-accent"
|
|
||||||
>
|
|
||||||
← 上一页
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => setCurrentIndex((i) => (i + 1) % sessions.length)}
|
|
||||||
className="px-3 py-1.5 text-sm border rounded-md hover:bg-accent"
|
|
||||||
>
|
|
||||||
下一页 →
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
:::
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -48,129 +48,7 @@
|
|||||||
|
|
||||||
## 实时演示
|
## 实时演示
|
||||||
|
|
||||||
:::demo TerminalDashboardOverview
|
:::demo TerminalDashboardOverview #TerminalDashboardOverview.tsx :::
|
||||||
# terminal-dashboard-overview.tsx
|
|
||||||
/**
|
|
||||||
* Terminal Dashboard Overview Demo
|
|
||||||
* Shows the three-column layout with resizable panes and toolbar
|
|
||||||
*/
|
|
||||||
export function TerminalDashboardOverview() {
|
|
||||||
const [fileSidebarOpen, setFileSidebarOpen] = React.useState(true)
|
|
||||||
const [sessionSidebarOpen, setSessionSidebarOpen] = React.useState(true)
|
|
||||||
const [activePanel, setActivePanel] = React.useState(null)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="h-[600px] flex flex-col bg-background">
|
|
||||||
{/* Toolbar */}
|
|
||||||
<div className="flex items-center justify-between px-3 py-2 border-b bg-muted/30">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<span className="text-sm font-medium">Terminal Dashboard</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-1">
|
|
||||||
{['Sessions', 'Files', 'Issues', 'Queue', 'Inspector', 'Scheduler'].map((item) => (
|
|
||||||
<button
|
|
||||||
key={item}
|
|
||||||
onClick={() => {
|
|
||||||
if (item === 'Sessions') setSessionSidebarOpen(!sessionSidebarOpen)
|
|
||||||
else if (item === 'Files') setFileSidebarOpen(!fileSidebarOpen)
|
|
||||||
else setActivePanel(activePanel === item.toLowerCase() ? null : item.toLowerCase())
|
|
||||||
}}
|
|
||||||
className={`px-2 py-1 text-xs rounded transition-colors ${
|
|
||||||
(item === 'Sessions' && sessionSidebarOpen) ||
|
|
||||||
(item === 'Files' && fileSidebarOpen) ||
|
|
||||||
activePanel === item.toLowerCase()
|
|
||||||
? 'bg-primary text-primary-foreground'
|
|
||||||
: 'hover:bg-accent'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{item}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Main Layout */}
|
|
||||||
<div className="flex-1 flex min-h-0">
|
|
||||||
{/* Session Sidebar */}
|
|
||||||
{sessionSidebarOpen && (
|
|
||||||
<div className="w-60 border-r flex flex-col">
|
|
||||||
<div className="px-3 py-2 text-xs font-semibold border-b bg-muted/30">
|
|
||||||
Session Groups
|
|
||||||
</div>
|
|
||||||
<div className="flex-1 p-2 space-y-1 text-sm overflow-auto">
|
|
||||||
{['Active Sessions', 'Completed', 'Archived'].map((group) => (
|
|
||||||
<div key={group}>
|
|
||||||
<div className="flex items-center gap-1 px-2 py-1 rounded hover:bg-accent cursor-pointer">
|
|
||||||
<span className="text-xs">▼</span>
|
|
||||||
<span>{group}</span>
|
|
||||||
</div>
|
|
||||||
<div className="ml-4 space-y-0.5">
|
|
||||||
<div className="px-2 py-1 text-xs text-muted-foreground hover:bg-accent rounded cursor-pointer">
|
|
||||||
Session 1
|
|
||||||
</div>
|
|
||||||
<div className="px-2 py-1 text-xs text-muted-foreground hover:bg-accent rounded cursor-pointer">
|
|
||||||
Session 2
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Terminal Grid */}
|
|
||||||
<div className="flex-1 bg-muted/20 p-2">
|
|
||||||
<div className="grid grid-cols-2 grid-rows-2 gap-2 h-full">
|
|
||||||
{[1, 2, 3, 4].map((i) => (
|
|
||||||
<div key={i} className="bg-background border rounded p-3 font-mono text-xs">
|
|
||||||
<div className="text-green-500 mb-2">$ Terminal {i}</div>
|
|
||||||
<div className="text-muted-foreground">
|
|
||||||
<div>Working directory: /project</div>
|
|
||||||
<div>Type a command to begin...</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* File Sidebar */}
|
|
||||||
{fileSidebarOpen && (
|
|
||||||
<div className="w-64 border-l flex flex-col">
|
|
||||||
<div className="px-3 py-2 text-xs font-semibold border-b bg-muted/30">
|
|
||||||
Project Files
|
|
||||||
</div>
|
|
||||||
<div className="flex-1 p-2 text-sm overflow-auto">
|
|
||||||
<div className="space-y-1">
|
|
||||||
{['src', 'docs', 'tests', 'package.json', 'README.md'].map((item) => (
|
|
||||||
<div key={item} className="px-2 py-1 rounded hover:bg-accent cursor-pointer flex items-center gap-2">
|
|
||||||
<span className="text-xs text-muted-foreground">📁</span>
|
|
||||||
{item}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Floating Panel */}
|
|
||||||
{activePanel && (
|
|
||||||
<div className="absolute top-12 right-4 w-80 bg-background border rounded-lg shadow-lg">
|
|
||||||
<div className="flex items-center justify-between px-3 py-2 border-b">
|
|
||||||
<span className="text-sm font-medium capitalize">{activePanel} Panel</span>
|
|
||||||
<button onClick={() => setActivePanel(null)} className="text-xs hover:bg-accent px-2 py-1 rounded">
|
|
||||||
✕
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="p-4 text-sm text-muted-foreground">
|
|
||||||
{activePanel} content placeholder
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
:::
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -323,231 +201,15 @@ function CustomLayout() {
|
|||||||
|
|
||||||
### 布局预设演示
|
### 布局预设演示
|
||||||
|
|
||||||
:::demo TerminalLayoutPresets
|
:::demo TerminalLayoutPresets #TerminalLayoutPresets.tsx :::
|
||||||
# terminal-layout-presets.tsx
|
|
||||||
/**
|
|
||||||
* Terminal Layout Presets Demo
|
|
||||||
* Interactive layout preset buttons
|
|
||||||
*/
|
|
||||||
export function TerminalLayoutPresets() {
|
|
||||||
const [layout, setLayout] = React.useState('grid-2x2')
|
|
||||||
|
|
||||||
const layouts = {
|
|
||||||
single: 'grid-cols-1 grid-rows-1',
|
|
||||||
'split-h': 'grid-cols-2 grid-rows-1',
|
|
||||||
'split-v': 'grid-cols-1 grid-rows-2',
|
|
||||||
'grid-2x2': 'grid-cols-2 grid-rows-2',
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="p-6 bg-background space-y-4">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<h3 className="text-sm font-semibold">Terminal Layout Presets</h3>
|
|
||||||
<div className="flex gap-2">
|
|
||||||
{Object.keys(layouts).map((preset) => (
|
|
||||||
<button
|
|
||||||
key={preset}
|
|
||||||
onClick={() => setLayout(preset)}
|
|
||||||
className={`px-3 py-1.5 text-xs rounded transition-colors ${
|
|
||||||
layout === preset
|
|
||||||
? 'bg-primary text-primary-foreground'
|
|
||||||
: 'border hover:bg-accent'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{preset.replace('-', ' ').toUpperCase()}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={`grid gap-2 h-64 ${layouts[layout]}`}>
|
|
||||||
{Array.from({ length: layout === 'single' ? 1 : layout.includes('2x') ? 4 : 2 }).map((_, i) => (
|
|
||||||
<div key={i} className="bg-muted/20 border rounded p-4 font-mono text-xs">
|
|
||||||
<div className="text-green-500">$ Terminal {i + 1}</div>
|
|
||||||
<div className="text-muted-foreground mt-1">Ready for input...</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
:::
|
|
||||||
|
|
||||||
### 浮动面板演示
|
### 浮动面板演示
|
||||||
|
|
||||||
:::demo FloatingPanelsDemo
|
:::demo FloatingPanelsDemo #FloatingPanelsDemo.tsx :::
|
||||||
# floating-panels-demo.tsx
|
|
||||||
/**
|
|
||||||
* Floating Panels Demo
|
|
||||||
* Mutually exclusive overlay panels
|
|
||||||
*/
|
|
||||||
export function FloatingPanelsDemo() {
|
|
||||||
const [activePanel, setActivePanel] = React.useState(null)
|
|
||||||
|
|
||||||
const panels = [
|
|
||||||
{ id: 'issues', title: 'Issues + Queue', side: 'left', width: 700 },
|
|
||||||
{ id: 'queue', title: 'Queue', side: 'right', width: 400 },
|
|
||||||
{ id: 'inspector', title: 'Inspector', side: 'right', width: 360 },
|
|
||||||
{ id: 'execution', title: 'Execution Monitor', side: 'right', width: 380 },
|
|
||||||
{ id: 'scheduler', title: 'Scheduler', side: 'right', width: 340 },
|
|
||||||
]
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="relative h-[500px] p-6 bg-background border rounded-lg">
|
|
||||||
<div className="flex items-center justify-between mb-4">
|
|
||||||
<h3 className="text-sm font-semibold">Floating Panels</h3>
|
|
||||||
<div className="flex gap-2">
|
|
||||||
{panels.map((panel) => (
|
|
||||||
<button
|
|
||||||
key={panel.id}
|
|
||||||
onClick={() => setActivePanel(activePanel === panel.id ? null : panel.id)}
|
|
||||||
className={`px-3 py-1.5 text-xs rounded transition-colors ${
|
|
||||||
activePanel === panel.id
|
|
||||||
? 'bg-primary text-primary-foreground'
|
|
||||||
: 'border hover:bg-accent'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{panel.title}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="h-[380px] bg-muted/20 border rounded flex items-center justify-center">
|
|
||||||
<p className="text-sm text-muted-foreground">
|
|
||||||
{activePanel ? `"${panels.find((p) => p.id === activePanel)?.title}" panel is open` : 'Click a button to open a floating panel'}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Floating Panel Overlay */}
|
|
||||||
{activePanel && (
|
|
||||||
<div
|
|
||||||
className={`absolute top-16 border rounded-lg shadow-lg bg-background ${
|
|
||||||
panels.find((p) => p.id === activePanel)?.side === 'left' ? 'left-6' : 'right-6'
|
|
||||||
}`}
|
|
||||||
style={{ width: panels.find((p) => p.id === activePanel)?.width }}
|
|
||||||
>
|
|
||||||
<div className="flex items-center justify-between px-3 py-2 border-b">
|
|
||||||
<span className="text-sm font-medium">{panels.find((p) => p.id === activePanel)?.title}</span>
|
|
||||||
<button
|
|
||||||
onClick={() => setActivePanel(null)}
|
|
||||||
className="text-xs hover:bg-accent px-2 py-1 rounded"
|
|
||||||
>
|
|
||||||
✕
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="p-4 text-sm text-muted-foreground">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<div className="w-2 h-2 rounded-full bg-blue-500"/>
|
|
||||||
<span>Item 1</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<div className="w-2 h-2 rounded-full bg-green-500"/>
|
|
||||||
<span>Item 2</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<div className="w-2 h-2 rounded-full bg-amber-500"/>
|
|
||||||
<span>Item 3</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
:::
|
|
||||||
|
|
||||||
### 可调整大小窗格演示
|
### 可调整大小窗格演示
|
||||||
|
|
||||||
:::demo ResizablePanesDemo
|
:::demo ResizablePanesDemo #ResizablePanesDemo.tsx :::
|
||||||
# resizable-panes-demo.tsx
|
|
||||||
/**
|
|
||||||
* Resizable Panes Demo
|
|
||||||
* Simulates the Allotment resizable split behavior
|
|
||||||
*/
|
|
||||||
export function ResizablePanesDemo() {
|
|
||||||
const [leftWidth, setLeftWidth] = React.useState(240)
|
|
||||||
const [rightWidth, setRightWidth] = React.useState(280)
|
|
||||||
const [isDragging, setIsDragging] = React.useState(null)
|
|
||||||
|
|
||||||
const handleDragStart = (side) => (e) => {
|
|
||||||
setIsDragging(side)
|
|
||||||
e.preventDefault()
|
|
||||||
}
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
const handleMouseMove = (e) => {
|
|
||||||
if (isDragging === 'left') {
|
|
||||||
setLeftWidth(Math.max(180, Math.min(320, e.clientX)))
|
|
||||||
} else if (isDragging === 'right') {
|
|
||||||
setRightWidth(Math.max(200, Math.min(400, window.innerWidth - e.clientX)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleMouseUp = () => setIsDragging(null)
|
|
||||||
|
|
||||||
if (isDragging) {
|
|
||||||
window.addEventListener('mousemove', handleMouseMove)
|
|
||||||
window.addEventListener('mouseup', handleMouseUp)
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener('mousemove', handleMouseMove)
|
|
||||||
window.removeEventListener('mouseup', handleMouseUp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [isDragging])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="h-[400px] flex bg-background border rounded-lg overflow-hidden">
|
|
||||||
{/* Left Sidebar */}
|
|
||||||
<div style={{ width: leftWidth }} className="border-r flex flex-col">
|
|
||||||
<div className="px-3 py-2 text-xs font-semibold border-b bg-muted/30">
|
|
||||||
Session Groups
|
|
||||||
</div>
|
|
||||||
<div className="flex-1 p-2 text-sm space-y-1">
|
|
||||||
{['Active Sessions', 'Completed'].map((g) => (
|
|
||||||
<div key={g} className="px-2 py-1 hover:bg-accent rounded cursor-pointer">{g}</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Left Drag Handle */}
|
|
||||||
<div
|
|
||||||
onMouseDown={handleDragStart('left')}
|
|
||||||
className={`w-1 bg-border hover:bg-primary cursor-col-resize transition-colors ${
|
|
||||||
isDragging === 'left' ? 'bg-primary' : ''
|
|
||||||
}`}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Main Content */}
|
|
||||||
<div className="flex-1 bg-muted/20 flex items-center justify-center">
|
|
||||||
<span className="text-sm text-muted-foreground">Terminal Grid Area</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Right Drag Handle */}
|
|
||||||
<div
|
|
||||||
onMouseDown={handleDragStart('right')}
|
|
||||||
className={`w-1 bg-border hover:bg-primary cursor-col-resize transition-colors ${
|
|
||||||
isDragging === 'right' ? 'bg-primary' : ''
|
|
||||||
}`}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Right Sidebar */}
|
|
||||||
<div style={{ width: rightWidth }} className="border-l flex flex-col">
|
|
||||||
<div className="px-3 py-2 text-xs font-semibold border-b bg-muted/30">
|
|
||||||
Project Files
|
|
||||||
</div>
|
|
||||||
<div className="flex-1 p-2 text-sm space-y-1">
|
|
||||||
{['src/', 'docs/', 'tests/'].map((f) => (
|
|
||||||
<div key={f} className="px-2 py-1 hover:bg-accent rounded cursor-pointer">{f}</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
:::
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
1140
docs/zh/skills/core-skills.md
Normal file
1140
docs/zh/skills/core-skills.md
Normal file
File diff suppressed because it is too large
Load Diff
270
docs/zh/skills/custom.md
Normal file
270
docs/zh/skills/custom.md
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
# 自定义技能开发
|
||||||
|
|
||||||
|
创建和部署自定义 CCW 技能指南。
|
||||||
|
|
||||||
|
## 技能结构
|
||||||
|
|
||||||
|
```
|
||||||
|
~/.claude/skills/my-skill/
|
||||||
|
├── SKILL.md # 技能定义(必需)
|
||||||
|
├── index.ts # 技能逻辑(可选)
|
||||||
|
├── examples/ # 使用示例
|
||||||
|
└── README.md # 文档
|
||||||
|
```
|
||||||
|
|
||||||
|
## 创建技能
|
||||||
|
|
||||||
|
### 1. 定义技能元数据
|
||||||
|
|
||||||
|
创建带 YAML 前言的 `SKILL.md`:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
name: my-skill
|
||||||
|
description: My custom skill for X
|
||||||
|
version: 1.0.0
|
||||||
|
author: Your Name <email@example.com>
|
||||||
|
tags: [custom, automation, frontend]
|
||||||
|
category: development
|
||||||
|
---
|
||||||
|
|
||||||
|
# My Custom Skill
|
||||||
|
|
||||||
|
## What It Does
|
||||||
|
技能用途和功能的详细描述。
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
\`\`\`javascript
|
||||||
|
Skill(skill="my-skill", args="your input here")
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Example 1: Basic Usage
|
||||||
|
\`\`\`javascript
|
||||||
|
Skill(skill="my-skill", args="create user form")
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### Example 2: With Options
|
||||||
|
\`\`\`javascript
|
||||||
|
Skill(skill="my-skill", args={
|
||||||
|
component: "UserForm",
|
||||||
|
typescript: true,
|
||||||
|
styling: "tailwind"
|
||||||
|
})
|
||||||
|
\`\`\`
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 实现技能逻辑(可选)
|
||||||
|
|
||||||
|
对于复杂技能,添加 `index.ts`:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import type { SkillContext, SkillResult } from '@ccw/types'
|
||||||
|
|
||||||
|
interface MySkillOptions {
|
||||||
|
component?: string
|
||||||
|
typescript?: boolean
|
||||||
|
styling?: 'css' | 'tailwind' | 'scss'
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function execute(
|
||||||
|
args: string | MySkillOptions,
|
||||||
|
context: SkillContext
|
||||||
|
): Promise<SkillResult> {
|
||||||
|
// 解析输入
|
||||||
|
const options = typeof args === 'string'
|
||||||
|
? { component: args }
|
||||||
|
: args
|
||||||
|
|
||||||
|
// 执行技能逻辑
|
||||||
|
const result = await generateComponent(options)
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
output: result,
|
||||||
|
metadata: {
|
||||||
|
skill: 'my-skill',
|
||||||
|
version: '1.0.0',
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function generateComponent(options: MySkillOptions) {
|
||||||
|
// 你的实现代码
|
||||||
|
return `// Generated code`
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 技能类别
|
||||||
|
|
||||||
|
### 开发
|
||||||
|
|
||||||
|
- 组件生成器
|
||||||
|
- API 脚手架
|
||||||
|
- 数据库模式创建
|
||||||
|
|
||||||
|
### 文档
|
||||||
|
|
||||||
|
- API 文档生成
|
||||||
|
- README 创建
|
||||||
|
- Changelog 生成
|
||||||
|
|
||||||
|
### DevOps
|
||||||
|
|
||||||
|
- CI/CD 配置
|
||||||
|
- Dockerfile 生成
|
||||||
|
- Kubernetes 清单
|
||||||
|
|
||||||
|
### 测试
|
||||||
|
|
||||||
|
- 测试生成
|
||||||
|
- Mock 创建
|
||||||
|
- 覆盖率报告
|
||||||
|
|
||||||
|
## 技能最佳实践
|
||||||
|
|
||||||
|
### 1. 明确用途
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# 好的:清晰和具体
|
||||||
|
name: generate-react-component
|
||||||
|
description: 生成带 hooks 和 TypeScript 的 React 组件
|
||||||
|
|
||||||
|
# 坏的:模糊和通用
|
||||||
|
name: code-helper
|
||||||
|
description: 帮助编码
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 类型安全
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 定义清晰的接口
|
||||||
|
interface Options {
|
||||||
|
name: string
|
||||||
|
typescript?: boolean
|
||||||
|
styling?: 'css' | 'tailwind'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证输入
|
||||||
|
function validateOptions(options: any): Options {
|
||||||
|
if (!options.name) {
|
||||||
|
throw new Error('组件名称是必需的')
|
||||||
|
}
|
||||||
|
return options as Options
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 错误处理
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
try {
|
||||||
|
const result = await generateComponent(options)
|
||||||
|
return { success: true, output: result }
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: error.message,
|
||||||
|
suggestion: '确保组件名称有效'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 文档
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
\`\`\`javascript
|
||||||
|
Skill(skill="my-skill", args="...")
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
| Option | Type | Default | Description |
|
||||||
|
|--------|------|---------|-------------|
|
||||||
|
| name | string | required | 组件名称 |
|
||||||
|
| typescript | boolean | true | 使用 TypeScript |
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
更多示例参见 `examples/` 目录。
|
||||||
|
```
|
||||||
|
|
||||||
|
## 发布技能
|
||||||
|
|
||||||
|
### 私有技能
|
||||||
|
|
||||||
|
`~/.claude/skills/` 中的技能自动可用。
|
||||||
|
|
||||||
|
### 团队技能
|
||||||
|
|
||||||
|
通过 git 共享技能:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 创建技能仓库
|
||||||
|
mkdir my-ccw-skills
|
||||||
|
cd my-ccw-skills
|
||||||
|
git init
|
||||||
|
|
||||||
|
# 添加技能
|
||||||
|
mkdir skills/skill-1
|
||||||
|
# ... 添加技能文件
|
||||||
|
|
||||||
|
# 与团队共享
|
||||||
|
git remote add origin <repo-url>
|
||||||
|
git push -u origin main
|
||||||
|
```
|
||||||
|
|
||||||
|
团队成员安装:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone <repo-url> ~/.claude/skills/team-skills
|
||||||
|
```
|
||||||
|
|
||||||
|
### 公共技能
|
||||||
|
|
||||||
|
发布到 npm:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "ccw-skills-my-skill",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"ccw": {
|
||||||
|
"skills": ["my-skill"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 测试技能
|
||||||
|
|
||||||
|
在 `tests/` 中创建测试:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { describe, it, expect } from 'vitest'
|
||||||
|
import { execute } from '../index'
|
||||||
|
|
||||||
|
describe('my-skill', () => {
|
||||||
|
it('should generate component', async () => {
|
||||||
|
const result = await execute('UserForm', {})
|
||||||
|
expect(result.success).toBe(true)
|
||||||
|
expect(result.output).toContain('UserForm')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## 调试
|
||||||
|
|
||||||
|
启用调试日志:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export CCW_DEBUG=1
|
||||||
|
ccw skill run my-skill "test input"
|
||||||
|
```
|
||||||
|
|
||||||
|
::: info 参见
|
||||||
|
- [核心技能](./core-skills.md) - 内置技能参考
|
||||||
|
- [技能库](./index.md) - 所有可用技能
|
||||||
|
:::
|
||||||
275
docs/zh/skills/index.md
Normal file
275
docs/zh/skills/index.md
Normal file
@@ -0,0 +1,275 @@
|
|||||||
|
# Skills 库
|
||||||
|
|
||||||
|
完整的 **33 个 CCW 内置技能**参考手册,涵盖 3 大类别,以及自定义技能开发指南。
|
||||||
|
|
||||||
|
## 什么是 Skills?
|
||||||
|
|
||||||
|
Skills 是 CCW 可执行的、可复用的、领域特定的能力。每个技能都针对特定的开发任务或工作流而设计,可以组合成强大的工作流链。
|
||||||
|
|
||||||
|
## 类别概览
|
||||||
|
|
||||||
|
| 类别 | 数量 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| [独立技能](./core-skills.md#standalone-skills) | 12 | 用于特定任务的单用途技能 |
|
||||||
|
| [团队技能](./core-skills.md#team-skills) | 14 | 多代理协作技能 |
|
||||||
|
| [工作流技能](./core-skills.md#workflow-skills) | 7 | 规划和执行流水线技能 |
|
||||||
|
|
||||||
|
## 快速参考
|
||||||
|
|
||||||
|
### 独立技能
|
||||||
|
|
||||||
|
| 技能 | 触发器 | 说明 |
|
||||||
|
|------|--------|------|
|
||||||
|
| [brainstorm](./core-skills.md#brainstorm) | `brainstorm`, `头脑风暴` | 双模式统一头脑风暴 |
|
||||||
|
| [ccw-help](./core-skills.md#ccw-help) | `ccw-help`, `ccw-issue` | 命令帮助系统 |
|
||||||
|
| [memory-capture](./core-skills.md#memory-capture) | `memory capture`, `compact session` | 会话压缩或快速提示 |
|
||||||
|
| [memory-manage](./core-skills.md#memory-manage) | `memory manage`, `update claude`, `更新记忆` | CLAUDE.md 更新和文档生成 |
|
||||||
|
| [issue-manage](./core-skills.md#issue-manage) | `manage issue`, `list issues` | 交互式问题管理 |
|
||||||
|
| [review-code](./core-skills.md#review-code) | `review code`, `code review` | 6 维度代码审查 |
|
||||||
|
| [review-cycle](./core-skills.md#review-cycle) | `workflow:review-cycle` | 带自动修复的审查 |
|
||||||
|
| [skill-generator](./core-skills.md#skill-generator) | `create skill`, `new skill` | 创建技能的元技能 |
|
||||||
|
| [skill-tuning](./core-skills.md#skill-tuning) | `skill tuning`, `tune skill` | 技能诊断和优化 |
|
||||||
|
| [spec-generator](./core-skills.md#spec-generator) | `generate spec`, `create specification`, `spec generator` | 6 阶段规范生成 |
|
||||||
|
| [software-manual](./core-skills.md#software-manual) | `software manual`, `user guide` | 交互式 HTML 文档 |
|
||||||
|
| [command-generator](./core-skills.md#command-generator) | `generate command` | 命令文件生成 |
|
||||||
|
|
||||||
|
### 团队技能
|
||||||
|
|
||||||
|
| 技能 | 触发器 | 角色 | 说明 |
|
||||||
|
|------|--------|------|------|
|
||||||
|
| [team-lifecycle-v4](./core-skills.md#team-lifecycle-v4) | `team lifecycle` | 8 | 完整规范/实现/测试生命周期 |
|
||||||
|
| [team-brainstorm](./core-skills.md#team-brainstorm) | `team brainstorm` | 5 | 多角度创意生成 |
|
||||||
|
| [team-frontend](./core-skills.md#team-frontend) | `team frontend` | 6 | 带 UI/UX 的前端开发 |
|
||||||
|
| [team-issue](./core-skills.md#team-issue) | `team issue` | 6 | 问题解决流水线 |
|
||||||
|
| [team-iterdev](./core-skills.md#team-iterdev) | `team iterdev` | 5 | 生成器-评论者循环 |
|
||||||
|
| [team-planex](./core-skills.md#team-planex) | `team planex` | 3 | 规划-执行流水线 |
|
||||||
|
| [team-quality-assurance](./core-skills.md#team-quality-assurance) | `team qa`, `team quality-assurance` | 6 | QA 测试工作流 |
|
||||||
|
| [team-review](./core-skills.md#team-review) | `team-review` | 4 | 代码扫描和修复 |
|
||||||
|
| [team-roadmap-dev](./core-skills.md#team-roadmap-dev) | `team roadmap-dev` | 4 | 路线图驱动开发 |
|
||||||
|
| [team-tech-debt](./core-skills.md#team-tech-debt) | `tech debt cleanup`, `技术债务` | 6 | 技术债务识别 |
|
||||||
|
| [team-testing](./core-skills.md#team-testing) | `team testing` | 5 | 渐进式测试覆盖 |
|
||||||
|
| [team-uidesign](./core-skills.md#team-uidesign) | `team uidesign` | 4 | 带 Design Token 的 UI 设计 |
|
||||||
|
| [team-ultra-analyze](./core-skills.md#team-ultra-analyze) | `team ultra-analyze`, `team analyze` | 5 | 深度协作分析 |
|
||||||
|
|
||||||
|
### 工作流技能
|
||||||
|
|
||||||
|
| 技能 | 触发器 | 说明 |
|
||||||
|
|------|--------|------|
|
||||||
|
| [workflow-plan](./core-skills.md#workflow-plan) | `workflow-plan`, `workflow-plan-verify`, `workflow:replan` | 4 阶段规划带验证 |
|
||||||
|
| [workflow-lite-plan](./core-skills.md#workflow-lite-plan) | `workflow-lite-plan` | 轻量级规划 |
|
||||||
|
| [workflow-multi-cli-plan](./core-skills.md#workflow-multi-cli-plan) | `workflow-multi-cli-plan`, `workflow:multi-cli-plan` | 多 CLI 协作规划 |
|
||||||
|
| [workflow-execute](./core-skills.md#workflow-execute) | `workflow-execute` | 任务执行协调 |
|
||||||
|
| [workflow-tdd-plan](./core-skills.md#workflow-tdd-plan) | `workflow-tdd-plan` | TDD 红-绿-重构 |
|
||||||
|
| [workflow-test-fix](./core-skills.md#workflow-test-fix) | `workflow-test-fix`, `test fix workflow` | 测试-修复流水线 |
|
||||||
|
| [workflow-skill-designer](./core-skills.md#workflow-skill-designer) | `design workflow skill`, `create workflow skill` | 工作流创建元技能 |
|
||||||
|
|
||||||
|
## 工作流组合
|
||||||
|
|
||||||
|
技能可以组合实现强大的工作流。参见 [工作流组合](./core-skills.md#workflow-combinations) 了解 15 种预定义组合。
|
||||||
|
|
||||||
|
### 常用组合
|
||||||
|
|
||||||
|
#### 完整生命周期开发
|
||||||
|
```bash
|
||||||
|
Skill(skill="brainstorm")
|
||||||
|
Skill(skill="workflow-plan")
|
||||||
|
Skill(skill="workflow-execute")
|
||||||
|
Skill(skill="review-cycle")
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 快速迭代
|
||||||
|
```bash
|
||||||
|
Skill(skill="workflow-lite-plan")
|
||||||
|
Skill(skill="workflow-execute")
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 测试驱动开发
|
||||||
|
```bash
|
||||||
|
Skill(skill="workflow-tdd-plan", args="--mode tdd-plan")
|
||||||
|
Skill(skill="workflow-execute")
|
||||||
|
Skill(skill="workflow-tdd-plan", args="--mode tdd-verify")
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用技能
|
||||||
|
|
||||||
|
### CLI 接口
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 通过 ccw 命令调用
|
||||||
|
ccw --help
|
||||||
|
|
||||||
|
# 或直接使用触发器
|
||||||
|
ccw brainstorm
|
||||||
|
ccw team lifecycle
|
||||||
|
```
|
||||||
|
|
||||||
|
### 编程接口
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// 基本使用
|
||||||
|
Skill(skill="brainstorm")
|
||||||
|
|
||||||
|
// 带参数
|
||||||
|
Skill(skill="team-lifecycle-v4", args="Build user authentication")
|
||||||
|
|
||||||
|
// 带模式选择
|
||||||
|
Skill(skill="workflow-plan", args="--mode verify")
|
||||||
|
```
|
||||||
|
|
||||||
|
## 自定义技能
|
||||||
|
|
||||||
|
为团队特定工作流创建自己的技能:
|
||||||
|
|
||||||
|
### 技能结构
|
||||||
|
|
||||||
|
```
|
||||||
|
~/.claude/skills/my-skill/
|
||||||
|
├── SKILL.md # 技能定义
|
||||||
|
├── phases/ # 阶段文件(可选)
|
||||||
|
│ ├── phase-1.md
|
||||||
|
│ └── phase-2.md
|
||||||
|
└── templates/ # 输出模板(可选)
|
||||||
|
└── output.md
|
||||||
|
```
|
||||||
|
|
||||||
|
### 技能模板
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
name: my-custom-skill
|
||||||
|
description: My custom skill for X
|
||||||
|
version: 1.0.0
|
||||||
|
triggers: [trigger1, trigger2]
|
||||||
|
---
|
||||||
|
|
||||||
|
# My Custom Skill
|
||||||
|
|
||||||
|
## Description
|
||||||
|
Detailed description of what this skill does.
|
||||||
|
|
||||||
|
## Phases
|
||||||
|
1. Phase 1: Description
|
||||||
|
2. Phase 2: Description
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
\`\`\`javascript
|
||||||
|
Skill(skill="my-custom-skill", args="input")
|
||||||
|
\`\`\`
|
||||||
|
```
|
||||||
|
|
||||||
|
### 最佳实践
|
||||||
|
|
||||||
|
1. **单一职责**:每个技能应做好一件事
|
||||||
|
2. **清晰触发器**:定义可识别的触发短语
|
||||||
|
3. **渐进式阶段**:将复杂技能分解为阶段
|
||||||
|
4. **紧凑恢复**:使用 TodoWrite 进行进度跟踪
|
||||||
|
5. **文档**:包含使用示例和预期输出
|
||||||
|
|
||||||
|
## 实践示例
|
||||||
|
|
||||||
|
### 示例 1:功能开发
|
||||||
|
|
||||||
|
**场景**:实现新的用户仪表板功能
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 步骤 1:头脑风暴功能
|
||||||
|
ccw brainstorm
|
||||||
|
# 按提示定义:
|
||||||
|
# - 仪表板小部件(统计、图表、最近活动)
|
||||||
|
# - 布局偏好
|
||||||
|
# - 数据刷新间隔
|
||||||
|
|
||||||
|
# 步骤 2:规划实现
|
||||||
|
ccw workflow-plan "Build user dashboard with configurable widgets"
|
||||||
|
# 输出:IMPL-001.json 包含任务分解
|
||||||
|
|
||||||
|
# 步骤 3:团队执行
|
||||||
|
ccw team lifecycle
|
||||||
|
# 或使用快速迭代:
|
||||||
|
ccw workflow-lite-plan && ccw workflow-execute
|
||||||
|
|
||||||
|
# 步骤 4:审查和优化
|
||||||
|
ccw review-code
|
||||||
|
# 修复发现的问题
|
||||||
|
```
|
||||||
|
|
||||||
|
### 示例 2:Bug 调查
|
||||||
|
|
||||||
|
**场景**:调试 API 端点性能问题
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 步骤 1:快速分析
|
||||||
|
ccw cli -p "Analyze /api/users endpoint for N+1 query issues" --tool gemini --mode analysis
|
||||||
|
|
||||||
|
# 步骤 2:深度调查(如需要)
|
||||||
|
ccw workflow:debug-with-file
|
||||||
|
# 创建假设、植入代码、分析日志
|
||||||
|
|
||||||
|
# 步骤 3:应用修复
|
||||||
|
ccw workflow-execute --task "Fix N+1 query in user endpoint"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 示例 3:代码迁移
|
||||||
|
|
||||||
|
**场景**:从 JavaScript 迁移到 TypeScript
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 步骤 1:分析代码库
|
||||||
|
ccw workflow:refactor-cycle
|
||||||
|
# 识别技术债务并创建迁移计划
|
||||||
|
|
||||||
|
# 步骤 2:分阶段执行迁移
|
||||||
|
ccw team roadmap-dev --epic "ts-migration"
|
||||||
|
# 渐进式迁移模块并测试
|
||||||
|
```
|
||||||
|
|
||||||
|
### 示例 4:文档生成
|
||||||
|
|
||||||
|
**场景**:生成 API 文档
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 步骤 1:捕获现有模式
|
||||||
|
ccw memory:capture "API patterns: REST, versioning, error handling"
|
||||||
|
|
||||||
|
# 步骤 2:生成文档
|
||||||
|
ccw software-manual --output ./docs/api/
|
||||||
|
```
|
||||||
|
|
||||||
|
### 示例 5:代码审查流水线
|
||||||
|
|
||||||
|
**场景**:审查 PR 变更
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 全面审查
|
||||||
|
ccw review-code --focus security,performance
|
||||||
|
|
||||||
|
# 或使用循环自动修复
|
||||||
|
ccw review-cycle --max-iterations 3
|
||||||
|
```
|
||||||
|
|
||||||
|
### 最佳效果提示
|
||||||
|
|
||||||
|
1. **从小开始**:简单任务使用 `workflow-lite-plan`
|
||||||
|
2. **使用记忆**:用 `memory:capture` 捕获见解供将来参考
|
||||||
|
3. **验证计划**:执行前始终审查生成的计划
|
||||||
|
4. **迭代**:使用 `review-cycle` 持续改进
|
||||||
|
5. **检查会话**:使用 `workflow:session:list` 跟踪进度
|
||||||
|
|
||||||
|
## 设计模式
|
||||||
|
|
||||||
|
技能使用这些经证明的模式:
|
||||||
|
|
||||||
|
| 模式 | 示例 |
|
||||||
|
|------|------|
|
||||||
|
| 编排器 + 工作者 | team-lifecycle-v4 |
|
||||||
|
| 生成器-评论者循环 | team-iterdev |
|
||||||
|
| 波浪流水线 | team-planex |
|
||||||
|
| 红-绿-重构 | workflow-tdd-plan |
|
||||||
|
|
||||||
|
::: info 参见
|
||||||
|
- [核心技能参考](./core-skills.md) - 详细技能文档
|
||||||
|
- [自定义技能](./custom.md) - 技能开发指南
|
||||||
|
- [CLI 命令](../cli/commands.md) - 命令参考
|
||||||
|
- [代理](../agents/builtin.md) - 专业代理
|
||||||
|
:::
|
||||||
174
docs/zh/skills/reference.md
Normal file
174
docs/zh/skills/reference.md
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
---
|
||||||
|
适用CLI: claude
|
||||||
|
分类: specialized
|
||||||
|
---
|
||||||
|
|
||||||
|
# Skills 参考
|
||||||
|
|
||||||
|
**33 个 CCW 内置技能**快速参考指南。
|
||||||
|
|
||||||
|
## 核心技能
|
||||||
|
|
||||||
|
| 技能 | 触发器 | 用途 |
|
||||||
|
|------|--------|------|
|
||||||
|
| **brainstorm** | `brainstorm`、`头脑风暴` | 统一头脑风暴,双模式操作(自动流水线 / 单角色) |
|
||||||
|
| **review-code** | `review code`、`code review`、`审查代码` | 多维度代码审查(6 维度) |
|
||||||
|
| **review-cycle** | `workflow:review-cycle` | 代码审查 + 自动修复编排 |
|
||||||
|
| **memory-capture** | `memory capture`、`compact session` | 会话压缩或快速提示捕获 |
|
||||||
|
| **memory-manage** | `memory manage`、`update claude`、`update memory`、`generate docs`、`更新记忆`、`生成文档` | CLAUDE.md 更新和文档生成 |
|
||||||
|
| **spec-generator** | `generate spec`、`create specification`、`spec generator`、`workflow:spec` | 6 阶段规范生成器(简介 → PRD → 架构 → 史诗) |
|
||||||
|
| **skill-generator** | `create skill`、`new skill` | 创建新 Claude Code 技能的元技能 |
|
||||||
|
| **skill-tuning** | `skill tuning`、`tune skill` | 通用技能诊断和优化工具 |
|
||||||
|
| **issue-manage** | `manage issue`、`list issues` | 交互式问题管理(CRUD 操作) |
|
||||||
|
| **ccw-help** | `ccw-help`、`ccw-issue` | CCW 命令帮助系统 |
|
||||||
|
| **software-manual** | `software manual`、`user guide` | 生成交互式 TiddlyWiki 风格 HTML 手册 |
|
||||||
|
|
||||||
|
## 工作流技能
|
||||||
|
|
||||||
|
| 技能 | 触发器 | 用途 |
|
||||||
|
|------|--------|------|
|
||||||
|
| **workflow-plan** | `workflow-plan`、`workflow-plan-verify`、`workflow:replan` | 4 阶段规划工作流,带验证和交互式重新规划 |
|
||||||
|
| **workflow-lite-plan** | `workflow-lite-plan`、`workflow:lite-execute` | 轻量级规划和执行技能 |
|
||||||
|
| **workflow-multi-cli-plan** | `workflow-multi-cli-plan`、`workflow:multi-cli-plan` | 多 CLI 协作规划,带 ACE 上下文引擎 |
|
||||||
|
| **workflow-execute** | `workflow-execute` | 协调工作流任务的代理执行 |
|
||||||
|
| **workflow-tdd-plan** | `workflow-tdd-plan`、`workflow-tdd-verify` | TDD 工作流,带红-绿-重构任务链 |
|
||||||
|
| **workflow-test-fix** | `workflow-test-fix`、`test fix workflow` | 统一测试-修复流水线,带自适应策略 |
|
||||||
|
| **workflow-skill-designer** | `design workflow skill`、`create workflow skill`、`workflow skill designer` | 设计编排器+阶段结构化工作流技能的元技能 |
|
||||||
|
|
||||||
|
## 团队技能
|
||||||
|
|
||||||
|
| 技能 | 触发器 | 角色 | 用途 |
|
||||||
|
|------|--------|------|------|
|
||||||
|
| **team-lifecycle** | `team lifecycle` | 可变 | 完整 spec/impl/test 生命周期团队(v5,team-worker 架构) |
|
||||||
|
| **team-coordinate** | `team coordinate` | 可变 | 通用团队协调(legacy) |
|
||||||
|
| **team-coordinate-v2** | - | 可变 | team-worker 架构协调 |
|
||||||
|
| **team-executor** | `team executor` | 可变 | 轻量级会话执行 |
|
||||||
|
| **team-executor-v2** | - | 可变 | team-worker 架构执行 |
|
||||||
|
| **team-planex** | `team planex` | 3 | 规划-执行波浪流水线 |
|
||||||
|
| **team-iterdev** | `team iterdev` | 5 | 生成器-评论者循环迭代开发 |
|
||||||
|
| **team-issue** | `team issue` | 6 | 问题解决流水线 |
|
||||||
|
| **team-testing** | `team testing` | 5 | 渐进式测试覆盖团队 |
|
||||||
|
| **team-quality-assurance** | `team qa`、`team quality-assurance` | 6 | QA 闭环工作流 |
|
||||||
|
| **team-brainstorm** | `team brainstorm` | 5 | 多角色协作头脑风暴 |
|
||||||
|
| **team-uidesign** | `team ui design` | 4 | 带 Design Token 系统的 UI 设计团队 |
|
||||||
|
| **team-frontend** | `team frontend` | 6 | 带 UI/UX 集成的前端开发 |
|
||||||
|
| **team-review** | `team-review` | 4 | 代码扫描和自动修复 |
|
||||||
|
| **team-roadmap-dev** | `team roadmap-dev` | 4 | 路线图驱动开发 |
|
||||||
|
| **team-tech-debt** | `tech debt cleanup`、`team tech-debt`、`技术债务` | 6 | 技术债务识别和清理 |
|
||||||
|
| **team-ultra-analyze** | `team ultra-analyze`、`team analyze` | 5 | 深度协作分析 |
|
||||||
|
|
||||||
|
## 命令生成技能
|
||||||
|
|
||||||
|
| 技能 | 触发器 | 用途 |
|
||||||
|
|------|--------|------|
|
||||||
|
| **command-generator** | `generate command` | 命令文件生成元技能 |
|
||||||
|
|
||||||
|
## 技能类别摘要
|
||||||
|
|
||||||
|
| 类别 | 数量 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| 核心技能 | 12 | 用于特定任务的单用途技能 |
|
||||||
|
| 工作流技能 | 7 | 规划和执行流水线技能 |
|
||||||
|
| 团队技能 | 17+ | 多代理协作技能 |
|
||||||
|
| 命令生成技能 | 1 | 命令文件生成 |
|
||||||
|
| **总计** | **37+** | |
|
||||||
|
|
||||||
|
## 使用
|
||||||
|
|
||||||
|
### 基本调用
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
Skill(skill="brainstorm")
|
||||||
|
Skill(skill="team-lifecycle", args="Build user authentication system")
|
||||||
|
Skill(skill="workflow-plan", args="--mode verify")
|
||||||
|
```
|
||||||
|
|
||||||
|
### CLI 调用
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 通过 /ccw 编排器
|
||||||
|
/ccw "brainstorm: user authentication flow"
|
||||||
|
/ccw "team planex: OAuth2 implementation"
|
||||||
|
|
||||||
|
# 直接触发器(某些上下文)
|
||||||
|
workflow-plan
|
||||||
|
team lifecycle
|
||||||
|
```
|
||||||
|
|
||||||
|
## 触发关键词
|
||||||
|
|
||||||
|
| 关键词 | 技能 |
|
||||||
|
|--------|------|
|
||||||
|
| `brainstorm`、`头脑风暴` | brainstorm |
|
||||||
|
| `review code`、`code review`、`审查代码` | review-code |
|
||||||
|
| `workflow:review-cycle` | review-cycle |
|
||||||
|
| `workflow-plan` | workflow-plan |
|
||||||
|
| `workflow-lite-plan` | workflow-lite-plan |
|
||||||
|
| `workflow-multi-cli-plan`、`workflow:multi-cli-plan` | workflow-multi-cli-plan |
|
||||||
|
| `workflow-execute` | workflow-execute |
|
||||||
|
| `workflow-tdd-plan` | workflow-tdd-plan |
|
||||||
|
| `workflow-test-fix`、`test fix workflow` | workflow-test-fix |
|
||||||
|
| `design workflow skill`、`create workflow skill`、`workflow skill designer` | workflow-skill-designer |
|
||||||
|
| `team lifecycle` | team-lifecycle (v5) |
|
||||||
|
| `team planex` | team-planex |
|
||||||
|
| `team iterdev` | team-iterdev |
|
||||||
|
| `team issue` | team-issue |
|
||||||
|
| `team testing` | team-testing |
|
||||||
|
| `team qa`、`team quality-assurance` | team-quality-assurance |
|
||||||
|
| `team brainstorm` | team-brainstorm |
|
||||||
|
| `team ui design`、`team uidesign` | team-uidesign |
|
||||||
|
| `team frontend` | team-frontend |
|
||||||
|
| `team-review` | team-review |
|
||||||
|
| `team roadmap-dev` | team-roadmap-dev |
|
||||||
|
| `tech debt cleanup`、`team tech-debt`、`技术债务` | team-tech-debt |
|
||||||
|
| `team ultra-analyze`、`team analyze` | team-ultra-analyze |
|
||||||
|
| `memory capture`、`compact session`、`记录`、`压缩会话` | memory-capture |
|
||||||
|
| `memory manage`、`update claude`、`update memory`、`generate docs`、`更新记忆`、`生成文档` | memory-manage |
|
||||||
|
| `generate spec`、`create specification`、`spec generator`、`workflow:spec` | spec-generator |
|
||||||
|
| `create skill`、`new skill` | skill-generator |
|
||||||
|
| `skill tuning`、`tune skill`、`skill diagnosis` | skill-tuning |
|
||||||
|
| `manage issue`、`list issues`、`edit issue` | issue-manage |
|
||||||
|
| `software manual`、`user guide`、`generate manual`、`create docs` | software-manual |
|
||||||
|
| `generate command` | command-generator |
|
||||||
|
|
||||||
|
## 团队技能架构
|
||||||
|
|
||||||
|
### 版本历史
|
||||||
|
|
||||||
|
| 版本 | 架构 | 状态 |
|
||||||
|
|------|------|------|
|
||||||
|
| v2 | Legacy | 已废弃 |
|
||||||
|
| v3 | 3 阶段生命周期 | Legacy |
|
||||||
|
| v4 | 5 阶段生命周期,带 inline discuss | 稳定 |
|
||||||
|
| **v5** | **team-worker 架构** | **最新** |
|
||||||
|
|
||||||
|
### v5 Team Worker 角色
|
||||||
|
|
||||||
|
最新的 team-lifecycle (v5) 使用 team-worker 代理,带动态角色分配:
|
||||||
|
|
||||||
|
| 角色 | 前缀 | 阶段 |
|
||||||
|
|------|------|------|
|
||||||
|
| doc-analyst | ANALYSIS | 需求分析 |
|
||||||
|
| doc-writer | DRAFT | 文档创建 |
|
||||||
|
| planner | PLAN | 实现规划 |
|
||||||
|
| executor | IMPL | 代码实现 |
|
||||||
|
| tester | TEST | 测试和 QA |
|
||||||
|
| reviewer | REVIEW | 代码审查 |
|
||||||
|
|
||||||
|
## 设计模式
|
||||||
|
|
||||||
|
| 模式 | 使用它的技能 |
|
||||||
|
|------|--------------|
|
||||||
|
| Orchestrator + Workers | team-lifecycle、team-testing、team-quality-assurance |
|
||||||
|
| Generator-Critic Loop | team-iterdev |
|
||||||
|
| Wave Pipeline | team-planex |
|
||||||
|
| Red-Green-Refactor | workflow-tdd-plan |
|
||||||
|
| Pure Orchestrator | workflow-plan、workflow-lite-plan |
|
||||||
|
| Progressive Phase Loading | workflow-plan、workflow-tdd-plan、team-lifecycle |
|
||||||
|
|
||||||
|
::: info 参见
|
||||||
|
- [核心技能详情](./core-skills.md) - 详细技能文档
|
||||||
|
- [自定义技能](./custom.md) - 创建你自己的技能
|
||||||
|
- [CLI 命令](../cli/commands.md) - 命令参考
|
||||||
|
- [团队工作流](../workflows/teams.md) - 团队工作流模式
|
||||||
|
:::
|
||||||
Reference in New Issue
Block a user