sillyspec 3.9.0 → 3.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/sillyspec-archive/SKILL.md +17 -0
- package/.claude/skills/sillyspec-auto/SKILL.md +78 -0
- package/.claude/skills/sillyspec-brainstorm/SKILL.md +17 -0
- package/{templates/commit.md → .claude/skills/sillyspec-commit/SKILL.md} +32 -47
- package/.claude/skills/sillyspec-continue/SKILL.md +45 -0
- package/.claude/skills/sillyspec-doctor/SKILL.md +27 -0
- package/.claude/skills/sillyspec-execute/SKILL.md +17 -0
- package/.claude/skills/sillyspec-explore/SKILL.md +105 -0
- package/.claude/skills/sillyspec-export/SKILL.md +53 -0
- package/.claude/skills/sillyspec-init/SKILL.md +170 -0
- package/.claude/skills/sillyspec-plan/SKILL.md +17 -0
- package/.claude/skills/sillyspec-propose/SKILL.md +17 -0
- package/.claude/skills/sillyspec-quick/SKILL.md +17 -0
- package/.claude/skills/sillyspec-resume/SKILL.md +111 -0
- package/.claude/skills/sillyspec-scan/SKILL.md +17 -0
- package/.claude/skills/sillyspec-state/SKILL.md +54 -0
- package/.claude/skills/sillyspec-status/SKILL.md +17 -0
- package/.claude/skills/sillyspec-verify/SKILL.md +17 -0
- package/.claude/skills/sillyspec-workspace/SKILL.md +149 -0
- package/README.md +19 -11
- package/SKILL.md +15 -10
- package/package.json +7 -9
- package/packages/dashboard/dist/assets/index-BcM2J-hv.css +1 -0
- package/packages/dashboard/dist/assets/index-DpLHK4jv.js +7446 -0
- package/packages/dashboard/dist/index.html +16 -16
- package/packages/dashboard/dist/prototype-dashboard.html +836 -0
- package/packages/dashboard/dist/prototype-overview.html +256 -0
- package/packages/dashboard/package-lock.json +226 -6
- package/packages/dashboard/package.json +8 -5
- package/packages/dashboard/public/logo.jpg +0 -0
- package/packages/dashboard/public/prototype-dashboard.html +836 -0
- package/packages/dashboard/public/prototype-overview.html +256 -0
- package/packages/dashboard/server/executor.js +1 -1
- package/packages/dashboard/server/index.js +341 -113
- package/packages/dashboard/server/parser.js +442 -30
- package/packages/dashboard/server/watcher.js +214 -134
- package/packages/dashboard/src/App.vue +475 -71
- package/packages/dashboard/src/components/ActionBar.vue +36 -43
- package/packages/dashboard/src/components/CommandPalette.vue +45 -66
- package/packages/dashboard/src/components/DetailPanel.vue +68 -53
- package/packages/dashboard/src/components/DocPreview.vue +257 -0
- package/packages/dashboard/src/components/DocTree.vue +114 -0
- package/packages/dashboard/src/components/HResizeHandle.vue +48 -0
- package/packages/dashboard/src/components/LogStream.vue +13 -33
- package/packages/dashboard/src/components/PipelineStage.vue +8 -8
- package/packages/dashboard/src/components/PipelineView.vue +99 -45
- package/packages/dashboard/src/components/ProjectCard.vue +187 -0
- package/packages/dashboard/src/components/ProjectList.vue +103 -45
- package/packages/dashboard/src/components/ProjectOverview.vue +152 -0
- package/packages/dashboard/src/components/StageBadge.vue +13 -13
- package/packages/dashboard/src/components/StepCard.vue +15 -15
- package/packages/dashboard/src/components/VResizeHandle.vue +61 -0
- package/packages/dashboard/src/components/detail/DocsDetail.vue +48 -0
- package/packages/dashboard/src/components/detail/GitDetail.vue +61 -0
- package/packages/dashboard/src/components/detail/TechDetail.vue +43 -0
- package/packages/dashboard/src/composables/useDashboard.js +48 -6
- package/packages/dashboard/src/composables/useKeyboard.js +6 -4
- package/packages/dashboard/src/composables/useLayout.js +131 -0
- package/packages/dashboard/src/main.js +4 -1
- package/packages/dashboard/src/style.css +17 -17
- package/src/index.js +141 -22
- package/src/init.js +93 -231
- package/src/migrate.js +117 -0
- package/src/progress.js +460 -0
- package/src/run.js +635 -0
- package/src/setup.js +2 -72
- package/src/stages/archive.js +54 -0
- package/src/stages/brainstorm.js +264 -0
- package/src/stages/doctor.js +303 -0
- package/src/stages/execute.js +287 -0
- package/src/stages/explore.js +34 -0
- package/src/stages/index.js +28 -0
- package/src/stages/plan.js +354 -0
- package/src/stages/propose.js +115 -0
- package/src/stages/quick.js +64 -0
- package/src/stages/scan.js +141 -0
- package/src/stages/status.js +65 -0
- package/src/stages/verify.js +135 -0
- package/.sillyspec/changes/dashboard/design.md +0 -219
- package/.sillyspec/plans/2026-04-05-dashboard.md +0 -737
- package/.sillyspec/specs/2026-04-05-dashboard-design.md +0 -206
- package/dist/steps/brainstorm/01-load-context.md +0 -30
- package/dist/steps/brainstorm/02-reuse-check.md +0 -6
- package/dist/steps/brainstorm/03-prototype-analysis.md +0 -11
- package/dist/steps/brainstorm/04-module-split.md +0 -23
- package/dist/steps/brainstorm/05-dialog-explore.md +0 -8
- package/dist/steps/brainstorm/06-propose-approaches.md +0 -3
- package/dist/steps/brainstorm/07-present-design.md +0 -3
- package/dist/steps/brainstorm/08-write-design.md +0 -21
- package/dist/steps/brainstorm/09-self-review.md +0 -15
- package/dist/steps/brainstorm/10-user-confirm.md +0 -3
- package/dist/steps/brainstorm/11-output-spec.md +0 -7
- package/dist/steps/brainstorm/manifest.yaml +0 -26
- package/dist/steps/execute/01-load-context.md +0 -41
- package/dist/steps/execute/02-scan-conventions.md +0 -47
- package/dist/steps/execute/03-skill-mcp.md +0 -19
- package/dist/steps/execute/04-assign-task.md +0 -22
- package/dist/steps/execute/04b-prompt-template.md +0 -54
- package/dist/steps/execute/05-write-test.md +0 -7
- package/dist/steps/execute/06-write-code.md +0 -8
- package/dist/steps/execute/07-run-test.md +0 -26
- package/dist/steps/execute/08-fix-issues.md +0 -28
- package/dist/steps/execute/09-next-task.md +0 -33
- package/dist/steps/execute/manifest.yaml +0 -28
- package/dist/steps/plan/01-load-context.md +0 -22
- package/dist/steps/plan/02-anchor-confirm.md +0 -1
- package/dist/steps/plan/03-expand-tasks.md +0 -33
- package/dist/steps/plan/04-mark-order.md +0 -15
- package/dist/steps/plan/05-e2e-planning.md +0 -17
- package/dist/steps/plan/06-self-check.md +0 -16
- package/dist/steps/plan/07-save.md +0 -1
- package/dist/steps/plan/manifest.yaml +0 -18
- package/dist/steps/scan/01-env-detect.md +0 -51
- package/dist/steps/scan/02-tech-stack.md +0 -16
- package/dist/steps/scan/03-conventions.md +0 -16
- package/dist/steps/scan/04-structure.md +0 -19
- package/dist/steps/scan/05-quality.md +0 -18
- package/dist/steps/scan/06-complete.md +0 -49
- package/dist/steps/scan/manifest.yaml +0 -16
- package/dist/steps/verify/01-load-specs.md +0 -28
- package/dist/steps/verify/02-check-tasks.md +0 -1
- package/dist/steps/verify/03-check-design.md +0 -6
- package/dist/steps/verify/04-run-tests.md +0 -7
- package/dist/steps/verify/05-e2e-tests.md +0 -27
- package/dist/steps/verify/05b-e2e-fix.md +0 -33
- package/dist/steps/verify/06-code-quality.md +0 -25
- package/dist/steps/verify/07-lint-check.md +0 -27
- package/dist/steps/verify/08-output-report.md +0 -14
- package/dist/steps/verify/manifest.yaml +0 -22
- package/docs/.vitepress/config.mts +0 -45
- package/docs/.vitepress/dist/404.html +0 -25
- package/docs/.vitepress/dist/assets/app.YytxICdd.js +0 -1
- package/docs/.vitepress/dist/assets/chunks/framework.Czhw_PXq.js +0 -19
- package/docs/.vitepress/dist/assets/chunks/theme.DusTRZQk.js +0 -1
- package/docs/.vitepress/dist/assets/index.md.C3VCvtQA.js +0 -1
- package/docs/.vitepress/dist/assets/index.md.C3VCvtQA.lean.js +0 -1
- package/docs/.vitepress/dist/assets/inter-italic-cyrillic-ext.r48I6akx.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-cyrillic.By2_1cv3.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-greek-ext.1u6EdAuj.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-greek.DJ8dCoTZ.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-latin-ext.CN1xVJS-.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-latin.C2AdPX0b.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-vietnamese.BSbpV94h.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-cyrillic-ext.BBPuwvHQ.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-cyrillic.C5lxZ8CY.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-greek-ext.CqjqNYQ-.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-greek.BBVDIX6e.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-latin-ext.4ZJIpNVo.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-latin.Di8DUHzh.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-vietnamese.BjW4sHH5.woff2 +0 -0
- package/docs/.vitepress/dist/assets/sillyspec_commands.md.CXFFsj08.js +0 -15
- package/docs/.vitepress/dist/assets/sillyspec_commands.md.CXFFsj08.lean.js +0 -1
- package/docs/.vitepress/dist/assets/sillyspec_dashboard.md.BuPXHqjX.js +0 -4
- package/docs/.vitepress/dist/assets/sillyspec_dashboard.md.BuPXHqjX.lean.js +0 -1
- package/docs/.vitepress/dist/assets/sillyspec_file-io.md.Cz3x7llx.js +0 -1
- package/docs/.vitepress/dist/assets/sillyspec_file-io.md.Cz3x7llx.lean.js +0 -1
- package/docs/.vitepress/dist/assets/sillyspec_getting-started.md.ClcvV8k3.js +0 -4
- package/docs/.vitepress/dist/assets/sillyspec_getting-started.md.ClcvV8k3.lean.js +0 -1
- package/docs/.vitepress/dist/assets/sillyspec_install.md.CKuR2tiT.js +0 -5
- package/docs/.vitepress/dist/assets/sillyspec_install.md.CKuR2tiT.lean.js +0 -1
- package/docs/.vitepress/dist/assets/sillyspec_lifecycle.md.DY293cR1.js +0 -28
- package/docs/.vitepress/dist/assets/sillyspec_lifecycle.md.DY293cR1.lean.js +0 -1
- package/docs/.vitepress/dist/assets/sillyspec_structure.md.sVYS4zPs.js +0 -30
- package/docs/.vitepress/dist/assets/sillyspec_structure.md.sVYS4zPs.lean.js +0 -1
- package/docs/.vitepress/dist/assets/style.DFTx90Kk.css +0 -1
- package/docs/.vitepress/dist/hashmap.json +0 -1
- package/docs/.vitepress/dist/index.html +0 -28
- package/docs/.vitepress/dist/sillyspec/commands.html +0 -42
- package/docs/.vitepress/dist/sillyspec/dashboard.html +0 -31
- package/docs/.vitepress/dist/sillyspec/file-io.html +0 -28
- package/docs/.vitepress/dist/sillyspec/getting-started.html +0 -31
- package/docs/.vitepress/dist/sillyspec/install.html +0 -32
- package/docs/.vitepress/dist/sillyspec/lifecycle.html +0 -55
- package/docs/.vitepress/dist/sillyspec/structure.html +0 -57
- package/docs/.vitepress/dist/vp-icons.css +0 -1
- package/docs/index.md +0 -34
- package/docs/sillyspec/commands.md +0 -218
- package/docs/sillyspec/dashboard.md +0 -51
- package/docs/sillyspec/file-io.md +0 -34
- package/docs/sillyspec/getting-started.md +0 -61
- package/docs/sillyspec/install.md +0 -51
- package/docs/sillyspec/lifecycle.md +0 -146
- package/docs/sillyspec/structure.md +0 -62
- package/packages/dashboard/dist/assets/index-Bh-GPjKY.css +0 -1
- package/packages/dashboard/dist/assets/index-CrCn5Gg6.js +0 -17
- package/src/step.js +0 -543
- package/templates/archive.md +0 -120
- package/templates/brainstorm.md +0 -170
- package/templates/continue.md +0 -32
- package/templates/execute.md +0 -304
- package/templates/explore.md +0 -59
- package/templates/export.md +0 -21
- package/templates/init.md +0 -61
- package/templates/plan.md +0 -146
- package/templates/quick.md +0 -135
- package/templates/scan-quick.md +0 -49
- package/templates/scan.md +0 -156
- package/templates/skills/playwright-e2e/SKILL.md +0 -340
- package/templates/status.md +0 -75
- package/templates/verify.md +0 -236
- package/templates/workspace-sync.md +0 -99
- package/templates/workspace.md +0 -70
- /package/{docs/.vitepress/dist/logo.jpg → logo.jpg} +0 -0
- /package/{docs/.vitepress → packages/dashboard}/dist/favicon.jpg +0 -0
- /package/{docs/public → packages/dashboard/dist}/logo.jpg +0 -0
- /package/{docs → packages/dashboard}/public/favicon.jpg +0 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
<template>
|
|
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>
|
|
13
|
+
</div>
|
|
14
|
+
|
|
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
|
+
/>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
</template>
|
|
27
|
+
|
|
28
|
+
<script setup>
|
|
29
|
+
import { defineProps, defineEmits } from 'vue'
|
|
30
|
+
import ProjectCard from './ProjectCard.vue'
|
|
31
|
+
import { useLayout } from '../composables/useLayout.js'
|
|
32
|
+
|
|
33
|
+
const props = defineProps({
|
|
34
|
+
projects: { type: Array, default: () => [] },
|
|
35
|
+
activeProject: { type: Object, default: null }
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const emit = defineEmits(['select'])
|
|
39
|
+
|
|
40
|
+
function handleSelect(project) {
|
|
41
|
+
emit('select', project)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function refresh() {
|
|
45
|
+
// TODO: 实现刷新逻辑
|
|
46
|
+
window.location.reload()
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function resetLayout() {
|
|
50
|
+
if (confirm('确定要重置布局吗?')) {
|
|
51
|
+
useLayout().resetLayout()
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
</script>
|
|
55
|
+
|
|
56
|
+
<style scoped>
|
|
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;
|
|
76
|
+
display: flex;
|
|
77
|
+
align-items: center;
|
|
78
|
+
gap: 8px;
|
|
79
|
+
}
|
|
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 {
|
|
91
|
+
display: flex;
|
|
92
|
+
gap: 8px;
|
|
93
|
+
align-items: center;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.btn {
|
|
97
|
+
padding: 6px 12px;
|
|
98
|
+
border-radius: 6px;
|
|
99
|
+
border: none;
|
|
100
|
+
cursor: pointer;
|
|
101
|
+
font-size: 13px;
|
|
102
|
+
font-weight: 500;
|
|
103
|
+
transition: all 0.2s;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.btn:hover {
|
|
107
|
+
opacity: 0.9;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.btn-secondary {
|
|
111
|
+
background: #E5E7EB;
|
|
112
|
+
color: #374151;
|
|
113
|
+
}
|
|
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;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.btn-icon:hover {
|
|
127
|
+
background: #E5E7EB;
|
|
128
|
+
}
|
|
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;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.cards-container::-webkit-scrollbar {
|
|
140
|
+
height: 8px;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.cards-container::-webkit-scrollbar-track {
|
|
144
|
+
background: #F3F4F6;
|
|
145
|
+
border-radius: 4px;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.cards-container::-webkit-scrollbar-thumb {
|
|
149
|
+
background: #D1D5DB;
|
|
150
|
+
border-radius: 4px;
|
|
151
|
+
}
|
|
152
|
+
</style>
|
|
@@ -19,19 +19,19 @@ const props = defineProps({
|
|
|
19
19
|
|
|
20
20
|
const displayLabel = computed(() => {
|
|
21
21
|
if (props.label) return props.label
|
|
22
|
-
const labels = { 'completed': 'done', 'in-progress': 'running', 'blocked': 'blocked', 'failed': 'error'
|
|
23
|
-
return labels[props.status] || '
|
|
22
|
+
const labels = { 'completed': 'done', 'in-progress': 'running', 'blocked': 'blocked', 'failed': 'error' }
|
|
23
|
+
return labels[props.status] || ''
|
|
24
24
|
})
|
|
25
25
|
|
|
26
26
|
const sizeClass = computed(() => props.size === 'sm' ? 'px-1.5 py-0.5' : 'px-2 py-1')
|
|
27
27
|
|
|
28
28
|
const badgeStyle = computed(() => {
|
|
29
29
|
const styles = {
|
|
30
|
-
'completed': { background: 'rgba(
|
|
31
|
-
'in-progress': { background: 'rgba(251,191,36,0.1)', color: '#
|
|
32
|
-
'blocked': { background: 'rgba(251,146,60,0.1)', color: '#
|
|
33
|
-
'failed': { background: 'rgba(239,68,68,0.1)', color: '#
|
|
34
|
-
'pending': { background: 'rgba(82,82,82,0.15)', color: '#
|
|
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
35
|
}
|
|
36
36
|
return styles[props.status] || styles.pending
|
|
37
37
|
})
|
|
@@ -42,12 +42,12 @@ const dotClass = computed(() => {
|
|
|
42
42
|
|
|
43
43
|
const dotStyle = computed(() => {
|
|
44
44
|
const colors = {
|
|
45
|
-
'completed': '#
|
|
46
|
-
'in-progress': '#
|
|
47
|
-
'blocked': '#
|
|
48
|
-
'failed': '#
|
|
49
|
-
'pending': '#
|
|
45
|
+
'completed': '#16A34A',
|
|
46
|
+
'in-progress': '#D97706',
|
|
47
|
+
'blocked': '#EA580C',
|
|
48
|
+
'failed': '#DC2626',
|
|
49
|
+
'pending': '#6B7280'
|
|
50
50
|
}
|
|
51
|
-
return { background: colors[props.status] || '#
|
|
51
|
+
return { background: colors[props.status] || '#6B7280' }
|
|
52
52
|
})
|
|
53
53
|
</script>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
|
-
:class="['group relative rounded-md cursor-pointer
|
|
3
|
+
:class="['group relative rounded-md cursor-pointer transition-all duration-200']"
|
|
4
4
|
:style="cardStyle"
|
|
5
5
|
@mouseenter="hovered = true"
|
|
6
6
|
@mouseleave="hovered = false"
|
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
<!-- Left accent bar -->
|
|
10
10
|
<div class="absolute left-0 top-0 bottom-0 w-[2px]" :style="{ background: barColor }" />
|
|
11
11
|
|
|
12
|
-
<div class="pl-
|
|
12
|
+
<div class="pl-4 pr-4 py-3">
|
|
13
13
|
<div class="flex items-center gap-2">
|
|
14
|
-
<h3 class="text-[12px] font-medium transition-colors duration-150" :style="{ color: isActive ? '#
|
|
14
|
+
<h3 class="text-[12px] font-medium transition-colors duration-150" :style="{ color: isActive ? '#D97706' : '#1C1C1E', fontFamily: '\'JetBrains Mono\', monospace' }">
|
|
15
15
|
{{ step.title || step.name }}
|
|
16
16
|
</h3>
|
|
17
17
|
<StageBadge v-if="step.status" :status="step.status" :label="statusLabel" />
|
|
@@ -20,28 +20,28 @@
|
|
|
20
20
|
<!-- Hover summary -->
|
|
21
21
|
<div
|
|
22
22
|
v-if="step.summary || step.description"
|
|
23
|
-
class="
|
|
24
|
-
:style="{ maxHeight: hovered ? '
|
|
23
|
+
class="transition-all duration-200"
|
|
24
|
+
:style="{ maxHeight: hovered ? '80px' : '0', opacity: hovered ? 1 : 0, overflow: 'hidden' }"
|
|
25
25
|
>
|
|
26
|
-
<p class="mt-1.5 text-[11px] line-clamp-2" style="color: #
|
|
26
|
+
<p class="mt-1.5 text-[11px] line-clamp-2" style="color: #636366;">
|
|
27
27
|
{{ step.summary || step.description }}
|
|
28
28
|
</p>
|
|
29
29
|
</div>
|
|
30
30
|
|
|
31
31
|
<!-- Active details -->
|
|
32
32
|
<div v-if="isActive" class="mt-2 space-y-1">
|
|
33
|
-
<p v-if="step.conclusion" class="text-[11px]" style="color: #
|
|
34
|
-
<span style="color: #
|
|
33
|
+
<p v-if="step.conclusion" class="text-[11px]" style="color: #1C1C1E;">
|
|
34
|
+
<span style="color: #D97706; font-weight: 600;">结论:</span> {{ step.conclusion }}
|
|
35
35
|
</p>
|
|
36
|
-
<p v-if="step.decision" class="text-[11px]" style="color: #
|
|
37
|
-
<span style="color: #
|
|
36
|
+
<p v-if="step.decision" class="text-[11px]" style="color: #1C1C1E;">
|
|
37
|
+
<span style="color: #D97706; font-weight: 600;">决策:</span> {{ step.decision }}
|
|
38
38
|
</p>
|
|
39
|
-
<p v-if="step.userQuery" class="text-[11px] italic" style="color: #
|
|
39
|
+
<p v-if="step.userQuery" class="text-[11px] italic" style="color: #636366;">
|
|
40
40
|
"{{ step.userQuery }}"
|
|
41
41
|
</p>
|
|
42
42
|
</div>
|
|
43
43
|
|
|
44
|
-
<div v-if="step.duration" class="mt-1 text-[10px] font-mono-log" style="color: #
|
|
44
|
+
<div v-if="step.duration" class="mt-1 text-[10px] font-mono-log" style="color: #6B7280;">
|
|
45
45
|
{{ step.duration }}
|
|
46
46
|
</div>
|
|
47
47
|
</div>
|
|
@@ -62,13 +62,13 @@ const emit = defineEmits(['select'])
|
|
|
62
62
|
const hovered = ref(false)
|
|
63
63
|
|
|
64
64
|
const barColor = computed(() => {
|
|
65
|
-
const colors = { 'completed': '#
|
|
65
|
+
const colors = { 'completed': '#16A34A', 'in-progress': '#D97706', 'blocked': '#EA580C', 'failed': '#DC2626', 'pending': '#E5E5EA' }
|
|
66
66
|
return colors[props.step.status] || colors.pending
|
|
67
67
|
})
|
|
68
68
|
|
|
69
69
|
const cardStyle = computed(() => ({
|
|
70
|
-
background: props.isActive ? 'rgba(
|
|
71
|
-
border: `1px solid ${props.isActive ? 'rgba(251,191,36,0.2)' : '#
|
|
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
72
|
}))
|
|
73
73
|
|
|
74
74
|
const statusLabel = computed(() => {
|
|
@@ -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>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div v-if="data?.length" class="px-4 py-3">
|
|
3
|
+
<div v-for="group in data" :key="group.key" class="mb-4">
|
|
4
|
+
<h4 class="text-[9px] font-semibold uppercase tracking-[0.2em] mb-2 font-[JetBrains_Mono,monospace]" style="color: #6B7280;">
|
|
5
|
+
{{ group.icon }} {{ group.label }} ({{ group.files.length }})
|
|
6
|
+
</h4>
|
|
7
|
+
<div class="space-y-1">
|
|
8
|
+
<div
|
|
9
|
+
v-for="f in group.files"
|
|
10
|
+
:key="f.path"
|
|
11
|
+
class="flex items-center gap-2 px-2 py-1.5 rounded text-[11px] cursor-pointer hover:bg-[#FEF3C7] transition-colors"
|
|
12
|
+
@click="$emit('open-file', f)"
|
|
13
|
+
>
|
|
14
|
+
<svg class="w-3 h-3 flex-shrink-0" fill="none" stroke="#9CA3AF" viewBox="0 0 24 24">
|
|
15
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
|
|
16
|
+
</svg>
|
|
17
|
+
<span class="truncate" style="color: #1C1C1E;">{{ f.name }}</span>
|
|
18
|
+
<span class="ml-auto flex-shrink-0 font-mono text-[10px]" style="color: #9CA3AF;">{{ formatSize(f.size) }}</span>
|
|
19
|
+
<span class="flex-shrink-0 text-[10px]" style="color: #9CA3AF;">{{ relativeTime(f.mtime) }}</span>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
<n-empty v-else description="无文档" style="padding: 48px 0;" />
|
|
25
|
+
</template>
|
|
26
|
+
|
|
27
|
+
<script setup>
|
|
28
|
+
defineProps({ data: { type: Array, default: () => [] } })
|
|
29
|
+
defineEmits(['open-file'])
|
|
30
|
+
|
|
31
|
+
function formatSize(bytes) {
|
|
32
|
+
if (!bytes) return '—'
|
|
33
|
+
if (bytes < 1024) return bytes + ' B'
|
|
34
|
+
if (bytes < 1048576) return (bytes / 1024).toFixed(1) + ' KB'
|
|
35
|
+
return (bytes / 1048576).toFixed(1) + ' MB'
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function relativeTime(iso) {
|
|
39
|
+
if (!iso) return ''
|
|
40
|
+
const d = new Date(iso)
|
|
41
|
+
const diff = Date.now() - d.getTime()
|
|
42
|
+
if (diff < 60000) return '刚刚'
|
|
43
|
+
if (diff < 3600000) return `${Math.floor(diff / 60000)} 分钟前`
|
|
44
|
+
if (diff < 86400000) return `${Math.floor(diff / 3600000)} 小时前`
|
|
45
|
+
if (diff < 604800000) return `${Math.floor(diff / 86400000)} 天前`
|
|
46
|
+
return `${d.getMonth() + 1}/${d.getDate()}`
|
|
47
|
+
}
|
|
48
|
+
</script>
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div v-if="data" class="px-4 py-3">
|
|
3
|
+
<!-- Branch -->
|
|
4
|
+
<div class="mb-4">
|
|
5
|
+
<h4 class="text-[9px] font-semibold uppercase tracking-[0.2em] mb-1.5 font-[JetBrains_Mono,monospace]" style="color: #6B7280;">当前分支</h4>
|
|
6
|
+
<div class="text-[18px] font-bold font-[JetBrains_Mono,monospace]" style="color: #D97706;">{{ data.branch || '—' }}</div>
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
<!-- Commits -->
|
|
10
|
+
<div v-if="data.commits?.length" class="mb-4">
|
|
11
|
+
<h4 class="text-[9px] font-semibold uppercase tracking-[0.2em] mb-2 font-[JetBrains_Mono,monospace]" style="color: #6B7280;">最近提交</h4>
|
|
12
|
+
<div class="space-y-2">
|
|
13
|
+
<div v-for="c in data.commits" :key="c.hash" class="px-3 py-2 rounded-md" style="background: #F5F5F7;">
|
|
14
|
+
<div class="flex items-center gap-2">
|
|
15
|
+
<span class="text-[11px] font-mono font-semibold" style="color: #D97706;">{{ c.hash }}</span>
|
|
16
|
+
<span class="text-[11px] flex-1 truncate" style="color: #1C1C1E;">{{ c.message }}</span>
|
|
17
|
+
</div>
|
|
18
|
+
<div class="flex items-center gap-2 mt-1 text-[10px]" style="color: #6B7280;">
|
|
19
|
+
<span>{{ c.author }}</span>
|
|
20
|
+
<span>·</span>
|
|
21
|
+
<span>{{ relativeTime(c.date) }}</span>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<!-- Untracked -->
|
|
28
|
+
<div v-if="data.untracked?.length">
|
|
29
|
+
<h4 class="text-[9px] font-semibold uppercase tracking-[0.2em] mb-2 font-[JetBrains_Mono,monospace]" style="color: #6B7280;">未提交文件</h4>
|
|
30
|
+
<div class="space-y-1">
|
|
31
|
+
<div v-for="f in data.untracked" :key="f.file" class="flex items-center gap-2 px-2 py-1 rounded text-[11px] font-[JetBrains_Mono,monospace]">
|
|
32
|
+
<span class="font-semibold w-5 text-center" :style="{ color: statusColor(f.status) }">{{ f.status }}</span>
|
|
33
|
+
<span class="truncate" style="color: #1C1C1E;">{{ f.file }}</span>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
<n-empty v-else description="无 Git 信息" style="padding: 48px 0;" />
|
|
39
|
+
</template>
|
|
40
|
+
|
|
41
|
+
<script setup>
|
|
42
|
+
defineProps({ data: { type: Object, default: null } })
|
|
43
|
+
|
|
44
|
+
function statusColor(s) {
|
|
45
|
+
if (s.includes('M')) return '#F59E0B'
|
|
46
|
+
if (s.includes('A') || s === '?') return '#10B981'
|
|
47
|
+
if (s.includes('D')) return '#EF4444'
|
|
48
|
+
return '#9CA3AF'
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function relativeTime(iso) {
|
|
52
|
+
if (!iso) return ''
|
|
53
|
+
const d = new Date(iso)
|
|
54
|
+
const diff = Date.now() - d.getTime()
|
|
55
|
+
if (diff < 60000) return '刚刚'
|
|
56
|
+
if (diff < 3600000) return `${Math.floor(diff / 60000)} 分钟前`
|
|
57
|
+
if (diff < 86400000) return `${Math.floor(diff / 3600000)} 小时前`
|
|
58
|
+
if (diff < 604800000) return `${Math.floor(diff / 86400000)} 天前`
|
|
59
|
+
return `${d.getMonth() + 1}/${d.getDate()}`
|
|
60
|
+
}
|
|
61
|
+
</script>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div v-if="data" class="px-4 py-3">
|
|
3
|
+
<!-- Frameworks -->
|
|
4
|
+
<div v-if="data.frameworks?.length" class="mb-4">
|
|
5
|
+
<h4 class="text-[9px] font-semibold uppercase tracking-[0.2em] mb-2 font-[JetBrains_Mono,monospace]" style="color: #6B7280;">识别框架</h4>
|
|
6
|
+
<div class="flex flex-wrap gap-2">
|
|
7
|
+
<n-tag v-for="f in data.frameworks" :key="f" type="warning" size="small" round>{{ f }}</n-tag>
|
|
8
|
+
</div>
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
<!-- Dependencies -->
|
|
12
|
+
<div v-if="depEntries.length" class="mb-4">
|
|
13
|
+
<h4 class="text-[9px] font-semibold uppercase tracking-[0.2em] mb-2 font-[JetBrains_Mono,monospace]" style="color: #6B7280;">dependencies ({{ depEntries.length }})</h4>
|
|
14
|
+
<div class="space-y-1">
|
|
15
|
+
<div v-for="[name, ver] in depEntries" :key="name" class="flex items-center gap-2 px-2 py-1 rounded text-[11px]" style="background: #F5F5F7;">
|
|
16
|
+
<span class="font-mono font-medium truncate" style="color: #1C1C1E;">{{ name }}</span>
|
|
17
|
+
<span class="font-mono ml-auto flex-shrink-0" style="color: #9CA3AF;">{{ ver }}</span>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
<!-- Dev Dependencies -->
|
|
23
|
+
<div v-if="devDepEntries.length">
|
|
24
|
+
<h4 class="text-[9px] font-semibold uppercase tracking-[0.2em] mb-2 font-[JetBrains_Mono,monospace]" style="color: #6B7280;">devDependencies ({{ devDepEntries.length }})</h4>
|
|
25
|
+
<div class="space-y-1">
|
|
26
|
+
<div v-for="[name, ver] in devDepEntries" :key="name" class="flex items-center gap-2 px-2 py-1 rounded text-[11px]" style="background: #F5F5F7;">
|
|
27
|
+
<span class="font-mono font-medium truncate" style="color: #1C1C1E;">{{ name }}</span>
|
|
28
|
+
<span class="font-mono ml-auto flex-shrink-0" style="color: #9CA3AF;">{{ ver }}</span>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
<n-empty v-else description="无依赖信息" style="padding: 48px 0;" />
|
|
34
|
+
</template>
|
|
35
|
+
|
|
36
|
+
<script setup>
|
|
37
|
+
import { computed } from 'vue'
|
|
38
|
+
|
|
39
|
+
const props = defineProps({ data: { type: Object, default: null } })
|
|
40
|
+
|
|
41
|
+
const depEntries = computed(() => props.data ? Object.entries(props.data.dependencies || {}) : [])
|
|
42
|
+
const devDepEntries = computed(() => props.data ? Object.entries(props.data.devDependencies || {}) : [])
|
|
43
|
+
</script>
|
|
@@ -13,7 +13,12 @@ export function useDashboard() {
|
|
|
13
13
|
logs: [],
|
|
14
14
|
isPanelOpen: true,
|
|
15
15
|
executingProject: null,
|
|
16
|
-
isLoading: true
|
|
16
|
+
isLoading: true,
|
|
17
|
+
activeTab: 'pipeline',
|
|
18
|
+
docs: { groups: [] },
|
|
19
|
+
selectedDocFile: null,
|
|
20
|
+
docContent: '',
|
|
21
|
+
docLoading: false
|
|
17
22
|
})
|
|
18
23
|
|
|
19
24
|
/**
|
|
@@ -21,13 +26,13 @@ export function useDashboard() {
|
|
|
21
26
|
* @param {string} name - Project name
|
|
22
27
|
* @returns {object|null} Project or null
|
|
23
28
|
*/
|
|
24
|
-
function getProject(
|
|
25
|
-
return state.projects.find(p => p.
|
|
29
|
+
function getProject(path) {
|
|
30
|
+
return state.projects.find(p => p.path === path) || null
|
|
26
31
|
}
|
|
27
32
|
|
|
28
33
|
/**
|
|
29
34
|
* Select a project as active
|
|
30
|
-
* @param {object|string} project - Project object or
|
|
35
|
+
* @param {object|string} project - Project object or path
|
|
31
36
|
*/
|
|
32
37
|
function selectProject(project) {
|
|
33
38
|
const proj = typeof project === 'string'
|
|
@@ -38,6 +43,8 @@ export function useDashboard() {
|
|
|
38
43
|
state.activeProject = proj
|
|
39
44
|
state.activeStep = null
|
|
40
45
|
state.logs = []
|
|
46
|
+
state.selectedDocFile = null
|
|
47
|
+
state.docContent = ''
|
|
41
48
|
|
|
42
49
|
// Load initial logs from project state if available
|
|
43
50
|
if (proj.state?.progress?.currentLogs) {
|
|
@@ -115,11 +122,38 @@ export function useDashboard() {
|
|
|
115
122
|
|
|
116
123
|
// Restore active project if it still exists
|
|
117
124
|
if (state.activeProject) {
|
|
118
|
-
const updated = getProject(state.activeProject.
|
|
125
|
+
const updated = getProject(state.activeProject.path)
|
|
119
126
|
if (updated) {
|
|
120
127
|
state.activeProject = updated
|
|
128
|
+
return
|
|
121
129
|
}
|
|
122
130
|
}
|
|
131
|
+
|
|
132
|
+
state.activeProject = state.projects[0] || null
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Merge a single project update into the current project list.
|
|
137
|
+
* @param {object} project - Partial project payload containing at least path
|
|
138
|
+
*/
|
|
139
|
+
function updateProject(project) {
|
|
140
|
+
if (!project?.path) return
|
|
141
|
+
|
|
142
|
+
const index = state.projects.findIndex(p => p.path === project.path)
|
|
143
|
+
if (index === -1) return
|
|
144
|
+
|
|
145
|
+
const updated = {
|
|
146
|
+
...state.projects[index],
|
|
147
|
+
...project,
|
|
148
|
+
state: project.state ?? state.projects[index].state,
|
|
149
|
+
overview: project.overview ?? state.projects[index].overview
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
state.projects.splice(index, 1, updated)
|
|
153
|
+
|
|
154
|
+
if (state.activeProject?.path === project.path) {
|
|
155
|
+
state.activeProject = updated
|
|
156
|
+
}
|
|
123
157
|
}
|
|
124
158
|
|
|
125
159
|
/**
|
|
@@ -141,6 +175,7 @@ export function useDashboard() {
|
|
|
141
175
|
|
|
142
176
|
// Computed properties
|
|
143
177
|
const activeProjectName = computed(() => state.activeProject?.name || null)
|
|
178
|
+
const activeProjectPath = computed(() => state.activeProject?.path || null)
|
|
144
179
|
const activeProjectStage = computed(() => state.activeProject?.state?.currentStage || null)
|
|
145
180
|
const hasProjects = computed(() => state.projects.length > 0)
|
|
146
181
|
const activeProjectSteps = computed(() => {
|
|
@@ -161,11 +196,18 @@ export function useDashboard() {
|
|
|
161
196
|
openPanel,
|
|
162
197
|
closePanel,
|
|
163
198
|
updateProjects,
|
|
199
|
+
updateProject,
|
|
164
200
|
setExecuting,
|
|
165
201
|
isExecuting,
|
|
166
202
|
activeProjectName,
|
|
203
|
+
activeProjectPath,
|
|
167
204
|
activeProjectStage,
|
|
168
205
|
hasProjects,
|
|
169
|
-
activeProjectSteps
|
|
206
|
+
activeProjectSteps,
|
|
207
|
+
setActiveTab(tab) { state.activeTab = tab },
|
|
208
|
+
updateDocs(docs) { state.docs = docs },
|
|
209
|
+
selectDocFile(file) { state.selectedDocFile = file },
|
|
210
|
+
setDocContent(content) { state.docContent = content },
|
|
211
|
+
setDocLoading(loading) { state.docLoading = loading }
|
|
170
212
|
}
|
|
171
213
|
}
|
|
@@ -14,15 +14,17 @@ export function useKeyboard(options = {}) {
|
|
|
14
14
|
onEnter = null,
|
|
15
15
|
onArrowUp = null,
|
|
16
16
|
onArrowDown = null,
|
|
17
|
-
disabled = false
|
|
17
|
+
disabled: initialDisabled = false
|
|
18
18
|
} = options
|
|
19
19
|
|
|
20
|
+
let isDisabled = initialDisabled
|
|
21
|
+
|
|
20
22
|
/**
|
|
21
23
|
* Handle keyboard events
|
|
22
24
|
* @param {KeyboardEvent} event
|
|
23
25
|
*/
|
|
24
26
|
function handleKeyDown(event) {
|
|
25
|
-
if (
|
|
27
|
+
if (isDisabled) return
|
|
26
28
|
|
|
27
29
|
// Ignore if in input field
|
|
28
30
|
const target = event.target
|
|
@@ -87,8 +89,8 @@ export function useKeyboard(options = {}) {
|
|
|
87
89
|
})
|
|
88
90
|
|
|
89
91
|
return {
|
|
90
|
-
disable: () => {
|
|
91
|
-
enable: () => {
|
|
92
|
+
disable: () => { isDisabled = true },
|
|
93
|
+
enable: () => { isDisabled = false }
|
|
92
94
|
}
|
|
93
95
|
}
|
|
94
96
|
|