sillyspec 3.9.1 → 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.
Files changed (64) hide show
  1. package/.claude/skills/sillyspec-commit/SKILL.md +1 -1
  2. package/.claude/skills/sillyspec-continue/SKILL.md +1 -1
  3. package/.claude/skills/sillyspec-explore/SKILL.md +9 -0
  4. package/.claude/skills/sillyspec-plan/SKILL.md +0 -35
  5. package/.claude/skills/sillyspec-workspace/SKILL.md +1 -1
  6. package/README.md +7 -6
  7. package/SKILL.md +15 -10
  8. package/package.json +1 -1
  9. package/packages/dashboard/dist/assets/index-BcM2J-hv.css +1 -0
  10. package/packages/dashboard/dist/assets/{index-RsLVPAy7.js → index-DpLHK4jv.js} +974 -974
  11. package/packages/dashboard/dist/index.html +16 -17
  12. package/packages/dashboard/dist/prototype-dashboard.html +836 -0
  13. package/packages/dashboard/dist/prototype-overview.html +256 -0
  14. package/packages/dashboard/public/prototype-dashboard.html +836 -0
  15. package/packages/dashboard/public/prototype-overview.html +256 -0
  16. package/packages/dashboard/server/index.js +18 -13
  17. package/packages/dashboard/server/parser.js +109 -1
  18. package/packages/dashboard/server/watcher.js +14 -6
  19. package/packages/dashboard/src/App.vue +414 -186
  20. package/packages/dashboard/src/components/ActionBar.vue +10 -1
  21. package/packages/dashboard/src/components/CommandPalette.vue +5 -1
  22. package/packages/dashboard/src/components/DocPreview.vue +105 -8
  23. package/packages/dashboard/src/components/DocTree.vue +75 -19
  24. package/packages/dashboard/src/components/HResizeHandle.vue +48 -0
  25. package/packages/dashboard/src/components/PipelineView.vue +23 -4
  26. package/packages/dashboard/src/components/ProjectCard.vue +187 -0
  27. package/packages/dashboard/src/components/ProjectOverview.vue +113 -139
  28. package/packages/dashboard/src/components/VResizeHandle.vue +61 -0
  29. package/packages/dashboard/src/composables/useDashboard.js +28 -0
  30. package/packages/dashboard/src/composables/useLayout.js +131 -0
  31. package/src/index.js +7 -0
  32. package/src/init.js +17 -10
  33. package/src/migrate.js +5 -5
  34. package/src/progress.js +2 -1
  35. package/src/run.js +72 -61
  36. package/src/stages/brainstorm.js +28 -3
  37. package/src/stages/execute.js +52 -27
  38. package/src/stages/explore.js +34 -0
  39. package/src/stages/index.js +3 -1
  40. package/src/stages/plan.js +86 -14
  41. package/src/stages/scan.js +11 -11
  42. package/src/stages/status.js +1 -1
  43. package/.sillyspec/changes/archive/2026-04-08-derive-state/design.md +0 -97
  44. package/.sillyspec/changes/archive/2026-04-08-derive-state/plan.md +0 -51
  45. package/.sillyspec/changes/archive/2026-04-08-derive-state/proposal.md +0 -29
  46. package/.sillyspec/changes/archive/2026-04-08-derive-state/requirements.md +0 -34
  47. package/.sillyspec/changes/archive/2026-04-08-derive-state/tasks.md +0 -13
  48. package/.sillyspec/changes/archive/2026-04-08-derive-state/verify-result.md +0 -43
  49. package/.sillyspec/changes/auto-mode/design.md +0 -50
  50. package/.sillyspec/changes/auto-mode/proposal.md +0 -19
  51. package/.sillyspec/changes/auto-mode/requirements.md +0 -21
  52. package/.sillyspec/changes/auto-mode/tasks.md +0 -7
  53. package/.sillyspec/changes/brainstorm-archive/2026-04-05-dashboard-design.md +0 -206
  54. package/.sillyspec/changes/brainstorm-archive/2026-04-05-unified-docs-design.md +0 -199
  55. package/.sillyspec/changes/dashboard/design.md +0 -219
  56. package/.sillyspec/changes/dashboard/design.md.braindraft +0 -206
  57. package/.sillyspec/changes/run-command-design/design.md +0 -1230
  58. package/.sillyspec/changes/unified-docs-design/design.md +0 -199
  59. package/.sillyspec/docs/sillyspec/scan/.gitkeep +0 -0
  60. package/.sillyspec/knowledge/INDEX.md +0 -8
  61. package/.sillyspec/knowledge/uncategorized.md +0 -3
  62. package/.sillyspec/plans/2026-04-05-dashboard.md +0 -737
  63. package/.sillyspec/projects/sillyspec.yaml +0 -3
  64. package/packages/dashboard/dist/assets/index-CntACGUN.css +0 -1
@@ -0,0 +1,836 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Dashboard 双层布局原型(可拖动)</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+ body {
14
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
15
+ background: #F5F5F7;
16
+ height: 100vh;
17
+ display: flex;
18
+ flex-direction: column;
19
+ overflow: hidden;
20
+ }
21
+
22
+ /* ========== 概览区域 - 默认 30% ========== */
23
+ .overview-section {
24
+ height: 30%;
25
+ min-height: 200px;
26
+ max-height: 70%;
27
+ background: #FFFFFF;
28
+ border-bottom: 2px solid #2A3040;
29
+ padding: 16px 24px;
30
+ display: flex;
31
+ flex-direction: column;
32
+ }
33
+
34
+ .section-header {
35
+ display: flex;
36
+ justify-content: space-between;
37
+ align-items: center;
38
+ margin-bottom: 16px;
39
+ }
40
+ .section-title {
41
+ font-size: 18px;
42
+ font-weight: 600;
43
+ color: #1A1A1A;
44
+ display: flex;
45
+ align-items: center;
46
+ gap: 8px;
47
+ }
48
+ .section-title .badge {
49
+ font-size: 12px;
50
+ padding: 2px 8px;
51
+ background: #E5E7EB;
52
+ border-radius: 10px;
53
+ color: #6B7280;
54
+ font-weight: 500;
55
+ }
56
+ .section-actions {
57
+ display: flex;
58
+ gap: 8px;
59
+ align-items: center;
60
+ }
61
+
62
+ .btn {
63
+ padding: 6px 12px;
64
+ border-radius: 6px;
65
+ border: none;
66
+ cursor: pointer;
67
+ font-size: 13px;
68
+ font-weight: 500;
69
+ transition: all 0.2s;
70
+ }
71
+ .btn:hover {
72
+ opacity: 0.9;
73
+ }
74
+ .btn-primary {
75
+ background: #D97706;
76
+ color: white;
77
+ }
78
+ .btn-secondary {
79
+ background: #E5E7EB;
80
+ color: #374151;
81
+ }
82
+ .btn-icon {
83
+ padding: 6px;
84
+ width: 32px;
85
+ height: 32px;
86
+ display: flex;
87
+ align-items: center;
88
+ justify-content: center;
89
+ background: #F3F4F6;
90
+ border-radius: 6px;
91
+ }
92
+ .btn-icon:hover {
93
+ background: #E5E7EB;
94
+ }
95
+
96
+ /* 水平滚动卡片容器 */
97
+ .cards-container {
98
+ flex: 1;
99
+ display: flex;
100
+ gap: 16px;
101
+ overflow-x: auto;
102
+ overflow-y: hidden;
103
+ padding-bottom: 8px;
104
+ }
105
+ .cards-container::-webkit-scrollbar {
106
+ height: 8px;
107
+ }
108
+ .cards-container::-webkit-scrollbar-track {
109
+ background: #F3F4F6;
110
+ border-radius: 4px;
111
+ }
112
+ .cards-container::-webkit-scrollbar-thumb {
113
+ background: #D1D5DB;
114
+ border-radius: 4px;
115
+ }
116
+
117
+ /* 项目卡片 */
118
+ .project-card {
119
+ width: 280px;
120
+ height: 120px;
121
+ background: white;
122
+ border: 2px solid #E5E7EB;
123
+ border-radius: 12px;
124
+ padding: 16px;
125
+ display: flex;
126
+ flex-direction: column;
127
+ justify-content: space-between;
128
+ cursor: pointer;
129
+ transition: all 0.2s;
130
+ flex-shrink: 0;
131
+ }
132
+ .project-card:hover {
133
+ border-color: #D97706;
134
+ box-shadow: 0 4px 12px rgba(217, 119, 6, 0.15);
135
+ }
136
+ .project-card.selected {
137
+ border-color: #D97706;
138
+ box-shadow: 0 0 0 3px rgba(217, 119, 6, 0.2);
139
+ }
140
+
141
+ .card-header {
142
+ display: flex;
143
+ justify-content: space-between;
144
+ align-items: flex-start;
145
+ }
146
+ .project-name {
147
+ font-size: 16px;
148
+ font-weight: 600;
149
+ color: #1A1A1A;
150
+ }
151
+ .last-active {
152
+ font-size: 11px;
153
+ color: #9CA3AF;
154
+ }
155
+
156
+ .stage-badge {
157
+ display: inline-block;
158
+ padding: 4px 10px;
159
+ border-radius: 12px;
160
+ font-size: 12px;
161
+ font-weight: 500;
162
+ }
163
+ .stage-badge.in-progress {
164
+ background: #DBEAFE;
165
+ color: #1D4ED8;
166
+ }
167
+ .stage-badge.completed {
168
+ background: #D1FAE5;
169
+ color: #047857;
170
+ }
171
+ .stage-badge.pending {
172
+ background: #F3F4F6;
173
+ color: #6B7280;
174
+ }
175
+
176
+ .progress-section {
177
+ display: flex;
178
+ align-items: center;
179
+ gap: 8px;
180
+ }
181
+ .progress-bar {
182
+ flex: 1;
183
+ height: 6px;
184
+ background: #E5E7EB;
185
+ border-radius: 3px;
186
+ overflow: hidden;
187
+ }
188
+ .progress-fill {
189
+ height: 100%;
190
+ border-radius: 3px;
191
+ transition: width 0.3s;
192
+ }
193
+ .progress-fill.in-progress { background: #3B82F6; }
194
+ .progress-fill.completed { background: #10B981; }
195
+ .progress-fill.pending { background: #9CA3AF; }
196
+ .progress-text {
197
+ font-size: 12px;
198
+ font-weight: 600;
199
+ color: #374151;
200
+ }
201
+
202
+ /* ========== 垂直拖动分割线(概览 ↔ 详情) ========== */
203
+ .v-resize-handle {
204
+ height: 6px;
205
+ background: #2A3040;
206
+ cursor: row-resize;
207
+ flex-shrink: 0;
208
+ position: relative;
209
+ transition: background 0.2s;
210
+ }
211
+ .v-resize-handle:hover, .v-resize-handle.dragging {
212
+ background: #D97706;
213
+ }
214
+ .v-resize-handle::after {
215
+ content: '';
216
+ position: absolute;
217
+ top: 50%;
218
+ left: 50%;
219
+ transform: translate(-50%, -50%);
220
+ width: 40px;
221
+ height: 4px;
222
+ background: rgba(255,255,255,0.3);
223
+ border-radius: 2px;
224
+ }
225
+
226
+ /* ========== 详情区域 - 默认 70% ========== */
227
+ .detail-section {
228
+ flex: 1;
229
+ background: #FAFAFA;
230
+ display: flex;
231
+ flex-direction: column;
232
+ overflow: hidden;
233
+ min-height: 100px;
234
+ }
235
+
236
+ .detail-header {
237
+ display: flex;
238
+ justify-content: space-between;
239
+ align-items: center;
240
+ padding: 12px 24px;
241
+ background: white;
242
+ border-bottom: 1px solid #E5E7EB;
243
+ flex-shrink: 0;
244
+ }
245
+ .detail-title {
246
+ font-size: 14px;
247
+ font-weight: 600;
248
+ color: #374151;
249
+ }
250
+ .detail-tabs {
251
+ display: flex;
252
+ gap: 4px;
253
+ }
254
+ .detail-tab {
255
+ padding: 6px 12px;
256
+ border-radius: 6px;
257
+ font-size: 13px;
258
+ color: #6B7280;
259
+ cursor: pointer;
260
+ transition: all 0.2s;
261
+ }
262
+ .detail-tab:hover {
263
+ background: #F3F4F6;
264
+ }
265
+ .detail-tab.active {
266
+ background: #D97706;
267
+ color: white;
268
+ }
269
+
270
+ /* 三栏布局容器 */
271
+ .detail-content {
272
+ flex: 1;
273
+ display: flex;
274
+ overflow: hidden;
275
+ }
276
+
277
+ /* 水平拖动分割线 */
278
+ .h-resize-handle {
279
+ width: 4px;
280
+ background: #E5E7EB;
281
+ cursor: col-resize;
282
+ flex-shrink: 0;
283
+ position: relative;
284
+ transition: background 0.2s;
285
+ }
286
+ .h-resize-handle:hover, .h-resize-handle.dragging {
287
+ background: #D97706;
288
+ }
289
+
290
+ /* 左栏 - 项目信息 默认 33.3% */
291
+ .detail-left {
292
+ width: 33.33%;
293
+ min-width: 150px;
294
+ background: white;
295
+ padding: 16px;
296
+ overflow-y: auto;
297
+ }
298
+ .detail-section-title {
299
+ font-size: 12px;
300
+ font-weight: 600;
301
+ color: #9CA3AF;
302
+ text-transform: uppercase;
303
+ margin-bottom: 12px;
304
+ }
305
+ .detail-item {
306
+ margin-bottom: 16px;
307
+ }
308
+ .detail-label {
309
+ font-size: 11px;
310
+ color: #9CA3AF;
311
+ margin-bottom: 4px;
312
+ }
313
+ .detail-value {
314
+ font-size: 14px;
315
+ color: #1A1A1A;
316
+ font-weight: 500;
317
+ }
318
+
319
+ /* 中栏 - Pipeline 默认 33.3% */
320
+ .detail-center {
321
+ width: 33.33%;
322
+ min-width: 200px;
323
+ background: white;
324
+ padding: 16px;
325
+ overflow-y: auto;
326
+ }
327
+ .stage-timeline {
328
+ display: flex;
329
+ flex-direction: column;
330
+ gap: 12px;
331
+ }
332
+ .stage-item {
333
+ display: flex;
334
+ align-items: center;
335
+ gap: 12px;
336
+ padding: 12px;
337
+ border: 1px solid #E5E7EB;
338
+ border-radius: 8px;
339
+ cursor: pointer;
340
+ transition: all 0.2s;
341
+ }
342
+ .stage-item:hover {
343
+ background: #F9FAFB;
344
+ }
345
+ .stage-item.active {
346
+ border-color: #D97706;
347
+ background: #FFFBEB;
348
+ }
349
+ .stage-icon {
350
+ width: 32px;
351
+ height: 32px;
352
+ border-radius: 8px;
353
+ display: flex;
354
+ align-items: center;
355
+ justify-content: center;
356
+ font-size: 14px;
357
+ flex-shrink: 0;
358
+ }
359
+ .stage-icon.completed {
360
+ background: #D1FAE5;
361
+ color: #047857;
362
+ }
363
+ .stage-icon.in-progress {
364
+ background: #DBEAFE;
365
+ color: #1D4ED8;
366
+ }
367
+ .stage-icon.pending {
368
+ background: #F3F4F6;
369
+ color: #9CA3AF;
370
+ }
371
+ .stage-info {
372
+ flex: 1;
373
+ min-width: 0;
374
+ }
375
+ .stage-name {
376
+ font-size: 14px;
377
+ font-weight: 500;
378
+ color: #1A1A1A;
379
+ }
380
+ .stage-status {
381
+ font-size: 12px;
382
+ color: #9CA3AF;
383
+ }
384
+
385
+ /* 右栏 - 详情面板 默认 33.3% */
386
+ .detail-right {
387
+ flex: 1;
388
+ min-width: 200px;
389
+ background: white;
390
+ padding: 16px;
391
+ overflow-y: auto;
392
+ }
393
+ .log-entry {
394
+ font-family: 'JetBrains Mono', monospace;
395
+ font-size: 12px;
396
+ padding: 8px;
397
+ background: #F9FAFB;
398
+ border-radius: 4px;
399
+ margin-bottom: 8px;
400
+ color: #374151;
401
+ }
402
+ .log-entry.success {
403
+ color: #047857;
404
+ background: #D1FAE5;
405
+ }
406
+ .log-entry.error {
407
+ color: #DC2626;
408
+ background: #FEE2E2;
409
+ }
410
+
411
+ /* 尺寸显示 */
412
+ .size-indicator {
413
+ position: fixed;
414
+ bottom: 20px;
415
+ left: 20px;
416
+ background: rgba(0,0,0,0.7);
417
+ color: white;
418
+ padding: 8px 12px;
419
+ border-radius: 6px;
420
+ font-size: 11px;
421
+ font-family: monospace;
422
+ opacity: 0;
423
+ transition: opacity 0.2s;
424
+ }
425
+ .size-indicator.visible {
426
+ opacity: 1;
427
+ }
428
+
429
+ /* 淡入动画 */
430
+ @keyframes fadeIn {
431
+ from { opacity: 0; transform: translateY(4px); }
432
+ to { opacity: 1; transform: translateY(0); }
433
+ }
434
+ .fade-in {
435
+ animation: fadeIn 0.2s ease-out;
436
+ }
437
+
438
+ /* 拖动时禁用选择 */
439
+ body.resizing {
440
+ user-select: none;
441
+ }
442
+ body.resizing * {
443
+ cursor: inherit !important;
444
+ }
445
+ </style>
446
+ </head>
447
+ <body>
448
+ <!-- 概览区域 -->
449
+ <div class="overview-section" id="overview">
450
+ <div class="section-header">
451
+ <h1 class="section-title">
452
+ 项目概览
453
+ <span class="badge">4 个项目</span>
454
+ </h1>
455
+ <div class="section-actions">
456
+ <button class="btn btn-icon" title="刷新">↻</button>
457
+ <button class="btn btn-primary">+ 添加项目</button>
458
+ <button class="btn btn-secondary" id="resetLayout">重置布局</button>
459
+ </div>
460
+ </div>
461
+ <div class="cards-container">
462
+ <!-- 项目卡片 1 -->
463
+ <div class="project-card selected" data-project="sillyspec">
464
+ <div class="card-header">
465
+ <span class="project-name">sillyspec</span>
466
+ <span class="last-active">2分钟前</span>
467
+ </div>
468
+ <div>
469
+ <span class="stage-badge in-progress">需求探索</span>
470
+ </div>
471
+ <div class="progress-section">
472
+ <div class="progress-bar">
473
+ <div class="progress-fill in-progress" style="width: 40%"></div>
474
+ </div>
475
+ <span class="progress-text">40%</span>
476
+ </div>
477
+ </div>
478
+ <!-- 项目卡片 2 -->
479
+ <div class="project-card" data-project="my-blog">
480
+ <div class="card-header">
481
+ <span class="project-name">my-blog</span>
482
+ <span class="last-active">15分钟前</span>
483
+ </div>
484
+ <div>
485
+ <span class="stage-badge in-progress">实现计划</span>
486
+ </div>
487
+ <div class="progress-section">
488
+ <div class="progress-bar">
489
+ <div class="progress-fill in-progress" style="width: 75%"></div>
490
+ </div>
491
+ <span class="progress-text">75%</span>
492
+ </div>
493
+ </div>
494
+ <!-- 项目卡片 3 -->
495
+ <div class="project-card" data-project="user-service">
496
+ <div class="card-header">
497
+ <span class="project-name">user-service</span>
498
+ <span class="last-active">1小时前</span>
499
+ </div>
500
+ <div>
501
+ <span class="stage-badge completed">验证确认</span>
502
+ </div>
503
+ <div class="progress-section">
504
+ <div class="progress-bar">
505
+ <div class="progress-fill completed" style="width: 100%"></div>
506
+ </div>
507
+ <span class="progress-text">100%</span>
508
+ </div>
509
+ </div>
510
+ <!-- 项目卡片 4 -->
511
+ <div class="project-card" data-project="payment-api">
512
+ <div class="card-header">
513
+ <span class="project-name">payment-api</span>
514
+ <span class="last-active">昨天</span>
515
+ </div>
516
+ <div>
517
+ <span class="stage-badge pending">未开始</span>
518
+ </div>
519
+ <div class="progress-section">
520
+ <div class="progress-bar">
521
+ <div class="progress-fill pending" style="width: 0%"></div>
522
+ </div>
523
+ <span class="progress-text">0%</span>
524
+ </div>
525
+ </div>
526
+ </div>
527
+ </div>
528
+
529
+ <!-- 垂直拖动分割线 -->
530
+ <div class="v-resize-handle" id="vResizeHandle"></div>
531
+
532
+ <!-- 详情区域 -->
533
+ <div class="detail-section" id="detail">
534
+ <div class="detail-header">
535
+ <span class="detail-title">sillyspec / 需求探索</span>
536
+ <div class="detail-tabs">
537
+ <span class="detail-tab active">Pipeline</span>
538
+ <span class="detail-tab">文档</span>
539
+ <span class="detail-tab">日志</span>
540
+ </div>
541
+ </div>
542
+ <div class="detail-content fade-in">
543
+ <!-- 左栏:项目信息 -->
544
+ <div class="detail-left" id="detailLeft">
545
+ <div class="detail-section-title">项目信息</div>
546
+ <div class="detail-item">
547
+ <div class="detail-label">项目名称</div>
548
+ <div class="detail-value">sillyspec</div>
549
+ </div>
550
+ <div class="detail-item">
551
+ <div class="detail-label">路径</div>
552
+ <div class="detail-value">/Users/qinyi/...</div>
553
+ </div>
554
+ <div class="detail-item">
555
+ <div class="detail-label">当前阶段</div>
556
+ <div class="detail-value">需求探索</div>
557
+ </div>
558
+ <div class="detail-item">
559
+ <div class="detail-label">进度</div>
560
+ <div class="detail-value">4/10 步骤</div>
561
+ </div>
562
+ <div class="detail-item">
563
+ <div class="detail-label">耗时</div>
564
+ <div class="detail-value">8 分钟</div>
565
+ </div>
566
+ </div>
567
+
568
+ <!-- 水平拖动分割线 1 -->
569
+ <div class="h-resize-handle" id="hResizeHandle1"></div>
570
+
571
+ <!-- 中栏:Pipeline -->
572
+ <div class="detail-center" id="detailCenter">
573
+ <div class="detail-section-title">阶段流程</div>
574
+ <div class="stage-timeline">
575
+ <div class="stage-item">
576
+ <div class="stage-icon completed">✓</div>
577
+ <div class="stage-info">
578
+ <div class="stage-name">✅ 代码扫描</div>
579
+ <div class="stage-status">已完成 · 8分钟</div>
580
+ </div>
581
+ </div>
582
+ <div class="stage-item active">
583
+ <div class="stage-icon in-progress">◷</div>
584
+ <div class="stage-info">
585
+ <div class="stage-name">🧠 需求探索</div>
586
+ <div class="stage-status">进行中 · 步骤 4/10</div>
587
+ </div>
588
+ </div>
589
+ <div class="stage-item">
590
+ <div class="stage-icon pending">○</div>
591
+ <div class="stage-info">
592
+ <div class="stage-name">📐 实现计划</div>
593
+ <div class="stage-status">未开始</div>
594
+ </div>
595
+ </div>
596
+ <div class="stage-item">
597
+ <div class="stage-icon pending">○</div>
598
+ <div class="stage-info">
599
+ <div class="stage-name">⚡ 波次执行</div>
600
+ <div class="stage-status">未开始</div>
601
+ </div>
602
+ </div>
603
+ <div class="stage-item">
604
+ <div class="stage-icon pending">○</div>
605
+ <div class="stage-info">
606
+ <div class="stage-name">🔍 验证确认</div>
607
+ <div class="stage-status">未开始</div>
608
+ </div>
609
+ </div>
610
+ </div>
611
+ </div>
612
+
613
+ <!-- 水平拖动分割线 2 -->
614
+ <div class="h-resize-handle" id="hResizeHandle2"></div>
615
+
616
+ <!-- 右栏:日志 -->
617
+ <div class="detail-right" id="detailRight">
618
+ <div class="detail-section-title">最近活动</div>
619
+ <div class="log-entry">✅ Step 3 完成:协作与复用检查</div>
620
+ <div class="log-entry">✅ Step 4 完成:原型/设计图分析</div>
621
+ <div class="log-entry">✅ Step 5 完成:需求范围评估</div>
622
+ <div class="log-entry">◷ Step 6 进行中:对话式探索...</div>
623
+ <div class="log-entry">📝 正在收集需求细节...</div>
624
+ <div class="log-entry">📝 分析信息架构...</div>
625
+ <div class="log-entry">📝 准备设计方案...</div>
626
+ </div>
627
+ </div>
628
+ </div>
629
+
630
+ <!-- 尺寸指示器 -->
631
+ <div class="size-indicator" id="sizeIndicator">概览 30% | 详情 70%</div>
632
+
633
+ <script>
634
+ // 常量
635
+ const STORAGE_KEY = 'dashboard-layout-v2';
636
+ const DEFAULT_OVERVIEW_HEIGHT = 30; // %
637
+ const DEFAULT_COLUMN_WIDTHS = [33.33, 33.33, 33.33]; // %
638
+
639
+ // 状态
640
+ let layout = {
641
+ overviewHeight: DEFAULT_OVERVIEW_HEIGHT,
642
+ columnWidths: [...DEFAULT_COLUMN_WIDTHS]
643
+ };
644
+
645
+ // DOM 元素
646
+ const overview = document.getElementById('overview');
647
+ const detail = document.getElementById('detail');
648
+ const vResizeHandle = document.getElementById('vResizeHandle');
649
+ const detailLeft = document.getElementById('detailLeft');
650
+ const detailCenter = document.getElementById('detailCenter');
651
+ const detailRight = document.getElementById('detailRight');
652
+ const hResizeHandle1 = document.getElementById('hResizeHandle1');
653
+ const hResizeHandle2 = document.getElementById('hResizeHandle2');
654
+ const sizeIndicator = document.getElementById('sizeIndicator');
655
+ const resetBtn = document.getElementById('resetLayout');
656
+
657
+ // 加载保存的布局
658
+ function loadLayout() {
659
+ try {
660
+ const saved = localStorage.getItem(STORAGE_KEY);
661
+ if (saved) {
662
+ layout = JSON.parse(saved);
663
+ applyLayout();
664
+ }
665
+ } catch (e) {
666
+ console.error('Failed to load layout:', e);
667
+ }
668
+ }
669
+
670
+ // 保存布局
671
+ function saveLayout() {
672
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(layout));
673
+ }
674
+
675
+ // 应用布局
676
+ function applyLayout() {
677
+ overview.style.height = layout.overviewHeight + '%';
678
+ updateColumnWidths();
679
+ }
680
+
681
+ // 更新三栏宽度
682
+ function updateColumnWidths() {
683
+ detailLeft.style.width = layout.columnWidths[0] + '%';
684
+ detailCenter.style.width = layout.columnWidths[1] + '%';
685
+ // detailRight 使用 flex: 1 自动填充
686
+ }
687
+
688
+ // 显示尺寸指示器
689
+ function showSizeIndicator(text) {
690
+ sizeIndicator.textContent = text;
691
+ sizeIndicator.classList.add('visible');
692
+ clearTimeout(sizeIndicator.timeout);
693
+ sizeIndicator.timeout = setTimeout(() => {
694
+ sizeIndicator.classList.remove('visible');
695
+ }, 1500);
696
+ }
697
+
698
+ // 垂直拖动(概览 ↔ 详情)
699
+ function initVResize() {
700
+ let startY, startHeight;
701
+
702
+ vResizeHandle.addEventListener('mousedown', (e) => {
703
+ e.preventDefault();
704
+ document.body.classList.add('resizing');
705
+ vResizeHandle.classList.add('dragging');
706
+ startY = e.clientY;
707
+ startHeight = overview.offsetHeight;
708
+
709
+ const onMove = (e) => {
710
+ const deltaY = e.clientY - startY;
711
+ const newHeight = startHeight + deltaY;
712
+ const newPercent = (newHeight / window.innerHeight) * 100;
713
+
714
+ // 限制范围 15%-75%
715
+ if (newPercent >= 15 && newPercent <= 75) {
716
+ layout.overviewHeight = Math.round(newPercent * 10) / 10;
717
+ overview.style.height = layout.overviewHeight + '%';
718
+ showSizeIndicator(`概览 ${Math.round(layout.overviewHeight)}% | 详情 ${Math.round(100 - layout.overviewHeight)}%`);
719
+ }
720
+ };
721
+
722
+ const onUp = () => {
723
+ document.body.classList.remove('resizing');
724
+ vResizeHandle.classList.remove('dragging');
725
+ window.removeEventListener('mousemove', onMove);
726
+ window.removeEventListener('mouseup', onUp);
727
+ saveLayout();
728
+ };
729
+
730
+ window.addEventListener('mousemove', onMove);
731
+ window.addEventListener('mouseup', onUp);
732
+ });
733
+ }
734
+
735
+ // 水平拖动(三栏分割)
736
+ function initHResize() {
737
+ // 分割线 1:左栏 ↔ 中栏
738
+ hResizeHandle1.addEventListener('mousedown', (e) => {
739
+ e.preventDefault();
740
+ startHResize(e, 0, 1);
741
+ });
742
+
743
+ // 分割线 2:中栏 ↔ 右栏
744
+ hResizeHandle2.addEventListener('mousedown', (e) => {
745
+ e.preventDefault();
746
+ startHResize(e, 1, 2);
747
+ });
748
+ }
749
+
750
+ function startHResize(e, colIndex, nextColIndex) {
751
+ document.body.classList.add('resizing');
752
+ const startX = e.clientX;
753
+ const containerWidth = detailContent.offsetWidth;
754
+ const startWidths = [
755
+ detailLeft.offsetWidth,
756
+ detailCenter.offsetWidth
757
+ ];
758
+
759
+ const handle = colIndex === 0 ? hResizeHandle1 : hResizeHandle2;
760
+ handle.classList.add('dragging');
761
+
762
+ const onMove = (e) => {
763
+ const deltaX = e.clientX - startX;
764
+ const deltaPercent = (deltaX / containerWidth) * 100;
765
+
766
+ let newWidth1, newWidth2;
767
+
768
+ if (colIndex === 0) {
769
+ // 左栏 ↔ 中栏
770
+ newWidth1 = ((startWidths[0] + deltaX) / containerWidth) * 100;
771
+ newWidth2 = ((startWidths[1] - deltaX) / containerWidth) * 100;
772
+ } else {
773
+ // 中栏 ↔ 右栏
774
+ newWidth1 = layout.columnWidths[1];
775
+ newWidth2 = ((startWidths[1] - deltaX) / containerWidth) * 100;
776
+ }
777
+
778
+ // 限制最小宽度
779
+ const minWidth = 10;
780
+ if (newWidth1 >= minWidth && newWidth2 >= minWidth) {
781
+ if (colIndex === 0) {
782
+ layout.columnWidths[0] = Math.round(newWidth1 * 10) / 10;
783
+ layout.columnWidths[1] = Math.round(newWidth2 * 10) / 10;
784
+ } else {
785
+ layout.columnWidths[1] = Math.round(newWidth1 * 10) / 10;
786
+ layout.columnWidths[2] = Math.round(newWidth2 * 10) / 10;
787
+ }
788
+ updateColumnWidths();
789
+ showSizeIndicator(`三栏: ${Math.round(layout.columnWidths[0])}% | ${Math.round(layout.columnWidths[1])}% | ${Math.round(layout.columnWidths[2])}%`);
790
+ }
791
+ };
792
+
793
+ const onUp = () => {
794
+ document.body.classList.remove('resizing');
795
+ handle.classList.remove('dragging');
796
+ window.removeEventListener('mousemove', onMove);
797
+ window.removeEventListener('mouseup', onUp);
798
+ saveLayout();
799
+ };
800
+
801
+ window.addEventListener('mousemove', onMove);
802
+ window.addEventListener('mouseup', onUp);
803
+ }
804
+
805
+ // 重置布局
806
+ resetBtn.addEventListener('click', () => {
807
+ layout = {
808
+ overviewHeight: DEFAULT_OVERVIEW_HEIGHT,
809
+ columnWidths: [...DEFAULT_COLUMN_WIDTHS]
810
+ };
811
+ applyLayout();
812
+ saveLayout();
813
+ showSizeIndicator('布局已重置');
814
+ });
815
+
816
+ // 选择项目
817
+ const cards = document.querySelectorAll('.project-card');
818
+ cards.forEach(card => {
819
+ card.addEventListener('click', () => {
820
+ cards.forEach(c => c.classList.remove('selected'));
821
+ card.classList.add('selected');
822
+ const content = document.querySelector('.detail-content');
823
+ content.classList.remove('fade-in');
824
+ void content.offsetWidth;
825
+ content.classList.add('fade-in');
826
+ });
827
+ });
828
+
829
+ // 初始化
830
+ const detailContent = document.querySelector('.detail-content');
831
+ loadLayout();
832
+ initVResize();
833
+ initHResize();
834
+ </script>
835
+ </body>
836
+ </html>