sillyspec 3.20.3 → 3.20.5

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.
Files changed (225) hide show
  1. package/.claude/skills/sillyspec-archive/SKILL.md +21 -17
  2. package/.claude/skills/sillyspec-auto/SKILL.md +83 -77
  3. package/.claude/skills/sillyspec-brainstorm/SKILL.md +44 -17
  4. package/.claude/skills/sillyspec-commit/SKILL.md +106 -105
  5. package/.claude/skills/sillyspec-continue/SKILL.md +45 -44
  6. package/.claude/skills/sillyspec-doctor/SKILL.md +31 -22
  7. package/.claude/skills/sillyspec-execute/SKILL.md +30 -17
  8. package/.claude/skills/sillyspec-explore/SKILL.md +109 -96
  9. package/.claude/skills/sillyspec-knowledge/SKILL.md +270 -0
  10. package/.claude/skills/sillyspec-plan/SKILL.md +21 -52
  11. package/.claude/skills/sillyspec-propose/SKILL.md +21 -17
  12. package/.claude/skills/sillyspec-quick/SKILL.md +21 -17
  13. package/.claude/skills/sillyspec-resume/SKILL.md +68 -111
  14. package/.claude/skills/sillyspec-scan/SKILL.md +21 -17
  15. package/.claude/skills/sillyspec-state/SKILL.md +54 -54
  16. package/.claude/skills/sillyspec-status/SKILL.md +21 -17
  17. package/.claude/skills/sillyspec-verify/SKILL.md +21 -17
  18. package/.claude/skills/sillyspec-workspace/SKILL.md +157 -149
  19. package/.husky/pre-push +13 -0
  20. package/CLAUDE.md +18 -0
  21. package/README.md +198 -186
  22. package/SKILL.md +90 -87
  23. package/bin/sillyspec.js +2 -2
  24. package/docs/brainstorm-plan-contract.md +64 -0
  25. package/docs/plan-execute-contract.md +123 -0
  26. package/docs/platform-scan-protocol.md +298 -0
  27. package/docs/revision-mode.md +115 -0
  28. package/docs/sillyspec/file-lifecycle/known-implementation-gaps.md +99 -0
  29. package/docs/sillyspec/file-lifecycle/platform-workflows-sync.md +218 -0
  30. package/docs/sillyspec/file-lifecycle/stage-artifacts.md +167 -0
  31. package/docs/sillyspec/file-lifecycle/storage-and-state.md +148 -0
  32. package/docs/sillyspec/file-lifecycle/worktree-and-guard.md +211 -0
  33. package/docs/sillyspec/file-lifecycle.md +131 -0
  34. package/docs/workflow-contract-regression.md +106 -0
  35. package/docs/worktree-isolation.md +252 -0
  36. package/package.json +40 -34
  37. package/packages/dashboard/dist/assets/{index-D1EVTLmc.js → index-Bq_Z2hne.js} +7446 -7446
  38. package/packages/dashboard/dist/assets/index-O2W5RV4z.css +1 -0
  39. package/packages/dashboard/dist/index.html +16 -16
  40. package/packages/dashboard/dist/prototype-dashboard.html +836 -0
  41. package/packages/dashboard/dist/prototype-overview.html +256 -0
  42. package/packages/dashboard/index.html +15 -15
  43. package/packages/dashboard/package-lock.json +2384 -2384
  44. package/packages/dashboard/package.json +25 -25
  45. package/packages/dashboard/public/prototype-dashboard.html +836 -0
  46. package/packages/dashboard/public/prototype-overview.html +256 -0
  47. package/packages/dashboard/server/executor.js +86 -86
  48. package/packages/dashboard/server/index.js +588 -508
  49. package/packages/dashboard/server/parser.js +526 -458
  50. package/packages/dashboard/server/watcher.js +350 -342
  51. package/packages/dashboard/src/App.vue +558 -325
  52. package/packages/dashboard/src/components/ActionBar.vue +93 -84
  53. package/packages/dashboard/src/components/CommandPalette.vue +96 -92
  54. package/packages/dashboard/src/components/DetailPanel.vue +137 -137
  55. package/packages/dashboard/src/components/DocPreview.vue +105 -8
  56. package/packages/dashboard/src/components/DocTree.vue +75 -19
  57. package/packages/dashboard/src/components/HResizeHandle.vue +48 -0
  58. package/packages/dashboard/src/components/LogStream.vue +65 -65
  59. package/packages/dashboard/src/components/PipelineStage.vue +95 -75
  60. package/packages/dashboard/src/components/PipelineView.vue +156 -129
  61. package/packages/dashboard/src/components/ProjectCard.vue +187 -0
  62. package/packages/dashboard/src/components/ProjectList.vue +210 -210
  63. package/packages/dashboard/src/components/ProjectOverview.vue +113 -139
  64. package/packages/dashboard/src/components/StageBadge.vue +67 -53
  65. package/packages/dashboard/src/components/StepCard.vue +94 -89
  66. package/packages/dashboard/src/components/VResizeHandle.vue +61 -0
  67. package/packages/dashboard/src/components/detail/DocsDetail.vue +48 -48
  68. package/packages/dashboard/src/components/detail/GitDetail.vue +61 -61
  69. package/packages/dashboard/src/components/detail/TechDetail.vue +43 -43
  70. package/packages/dashboard/src/composables/useDashboard.js +192 -164
  71. package/packages/dashboard/src/composables/useKeyboard.js +119 -119
  72. package/packages/dashboard/src/composables/useLayout.js +131 -0
  73. package/packages/dashboard/src/composables/useWebSocket.js +129 -129
  74. package/packages/dashboard/src/main.js +8 -8
  75. package/packages/dashboard/src/style.css +132 -132
  76. package/packages/dashboard/vite.config.js +18 -18
  77. package/src/brainstorm-postcheck.js +158 -0
  78. package/src/change-list.js +52 -0
  79. package/src/change-risk-profile.js +352 -0
  80. package/src/classify-change.js +73 -0
  81. package/src/constants.js +70 -0
  82. package/src/contract-matrix.js +278 -0
  83. package/src/db.js +201 -0
  84. package/src/endpoint-extractor.js +315 -0
  85. package/src/hooks/claude-pre-tool-use.cjs +125 -0
  86. package/src/hooks/worktree-guard.js +761 -0
  87. package/src/index.js +922 -252
  88. package/src/init.js +470 -375
  89. package/src/knowledge-match.js +130 -0
  90. package/src/migrate.js +117 -117
  91. package/src/modules.js +482 -0
  92. package/src/progress.js +1734 -508
  93. package/src/run.js +3465 -652
  94. package/src/scan-postcheck.js +387 -0
  95. package/src/setup.js +398 -460
  96. package/src/stage-contract.js +700 -0
  97. package/src/stages/archive.js +160 -54
  98. package/src/stages/brainstorm-auto.js +229 -0
  99. package/src/stages/brainstorm.js +645 -239
  100. package/src/stages/doctor.js +365 -312
  101. package/src/stages/execute.js +634 -264
  102. package/src/stages/explore.js +34 -0
  103. package/src/stages/index.js +29 -35
  104. package/src/stages/knowledge.js +498 -0
  105. package/src/stages/plan-postcheck.js +518 -0
  106. package/src/stages/plan.js +582 -279
  107. package/src/stages/propose.js +174 -115
  108. package/src/stages/quick.js +102 -63
  109. package/src/stages/scan.js +558 -141
  110. package/src/stages/status.js +65 -65
  111. package/src/stages/verify.js +322 -135
  112. package/src/sync.js +497 -0
  113. package/src/task-review.js +346 -0
  114. package/src/workflow.js +785 -0
  115. package/src/worktree-apply.js +549 -0
  116. package/src/worktree-deps.js +185 -0
  117. package/src/worktree.js +982 -0
  118. package/templates/workflows/archive-impact.yaml +79 -0
  119. package/templates/workflows/scan-docs.yaml +132 -0
  120. package/test/brainstorm-plan-contract.test.mjs +273 -0
  121. package/test/check-syntax.mjs +26 -0
  122. package/test/cli-top-level-aliases.test.mjs +174 -0
  123. package/test/contract-artifacts.test.mjs +323 -0
  124. package/test/decision-supersede.test.mjs +277 -0
  125. package/test/knowledge-match.test.mjs +231 -0
  126. package/test/plan-execute-contract.test.mjs +357 -0
  127. package/test/plan-optimization.test.mjs +572 -0
  128. package/test/platform-artifacts.test.mjs +190 -0
  129. package/test/platform-failure-samples.test.mjs +199 -0
  130. package/test/platform-recovery-chain.test.mjs +179 -0
  131. package/test/platform-recovery.test.mjs +167 -0
  132. package/test/platform-scan-p0.test.mjs +175 -0
  133. package/test/revision-v1.test.mjs +1145 -0
  134. package/test/run-sanitize-project-name.test.mjs +51 -0
  135. package/test/run-scan-postcheck-fail.test.mjs +64 -0
  136. package/test/run-scan-project-parse.test.mjs +200 -0
  137. package/test/run-tests.mjs +48 -0
  138. package/test/runtime-cleanup-keeps-worktree.test.mjs +107 -0
  139. package/test/scan-docs-yaml-placeholders.test.mjs +84 -0
  140. package/test/scan-knowledge.test.mjs +175 -0
  141. package/test/scan-paths.test.mjs +68 -0
  142. package/test/scan-postcheck-project-priority.test.mjs +85 -0
  143. package/test/scan-postcheck.test.mjs +197 -0
  144. package/test/scan-workflow-anyfailed-block.test.mjs +52 -0
  145. package/test/spec-dir.test.mjs +206 -0
  146. package/test/stage-contract-failed-post-check.test.mjs +102 -0
  147. package/test/stage-contract.test.mjs +299 -0
  148. package/test/stage-definitions.test.mjs +39 -0
  149. package/test/wait-gates.test.mjs +501 -0
  150. package/test/workflow-spec-base.test.mjs +142 -0
  151. package/test/worktree-deps-provision.test.mjs +148 -0
  152. package/test/worktree-guard.test.mjs +136 -0
  153. package/test/worktree-native-overlay.test.mjs +188 -0
  154. package/.sillyspec/changes/archive/2026-04-08-derive-state/design.md +0 -97
  155. package/.sillyspec/changes/archive/2026-04-08-derive-state/plan.md +0 -51
  156. package/.sillyspec/changes/archive/2026-04-08-derive-state/proposal.md +0 -29
  157. package/.sillyspec/changes/archive/2026-04-08-derive-state/requirements.md +0 -34
  158. package/.sillyspec/changes/archive/2026-04-08-derive-state/tasks.md +0 -13
  159. package/.sillyspec/changes/archive/2026-04-08-derive-state/verify-result.md +0 -43
  160. package/.sillyspec/changes/auto-mode/design.md +0 -50
  161. package/.sillyspec/changes/auto-mode/proposal.md +0 -19
  162. package/.sillyspec/changes/auto-mode/requirements.md +0 -21
  163. package/.sillyspec/changes/auto-mode/tasks.md +0 -7
  164. package/.sillyspec/changes/brainstorm-archive/2026-04-05-dashboard-design.md +0 -206
  165. package/.sillyspec/changes/brainstorm-archive/2026-04-05-unified-docs-design.md +0 -199
  166. package/.sillyspec/changes/dashboard/design.md +0 -219
  167. package/.sillyspec/changes/dashboard/design.md.braindraft +0 -206
  168. package/.sillyspec/changes/run-command-design/design.md +0 -1230
  169. package/.sillyspec/changes/unified-docs-design/design.md +0 -199
  170. package/.sillyspec/docs/sillyspec/scan/.gitkeep +0 -0
  171. package/.sillyspec/knowledge/INDEX.md +0 -8
  172. package/.sillyspec/knowledge/uncategorized.md +0 -3
  173. package/.sillyspec/plans/2026-04-05-dashboard.md +0 -737
  174. package/.sillyspec/projects/sillyspec.yaml +0 -3
  175. package/dist/steps/brainstorm/01-load-context.md +0 -30
  176. package/dist/steps/brainstorm/02-reuse-check.md +0 -6
  177. package/dist/steps/brainstorm/03-prototype-analysis.md +0 -11
  178. package/dist/steps/brainstorm/04-module-split.md +0 -23
  179. package/dist/steps/brainstorm/05-dialog-explore.md +0 -8
  180. package/dist/steps/brainstorm/06-propose-approaches.md +0 -3
  181. package/dist/steps/brainstorm/07-present-design.md +0 -3
  182. package/dist/steps/brainstorm/08-write-design.md +0 -21
  183. package/dist/steps/brainstorm/09-self-review.md +0 -15
  184. package/dist/steps/brainstorm/10-user-confirm.md +0 -3
  185. package/dist/steps/brainstorm/11-output-spec.md +0 -7
  186. package/dist/steps/brainstorm/manifest.yaml +0 -26
  187. package/dist/steps/execute/01-load-context.md +0 -41
  188. package/dist/steps/execute/02-scan-conventions.md +0 -47
  189. package/dist/steps/execute/03-skill-mcp.md +0 -19
  190. package/dist/steps/execute/04-assign-task.md +0 -22
  191. package/dist/steps/execute/04b-prompt-template.md +0 -54
  192. package/dist/steps/execute/05-write-test.md +0 -7
  193. package/dist/steps/execute/06-write-code.md +0 -8
  194. package/dist/steps/execute/07-run-test.md +0 -26
  195. package/dist/steps/execute/08-fix-issues.md +0 -28
  196. package/dist/steps/execute/09-next-task.md +0 -33
  197. package/dist/steps/execute/manifest.yaml +0 -28
  198. package/dist/steps/plan/01-load-context.md +0 -22
  199. package/dist/steps/plan/02-anchor-confirm.md +0 -1
  200. package/dist/steps/plan/03-expand-tasks.md +0 -33
  201. package/dist/steps/plan/04-mark-order.md +0 -15
  202. package/dist/steps/plan/05-e2e-planning.md +0 -17
  203. package/dist/steps/plan/06-self-check.md +0 -16
  204. package/dist/steps/plan/07-save.md +0 -1
  205. package/dist/steps/plan/manifest.yaml +0 -18
  206. package/dist/steps/scan/01-env-detect.md +0 -51
  207. package/dist/steps/scan/02-tech-stack.md +0 -16
  208. package/dist/steps/scan/03-conventions.md +0 -16
  209. package/dist/steps/scan/04-structure.md +0 -19
  210. package/dist/steps/scan/05-quality.md +0 -18
  211. package/dist/steps/scan/06-complete.md +0 -49
  212. package/dist/steps/scan/manifest.yaml +0 -16
  213. package/dist/steps/verify/01-load-specs.md +0 -28
  214. package/dist/steps/verify/02-check-tasks.md +0 -1
  215. package/dist/steps/verify/03-check-design.md +0 -6
  216. package/dist/steps/verify/04-run-tests.md +0 -7
  217. package/dist/steps/verify/05-e2e-tests.md +0 -27
  218. package/dist/steps/verify/05b-e2e-fix.md +0 -33
  219. package/dist/steps/verify/06-code-quality.md +0 -25
  220. package/dist/steps/verify/07-lint-check.md +0 -27
  221. package/dist/steps/verify/08-output-report.md +0 -14
  222. package/dist/steps/verify/manifest.yaml +0 -22
  223. package/packages/dashboard/dist/assets/index-DGe8CqeP.css +0 -1
  224. package/src/derive.js +0 -147
  225. package/src/step.js +0 -543
@@ -1,178 +1,152 @@
1
1
  <template>
2
- <div v-if="project" class="project-overview">
3
- <!-- Name + Path -->
4
- <div class="ov-section">
5
- <span class="ov-name">{{ project.name }}</span>
6
- <span class="ov-path">{{ shortPath }}</span>
2
+ <div class="overview-section">
3
+ <!-- 头部 -->
4
+ <div class="section-header">
5
+ <h1 class="section-title">
6
+ 项目概览
7
+ <span class="badge">{{ projects.length }} 个项目</span>
8
+ </h1>
9
+ <div class="section-actions">
10
+ <button class="btn btn-icon" title="刷新" @click="refresh">↻</button>
11
+ <button class="btn btn-secondary" @click="resetLayout">重置布局</button>
12
+ </div>
7
13
  </div>
8
- <div class="ov-divider" />
9
14
 
10
- <!-- Tech Stack -->
11
- <div class="ov-section ov-clickable" @click="$emit('show-detail', 'tech')">
12
- <span class="ov-label">技术栈</span>
13
- <span v-for="t in overview.techStack" :key="t" class="ov-tag">{{ t }}</span>
14
- </div>
15
- <div class="ov-divider" />
16
-
17
- <!-- Stage -->
18
- <div v-if="project.state?.currentStage" class="ov-section">
19
- <span class="ov-label">阶段</span>
20
- <span class="ov-value">{{ project.state.currentStage }}</span>
21
- </div>
22
- <div v-else-if="project.state" class="ov-section">
23
- <span class="ov-label">阶段</span>
24
- <span class="ov-value" style="color:#9CA3AF;">未执行</span>
25
- </div>
26
- <div v-if="project.state?.currentStage" class="ov-divider" />
27
-
28
- <!-- Last Active -->
29
- <div v-if="overview.lastActive" class="ov-section">
30
- <span class="ov-label">最近活跃</span>
31
- <span class="ov-value">{{ formatTime(overview.lastActive) }}</span>
32
- </div>
33
- <div v-if="overview.lastActive" class="ov-divider" />
34
-
35
- <!-- Doc Stats -->
36
- <div v-if="overview.docStats.total > 0" class="ov-section ov-clickable" @click="$emit('show-detail', 'docs')">
37
- <span class="ov-label">文档</span>
38
- <span class="ov-value">{{ docSummary }}</span>
39
- </div>
40
- <div v-if="overview.docStats.total > 0" class="ov-divider" />
41
-
42
- <!-- Git -->
43
- <div class="ov-section ov-git ov-clickable" @click="$emit('show-detail', 'git')">
44
- <span v-if="overview.git.branch" class="ov-branch">{{ overview.git.branch }}</span>
45
- <span v-if="overview.git.lastCommit" class="ov-commit" :title="overview.git.lastCommit">{{ truncate(overview.git.lastCommit, 40) }}</span>
46
- <span v-if="overview.git.dirtyCount > 0" class="ov-dirty">{{ overview.git.dirtyCount }} 未提交</span>
15
+ <!-- 卡片容器 -->
16
+ <div class="cards-container">
17
+ <ProjectCard
18
+ v-for="project in projects"
19
+ :key="project.name"
20
+ :project="project"
21
+ :is-selected="activeProject?.name === project.name"
22
+ @select="handleSelect"
23
+ />
47
24
  </div>
48
25
  </div>
49
26
  </template>
50
27
 
51
28
  <script setup>
52
- import { computed } from 'vue'
29
+ import { defineProps, defineEmits } from 'vue'
30
+ import ProjectCard from './ProjectCard.vue'
31
+ import { useLayout } from '../composables/useLayout.js'
53
32
 
54
33
  const props = defineProps({
55
- project: { type: Object, default: null }
34
+ projects: { type: Array, default: () => [] },
35
+ activeProject: { type: Object, default: null }
56
36
  })
57
37
 
58
- defineEmits(['show-detail'])
59
-
60
- const overview = computed(() => props.project?.overview || { techStack: [], lastActive: null, docStats: { design: 0, plan: 0, archive: 0, changes: 0, scan: 0, quicklog: 0, total: 0 }, git: { branch: '', lastCommit: '', dirtyCount: 0 } })
61
-
62
- const shortPath = computed(() => {
63
- const p = props.project?.path || ''
64
- return p.replace(/^\/Users\/[^/]+/, '~')
65
- })
38
+ const emit = defineEmits(['select'])
66
39
 
67
- const docSummary = computed(() => {
68
- const s = overview.value.docStats
69
- const parts = []
70
- if (s.design) parts.push(`设计 ${s.design}`)
71
- if (s.plan) parts.push(`计划 ${s.plan}`)
72
- if (s.archive) parts.push(`归档 ${s.archive}`)
73
- if (s.changes) parts.push(`变更 ${s.changes}`)
74
- return parts.join(' / ') || `${s.total} 篇`
75
- })
40
+ function handleSelect(project) {
41
+ emit('select', project)
42
+ }
76
43
 
77
- function formatTime(iso) {
78
- if (!iso) return ''
79
- const d = new Date(iso)
80
- const now = new Date()
81
- const diffMs = now - d
82
- if (diffMs < 60000) return '刚刚'
83
- if (diffMs < 3600000) return `${Math.floor(diffMs / 60000)} 分钟前`
84
- if (diffMs < 86400000) return `${Math.floor(diffMs / 3600000)} 小时前`
85
- if (diffMs < 604800000) return `${Math.floor(diffMs / 86400000)} 天前`
86
- return `${d.getMonth() + 1}/${d.getDate()}`
44
+ function refresh() {
45
+ // TODO: 实现刷新逻辑
46
+ window.location.reload()
87
47
  }
88
48
 
89
- function truncate(s, n) {
90
- return s.length > n ? s.slice(0, n) + '' : s
49
+ function resetLayout() {
50
+ if (confirm('确定要重置布局吗?')) {
51
+ useLayout().resetLayout()
52
+ }
91
53
  }
92
54
  </script>
93
55
 
94
56
  <style scoped>
95
- .project-overview {
57
+ .overview-section {
58
+ height: 100%;
59
+ background: #FFFFFF;
60
+ padding: 16px 24px;
61
+ display: flex;
62
+ flex-direction: column;
63
+ }
64
+
65
+ .section-header {
66
+ display: flex;
67
+ justify-content: space-between;
68
+ align-items: center;
69
+ margin-bottom: 16px;
70
+ }
71
+
72
+ .section-title {
73
+ font-size: 18px;
74
+ font-weight: 600;
75
+ color: #1A1A1A;
96
76
  display: flex;
97
77
  align-items: center;
98
- background: #FFFFFF;
99
- border-bottom: 1px solid #E5E5EA;
100
- padding: 8px 16px;
101
- min-height: 48px;
102
78
  gap: 8px;
103
- flex-shrink: 0;
104
- overflow-x: auto;
105
79
  }
106
- .ov-section {
80
+
81
+ .badge {
82
+ font-size: 12px;
83
+ padding: 2px 8px;
84
+ background: #E5E7EB;
85
+ border-radius: 10px;
86
+ color: #6B7280;
87
+ font-weight: 500;
88
+ }
89
+
90
+ .section-actions {
107
91
  display: flex;
92
+ gap: 8px;
108
93
  align-items: center;
109
- gap: 6px;
110
- white-space: nowrap;
111
- flex-shrink: 0;
112
94
  }
113
- .ov-clickable {
95
+
96
+ .btn {
97
+ padding: 6px 12px;
98
+ border-radius: 6px;
99
+ border: none;
114
100
  cursor: pointer;
115
- border-radius: 4px;
116
- padding: 2px 6px;
117
- margin: -2px -6px;
118
- transition: background 0.15s;
101
+ font-size: 13px;
102
+ font-weight: 500;
103
+ transition: all 0.2s;
119
104
  }
120
- .ov-clickable:hover {
121
- background: rgba(217,119,6,0.06);
105
+
106
+ .btn:hover {
107
+ opacity: 0.9;
122
108
  }
123
- .ov-name {
124
- font-weight: 600;
125
- font-size: 13px;
126
- color: #1C1C1E;
109
+
110
+ .btn-secondary {
111
+ background: #E5E7EB;
112
+ color: #374151;
127
113
  }
128
- .ov-path {
129
- font-size: 11px;
130
- color: #636366;
114
+
115
+ .btn-icon {
116
+ padding: 6px;
117
+ width: 32px;
118
+ height: 32px;
119
+ display: flex;
120
+ align-items: center;
121
+ justify-content: center;
122
+ background: #F3F4F6;
123
+ border-radius: 6px;
131
124
  }
132
- .ov-label {
133
- font-size: 11px;
134
- color: #636366;
125
+
126
+ .btn-icon:hover {
127
+ background: #E5E7EB;
135
128
  }
136
- .ov-value {
137
- font-size: 12px;
138
- color: #1C1C1E;
139
- font-weight: 500;
129
+
130
+ .cards-container {
131
+ flex: 1;
132
+ display: flex;
133
+ gap: 16px;
134
+ overflow-x: auto;
135
+ overflow-y: hidden;
136
+ padding-bottom: 8px;
140
137
  }
141
- .ov-tag {
142
- font-size: 10px;
143
- padding: 1px 8px;
144
- border-radius: 10px;
145
- background: rgba(217,119,6,0.08);
146
- color: #D97706;
147
- font-weight: 500;
138
+
139
+ .cards-container::-webkit-scrollbar {
140
+ height: 8px;
148
141
  }
149
- .ov-divider {
150
- width: 1px;
151
- height: 20px;
152
- background: #E5E5EA;
153
- flex-shrink: 0;
154
- }
155
- .ov-git {
156
- margin-left: auto;
157
- gap: 10px;
158
- }
159
- .ov-branch {
160
- font-size: 11px;
161
- color: #636366;
162
- background: #F5F5F7;
163
- padding: 1px 8px;
142
+
143
+ .cards-container::-webkit-scrollbar-track {
144
+ background: #F3F4F6;
164
145
  border-radius: 4px;
165
146
  }
166
- .ov-commit {
167
- font-size: 11px;
168
- color: #636366;
169
- max-width: 200px;
170
- overflow: hidden;
171
- text-overflow: ellipsis;
172
- }
173
- .ov-dirty {
174
- font-size: 11px;
175
- color: #D97706;
176
- font-weight: 500;
147
+
148
+ .cards-container::-webkit-scrollbar-thumb {
149
+ background: #D1D5DB;
150
+ border-radius: 4px;
177
151
  }
178
152
  </style>
@@ -1,53 +1,67 @@
1
- <template>
2
- <span
3
- :class="['inline-flex items-center gap-1.5 rounded-sm text-[10px] font-medium font-[JetBrains_Mono,monospace] uppercase tracking-wider transition-colors duration-150', sizeClass]"
4
- :style="badgeStyle"
5
- >
6
- <span :class="['w-1 h-1 rounded-full', dotClass]" :style="dotStyle" />
7
- <span>{{ displayLabel }}</span>
8
- </span>
9
- </template>
10
-
11
- <script setup>
12
- import { computed } from 'vue'
13
-
14
- const props = defineProps({
15
- status: { type: String, default: 'pending' },
16
- label: { type: String, default: '' },
17
- size: { type: String, default: 'md' }
18
- })
19
-
20
- const displayLabel = computed(() => {
21
- if (props.label) return props.label
22
- const labels = { 'completed': 'done', 'in-progress': 'running', 'blocked': 'blocked', 'failed': 'error' }
23
- return labels[props.status] || ''
24
- })
25
-
26
- const sizeClass = computed(() => props.size === 'sm' ? 'px-1.5 py-0.5' : 'px-2 py-1')
27
-
28
- const badgeStyle = computed(() => {
29
- const styles = {
30
- 'completed': { background: 'rgba(22,163,74,0.1)', color: '#16A34A' },
31
- 'in-progress': { background: 'rgba(251,191,36,0.1)', color: '#D97706' },
32
- 'blocked': { background: 'rgba(251,146,60,0.1)', color: '#EA580C' },
33
- 'failed': { background: 'rgba(239,68,68,0.1)', color: '#DC2626' },
34
- 'pending': { background: 'rgba(82,82,82,0.15)', color: '#6B7280' }
35
- }
36
- return styles[props.status] || styles.pending
37
- })
38
-
39
- const dotClass = computed(() => {
40
- return props.status === 'in-progress' ? 'animate-pulse-dot' : ''
41
- })
42
-
43
- const dotStyle = computed(() => {
44
- const colors = {
45
- 'completed': '#16A34A',
46
- 'in-progress': '#D97706',
47
- 'blocked': '#EA580C',
48
- 'failed': '#DC2626',
49
- 'pending': '#6B7280'
50
- }
51
- return { background: colors[props.status] || '#6B7280' }
52
- })
53
- </script>
1
+ <template>
2
+ <span
3
+ :class="['inline-flex items-center gap-1.5 rounded-sm text-[10px] font-medium font-[JetBrains_Mono,monospace] uppercase tracking-wider transition-colors duration-150', sizeClass]"
4
+ :style="badgeStyle"
5
+ >
6
+ <span :class="['w-1 h-1 rounded-full', dotClass]" :style="dotStyle" />
7
+ <span>{{ displayLabel }}</span>
8
+ </span>
9
+ </template>
10
+
11
+ <script setup>
12
+ import { computed } from 'vue'
13
+
14
+ const props = defineProps({
15
+ status: { type: String, default: 'pending' },
16
+ label: { type: String, default: '' },
17
+ size: { type: String, default: 'md' }
18
+ })
19
+
20
+ const displayLabel = computed(() => {
21
+ if (props.label) return props.label
22
+ const labels = {
23
+ 'completed': 'done',
24
+ 'in-progress': 'running',
25
+ 'blocked': 'blocked',
26
+ 'failed': 'error',
27
+ 'revising': 'revising',
28
+ 'stale': 'stale',
29
+ 'waiting': 'waiting',
30
+ }
31
+ return labels[props.status] || ''
32
+ })
33
+
34
+ const sizeClass = computed(() => props.size === 'sm' ? 'px-1.5 py-0.5' : 'px-2 py-1')
35
+
36
+ const badgeStyle = computed(() => {
37
+ const styles = {
38
+ 'completed': { background: 'rgba(22,163,74,0.1)', color: '#16A34A' },
39
+ 'in-progress': { background: 'rgba(251,191,36,0.1)', color: '#D97706' },
40
+ 'blocked': { background: 'rgba(251,146,60,0.1)', color: '#EA580C' },
41
+ 'failed': { background: 'rgba(239,68,68,0.1)', color: '#DC2626' },
42
+ 'pending': { background: 'rgba(82,82,82,0.15)', color: '#6B7280' },
43
+ 'revising': { background: 'rgba(139,92,246,0.1)', color: '#7C3AED' },
44
+ 'stale': { background: 'rgba(234,179,8,0.1)', color: '#CA8A04' },
45
+ 'waiting': { background: 'rgba(59,130,246,0.1)', color: '#2563EB' },
46
+ }
47
+ return styles[props.status] || styles.pending
48
+ })
49
+
50
+ const dotClass = computed(() => {
51
+ return props.status === 'in-progress' ? 'animate-pulse-dot' : ''
52
+ })
53
+
54
+ const dotStyle = computed(() => {
55
+ const colors = {
56
+ 'completed': '#16A34A',
57
+ 'in-progress': '#D97706',
58
+ 'blocked': '#EA580C',
59
+ 'failed': '#DC2626',
60
+ 'pending': '#6B7280',
61
+ 'revising': '#7C3AED',
62
+ 'stale': '#CA8A04',
63
+ 'waiting': '#2563EB',
64
+ }
65
+ return { background: colors[props.status] || '#6B7280' }
66
+ })
67
+ </script>
@@ -1,89 +1,94 @@
1
- <template>
2
- <div
3
- :class="['group relative rounded-md cursor-pointer transition-all duration-200']"
4
- :style="cardStyle"
5
- @mouseenter="hovered = true"
6
- @mouseleave="hovered = false"
7
- @click="handleClick"
8
- >
9
- <!-- Left accent bar -->
10
- <div class="absolute left-0 top-0 bottom-0 w-[2px]" :style="{ background: barColor }" />
11
-
12
- <div class="pl-4 pr-4 py-3">
13
- <div class="flex items-center gap-2">
14
- <h3 class="text-[12px] font-medium transition-colors duration-150" :style="{ color: isActive ? '#D97706' : '#1C1C1E', fontFamily: '\'JetBrains Mono\', monospace' }">
15
- {{ step.title || step.name }}
16
- </h3>
17
- <StageBadge v-if="step.status" :status="step.status" :label="statusLabel" />
18
- </div>
19
-
20
- <!-- Hover summary -->
21
- <div
22
- v-if="step.summary || step.description"
23
- class="transition-all duration-200"
24
- :style="{ maxHeight: hovered ? '80px' : '0', opacity: hovered ? 1 : 0, overflow: 'hidden' }"
25
- >
26
- <p class="mt-1.5 text-[11px] line-clamp-2" style="color: #636366;">
27
- {{ step.summary || step.description }}
28
- </p>
29
- </div>
30
-
31
- <!-- Active details -->
32
- <div v-if="isActive" class="mt-2 space-y-1">
33
- <p v-if="step.conclusion" class="text-[11px]" style="color: #1C1C1E;">
34
- <span style="color: #D97706; font-weight: 600;">结论:</span> {{ step.conclusion }}
35
- </p>
36
- <p v-if="step.decision" class="text-[11px]" style="color: #1C1C1E;">
37
- <span style="color: #D97706; font-weight: 600;">决策:</span> {{ step.decision }}
38
- </p>
39
- <p v-if="step.userQuery" class="text-[11px] italic" style="color: #636366;">
40
- "{{ step.userQuery }}"
41
- </p>
42
- </div>
43
-
44
- <div v-if="step.duration" class="mt-1 text-[10px] font-mono-log" style="color: #6B7280;">
45
- {{ step.duration }}
46
- </div>
47
- </div>
48
- </div>
49
- </template>
50
-
51
- <script setup>
52
- import { ref, computed } from 'vue'
53
- import StageBadge from './StageBadge.vue'
54
-
55
- const props = defineProps({
56
- step: { type: Object, required: true },
57
- isActive: { type: Boolean, default: false },
58
- isClickable: { type: Boolean, default: true }
59
- })
60
-
61
- const emit = defineEmits(['select'])
62
- const hovered = ref(false)
63
-
64
- const barColor = computed(() => {
65
- const colors = { 'completed': '#16A34A', 'in-progress': '#D97706', 'blocked': '#EA580C', 'failed': '#DC2626', 'pending': '#E5E5EA' }
66
- return colors[props.step.status] || colors.pending
67
- })
68
-
69
- const cardStyle = computed(() => ({
70
- background: props.isActive ? 'rgba(217,119,6,0.08)' : (hovered.value ? 'rgba(0,0,0,0.02)' : '#FFFFFF'),
71
- border: `1px solid ${props.isActive ? 'rgba(251,191,36,0.2)' : '#F0F0F3'}`,
72
- }))
73
-
74
- const statusLabel = computed(() => {
75
- const labels = { 'completed': '完成', 'in-progress': '进行中', 'blocked': '阻塞', 'failed': '失败', 'pending': '待办' }
76
- return labels[props.step.status] || '待办'
77
- })
78
-
79
- function handleClick() { if (props.isClickable) emit('select', props.step) }
80
- </script>
81
-
82
- <style scoped>
83
- .line-clamp-2 {
84
- display: -webkit-box;
85
- -webkit-line-clamp: 2;
86
- -webkit-box-orient: vertical;
87
- overflow: hidden;
88
- }
89
- </style>
1
+ <template>
2
+ <div
3
+ :class="['group relative rounded-md cursor-pointer transition-all duration-200']"
4
+ :style="cardStyle"
5
+ @mouseenter="hovered = true"
6
+ @mouseleave="hovered = false"
7
+ @click="handleClick"
8
+ >
9
+ <!-- Left accent bar -->
10
+ <div class="absolute left-0 top-0 bottom-0 w-[2px]" :style="{ background: barColor }" />
11
+
12
+ <div class="pl-4 pr-4 py-3">
13
+ <div class="flex items-center gap-2">
14
+ <h3 class="text-[12px] font-medium transition-colors duration-150" :style="{ color: isActive ? '#D97706' : '#1C1C1E', fontFamily: '\'JetBrains Mono\', monospace' }">
15
+ {{ step.title || step.name }}
16
+ </h3>
17
+ <StageBadge v-if="step.status" :status="step.status" :label="statusLabel" />
18
+ </div>
19
+
20
+ <!-- Hover summary -->
21
+ <div
22
+ v-if="step.summary || step.description"
23
+ class="transition-all duration-200"
24
+ :style="{ maxHeight: hovered ? '80px' : '0', opacity: hovered ? 1 : 0, overflow: 'hidden' }"
25
+ >
26
+ <p class="mt-1.5 text-[11px] line-clamp-2" style="color: #636366;">
27
+ {{ step.summary || step.description }}
28
+ </p>
29
+ </div>
30
+
31
+ <!-- Active details -->
32
+ <div v-if="isActive" class="mt-2 space-y-1">
33
+ <p v-if="step.conclusion" class="text-[11px]" style="color: #1C1C1E;">
34
+ <span style="color: #D97706; font-weight: 600;">结论:</span> {{ step.conclusion }}
35
+ </p>
36
+ <p v-if="step.decision" class="text-[11px]" style="color: #1C1C1E;">
37
+ <span style="color: #D97706; font-weight: 600;">决策:</span> {{ step.decision }}
38
+ </p>
39
+ <p v-if="step.userQuery" class="text-[11px] italic" style="color: #636366;">
40
+ "{{ step.userQuery }}"
41
+ </p>
42
+ </div>
43
+
44
+ <div v-if="step.duration" class="mt-1 text-[10px] font-mono-log" style="color: #6B7280;">
45
+ {{ step.duration }}
46
+ </div>
47
+ </div>
48
+ </div>
49
+ </template>
50
+
51
+ <script setup>
52
+ import { ref, computed } from 'vue'
53
+ import StageBadge from './StageBadge.vue'
54
+
55
+ const props = defineProps({
56
+ step: { type: Object, required: true },
57
+ isActive: { type: Boolean, default: false },
58
+ isClickable: { type: Boolean, default: true }
59
+ })
60
+
61
+ const emit = defineEmits(['select'])
62
+ const hovered = ref(false)
63
+
64
+ const barColor = computed(() => {
65
+ const colors = {
66
+ 'completed': '#16A34A', 'in-progress': '#D97706', 'blocked': '#EA580C', 'failed': '#DC2626', 'pending': '#E5E5EA',
67
+ 'revising': '#7C3AED', 'stale': '#CA8A04', 'waiting': '#2563EB',
68
+ }
69
+ return colors[props.step.status] || colors.pending
70
+ })
71
+
72
+ const cardStyle = computed(() => ({
73
+ background: props.isActive ? 'rgba(217,119,6,0.08)' : (hovered.value ? 'rgba(0,0,0,0.02)' : '#FFFFFF'),
74
+ border: `1px solid ${props.isActive ? 'rgba(251,191,36,0.2)' : '#F0F0F3'}`,
75
+ }))
76
+
77
+ const statusLabel = computed(() => {
78
+ const labels = { 'completed': '完成', 'in-progress': '进行中', 'blocked': '阻塞', 'failed': '失败', 'pending': '待办',
79
+ 'revising': '修订中', 'stale': '已失效', 'waiting': '等待中',
80
+ }
81
+ return labels[props.step.status] || '待办'
82
+ })
83
+
84
+ function handleClick() { if (props.isClickable) emit('select', props.step) }
85
+ </script>
86
+
87
+ <style scoped>
88
+ .line-clamp-2 {
89
+ display: -webkit-box;
90
+ -webkit-line-clamp: 2;
91
+ -webkit-box-orient: vertical;
92
+ overflow: hidden;
93
+ }
94
+ </style>
@@ -0,0 +1,61 @@
1
+ <template>
2
+ <div
3
+ class="v-resize-handle"
4
+ :class="{ 'dragging': isDragging }"
5
+ @mousedown="handleMouseDown"
6
+ >
7
+ <div class="drag-indicator"></div>
8
+ </div>
9
+ </template>
10
+
11
+ <script setup>
12
+ const props = defineProps({
13
+ isDragging: { type: Boolean, default: false }
14
+ })
15
+
16
+ const emit = defineEmits(['drag-start', 'drag-end', 'resize'])
17
+
18
+ function handleMouseDown(e) {
19
+ emit('drag-start', { type: 'vertical', startY: e.clientY })
20
+
21
+ const onMove = (ev) => {
22
+ emit('resize', { deltaY: ev.clientY - e.clientY })
23
+ }
24
+
25
+ const onUp = () => {
26
+ emit('drag-end')
27
+ window.removeEventListener('mousemove', onMove)
28
+ window.removeEventListener('mouseup', onUp)
29
+ }
30
+
31
+ window.addEventListener('mousemove', onMove)
32
+ window.addEventListener('mouseup', onUp)
33
+ }
34
+ </script>
35
+
36
+ <style scoped>
37
+ .v-resize-handle {
38
+ height: 6px;
39
+ background: #2A3040;
40
+ cursor: row-resize;
41
+ position: relative;
42
+ transition: background 0.2s;
43
+ }
44
+
45
+ .v-resize-handle:hover,
46
+ .v-resize-handle.dragging {
47
+ background: #D97706;
48
+ }
49
+
50
+ .drag-indicator {
51
+ position: absolute;
52
+ top: 50%;
53
+ left: 50%;
54
+ transform: translate(-50%, -50%);
55
+ width: 40px;
56
+ height: 4px;
57
+ background: rgba(255, 255, 255, 0.3);
58
+ border-radius: 2px;
59
+ pointer-events: none;
60
+ }
61
+ </style>