diff --git a/.claude/agents/team-worker.md b/.claude/agents/team-worker.md index 76f8848e..e22e6bc6 100644 --- a/.claude/agents/team-worker.md +++ b/.claude/agents/team-worker.md @@ -237,7 +237,7 @@ After Phase 4 completes, determine Phase 5 variant: ### Phase 5-L: Loop Completion (when inner_loop=true AND more same-prefix tasks pending) 1. **TaskUpdate**: Mark current task `completed` -2. **Message Bus**: Log completion +2. **Message Bus**: Log completion with verification evidence ``` mcp__ccw-tools__team_msg( operation="log", @@ -245,7 +245,7 @@ After Phase 4 completes, determine Phase 5 variant: from=, to="coordinator", type=, - summary="[] complete. ", + summary="[] complete. . Verified: ", ref= ) ``` @@ -283,7 +283,7 @@ After Phase 4 completes, determine Phase 5 variant: | Condition | Action | |-----------|--------| | Same-prefix successor (inner loop role) | Do NOT spawn — main agent handles via inner loop | -| 1 ready task, simple linear successor, different prefix | Spawn directly via `Task(run_in_background: true)` | +| 1 ready task, simple linear successor, different prefix | Spawn directly via `Task(run_in_background: true)` + log `fast_advance` to message bus | | Multiple ready tasks (parallel window) | SendMessage to coordinator (needs orchestration) | | No ready tasks + others running | SendMessage to coordinator (status update) | | No ready tasks + nothing running | SendMessage to coordinator (pipeline may be complete) | @@ -311,6 +311,23 @@ inner_loop: ` }) ``` +### Fast-Advance Notification + +After spawning a successor via fast-advance, MUST log to message bus: + +``` +mcp__ccw-tools__team_msg( + operation="log", + team=, + from=, + to="coordinator", + type="fast_advance", + summary="[] fast-advanced → spawned for " +) +``` + +This is a passive log entry (NOT a SendMessage). Coordinator reads it on next callback to reconcile `active_workers`. + ### SendMessage Format ``` @@ -320,8 +337,10 @@ SendMessage(team_name=, recipient="coordinator", message="[] [explore], code-gen (docs) -> [explore], validation -> [] | +| `subagents` | Suggested, not mandatory — coordinator may adjust based on task needs | +| `pattern_hint` | Reference pattern name from role-spec-template (research/document/code/analysis/validation) — guides coordinator's Phase 2-4 composition, NOT a rigid template selector | +| `output_type` | `artifact` (new files in session/artifacts/) / `codebase` (modify existing project files) / `mixed` (both) — determines verification strategy in Behavioral Traits | | `message_types.success` | `_complete` | | `message_types.error` | `error` | +**output_type derivation**: + +| Task Signal | output_type | Example | +|-------------|-------------|---------| +| "write report", "analyze", "research" | `artifact` | New analysis-report.md in session | +| "update docs", "modify code", "fix bug" | `codebase` | Modify existing project files | +| "implement feature + write summary" | `mixed` | Code changes + implementation summary | + ## Phase 4: Output Write `/task-analysis.json`: @@ -132,6 +142,8 @@ Write `/task-analysis.json`: "inner_loop": false, "role_spec_metadata": { "subagents": ["explore"], + "pattern_hint": "research", + "output_type": "artifact", "message_types": { "success": "research_complete", "error": "error" diff --git a/.claude/skills/team-coordinate-v2/roles/coordinator/commands/monitor.md b/.claude/skills/team-coordinate-v2/roles/coordinator/commands/monitor.md index dc972d3f..350dae4c 100644 --- a/.claude/skills/team-coordinate-v2/roles/coordinator/commands/monitor.md +++ b/.claude/skills/team-coordinate-v2/roles/coordinator/commands/monitor.md @@ -60,10 +60,12 @@ Receive callback from [] +- None completed -> STOP ``` -**Fast-advance note**: A worker may have already spawned its successor via fast-advance. When processing a callback: -1. Check if the expected next task is already `in_progress` (fast-advanced) -2. If yes -> skip spawning that task, update active_workers to include the fast-advanced worker -3. If no -> normal handleSpawnNext +**Fast-advance reconciliation**: A worker may have already spawned its successor via fast-advance. When processing any callback or resume: +1. Read recent `fast_advance` messages from team_msg (type="fast_advance") +2. For each fast_advance message: add the spawned successor to `active_workers` if not already present +3. Check if the expected next task is already `in_progress` (fast-advanced) +4. If yes -> skip spawning that task (already running) +5. If no -> normal handleSpawnNext --- @@ -262,6 +264,13 @@ handleCallback / handleResume detects: 4. -> handleSpawnNext (will re-spawn the task normally) ``` +### Fast-Advance State Sync + +On every coordinator wake (handleCallback, handleResume, handleCheck): +1. Read team_msg entries with `type="fast_advance"` since last coordinator wake +2. For each entry: sync `active_workers` with the spawned successor +3. This ensures coordinator's state reflects fast-advance decisions even before the successor's callback arrives + ### Consensus-Blocked Handling ``` diff --git a/.claude/skills/team-coordinate-v2/roles/coordinator/role.md b/.claude/skills/team-coordinate-v2/roles/coordinator/role.md index e22c00c6..0416bb1b 100644 --- a/.claude/skills/team-coordinate-v2/roles/coordinator/role.md +++ b/.claude/skills/team-coordinate-v2/roles/coordinator/role.md @@ -182,13 +182,15 @@ Regardless of complexity score or role count, coordinator MUST: 4. **Call TeamCreate** with team name derived from session ID -5. **Read `specs/role-spec-template.md`** + task-analysis.json +5. **Read `specs/role-spec-template.md`** for Behavioral Traits + Reference Patterns 6. **For each role in task-analysis.json#roles**: - - Fill role-spec template with: - - YAML frontmatter: role, prefix, inner_loop, subagents, message_types - - Phase 2-4 content from responsibility type reference sections in template - - Task-specific instructions from task description + - Fill YAML frontmatter: role, prefix, inner_loop, subagents, message_types + - **Compose Phase 2-4 content** (NOT copy from template): + - Phase 2: Derive input sources and context loading steps from **task description + upstream dependencies** + - Phase 3: Describe **execution goal** (WHAT to achieve) from task description — do NOT prescribe specific subagent or tool + - Phase 4: Combine **Behavioral Traits** (from template) + **output_type** (from task analysis) to compose verification steps + - Reference Patterns may guide phase structure, but task description determines specific content - Write generated role-spec to `/role-specs/.md` 7. **Register roles** in team-session.json#roles (with `role_spec` path instead of `role_file`) diff --git a/.claude/skills/team-coordinate-v2/specs/role-spec-template.md b/.claude/skills/team-coordinate-v2/specs/role-spec-template.md index f9c27816..bbb29b0a 100644 --- a/.claude/skills/team-coordinate-v2/specs/role-spec-template.md +++ b/.claude/skills/team-coordinate-v2/specs/role-spec-template.md @@ -63,233 +63,74 @@ message_types: | `` notation | Use angle brackets for variable substitution | | Reference subagents by name | team-worker resolves invocation from its delegation templates | -## Phase 2-4 Content by Responsibility Type +## Behavioral Traits -Select the matching section based on `responsibility_type` from task analysis. +All dynamically generated role-specs MUST embed these traits into Phase 4. Coordinator copies this section verbatim into every generated role-spec as a Phase 4 appendix. -### orchestration +**Design principle**: Constrain behavioral characteristics (accuracy, feedback, quality gates), NOT specific actions (which tool, which subagent, which path). Tasks are diverse — the coordinator composes task-specific Phase 2-3 instructions, while these traits ensure execution quality regardless of task type. -**Phase 2: Context Assessment** +### Accuracy — outputs must be verifiable -``` -| Input | Source | Required | -|-------|--------|----------| -| Task description | From TaskGet | Yes | -| Shared memory | /shared-memory.json | No | -| Prior artifacts | /artifacts/ | No | -| Wisdom | /wisdom/ | No | +- Files claimed as **created** → Read to confirm file exists and has content +- Files claimed as **modified** → Read to confirm content actually changed +- Analysis claimed as **complete** → artifact file exists in `/artifacts/` -Loading steps: -1. Extract session path from task description -2. Read shared-memory.json for cross-role context -3. Read prior artifacts (if any from upstream tasks) -4. Load wisdom files for accumulated knowledge -5. Optionally call explore subagent for codebase context -``` +### Feedback Contract — completion report must include evidence -**Phase 3: Subagent Execution** +Phase 4 must produce a verification summary with these fields: -``` -Delegate to appropriate subagent based on task: +| Field | When Required | Content | +|-------|---------------|---------| +| `files_produced` | New files created | Path list | +| `files_modified` | Existing files changed | Path + before/after line count | +| `artifacts_written` | Always | Paths in `/artifacts/` | +| `verification_method` | Always | How verified: Read confirm / syntax check / diff | -Task({ - subagent_type: "general-purpose", - run_in_background: false, - description: " for ", - prompt: "## Task - - - - Session: - ## Context - - ## Expected Output - Write artifact to: /artifacts/.md - Return JSON summary: { artifact_path, summary, key_decisions[], warnings[] }" -}) -``` +### Quality Gate — verify before reporting complete -**Phase 4: Result Aggregation** +- Phase 4 MUST verify Phase 3's **actual output** (not planned output) +- Verification fails → retry Phase 3 (max 2 retries) +- Still fails → report `partial_completion` with details, NOT `completed` +- Update `shared-memory.json` with key findings after verification passes -``` -1. Verify subagent output artifact exists -2. Read artifact, validate structure/completeness -3. Update shared-memory.json with key findings -4. Write insights to wisdom/ files -``` +### Error Protocol -### code-gen (docs) +- Primary approach fails → try alternative (different subagent / different tool) +- 2 retries exhausted → escalate to coordinator with failure details +- NEVER: skip verification and report completed -**Phase 2: Load Prior Context** +--- -``` -| Input | Source | Required | -|-------|--------|----------| -| Task description | From TaskGet | Yes | -| Prior artifacts | /artifacts/ from upstream | Conditional | -| Shared memory | /shared-memory.json | No | +## Reference Patterns -Loading steps: -1. Extract session path from task description -2. Read upstream artifacts -3. Read shared-memory.json for cross-role context -``` +Coordinator MAY reference these patterns when composing Phase 2-4 content for a role-spec. These are **structural guidance, not mandatory templates**. The task description determines specific behavior — patterns only suggest common phase structures. -**Phase 3: Document Generation** +### Research / Exploration -``` -Task({ - subagent_type: "universal-executor", - run_in_background: false, - description: "Generate for ", - prompt: "## Task - - Generate: - - Session: - ## Prior Context - - ## Expected Output - Write document to: /artifacts/.md - Return JSON: { artifact_path, summary, key_decisions[], warnings[] }" -}) -``` +- Phase 2: Define exploration scope + load prior knowledge from shared-memory and wisdom +- Phase 3: Explore via subagents, direct tool calls, or codebase search — approach chosen by agent +- Phase 4: Verify findings documented (Behavioral Traits) + update shared-memory -**Phase 4: Structure Validation** +### Document / Content -``` -1. Verify document artifact exists -2. Check document has expected sections -3. Validate no placeholder text remains -4. Update shared-memory.json with document metadata -``` +- Phase 2: Load upstream artifacts + read target files (if modifying existing docs) +- Phase 3: Create new documents OR modify existing documents — determined by task, not template +- Phase 4: Verify documents exist with expected content (Behavioral Traits) + update shared-memory -### code-gen (code) +### Code Implementation -**Phase 2: Load Plan/Specs** +- Phase 2: Load design/spec artifacts from upstream +- Phase 3: Implement code changes — subagent choice and approach determined by task complexity +- Phase 4: Syntax check + file verification (Behavioral Traits) + update shared-memory -``` -| Input | Source | Required | -|-------|--------|----------| -| Task description | From TaskGet | Yes | -| Plan/design artifacts | /artifacts/ | Conditional | -| Shared memory | /shared-memory.json | No | +### Analysis / Audit -Loading steps: -1. Extract session path from task description -2. Read plan/design artifacts from upstream -3. Load shared-memory.json for implementation context -``` +- Phase 2: Load analysis targets (artifacts or source files) +- Phase 3: Multi-dimension analysis — perspectives and depth determined by task +- Phase 4: Verify report exists + severity classification (Behavioral Traits) + update shared-memory -**Phase 3: Code Implementation** +### Validation / Testing -``` -Task({ - subagent_type: "code-developer", - run_in_background: false, - description: "Implement ", - prompt: "## Task - - - - Session: - ## Plan/Design Context - - ## Expected Output - Implement code changes. - Write summary to: /artifacts/implementation-summary.md - Return JSON: { artifact_path, summary, files_changed[], warnings[] }" -}) -``` - -**Phase 4: Syntax Validation** - -``` -1. Run syntax check (tsc --noEmit or equivalent) -2. Verify all planned files exist -3. If validation fails -> attempt auto-fix (max 2 attempts) -4. Write implementation summary to artifacts/ -``` - -### read-only - -**Phase 2: Target Loading** - -``` -| Input | Source | Required | -|-------|--------|----------| -| Task description | From TaskGet | Yes | -| Target artifacts/files | From task description or upstream | Yes | -| Shared memory | /shared-memory.json | No | - -Loading steps: -1. Extract session path and target files from task description -2. Read target artifacts or source files for analysis -3. Load shared-memory.json for context -``` - -**Phase 3: Multi-Dimension Analysis** - -``` -Task({ - subagent_type: "general-purpose", - run_in_background: false, - description: "Analyze for ", - prompt: "## Task - - Analyze: - - Dimensions: - - Session: - ## Target Content - - ## Expected Output - Write report to: /artifacts/analysis-report.md - Return JSON: { artifact_path, summary, findings[], severity_counts }" -}) -``` - -**Phase 4: Severity Classification** - -``` -1. Verify analysis report exists -2. Classify findings by severity (Critical/High/Medium/Low) -3. Update shared-memory.json with key findings -4. Write issues to wisdom/issues.md -``` - -### validation - -**Phase 2: Environment Detection** - -``` -| Input | Source | Required | -|-------|--------|----------| -| Task description | From TaskGet | Yes | -| Implementation artifacts | Upstream code changes | Yes | - -Loading steps: -1. Detect test framework from project files -2. Get changed files from implementation -3. Identify test command and coverage tool -``` - -**Phase 3: Test-Fix Cycle** - -``` -Task({ - subagent_type: "test-fix-agent", - run_in_background: false, - description: "Test-fix for ", - prompt: "## Task - - Run tests and fix failures - - Session: - - Max iterations: 5 - ## Changed Files - - ## Expected Output - Write report to: /artifacts/test-report.md - Return JSON: { artifact_path, pass_rate, coverage, remaining_failures[] }" -}) -``` - -**Phase 4: Result Analysis** - -``` -1. Check pass rate >= 95% -2. Check coverage meets threshold -3. Generate test report with pass/fail counts -4. Update shared-memory.json with test results -``` +- Phase 2: Detect test framework + identify changed files from upstream +- Phase 3: Run test-fix cycle — iteration count and strategy determined by task +- Phase 4: Verify pass rate + coverage (Behavioral Traits) + update shared-memory diff --git a/ccw/frontend/index.html b/ccw/frontend/index.html index c8c33d00..9ba91a2c 100644 --- a/ccw/frontend/index.html +++ b/ccw/frontend/index.html @@ -2,7 +2,7 @@ - + diff --git a/ccw/frontend/public/favicon.svg b/ccw/frontend/public/favicon.svg new file mode 100644 index 00000000..b6dc7196 --- /dev/null +++ b/ccw/frontend/public/favicon.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/ccw/frontend/src/components/icons/CCWLogo.tsx b/ccw/frontend/src/components/icons/CCWLogo.tsx new file mode 100644 index 00000000..618d4596 --- /dev/null +++ b/ccw/frontend/src/components/icons/CCWLogo.tsx @@ -0,0 +1,75 @@ +// ======================================== +// CCW Logo Component +// ======================================== +// Line-style logo for Claude Code Workflow + +import { cn } from '@/lib/utils'; + +interface CCWLogoProps { + /** Size of the icon */ + size?: number; + /** Additional class names */ + className?: string; + /** Whether to show the status dot */ + showDot?: boolean; +} + +/** + * Line-style CCW logo component + * Features three horizontal lines with a status dot that follows theme color + */ +export function CCWLogo({ size = 24, className, showDot = true }: CCWLogoProps) { + return ( + + {/* Three horizontal lines - line style */} + + + + + {/* Status dot - follows theme color via currentColor */} + {showDot && ( + + )} + + ); +} + +export default CCWLogo; diff --git a/ccw/frontend/src/components/icons/index.ts b/ccw/frontend/src/components/icons/index.ts new file mode 100644 index 00000000..364ddfc3 --- /dev/null +++ b/ccw/frontend/src/components/icons/index.ts @@ -0,0 +1,6 @@ +// ======================================== +// Icons Index +// ======================================== +// Custom icon components for CCW Dashboard + +export { CCWLogo } from './CCWLogo'; diff --git a/ccw/frontend/src/components/layout/Header.tsx b/ccw/frontend/src/components/layout/Header.tsx index 2523fc19..fbc1fada 100644 --- a/ccw/frontend/src/components/layout/Header.tsx +++ b/ccw/frontend/src/components/layout/Header.tsx @@ -7,7 +7,6 @@ import { useCallback } from 'react'; import { Link } from 'react-router-dom'; import { useIntl } from 'react-intl'; import { - Workflow, Moon, Sun, RefreshCw, @@ -19,6 +18,7 @@ import { Monitor, SquareTerminal, } from 'lucide-react'; +import { CCWLogo } from '@/components/icons/CCWLogo'; import { cn } from '@/lib/utils'; import { Button } from '@/components/ui/Button'; import { Badge } from '@/components/ui/Badge'; @@ -72,11 +72,11 @@ export function Header({ {/* Logo / Brand */} - - {formatMessage({ id: 'navigation.header.brand' })} - {formatMessage({ id: 'navigation.header.brandShort' })} + + {formatMessage({ id: 'navigation.header.brand' })} + {formatMessage({ id: 'navigation.header.brandShort' })} {/* A2UI Quick Action Button */} diff --git a/ccw/frontend/src/index.css b/ccw/frontend/src/index.css index 05dcc459..125cb095 100644 --- a/ccw/frontend/src/index.css +++ b/ccw/frontend/src/index.css @@ -866,3 +866,10 @@ [data-theme^="dark"] .markdown-preview .prose code { background-color: hsl(220 25% 18%); } + +/* =========================== + CCW Logo + =========================== */ +.ccw-logo { + color: hsl(var(--accent)); +} diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index d9b02dd6..a6a5baea 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -1,4 +1,5 @@ import { defineConfig } from 'vitepress' +import { withMermaid } from 'vitepress-plugin-mermaid' const repoName = process.env.GITHUB_REPOSITORY?.split('/')[1] const isUserOrOrgSite = Boolean(repoName && repoName.endsWith('.github.io')) @@ -7,8 +8,8 @@ const base = process.env.CCW_DOCS_BASE || (process.env.GITHUB_ACTIONS && repoName && !isUserOrOrgSite ? `/${repoName}/` : '/') -export default defineConfig({ - title: 'CCW Documentation', +export default withMermaid(defineConfig({ + title: 'Claude Code Workflow Documentation', description: 'Claude Code Workspace - Advanced AI-Powered Development Environment', lang: 'zh-CN', base, @@ -83,7 +84,7 @@ export default defineConfig({ text: '📖 指南', collapsible: false, items: [ - { text: 'What is Claude_dms3', link: '/guide/ch01-what-is-claude-dms3' }, + { text: 'What is Claude Code Workflow', link: '/guide/ch01-what-is-claude-dms3' }, { text: 'Getting Started', link: '/guide/ch02-getting-started' }, { text: 'Core Concepts', link: '/guide/ch03-core-concepts' }, { text: 'Workflow Basics', link: '/guide/ch04-workflow-basics' }, @@ -287,7 +288,7 @@ export default defineConfig({ zh: { label: '简体中文', lang: 'zh-CN', - title: 'CCW 文档', + title: 'Claude Code Workflow 文档', description: 'Claude Code Workspace - 高级 AI 驱动开发环境', themeConfig: { outline: { @@ -312,7 +313,7 @@ export default defineConfig({ text: '📖 指南', collapsible: false, items: [ - { text: 'Claude_dms3 是什么', link: '/zh/guide/ch01-what-is-claude-dms3' }, + { text: 'Claude Code Workflow 是什么', link: '/zh/guide/ch01-what-is-claude-dms3' }, { text: '快速开始', link: '/zh/guide/ch02-getting-started' }, { text: '核心概念', link: '/zh/guide/ch03-core-concepts' }, { text: '工作流基础', link: '/zh/guide/ch04-workflow-basics' }, @@ -425,4 +426,4 @@ export default defineConfig({ } } } -}) +})) diff --git a/docs/.vitepress/theme/components/AgentOrchestration.vue b/docs/.vitepress/theme/components/AgentOrchestration.vue index 08f65f16..088b2c48 100644 --- a/docs/.vitepress/theme/components/AgentOrchestration.vue +++ b/docs/.vitepress/theme/components/AgentOrchestration.vue @@ -1,46 +1,89 @@ @@ -129,6 +149,7 @@ onMounted(() => { border-radius: 16px; box-shadow: var(--vp-shadow-sm); transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + cursor: default; } .workflow-node:hover { @@ -138,8 +159,14 @@ onMounted(() => { } .node-icon { - font-size: 2rem; + width: 40px; + height: 40px; + border-radius: 10px; + display: flex; + align-items: center; + justify-content: center; margin-bottom: 0.5rem; + color: var(--vp-c-text-2); } .node-label { @@ -155,10 +182,19 @@ onMounted(() => { color: white; } +.workflow-node.coordinator .node-icon { + color: white; +} + .workflow-node.coordinator .node-label { color: white; } +.workflow-node.analyst .node-icon { color: #3B82F6; } +.workflow-node.writer .node-icon { color: #8B5CF6; } +.workflow-node.executor .node-icon { color: #10B981; } +.workflow-node.tester .node-icon { color: #F59E0B; } + .workflow-legend { display: flex; justify-content: center; @@ -186,15 +222,23 @@ onMounted(() => { .dot.impl { background: var(--vp-c-secondary-500); } .dot.test { background: var(--vp-c-accent-400); } +@media (prefers-reduced-motion: reduce) { + *, *::before, *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } +} + @media (max-width: 768px) { .workflow-animation { padding: 1.5rem; } - + .workflow-container { flex-direction: column; } - + .workflow-nodes { width: 100%; } diff --git a/docs/.vitepress/theme/composables/useDynamicIcon.ts b/docs/.vitepress/theme/composables/useDynamicIcon.ts new file mode 100644 index 00000000..c8dafb73 --- /dev/null +++ b/docs/.vitepress/theme/composables/useDynamicIcon.ts @@ -0,0 +1,209 @@ +import { onMounted, onBeforeUnmount, ref, computed } from 'vue' + +/** + * Theme color mappings for light and dark modes + */ +export const THEME_COLORS = { + blue: { + light: '#3B82F6', + dark: '#60A5FA' + }, + green: { + light: '#22C55E', + dark: '#34D399' + }, + orange: { + light: '#F59E0B', + dark: '#FBBF24' + }, + purple: { + light: '#8B5CF6', + dark: '#A78BFA' + } +} as const + +export type ThemeName = keyof typeof THEME_COLORS + +/** + * Status dot colors (always green) + */ +export const STATUS_COLORS = { + light: '#22C55E', + dark: '#34D399' +} as const + +const STORAGE_KEY_THEME = 'ccw-theme' +const STORAGE_KEY_COLOR_MODE = 'ccw-color-mode' + +/** + * Get current theme from localStorage or default + */ +export function getCurrentTheme(): ThemeName { + const saved = localStorage.getItem(STORAGE_KEY_THEME) + if (saved && saved in THEME_COLORS) { + return saved as ThemeName + } + return 'blue' +} + +/** + * Check if dark mode is active + */ +export function isDarkMode(): boolean { + const mode = localStorage.getItem(STORAGE_KEY_COLOR_MODE) || 'auto' + const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches + return mode === 'dark' || (mode === 'auto' && prefersDark) +} + +/** + * Get the appropriate theme color based on current mode + */ +export function getThemeColor(theme: ThemeName, isDark: boolean): string { + return isDark ? THEME_COLORS[theme].dark : THEME_COLORS[theme].light +} + +/** + * Get the appropriate status color based on current mode + */ +export function getStatusColor(isDark: boolean): string { + return isDark ? STATUS_COLORS.dark : STATUS_COLORS.light +} + +/** + * Generate favicon SVG with dynamic colors (line style) + */ +export function generateFaviconSvg(theme: ThemeName, isDark: boolean): string { + const lineColor = getThemeColor(theme, isDark) + const dotColor = getThemeColor(theme, isDark) // Dot follows theme color + + return ` + + + + +` +} + +/** + * Update the favicon with current theme colors + */ +export function updateFavicon(theme: ThemeName, isDark: boolean): void { + const svg = generateFaviconSvg(theme, isDark) + const dataUrl = `data:image/svg+xml,${encodeURIComponent(svg)}` + + // Update existing favicon or create new one + let link = document.querySelector('link[rel="icon"]') + if (!link) { + link = document.createElement('link') + link.rel = 'icon' + link.type = 'image/svg+xml' + document.head.appendChild(link) + } + link.href = dataUrl + + // Also update apple-touch-icon if exists + const appleLink = document.querySelector('link[rel="apple-touch-icon"]') + if (appleLink) { + appleLink.href = dataUrl + } +} + +/** + * Composable for dynamic icon management + */ +export function useDynamicIcon() { + const currentTheme = ref(getCurrentTheme()) + const darkMode = ref(isDarkMode()) + + let mediaQuery: MediaQueryList | null = null + let mutationObserver: MutationObserver | null = null + let storageHandler: ((e: StorageEvent) => void) | null = null + + /** + * Update favicon based on current state + */ + const updateIcon = () => { + updateFavicon(currentTheme.value, darkMode.value) + } + + /** + * Check and update dark mode state + */ + const checkDarkMode = () => { + darkMode.value = isDarkMode() + updateIcon() + } + + /** + * Check and update theme state + */ + const checkTheme = () => { + currentTheme.value = getCurrentTheme() + updateIcon() + } + + onMounted(() => { + // Initial update + updateIcon() + + // Listen for system color scheme changes + mediaQuery = window.matchMedia('(prefers-color-scheme: dark)') + mediaQuery.addEventListener('change', checkDarkMode) + + // Listen for storage changes (cross-tab sync) + storageHandler = (e: StorageEvent) => { + if (e.key === STORAGE_KEY_THEME) checkTheme() + if (e.key === STORAGE_KEY_COLOR_MODE) checkDarkMode() + } + window.addEventListener('storage', storageHandler) + + // Observe DOM changes for dark class and data-theme attribute + mutationObserver = new MutationObserver((mutations) => { + for (const mutation of mutations) { + if (mutation.type === 'attributes') { + const target = mutation.target as HTMLElement + if (mutation.attributeName === 'class') { + const isDark = target.classList.contains('dark') + if (isDark !== darkMode.value) { + darkMode.value = isDark + updateIcon() + } + } + if (mutation.attributeName === 'data-theme') { + const theme = target.getAttribute('data-theme') as ThemeName + if (theme && theme !== currentTheme.value && theme in THEME_COLORS) { + currentTheme.value = theme + updateIcon() + } + } + } + } + }) + + mutationObserver.observe(document.documentElement, { + attributes: true, + attributeFilter: ['class', 'data-theme'] + }) + }) + + onBeforeUnmount(() => { + if (mediaQuery) { + mediaQuery.removeEventListener('change', checkDarkMode) + } + if (storageHandler) { + window.removeEventListener('storage', storageHandler) + } + if (mutationObserver) { + mutationObserver.disconnect() + } + }) + + return { + currentTheme, + darkMode, + updateIcon, + themeColors: THEME_COLORS + } +} + +export type UseDynamicIconReturn = ReturnType diff --git a/docs/.vitepress/theme/layouts/Layout.vue b/docs/.vitepress/theme/layouts/Layout.vue index b883e942..9f9f5ae3 100644 --- a/docs/.vitepress/theme/layouts/Layout.vue +++ b/docs/.vitepress/theme/layouts/Layout.vue @@ -1,6 +1,7 @@