sillyspec 3.20.2 → 3.20.3

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 (208) hide show
  1. package/.claude/skills/sillyspec-archive/SKILL.md +1 -5
  2. package/.claude/skills/sillyspec-auto/SKILL.md +2 -8
  3. package/.claude/skills/sillyspec-brainstorm/SKILL.md +1 -28
  4. package/.claude/skills/sillyspec-commit/SKILL.md +3 -4
  5. package/.claude/skills/sillyspec-continue/SKILL.md +4 -5
  6. package/.claude/skills/sillyspec-doctor/SKILL.md +3 -12
  7. package/.claude/skills/sillyspec-execute/SKILL.md +2 -15
  8. package/.claude/skills/sillyspec-explore/SKILL.md +1 -14
  9. package/.claude/skills/sillyspec-plan/SKILL.md +36 -5
  10. package/.claude/skills/sillyspec-propose/SKILL.md +0 -4
  11. package/.claude/skills/sillyspec-quick/SKILL.md +1 -5
  12. package/.claude/skills/sillyspec-resume/SKILL.md +66 -23
  13. package/.claude/skills/sillyspec-scan/SKILL.md +1 -5
  14. package/.claude/skills/sillyspec-state/SKILL.md +8 -8
  15. package/.claude/skills/sillyspec-status/SKILL.md +1 -5
  16. package/.claude/skills/sillyspec-verify/SKILL.md +1 -5
  17. package/.claude/skills/sillyspec-workspace/SKILL.md +3 -11
  18. package/.sillyspec/changes/archive/2026-04-08-derive-state/design.md +97 -0
  19. package/.sillyspec/changes/archive/2026-04-08-derive-state/plan.md +51 -0
  20. package/.sillyspec/changes/archive/2026-04-08-derive-state/proposal.md +29 -0
  21. package/.sillyspec/changes/archive/2026-04-08-derive-state/requirements.md +34 -0
  22. package/.sillyspec/changes/archive/2026-04-08-derive-state/tasks.md +13 -0
  23. package/.sillyspec/changes/archive/2026-04-08-derive-state/verify-result.md +43 -0
  24. package/.sillyspec/changes/auto-mode/design.md +50 -0
  25. package/.sillyspec/changes/auto-mode/proposal.md +19 -0
  26. package/.sillyspec/changes/auto-mode/requirements.md +21 -0
  27. package/.sillyspec/changes/auto-mode/tasks.md +7 -0
  28. package/.sillyspec/changes/brainstorm-archive/2026-04-05-dashboard-design.md +206 -0
  29. package/.sillyspec/changes/brainstorm-archive/2026-04-05-unified-docs-design.md +199 -0
  30. package/.sillyspec/changes/dashboard/design.md +219 -0
  31. package/.sillyspec/changes/dashboard/design.md.braindraft +206 -0
  32. package/.sillyspec/changes/run-command-design/design.md +1230 -0
  33. package/.sillyspec/changes/unified-docs-design/design.md +199 -0
  34. package/.sillyspec/docs/sillyspec/scan/.gitkeep +0 -0
  35. package/.sillyspec/knowledge/INDEX.md +8 -0
  36. package/.sillyspec/knowledge/uncategorized.md +3 -0
  37. package/.sillyspec/plans/2026-04-05-dashboard.md +737 -0
  38. package/.sillyspec/projects/sillyspec.yaml +3 -0
  39. package/README.md +11 -13
  40. package/SKILL.md +40 -44
  41. package/dist/steps/brainstorm/01-load-context.md +30 -0
  42. package/dist/steps/brainstorm/02-reuse-check.md +6 -0
  43. package/dist/steps/brainstorm/03-prototype-analysis.md +11 -0
  44. package/dist/steps/brainstorm/04-module-split.md +23 -0
  45. package/dist/steps/brainstorm/05-dialog-explore.md +8 -0
  46. package/dist/steps/brainstorm/06-propose-approaches.md +3 -0
  47. package/dist/steps/brainstorm/07-present-design.md +3 -0
  48. package/dist/steps/brainstorm/08-write-design.md +21 -0
  49. package/dist/steps/brainstorm/09-self-review.md +15 -0
  50. package/dist/steps/brainstorm/10-user-confirm.md +3 -0
  51. package/dist/steps/brainstorm/11-output-spec.md +7 -0
  52. package/dist/steps/brainstorm/manifest.yaml +26 -0
  53. package/dist/steps/execute/01-load-context.md +41 -0
  54. package/dist/steps/execute/02-scan-conventions.md +47 -0
  55. package/dist/steps/execute/03-skill-mcp.md +19 -0
  56. package/dist/steps/execute/04-assign-task.md +22 -0
  57. package/dist/steps/execute/04b-prompt-template.md +54 -0
  58. package/dist/steps/execute/05-write-test.md +7 -0
  59. package/dist/steps/execute/06-write-code.md +8 -0
  60. package/dist/steps/execute/07-run-test.md +26 -0
  61. package/dist/steps/execute/08-fix-issues.md +28 -0
  62. package/dist/steps/execute/09-next-task.md +33 -0
  63. package/dist/steps/execute/manifest.yaml +28 -0
  64. package/dist/steps/plan/01-load-context.md +22 -0
  65. package/dist/steps/plan/02-anchor-confirm.md +1 -0
  66. package/dist/steps/plan/03-expand-tasks.md +33 -0
  67. package/dist/steps/plan/04-mark-order.md +15 -0
  68. package/dist/steps/plan/05-e2e-planning.md +17 -0
  69. package/dist/steps/plan/06-self-check.md +16 -0
  70. package/dist/steps/plan/07-save.md +1 -0
  71. package/dist/steps/plan/manifest.yaml +18 -0
  72. package/dist/steps/scan/01-env-detect.md +51 -0
  73. package/dist/steps/scan/02-tech-stack.md +16 -0
  74. package/dist/steps/scan/03-conventions.md +16 -0
  75. package/dist/steps/scan/04-structure.md +19 -0
  76. package/dist/steps/scan/05-quality.md +18 -0
  77. package/dist/steps/scan/06-complete.md +49 -0
  78. package/dist/steps/scan/manifest.yaml +16 -0
  79. package/dist/steps/verify/01-load-specs.md +28 -0
  80. package/dist/steps/verify/02-check-tasks.md +1 -0
  81. package/dist/steps/verify/03-check-design.md +6 -0
  82. package/dist/steps/verify/04-run-tests.md +7 -0
  83. package/dist/steps/verify/05-e2e-tests.md +27 -0
  84. package/dist/steps/verify/05b-e2e-fix.md +33 -0
  85. package/dist/steps/verify/06-code-quality.md +25 -0
  86. package/dist/steps/verify/07-lint-check.md +27 -0
  87. package/dist/steps/verify/08-output-report.md +14 -0
  88. package/dist/steps/verify/manifest.yaml +22 -0
  89. package/package.json +1 -7
  90. package/packages/dashboard/dist/assets/{index-Bq_Z2hne.js → index-D1EVTLmc.js} +1264 -1264
  91. package/packages/dashboard/dist/assets/index-DGe8CqeP.css +1 -0
  92. package/packages/dashboard/dist/index.html +2 -2
  93. package/packages/dashboard/package-lock.json +6 -6
  94. package/packages/dashboard/server/executor.js +1 -1
  95. package/packages/dashboard/server/index.js +18 -98
  96. package/packages/dashboard/server/parser.js +71 -139
  97. package/packages/dashboard/server/watcher.js +6 -14
  98. package/packages/dashboard/src/App.vue +185 -418
  99. package/packages/dashboard/src/components/ActionBar.vue +1 -10
  100. package/packages/dashboard/src/components/CommandPalette.vue +1 -5
  101. package/packages/dashboard/src/components/DocPreview.vue +8 -105
  102. package/packages/dashboard/src/components/DocTree.vue +19 -75
  103. package/packages/dashboard/src/components/PipelineStage.vue +2 -22
  104. package/packages/dashboard/src/components/PipelineView.vue +5 -32
  105. package/packages/dashboard/src/components/ProjectOverview.vue +139 -113
  106. package/packages/dashboard/src/components/StageBadge.vue +3 -17
  107. package/packages/dashboard/src/components/StepCard.vue +2 -7
  108. package/packages/dashboard/src/composables/useDashboard.js +0 -28
  109. package/src/derive.js +147 -0
  110. package/src/index.js +40 -688
  111. package/src/init.js +63 -119
  112. package/src/migrate.js +7 -7
  113. package/src/progress.js +248 -1474
  114. package/src/run.js +302 -3008
  115. package/src/setup.js +64 -2
  116. package/src/stages/archive.js +17 -123
  117. package/src/stages/brainstorm.js +48 -454
  118. package/src/stages/doctor.js +46 -99
  119. package/src/stages/execute.js +59 -420
  120. package/src/stages/index.js +18 -12
  121. package/src/stages/plan.js +189 -492
  122. package/src/stages/propose.js +11 -70
  123. package/src/stages/quick.js +13 -52
  124. package/src/stages/scan.js +68 -485
  125. package/src/stages/status.js +1 -1
  126. package/src/stages/verify.js +16 -203
  127. package/src/step.js +543 -0
  128. package/.claude/skills/sillyspec-knowledge/SKILL.md +0 -270
  129. package/.husky/pre-push +0 -13
  130. package/CLAUDE.md +0 -18
  131. package/docs/brainstorm-plan-contract.md +0 -64
  132. package/docs/plan-execute-contract.md +0 -123
  133. package/docs/platform-scan-protocol.md +0 -298
  134. package/docs/revision-mode.md +0 -115
  135. package/docs/sillyspec/file-lifecycle/known-implementation-gaps.md +0 -99
  136. package/docs/sillyspec/file-lifecycle/platform-workflows-sync.md +0 -218
  137. package/docs/sillyspec/file-lifecycle/stage-artifacts.md +0 -167
  138. package/docs/sillyspec/file-lifecycle/storage-and-state.md +0 -148
  139. package/docs/sillyspec/file-lifecycle/worktree-and-guard.md +0 -193
  140. package/docs/sillyspec/file-lifecycle.md +0 -125
  141. package/docs/workflow-contract-regression.md +0 -106
  142. package/docs/worktree-isolation.md +0 -252
  143. package/packages/dashboard/dist/assets/index-O2W5RV4z.css +0 -1
  144. package/packages/dashboard/dist/prototype-dashboard.html +0 -836
  145. package/packages/dashboard/dist/prototype-overview.html +0 -256
  146. package/packages/dashboard/public/prototype-dashboard.html +0 -836
  147. package/packages/dashboard/public/prototype-overview.html +0 -256
  148. package/packages/dashboard/src/components/HResizeHandle.vue +0 -48
  149. package/packages/dashboard/src/components/ProjectCard.vue +0 -187
  150. package/packages/dashboard/src/components/VResizeHandle.vue +0 -61
  151. package/packages/dashboard/src/composables/useLayout.js +0 -131
  152. package/src/brainstorm-postcheck.js +0 -158
  153. package/src/change-list.js +0 -52
  154. package/src/change-risk-profile.js +0 -352
  155. package/src/classify-change.js +0 -73
  156. package/src/constants.js +0 -70
  157. package/src/contract-matrix.js +0 -278
  158. package/src/db.js +0 -201
  159. package/src/endpoint-extractor.js +0 -315
  160. package/src/hooks/claude-pre-tool-use.cjs +0 -125
  161. package/src/hooks/worktree-guard.js +0 -761
  162. package/src/knowledge-match.js +0 -130
  163. package/src/modules.js +0 -482
  164. package/src/scan-postcheck.js +0 -383
  165. package/src/stage-contract.js +0 -700
  166. package/src/stages/brainstorm-auto.js +0 -229
  167. package/src/stages/explore.js +0 -34
  168. package/src/stages/knowledge.js +0 -498
  169. package/src/stages/plan-postcheck.js +0 -513
  170. package/src/sync.js +0 -497
  171. package/src/task-review.js +0 -346
  172. package/src/workflow.js +0 -785
  173. package/src/worktree-apply.js +0 -549
  174. package/src/worktree.js +0 -932
  175. package/templates/workflows/archive-impact.yaml +0 -79
  176. package/templates/workflows/scan-docs.yaml +0 -132
  177. package/test/brainstorm-plan-contract.test.mjs +0 -273
  178. package/test/check-syntax.mjs +0 -26
  179. package/test/cli-top-level-aliases.test.mjs +0 -174
  180. package/test/contract-artifacts.test.mjs +0 -323
  181. package/test/decision-supersede.test.mjs +0 -277
  182. package/test/knowledge-match.test.mjs +0 -231
  183. package/test/plan-execute-contract.test.mjs +0 -330
  184. package/test/plan-optimization.test.mjs +0 -572
  185. package/test/platform-artifacts.test.mjs +0 -190
  186. package/test/platform-failure-samples.test.mjs +0 -199
  187. package/test/platform-recovery-chain.test.mjs +0 -179
  188. package/test/platform-recovery.test.mjs +0 -167
  189. package/test/platform-scan-p0.test.mjs +0 -175
  190. package/test/revision-v1.test.mjs +0 -1145
  191. package/test/run-sanitize-project-name.test.mjs +0 -51
  192. package/test/run-scan-postcheck-fail.test.mjs +0 -64
  193. package/test/run-scan-project-parse.test.mjs +0 -200
  194. package/test/run-tests.mjs +0 -48
  195. package/test/scan-docs-yaml-placeholders.test.mjs +0 -84
  196. package/test/scan-knowledge.test.mjs +0 -175
  197. package/test/scan-paths.test.mjs +0 -68
  198. package/test/scan-postcheck-project-priority.test.mjs +0 -85
  199. package/test/scan-postcheck.test.mjs +0 -197
  200. package/test/scan-workflow-anyfailed-block.test.mjs +0 -52
  201. package/test/spec-dir.test.mjs +0 -206
  202. package/test/stage-contract-failed-post-check.test.mjs +0 -102
  203. package/test/stage-contract.test.mjs +0 -299
  204. package/test/stage-definitions.test.mjs +0 -39
  205. package/test/wait-gates.test.mjs +0 -501
  206. package/test/workflow-spec-base.test.mjs +0 -142
  207. package/test/worktree-guard.test.mjs +0 -136
  208. package/test/worktree-native-overlay.test.mjs +0 -188
@@ -4,127 +4,92 @@
4
4
  <!-- Ambient background -->
5
5
  <div class="absolute inset-0 pointer-events-none" style="background: radial-gradient(ellipse 60% 40% at 10% 20%, rgba(251,191,36,0.04) 0%, transparent 70%), radial-gradient(ellipse 50% 50% at 90% 80%, rgba(251,191,36,0.02) 0%, transparent 70%);" />
6
6
 
7
- <!-- 上层:多项目概览区域 -->
7
+ <!-- Main Content -->
8
8
  <div
9
- class="relative overflow-hidden flex-shrink-0"
10
- :style="{ height: layout.overviewHeight + '%' }"
9
+ class="flex-1 flex overflow-hidden relative z-10"
10
+ :style="isDragging ? 'user-select: none' : ''"
11
11
  >
12
- <!-- 项目概览 -->
13
- <ProjectOverview
14
- :projects="dashboard.state.projects"
15
- :active-project="dashboard.state.activeProject"
16
- @select="handleSelectProject"
12
+ <!-- Left: Project List -->
13
+ <aside
14
+ class="flex-shrink-0 relative overflow-hidden"
15
+ :style="`width: ${leftWidth}px; background: #FFFFFF; border-right: none;`"
16
+ >
17
+ <ProjectList
18
+ :projects="dashboard.state.projects"
19
+ :active-project="dashboard.state.activeProject"
20
+ :is-loading="dashboard.state.isLoading"
21
+ :scan-paths="scanPaths"
22
+ @select="handleSelectProject"
23
+ @scan:add-path="handleAddScanPath"
24
+ @scan:remove-path="handleRemoveScanPath"
25
+ />
26
+ </aside>
27
+
28
+ <!-- Left ↔ Center resize handle -->
29
+ <div
30
+ class="w-[2px] flex-shrink-0 cursor-col-resize hover:bg-[#D97706] active:bg-[#D97706] relative z-20"
31
+ style="background: #2A3040;"
32
+ @mousedown="startDragLeft"
17
33
  />
18
- </div>
19
-
20
- <!-- 垂直拖动分割线 -->
21
- <div
22
- class="h-[6px] flex-shrink-0 cursor-row-resize hover:bg-[#D97706] active:bg-[#D97706] relative z-20 transition-colors duration-200"
23
- :class="{ 'bg-[#D97706]': isDragging }"
24
- style="background: #2A3040;"
25
- @mousedown="startDragVertical"
26
- >
27
- <div class="absolute inset-0 flex items-center justify-center pointer-events-none">
28
- <div class="w-10 h-1 bg-white/30 rounded-full"></div>
29
- </div>
30
- </div>
31
-
32
- <!-- 下层:详情区域 -->
33
- <div class="flex-1 overflow-hidden flex flex-col">
34
- <!-- 详情内容 -->
35
- <div class="flex-1 flex overflow-hidden">
36
- <!-- 左栏:项目信息 -->
37
- <div
38
- class="detail-column flex-shrink-0 bg-white overflow-hidden"
39
- :style="{ width: layout.columnWidths[0] + '%', minWidth: '150px' }"
40
- :class="{ 'fade-in': projectSwitched }"
41
- >
42
- <div v-if="activeProject" class="project-info">
43
- <div class="detail-section-title">项目信息</div>
44
- <div class="detail-item">
45
- <div class="detail-label">项目名称</div>
46
- <div class="detail-value">{{ activeProject.name }}</div>
47
- </div>
48
- <div class="detail-item">
49
- <div class="detail-label">路径</div>
50
- <div class="detail-value detail-path">{{ shortPath }}</div>
51
- </div>
52
- <div class="detail-item">
53
- <div class="detail-label">当前阶段</div>
54
- <div class="detail-value">{{ currentStageLabel }}</div>
55
- </div>
56
- <div class="detail-item">
57
- <div class="detail-label">进度</div>
58
- <div class="detail-value">{{ progressLabel }}</div>
59
- </div>
60
- </div>
61
- <div v-else class="text-gray-400 text-sm">选择项目查看详情</div>
62
- </div>
63
-
64
- <!-- 水平拖动分割线 1 -->
65
- <div
66
- class="w-[4px] flex-shrink-0 cursor-col-resize hover:bg-[#D97706] active:bg-[#D97706] relative z-20 transition-colors duration-200"
67
- style="background: #E5E7EB;"
68
- @mousedown="startDragHorizontal(0)"
69
- ></div>
70
-
71
- <!-- 中栏:Pipeline -->
72
- <div
73
- class="pipeline-column flex-1 bg-white overflow-hidden"
74
- :style="{ minWidth: '200px' }"
75
- >
76
- <PipelineView
77
- :project="dashboard.state.activeProject"
78
- :active-step="dashboard.state.activeStep"
79
- :active-tab="dashboard.state.activeTab"
80
- :docs="dashboard.state.docs"
81
- :selected-doc-file="dashboard.state.selectedDocFile"
82
- :doc-content="dashboard.state.docContent"
83
- :doc-loading="dashboard.state.docLoading"
84
- @select-step="handleSelectStep"
85
- @switch-tab="handleSwitchTab"
86
- @select-doc-file="handleSelectDocFile"
87
- />
88
- </div>
89
34
 
90
- <!-- 水平拖动分割线 2 -->
91
- <div
92
- class="w-[4px] flex-shrink-0 cursor-col-resize hover:bg-[#D97706] active:bg-[#D97706] relative z-20 transition-colors duration-200"
93
- style="background: #E5E7EB;"
94
- @mousedown="startDragHorizontal(1)"
95
- ></div>
96
-
97
- <!-- 右栏:日志/详情 -->
98
- <div
99
- class="activity-column flex-shrink-0 bg-white overflow-hidden flex flex-col"
100
- :style="{ width: layout.columnWidths[2] + '%', minWidth: '200px' }"
101
- >
102
- <div class="detail-section-title px-4 pt-4">最近活动</div>
103
- <div class="flex-1 overflow-y-auto px-4 pb-4">
104
- <div
105
- v-for="(log, i) in recentLogs"
106
- :key="i"
107
- class="log-entry"
108
- :class="{ success: log.includes('✅'), error: log.includes('❌') }"
109
- >
110
- {{ log }}
111
- </div>
112
- <div v-if="recentLogs.length === 0" class="text-gray-400 text-sm">暂无活动</div>
113
- </div>
114
- </div>
115
- </div>
116
-
117
- <!-- Bottom: Action Bar -->
118
- <ActionBar
119
- :project="dashboard.state.activeProject"
120
- :is-executing="dashboard.state.executingProject !== null"
121
- :execution-result="executionResult"
122
- @execute="handleExecute"
123
- @kill="handleKill"
124
- @open-palette="isCommandPaletteOpen = true"
35
+ <!-- Center: Pipeline View -->
36
+ <main class="flex-1 overflow-hidden accent-stripe flex flex-col" style="min-width: 300px;">
37
+ <ProjectOverview :project="dashboard.state.activeProject" @show-detail="handleShowDetail" />
38
+ <PipelineView
39
+ :project="dashboard.state.activeProject"
40
+ :active-step="dashboard.state.activeStep"
41
+ :active-tab="dashboard.state.activeTab"
42
+ :docs="dashboard.state.docs"
43
+ :selected-doc-file="dashboard.state.selectedDocFile"
44
+ :doc-content="dashboard.state.docContent"
45
+ :doc-loading="dashboard.state.docLoading"
46
+ @select-step="handleSelectStep"
47
+ @switch-tab="handleSwitchTab"
48
+ @select-doc-file="handleSelectDocFile"
49
+ />
50
+ </main>
51
+
52
+ <!-- Center ↔ Right resize handle -->
53
+ <div
54
+ v-if="dashboard.state.isPanelOpen"
55
+ class="w-[2px] flex-shrink-0 cursor-col-resize hover:bg-[#D97706] active:bg-[#D97706] relative z-20"
56
+ style="background: #2A3040;"
57
+ @mousedown="startDragRight"
125
58
  />
59
+
60
+ <!-- Right: Detail Panel -->
61
+ <aside
62
+ :class="[
63
+ 'flex-shrink-0 relative overflow-hidden',
64
+ dashboard.state.isPanelOpen ? '' : 'w-0'
65
+ ]"
66
+ :style="dashboard.state.isPanelOpen ? `width: ${rightWidth}px; background: #FFFFFF; transition: none;` : 'background: #FFFFFF;'"
67
+ >
68
+ <DetailPanel
69
+ :is-open="dashboard.state.isPanelOpen"
70
+ :active-step="dashboard.state.activeStep"
71
+ :logs="dashboard.state.logs"
72
+ :detail-type="detailType"
73
+ :detail-data="detailData"
74
+ @close="handleDetailClose"
75
+ @clear-logs="dashboard.clearLogs"
76
+ @open-doc-file="handleOpenDocFromDetail"
77
+ />
78
+ </aside>
126
79
  </div>
127
80
 
81
+ <!-- Bottom: Action Bar -->
82
+ <ActionBar
83
+ :project="dashboard.state.activeProject"
84
+ :is-executing="dashboard.state.executingProject !== null"
85
+ :execution-result="executionResult"
86
+ :is-panel-open="dashboard.state.isPanelOpen"
87
+ @execute="handleExecute"
88
+ @kill="handleKill"
89
+ @toggle-panel="dashboard.togglePanel"
90
+ @open-palette="isCommandPaletteOpen = true"
91
+ />
92
+
128
93
  <!-- Command Palette Overlay -->
129
94
  <CommandPalette
130
95
  :is-open="isCommandPaletteOpen"
@@ -133,195 +98,110 @@
133
98
  @select-project="handleSelectProject"
134
99
  @select-stage="handleSelectStage"
135
100
  />
136
-
137
- <!-- 尺寸指示器 -->
138
- <div
139
- v-if="isDragging"
140
- class="fixed bottom-5 left-5 bg-black/70 text-white px-3 py-2 rounded text-xs font-mono z-50"
141
- >
142
- {{ sizeIndicatorText }}
143
- </div>
144
101
  </div>
145
102
  </n-config-provider>
146
103
  </template>
147
104
 
148
105
  <script setup>
149
- import { ref, computed, onMounted } from 'vue'
106
+ import { ref, onMounted, readonly } from 'vue'
150
107
  import { useWebSocket } from './composables/useWebSocket.js'
151
108
  import { useDashboard } from './composables/useDashboard.js'
152
- import { useLayout } from './composables/useLayout.js'
109
+ import { useDashboardKeyboard } from './composables/useKeyboard.js'
110
+ import ProjectList from './components/ProjectList.vue'
153
111
  import PipelineView from './components/PipelineView.vue'
154
- import ProjectOverview from './components/ProjectOverview.vue'
112
+ import DetailPanel from './components/DetailPanel.vue'
155
113
  import ActionBar from './components/ActionBar.vue'
114
+ import ProjectOverview from './components/ProjectOverview.vue'
156
115
  import CommandPalette from './components/CommandPalette.vue'
157
116
 
158
117
  // Composables
159
118
  const ws = useWebSocket()
160
119
  const dashboard = useDashboard()
161
- const layoutManager = useLayout()
162
-
163
- // 解构 layout
164
- const { layout, isDragging, startDrag, endDrag } = layoutManager
165
-
166
- // 状态
167
120
  const isCommandPaletteOpen = ref(false)
168
121
  const executionResult = ref(null)
169
- const projectSwitched = ref(false)
170
- const dragState = ref({
171
- active: false,
172
- type: null,
173
- startX: 0,
174
- startY: 0,
175
- startValue: 0
176
- })
177
-
178
- // 尺寸指示器文本
179
- const sizeIndicatorText = computed(() => {
180
- if (dragState.value.type === 'vertical') {
181
- return `概览 ${Math.round(layout.overviewHeight)}% | 详情 ${Math.round(100 - layout.overviewHeight)}%`
182
- } else if (dragState.value.type === 'horizontal') {
183
- return `三栏: ${Math.round(layout.columnWidths[0])}% | ${Math.round(layout.columnWidths[1])}% | ${Math.round(layout.columnWidths[2])}%`
184
- }
185
- return ''
186
- })
187
-
188
- // 当前项目
189
- const activeProject = computed(() => dashboard.state.activeProject)
190
-
191
- // 项目短路径
192
- const shortPath = computed(() => {
193
- const path = activeProject.value?.path || ''
194
- return path.replace(/^\/Users\/[^/]+/, '~')
195
- })
122
+ const scanPaths = ref([])
123
+ const detailType = ref(null)
124
+ const detailData = ref(null)
125
+
126
+ // Panel resize state
127
+ const STORAGE_KEY = 'dashboard-panel-widths'
128
+ const MIN_LEFT = 180
129
+ const MIN_CENTER = 300
130
+ const MIN_RIGHT = 260
131
+ const DEFAULT_LEFT = 240
132
+ const DEFAULT_RIGHT = 340
133
+
134
+ const leftWidth = ref(DEFAULT_LEFT)
135
+ const rightWidth = ref(DEFAULT_RIGHT)
136
+ const isDragging = ref(false)
137
+
138
+ // Load persisted widths
139
+ try {
140
+ const saved = JSON.parse(localStorage.getItem(STORAGE_KEY))
141
+ if (saved?.left >= MIN_LEFT) leftWidth.value = saved.left
142
+ if (saved?.right >= MIN_RIGHT) rightWidth.value = saved.right
143
+ } catch {}
144
+
145
+ function persistWidths() {
146
+ localStorage.setItem(STORAGE_KEY, JSON.stringify({ left: leftWidth.value, right: rightWidth.value }))
147
+ }
196
148
 
197
- // 当前阶段标签
198
- const currentStageLabel = computed(() => {
199
- const stage = activeProject.value?.state?.currentStage
200
- if (!stage) return '未开始'
201
- const stageNames = {
202
- scan: '代码扫描',
203
- brainstorm: '需求探索',
204
- plan: '实现计划',
205
- execute: '波次执行',
206
- verify: '验证确认',
207
- archive: '归档变更',
208
- quick: '快速任务',
209
- explore: '自由探索'
149
+ function startDragLeft(e) {
150
+ e.preventDefault()
151
+ isDragging.value = true
152
+ const startX = e.clientX
153
+ const startW = leftWidth.value
154
+ const onMove = (ev) => {
155
+ const delta = ev.clientX - startX
156
+ leftWidth.value = Math.max(MIN_LEFT, startW + delta)
210
157
  }
211
- return stageNames[stage] || stage
212
- })
213
-
214
- // 进度标签
215
- const progressLabel = computed(() => {
216
- const progress = activeProject.value?.state?.progress
217
- if (!progress) return '-'
218
- const currentStage = activeProject.value?.state?.currentStage
219
- const stageProgress = currentStage ? progress.stages?.[currentStage] : null
220
- if (!stageProgress) return '-'
221
- if (stageProgress.completedSteps !== undefined && stageProgress.steps !== undefined) {
222
- return `${stageProgress.completedSteps}/${stageProgress.steps} 步骤`
158
+ const onUp = () => {
159
+ window.removeEventListener('mousemove', onMove)
160
+ window.removeEventListener('mouseup', onUp)
161
+ isDragging.value = false
162
+ persistWidths()
223
163
  }
224
- return stageProgress.status || '-'
225
- })
226
-
227
- // 最近日志(最近 10 条)
228
- const recentLogs = computed(() => {
229
- const logs = dashboard.state.logs || []
230
- return logs.slice(-10).reverse()
231
- })
164
+ window.addEventListener('mousemove', onMove)
165
+ window.addEventListener('mouseup', onUp)
166
+ }
232
167
 
233
- // 垂直拖动(概览 ↔ 详情)
234
- function startDragVertical(e) {
168
+ function startDragRight(e) {
235
169
  e.preventDefault()
236
- dragState.value = {
237
- active: true,
238
- type: 'vertical',
239
- startY: e.clientY,
240
- startValue: layout.overviewHeight
241
- }
242
- document.body.classList.add('resizing')
243
- startDrag('vertical')
244
-
170
+ isDragging.value = true
171
+ const startX = e.clientX
172
+ const startW = rightWidth.value
245
173
  const onMove = (ev) => {
246
- const deltaY = ev.clientY - dragState.value.startY
247
- const windowHeight = window.innerHeight
248
- const deltaPercent = (deltaY / windowHeight) * 100
249
- const newHeight = dragState.value.startValue + deltaPercent
250
- layout.overviewHeight = Math.max(15, Math.min(75, newHeight))
174
+ const delta = startX - ev.clientX
175
+ rightWidth.value = Math.max(MIN_RIGHT, startW + delta)
251
176
  }
252
-
253
177
  const onUp = () => {
254
- document.body.classList.remove('resizing')
255
- dragState.value.active = false
256
- dragState.value.type = null
257
178
  window.removeEventListener('mousemove', onMove)
258
179
  window.removeEventListener('mouseup', onUp)
259
- endDrag()
180
+ isDragging.value = false
181
+ persistWidths()
260
182
  }
261
-
262
183
  window.addEventListener('mousemove', onMove)
263
184
  window.addEventListener('mouseup', onUp)
264
185
  }
265
186
 
266
- // 水平拖动(三栏分割)
267
- function startDragHorizontal(colIndex) {
268
- return (e) => {
269
- e.preventDefault()
270
- dragState.value = {
271
- active: true,
272
- type: 'horizontal',
273
- colIndex,
274
- startX: e.clientX,
275
- startWidths: [...layout.columnWidths]
276
- }
277
- document.body.classList.add('resizing')
278
- startDrag('horizontal')
279
-
280
- const onMove = (ev) => {
281
- const deltaX = ev.clientX - dragState.value.startX
282
- const containerWidth = document.querySelector('.flex-1.flex.overflow-hidden').offsetWidth
283
- const deltaPercent = (deltaX / containerWidth) * 100
284
-
285
- if (colIndex === 0) {
286
- // 左栏 ↔ 中栏
287
- const newWidth1 = ((dragState.value.startWidths[0] * containerWidth / 100) + deltaX) / containerWidth * 100
288
- const newWidth2 = ((dragState.value.startWidths[1] * containerWidth / 100) - deltaX) / containerWidth * 100
289
- if (newWidth1 >= 10 && newWidth2 >= 10) {
290
- layout.columnWidths[0] = newWidth1
291
- layout.columnWidths[1] = newWidth2
292
- }
293
- } else {
294
- // 中栏 ↔ 右栏
295
- const newWidth2 = ((dragState.value.startWidths[1] * containerWidth / 100) + deltaX) / containerWidth * 100
296
- const newWidth3 = ((dragState.value.startWidths[2] * containerWidth / 100) - deltaX) / containerWidth * 100
297
- if (newWidth2 >= 10 && newWidth3 >= 10) {
298
- layout.columnWidths[1] = newWidth2
299
- layout.columnWidths[2] = newWidth3
300
- }
301
- }
302
- }
303
-
304
- const onUp = () => {
305
- document.body.classList.remove('resizing')
306
- dragState.value.active = false
307
- dragState.value.type = null
308
- window.removeEventListener('mousemove', onMove)
309
- window.removeEventListener('mouseup', onUp)
310
- endDrag()
187
+ // Keyboard shortcuts
188
+ useDashboardKeyboard({
189
+ onOpenCommandPalette: () => { isCommandPaletteOpen.value = true },
190
+ onClose: () => {
191
+ if (isCommandPaletteOpen.value) {
192
+ isCommandPaletteOpen.value = false
193
+ } else if (dashboard.state.activeStep) {
194
+ dashboard.state.activeStep = null
195
+ } else {
196
+ dashboard.closePanel()
311
197
  }
312
-
313
- window.addEventListener('mousemove', onMove)
314
- window.addEventListener('mouseup', onUp)
315
198
  }
316
- }
199
+ })
317
200
 
318
- // WebSocket 事件处理
201
+ // WebSocket event handlers
319
202
  onMounted(() => {
320
- ws.on('projects:init', handleProjectsUpdate)
321
- ws.on('projects:updated', handleProjectsUpdate)
322
- ws.on('project:update', (project) => {
323
- dashboard.updateProject(project)
324
- })
203
+ ws.on('projects:init', (projects) => { dashboard.updateProjects(projects) })
204
+ ws.on('projects:updated', (projects) => { dashboard.updateProjects(projects) })
325
205
  ws.on('cli:output', (data) => {
326
206
  if (data.projectName === dashboard.activeProjectName.value) {
327
207
  dashboard.appendLog(data.output)
@@ -349,34 +229,22 @@ onMounted(() => {
349
229
  ws.on('docs:tree', (docs) => { dashboard.updateDocs(docs) })
350
230
  })
351
231
 
352
- function handleProjectsUpdate(projects) {
353
- const previousPath = dashboard.activeProjectPath.value
354
- dashboard.updateProjects(projects)
355
-
356
- if (!previousPath && dashboard.state.activeProject?.path) {
357
- ws.send({ type: 'docs:get', data: { projectPath: dashboard.state.activeProject.path } })
358
- }
359
- }
360
-
361
232
  function handleSelectProject(project) {
362
233
  dashboard.selectProject(project)
363
234
  dashboard.selectDocFile(null)
364
235
  dashboard.setDocContent('')
365
- // 触发淡入动画
366
- projectSwitched.value = false
367
- setTimeout(() => { projectSwitched.value = true }, 10)
368
- setTimeout(() => { projectSwitched.value = false }, 210)
236
+ // Request docs for this project
369
237
  if (project?.path) {
370
238
  ws.send({ type: 'docs:get', data: { projectPath: project.path } })
371
239
  }
372
240
  }
373
-
374
241
  function handleSelectStage({ project, stage }) { dashboard.selectProject(project) }
375
242
  function handleSelectStep(step) { dashboard.selectStep(step) }
376
243
  function handleSwitchTab(tab) { dashboard.setActiveTab(tab) }
377
244
  function handleSelectDocFile(file) {
378
245
  dashboard.selectDocFile(file)
379
246
  dashboard.setDocLoading(true)
247
+ // Fetch doc content via REST API
380
248
  fetch(`/api/docs/content?path=${encodeURIComponent(file.path)}`)
381
249
  .then(r => r.ok ? r.text() : '')
382
250
  .then(content => {
@@ -388,24 +256,49 @@ function handleSelectDocFile(file) {
388
256
  dashboard.setDocLoading(false)
389
257
  })
390
258
  }
391
-
392
259
  function handleExecute() {
393
260
  const projectName = dashboard.activeProjectName.value
394
261
  if (!projectName) return
395
- const progress = dashboard.state.activeProject?.state?.progress
396
- const stages = ['scan', 'brainstorm', 'plan', 'execute', 'verify', 'archive']
397
- const currentStage = dashboard.state.activeProject?.state?.currentStage
398
- || stages.find(stage => progress?.stages?.[stage]?.status !== 'completed')
399
- || 'scan'
400
262
  dashboard.clearLogs()
401
- ws.send({ type: 'cli:execute', data: { projectName, command: `run ${currentStage}` } })
263
+ ws.send({ type: 'cli:execute', data: { projectName, command: 'next' } })
402
264
  }
403
-
404
265
  function handleKill() {
405
266
  const projectName = dashboard.activeProjectName.value
406
267
  if (!projectName) return
407
268
  ws.send({ type: 'cli:kill', data: { projectName } })
408
269
  }
270
+ function handleAddScanPath(path) {
271
+ ws.send({ type: 'scan:add-path', data: { path } })
272
+ }
273
+ function handleRemoveScanPath(path) {
274
+ ws.send({ type: 'scan:remove-path', data: { path } })
275
+ }
276
+
277
+ async function handleShowDetail(type) {
278
+ const project = dashboard.state.activeProject
279
+ if (!project?.path) return
280
+ detailType.value = type
281
+ detailData.value = null
282
+ dashboard.openPanel()
283
+ try {
284
+ const url = `/api/projects/${encodeURIComponent(project.path)}/detail?type=${type}`
285
+ const res = await fetch(url)
286
+ if (res.ok) detailData.value = await res.json()
287
+ } catch {}
288
+ }
289
+
290
+ function handleDetailClose() {
291
+ detailType.value = null
292
+ detailData.value = null
293
+ dashboard.closePanel()
294
+ }
295
+
296
+ function handleOpenDocFromDetail(file) {
297
+ detailType.value = null
298
+ detailData.value = null
299
+ dashboard.setActiveTab('docs')
300
+ handleSelectDocFile(file)
301
+ }
409
302
 
410
303
  const themeOverrides = {
411
304
  common: {
@@ -425,134 +318,8 @@ const themeOverrides = {
425
318
  body {
426
319
  font-family: 'DM Sans', -apple-system, BlinkMacSystemFont, sans-serif;
427
320
  -webkit-font-smoothing: antialiased;
428
- -moz-osx-smoothing: grayscale;
321
+ -moz-osx-font-smoothing: grayscale;
429
322
  }
430
323
 
431
- #app { width: 100vw; height: 100vh; overflow: hidden; min-width: 0; }
432
-
433
- /* 拖动时禁用选择 */
434
- body.resizing {
435
- user-select: none;
436
- }
437
- body.resizing * {
438
- cursor: inherit !important;
439
- }
440
-
441
- /* 响应式:小窗口时调整布局 */
442
- @media (max-width: 1280px) {
443
- .detail-section-title {
444
- font-size: 11px;
445
- }
446
- .detail-value {
447
- font-size: 13px;
448
- }
449
- .log-entry {
450
- font-size: 11px;
451
- }
452
- }
453
-
454
- /* 项目信息样式 */
455
- .detail-column,
456
- .activity-column {
457
- padding: 18px 20px;
458
- }
459
-
460
- .pipeline-column {
461
- border-left: 1px solid #EEF0F4;
462
- border-right: 1px solid #EEF0F4;
463
- }
464
-
465
- .pipeline-column > * {
466
- min-width: 0;
467
- padding-left: 18px;
468
- padding-right: 18px;
469
- }
470
-
471
- .activity-column {
472
- gap: 12px;
473
- }
474
-
475
- .activity-column .detail-section-title {
476
- padding: 0;
477
- margin-bottom: 0;
478
- }
479
-
480
- .activity-column > .flex-1 {
481
- padding: 0;
482
- }
483
-
484
- .project-info {
485
- min-width: 0;
486
- }
487
-
488
- .detail-section-title {
489
- font-size: 12px;
490
- font-weight: 600;
491
- color: #9CA3AF;
492
- text-transform: uppercase;
493
- margin-bottom: 12px;
494
- }
495
-
496
- .detail-item {
497
- margin-bottom: 16px;
498
- }
499
-
500
- .detail-label {
501
- font-size: 11px;
502
- color: #9CA3AF;
503
- margin-bottom: 4px;
504
- }
505
-
506
- .detail-value {
507
- font-size: 14px;
508
- color: #1A1A1A;
509
- font-weight: 500;
510
- min-width: 0;
511
- overflow-wrap: anywhere;
512
- word-break: break-word;
513
- line-height: 1.45;
514
- }
515
-
516
- .detail-path {
517
- font-size: 12px;
518
- font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
519
- white-space: normal;
520
- }
521
-
522
- /* 日志条目样式 */
523
- .log-entry {
524
- font-family: 'JetBrains Mono', monospace;
525
- font-size: 12px;
526
- padding: 8px;
527
- background: #F9FAFB;
528
- border-radius: 4px;
529
- margin-bottom: 8px;
530
- color: #374151;
531
- }
532
-
533
- .log-entry.success {
534
- color: #047857;
535
- background: #D1FAE5;
536
- }
537
-
538
- .log-entry.error {
539
- color: #DC2626;
540
- background: #FEE2E2;
541
- }
542
-
543
- /* 淡入动画 */
544
- .fade-in {
545
- animation: fadeIn 0.2s ease-out;
546
- }
547
-
548
- @keyframes fadeIn {
549
- from {
550
- opacity: 0;
551
- transform: translateY(4px);
552
- }
553
- to {
554
- opacity: 1;
555
- transform: translateY(0);
556
- }
557
- }
324
+ #app { width: 100vw; height: 100vh; overflow: hidden; }
558
325
  </style>