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.
- package/.claude/skills/sillyspec-archive/SKILL.md +1 -5
- package/.claude/skills/sillyspec-auto/SKILL.md +2 -8
- package/.claude/skills/sillyspec-brainstorm/SKILL.md +1 -28
- package/.claude/skills/sillyspec-commit/SKILL.md +3 -4
- package/.claude/skills/sillyspec-continue/SKILL.md +4 -5
- package/.claude/skills/sillyspec-doctor/SKILL.md +3 -12
- package/.claude/skills/sillyspec-execute/SKILL.md +2 -15
- package/.claude/skills/sillyspec-explore/SKILL.md +1 -14
- package/.claude/skills/sillyspec-plan/SKILL.md +36 -5
- package/.claude/skills/sillyspec-propose/SKILL.md +0 -4
- package/.claude/skills/sillyspec-quick/SKILL.md +1 -5
- package/.claude/skills/sillyspec-resume/SKILL.md +66 -23
- package/.claude/skills/sillyspec-scan/SKILL.md +1 -5
- package/.claude/skills/sillyspec-state/SKILL.md +8 -8
- package/.claude/skills/sillyspec-status/SKILL.md +1 -5
- package/.claude/skills/sillyspec-verify/SKILL.md +1 -5
- package/.claude/skills/sillyspec-workspace/SKILL.md +3 -11
- 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-dashboard-design.md +206 -0
- package/.sillyspec/changes/brainstorm-archive/2026-04-05-unified-docs-design.md +199 -0
- package/.sillyspec/changes/dashboard/design.md +219 -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/plans/2026-04-05-dashboard.md +737 -0
- package/.sillyspec/projects/sillyspec.yaml +3 -0
- package/README.md +11 -13
- package/SKILL.md +40 -44
- package/dist/steps/brainstorm/01-load-context.md +30 -0
- package/dist/steps/brainstorm/02-reuse-check.md +6 -0
- package/dist/steps/brainstorm/03-prototype-analysis.md +11 -0
- package/dist/steps/brainstorm/04-module-split.md +23 -0
- package/dist/steps/brainstorm/05-dialog-explore.md +8 -0
- package/dist/steps/brainstorm/06-propose-approaches.md +3 -0
- package/dist/steps/brainstorm/07-present-design.md +3 -0
- package/dist/steps/brainstorm/08-write-design.md +21 -0
- package/dist/steps/brainstorm/09-self-review.md +15 -0
- package/dist/steps/brainstorm/10-user-confirm.md +3 -0
- package/dist/steps/brainstorm/11-output-spec.md +7 -0
- package/dist/steps/brainstorm/manifest.yaml +26 -0
- package/dist/steps/execute/01-load-context.md +41 -0
- package/dist/steps/execute/02-scan-conventions.md +47 -0
- package/dist/steps/execute/03-skill-mcp.md +19 -0
- package/dist/steps/execute/04-assign-task.md +22 -0
- package/dist/steps/execute/04b-prompt-template.md +54 -0
- package/dist/steps/execute/05-write-test.md +7 -0
- package/dist/steps/execute/06-write-code.md +8 -0
- package/dist/steps/execute/07-run-test.md +26 -0
- package/dist/steps/execute/08-fix-issues.md +28 -0
- package/dist/steps/execute/09-next-task.md +33 -0
- package/dist/steps/execute/manifest.yaml +28 -0
- package/dist/steps/plan/01-load-context.md +22 -0
- package/dist/steps/plan/02-anchor-confirm.md +1 -0
- package/dist/steps/plan/03-expand-tasks.md +33 -0
- package/dist/steps/plan/04-mark-order.md +15 -0
- package/dist/steps/plan/05-e2e-planning.md +17 -0
- package/dist/steps/plan/06-self-check.md +16 -0
- package/dist/steps/plan/07-save.md +1 -0
- package/dist/steps/plan/manifest.yaml +18 -0
- package/dist/steps/scan/01-env-detect.md +51 -0
- package/dist/steps/scan/02-tech-stack.md +16 -0
- package/dist/steps/scan/03-conventions.md +16 -0
- package/dist/steps/scan/04-structure.md +19 -0
- package/dist/steps/scan/05-quality.md +18 -0
- package/dist/steps/scan/06-complete.md +49 -0
- package/dist/steps/scan/manifest.yaml +16 -0
- package/dist/steps/verify/01-load-specs.md +28 -0
- package/dist/steps/verify/02-check-tasks.md +1 -0
- package/dist/steps/verify/03-check-design.md +6 -0
- package/dist/steps/verify/04-run-tests.md +7 -0
- package/dist/steps/verify/05-e2e-tests.md +27 -0
- package/dist/steps/verify/05b-e2e-fix.md +33 -0
- package/dist/steps/verify/06-code-quality.md +25 -0
- package/dist/steps/verify/07-lint-check.md +27 -0
- package/dist/steps/verify/08-output-report.md +14 -0
- package/dist/steps/verify/manifest.yaml +22 -0
- package/package.json +1 -7
- package/packages/dashboard/dist/assets/{index-Bq_Z2hne.js → index-D1EVTLmc.js} +1264 -1264
- package/packages/dashboard/dist/assets/index-DGe8CqeP.css +1 -0
- package/packages/dashboard/dist/index.html +2 -2
- package/packages/dashboard/package-lock.json +6 -6
- package/packages/dashboard/server/executor.js +1 -1
- package/packages/dashboard/server/index.js +18 -98
- package/packages/dashboard/server/parser.js +71 -139
- package/packages/dashboard/server/watcher.js +6 -14
- package/packages/dashboard/src/App.vue +185 -418
- package/packages/dashboard/src/components/ActionBar.vue +1 -10
- package/packages/dashboard/src/components/CommandPalette.vue +1 -5
- package/packages/dashboard/src/components/DocPreview.vue +8 -105
- package/packages/dashboard/src/components/DocTree.vue +19 -75
- package/packages/dashboard/src/components/PipelineStage.vue +2 -22
- package/packages/dashboard/src/components/PipelineView.vue +5 -32
- package/packages/dashboard/src/components/ProjectOverview.vue +139 -113
- package/packages/dashboard/src/components/StageBadge.vue +3 -17
- package/packages/dashboard/src/components/StepCard.vue +2 -7
- package/packages/dashboard/src/composables/useDashboard.js +0 -28
- package/src/derive.js +147 -0
- package/src/index.js +40 -688
- package/src/init.js +63 -119
- package/src/migrate.js +7 -7
- package/src/progress.js +248 -1474
- package/src/run.js +302 -3008
- package/src/setup.js +64 -2
- package/src/stages/archive.js +17 -123
- package/src/stages/brainstorm.js +48 -454
- package/src/stages/doctor.js +46 -99
- package/src/stages/execute.js +59 -420
- package/src/stages/index.js +18 -12
- package/src/stages/plan.js +189 -492
- package/src/stages/propose.js +11 -70
- package/src/stages/quick.js +13 -52
- package/src/stages/scan.js +68 -485
- package/src/stages/status.js +1 -1
- package/src/stages/verify.js +16 -203
- package/src/step.js +543 -0
- package/.claude/skills/sillyspec-knowledge/SKILL.md +0 -270
- package/.husky/pre-push +0 -13
- package/CLAUDE.md +0 -18
- package/docs/brainstorm-plan-contract.md +0 -64
- package/docs/plan-execute-contract.md +0 -123
- package/docs/platform-scan-protocol.md +0 -298
- package/docs/revision-mode.md +0 -115
- package/docs/sillyspec/file-lifecycle/known-implementation-gaps.md +0 -99
- package/docs/sillyspec/file-lifecycle/platform-workflows-sync.md +0 -218
- package/docs/sillyspec/file-lifecycle/stage-artifacts.md +0 -167
- package/docs/sillyspec/file-lifecycle/storage-and-state.md +0 -148
- package/docs/sillyspec/file-lifecycle/worktree-and-guard.md +0 -193
- package/docs/sillyspec/file-lifecycle.md +0 -125
- package/docs/workflow-contract-regression.md +0 -106
- package/docs/worktree-isolation.md +0 -252
- package/packages/dashboard/dist/assets/index-O2W5RV4z.css +0 -1
- package/packages/dashboard/dist/prototype-dashboard.html +0 -836
- package/packages/dashboard/dist/prototype-overview.html +0 -256
- package/packages/dashboard/public/prototype-dashboard.html +0 -836
- package/packages/dashboard/public/prototype-overview.html +0 -256
- package/packages/dashboard/src/components/HResizeHandle.vue +0 -48
- package/packages/dashboard/src/components/ProjectCard.vue +0 -187
- package/packages/dashboard/src/components/VResizeHandle.vue +0 -61
- package/packages/dashboard/src/composables/useLayout.js +0 -131
- package/src/brainstorm-postcheck.js +0 -158
- package/src/change-list.js +0 -52
- package/src/change-risk-profile.js +0 -352
- package/src/classify-change.js +0 -73
- package/src/constants.js +0 -70
- package/src/contract-matrix.js +0 -278
- package/src/db.js +0 -201
- package/src/endpoint-extractor.js +0 -315
- package/src/hooks/claude-pre-tool-use.cjs +0 -125
- package/src/hooks/worktree-guard.js +0 -761
- package/src/knowledge-match.js +0 -130
- package/src/modules.js +0 -482
- package/src/scan-postcheck.js +0 -383
- package/src/stage-contract.js +0 -700
- package/src/stages/brainstorm-auto.js +0 -229
- package/src/stages/explore.js +0 -34
- package/src/stages/knowledge.js +0 -498
- package/src/stages/plan-postcheck.js +0 -513
- package/src/sync.js +0 -497
- package/src/task-review.js +0 -346
- package/src/workflow.js +0 -785
- package/src/worktree-apply.js +0 -549
- package/src/worktree.js +0 -932
- package/templates/workflows/archive-impact.yaml +0 -79
- package/templates/workflows/scan-docs.yaml +0 -132
- package/test/brainstorm-plan-contract.test.mjs +0 -273
- package/test/check-syntax.mjs +0 -26
- package/test/cli-top-level-aliases.test.mjs +0 -174
- package/test/contract-artifacts.test.mjs +0 -323
- package/test/decision-supersede.test.mjs +0 -277
- package/test/knowledge-match.test.mjs +0 -231
- package/test/plan-execute-contract.test.mjs +0 -330
- package/test/plan-optimization.test.mjs +0 -572
- package/test/platform-artifacts.test.mjs +0 -190
- package/test/platform-failure-samples.test.mjs +0 -199
- package/test/platform-recovery-chain.test.mjs +0 -179
- package/test/platform-recovery.test.mjs +0 -167
- package/test/platform-scan-p0.test.mjs +0 -175
- package/test/revision-v1.test.mjs +0 -1145
- package/test/run-sanitize-project-name.test.mjs +0 -51
- package/test/run-scan-postcheck-fail.test.mjs +0 -64
- package/test/run-scan-project-parse.test.mjs +0 -200
- package/test/run-tests.mjs +0 -48
- package/test/scan-docs-yaml-placeholders.test.mjs +0 -84
- package/test/scan-knowledge.test.mjs +0 -175
- package/test/scan-paths.test.mjs +0 -68
- package/test/scan-postcheck-project-priority.test.mjs +0 -85
- package/test/scan-postcheck.test.mjs +0 -197
- package/test/scan-workflow-anyfailed-block.test.mjs +0 -52
- package/test/spec-dir.test.mjs +0 -206
- package/test/stage-contract-failed-post-check.test.mjs +0 -102
- package/test/stage-contract.test.mjs +0 -299
- package/test/stage-definitions.test.mjs +0 -39
- package/test/wait-gates.test.mjs +0 -501
- package/test/workflow-spec-base.test.mjs +0 -142
- package/test/worktree-guard.test.mjs +0 -136
- 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="
|
|
10
|
-
:style="
|
|
9
|
+
class="flex-1 flex overflow-hidden relative z-10"
|
|
10
|
+
:style="isDragging ? 'user-select: none' : ''"
|
|
11
11
|
>
|
|
12
|
-
<!--
|
|
13
|
-
<
|
|
14
|
-
|
|
15
|
-
:
|
|
16
|
-
|
|
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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
:
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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,
|
|
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 {
|
|
109
|
+
import { useDashboardKeyboard } from './composables/useKeyboard.js'
|
|
110
|
+
import ProjectList from './components/ProjectList.vue'
|
|
153
111
|
import PipelineView from './components/PipelineView.vue'
|
|
154
|
-
import
|
|
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
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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
|
-
|
|
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
|
-
|
|
237
|
-
|
|
238
|
-
|
|
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
|
|
247
|
-
|
|
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
|
-
|
|
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
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
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',
|
|
321
|
-
ws.on('projects:updated',
|
|
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:
|
|
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;
|
|
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>
|