sillyspec 3.9.0 → 3.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/sillyspec-archive/SKILL.md +17 -0
- package/.claude/skills/sillyspec-auto/SKILL.md +78 -0
- package/.claude/skills/sillyspec-brainstorm/SKILL.md +17 -0
- package/{templates/commit.md → .claude/skills/sillyspec-commit/SKILL.md} +32 -47
- package/.claude/skills/sillyspec-continue/SKILL.md +45 -0
- package/.claude/skills/sillyspec-doctor/SKILL.md +27 -0
- package/.claude/skills/sillyspec-execute/SKILL.md +17 -0
- package/.claude/skills/sillyspec-explore/SKILL.md +96 -0
- package/.claude/skills/sillyspec-export/SKILL.md +53 -0
- package/.claude/skills/sillyspec-init/SKILL.md +170 -0
- package/.claude/skills/sillyspec-plan/SKILL.md +52 -0
- package/.claude/skills/sillyspec-propose/SKILL.md +17 -0
- package/.claude/skills/sillyspec-quick/SKILL.md +17 -0
- package/.claude/skills/sillyspec-resume/SKILL.md +111 -0
- package/.claude/skills/sillyspec-scan/SKILL.md +17 -0
- package/.claude/skills/sillyspec-state/SKILL.md +54 -0
- package/.claude/skills/sillyspec-status/SKILL.md +17 -0
- package/.claude/skills/sillyspec-verify/SKILL.md +17 -0
- package/.claude/skills/sillyspec-workspace/SKILL.md +149 -0
- package/.sillyspec/changes/archive/2026-04-08-derive-state/design.md +97 -0
- package/.sillyspec/changes/archive/2026-04-08-derive-state/plan.md +51 -0
- package/.sillyspec/changes/archive/2026-04-08-derive-state/proposal.md +29 -0
- package/.sillyspec/changes/archive/2026-04-08-derive-state/requirements.md +34 -0
- package/.sillyspec/changes/archive/2026-04-08-derive-state/tasks.md +13 -0
- package/.sillyspec/changes/archive/2026-04-08-derive-state/verify-result.md +43 -0
- package/.sillyspec/changes/auto-mode/design.md +50 -0
- package/.sillyspec/changes/auto-mode/proposal.md +19 -0
- package/.sillyspec/changes/auto-mode/requirements.md +21 -0
- package/.sillyspec/changes/auto-mode/tasks.md +7 -0
- package/.sillyspec/changes/brainstorm-archive/2026-04-05-unified-docs-design.md +199 -0
- package/.sillyspec/changes/dashboard/design.md.braindraft +206 -0
- package/.sillyspec/changes/run-command-design/design.md +1230 -0
- package/.sillyspec/changes/unified-docs-design/design.md +199 -0
- package/.sillyspec/docs/sillyspec/scan/.gitkeep +0 -0
- package/.sillyspec/knowledge/INDEX.md +8 -0
- package/.sillyspec/knowledge/uncategorized.md +3 -0
- package/.sillyspec/projects/sillyspec.yaml +3 -0
- package/README.md +12 -5
- package/package.json +7 -9
- package/packages/dashboard/dist/assets/index-CntACGUN.css +1 -0
- package/packages/dashboard/dist/assets/index-RsLVPAy7.js +7446 -0
- package/packages/dashboard/dist/index.html +3 -2
- package/packages/dashboard/package-lock.json +226 -6
- package/packages/dashboard/package.json +8 -5
- package/packages/dashboard/public/logo.jpg +0 -0
- package/packages/dashboard/server/executor.js +1 -1
- package/packages/dashboard/server/index.js +336 -113
- package/packages/dashboard/server/parser.js +333 -29
- package/packages/dashboard/server/watcher.js +203 -131
- package/packages/dashboard/src/App.vue +187 -11
- package/packages/dashboard/src/components/ActionBar.vue +26 -42
- package/packages/dashboard/src/components/CommandPalette.vue +40 -65
- package/packages/dashboard/src/components/DetailPanel.vue +68 -53
- package/packages/dashboard/src/components/DocPreview.vue +160 -0
- package/packages/dashboard/src/components/DocTree.vue +58 -0
- package/packages/dashboard/src/components/LogStream.vue +13 -33
- package/packages/dashboard/src/components/PipelineStage.vue +8 -8
- package/packages/dashboard/src/components/PipelineView.vue +80 -45
- package/packages/dashboard/src/components/ProjectList.vue +103 -45
- package/packages/dashboard/src/components/ProjectOverview.vue +178 -0
- package/packages/dashboard/src/components/StageBadge.vue +13 -13
- package/packages/dashboard/src/components/StepCard.vue +15 -15
- package/packages/dashboard/src/components/detail/DocsDetail.vue +48 -0
- package/packages/dashboard/src/components/detail/GitDetail.vue +61 -0
- package/packages/dashboard/src/components/detail/TechDetail.vue +43 -0
- package/packages/dashboard/src/composables/useDashboard.js +20 -6
- package/packages/dashboard/src/composables/useKeyboard.js +6 -4
- package/packages/dashboard/src/main.js +4 -1
- package/packages/dashboard/src/style.css +17 -17
- package/src/index.js +134 -22
- package/src/init.js +83 -228
- package/src/migrate.js +117 -0
- package/src/progress.js +459 -0
- package/src/run.js +624 -0
- package/src/setup.js +2 -72
- package/src/stages/archive.js +54 -0
- package/src/stages/brainstorm.js +239 -0
- package/src/stages/doctor.js +303 -0
- package/src/stages/execute.js +262 -0
- package/src/stages/index.js +26 -0
- package/src/stages/plan.js +282 -0
- package/src/stages/propose.js +115 -0
- package/src/stages/quick.js +64 -0
- package/src/stages/scan.js +141 -0
- package/src/stages/status.js +65 -0
- package/src/stages/verify.js +135 -0
- package/dist/steps/brainstorm/01-load-context.md +0 -30
- package/dist/steps/brainstorm/02-reuse-check.md +0 -6
- package/dist/steps/brainstorm/03-prototype-analysis.md +0 -11
- package/dist/steps/brainstorm/04-module-split.md +0 -23
- package/dist/steps/brainstorm/05-dialog-explore.md +0 -8
- package/dist/steps/brainstorm/06-propose-approaches.md +0 -3
- package/dist/steps/brainstorm/07-present-design.md +0 -3
- package/dist/steps/brainstorm/08-write-design.md +0 -21
- package/dist/steps/brainstorm/09-self-review.md +0 -15
- package/dist/steps/brainstorm/10-user-confirm.md +0 -3
- package/dist/steps/brainstorm/11-output-spec.md +0 -7
- package/dist/steps/brainstorm/manifest.yaml +0 -26
- package/dist/steps/execute/01-load-context.md +0 -41
- package/dist/steps/execute/02-scan-conventions.md +0 -47
- package/dist/steps/execute/03-skill-mcp.md +0 -19
- package/dist/steps/execute/04-assign-task.md +0 -22
- package/dist/steps/execute/04b-prompt-template.md +0 -54
- package/dist/steps/execute/05-write-test.md +0 -7
- package/dist/steps/execute/06-write-code.md +0 -8
- package/dist/steps/execute/07-run-test.md +0 -26
- package/dist/steps/execute/08-fix-issues.md +0 -28
- package/dist/steps/execute/09-next-task.md +0 -33
- package/dist/steps/execute/manifest.yaml +0 -28
- package/dist/steps/plan/01-load-context.md +0 -22
- package/dist/steps/plan/02-anchor-confirm.md +0 -1
- package/dist/steps/plan/03-expand-tasks.md +0 -33
- package/dist/steps/plan/04-mark-order.md +0 -15
- package/dist/steps/plan/05-e2e-planning.md +0 -17
- package/dist/steps/plan/06-self-check.md +0 -16
- package/dist/steps/plan/07-save.md +0 -1
- package/dist/steps/plan/manifest.yaml +0 -18
- package/dist/steps/scan/01-env-detect.md +0 -51
- package/dist/steps/scan/02-tech-stack.md +0 -16
- package/dist/steps/scan/03-conventions.md +0 -16
- package/dist/steps/scan/04-structure.md +0 -19
- package/dist/steps/scan/05-quality.md +0 -18
- package/dist/steps/scan/06-complete.md +0 -49
- package/dist/steps/scan/manifest.yaml +0 -16
- package/dist/steps/verify/01-load-specs.md +0 -28
- package/dist/steps/verify/02-check-tasks.md +0 -1
- package/dist/steps/verify/03-check-design.md +0 -6
- package/dist/steps/verify/04-run-tests.md +0 -7
- package/dist/steps/verify/05-e2e-tests.md +0 -27
- package/dist/steps/verify/05b-e2e-fix.md +0 -33
- package/dist/steps/verify/06-code-quality.md +0 -25
- package/dist/steps/verify/07-lint-check.md +0 -27
- package/dist/steps/verify/08-output-report.md +0 -14
- package/dist/steps/verify/manifest.yaml +0 -22
- package/docs/.vitepress/config.mts +0 -45
- package/docs/.vitepress/dist/404.html +0 -25
- package/docs/.vitepress/dist/assets/app.YytxICdd.js +0 -1
- package/docs/.vitepress/dist/assets/chunks/framework.Czhw_PXq.js +0 -19
- package/docs/.vitepress/dist/assets/chunks/theme.DusTRZQk.js +0 -1
- package/docs/.vitepress/dist/assets/index.md.C3VCvtQA.js +0 -1
- package/docs/.vitepress/dist/assets/index.md.C3VCvtQA.lean.js +0 -1
- package/docs/.vitepress/dist/assets/inter-italic-cyrillic-ext.r48I6akx.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-cyrillic.By2_1cv3.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-greek-ext.1u6EdAuj.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-greek.DJ8dCoTZ.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-latin-ext.CN1xVJS-.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-latin.C2AdPX0b.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-vietnamese.BSbpV94h.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-cyrillic-ext.BBPuwvHQ.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-cyrillic.C5lxZ8CY.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-greek-ext.CqjqNYQ-.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-greek.BBVDIX6e.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-latin-ext.4ZJIpNVo.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-latin.Di8DUHzh.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-vietnamese.BjW4sHH5.woff2 +0 -0
- package/docs/.vitepress/dist/assets/sillyspec_commands.md.CXFFsj08.js +0 -15
- package/docs/.vitepress/dist/assets/sillyspec_commands.md.CXFFsj08.lean.js +0 -1
- package/docs/.vitepress/dist/assets/sillyspec_dashboard.md.BuPXHqjX.js +0 -4
- package/docs/.vitepress/dist/assets/sillyspec_dashboard.md.BuPXHqjX.lean.js +0 -1
- package/docs/.vitepress/dist/assets/sillyspec_file-io.md.Cz3x7llx.js +0 -1
- package/docs/.vitepress/dist/assets/sillyspec_file-io.md.Cz3x7llx.lean.js +0 -1
- package/docs/.vitepress/dist/assets/sillyspec_getting-started.md.ClcvV8k3.js +0 -4
- package/docs/.vitepress/dist/assets/sillyspec_getting-started.md.ClcvV8k3.lean.js +0 -1
- package/docs/.vitepress/dist/assets/sillyspec_install.md.CKuR2tiT.js +0 -5
- package/docs/.vitepress/dist/assets/sillyspec_install.md.CKuR2tiT.lean.js +0 -1
- package/docs/.vitepress/dist/assets/sillyspec_lifecycle.md.DY293cR1.js +0 -28
- package/docs/.vitepress/dist/assets/sillyspec_lifecycle.md.DY293cR1.lean.js +0 -1
- package/docs/.vitepress/dist/assets/sillyspec_structure.md.sVYS4zPs.js +0 -30
- package/docs/.vitepress/dist/assets/sillyspec_structure.md.sVYS4zPs.lean.js +0 -1
- package/docs/.vitepress/dist/assets/style.DFTx90Kk.css +0 -1
- package/docs/.vitepress/dist/hashmap.json +0 -1
- package/docs/.vitepress/dist/index.html +0 -28
- package/docs/.vitepress/dist/sillyspec/commands.html +0 -42
- package/docs/.vitepress/dist/sillyspec/dashboard.html +0 -31
- package/docs/.vitepress/dist/sillyspec/file-io.html +0 -28
- package/docs/.vitepress/dist/sillyspec/getting-started.html +0 -31
- package/docs/.vitepress/dist/sillyspec/install.html +0 -32
- package/docs/.vitepress/dist/sillyspec/lifecycle.html +0 -55
- package/docs/.vitepress/dist/sillyspec/structure.html +0 -57
- package/docs/.vitepress/dist/vp-icons.css +0 -1
- package/docs/index.md +0 -34
- package/docs/sillyspec/commands.md +0 -218
- package/docs/sillyspec/dashboard.md +0 -51
- package/docs/sillyspec/file-io.md +0 -34
- package/docs/sillyspec/getting-started.md +0 -61
- package/docs/sillyspec/install.md +0 -51
- package/docs/sillyspec/lifecycle.md +0 -146
- package/docs/sillyspec/structure.md +0 -62
- package/packages/dashboard/dist/assets/index-Bh-GPjKY.css +0 -1
- package/packages/dashboard/dist/assets/index-CrCn5Gg6.js +0 -17
- package/src/step.js +0 -543
- package/templates/archive.md +0 -120
- package/templates/brainstorm.md +0 -170
- package/templates/continue.md +0 -32
- package/templates/execute.md +0 -304
- package/templates/explore.md +0 -59
- package/templates/export.md +0 -21
- package/templates/init.md +0 -61
- package/templates/plan.md +0 -146
- package/templates/quick.md +0 -135
- package/templates/scan-quick.md +0 -49
- package/templates/scan.md +0 -156
- package/templates/skills/playwright-e2e/SKILL.md +0 -340
- package/templates/status.md +0 -75
- package/templates/verify.md +0 -236
- package/templates/workspace-sync.md +0 -99
- package/templates/workspace.md +0 -70
- /package/.sillyspec/{specs → changes/brainstorm-archive}/2026-04-05-dashboard-design.md +0 -0
- /package/{docs/.vitepress/dist/logo.jpg → logo.jpg} +0 -0
- /package/{docs/.vitepress → packages/dashboard}/dist/favicon.jpg +0 -0
- /package/{docs/public → packages/dashboard/dist}/logo.jpg +0 -0
- /package/{docs → packages/dashboard}/public/favicon.jpg +0 -0
|
@@ -1,51 +1,31 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="flex flex-col h-full" style="background: #
|
|
2
|
+
<div class="flex flex-col h-full" style="background: #F5F5F7;">
|
|
3
3
|
<!-- Header -->
|
|
4
|
-
<div class="px-3 py-2 flex items-center gap-2" style="border-bottom: 1px solid #
|
|
5
|
-
<input
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
style="background: #141416; border: 1px solid #1F1F22; color: #8B8B8E;"
|
|
11
|
-
/>
|
|
12
|
-
<button
|
|
13
|
-
@click="clearLogs"
|
|
14
|
-
class="px-2 py-1 text-[10px] rounded-sm transition-colors duration-100"
|
|
15
|
-
style="color: #525252; border: 1px solid #1F1F22;"
|
|
16
|
-
>
|
|
17
|
-
clear
|
|
18
|
-
</button>
|
|
19
|
-
<button
|
|
20
|
-
@click="toggleAutoScroll"
|
|
21
|
-
class="px-2 py-1 text-[10px] rounded-sm font-mono-log transition-colors duration-100"
|
|
22
|
-
:style="{
|
|
23
|
-
color: autoScroll ? '#FBBF24' : '#525252',
|
|
24
|
-
background: autoScroll ? 'rgba(251,191,36,0.08)' : 'transparent',
|
|
25
|
-
border: autoScroll ? '1px solid rgba(251,191,36,0.2)' : '1px solid #1F1F22'
|
|
26
|
-
}"
|
|
27
|
-
>
|
|
28
|
-
{{ autoScroll ? 'auto' : 'pause' }}
|
|
29
|
-
</button>
|
|
4
|
+
<div class="px-3 py-2 flex items-center gap-2" style="border-bottom: 1px solid #F0F0F3;">
|
|
5
|
+
<n-input v-model:value="searchQuery" size="tiny" placeholder="过滤日志..." clearable style="flex: 1;" />
|
|
6
|
+
<n-button size="tiny" @click="clearLogs">清空</n-button>
|
|
7
|
+
<n-button size="tiny" :type="autoScroll ? 'primary' : 'default'" @click="toggleAutoScroll">
|
|
8
|
+
{{ autoScroll ? '自动' : '暂停' }}
|
|
9
|
+
</n-button>
|
|
30
10
|
</div>
|
|
31
11
|
|
|
32
12
|
<!-- Log output -->
|
|
33
|
-
<div ref="logContainer" class="flex-1 overflow-y-auto px-2 py-1.5 font-mono-log text-[10px]" style="background: #
|
|
13
|
+
<div ref="logContainer" class="flex-1 overflow-y-auto px-2 py-1.5 font-mono-log text-[10px]" style="background: #F0F0F3;" @scroll="handleScroll">
|
|
34
14
|
<div v-if="filteredLogs.length === 0" class="flex items-center justify-center h-full">
|
|
35
|
-
<span class="font-mono-log" style="color: #
|
|
15
|
+
<span class="font-mono-log" style="color: #E5E5EA;">{{ logs.length === 0 ? '暂无日志' : '无匹配' }}</span>
|
|
36
16
|
</div>
|
|
37
17
|
<div v-else class="space-y-px">
|
|
38
18
|
<div v-for="log in filteredLogs" :key="log.id" class="px-1.5 py-px rounded-sm" :style="{ background: logBg(log.type) }">
|
|
39
|
-
<span style="color: #
|
|
19
|
+
<span style="color: #D1D1D6;" class="select-none">[{{ formatTime(log.timestamp) }}]</span>
|
|
40
20
|
<span :style="{ color: logColor(log.type) }">{{ escapeHtml(log.content) }}</span>
|
|
41
21
|
</div>
|
|
42
22
|
</div>
|
|
43
23
|
</div>
|
|
44
24
|
|
|
45
25
|
<!-- Footer -->
|
|
46
|
-
<div class="px-3 py-1 flex items-center justify-between text-[9px] font-mono-log" style="border-top: 1px solid #
|
|
26
|
+
<div class="px-3 py-1 flex items-center justify-between text-[9px] font-mono-log" style="border-top: 1px solid #F0F0F3; background: #F5F5F7; color: #D1D1D6;">
|
|
47
27
|
<span>{{ filteredLogs.length }}/{{ logs.length }}</span>
|
|
48
|
-
<span v-if="!autoScroll" style="color: #
|
|
28
|
+
<span v-if="!autoScroll" style="color: #EA580C;">已暂停</span>
|
|
49
29
|
</div>
|
|
50
30
|
</div>
|
|
51
31
|
</template>
|
|
@@ -69,7 +49,7 @@ const filteredLogs = computed(() => {
|
|
|
69
49
|
function formatTime(ts) { if (!ts) return ''; const d = new Date(ts); return d.toLocaleTimeString('zh-CN', { hour12: false }) }
|
|
70
50
|
function escapeHtml(t) { if (!t) return ''; return t.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>') }
|
|
71
51
|
function logBg(t) { return t === 'error' ? 'rgba(239,68,68,0.05)' : t === 'warn' ? 'rgba(251,146,60,0.05)' : 'transparent' }
|
|
72
|
-
function logColor(t) { return t === 'error' ? '#
|
|
52
|
+
function logColor(t) { return t === 'error' ? '#DC2626' : t === 'warn' ? '#EA580C' : t === 'debug' ? '#6B7280' : '#636366' }
|
|
73
53
|
function clearLogs() { emit('clear') }
|
|
74
54
|
function toggleAutoScroll() { autoScroll.value = !autoScroll.value; if (autoScroll.value) scrollToBottom() }
|
|
75
55
|
function handleScroll() {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
<div class="flex items-center gap-2.5 mb-3">
|
|
17
17
|
<span
|
|
18
18
|
class="text-[12px] font-semibold font-[JetBrains_Mono,monospace] tracking-tight"
|
|
19
|
-
:style="{ color: isActive ? '#
|
|
19
|
+
:style="{ color: isActive ? '#D97706' : '#1C1C1E' }"
|
|
20
20
|
>
|
|
21
21
|
{{ title }}
|
|
22
22
|
</span>
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
|
|
26
26
|
<!-- Steps -->
|
|
27
27
|
<div class="space-y-1">
|
|
28
|
-
<div v-if="steps.length === 0" class="text-[11px] italic py-1" style="color: #
|
|
28
|
+
<div v-if="steps.length === 0" class="text-[11px] italic py-1" style="color: #6B7280;">
|
|
29
29
|
No steps yet
|
|
30
30
|
</div>
|
|
31
31
|
<StepCard
|
|
@@ -58,13 +58,13 @@ const props = defineProps({
|
|
|
58
58
|
const emit = defineEmits(['select-step'])
|
|
59
59
|
|
|
60
60
|
const nodeStyle = computed(() => {
|
|
61
|
-
if (props.isActive) return { background: '#
|
|
61
|
+
if (props.isActive) return { background: '#D97706', boxShadow: '0 0 8px rgba(251,191,36,0.4)' }
|
|
62
62
|
const colors = {
|
|
63
|
-
'completed': '#
|
|
64
|
-
'in-progress': '#
|
|
65
|
-
'blocked': '#
|
|
66
|
-
'failed': '#
|
|
67
|
-
'pending': '#
|
|
63
|
+
'completed': '#16A34A',
|
|
64
|
+
'in-progress': '#D97706',
|
|
65
|
+
'blocked': '#EA580C',
|
|
66
|
+
'failed': '#DC2626',
|
|
67
|
+
'pending': '#E5E5EA'
|
|
68
68
|
}
|
|
69
69
|
return { background: colors[props.status] || colors.pending }
|
|
70
70
|
})
|
|
@@ -1,53 +1,73 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="flex flex-col h-full" style="background: #
|
|
3
|
-
<!-- Header -->
|
|
4
|
-
<div class="px-6 pt-
|
|
5
|
-
<
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
2
|
+
<div class="flex flex-col h-full" style="background: #F5F5F7;">
|
|
3
|
+
<!-- Header with Tabs -->
|
|
4
|
+
<div class="px-6 pt-4 pb-0" style="border-bottom: 1px solid #F0F0F3;">
|
|
5
|
+
<div class="flex items-center gap-6">
|
|
6
|
+
<h2 class="text-[11px] font-semibold uppercase tracking-[0.2em] font-[JetBrains_Mono,monospace]" style="color: #6B7280;">
|
|
7
|
+
{{ project?.name || '项目' }}
|
|
8
|
+
</h2>
|
|
9
|
+
<n-tabs :value="activeTab" type="segment" size="small" @update:value="$emit('switch-tab', $event)" style="max-width: 200px;">
|
|
10
|
+
<n-tab name="pipeline">流水线</n-tab>
|
|
11
|
+
<n-tab name="docs">文档</n-tab>
|
|
12
|
+
</n-tabs>
|
|
13
|
+
</div>
|
|
11
14
|
</div>
|
|
12
15
|
|
|
13
|
-
<!--
|
|
14
|
-
<div class="flex
|
|
16
|
+
<!-- Pipeline Tab -->
|
|
17
|
+
<div v-if="activeTab === 'pipeline'" class="flex flex-col flex-1 overflow-hidden">
|
|
18
|
+
<div v-if="project && currentStage" class="px-6 pt-4 pb-2">
|
|
19
|
+
<p class="text-[12px] font-[JetBrains_Mono,monospace]" style="color: #636366;">
|
|
20
|
+
<span style="color: #D97706;">{{ currentStage }}</span>
|
|
21
|
+
</p>
|
|
22
|
+
</div>
|
|
23
|
+
|
|
15
24
|
<!-- Empty state -->
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
<n-empty v-if="!project || !project.state" description="选择一个项目查看流水线" style="margin: auto;" />
|
|
26
|
+
|
|
27
|
+
<!-- Stages -->
|
|
28
|
+
<div v-else class="flex-1 overflow-y-auto px-6 pb-5 space-y-5">
|
|
29
|
+
<PipelineStage name="brainstorm" title="头脑风暴" :steps="getStageSteps('brainstorm')" :status="getStageStatus('brainstorm')" :is-active="currentStage === 'brainstorm'" :active-step="activeStep" @select-step="handleSelectStep" />
|
|
30
|
+
<div v-if="hasStage('plan')" class="flex items-center pl-[3px]"><div class="w-px h-4" style="background: #F0F0F3;" /></div>
|
|
31
|
+
<PipelineStage name="plan" title="规划" :steps="getStageSteps('plan')" :status="getStageStatus('plan')" :is-active="currentStage === 'plan'" :active-step="activeStep" @select-step="handleSelectStep" />
|
|
32
|
+
<div v-if="hasStage('execute')" class="flex items-center pl-[3px]"><div class="w-px h-4" style="background: #F0F0F3;" /></div>
|
|
33
|
+
<PipelineStage name="execute" title="执行" :steps="getStageSteps('execute')" :status="getStageStatus('execute')" :is-active="currentStage === 'execute'" :active-step="activeStep" @select-step="handleSelectStep" />
|
|
34
|
+
<div v-if="hasStage('verify')" class="flex items-center pl-[3px]"><div class="w-px h-4" style="background: #F0F0F3;" /></div>
|
|
35
|
+
<PipelineStage name="verify" title="验证" :steps="getStageSteps('verify')" :status="getStageStatus('verify')" :is-active="currentStage === 'verify'" :active-step="activeStep" @select-step="handleSelectStep" />
|
|
25
36
|
</div>
|
|
26
37
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
<div
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
<div
|
|
34
|
-
|
|
38
|
+
<!-- Activity Log -->
|
|
39
|
+
<div v-if="project?.state?.progress" style="border-top: 1px solid #F0F0F3; background: rgba(0,0,0,0.03);">
|
|
40
|
+
<div class="px-6 py-2.5 flex items-center justify-between">
|
|
41
|
+
<div class="text-[9px] font-semibold uppercase tracking-[0.25em] font-[JetBrains_Mono,monospace]" style="color: #6B7280;">活动日志</div>
|
|
42
|
+
<div class="text-[10px] font-mono-log" style="color: #D1D1D6;">{{ activityLogs.length }}</div>
|
|
43
|
+
</div>
|
|
44
|
+
<div class="px-6 pb-3 max-h-32 overflow-y-auto">
|
|
45
|
+
<n-timeline size="small">
|
|
46
|
+
<n-timeline-item
|
|
47
|
+
v-for="(log, i) in activityLogs"
|
|
48
|
+
:key="i"
|
|
49
|
+
:type="log.status === 'completed' ? 'success' : 'warning'"
|
|
50
|
+
>
|
|
51
|
+
<template #default>
|
|
52
|
+
<span class="text-[11px]" style="color: #636366;">{{ log.description }}</span>
|
|
53
|
+
</template>
|
|
54
|
+
<template #time>
|
|
55
|
+
<span class="text-[10px] font-mono-log" style="color: #D1D1D6;">{{ log.time }}</span>
|
|
56
|
+
</template>
|
|
57
|
+
</n-timeline-item>
|
|
58
|
+
</n-timeline>
|
|
59
|
+
<div v-if="activityLogs.length === 0" class="text-[10px] py-1" style="color: #D1D1D6;">暂无活动记录</div>
|
|
60
|
+
</div>
|
|
35
61
|
</div>
|
|
36
62
|
</div>
|
|
37
63
|
|
|
38
|
-
<!--
|
|
39
|
-
<div v-if="
|
|
40
|
-
<div class="
|
|
41
|
-
<
|
|
42
|
-
<div class="text-[10px] font-mono-log" style="color: #3A3A3D;">{{ activityLogs.length }}</div>
|
|
64
|
+
<!-- Docs Tab -->
|
|
65
|
+
<div v-if="activeTab === 'docs'" class="flex-1 flex overflow-hidden">
|
|
66
|
+
<div class="w-[200px] flex-shrink-0 overflow-hidden" style="border-right: 1px solid #F0F0F3;">
|
|
67
|
+
<DocTree :groups="docs.groups" :selected-file="selectedDocFile" @select-file="$emit('select-doc-file', $event)" />
|
|
43
68
|
</div>
|
|
44
|
-
<div class="
|
|
45
|
-
<
|
|
46
|
-
<span class="w-10 font-mono-log flex-shrink-0" style="color: #3A3A3D;">{{ log.time }}</span>
|
|
47
|
-
<span :style="{ color: log.status === 'completed' ? '#34D399' : '#FBBF24' }">›</span>
|
|
48
|
-
<span style="color: #8B8B8E;">{{ log.description }}</span>
|
|
49
|
-
</div>
|
|
50
|
-
<div v-if="activityLogs.length === 0" class="text-[10px] py-1" style="color: #3A3A3D;">No activity</div>
|
|
69
|
+
<div class="flex-1 overflow-hidden">
|
|
70
|
+
<DocPreview :content="docContent" :loading="docLoading" />
|
|
51
71
|
</div>
|
|
52
72
|
</div>
|
|
53
73
|
</div>
|
|
@@ -56,24 +76,39 @@
|
|
|
56
76
|
<script setup>
|
|
57
77
|
import { computed } from 'vue'
|
|
58
78
|
import PipelineStage from './PipelineStage.vue'
|
|
79
|
+
import DocTree from './DocTree.vue'
|
|
80
|
+
import DocPreview from './DocPreview.vue'
|
|
59
81
|
|
|
60
82
|
const props = defineProps({
|
|
61
83
|
project: { type: Object, default: null },
|
|
62
|
-
activeStep: { type: Object, default: null }
|
|
84
|
+
activeStep: { type: Object, default: null },
|
|
85
|
+
activeTab: { type: String, default: 'pipeline' },
|
|
86
|
+
docs: { type: Object, default: () => ({ groups: [] }) },
|
|
87
|
+
selectedDocFile: { type: Object, default: null },
|
|
88
|
+
docContent: { type: String, default: '' },
|
|
89
|
+
docLoading: { type: Boolean, default: false }
|
|
63
90
|
})
|
|
64
91
|
|
|
65
|
-
const emit = defineEmits(['select-step'])
|
|
92
|
+
const emit = defineEmits(['select-step', 'switch-tab', 'select-doc-file'])
|
|
66
93
|
|
|
67
|
-
const currentStage = computed(() => props.project?.state?.currentStage || '
|
|
94
|
+
const currentStage = computed(() => props.project?.state?.currentStage || '')
|
|
68
95
|
const progress = computed(() => props.project?.state?.progress || {})
|
|
69
96
|
const stages = computed(() => progress.value.stages || {})
|
|
70
97
|
|
|
98
|
+
const stageNameMap = {
|
|
99
|
+
brainstorm: '头脑风暴',
|
|
100
|
+
plan: '规划',
|
|
101
|
+
execute: '执行',
|
|
102
|
+
verify: '验证'
|
|
103
|
+
}
|
|
104
|
+
|
|
71
105
|
const activityLogs = computed(() => {
|
|
72
106
|
if (!props.project?.state) return []
|
|
73
107
|
const logs = []
|
|
74
108
|
for (const [name, data] of Object.entries(progress.value.stages || {})) {
|
|
75
|
-
|
|
76
|
-
|
|
109
|
+
const label = stageNameMap[name] || name
|
|
110
|
+
if (data.status === 'completed') logs.push({ time: data.completedAt ? formatTime(data.completedAt) : '--:--', status: 'completed', description: `${label} 已完成` })
|
|
111
|
+
else if (data.status === 'in-progress') logs.push({ time: '--:--', status: 'in-progress', description: `${label} 运行中` })
|
|
77
112
|
}
|
|
78
113
|
return logs.reverse()
|
|
79
114
|
})
|
|
@@ -3,84 +3,109 @@
|
|
|
3
3
|
<!-- Header -->
|
|
4
4
|
<div class="relative z-10 px-5 pt-5 pb-4">
|
|
5
5
|
<div class="flex items-center gap-3">
|
|
6
|
-
<div class="w-8 h-8 rounded-md flex items-center justify-center" style="background: linear-gradient(135deg, #
|
|
6
|
+
<div class="w-8 h-8 rounded-md flex items-center justify-center" style="background: linear-gradient(135deg, #D97706 0%, #F59E0B 100%); clip-path: polygon(0 0, 100% 0, 85% 100%, 15% 100%);">
|
|
7
7
|
<span class="text-[10px] font-bold text-black font-[JetBrains_Mono,monospace]">S</span>
|
|
8
8
|
</div>
|
|
9
|
-
<
|
|
10
|
-
|
|
9
|
+
<img src="/logo.jpg" style="width:28px;height:28px;border-radius:6px;margin-right:8px;">
|
|
10
|
+
<div class="flex-1">
|
|
11
|
+
<h1 class="text-[13px] font-semibold tracking-tight font-[JetBrains_Mono,monospace]" style="color: #1C1C1E;">
|
|
11
12
|
SillySpec
|
|
12
13
|
</h1>
|
|
13
|
-
<p class="text-[10px] tracking-widest uppercase" style="color: #
|
|
14
|
+
<p class="text-[10px] tracking-widest uppercase" style="color: #6B7280;">控制台</p>
|
|
14
15
|
</div>
|
|
16
|
+
<!-- Scan paths gear button -->
|
|
17
|
+
<n-button quaternary size="tiny" @click="showScanPanel = !showScanPanel" :type="showScanPanel ? 'primary' : 'default'">
|
|
18
|
+
<template #icon>
|
|
19
|
+
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
20
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.066 2.573c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.573 1.066c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.066-2.573c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/>
|
|
21
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
|
|
22
|
+
</svg>
|
|
23
|
+
</template>
|
|
24
|
+
</n-button>
|
|
15
25
|
</div>
|
|
26
|
+
|
|
27
|
+
<!-- Scan paths panel (inline) -->
|
|
28
|
+
<Transition name="slide">
|
|
29
|
+
<div v-if="showScanPanel" class="mt-3 rounded-md p-3" style="background: #FFFFFF; border: 1px solid #F0F0F3;">
|
|
30
|
+
<div class="text-[10px] font-semibold uppercase tracking-[0.15em] mb-2 font-[JetBrains_Mono,monospace]" style="color: #6B7280;">扫描路径</div>
|
|
31
|
+
|
|
32
|
+
<div v-if="scanPaths.length === 0" class="text-[10px] py-1" style="color: #D1D1D6;">暂无自定义路径</div>
|
|
33
|
+
<div v-else class="space-y-1 mb-2">
|
|
34
|
+
<div v-for="(p, i) in scanPaths" :key="i" class="flex items-center gap-2 text-[10px] group">
|
|
35
|
+
<span class="flex-1 truncate font-mono-log" style="color: #636366;">{{ p }}</span>
|
|
36
|
+
<n-button quaternary size="tiny" type="error" @click="removePath(p)">✕</n-button>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<!-- Add path -->
|
|
41
|
+
<div v-if="showAddInput" class="flex items-center gap-2">
|
|
42
|
+
<n-input v-model:value="newPath" size="tiny" placeholder="输入目录路径..." @keydown.enter="addPath" @keydown.escape="showAddInput = false" ref="pathInput" />
|
|
43
|
+
<n-button size="tiny" type="primary" @click="addPath">添加</n-button>
|
|
44
|
+
</div>
|
|
45
|
+
<n-button v-else size="tiny" dashed @click="showAddInput = true">+ 添加目录</n-button>
|
|
46
|
+
</div>
|
|
47
|
+
</Transition>
|
|
16
48
|
</div>
|
|
17
49
|
|
|
18
50
|
<!-- Divider -->
|
|
19
|
-
<div class="mx-4 h-px" style="background: linear-gradient(90deg, transparent, #
|
|
51
|
+
<div class="mx-4 h-px" style="background: linear-gradient(90deg, transparent, #E5E5EA, transparent);"></div>
|
|
20
52
|
|
|
21
53
|
<!-- Projects List -->
|
|
22
54
|
<div class="flex-1 overflow-y-auto py-3 relative z-10">
|
|
23
55
|
<!-- Loading skeleton -->
|
|
24
56
|
<div v-if="isLoading" class="px-4 space-y-2">
|
|
25
|
-
<
|
|
26
|
-
<
|
|
27
|
-
<
|
|
28
|
-
</
|
|
29
|
-
<p class="text-center text-[10px] mt-4 font-[JetBrains_Mono,monospace]" style="color: #
|
|
30
|
-
|
|
57
|
+
<n-card v-for="i in 4" :key="i" size="small" :bordered="false">
|
|
58
|
+
<n-skeleton text :width="80" size="small" />
|
|
59
|
+
<n-skeleton text :width="140" size="small" style="margin-top: 6px;" />
|
|
60
|
+
</n-card>
|
|
61
|
+
<p class="text-center text-[10px] mt-4 font-[JetBrains_Mono,monospace]" style="color: #6B7280;">
|
|
62
|
+
正在扫描项目...
|
|
31
63
|
</p>
|
|
32
64
|
</div>
|
|
33
65
|
|
|
34
66
|
<!-- Empty state -->
|
|
35
|
-
<
|
|
36
|
-
<div class="w-10 h-10 mx-auto mb-3 rounded-full flex items-center justify-center" style="border: 1px dashed #2A2A2D;">
|
|
37
|
-
<svg class="w-4 h-4" style="color: #525252;" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
38
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>
|
|
39
|
-
</svg>
|
|
40
|
-
</div>
|
|
41
|
-
<p class="text-[11px]" style="color: #8B8B8E;">No projects found</p>
|
|
42
|
-
<p class="text-[10px] mt-1" style="color: #525252;">Looking for .sillyspec dirs</p>
|
|
43
|
-
</div>
|
|
67
|
+
<n-empty v-else-if="projects.length === 0" description="未发现 SillySpec 项目" style="padding: 48px 0;" />
|
|
44
68
|
|
|
45
69
|
<!-- Projects -->
|
|
46
70
|
<div v-else class="px-3 space-y-0.5">
|
|
47
71
|
<div
|
|
48
72
|
v-for="project in projects"
|
|
49
|
-
:key="project.
|
|
50
|
-
:class="[
|
|
51
|
-
'relative rounded-md cursor-pointer transition-all duration-150 overflow-hidden group',
|
|
52
|
-
]"
|
|
73
|
+
:key="project.path"
|
|
74
|
+
:class="['relative rounded-md cursor-pointer transition-all duration-150 overflow-hidden group']"
|
|
53
75
|
:style="{
|
|
54
|
-
background: isActive(project) ? 'rgba(
|
|
55
|
-
borderLeft: isActive(project) ? '2px solid #
|
|
76
|
+
background: isActive(project) ? 'rgba(217,119,6,0.06)' : 'transparent',
|
|
77
|
+
borderLeft: isActive(project) ? '2px solid #D97706' : '2px solid transparent',
|
|
56
78
|
}"
|
|
57
|
-
@mouseenter="$event.currentTarget.style.background = isActive(project) ? 'rgba(
|
|
58
|
-
@mouseleave="$event.currentTarget.style.background = isActive(project) ? 'rgba(
|
|
79
|
+
@mouseenter="$event.currentTarget.style.background = isActive(project) ? 'rgba(217,119,6,0.08)' : 'rgba(255,255,255,0.02)'"
|
|
80
|
+
@mouseleave="$event.currentTarget.style.background = isActive(project) ? 'rgba(217,119,6,0.06)' : 'transparent'"
|
|
59
81
|
@click="$emit('select', project)"
|
|
60
82
|
>
|
|
61
83
|
<div class="px-3 py-2.5">
|
|
62
84
|
<div class="flex items-center justify-between gap-2">
|
|
63
85
|
<div class="flex-1 min-w-0">
|
|
64
86
|
<h3
|
|
65
|
-
|
|
66
|
-
:style="{ color: isActive(project) ? '#
|
|
87
|
+
class="text-[12px] font-medium truncate transition-colors duration-150 font-[JetBrains_Mono,monospace]"
|
|
88
|
+
:style="{ color: isActive(project) ? '#D97706' : '#1C1C1E' }"
|
|
67
89
|
>
|
|
68
90
|
{{ project.name }}
|
|
69
91
|
</h3>
|
|
70
|
-
<p class="text-[10px] mt-0.5 truncate font-mono-log" style="color: #
|
|
92
|
+
<p class="text-[10px] mt-0.5 truncate font-mono-log" style="color: #6B7280;">
|
|
71
93
|
{{ project.path }}
|
|
72
94
|
</p>
|
|
73
95
|
</div>
|
|
74
|
-
<
|
|
96
|
+
<n-tag
|
|
75
97
|
v-if="project.state?.currentStage"
|
|
76
|
-
:
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
98
|
+
:type="statusTagType(getProjectStatus(project))"
|
|
99
|
+
size="small"
|
|
100
|
+
:bordered="false"
|
|
101
|
+
round
|
|
102
|
+
>
|
|
103
|
+
{{ stageLabel(project) }}
|
|
104
|
+
</n-tag>
|
|
80
105
|
</div>
|
|
81
106
|
|
|
82
107
|
<!-- Progress -->
|
|
83
|
-
<div v-if="project.state?.progress" class="mt-2 h-[2px] rounded-full overflow-hidden" style="background: #
|
|
108
|
+
<div v-if="project.state?.progress" class="mt-2 h-[2px] rounded-full overflow-hidden" style="background: #FFFFFF;">
|
|
84
109
|
<div
|
|
85
110
|
class="h-full rounded-full transition-all duration-500 progress-gradient"
|
|
86
111
|
:style="{ width: getProjectProgress(project) + '%' }"
|
|
@@ -92,29 +117,43 @@
|
|
|
92
117
|
</div>
|
|
93
118
|
|
|
94
119
|
<!-- Footer -->
|
|
95
|
-
<div class="relative z-10 px-4 py-2.5" style="border-top: 1px solid #
|
|
120
|
+
<div class="relative z-10 px-4 py-2.5" style="border-top: 1px solid #F0F0F3;">
|
|
96
121
|
<div class="flex items-center justify-between">
|
|
97
|
-
<span class="text-[10px] font-[JetBrains_Mono,monospace]" style="color: #
|
|
98
|
-
<kbd class="text-[9px] px-1.5 py-0.5 rounded font-mono-log" style="color: #
|
|
122
|
+
<span class="text-[10px] font-[JetBrains_Mono,monospace]" style="color: #6B7280;">{{ projects.length }} 个项目</span>
|
|
123
|
+
<kbd class="text-[9px] px-1.5 py-0.5 rounded font-mono-log" style="color: #6B7280; background: #FFFFFF; border: 1px solid #E5E5EA;">⌘K</kbd>
|
|
99
124
|
</div>
|
|
100
125
|
</div>
|
|
101
126
|
</div>
|
|
102
127
|
</template>
|
|
103
128
|
|
|
104
129
|
<script setup>
|
|
105
|
-
import {
|
|
106
|
-
import StageBadge from './StageBadge.vue'
|
|
130
|
+
import { ref, nextTick, watch } from 'vue'
|
|
107
131
|
|
|
108
132
|
const props = defineProps({
|
|
109
133
|
projects: { type: Array, default: () => [] },
|
|
110
134
|
activeProject: { type: Object, default: null },
|
|
111
|
-
isLoading: { type: Boolean, default: false }
|
|
135
|
+
isLoading: { type: Boolean, default: false },
|
|
136
|
+
scanPaths: { type: Array, default: () => [] }
|
|
112
137
|
})
|
|
113
138
|
|
|
114
|
-
const emit = defineEmits(['select'])
|
|
139
|
+
const emit = defineEmits(['select', 'scan:add-path', 'scan:remove-path'])
|
|
140
|
+
|
|
141
|
+
const showScanPanel = ref(false)
|
|
142
|
+
const showAddInput = ref(false)
|
|
143
|
+
const newPath = ref('')
|
|
144
|
+
const pathInput = ref(null)
|
|
145
|
+
|
|
146
|
+
watch(showAddInput, (v) => {
|
|
147
|
+
if (v) nextTick(() => { pathInput.value?.focus() })
|
|
148
|
+
})
|
|
115
149
|
|
|
116
150
|
function isActive(project) {
|
|
117
|
-
return props.activeProject?.
|
|
151
|
+
return props.activeProject?.path === project.path
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function statusTagType(status) {
|
|
155
|
+
const map = { 'in-progress': 'warning', 'completed': 'success', 'failed': 'error', 'blocked': 'warning' }
|
|
156
|
+
return map[status] || 'default'
|
|
118
157
|
}
|
|
119
158
|
|
|
120
159
|
function getProjectStatus(project) {
|
|
@@ -149,4 +188,23 @@ function getProjectProgress(project) {
|
|
|
149
188
|
}
|
|
150
189
|
return total === 0 ? 0 : Math.round((done / total) * 100)
|
|
151
190
|
}
|
|
191
|
+
|
|
192
|
+
function addPath() {
|
|
193
|
+
const p = newPath.value.trim()
|
|
194
|
+
if (p) {
|
|
195
|
+
emit('scan:add-path', p)
|
|
196
|
+
newPath.value = ''
|
|
197
|
+
showAddInput.value = false
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function removePath(p) {
|
|
202
|
+
emit('scan:remove-path', p)
|
|
203
|
+
}
|
|
152
204
|
</script>
|
|
205
|
+
|
|
206
|
+
<style scoped>
|
|
207
|
+
.slide-enter-active, .slide-leave-active { transition: all 200ms ease; }
|
|
208
|
+
.slide-enter-from, .slide-leave-to { opacity: 0; max-height: 0; overflow: hidden; }
|
|
209
|
+
.slide-enter-to, .slide-leave-from { max-height: 300px; }
|
|
210
|
+
</style>
|