bobs-workshop 0.3.3 → 3.1.1

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 (200) hide show
  1. package/LICENSE +2 -2
  2. package/README.md +199 -210
  3. package/bin/bobs-workshop.js +109 -0
  4. package/config/agents.json +27 -0
  5. package/dist/plugins/bobs-workshop.js +34 -0
  6. package/dist/tools/background-agent/cancel.d.ts +3 -0
  7. package/dist/tools/background-agent/cancel.d.ts.map +1 -0
  8. package/dist/tools/background-agent/cancel.js +52 -0
  9. package/dist/tools/background-agent/concurrency.d.ts +15 -0
  10. package/dist/tools/background-agent/concurrency.d.ts.map +1 -0
  11. package/dist/tools/background-agent/concurrency.js +61 -0
  12. package/dist/tools/background-agent/index.d.ts +8 -0
  13. package/dist/tools/background-agent/index.d.ts.map +1 -0
  14. package/dist/tools/background-agent/index.js +7 -0
  15. package/dist/tools/background-agent/launch.d.ts +6 -0
  16. package/dist/tools/background-agent/launch.d.ts.map +1 -0
  17. package/dist/tools/background-agent/launch.js +33 -0
  18. package/dist/tools/background-agent/list.d.ts +7 -0
  19. package/dist/tools/background-agent/list.d.ts.map +1 -0
  20. package/dist/tools/background-agent/list.js +40 -0
  21. package/dist/tools/background-agent/manager.d.ts +29 -0
  22. package/dist/tools/background-agent/manager.d.ts.map +1 -0
  23. package/dist/tools/background-agent/manager.js +388 -0
  24. package/dist/tools/background-agent/output.d.ts +3 -0
  25. package/dist/tools/background-agent/output.d.ts.map +1 -0
  26. package/dist/tools/background-agent/output.js +41 -0
  27. package/dist/tools/background-agent/types.d.ts +46 -0
  28. package/dist/tools/background-agent/types.d.ts.map +1 -0
  29. package/dist/tools/background-agent/types.js +1 -0
  30. package/dist/tools/index.d.ts +9 -0
  31. package/dist/tools/index.d.ts.map +1 -0
  32. package/dist/tools/index.js +8 -0
  33. package/dist/tools/manual/index.d.ts +3 -0
  34. package/dist/tools/manual/index.d.ts.map +1 -0
  35. package/dist/tools/manual/index.js +2 -0
  36. package/dist/tools/manual/manual-update.d.ts +4 -0
  37. package/dist/tools/manual/manual-update.d.ts.map +1 -0
  38. package/dist/tools/manual/manual-update.js +190 -0
  39. package/dist/tools/manual/verify-manual.d.ts +4 -0
  40. package/dist/tools/manual/verify-manual.d.ts.map +1 -0
  41. package/dist/tools/manual/verify-manual.js +51 -0
  42. package/package.json +34 -66
  43. package/postinstall.js +193 -0
  44. package/src/agents/alice.md +466 -0
  45. package/src/agents/bob-rev.md +493 -0
  46. package/src/agents/bob-send.md +277 -0
  47. package/src/agents/bob.md +442 -0
  48. package/src/agents/trace.md +451 -0
  49. package/src/plugins/bobs-workshop.ts +45 -0
  50. package/src/skills/api-patterns/SKILL.md +376 -0
  51. package/src/skills/architecture/SKILL.md +271 -0
  52. package/src/skills/bobs-workshop/performance/icon.svg +3 -0
  53. package/src/skills/brainstorming/SKILL.md +210 -0
  54. package/src/skills/clean-code/SKILL.md +151 -0
  55. package/src/skills/code-review-checklist/SKILL.md +220 -0
  56. package/src/skills/database-design/SKILL.md +271 -0
  57. package/src/skills/exploration/SKILL.md +257 -0
  58. package/src/skills/frontend-ui-ux/SKILL.md +78 -0
  59. package/src/skills/git-master/SKILL.md +1105 -0
  60. package/src/skills/performance/SKILL.md +144 -0
  61. package/src/skills/performance/icon.svg +3 -0
  62. package/src/skills/plan-writing/SKILL.md +225 -0
  63. package/src/skills/security/SKILL.md +410 -0
  64. package/src/skills/simplification/SKILL.md +238 -0
  65. package/src/skills/systematic-debugging/SKILL.md +175 -0
  66. package/src/skills/testing-patterns/SKILL.md +305 -0
  67. package/src/skills/verification/SKILL.md +286 -0
  68. package/src/tools/background-agent/cancel.ts +67 -0
  69. package/src/tools/background-agent/concurrency.ts +71 -0
  70. package/src/tools/background-agent/index.ts +7 -0
  71. package/src/tools/background-agent/launch.ts +39 -0
  72. package/src/tools/background-agent/list.ts +50 -0
  73. package/src/tools/background-agent/manager.ts +466 -0
  74. package/src/tools/background-agent/output.ts +57 -0
  75. package/src/tools/background-agent/types.ts +55 -0
  76. package/src/tools/index.ts +8 -0
  77. package/src/tools/manual/index.ts +2 -0
  78. package/src/tools/manual/manual-update.ts +197 -0
  79. package/src/tools/manual/verify-manual.ts +60 -0
  80. package/uninstall.js +64 -0
  81. package/Claude.md +0 -162
  82. package/bin/bobs-mcp-server.js +0 -11
  83. package/bin/bobs-mcp.js +0 -130
  84. package/dist/api/taskLogger.js +0 -106
  85. package/dist/api/taskLogger.js.map +0 -1
  86. package/dist/cli/checker.js +0 -401
  87. package/dist/cli/checker.js.map +0 -1
  88. package/dist/cli/cleanup.js +0 -131
  89. package/dist/cli/cleanup.js.map +0 -1
  90. package/dist/cli/debug.js +0 -157
  91. package/dist/cli/debug.js.map +0 -1
  92. package/dist/cli/health.js +0 -97
  93. package/dist/cli/health.js.map +0 -1
  94. package/dist/cli/setup.js +0 -81
  95. package/dist/cli/setup.js.map +0 -1
  96. package/dist/cli/workshop.js +0 -42
  97. package/dist/cli/workshop.js.map +0 -1
  98. package/dist/dashboard/server.js +0 -1203
  99. package/dist/dashboard/server.js.map +0 -1
  100. package/dist/index.js +0 -960
  101. package/dist/index.js.map +0 -1
  102. package/dist/prompts/architect.js +0 -221
  103. package/dist/prompts/architect.js.map +0 -1
  104. package/dist/prompts/debugger.js +0 -257
  105. package/dist/prompts/debugger.js.map +0 -1
  106. package/dist/prompts/engineer.js +0 -249
  107. package/dist/prompts/engineer.js.map +0 -1
  108. package/dist/prompts/orchestrator.js +0 -304
  109. package/dist/prompts/orchestrator.js.map +0 -1
  110. package/dist/prompts/reviewer.js +0 -289
  111. package/dist/prompts/reviewer.js.map +0 -1
  112. package/dist/services/activitySummarizer.js +0 -388
  113. package/dist/services/activitySummarizer.js.map +0 -1
  114. package/dist/services/changeValidator.js +0 -396
  115. package/dist/services/changeValidator.js.map +0 -1
  116. package/dist/services/claudeOrchestrator.js +0 -343
  117. package/dist/services/claudeOrchestrator.js.map +0 -1
  118. package/dist/services/fileMonitor.js +0 -250
  119. package/dist/services/fileMonitor.js.map +0 -1
  120. package/dist/services/implementationSummarizer.js +0 -306
  121. package/dist/services/implementationSummarizer.js.map +0 -1
  122. package/dist/services/liveMonitor.js +0 -315
  123. package/dist/services/liveMonitor.js.map +0 -1
  124. package/dist/services/mcpAuditLogger.js +0 -104
  125. package/dist/services/mcpAuditLogger.js.map +0 -1
  126. package/dist/services/mcpLogger.js +0 -223
  127. package/dist/services/mcpLogger.js.map +0 -1
  128. package/dist/services/tmuxManager.js +0 -541
  129. package/dist/services/tmuxManager.js.map +0 -1
  130. package/dist/tools/approvalTools.js +0 -244
  131. package/dist/tools/approvalTools.js.map +0 -1
  132. package/dist/tools/autoDebugger.js +0 -147
  133. package/dist/tools/autoDebugger.js.map +0 -1
  134. package/dist/tools/cleanupService.js +0 -221
  135. package/dist/tools/cleanupService.js.map +0 -1
  136. package/dist/tools/dashboardTools.js +0 -342
  137. package/dist/tools/dashboardTools.js.map +0 -1
  138. package/dist/tools/developmentNudges.js +0 -336
  139. package/dist/tools/developmentNudges.js.map +0 -1
  140. package/dist/tools/gitTools.js +0 -741
  141. package/dist/tools/gitTools.js.map +0 -1
  142. package/dist/tools/orchestratorTools.js +0 -832
  143. package/dist/tools/orchestratorTools.js.map +0 -1
  144. package/dist/tools/searchCache.js +0 -64
  145. package/dist/tools/searchCache.js.map +0 -1
  146. package/dist/tools/searchTools.js +0 -1107
  147. package/dist/tools/searchTools.js.map +0 -1
  148. package/dist/tools/semgrep-patterns.js +0 -296
  149. package/dist/tools/semgrep-patterns.js.map +0 -1
  150. package/dist/tools/specTools.js +0 -332
  151. package/dist/tools/specTools.js.map +0 -1
  152. package/dist/tools/structural/__tests__/orchestrator.test.js +0 -61
  153. package/dist/tools/structural/__tests__/orchestrator.test.js.map +0 -1
  154. package/dist/tools/structural/cache.js +0 -226
  155. package/dist/tools/structural/cache.js.map +0 -1
  156. package/dist/tools/structural/engines/python/index.js +0 -118
  157. package/dist/tools/structural/engines/python/index.js.map +0 -1
  158. package/dist/tools/structural/engines/typescript/__tests__/typescript-engine.test.js +0 -97
  159. package/dist/tools/structural/engines/typescript/__tests__/typescript-engine.test.js.map +0 -1
  160. package/dist/tools/structural/engines/typescript/analyzer.js +0 -433
  161. package/dist/tools/structural/engines/typescript/analyzer.js.map +0 -1
  162. package/dist/tools/structural/engines/typescript/index.js +0 -381
  163. package/dist/tools/structural/engines/typescript/index.js.map +0 -1
  164. package/dist/tools/structural/engines/typescript/utils.js +0 -279
  165. package/dist/tools/structural/engines/typescript/utils.js.map +0 -1
  166. package/dist/tools/structural/index.js +0 -248
  167. package/dist/tools/structural/index.js.map +0 -1
  168. package/dist/tools/structural/types.js +0 -18
  169. package/dist/tools/structural/types.js.map +0 -1
  170. package/dist/tools/tmuxTools.js +0 -100
  171. package/dist/tools/tmuxTools.js.map +0 -1
  172. package/dist/tools/workRecorder.js +0 -215
  173. package/dist/tools/workRecorder.js.map +0 -1
  174. package/dist/tools/worktreeTools.js +0 -705
  175. package/dist/tools/worktreeTools.js.map +0 -1
  176. package/dist/utils/__tests__/integration.test.js +0 -57
  177. package/dist/utils/__tests__/integration.test.js.map +0 -1
  178. package/dist/utils/__tests__/serverDetection.test.js +0 -151
  179. package/dist/utils/__tests__/serverDetection.test.js.map +0 -1
  180. package/dist/utils/errorHandling.js +0 -336
  181. package/dist/utils/errorHandling.js.map +0 -1
  182. package/dist/utils/processManager.js +0 -172
  183. package/dist/utils/processManager.js.map +0 -1
  184. package/dist/utils/reliability.js +0 -263
  185. package/dist/utils/reliability.js.map +0 -1
  186. package/dist/utils/responseFormatter.js +0 -250
  187. package/dist/utils/responseFormatter.js.map +0 -1
  188. package/dist/utils/serverDetection.js +0 -133
  189. package/dist/utils/serverDetection.js.map +0 -1
  190. package/dist/utils/specMigration.js +0 -105
  191. package/dist/utils/specMigration.js.map +0 -1
  192. package/dist/validation/schemas.js +0 -299
  193. package/dist/validation/schemas.js.map +0 -1
  194. package/public/.well-known/mcp/manifest.json +0 -473
  195. package/public/index.html +0 -3157
  196. package/public/index.html.backup +0 -2805
  197. package/public/index.html.backup2 +0 -1292
  198. package/scripts/cleanup-system-logs.ts +0 -121
  199. package/scripts/init-workspace.js +0 -63
  200. package/scripts/install-search-tools.js +0 -116
package/public/index.html DELETED
@@ -1,3157 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en" class="h-full">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>🔧 Bob's Workshop - Manual Management Dashboard</title>
7
- <script src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
8
-
9
- <!-- Google Fonts: Poppins and Outfit -->
10
- <link rel="preconnect" href="https://fonts.googleapis.com">
11
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
12
- <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet">
13
-
14
- <style>
15
- :root {
16
- /* Color System */
17
- --bg-primary: linear-gradient(to bottom, #000000 0%, #1a1a1a 50%, #1f1f21 100%);
18
- --bg-glass: rgba(255, 255, 255, 0.1);
19
- --bg-glass-hover: rgba(255, 255, 255, 0.15);
20
- --border-glass: rgba(255, 255, 255, 0.2);
21
- --text-primary: #ffffff;
22
- --text-secondary: rgba(255, 255, 255, 0.8);
23
- --text-muted: rgba(255, 255, 255, 0.6);
24
- --accent-blue: linear-gradient(135deg, #3b82f6 0%, #6366f1 100%);
25
- --accent-green: #10b981;
26
- --accent-orange: #f59e0b;
27
- --accent-yellow: #f59e0b;
28
- --accent-red: #ef4444;
29
-
30
- /* Typography */
31
- --font-heading: 'Poppins', sans-serif;
32
- --font-body: 'Outfit', sans-serif;
33
- }
34
-
35
- * {
36
- box-sizing: border-box;
37
- }
38
-
39
- body {
40
- margin: 0;
41
- padding: 0;
42
- font-family: var(--font-body);
43
- background: var(--bg-primary);
44
- color: var(--text-primary);
45
- min-height: 100vh;
46
- overflow-x: hidden;
47
- zoom: 0.69;
48
- }
49
-
50
- /* Glass morphism utility classes */
51
- .glass {
52
- background: var(--bg-glass);
53
- backdrop-filter: blur(12px);
54
- -webkit-backdrop-filter: blur(12px);
55
- border: 1px solid var(--border-glass);
56
- border-radius: 16px;
57
- }
58
-
59
- .glass-hover {
60
- transition: all 0.3s ease;
61
- }
62
-
63
- .glass-hover:hover {
64
- background: var(--bg-glass-hover);
65
- transform: translateY(-2px);
66
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
67
- }
68
-
69
- /* Typography */
70
- .heading {
71
- font-family: var(--font-heading);
72
- font-weight: 600;
73
- }
74
-
75
- .text-gradient {
76
- background: var(--accent-orange);
77
- -webkit-background-clip: text;
78
- -webkit-text-fill-color: transparent;
79
- background-clip: text;
80
- }
81
-
82
- /* Header */
83
- .header {
84
- background: var(--bg-glass);
85
- backdrop-filter: blur(20px);
86
- border-bottom: 1px solid var(--border-glass);
87
- padding: 1.5rem 2rem;
88
- display: flex;
89
- justify-content: space-between;
90
- align-items: center;
91
- position: sticky;
92
- top: 0;
93
- z-index: 50;
94
- }
95
-
96
- .logo {
97
- font-family: var(--font-heading);
98
- font-size: 1.5rem;
99
- font-weight: 700;
100
- display: flex;
101
- align-items: center;
102
- gap: 0.5rem;
103
- }
104
-
105
- /* Metrics Cards */
106
- .metrics-grid {
107
- display: grid;
108
- grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
109
- gap: 1.5rem;
110
- padding: 2rem;
111
- }
112
-
113
- .metric-card {
114
- padding: 1.5rem;
115
- background: var(--bg-glass);
116
- backdrop-filter: blur(12px);
117
- border-radius: 16px;
118
- border: 1px solid var(--border-glass);
119
- transition: all 0.3s ease;
120
- }
121
-
122
- .metric-card:hover {
123
- background: var(--bg-glass-hover);
124
- transform: translateY(-4px);
125
- box-shadow: 0 12px 40px rgba(0, 0, 0, 0.4);
126
- }
127
-
128
- .metric-value {
129
- font-family: var(--font-heading);
130
- font-size: 2.5rem;
131
- font-weight: 700;
132
- margin-bottom: 0.5rem;
133
- }
134
-
135
- .metric-label {
136
- font-size: 0.875rem;
137
- color: var(--text-secondary);
138
- text-transform: uppercase;
139
- letter-spacing: 0.05em;
140
- }
141
-
142
- /* Spec Table */
143
- .spec-container {
144
- margin: 2rem;
145
- background: var(--bg-glass);
146
- backdrop-filter: blur(12px);
147
- border-radius: 20px;
148
- border: 1px solid var(--border-glass);
149
- border-top: 3px solid var(--accent-orange);
150
- overflow: hidden;
151
- }
152
-
153
- .spec-header {
154
- padding: 1.5rem 2rem;
155
- background: linear-gradient(135deg, rgba(59, 130, 246, 0.1) 0%, rgba(99, 102, 241, 0.1) 100%);
156
- border-bottom: 1px solid var(--border-glass);
157
- display: flex;
158
- justify-content: space-between;
159
- align-items: center;
160
- }
161
-
162
- .spec-table {
163
- width: 100%;
164
- border-collapse: collapse;
165
- }
166
-
167
- .spec-table th {
168
- background: rgba(255, 255, 255, 0.05);
169
- padding: 1rem 1.5rem;
170
- text-align: left;
171
- font-family: var(--font-heading);
172
- font-weight: 600;
173
- font-size: 0.875rem;
174
- color: var(--text-secondary);
175
- text-transform: uppercase;
176
- letter-spacing: 0.05em;
177
- border-bottom: 1px solid var(--border-glass);
178
- cursor: pointer;
179
- user-select: none;
180
- position: relative;
181
- transition: all 0.2s ease;
182
- }
183
-
184
- .spec-table th:hover {
185
- background: rgba(255, 255, 255, 0.1);
186
- color: var(--text-primary);
187
- }
188
-
189
- .spec-table th.sortable::after {
190
- content: '↕';
191
- position: absolute;
192
- right: 0.5rem;
193
- opacity: 0.3;
194
- font-size: 0.8rem;
195
- }
196
-
197
- .spec-table th.sorted-asc::after {
198
- content: '↑';
199
- opacity: 1;
200
- color: var(--accent-blue);
201
- }
202
-
203
- .spec-table th.sorted-desc::after {
204
- content: '↓';
205
- opacity: 1;
206
- color: var(--accent-blue);
207
- }
208
-
209
- .spec-table td {
210
- padding: 1rem 1.5rem;
211
- border-bottom: 1px solid rgba(255, 255, 255, 0.05);
212
- transition: all 0.2s ease;
213
- }
214
-
215
- .spec-table tr:hover td {
216
- background: rgba(255, 255, 255, 0.02);
217
- }
218
-
219
- /* Table gradient - darker at top, lighter only for last 4-5 rows */
220
- .spec-table tbody {
221
- background: linear-gradient(to bottom,
222
- rgba(0, 0, 0, 0.4) 0%,
223
- rgba(0, 0, 0, 0.3) 70%,
224
- rgba(255, 255, 255, 0.02) 85%,
225
- rgba(255, 255, 255, 0.05) 100%);
226
- }
227
-
228
- /* Status badges */
229
- .status-badge {
230
- padding: 0.25rem 0.75rem;
231
- border-radius: 20px;
232
- font-size: 0.75rem;
233
- font-weight: 600;
234
- text-transform: uppercase;
235
- letter-spacing: 0.05em;
236
- }
237
-
238
- /* New 4-state system with improved colors */
239
- .status-draft { background: rgba(156, 163, 175, 0.2); color: #9ca3af; border: 1px solid rgba(156, 163, 175, 0.3); }
240
- .status-ready { background: rgba(245, 158, 11, 0.2); color: #f59e0b; border: 1px solid rgba(245, 158, 11, 0.3); }
241
- .status-engineered { background: rgba(59, 130, 246, 0.2); color: #3b82f6; border: 1px solid rgba(59, 130, 246, 0.3); }
242
- .status-done { background: rgba(16, 185, 129, 0.2); color: #10b981; border: 1px solid rgba(16, 185, 129, 0.3); }
243
-
244
- /* Legacy state mapping for backward compatibility */
245
- .status-planning { background: rgba(245, 158, 11, 0.2); color: #f59e0b; border: 1px solid rgba(245, 158, 11, 0.3); }
246
- .status-implementation { background: rgba(59, 130, 246, 0.2); color: #3b82f6; border: 1px solid rgba(59, 130, 246, 0.3); }
247
- .status-review { background: rgba(147, 51, 234, 0.2); color: #a855f7; border: 1px solid rgba(147, 51, 234, 0.3); }
248
- .status-completed { background: rgba(16, 185, 129, 0.2); color: #10b981; border: 1px solid rgba(16, 185, 129, 0.3); }
249
- .status-in-progress { background: rgba(59, 130, 246, 0.2); color: #3b82f6; border: 1px solid rgba(59, 130, 246, 0.3); }
250
-
251
- /* Buttons */
252
- .btn {
253
- padding: 0.75rem 1.5rem;
254
- border-radius: 12px;
255
- font-family: var(--font-heading);
256
- font-weight: 600;
257
- font-size: 0.875rem;
258
- border: none;
259
- cursor: pointer;
260
- transition: all 0.3s ease;
261
- display: inline-flex;
262
- align-items: center;
263
- gap: 0.5rem;
264
- }
265
-
266
- .btn-primary {
267
- background: var(--accent-blue);
268
- color: white;
269
- }
270
-
271
- .btn-primary:hover {
272
- transform: translateY(-2px);
273
- box-shadow: 0 8px 25px rgba(59, 130, 246, 0.4);
274
- }
275
-
276
- .btn-secondary {
277
- background: var(--bg-glass);
278
- backdrop-filter: blur(12px);
279
- color: var(--text-primary);
280
- border: 1px solid var(--border-glass);
281
- }
282
-
283
- .btn-secondary:hover {
284
- background: var(--bg-glass-hover);
285
- transform: translateY(-2px);
286
- }
287
-
288
- /* Modal */
289
- .modal-overlay {
290
- position: fixed;
291
- inset: 0;
292
- background: rgba(0, 0, 0, 0.8);
293
- backdrop-filter: blur(8px);
294
- display: flex;
295
- align-items: flex-start;
296
- justify-content: center;
297
- z-index: 100;
298
- padding: 1rem;
299
- padding-top: 5vh; /* Move up by 10% from center (50vh - 10% = 40vh, but flex-start means 5vh works) */
300
- }
301
-
302
- .modal-content {
303
- background: var(--bg-glass);
304
- backdrop-filter: blur(20px);
305
- border: 1px solid var(--border-glass);
306
- border-radius: 24px;
307
- width: 99vw;
308
- height: 95vh;
309
- max-width: none;
310
- min-width: 1728px; /* Increased from 1440px by 20% (1440 * 1.2) */
311
- min-height: 1000px;
312
- display: grid;
313
- grid-template-columns: 60% 40%;
314
- grid-template-rows: auto 1fr;
315
- overflow: hidden;
316
- }
317
-
318
- .modal-header {
319
- grid-column: 1 / -1;
320
- background: linear-gradient(135deg, rgba(59, 130, 246, 0.1) 0%, rgba(99, 102, 241, 0.1) 100%);
321
- padding: 1.5rem 2rem;
322
- border-bottom: 1px solid var(--border-glass);
323
- display: flex;
324
- justify-content: space-between;
325
- align-items: center;
326
- }
327
-
328
- .modal-title-section {
329
- display: flex;
330
- align-items: center;
331
- gap: 1rem;
332
- }
333
-
334
- .worktree-info {
335
- background: rgba(255, 255, 255, 0.1);
336
- padding: 0.5rem 1rem;
337
- border-radius: 20px;
338
- font-size: 0.875rem;
339
- display: flex;
340
- align-items: center;
341
- gap: 0.5rem;
342
- }
343
-
344
- .modal-main-container {
345
- display: flex;
346
- flex-direction: column;
347
- overflow: hidden;
348
- height: 100%; /* Ensure full height usage */
349
- }
350
-
351
- .modal-nav {
352
- padding: 1rem 2rem 0;
353
- border-bottom: 1px solid var(--border-glass);
354
- }
355
-
356
- .nav-tabs {
357
- display: flex;
358
- gap: 0.5rem;
359
- margin-bottom: 1rem;
360
- overflow-x: auto;
361
- scrollbar-width: thin;
362
- scrollbar-color: rgba(255, 255, 255, 0.3) transparent;
363
- padding-bottom: 0.5rem;
364
- }
365
-
366
- .nav-tabs::-webkit-scrollbar {
367
- height: 4px;
368
- }
369
-
370
- .nav-tabs::-webkit-scrollbar-track {
371
- background: rgba(255, 255, 255, 0.1);
372
- border-radius: 2px;
373
- }
374
-
375
- .nav-tabs::-webkit-scrollbar-thumb {
376
- background: rgba(255, 255, 255, 0.3);
377
- border-radius: 2px;
378
- }
379
-
380
- .nav-tabs::-webkit-scrollbar-thumb:hover {
381
- background: rgba(255, 255, 255, 0.5);
382
- }
383
-
384
- .nav-tab {
385
- padding: 0.5rem 1rem;
386
- border-radius: 20px;
387
- background: transparent;
388
- border: 1px solid rgba(255, 255, 255, 0.1);
389
- color: var(--text-secondary);
390
- font-family: var(--font-body);
391
- font-size: 0.875rem;
392
- cursor: pointer;
393
- transition: all 0.2s ease;
394
- white-space: nowrap;
395
- flex-shrink: 0;
396
- min-width: fit-content;
397
- }
398
-
399
- .nav-tab:hover {
400
- background: rgba(255, 255, 255, 0.05);
401
- color: var(--text-primary);
402
- }
403
-
404
- .nav-tab.active {
405
- background: var(--accent-blue);
406
- color: white;
407
- border-color: var(--accent-blue);
408
- }
409
-
410
- .nav-tab.keyboard-focus {
411
- box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.4);
412
- outline: none;
413
- }
414
-
415
- .modal-main {
416
- padding: 2rem;
417
- overflow-y: auto;
418
- flex: 1; /* Take all available space */
419
- min-height: 0; /* Allow flex shrinking */
420
- }
421
-
422
- .spec-content {
423
- background: rgba(255, 255, 255, 0.03);
424
- padding: 2.5rem 3.5rem 2.5rem 3.5rem; /* Increased left padding for better breathing room */
425
- border-radius: 12px;
426
- border: 1px solid rgba(255, 255, 255, 0.05);
427
- font-family: var(--font-body);
428
- line-height: 1.7; /* Improved line height for better readability */
429
- overflow-y: auto;
430
- height: 100%; /* Use full available height */
431
- }
432
-
433
- .spec-content h1, .spec-content h2, .spec-content h3 {
434
- color: var(--text-primary);
435
- margin-top: 2.5rem; /* Increased top margin */
436
- margin-bottom: 1.5rem; /* Increased bottom margin */
437
- font-family: var(--font-heading);
438
- }
439
-
440
- .spec-content h1:first-child,
441
- .spec-content h2:first-child,
442
- .spec-content h3:first-child {
443
- margin-top: 0; /* Remove top margin from first heading */
444
- }
445
-
446
- .spec-content h1 { font-size: 1.5rem; }
447
- .spec-content h2 { font-size: 1.3rem; }
448
- .spec-content h3 { font-size: 1.1rem; }
449
-
450
- .spec-content p {
451
- margin-bottom: 1.5rem; /* Increased paragraph spacing */
452
- color: var(--text-secondary);
453
- }
454
-
455
- .spec-content ul, .spec-content ol {
456
- margin-bottom: 1.5rem; /* Increased list spacing */
457
- padding-left: 2rem; /* Better indentation */
458
- color: var(--text-secondary);
459
- }
460
-
461
- .spec-content li {
462
- margin-bottom: 0.75rem; /* Increased list item spacing */
463
- line-height: 1.6;
464
- }
465
-
466
- .spec-content code {
467
- background: rgba(255, 255, 255, 0.1);
468
- padding: 0.2rem 0.4rem;
469
- border-radius: 4px;
470
- font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
471
- font-size: 0.9em;
472
- color: var(--accent-blue);
473
- }
474
-
475
- .spec-content pre {
476
- background: rgba(0, 0, 0, 0.4);
477
- padding: 1.5rem; /* Increased padding for code blocks */
478
- border-radius: 8px;
479
- overflow-x: auto;
480
- margin: 2rem 0; /* Increased margin around code blocks */
481
- border: 1px solid rgba(255, 255, 255, 0.15);
482
- line-height: 1.5;
483
- }
484
-
485
- .spec-content pre code {
486
- background: none;
487
- padding: 0;
488
- color: var(--text-primary);
489
- }
490
-
491
- .spec-content blockquote {
492
- border-left: 3px solid var(--accent-blue);
493
- padding-left: 1rem;
494
- margin: 1rem 0;
495
- font-style: italic;
496
- color: var(--text-secondary);
497
- }
498
-
499
- .spec-content table {
500
- width: 100%;
501
- border-collapse: collapse;
502
- margin: 1rem 0;
503
- }
504
-
505
- .spec-content th, .spec-content td {
506
- border: 1px solid rgba(255, 255, 255, 0.1);
507
- padding: 0.75rem;
508
- text-align: left;
509
- }
510
-
511
- .spec-content th {
512
- background: rgba(255, 255, 255, 0.05);
513
- font-weight: 600;
514
- color: var(--text-primary);
515
- }
516
-
517
- .spec-content a {
518
- color: var(--accent-blue);
519
- text-decoration: underline;
520
- }
521
-
522
- .spec-content a:hover {
523
- color: #60a5fa;
524
- }
525
-
526
- .modal-activity {
527
- border-left: 1px solid var(--border-glass);
528
- overflow: hidden;
529
- background: rgba(0, 0, 0, 0.1);
530
- display: flex;
531
- flex-direction: column;
532
- }
533
-
534
- .activity-nav {
535
- padding: 1rem 1.5rem 0.5rem;
536
- }
537
-
538
- .activity-tabs {
539
- display: flex;
540
- gap: 0.5rem;
541
- margin-bottom: 1rem;
542
- }
543
-
544
- .activity-tab {
545
- padding: 0.5rem 1rem;
546
- border-radius: 20px;
547
- background: transparent;
548
- border: 1px solid rgba(255, 255, 255, 0.1);
549
- color: var(--text-secondary);
550
- font-family: var(--font-body);
551
- font-size: 0.875rem;
552
- cursor: pointer;
553
- transition: all 0.2s ease;
554
- }
555
-
556
- .activity-tab:hover {
557
- background: rgba(255, 255, 255, 0.05);
558
- color: var(--text-primary);
559
- }
560
-
561
- .activity-tab.active {
562
- background: var(--accent-blue);
563
- color: white;
564
- border-color: var(--accent-blue);
565
- }
566
-
567
- .activity-content {
568
- overflow-y: auto;
569
- min-height: 0; /* Allow flex shrinking */
570
- }
571
-
572
- .sidebar-nav {
573
- list-style: none;
574
- padding: 0;
575
- margin: 0;
576
- }
577
-
578
- .sidebar-nav li {
579
- margin-bottom: 0.5rem;
580
- }
581
-
582
- .sidebar-nav button {
583
- width: 100%;
584
- text-align: left;
585
- padding: 0.75rem;
586
- border-radius: 8px;
587
- background: transparent;
588
- border: none;
589
- color: var(--text-secondary);
590
- font-family: var(--font-body);
591
- cursor: pointer;
592
- transition: all 0.2s ease;
593
- }
594
-
595
- .sidebar-nav button:hover {
596
- background: rgba(255, 255, 255, 0.05);
597
- color: var(--text-primary);
598
- }
599
-
600
- .sidebar-nav button.active {
601
- background: var(--accent-blue);
602
- color: white;
603
- }
604
-
605
- /* Timeline Activity Layout */
606
- .timeline-container {
607
- position: relative;
608
- padding: 1rem 0;
609
- min-height: 60vh;
610
- height: 100%;
611
- }
612
-
613
- .timeline-line {
614
- position: absolute;
615
- left: 200px; /* Align with fixed left column width */
616
- top: 0;
617
- height: 100%; /* Dynamic height to extend through all content */
618
- width: 2px;
619
- background: linear-gradient(to bottom,
620
- transparent 0%,
621
- rgba(59, 130, 246, 0.3) 2%,
622
- rgba(59, 130, 246, 0.6) 10%,
623
- rgba(59, 130, 246, 0.6) 90%,
624
- rgba(59, 130, 246, 0.3) 98%,
625
- transparent 100%);
626
- z-index: 1;
627
- }
628
-
629
- .timeline-item {
630
- display: flex;
631
- margin-bottom: 2.5rem; /* Increased spacing between timeline items */
632
- position: relative;
633
- align-items: flex-start; /* Better alignment */
634
- }
635
-
636
- .timeline-left {
637
- flex: 0 0 200px; /* Fixed width for better alignment */
638
- text-align: right;
639
- padding-right: 1.5rem;
640
- display: flex;
641
- flex-direction: column;
642
- justify-content: center;
643
- min-height: 60px; /* Ensure minimum height */
644
- }
645
-
646
- .timeline-right {
647
- flex: 1;
648
- padding-left: 1.5rem;
649
- min-width: 0; /* Allow content to shrink properly */
650
- }
651
-
652
- .timeline-marker {
653
- position: absolute;
654
- left: 200px; /* Align with timeline line */
655
- top: 50%;
656
- transform: translate(-50%, -50%);
657
- width: 12px;
658
- height: 12px;
659
- border-radius: 50%;
660
- background: var(--accent-blue);
661
- border: 3px solid var(--bg-primary);
662
- box-shadow: 0 0 0 2px var(--accent-blue);
663
- z-index: 2;
664
- }
665
-
666
- .timeline-marker.debugger { background: var(--accent-red); box-shadow: 0 0 0 2px var(--accent-red); }
667
- .timeline-marker.engineer { background: var(--accent-green); box-shadow: 0 0 0 2px var(--accent-green); }
668
- .timeline-marker.architect { background: var(--accent-orange); box-shadow: 0 0 0 2px var(--accent-orange); }
669
- .timeline-marker.reviewer { background: var(--accent-purple); box-shadow: 0 0 0 2px var(--accent-purple); }
670
- .timeline-marker.orchestrator { background: #6366f1; box-shadow: 0 0 0 2px #6366f1; }
671
- .timeline-marker.system { background: var(--text-muted); box-shadow: 0 0 0 2px var(--text-muted); }
672
-
673
- /* Role-based status badge colors */
674
- .status-badge.role-system { background: rgba(156, 163, 175, 0.2); color: var(--text-muted); }
675
- .status-badge.role-engineer { background: rgba(16, 185, 129, 0.2); color: var(--accent-green); }
676
- .status-badge.role-architect { background: rgba(245, 158, 11, 0.2); color: var(--accent-orange); }
677
- .status-badge.role-debugger { background: rgba(239, 68, 68, 0.2); color: var(--accent-red); }
678
- .status-badge.role-reviewer { background: rgba(139, 92, 246, 0.2); color: var(--accent-purple); }
679
- .status-badge.role-orchestrator { background: rgba(99, 102, 241, 0.2); color: #6366f1; }
680
-
681
- .timeline-time {
682
- font-size: 0.8rem; /* Slightly larger font */
683
- color: var(--text-muted);
684
- font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
685
- margin-bottom: 0.75rem; /* More space between time and role */
686
- line-height: 1.4;
687
- }
688
-
689
- .timeline-role {
690
- font-weight: 700; /* Bold headings only */
691
- color: var(--text-primary);
692
- margin-bottom: 0.5rem; /* More space after role */
693
- font-size: 0.9rem;
694
- }
695
-
696
- .timeline-content {
697
- background: rgba(255, 255, 255, 0.03);
698
- border-radius: 12px;
699
- padding: 1rem 1.25rem;
700
- border: 1px solid rgba(255, 255, 255, 0.08);
701
- backdrop-filter: blur(12px);
702
- transition: all 0.2s ease;
703
- margin-bottom: 0.5rem;
704
- box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
705
- }
706
-
707
- .timeline-content:hover {
708
- background: rgba(255, 255, 255, 0.06);
709
- border-color: rgba(255, 255, 255, 0.15);
710
- transform: translateY(-1px);
711
- box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
712
- }
713
-
714
- .activity-role {
715
- font-weight: 600;
716
- color: var(--text-primary);
717
- margin-bottom: 0.25rem;
718
- }
719
-
720
- .activity-note {
721
- font-size: 0.9rem; /* Slightly larger for better readability */
722
- color: var(--text-secondary);
723
- line-height: 1.5;
724
- margin-bottom: 0.5rem;
725
- }
726
-
727
- /* Compliance Status Design System */
728
- .compliance-compliant {
729
- background: rgba(34, 197, 94, 0.1);
730
- border-color: rgba(34, 197, 94, 0.3);
731
- color: #22c55e;
732
- }
733
- .compliance-deviation {
734
- background: rgba(245, 158, 11, 0.1);
735
- border-color: rgba(245, 158, 11, 0.3);
736
- color: #f59e0b;
737
- }
738
- .compliance-unexpected {
739
- background: rgba(249, 115, 22, 0.1);
740
- border-color: rgba(249, 115, 22, 0.3);
741
- color: #f97316;
742
- }
743
- .compliance-unknown {
744
- background: rgba(107, 114, 128, 0.1);
745
- border-color: rgba(107, 114, 128, 0.3);
746
- color: #6b7280;
747
- }
748
-
749
- /* Compliance Badge */
750
- .compliance-badge {
751
- display: inline-flex;
752
- align-items: center;
753
- gap: 0.25rem;
754
- padding: 0.125rem 0.5rem;
755
- border-radius: 12px;
756
- font-size: 0.75rem;
757
- font-weight: 500;
758
- border: 1px solid;
759
- }
760
-
761
- /* Enhanced File Change Cards */
762
- .file-change-card {
763
- background: rgba(255, 255, 255, 0.02);
764
- border-radius: 8px;
765
- padding: 1rem;
766
- margin-bottom: 0.75rem;
767
- border: 1px solid rgba(255, 255, 255, 0.05);
768
- transition: all 0.2s ease;
769
- }
770
-
771
- .file-change-card:hover {
772
- background: rgba(255, 255, 255, 0.04);
773
- transform: translateY(-1px);
774
- }
775
-
776
- .file-header {
777
- display: flex;
778
- align-items: center;
779
- gap: 0.5rem;
780
- margin-bottom: 0.5rem;
781
- }
782
-
783
- .file-icon {
784
- font-size: 1rem;
785
- }
786
-
787
- .file-name {
788
- font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
789
- font-size: 0.85rem;
790
- color: var(--text-primary);
791
- flex: 1;
792
- }
793
-
794
- .file-description {
795
- color: var(--text-secondary);
796
- font-size: 0.85rem;
797
- margin-bottom: 0.5rem;
798
- line-height: 1.4;
799
- }
800
-
801
- .file-meta {
802
- display: flex;
803
- align-items: center;
804
- gap: 1rem;
805
- font-size: 0.75rem;
806
- color: var(--text-muted);
807
- }
808
-
809
- .change-type {
810
- padding: 0.125rem 0.5rem;
811
- border-radius: 12px;
812
- background: rgba(99, 102, 241, 0.1);
813
- color: #818cf8;
814
- border: 1px solid rgba(99, 102, 241, 0.2);
815
- }
816
-
817
- .lines-changed {
818
- color: var(--text-secondary);
819
- }
820
-
821
- /* Responsive design */
822
- @media (max-width: 768px) {
823
- .metrics-grid {
824
- grid-template-columns: 1fr;
825
- padding: 1rem;
826
- }
827
-
828
- .spec-container {
829
- margin: 1rem;
830
- }
831
-
832
- .modal-content {
833
- grid-template-columns: 1fr;
834
- grid-template-rows: auto auto 1fr auto;
835
- }
836
-
837
- .modal-sidebar,
838
- .modal-activity {
839
- border: none;
840
- border-top: 1px solid var(--border-glass);
841
- }
842
- }
843
-
844
- /* Loading animation */
845
- .loading {
846
- display: inline-block;
847
- width: 20px;
848
- height: 20px;
849
- border: 2px solid rgba(255, 255, 255, 0.3);
850
- border-radius: 50%;
851
- border-top-color: #ffffff;
852
- animation: spin 1s ease-in-out infinite;
853
- }
854
-
855
- @keyframes spin {
856
- to { transform: rotate(360deg); }
857
- }
858
-
859
- /* Fade in animation */
860
- .fade-in {
861
- animation: fadeIn 0.5s ease-out;
862
- }
863
-
864
- @keyframes fadeIn {
865
- from { opacity: 0; transform: translateY(20px); }
866
- to { opacity: 1; transform: translateY(0); }
867
- }
868
-
869
- /* Status Banner */
870
- .status-banner {
871
- background: var(--bg-glass);
872
- backdrop-filter: blur(12px);
873
- border: 1px solid var(--border-glass);
874
- border-left: 4px solid var(--accent-orange);
875
- border-radius: 16px;
876
- margin: 1.5rem 2rem;
877
- padding: 1.5rem 2rem;
878
- font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
879
- }
880
-
881
- .status-content {
882
- display: grid;
883
- grid-template-columns: 1fr auto 1fr;
884
- align-items: center;
885
- gap: 1rem;
886
- }
887
-
888
- .status-left {
889
- display: flex;
890
- align-items: center;
891
- gap: 0.75rem;
892
- font-size: 0.95rem;
893
- }
894
-
895
- .workshop-icon {
896
- font-size: 1.2rem;
897
- }
898
-
899
- .workshop-name {
900
- font-weight: 600;
901
- color: var(--text-primary);
902
- }
903
-
904
- .status-separator {
905
- color: var(--text-muted);
906
- margin: 0 0.25rem;
907
- }
908
-
909
- .build-status .building {
910
- color: var(--accent-yellow);
911
- display: flex;
912
- align-items: center;
913
- gap: 0.5rem;
914
- }
915
-
916
- .build-status .idle {
917
- color: var(--text-secondary);
918
- display: flex;
919
- align-items: center;
920
- gap: 0.5rem;
921
- }
922
-
923
- .building-icon {
924
- animation: spin 2s linear infinite;
925
- }
926
-
927
- .status-right {
928
- font-size: 0.9rem;
929
- }
930
-
931
- .worktree-stats {
932
- display: flex;
933
- align-items: center;
934
- gap: 0.5rem;
935
- flex-wrap: wrap;
936
- }
937
-
938
- .stat-label {
939
- color: var(--text-secondary);
940
- font-weight: 600;
941
- }
942
-
943
- .stat-value {
944
- color: var(--text-primary);
945
- }
946
-
947
- .stat-value.draft {
948
- color: #9ca3af;
949
- }
950
-
951
- .stat-value.in-progress {
952
- color: var(--accent-yellow);
953
- }
954
-
955
- .stat-value.done {
956
- color: var(--accent-green);
957
- }
958
-
959
- .stat-value.committed {
960
- color: var(--accent-green);
961
- }
962
-
963
- .stat-value.dirty {
964
- color: var(--accent-yellow);
965
- }
966
-
967
- .stat-value.clean {
968
- color: var(--text-secondary);
969
- }
970
-
971
- /* Responsive adjustments for status banner */
972
- @media (max-width: 768px) {
973
- .status-banner {
974
- margin: 1rem;
975
- padding: 1rem;
976
- }
977
-
978
- .status-content {
979
- flex-direction: column;
980
- align-items: flex-start;
981
- }
982
-
983
- .worktree-stats {
984
- font-size: 0.8rem;
985
- }
986
- }
987
-
988
-
989
- .btn {
990
- padding: 0.5rem 1rem;
991
- border-radius: 8px;
992
- font-size: 0.85rem;
993
- font-weight: 500;
994
- cursor: pointer;
995
- transition: all 0.2s ease;
996
- border: 1px solid transparent;
997
- }
998
-
999
- .btn-primary {
1000
- background: linear-gradient(135deg, rgba(59, 130, 246, 0.9) 0%, rgba(59, 130, 246, 1) 100%);
1001
- border: 1px solid rgba(59, 130, 246, 1);
1002
- color: white;
1003
- }
1004
-
1005
- .btn-primary:hover {
1006
- background: linear-gradient(135deg, rgba(59, 130, 246, 1) 0%, rgba(79, 150, 255, 1) 100%);
1007
- transform: translateY(-1px);
1008
- }
1009
-
1010
- .btn-secondary {
1011
- background: rgba(34, 197, 94, 0.1);
1012
- border: 1px solid rgba(34, 197, 94, 0.3);
1013
- color: rgba(34, 197, 94, 1);
1014
- }
1015
-
1016
- .btn-secondary:hover {
1017
- background: rgba(34, 197, 94, 0.2);
1018
- }
1019
-
1020
- .btn-danger {
1021
- background: rgba(239, 68, 68, 0.1);
1022
- border: 1px solid rgba(239, 68, 68, 0.3);
1023
- color: rgba(239, 68, 68, 1);
1024
- }
1025
-
1026
- .btn-danger:hover {
1027
- background: rgba(239, 68, 68, 0.2);
1028
- }
1029
- </style>
1030
- </head>
1031
-
1032
- <script>
1033
- document.addEventListener('alpine:init', () => {
1034
- Alpine.data('dashboard', () => ({
1035
- selectedSpec: null,
1036
- section: 'summary',
1037
- activityTab: 'activity',
1038
- specs: [],
1039
- filteredSpecs: [],
1040
- loading: false,
1041
- searchTerm: '',
1042
- categoryFilter: 'all',
1043
- statusFilter: 'all',
1044
- showActivityLog: false,
1045
- showWorktreeModal: false,
1046
- activities: [],
1047
-
1048
- // Main prompt interface
1049
- mainPrompt: '',
1050
- selectedTemplate: '',
1051
- uploadedImages: [], // Array of {id, filename, path, placeholder}
1052
- imageCounter: 0,
1053
-
1054
- // Toast notifications
1055
- toasts: [],
1056
- sortBy: 'modified',
1057
- sortDirection: 'desc',
1058
- confirmDelete: null,
1059
-
1060
- // Button state tracking for copy buttons
1061
- buttonStates: {},
1062
-
1063
- // Keyboard navigation
1064
- selectedRowIndex: 0,
1065
- keyboardMode: false,
1066
-
1067
- // Section navigation
1068
- sectionKeyboardMode: false,
1069
- hasContent(section) {
1070
- try {
1071
- // Special handling for log sections (stored as arrays, not in sections object)
1072
- if (section === 'execution_logs' || section === 'execution_log') {
1073
- const logs = this.selectedSpec?.execution_logs || this.selectedSpec?.execution_log || [];
1074
- return Array.isArray(logs) && logs.length > 0;
1075
- }
1076
- if (section === 'debug_logs' || section === 'debug_log') {
1077
- const logs = this.selectedSpec?.debug_logs || this.selectedSpec?.debug_log || [];
1078
- return Array.isArray(logs) && logs.length > 0;
1079
- }
1080
-
1081
- // Regular section content check
1082
- if (!this.selectedSpec?.sections) return false;
1083
- const sectionData = this.selectedSpec.sections[section];
1084
- return sectionData && sectionData.content && sectionData.content.trim() !== '';
1085
- } catch (error) {
1086
- console.warn('Error checking content for section:', section, error);
1087
- return false;
1088
- }
1089
- },
1090
- getAvailableSections() {
1091
- try {
1092
- if (!this.selectedSpec?.sections) return [
1093
- { id: 'summary', label: 'Summary', key: 'executive_summary' }
1094
- ];
1095
- const sections = [
1096
- { id: 'summary', label: 'Summary', key: 'executive_summary' },
1097
- { id: 'product', label: 'Product', key: 'product_specifications' },
1098
- { id: 'architecture', label: 'Architecture', key: 'architecture_analysis' },
1099
- { id: 'implementation', label: 'Implementation', key: 'implementation_plan' },
1100
- { id: 'research', label: 'Research', key: 'research' },
1101
- { id: 'testing', label: 'Testing', key: 'testing' },
1102
- { id: 'execution', label: '🛠️ Execution Logs', key: 'execution_logs' },
1103
- { id: 'debug', label: '🐛 Debug Logs', key: 'debug_logs' },
1104
- { id: 'review', label: 'Review', key: 'review' }
1105
- ];
1106
- return sections.filter(section => this.hasContent(section.key));
1107
- } catch (error) {
1108
- console.warn('Error getting available sections:', error);
1109
- return [{ id: 'summary', label: 'Summary', key: 'executive_summary' }];
1110
- }
1111
- },
1112
- async fetchSpecs() {
1113
- this.loading = true;
1114
- try {
1115
- const response = await fetch('/api/tools/bob.spec.list');
1116
- const data = await response.json();
1117
- this.specs = data.specs || [];
1118
- this.filterSpecs();
1119
- // Initialize real-time events after first load
1120
- this.initRealTimeEvents();
1121
- } catch (error) {
1122
- console.error('Failed to fetch specs:', error);
1123
- } finally {
1124
- this.loading = false;
1125
- }
1126
- },
1127
- initRealTimeEvents() {
1128
- if (this.eventSource) return; // Already initialized
1129
-
1130
- // Real-time event streaming
1131
- this.eventSource = new EventSource('/api/events/stream');
1132
- this.activeOperations = new Map();
1133
-
1134
- this.eventSource.onmessage = (event) => {
1135
- const operation = JSON.parse(event.data);
1136
-
1137
- if (operation.type === 'connected') return;
1138
-
1139
- // Update timeline card with sub-events
1140
- this.updateTimelineCardWithSubEvents(operation);
1141
- };
1142
-
1143
- this.eventSource.onerror = (error) => {
1144
- console.warn('SSE connection error:', error);
1145
- // Reconnect after 5 seconds
1146
- setTimeout(() => {
1147
- this.eventSource.close();
1148
- this.eventSource = null;
1149
- this.initRealTimeEvents();
1150
- }, 5000);
1151
- };
1152
- },
1153
- updateTimelineCardWithSubEvents(operation) {
1154
- // Find or create timeline card for this operation
1155
- const existingActivity = this.activities.find(activity =>
1156
- activity.spec_id === operation.spec_id &&
1157
- activity.operation_id === operation.id
1158
- );
1159
-
1160
- if (existingActivity) {
1161
- // Update existing activity with sub-events
1162
- existingActivity.sub_events = operation.sub_events || [];
1163
- existingActivity.operation = operation.operation;
1164
- } else if (operation.type !== 'tool_call') {
1165
- // Add new activity for non-tool-call operations (role transitions, etc.)
1166
- this.activities.unshift({
1167
- timestamp: operation.timestamp,
1168
- spec_id: operation.spec_id,
1169
- operation_id: operation.id,
1170
- operation: operation.operation,
1171
- type: operation.type,
1172
- sub_events: operation.sub_events || [],
1173
- role: this.getRoleFromOperationType(operation.type)
1174
- });
1175
-
1176
- // Keep only last 50 activities
1177
- if (this.activities.length > 50) {
1178
- this.activities = this.activities.slice(0, 50);
1179
- }
1180
- }
1181
- },
1182
- getRoleFromOperationType(type) {
1183
- const roleMap = {
1184
- 'role_transition': 'orchestrator',
1185
- 'engineer_session': 'engineer',
1186
- 'debug_session': 'debugger',
1187
- 'review_session': 'reviewer',
1188
- 'tool_call': 'system'
1189
- };
1190
- return roleMap[type] || 'system';
1191
- },
1192
- sortSpecs(column) {
1193
- if (this.sortBy === column) {
1194
- this.sortDirection = this.sortDirection === 'asc' ? 'desc' : 'asc';
1195
- } else {
1196
- this.sortBy = column;
1197
- this.sortDirection = column === 'created_at' ? 'desc' : 'asc';
1198
- }
1199
- this.filterSpecs();
1200
- },
1201
- filterSpecs() {
1202
- this.filteredSpecs = this.specs.filter(spec => {
1203
- const matchesSearch = this.searchTerm === '' ||
1204
- spec.title.toLowerCase().includes(this.searchTerm.toLowerCase());
1205
- const matchesCategory = this.categoryFilter === 'all' ||
1206
- spec.category === this.categoryFilter;
1207
- const matchesStatus = this.statusFilter === 'all' ||
1208
- spec.state === this.statusFilter;
1209
- return matchesSearch && matchesCategory && matchesStatus;
1210
- });
1211
-
1212
- // Sort filtered results
1213
- this.filteredSpecs.sort((a, b) => {
1214
- let valueA, valueB;
1215
-
1216
- switch (this.sortBy) {
1217
- case 'title':
1218
- valueA = a.title?.toLowerCase() || '';
1219
- valueB = b.title?.toLowerCase() || '';
1220
- break;
1221
- case 'category':
1222
- valueA = a.category?.toLowerCase() || '';
1223
- valueB = b.category?.toLowerCase() || '';
1224
- break;
1225
- case 'state':
1226
- valueA = a.state?.toLowerCase() || '';
1227
- valueB = b.state?.toLowerCase() || '';
1228
- break;
1229
- case 'created_at':
1230
- valueA = new Date(a.created_at || '').getTime();
1231
- valueB = new Date(b.created_at || '').getTime();
1232
- break;
1233
- case 'modified':
1234
- valueA = new Date(a.updated_at || a.created_at || '').getTime();
1235
- valueB = new Date(b.updated_at || b.created_at || '').getTime();
1236
- break;
1237
- default:
1238
- return 0;
1239
- }
1240
-
1241
- if (this.sortBy === 'created_at' || this.sortBy === 'modified') {
1242
- return this.sortDirection === 'desc' ? valueB - valueA : valueA - valueB;
1243
- } else {
1244
- if (valueA < valueB) return this.sortDirection === 'desc' ? 1 : -1;
1245
- if (valueA > valueB) return this.sortDirection === 'desc' ? -1 : 1;
1246
- return 0;
1247
- }
1248
- });
1249
- },
1250
- confirmDeleteSpec(specId, title) {
1251
- console.log('Confirming delete for:', specId, title);
1252
- this.confirmDelete = { id: specId, title: title };
1253
- },
1254
- cancelDelete() {
1255
- this.confirmDelete = null;
1256
- },
1257
- async deleteSpec() {
1258
- if (!this.confirmDelete) {
1259
- console.error('No confirmDelete object found');
1260
- return;
1261
- }
1262
-
1263
- const specId = this.confirmDelete.id;
1264
- const title = this.confirmDelete.title;
1265
-
1266
- console.log('Deleting spec:', specId, title);
1267
-
1268
- try {
1269
- const response = await fetch('/api/tools/bob.spec.delete', {
1270
- method: 'POST',
1271
- headers: { 'Content-Type': 'application/json' },
1272
- body: JSON.stringify({ spec_id: specId })
1273
- });
1274
-
1275
- const result = await response.json();
1276
- console.log('Delete response:', result);
1277
-
1278
- if (response.ok && (result.status === 'deleted' || result.success)) {
1279
- // Remove from local arrays
1280
- this.specs = this.specs.filter(spec => spec.spec_id !== specId);
1281
- this.filteredSpecs = this.filteredSpecs.filter(spec => spec.spec_id !== specId);
1282
- this.confirmDelete = null; // Close the confirmation modal
1283
- this.showToast(`✅ Manual "${title}" deleted successfully`, 'success');
1284
- } else {
1285
- console.error('Delete failed:', result);
1286
- this.showToast(`❌ Failed to delete manual: ${result.error || result.message || 'Unknown error'}`, 'error');
1287
- }
1288
- } catch (error) {
1289
- console.error('Error deleting spec:', error);
1290
- this.showToast('❌ Error deleting manual. Please try again.', 'error');
1291
- }
1292
- },
1293
- async openSpec(specId) {
1294
- try {
1295
- const response = await fetch('/api/tools/bob.spec.get', {
1296
- method: 'POST',
1297
- headers: { 'Content-Type': 'application/json' },
1298
- body: JSON.stringify({ spec_id: specId })
1299
- });
1300
- const spec = await response.json();
1301
- this.selectedSpec = spec;
1302
- // Set to first available section with content
1303
- const availableSections = this.getAvailableSections();
1304
- this.section = availableSections.length > 0 ? availableSections[0].id : 'summary';
1305
- } catch (error) {
1306
- console.error('Failed to fetch spec:', error);
1307
- alert('Failed to load MANUAL details');
1308
- }
1309
- },
1310
- async exportToMarkdown(specId) {
1311
- try {
1312
- const response = await fetch('/api/tools/bob.spec.export', {
1313
- method: 'POST',
1314
- headers: { 'Content-Type': 'application/json' },
1315
- body: JSON.stringify({ spec_id: specId })
1316
- });
1317
- const data = await response.json();
1318
- if (data.markdown_path) {
1319
- alert(`✅ MANUAL exported to: ${data.markdown_path}`);
1320
- } else {
1321
- alert('❌ Export failed');
1322
- }
1323
- } catch (error) {
1324
- console.error('Export error:', error);
1325
- alert('❌ Export failed');
1326
- }
1327
- },
1328
- getWorktreeName(spec) {
1329
- // Generate worktree name from spec title
1330
- if (!spec || !spec.title) return 'none';
1331
- return 'feature/' + spec.title.toLowerCase()
1332
- .replace(/[^a-z0-9\s]/g, '')
1333
- .replace(/\s+/g, '-')
1334
- .substring(0, 20);
1335
- },
1336
- renderMarkdown(text) {
1337
- if (!text) return '';
1338
-
1339
- // Escape HTML first
1340
- text = text.replace(/&/g, '&amp;')
1341
- .replace(/</g, '&lt;')
1342
- .replace(/>/g, '&gt;');
1343
-
1344
- // Handle language-specific code blocks with copy buttons
1345
- text = text.replace(/```(\w+)?\n?([\s\S]*?)\n?```/g, (match, language, code) => {
1346
- const cleanCode = code.trim();
1347
- const copyId = 'copy_' + Math.random().toString(36).substr(2, 9);
1348
- const lang = language || 'code';
1349
-
1350
- // Special handling for JSON - format it nicely
1351
- let formattedCode = cleanCode;
1352
- if (lang === 'json') {
1353
- try {
1354
- const parsed = JSON.parse(cleanCode);
1355
- formattedCode = JSON.stringify(parsed, null, 2);
1356
- } catch (e) {
1357
- // If JSON parsing fails, use original code
1358
- formattedCode = cleanCode;
1359
- }
1360
- }
1361
-
1362
- // Language display names
1363
- const languageNames = {
1364
- 'js': 'JavaScript',
1365
- 'javascript': 'JavaScript',
1366
- 'ts': 'TypeScript',
1367
- 'typescript': 'TypeScript',
1368
- 'json': 'JSON',
1369
- 'bash': 'Bash',
1370
- 'sh': 'Shell',
1371
- 'python': 'Python',
1372
- 'py': 'Python',
1373
- 'html': 'HTML',
1374
- 'css': 'CSS',
1375
- 'yaml': 'YAML',
1376
- 'yml': 'YAML',
1377
- 'xml': 'XML',
1378
- 'sql': 'SQL',
1379
- 'java': 'Java',
1380
- 'go': 'Go',
1381
- 'rust': 'Rust',
1382
- 'cpp': 'C++',
1383
- 'c': 'C',
1384
- 'php': 'PHP',
1385
- 'ruby': 'Ruby',
1386
- 'swift': 'Swift',
1387
- 'kotlin': 'Kotlin',
1388
- 'dart': 'Dart',
1389
- 'scala': 'Scala',
1390
- 'r': 'R',
1391
- 'matlab': 'MATLAB'
1392
- };
1393
-
1394
- const displayName = languageNames[lang.toLowerCase()] || lang.charAt(0).toUpperCase() + lang.slice(1);
1395
-
1396
- return `&lt;div class="code-block-container"&gt;
1397
- &lt;div class="code-block-header"&gt;
1398
- &lt;span class="code-lang"&gt;${displayName}&lt;/span&gt;
1399
- &lt;button class="copy-code-btn" onclick="copyToClipboard('${formattedCode.replace(/'/g, "\\'")}', '${copyId}')" id="${copyId}"&gt;📋 Copy&lt;/button&gt;
1400
- &lt;/div&gt;
1401
- &lt;pre&gt;&lt;code&gt;${formattedCode}&lt;/code&gt;&lt;/pre&gt;
1402
- &lt;/div&gt;`;
1403
- });
1404
-
1405
- // Protect code block content from further processing
1406
- const codeBlockPlaceholders = [];
1407
- text = text.replace(/(&lt;div class="code-block-container"&gt;[\s\S]*?&lt;\/div&gt;)/g, (match) => {
1408
- const placeholder = `__CODE_BLOCK_${codeBlockPlaceholders.length}__`;
1409
- codeBlockPlaceholders.push(match);
1410
- return placeholder;
1411
- });
1412
-
1413
- // Simple markdown renderer (now safe from code block interference)
1414
- let html = text
1415
- // Headers
1416
- .replace(/^### (.*$)/gim, '&lt;h3&gt;$1&lt;/h3&gt;')
1417
- .replace(/^## (.*$)/gim, '&lt;h2&gt;$1&lt;/h2&gt;')
1418
- .replace(/^# (.*$)/gim, '&lt;h1&gt;$1&lt;/h1&gt;')
1419
- // Bold
1420
- .replace(/\*\*(.*?)\*\*/g, '&lt;strong&gt;$1&lt;/strong&gt;')
1421
- // Italic
1422
- .replace(/\*(.*?)\*/g, '&lt;em&gt;$1&lt;/em&gt;')
1423
- // Inline code
1424
- .replace(/`([^`]+)`/g, '&lt;code class="inline-code"&gt;$1&lt;/code&gt;')
1425
- // Line breaks
1426
- .replace(/\n\n/g, '&lt;/p&gt;&lt;p&gt;')
1427
- .replace(/\n/g, '&lt;br&gt;')
1428
- // Lists
1429
- .replace(/^\* (.*$)/gim, '&lt;li&gt;$1&lt;/li&gt;')
1430
- .replace(/^- (.*$)/gim, '&lt;li&gt;$1&lt;/li&gt;')
1431
- // Checkmarks and emojis
1432
- .replace(/✅/g, '✅')
1433
- .replace(/🚀/g, '🚀')
1434
- .replace(/🔗/g, '🔗')
1435
- .replace(/📋/g, '📋')
1436
- // Wrap in paragraphs
1437
- .replace(/^(?!&lt;)/gm, '&lt;p&gt;')
1438
- .replace(/$/gm, '&lt;/p&gt;')
1439
- // Clean up
1440
- .replace(/&lt;\/p&gt;&lt;p&gt;/g, '&lt;/p&gt;\n&lt;p&gt;')
1441
- .replace(/(&lt;li&gt;.*&lt;\/li&gt;)/gs, '&lt;ul&gt;$1&lt;/ul&gt;')
1442
- .replace(/&lt;p&gt;&lt;\/p&gt;/g, '');
1443
-
1444
- // Restore code blocks
1445
- codeBlockPlaceholders.forEach((codeBlock, index) => {
1446
- html = html.replace(`__CODE_BLOCK_${index}__`, codeBlock);
1447
- });
1448
-
1449
- // Unescape the HTML tags we want
1450
- return html.replace(/&lt;/g, '<').replace(/&gt;/g, '>');
1451
- },
1452
- simplifyFilePath(fullPath) {
1453
- if (!fullPath) return '';
1454
- // Extract just the repo name and path after it
1455
- const match = fullPath.match(/\/([^\/]+)\/(.+)$/);
1456
- if (match && match[1] && match[2]) {
1457
- return `${match[1]}/${match[2]}`;
1458
- }
1459
- return fullPath; // fallback to full path if pattern doesn't match
1460
- },
1461
-
1462
- // Helper functions for enhanced file change visualization
1463
- getFileName(filePath) {
1464
- if (!filePath) return '';
1465
- return filePath.split('/').pop() || filePath;
1466
- },
1467
-
1468
- getFileIcon(filePath) {
1469
- if (!filePath) return '📄';
1470
- const ext = filePath.split('.').pop()?.toLowerCase();
1471
- const iconMap = {
1472
- 'ts': '🟦', 'tsx': '🔷', 'js': '🟨', 'jsx': '🔸',
1473
- 'json': '📋', 'md': '📖', 'css': '🎨', 'html': '🌐',
1474
- 'vue': '💚', 'py': '🐍', 'go': '🐹', 'rs': '🦀',
1475
- 'yml': '⚙️', 'yaml': '⚙️', 'xml': '📜', 'sql': '🗃️'
1476
- };
1477
- return iconMap[ext] || '📄';
1478
- },
1479
-
1480
- getComplianceClass(fileChange) {
1481
- const status = fileChange?.compliance_status || 'unknown';
1482
- return `compliance-${status}`;
1483
- },
1484
-
1485
- getComplianceCardClass(fileChange) {
1486
- const status = fileChange?.compliance_status || 'unknown';
1487
- return `compliance-${status}-border`;
1488
- },
1489
-
1490
- getComplianceBadge(fileChange) {
1491
- const status = fileChange?.compliance_status || 'unknown';
1492
- const badgeMap = {
1493
- 'compliant': '✅ Compliant',
1494
- 'deviation': '⚠️ Deviation',
1495
- 'unexpected': '❓ Unexpected',
1496
- 'unknown': '⚪ Unknown'
1497
- };
1498
- return badgeMap[status] || '⚪ Unknown';
1499
- },
1500
- getActivityTimeline(spec) {
1501
- if (!spec) return [];
1502
-
1503
- // PRIORITY 1: Use pre-built timeline from backend (includes enhanced activities)
1504
- // Backend already prioritizes enhanced_activities and enriches with git diff
1505
- if (spec.timeline && spec.timeline.length > 0) {
1506
- // Timeline already sorted by backend, reverse for newest first
1507
- return spec.timeline.slice().reverse().slice(0, 50);
1508
- }
1509
-
1510
- // PRIORITY 2: Build timeline from enhanced_activities if available
1511
- const timeline = [];
1512
- const enhancedActivities = spec.enhanced_activities || [];
1513
-
1514
- if (enhancedActivities.length > 0) {
1515
- enhancedActivities.forEach(activity => {
1516
- timeline.push({
1517
- ...activity,
1518
- human_description: activity.summary,
1519
- is_enhanced: true
1520
- });
1521
- });
1522
-
1523
- // Sort by timestamp and return
1524
- return timeline.sort((a, b) =>
1525
- new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()
1526
- ).slice(0, 50);
1527
- }
1528
-
1529
- // FALLBACK: Build timeline client-side from legacy logs
1530
- // Add execution logs
1531
- const executionLogs = spec.execution_logs || spec.execution_log || [];
1532
- executionLogs.forEach(log => {
1533
- timeline.push({
1534
- ...log,
1535
- type: 'execution',
1536
- role: 'Engineer',
1537
- is_enhanced: false
1538
- });
1539
- });
1540
-
1541
- // Add debug logs
1542
- const debugLogs = spec.debug_logs || spec.debug_log || [];
1543
- debugLogs.forEach(log => {
1544
- timeline.push({
1545
- ...log,
1546
- type: 'debug',
1547
- role: 'Debugger',
1548
- is_enhanced: false
1549
- });
1550
- });
1551
-
1552
- // Add activity logs (new field for spec activity tracking)
1553
- if (spec.activity) {
1554
- spec.activity.forEach(activity => {
1555
- timeline.push({
1556
- ...activity,
1557
- type: activity.type || 'activity',
1558
- action: activity.action || activity.note || 'Activity logged',
1559
- is_enhanced: false
1560
- });
1561
- });
1562
- }
1563
-
1564
- // Add synthetic spec creation event if timeline is empty
1565
- if (timeline.length === 0 && spec.created_at) {
1566
- timeline.push({
1567
- timestamp: spec.created_at,
1568
- type: 'spec_created',
1569
- role: 'Architect',
1570
- action: 'Created MANUAL',
1571
- note: `MANUAL ${spec.spec_id} created`,
1572
- is_enhanced: false
1573
- });
1574
- }
1575
-
1576
- // Add worktree creation if it exists
1577
- if (spec.worktree && timeline.length <= 2) {
1578
- const workTreeTime = new Date(spec.modified_at || Date.now()).toISOString();
1579
- timeline.push({
1580
- timestamp: workTreeTime,
1581
- type: 'worktree_created',
1582
- role: 'System',
1583
- action: 'worktree_created',
1584
- note: `Created worktree: ${spec.worktree}`,
1585
- branch: spec.worktree,
1586
- is_enhanced: false
1587
- });
1588
- }
1589
-
1590
- // Sort by timestamp (newest first for better UX)
1591
- return timeline.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()).slice(0, 50);
1592
- },
1593
- async loadActivityLog() {
1594
- try {
1595
- // Load real activity from dashboard API
1596
- const response = await fetch('/api/dashboard-data');
1597
- const data = await response.json();
1598
-
1599
- // Format recent changes for display
1600
- this.activities = (data.recent_changes || []).slice(0, 20).map(change => ({
1601
- timestamp: change.timestamp,
1602
- type: change.event || change.type,
1603
- author: change.role || 'system',
1604
- message: this.formatActivityMessage(change),
1605
- spec_id: change.spec_id,
1606
- data: change.data
1607
- }));
1608
-
1609
- } catch (error) {
1610
- console.error('Failed to load activity log:', error);
1611
- // Fallback to empty state
1612
- this.activities = [];
1613
- }
1614
- this.showActivityLog = true;
1615
- },
1616
-
1617
- formatActivityMessage(change) {
1618
- if (change.event === 'orchestrator_routing') {
1619
- return `Orchestrator routed to ${change.data?.target_mode} mode`;
1620
- } else if (change.event === 'mcp_tool_workshop') {
1621
- return `MCP tool executed: ${change.data?.tool_name} (${change.data?.duration_ms}ms)`;
1622
- } else if (change.type === 'file') {
1623
- return `File changed: ${change.path}`;
1624
- } else {
1625
- return change.event || change.type || 'Unknown activity';
1626
- }
1627
- },
1628
-
1629
- showToast(message, type = 'info') {
1630
- console.log('🔔 showToast called:', message, type);
1631
- const toast = {
1632
- id: Date.now() + Math.random(),
1633
- message,
1634
- type,
1635
- visible: true
1636
- };
1637
- console.log('🔔 Toast object created:', toast);
1638
- this.toasts.push(toast);
1639
- console.log('🔔 Toasts array after push:', this.toasts.length, this.toasts);
1640
-
1641
- // Auto-remove after 4 seconds
1642
- setTimeout(() => {
1643
- console.log('🔔 Removing toast:', toast.id);
1644
- this.removeToast(toast.id);
1645
- }, 4000);
1646
- },
1647
-
1648
- removeToast(id) {
1649
- const index = this.toasts.findIndex(toast => toast.id === id);
1650
- if (index !== -1) {
1651
- this.toasts.splice(index, 1);
1652
- }
1653
- },
1654
-
1655
-
1656
- async handleImagePaste(event) {
1657
- const items = event.clipboardData?.items;
1658
- if (!items) return;
1659
-
1660
- for (let i = 0; i < items.length; i++) {
1661
- const item = items[i];
1662
- if (item.type.startsWith('image/')) {
1663
- event.preventDefault();
1664
- const file = item.getAsFile();
1665
- if (file) {
1666
- await this.uploadImageFile(file);
1667
- }
1668
- }
1669
- }
1670
- },
1671
-
1672
- async uploadImageFile(file) {
1673
- try {
1674
- const formData = new FormData();
1675
- formData.append('image', file);
1676
-
1677
- const response = await fetch('/api/images/upload-inline', {
1678
- method: 'POST',
1679
- body: formData
1680
- });
1681
-
1682
- const result = await response.json();
1683
-
1684
- if (response.ok && result.success) {
1685
- // Add image to uploaded list
1686
- this.uploadedImages.push(result.placeholder);
1687
-
1688
- // Insert placeholder into prompt text at cursor position
1689
- const placeholder = `[🖼️ ${result.placeholder.filename}]`;
1690
- this.insertTextAtCursor(placeholder);
1691
-
1692
- this.addToast(`📎 Image uploaded: ${result.placeholder.filename}`, 'success');
1693
- } else {
1694
- throw new Error(result.error || 'Upload failed');
1695
- }
1696
- } catch (error) {
1697
- console.error('Image upload error:', error);
1698
- this.addToast(`Failed to upload image: ${error.message}`, 'error');
1699
- }
1700
- },
1701
-
1702
- async handleImageDrop(event) {
1703
- const files = event.dataTransfer?.files;
1704
- if (!files) return;
1705
-
1706
- for (let i = 0; i < files.length; i++) {
1707
- const file = files[i];
1708
- if (file.type.startsWith('image/')) {
1709
- await this.uploadImageFile(file);
1710
- }
1711
- }
1712
- },
1713
-
1714
- insertTextAtCursor(text) {
1715
- // Simple text insertion - append to current prompt
1716
- if (this.mainPrompt) {
1717
- this.mainPrompt += ' ' + text;
1718
- } else {
1719
- this.mainPrompt = text;
1720
- }
1721
- },
1722
-
1723
- removeImage(imageId) {
1724
- // Remove from uploaded images
1725
- this.uploadedImages = this.uploadedImages.filter(img => img.id !== imageId);
1726
-
1727
- // Remove placeholder from prompt text
1728
- const placeholderPattern = new RegExp(`\\[🖼️ ${imageId}\\.(png|jpg|jpeg|gif|webp)\\]`, 'gi');
1729
- this.mainPrompt = this.mainPrompt.replace(placeholderPattern, '').trim();
1730
-
1731
- this.addToast('Image removed', 'info');
1732
- },
1733
-
1734
- translatePromptWithImages() {
1735
- let translatedPrompt = this.mainPrompt;
1736
-
1737
- // Replace image placeholders with file paths
1738
- this.uploadedImages.forEach(image => {
1739
- const placeholderPattern = new RegExp(`\\[🖼️ ${image.filename}\\]`, 'g');
1740
- translatedPrompt = translatedPrompt.replace(placeholderPattern, image.path);
1741
- });
1742
-
1743
- return translatedPrompt;
1744
- },
1745
-
1746
-
1747
-
1748
- // Duplicate toast system removed - using main toast system at line 1020
1749
-
1750
- getToastIcon(type) {
1751
- switch (type) {
1752
- case 'success': return '✅';
1753
- case 'error': return '❌';
1754
- case 'warning': return '⚠️';
1755
- case 'info': return 'ℹ️';
1756
- default: return '💬';
1757
- }
1758
- },
1759
-
1760
- getToastColor(type) {
1761
- switch (type) {
1762
- case 'success': return 'rgba(34, 197, 94, 0.9)';
1763
- case 'error': return 'rgba(239, 68, 68, 0.9)';
1764
- case 'warning': return 'rgba(245, 158, 11, 0.9)';
1765
- case 'info': return 'rgba(59, 130, 246, 0.9)';
1766
- default: return 'rgba(75, 85, 99, 0.9)';
1767
- }
1768
- },
1769
-
1770
- // Keyboard navigation methods
1771
- handleKeydown(event) {
1772
- // Handle Tab navigation when in a manual modal
1773
- if (this.selectedSpec) {
1774
- switch(event.key) {
1775
- case 'Tab':
1776
- event.preventDefault();
1777
- this.navigateToNextSection(event.shiftKey);
1778
- break;
1779
- }
1780
- return;
1781
- }
1782
-
1783
- // Only handle keys when not in other modals and when we have specs
1784
- if (this.showActivityLog || this.showWorktreeModal || this.filteredSpecs.length === 0) {
1785
- return;
1786
- }
1787
-
1788
- switch(event.key) {
1789
- case 'ArrowDown':
1790
- event.preventDefault();
1791
- this.keyboardMode = true;
1792
- this.selectedRowIndex = Math.min(this.selectedRowIndex + 1, this.filteredSpecs.length - 1);
1793
- this.scrollToSelectedRow();
1794
- break;
1795
- case 'ArrowUp':
1796
- event.preventDefault();
1797
- this.keyboardMode = true;
1798
- this.selectedRowIndex = Math.max(this.selectedRowIndex - 1, 0);
1799
- this.scrollToSelectedRow();
1800
- break;
1801
- case 'Enter':
1802
- event.preventDefault();
1803
- if (this.keyboardMode && this.filteredSpecs[this.selectedRowIndex]) {
1804
- this.openSpec(this.filteredSpecs[this.selectedRowIndex].spec_id);
1805
- }
1806
- break;
1807
- case 'Delete':
1808
- case 'Backspace':
1809
- event.preventDefault();
1810
- if (this.keyboardMode && this.filteredSpecs[this.selectedRowIndex]) {
1811
- const spec = this.filteredSpecs[this.selectedRowIndex];
1812
- this.confirmDeleteSpec(spec.spec_id, spec.title);
1813
- }
1814
- break;
1815
- }
1816
- },
1817
-
1818
- scrollToSelectedRow() {
1819
- this.$nextTick(() => {
1820
- const table = document.querySelector('.spec-table tbody');
1821
- const rows = table?.querySelectorAll('tr');
1822
- if (rows && rows[this.selectedRowIndex]) {
1823
- rows[this.selectedRowIndex].scrollIntoView({
1824
- behavior: 'smooth',
1825
- block: 'nearest'
1826
- });
1827
- }
1828
- });
1829
- },
1830
-
1831
- isRowSelected(index) {
1832
- return this.keyboardMode && this.selectedRowIndex === index;
1833
- },
1834
-
1835
- // Section navigation methods
1836
- navigateToNextSection(reverse = false) {
1837
- const sections = this.getAvailableSections();
1838
- if (sections.length <= 1) return; // No navigation needed with 0 or 1 sections
1839
-
1840
- const currentIndex = sections.findIndex(s => s.id === this.section);
1841
- let nextIndex;
1842
-
1843
- if (reverse) {
1844
- // Shift+Tab: go to previous section
1845
- nextIndex = currentIndex > 0 ? currentIndex - 1 : sections.length - 1;
1846
- } else {
1847
- // Tab: go to next section
1848
- nextIndex = currentIndex < sections.length - 1 ? currentIndex + 1 : 0;
1849
- }
1850
-
1851
- this.section = sections[nextIndex].id;
1852
- this.sectionKeyboardMode = true;
1853
- },
1854
-
1855
-
1856
- }));
1857
- });
1858
- </script>
1859
-
1860
- <body class="h-full" x-data="dashboard" x-init="fetchSpecs()" @keydown.escape.window="selectedSpec = null; showActivityLog = false; showWorktreeModal = false" @keydown.window="handleKeydown($event)">
1861
-
1862
- <!-- Header -->
1863
- <header class="header">
1864
- <div class="logo">
1865
- <span style="color: var(--accent-orange);">👷</span>
1866
- <span class="text-gradient" style="text-transform: lowercase;">Bob's Workshop</span>
1867
- </div>
1868
-
1869
- <!-- Separate Metric Boxes -->
1870
- <div style="display: flex; align-items: center; gap: 1rem;"
1871
- x-data="{
1872
- worktrees: { committed: 0, dirty: 0, clean: 0 },
1873
- specs: { draft: 0, 'in-progress': 0, completed: 0 },
1874
- async loadMetrics() {
1875
- try {
1876
- const [worktreesRes, specsRes] = await Promise.all([
1877
- fetch('/api/tools/bob.worktree.list').catch(() => ({ json: () => ({committed:0,dirty:0,clean:0}) })),
1878
- fetch('/api/tools/bob.spec.list')
1879
- ]);
1880
- const worktreesData = await worktreesRes.json();
1881
- this.worktrees = worktreesData;
1882
- const specsData = await specsRes.json();
1883
- const allSpecs = specsData.specs || [];
1884
- this.specs = {
1885
- draft: allSpecs.filter(s => s.state === 'draft').length,
1886
- 'in-progress': allSpecs.filter(s => s.state === 'in-progress').length,
1887
- completed: allSpecs.filter(s => s.state === 'completed').length
1888
- };
1889
- } catch (error) {
1890
- console.error('Failed to load metrics:', error);
1891
- }
1892
- }
1893
- }" x-init="loadMetrics()">
1894
-
1895
- <!-- Manuals Box -->
1896
- <div style="background: rgba(59, 130, 246, 0.12); backdrop-filter: blur(10px); border: 1px solid rgba(59, 130, 246, 0.2); border-radius: 10px; padding: 0.6rem 0.9rem; display: flex; align-items: center; gap: 0.6rem; font-size: 0.85rem; font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; color: var(--text-secondary);">
1897
- <span>📖 Manuals:</span>
1898
- <span style="color: #9ca3af;" x-text="specs.draft + 'd'"></span>
1899
- <span style="color: var(--text-muted);">|</span>
1900
- <span style="color: var(--accent-orange);" x-text="specs['in-progress'] + 'p'"></span>
1901
- <span style="color: var(--text-muted);">|</span>
1902
- <span style="color: var(--accent-green);" x-text="specs.completed + '✓'"></span>
1903
- </div>
1904
-
1905
- <!-- Trees Box -->
1906
- <div style="background: rgba(16, 185, 129, 0.12); backdrop-filter: blur(10px); border: 1px solid rgba(16, 185, 129, 0.2); border-radius: 10px; padding: 0.6rem 0.9rem; display: flex; align-items: center; gap: 0.6rem; font-size: 0.85rem; font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; color: var(--text-secondary);">
1907
- <span>🌿 Trees:</span>
1908
- <span style="color: var(--accent-green);" x-text="(worktrees.committed?.length || 0) + 'c'"></span>
1909
- <span style="color: var(--text-muted);">|</span>
1910
- <span style="color: var(--accent-orange);" x-text="(worktrees.dirty?.length || 0) + '!'"></span>
1911
- <span style="color: var(--text-muted);">|</span>
1912
- <span style="color: var(--text-secondary);" x-text="(worktrees.clean?.length || 0) + '~'"></span>
1913
- <button @click="showWorktreeModal = true"
1914
- style="margin-left: 0.5rem; padding: 0.25rem 0.5rem; background: var(--accent-orange); border: 1px solid var(--accent-orange); border-radius: 6px; color: white; font-size: 0.75rem; cursor: pointer; transition: all 0.2s ease; font-weight: 600;"
1915
- @mouseenter="$el.style.background = '#e97b00'"
1916
- @mouseleave="$el.style.background = 'var(--accent-orange)'"
1917
- title="View Active Worktrees">
1918
- 🏗️ View
1919
- </button>
1920
- </div>
1921
-
1922
- </div>
1923
-
1924
- <div style="font-size: 0.9rem; color: var(--text-secondary); white-space: nowrap;"
1925
- x-data="{
1926
- projectName: 'loading...',
1927
- workshopVerbs: ['servicing', 'working on', 'fixing', 'building', 'crafting', 'assembling', 'engineering', 'constructing', 'maintaining', 'upgrading'],
1928
- currentVerb: 'servicing',
1929
- async init() {
1930
- this.currentVerb = this.workshopVerbs[Math.floor(Math.random() * this.workshopVerbs.length)];
1931
- // Fetch dynamic repo name from API
1932
- try {
1933
- const response = await fetch('/api/project-info');
1934
- const data = await response.json();
1935
- this.projectName = data.repoName || 'unknown';
1936
- } catch (error) {
1937
- console.error('Failed to fetch project info:', error);
1938
- this.projectName = 'unknown';
1939
- }
1940
- }
1941
- }">
1942
- <span style="background: linear-gradient(135deg, rgba(245, 158, 11, 0.15) 0%, rgba(245, 158, 11, 0.08) 100%); backdrop-filter: blur(8px); border: 1px solid rgba(245, 158, 11, 0.25); border-radius: 12px; padding: 0.7rem 1.1rem; display: inline-flex; align-items: center; gap: 0.5rem; font-family: var(--font-heading); font-weight: 500; font-size: 0.9rem; color: var(--text-primary); box-shadow: 0 2px 8px rgba(245, 158, 11, 0.1);">
1943
- <span style="color: #fbbf24;">now</span>
1944
- <span style="color: var(--accent-orange);" x-text="currentVerb"></span>
1945
- <span style="color: var(--text-muted);">:</span>
1946
- <span style="color: #fbbf24; font-weight: 600;" x-text="projectName"></span>
1947
- </span>
1948
- </div>
1949
- </header>
1950
-
1951
-
1952
-
1953
- <!-- Manual Table -->
1954
- <div class="spec-container fade-in" style="margin: 2rem;">
1955
- <!-- Header with integrated controls -->
1956
- <div class="spec-header" style="padding: 1.5rem 2rem; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 1rem;">
1957
- <div style="display: flex; align-items: center; gap: 1rem;">
1958
- <h2 class="heading" style="margin: 0; font-size: 1.25rem; color: white; text-transform: lowercase;">📄 manual explorer</h2>
1959
- <span style="background: rgba(255, 255, 255, 0.1); color: var(--text-secondary); padding: 0.25rem 0.75rem; border-radius: 12px; font-size: 0.75rem; font-weight: 600; font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;"
1960
- x-text="`${filteredSpecs.length} of ${specs.length} Manuals`"></span>
1961
- <span x-show="loading" class="loading"></span>
1962
- </div>
1963
-
1964
- <!-- Integrated Controls Row -->
1965
- <div style="display: flex; align-items: center; gap: 1rem; flex: 1; max-width: 800px;">
1966
- <!-- Search Input -->
1967
- <div style="flex: 2; min-width: 200px;">
1968
- <input type="text"
1969
- placeholder="🔍 Search Manuals..."
1970
- x-model="searchTerm"
1971
- @input="filterSpecs()"
1972
- style="width: 100%; padding: 0.5rem 0.75rem; background: var(--bg-glass); border: 1px solid var(--border-glass); border-radius: 6px; color: var(--text-primary); font-family: var(--font-body); font-size: 0.875rem;">
1973
- </div>
1974
-
1975
- <!-- Category Filter -->
1976
- <select x-model="categoryFilter"
1977
- @change="filterSpecs()"
1978
- style="padding: 0.5rem 0.75rem; background: var(--bg-glass); border: 1px solid var(--border-glass); border-radius: 6px; color: var(--text-primary); font-family: var(--font-body); font-size: 0.875rem;">
1979
- <option value="all">All Categories</option>
1980
- <option value="frontend">Frontend</option>
1981
- <option value="backend">Backend</option>
1982
- <option value="fullstack">Fullstack</option>
1983
- <option value="general">General</option>
1984
- </select>
1985
-
1986
- <!-- Status Filter -->
1987
- <select x-model="statusFilter"
1988
- @change="filterSpecs()"
1989
- style="padding: 0.5rem 0.75rem; background: var(--bg-glass); border: 1px solid var(--border-glass); border-radius: 6px; color: var(--text-primary); font-family: var(--font-body); font-size: 0.875rem;">
1990
- <option value="all">All Status</option>
1991
- <option value="draft">Draft</option>
1992
- <option value="planning">Planning</option>
1993
- <option value="in-progress">In Progress</option>
1994
- <option value="completed">Completed</option>
1995
- </select>
1996
-
1997
- <!-- Clear Filters -->
1998
- <button class="btn btn-secondary"
1999
- @click="searchTerm = ''; categoryFilter = 'all'; statusFilter = 'all'; filterSpecs()"
2000
- style="padding: 0.5rem 0.75rem; font-size: 0.875rem; white-space: nowrap;">
2001
- ✕ Clear
2002
- </button>
2003
-
2004
- <!-- Refresh Button -->
2005
- <button class="btn btn-secondary" @click="fetchSpecs()" style="padding: 0.5rem 0.75rem; font-size: 0.875rem; white-space: nowrap; font-family: var(--font-body);">
2006
- 🔄 Refresh
2007
- </button>
2008
- </div>
2009
- </div>
2010
-
2011
-
2012
- <table class="spec-table">
2013
- <thead>
2014
- <tr>
2015
- <th class="sortable"
2016
- :class="{ 'sorted-asc': sortBy === 'title' && sortDirection === 'asc', 'sorted-desc': sortBy === 'title' && sortDirection === 'desc' }"
2017
- @click="sortSpecs('title')">Title</th>
2018
- <th class="sortable"
2019
- :class="{ 'sorted-asc': sortBy === 'category' && sortDirection === 'asc', 'sorted-desc': sortBy === 'category' && sortDirection === 'desc' }"
2020
- @click="sortSpecs('category')">Category</th>
2021
- <th class="sortable"
2022
- :class="{ 'sorted-asc': sortBy === 'state' && sortDirection === 'asc', 'sorted-desc': sortBy === 'state' && sortDirection === 'desc' }"
2023
- @click="sortSpecs('state')">Status</th>
2024
- <th>Worktree</th>
2025
- <th class="sortable"
2026
- :class="{ 'sorted-asc': sortBy === 'modified' && sortDirection === 'asc', 'sorted-desc': sortBy === 'modified' && sortDirection === 'desc' }"
2027
- @click="sortSpecs('modified')">Modified</th>
2028
- <th>Actions</th>
2029
- </tr>
2030
- </thead>
2031
- <tbody>
2032
- <template x-for="(spec, index) in filteredSpecs" :key="spec.spec_id">
2033
- <tr :class="{ 'keyboard-selected': isRowSelected(index) }"
2034
- @click="keyboardMode = false; selectedRowIndex = index"
2035
- :style="isRowSelected(index) ? 'background: rgba(245, 158, 11, 0.1); border-left: 3px solid var(--accent-orange);' : ''"
2036
- tabindex="0">
2037
- <td>
2038
- <div style="font-weight: 600; cursor: pointer; color: var(--text-primary);"
2039
- x-text="spec.title"
2040
- @click="openSpec(spec.spec_id)"></div>
2041
- <div style="font-size: 0.75rem; color: var(--text-muted); margin-top: 0.25rem;"
2042
- x-text="spec.spec_id"></div>
2043
- </td>
2044
- <td style="color: var(--text-secondary);" x-text="spec.category || '—'"></td>
2045
- <td>
2046
- <span class="status-badge"
2047
- :class="`status-${spec.state}`"
2048
- x-text="spec.state"></span>
2049
- </td>
2050
- <td style="color: var(--text-secondary); font-family: monospace; font-size: 0.875rem;">
2051
- <div x-data="{ worktreeName: getWorktreeName(spec) }">
2052
- <span x-show="worktreeName !== 'none'" x-text="worktreeName" style="color: var(--accent-blue);"></span>
2053
- <span x-show="worktreeName === 'none'" style="color: var(--text-muted);">—</span>
2054
- <div x-show="worktreeName !== 'none'" style="font-size: 0.75rem; margin-top: 0.25rem;">
2055
- <span style="color: var(--accent-green);">✓ clean</span>
2056
- </div>
2057
- </div>
2058
- </td>
2059
- <td style="color: var(--text-secondary); font-size: 0.875rem;"
2060
- x-text="new Date(spec.updated_at).toLocaleDateString()"></td>
2061
- <td>
2062
- <div style="display: flex; gap: 0.5rem;">
2063
- <button class="btn btn-primary" style="padding: 0.5rem 1rem; font-size: 0.75rem; font-family: var(--font-body);"
2064
- @click="openSpec(spec.spec_id)">
2065
- View
2066
- </button>
2067
- <button class="btn btn-danger" style="padding: 0.5rem 0.75rem; font-size: 0.75rem; background: var(--accent-red); border: 1px solid var(--accent-red); font-family: var(--font-body);"
2068
- @click="confirmDeleteSpec(spec.spec_id, spec.title)"
2069
- :title="`Delete ${spec.title}`">
2070
- 🗑
2071
- </button>
2072
- </div>
2073
- </td>
2074
- </tr>
2075
- </template>
2076
- </tbody>
2077
- </table>
2078
-
2079
- <div x-show="filteredSpecs.length === 0 && !loading"
2080
- style="text-align: center; padding: 3rem; color: var(--text-muted);">
2081
- <div x-show="specs.length === 0">No Manuals found. Create your first Manual to get started!</div>
2082
- <div x-show="specs.length > 0">No Manuals match your current filters.</div>
2083
- </div>
2084
-
2085
- <!-- Activity Log Button -->
2086
- <div style="padding: 1.5rem; border-top: 1px solid var(--border-glass); text-center;">
2087
- <button class="btn btn-secondary" @click="loadActivityLog()">
2088
- 📊 View Activity Log
2089
- </button>
2090
- </div>
2091
- </div>
2092
-
2093
- <!-- Enhanced Modal -->
2094
- <div class="modal-overlay"
2095
- x-show="selectedSpec"
2096
- x-transition:enter="transition ease-out duration-300"
2097
- x-transition:enter-start="opacity-0"
2098
- x-transition:enter-end="opacity-100"
2099
- x-transition:leave="transition ease-in duration-200"
2100
- x-transition:leave-start="opacity-100"
2101
- x-transition:leave-end="opacity-0"
2102
- @click.self="selectedSpec=null">
2103
-
2104
- <div class="modal-content"
2105
- x-show="selectedSpec"
2106
- x-transition:enter="transition ease-out duration-300"
2107
- x-transition:enter-start="opacity-0 scale-95"
2108
- x-transition:enter-end="opacity-100 scale-100"
2109
- x-transition:leave="transition ease-in duration-200"
2110
- x-transition:leave-start="opacity-100 scale-100"
2111
- x-transition:leave-end="opacity-0 scale-95">
2112
-
2113
- <!-- Modal Header -->
2114
- <div class="modal-header" style="flex-direction: column; align-items: stretch; gap: 0.75rem; background: linear-gradient(135deg, rgba(245, 158, 11, 0.15) 0%, rgba(245, 158, 11, 0.08) 100%); border-bottom: 1px solid rgba(245, 158, 11, 0.2);">
2115
- <!-- Top Row: Title, Status, and Worktree Info -->
2116
- <div style="display: flex; justify-content: space-between; align-items: center;">
2117
- <div class="modal-title-section">
2118
- <h2 class="heading" style="margin: 0; font-size: 1.5rem;" x-text="selectedSpec?.title"></h2>
2119
- <div style="display: flex; align-items: center; gap: 1rem;">
2120
- <!-- Workflow Status Badge -->
2121
- <div style="background: rgba(255, 255, 255, 0.1); padding: 0.5rem 1rem; border-radius: 20px; font-size: 0.875rem; display: flex; align-items: center; gap: 0.5rem;">
2122
- <span>📋</span>
2123
- <span class="status-badge"
2124
- :class="`status-${selectedSpec?.state}`"
2125
- x-text="selectedSpec?.state || 'draft'"></span>
2126
- </div>
2127
- <!-- Worktree Info -->
2128
- <div class="worktree-info" x-show="getWorktreeName(selectedSpec)">
2129
- <span>🔧</span>
2130
- <span x-text="getWorktreeName(selectedSpec)"></span>
2131
- <span class="status-badge status-completed" style="font-size: 0.7rem; padding: 0.2rem 0.5rem;">✓ clean</span>
2132
- </div>
2133
- </div>
2134
- </div>
2135
- <div style="display: flex; gap: 1rem;">
2136
- <button class="btn btn-primary" @click="exportToMarkdown(selectedSpec?.spec_id)">
2137
- 📄 Export
2138
- </button>
2139
- <button class="btn btn-secondary" @click="selectedSpec=null"
2140
- style="background: rgba(245, 158, 11, 0.1); border: 1px solid rgba(245, 158, 11, 0.3); color: var(--accent-orange);"
2141
- @mouseenter="$el.style.background = 'rgba(245, 158, 11, 0.2)'"
2142
- @mouseleave="$el.style.background = 'rgba(245, 158, 11, 0.1)'">
2143
-
2144
- </button>
2145
- </div>
2146
- </div>
2147
-
2148
- </div>
2149
-
2150
- <!-- Main Content Container -->
2151
- <div class="modal-main-container">
2152
- <!-- Navigation Tabs - Only show sections with content -->
2153
- <div class="modal-nav">
2154
- <div class="nav-tabs">
2155
- <template x-for="sectionItem in getAvailableSections()" :key="sectionItem.id">
2156
- <button class="nav-tab"
2157
- :class="{'active': section === sectionItem.id, 'keyboard-focus': sectionKeyboardMode && section === sectionItem.id}"
2158
- @click="section = sectionItem.id; sectionKeyboardMode = false"
2159
- x-text="sectionItem.label"
2160
- tabindex="-1">
2161
- </button>
2162
- </template>
2163
- </div>
2164
- </div>
2165
-
2166
- <!-- Content Area -->
2167
- <div class="modal-main">
2168
- <template x-if="section==='summary'">
2169
- <div class="spec-content" x-html="renderMarkdown(selectedSpec?.executive_summary || selectedSpec?.sections?.executive_summary?.content || 'No executive summary yet')">
2170
- </div>
2171
- </template>
2172
-
2173
- <template x-if="section==='product'">
2174
- <div class="spec-content" x-html="renderMarkdown(selectedSpec?.sections?.product_specifications?.content || 'No product specifications yet')">
2175
- </div>
2176
- </template>
2177
-
2178
- <template x-if="section==='architecture'">
2179
- <div class="spec-content" x-html="renderMarkdown(selectedSpec?.sections?.architecture_analysis?.content || 'No architecture analysis yet')">
2180
- </div>
2181
- </template>
2182
-
2183
- <template x-if="section==='implementation'">
2184
- <div class="spec-content" x-html="renderMarkdown(selectedSpec?.sections?.implementation_plan?.content || 'No implementation plan yet')">
2185
- </div>
2186
- </template>
2187
-
2188
- <template x-if="section==='research'">
2189
- <div class="spec-content" x-html="renderMarkdown(selectedSpec?.sections?.research?.content || 'No research notes yet')">
2190
- </div>
2191
- </template>
2192
-
2193
- <template x-if="section==='testing'">
2194
- <div class="spec-content" x-html="renderMarkdown(selectedSpec?.sections?.testing?.content || 'No testing notes yet')">
2195
- </div>
2196
- </template>
2197
-
2198
- <template x-if="section==='review'">
2199
- <div class="spec-content" x-html="renderMarkdown(selectedSpec?.sections?.review?.content || 'No review notes yet')">
2200
- </div>
2201
- </template>
2202
-
2203
- <template x-if="section==='execution'">
2204
- <div class="spec-content">
2205
- <h3 style="color: var(--text-primary); margin-bottom: 1rem;">🛠️ Execution Logs</h3>
2206
- <template x-for="(log, index) in (selectedSpec?.execution_logs || selectedSpec?.execution_log || [])" :key="'exec_' + index">
2207
- <div style="background: rgba(59, 130, 246, 0.1); border-left: 3px solid var(--accent-blue); padding: 1rem; margin-bottom: 1rem; border-radius: 6px;">
2208
- <div style="display: flex; justify-content: space-between; align-items: start; margin-bottom: 0.5rem;">
2209
- <span style="font-size: 0.75rem; color: var(--text-muted);" x-text="new Date(log.timestamp).toLocaleString()"></span>
2210
- <span x-show="log.engineer" style="background: rgba(59, 130, 246, 0.2); color: var(--accent-blue); padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.75rem;" x-text="log.engineer"></span>
2211
- </div>
2212
- <div style="margin-bottom: 0.5rem;">
2213
- <strong style="color: var(--accent-blue);">Action:</strong>
2214
- <span x-text="log.action" style="color: var(--text-primary); margin-left: 0.5rem;"></span>
2215
- </div>
2216
- <div x-show="log.task_id" style="margin-bottom: 0.5rem;">
2217
- <strong style="color: var(--accent-blue);">Task ID:</strong>
2218
- <span x-text="log.task_id" style="color: var(--text-primary); margin-left: 0.5rem; font-family: monospace;"></span>
2219
- </div>
2220
- <div x-show="log.note" style="margin-bottom: 0.5rem;">
2221
- <strong style="color: var(--accent-blue);">Note:</strong>
2222
- <span x-text="log.note" style="color: var(--text-primary); margin-left: 0.5rem;"></span>
2223
- </div>
2224
- <div x-show="log.files_changed && log.files_changed.length > 0" style="margin-bottom: 0.5rem;">
2225
- <strong style="color: var(--accent-blue);">Files Changed:</strong>
2226
- <div style="margin-top: 0.25rem;">
2227
- <template x-for="file in log.files_changed" :key="file">
2228
- <span style="background: rgba(0, 0, 0, 0.2); padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.75rem; font-family: monospace; display: inline-block; margin: 0.25rem 0.25rem 0 0;" x-text="file"></span>
2229
- </template>
2230
- </div>
2231
- </div>
2232
- <div x-show="log.commit_hash" style="font-size: 0.75rem; color: var(--text-muted);">
2233
- <strong>Commit:</strong> <span x-text="log.commit_hash" style="font-family: monospace;"></span>
2234
- </div>
2235
- </div>
2236
- </template>
2237
- </div>
2238
- </template>
2239
-
2240
- <template x-if="section==='debug'">
2241
- <div class="spec-content">
2242
- <h3 style="color: var(--text-primary); margin-bottom: 1rem;">🐛 Debug Logs</h3>
2243
- <template x-for="(log, index) in (selectedSpec?.debug_logs || selectedSpec?.debug_log || [])" :key="'debug_' + index">
2244
- <div style="background: rgba(239, 68, 68, 0.1); border-left: 3px solid var(--accent-red); padding: 1rem; margin-bottom: 1rem; border-radius: 6px;">
2245
- <div style="display: flex; justify-content: space-between; align-items: start; margin-bottom: 0.5rem;">
2246
- <span style="font-size: 0.75rem; color: var(--text-muted);" x-text="new Date(log.timestamp).toLocaleString()"></span>
2247
- <span x-show="log.confidence" style="padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.75rem; text-transform: capitalize;"
2248
- :style="log.confidence === 'High' || log.confidence === 'high' ? 'background: rgba(16, 185, 129, 0.2); color: var(--accent-green);' : log.confidence === 'Medium' || log.confidence === 'medium' ? 'background: rgba(245, 158, 11, 0.2); color: var(--accent-orange);' : 'background: rgba(239, 68, 68, 0.2); color: var(--accent-red);'"
2249
- x-text="log.confidence"></span>
2250
- </div>
2251
- <div style="margin-bottom: 0.75rem;">
2252
- <strong style="color: var(--accent-red);">Issue:</strong>
2253
- <div x-text="log.issue" style="color: var(--text-primary); margin-top: 0.25rem;"></div>
2254
- </div>
2255
- <div style="margin-bottom: 0.75rem;">
2256
- <strong style="color: var(--accent-orange);">Root Cause:</strong>
2257
- <div x-text="log.root_cause" style="color: var(--text-primary); margin-top: 0.25rem;"></div>
2258
- </div>
2259
- <div>
2260
- <strong style="color: var(--accent-green);">Fix:</strong>
2261
- <div x-text="log.fix" style="color: var(--text-primary); margin-top: 0.25rem;"></div>
2262
- </div>
2263
- </div>
2264
- </template>
2265
- </div>
2266
- </template>
2267
-
2268
- </div>
2269
- </div>
2270
-
2271
- <!-- Right Sidebar: Activity & Files -->
2272
- <div class="modal-activity">
2273
- <!-- Activity Navigation Tabs -->
2274
- <div class="activity-nav">
2275
- <div class="activity-tabs">
2276
- <button class="activity-tab" :class="{'active': activityTab==='activity'}" @click="activityTab='activity'">📊 Activity</button>
2277
- <button class="activity-tab" :class="{'active': activityTab==='insights'}" @click="activityTab='insights'" x-show="selectedSpec?.implementation_summary || selectedSpec?.change_validation">🔍 Insights</button>
2278
- </div>
2279
- </div>
2280
- <!-- Activity Content -->
2281
- <!-- Activity Log Section - Timeline Visualization -->
2282
- <div x-show="activityTab==='activity'" class="activity-content" style="padding: 1.5rem; flex: 1; display: flex; flex-direction: column;"
2283
- >
2284
- <div class="timeline-container" style="overflow-y: auto; flex: 1; min-height: fit-content;">
2285
- <!-- Combined Timeline -->
2286
- <template x-for="(event, index) in getActivityTimeline(selectedSpec)" :key="'timeline_' + index">
2287
- <div class="timeline-item">
2288
- <!-- Left side: Time and Tool info -->
2289
- <div class="timeline-left">
2290
- <div class="timeline-time" x-text="new Date(event.timestamp).toLocaleString()"></div>
2291
- <div style="display: flex; align-items: center; justify-content: flex-end; gap: 0.5rem;">
2292
- <span class="status-badge"
2293
- :class="'role-' + (event.role || 'system').toLowerCase()"
2294
- x-text="(event.role || 'System').toUpperCase()"
2295
- </span>
2296
- <div style="font-size: 1.2rem;" x-text="event.role === 'DEBUGGER' ? '🐛' : event.role === 'ENGINEER' ? '⚙️' : event.role === 'ARCHITECT' ? '📐' : event.role === 'ORCHESTRATOR' ? '🎯' : event.role === 'REVIEWER' ? '👁️' : event.role === 'system' ? '⚙️' : '🔧'"></div>
2297
- </div>
2298
- </div>
2299
-
2300
- <!-- Timeline marker -->
2301
- <div class="timeline-marker"
2302
- :class="(event.role || 'system').toLowerCase()"></div>
2303
-
2304
- <!-- Right side: Content -->
2305
- <div class="timeline-right">
2306
- <div class="timeline-content">
2307
- <div class="timeline-role">
2308
- <span x-text="event.human_description || event.action || event.note || event.message || event.issue || 'Activity recorded'"></span>
2309
- </div>
2310
-
2311
- <!-- Enhanced Engineer Data -->
2312
- <div x-show="event.is_enhanced && event.engineer_data" style="margin: 0.75rem 0; font-size: 0.85rem; line-height: 1.5;">
2313
- <!-- Layer Progress -->
2314
- <div x-show="event.engineer_data.layer" style="background: rgba(59, 130, 246, 0.1); padding: 0.75rem; border-radius: 6px; margin-bottom: 0.5rem; border-left: 3px solid var(--accent-blue);">
2315
- <strong style="color: var(--accent-blue);">Layer:</strong>
2316
- <span x-text="event.engineer_data.layer.charAt(0).toUpperCase() + event.engineer_data.layer.slice(1)" style="color: var(--text-primary); text-transform: capitalize;"></span>
2317
- <span x-show="event.engineer_data.completion_percentage !== undefined" style="color: var(--text-muted); margin-left: 0.5rem;" x-text="'(' + event.engineer_data.completion_percentage + '% complete)'"></span>
2318
- </div>
2319
-
2320
- <!-- Build/Test Status -->
2321
- <div x-show="event.engineer_data.build_status || event.engineer_data.test_status" style="display: flex; gap: 0.5rem; margin-bottom: 0.5rem;">
2322
- <span x-show="event.engineer_data.build_status === 'success'" style="background: rgba(16, 185, 129, 0.2); color: var(--accent-green); padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.75rem;">✅ Build</span>
2323
- <span x-show="event.engineer_data.build_status === 'failed'" style="background: rgba(239, 68, 68, 0.2); color: var(--accent-red); padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.75rem;">❌ Build</span>
2324
- <span x-show="event.engineer_data.test_status === 'passed'" style="background: rgba(16, 185, 129, 0.2); color: var(--accent-green); padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.75rem;">🧪 Tests</span>
2325
- <span x-show="event.engineer_data.test_status === 'failed'" style="background: rgba(239, 68, 68, 0.2); color: var(--accent-red); padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.75rem;">🧪 Tests</span>
2326
- </div>
2327
- </div>
2328
-
2329
- <!-- Enhanced Debugger Data -->
2330
- <div x-show="event.is_enhanced && event.debugger_data" style="margin: 0.75rem 0; font-size: 0.85rem; line-height: 1.5;">
2331
- <div x-show="event.debugger_data.issue" style="background: rgba(239, 68, 68, 0.1); padding: 0.75rem; border-radius: 6px; margin-bottom: 0.5rem; border-left: 3px solid var(--accent-red);">
2332
- <strong style="color: var(--accent-red);">Issue:</strong> <span x-text="event.debugger_data.issue" style="color: var(--text-primary);"></span>
2333
- </div>
2334
- <div x-show="event.debugger_data.root_cause" style="background: rgba(245, 158, 11, 0.1); padding: 0.75rem; border-radius: 6px; margin-bottom: 0.5rem; border-left: 3px solid var(--accent-orange);">
2335
- <strong style="color: var(--accent-orange);">Root Cause:</strong> <span x-text="event.debugger_data.root_cause" style="color: var(--text-primary);"></span>
2336
- </div>
2337
- <div x-show="event.debugger_data.fix" style="background: rgba(16, 185, 129, 0.1); padding: 0.75rem; border-radius: 6px; margin-bottom: 0.5rem; border-left: 3px solid var(--accent-green);">
2338
- <strong style="color: var(--accent-green);">Fix:</strong> <span x-text="event.debugger_data.fix" style="color: var(--text-primary);"></span>
2339
- </div>
2340
- <div x-show="event.debugger_data.confidence" style="display: flex; align-items: center; gap: 0.5rem;">
2341
- <span x-text="event.debugger_data.confidence === 'high' ? '🎯' : event.debugger_data.confidence === 'medium' ? '⚖️' : '❓'"></span>
2342
- <span style="font-size: 0.75rem; color: var(--text-muted);">Confidence: </span>
2343
- <span x-text="event.debugger_data.confidence" style="font-size: 0.75rem; color: var(--text-primary); text-transform: capitalize;"></span>
2344
- </div>
2345
- </div>
2346
-
2347
- <!-- Enhanced Architect Data -->
2348
- <div x-show="event.is_enhanced && event.architect_data" style="margin: 0.75rem 0; font-size: 0.85rem; line-height: 1.5;">
2349
- <div x-show="event.architect_data.phase" style="background: rgba(139, 92, 246, 0.1); padding: 0.75rem; border-radius: 6px; margin-bottom: 0.5rem; border-left: 3px solid #8b5cf6;">
2350
- <strong style="color: #8b5cf6;">Phase:</strong>
2351
- <span x-text="event.architect_data.phase.charAt(0).toUpperCase() + event.architect_data.phase.slice(1)" style="color: var(--text-primary); text-transform: capitalize;"></span>
2352
- </div>
2353
-
2354
- <!-- Clarifications with Status Badges -->
2355
- <div x-show="event.architect_data.clarifications && event.architect_data.clarifications.length > 0" style="background: rgba(245, 158, 11, 0.1); padding: 0.75rem; border-radius: 6px; margin-bottom: 0.5rem; border-left: 3px solid var(--accent-orange);">
2356
- <strong style="color: var(--accent-orange);">Clarifications:</strong>
2357
- <div style="margin-top: 0.5rem;">
2358
- <template x-for="clarification in event.architect_data.clarifications">
2359
- <div style="margin-bottom: 0.5rem; padding: 0.5rem; background: rgba(0, 0, 0, 0.2); border-radius: 4px;">
2360
- <div style="display: flex; align-items: start; gap: 0.5rem;">
2361
- <span x-show="clarification.status === 'pending'" style="background: rgba(245, 158, 11, 0.2); color: var(--accent-orange); padding: 0.125rem 0.375rem; border-radius: 3px; font-size: 0.65rem; flex-shrink: 0;">⏳ Awaiting</span>
2362
- <span x-show="clarification.status === 'answered'" style="background: rgba(16, 185, 129, 0.2); color: var(--accent-green); padding: 0.125rem 0.375rem; border-radius: 3px; font-size: 0.65rem; flex-shrink: 0;">✅ Answered</span>
2363
- <div style="flex: 1;">
2364
- <div x-text="clarification.question" style="color: var(--text-primary); font-size: 0.8rem; margin-bottom: 0.25rem;"></div>
2365
- <div x-show="clarification.answer" x-text="'Answer: ' + clarification.answer" style="color: var(--text-muted); font-size: 0.75rem; font-style: italic;"></div>
2366
- </div>
2367
- </div>
2368
- </div>
2369
- </template>
2370
- </div>
2371
- </div>
2372
-
2373
- <div x-show="event.architect_data.design_decisions && event.architect_data.design_decisions.length > 0" style="background: rgba(59, 130, 246, 0.1); padding: 0.75rem; border-radius: 6px; border-left: 3px solid var(--accent-blue);">
2374
- <strong style="color: var(--accent-blue);">Design Decisions:</strong>
2375
- <ul style="margin: 0.5rem 0 0 1rem; color: var(--text-primary);">
2376
- <template x-for="decision in event.architect_data.design_decisions">
2377
- <li x-text="decision" style="margin-bottom: 0.25rem;"></li>
2378
- </template>
2379
- </ul>
2380
- </div>
2381
- </div>
2382
-
2383
- <!-- Enhanced Orchestrator Data -->
2384
- <div x-show="event.is_enhanced && event.orchestrator_data" style="margin: 0.75rem 0; font-size: 0.85rem; line-height: 1.5;">
2385
- <div x-show="event.orchestrator_data.workflow_action" style="background: rgba(99, 102, 241, 0.1); padding: 0.75rem; border-radius: 6px; margin-bottom: 0.5rem; border-left: 3px solid #6366f1;">
2386
- <strong style="color: #6366f1;">Workflow:</strong>
2387
- <span x-text="event.orchestrator_data.workflow_action" style="color: var(--text-primary);"></span>
2388
- </div>
2389
-
2390
- <div x-show="event.orchestrator_data.coordinated_roles && event.orchestrator_data.coordinated_roles.length > 0" style="background: rgba(139, 92, 246, 0.1); padding: 0.75rem; border-radius: 6px; margin-bottom: 0.5rem; border-left: 3px solid #8b5cf6;">
2391
- <strong style="color: #8b5cf6;">Coordinated Roles:</strong>
2392
- <span x-text="event.orchestrator_data.coordinated_roles.join(', ')" style="color: var(--text-primary); margin-left: 0.5rem;"></span>
2393
- </div>
2394
-
2395
- <div x-show="event.orchestrator_data.milestones && event.orchestrator_data.milestones.length > 0" style="background: rgba(16, 185, 129, 0.1); padding: 0.75rem; border-radius: 6px; margin-bottom: 0.5rem; border-left: 3px solid var(--accent-green);">
2396
- <strong style="color: var(--accent-green);">Milestones:</strong>
2397
- <ul style="margin: 0.5rem 0 0 1rem; color: var(--text-primary);">
2398
- <template x-for="milestone in event.orchestrator_data.milestones">
2399
- <li x-text="milestone" style="margin-bottom: 0.25rem;"></li>
2400
- </template>
2401
- </ul>
2402
- </div>
2403
-
2404
- <div x-show="event.orchestrator_data.blockers && event.orchestrator_data.blockers.length > 0" style="background: rgba(239, 68, 68, 0.1); padding: 0.75rem; border-radius: 6px; border-left: 3px solid var(--accent-red);">
2405
- <strong style="color: var(--accent-red);">Blockers:</strong>
2406
- <ul style="margin: 0.5rem 0 0 1rem; color: var(--text-primary);">
2407
- <template x-for="blocker in event.orchestrator_data.blockers">
2408
- <li x-text="blocker" style="margin-bottom: 0.25rem;"></li>
2409
- </template>
2410
- </ul>
2411
- </div>
2412
- </div>
2413
-
2414
- <!-- Legacy Root cause & fix (debug logs) -->
2415
- <div x-show="!event.is_enhanced && event.root_cause" style="margin: 0.75rem 0; font-size: 0.85rem; line-height: 1.5;">
2416
- <div style="color: var(--text-secondary); background: rgba(239, 68, 68, 0.1); padding: 0.75rem; border-radius: 6px; margin-bottom: 0.5rem; border-left: 3px solid var(--accent-red);"><strong style="color: var(--accent-red);">Root cause:</strong> <span x-text="event.root_cause" style="color: var(--text-primary);"></span></div>
2417
- <div x-show="event.fix" style="color: var(--text-secondary); background: rgba(16, 185, 129, 0.1); padding: 0.75rem; border-radius: 6px; border-left: 3px solid var(--accent-green);"><strong style="color: var(--accent-green);">Fix:</strong> <span x-text="event.fix" style="color: var(--text-primary);"></span></div>
2418
- </div>
2419
-
2420
- <!-- Enhanced Verbose Activity Details -->
2421
- <div x-show="event.data?.verbose_details" style="margin: 0.75rem 0;">
2422
-
2423
- <!-- Engineer Mode Details -->
2424
- <div x-show="event.data?.mode === 'engineer' && event.data?.verbose_details?.engineer" style="font-size: 0.85rem; line-height: 1.5;">
2425
- <div x-show="event.data.verbose_details.engineer.completion_percentage !== undefined" style="background: rgba(59, 130, 246, 0.1); padding: 0.75rem; border-radius: 6px; margin-bottom: 0.5rem; border-left: 3px solid var(--accent-blue);">
2426
- <strong style="color: var(--accent-blue);">Progress:</strong>
2427
- <span x-text="event.data.verbose_details.engineer.completion_percentage + '% complete'" style="color: var(--text-primary);"></span>
2428
- </div>
2429
-
2430
- <div x-show="event.data.verbose_details.engineer.tasks_completed?.length" style="background: rgba(16, 185, 129, 0.1); padding: 0.75rem; border-radius: 6px; margin-bottom: 0.5rem; border-left: 3px solid var(--accent-green);">
2431
- <strong style="color: var(--accent-green);">Tasks Completed:</strong>
2432
- <ul style="margin: 0.5rem 0 0 1rem; color: var(--text-primary);">
2433
- <template x-for="task in event.data.verbose_details.engineer.tasks_completed">
2434
- <li x-text="task" style="margin-bottom: 0.25rem;"></li>
2435
- </template>
2436
- </ul>
2437
- </div>
2438
-
2439
- <div x-show="event.data.verbose_details.engineer.tasks_remaining?.length" style="background: rgba(245, 158, 11, 0.1); padding: 0.75rem; border-radius: 6px; margin-bottom: 0.5rem; border-left: 3px solid var(--accent-orange);">
2440
- <strong style="color: var(--accent-orange);">Remaining Tasks:</strong>
2441
- <ul style="margin: 0.5rem 0 0 1rem; color: var(--text-primary);">
2442
- <template x-for="task in event.data.verbose_details.engineer.tasks_remaining">
2443
- <li x-text="task" style="margin-bottom: 0.25rem;"></li>
2444
- </template>
2445
- </ul>
2446
- </div>
2447
-
2448
- <div x-show="event.data.verbose_details.engineer.build_status || event.data.verbose_details.engineer.test_status" style="display: flex; gap: 0.5rem; margin-bottom: 0.5rem;">
2449
- <span x-show="event.data.verbose_details.engineer.build_status === 'success'" style="background: rgba(16, 185, 129, 0.2); color: var(--accent-green); padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.75rem;">✅ Build Passed</span>
2450
- <span x-show="event.data.verbose_details.engineer.build_status === 'failed'" style="background: rgba(239, 68, 68, 0.2); color: var(--accent-red); padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.75rem;">❌ Build Failed</span>
2451
- <span x-show="event.data.verbose_details.engineer.test_status === 'passed'" style="background: rgba(16, 185, 129, 0.2); color: var(--accent-green); padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.75rem;">🧪 Tests Passed</span>
2452
- <span x-show="event.data.verbose_details.engineer.test_status === 'failed'" style="background: rgba(239, 68, 68, 0.2); color: var(--accent-red); padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.75rem;">🧪 Tests Failed</span>
2453
- </div>
2454
- </div>
2455
-
2456
- <!-- Debugger Mode Details -->
2457
- <div x-show="event.data?.mode === 'debugger' && event.data?.verbose_details?.debugger" style="font-size: 0.85rem; line-height: 1.5;">
2458
- <div x-show="event.data.verbose_details.debugger.issue_description" style="background: rgba(239, 68, 68, 0.1); padding: 0.75rem; border-radius: 6px; margin-bottom: 0.5rem; border-left: 3px solid var(--accent-red);">
2459
- <strong style="color: var(--accent-red);">Issue:</strong>
2460
- <span x-text="event.data.verbose_details.debugger.issue_description" style="color: var(--text-primary);"></span>
2461
- </div>
2462
-
2463
- <div x-show="event.data.verbose_details.debugger.reproduction_steps?.length" style="background: rgba(156, 163, 175, 0.1); padding: 0.75rem; border-radius: 6px; margin-bottom: 0.5rem; border-left: 3px solid var(--text-muted);">
2464
- <strong style="color: var(--text-muted);">Reproduction Steps:</strong>
2465
- <ol style="margin: 0.5rem 0 0 1rem; color: var(--text-primary);">
2466
- <template x-for="step in event.data.verbose_details.debugger.reproduction_steps">
2467
- <li x-text="step" style="margin-bottom: 0.25rem;"></li>
2468
- </template>
2469
- </ol>
2470
- </div>
2471
-
2472
- <div x-show="event.data.verbose_details.debugger.confidence_level" style="display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.5rem;">
2473
- <span x-text="event.data.verbose_details.debugger.confidence_level === 'high' ? '🎯' : event.data.verbose_details.debugger.confidence_level === 'medium' ? '⚖️' : '❓'"></span>
2474
- <span style="font-size: 0.75rem; color: var(--text-muted);">Confidence: </span>
2475
- <span x-text="event.data.verbose_details.debugger.confidence_level" style="font-size: 0.75rem; color: var(--text-primary); text-transform: capitalize;"></span>
2476
- </div>
2477
- </div>
2478
-
2479
- <!-- Architect Mode Details -->
2480
- <div x-show="event.data?.mode === 'architect' && event.data?.verbose_details?.architect" style="font-size: 0.85rem; line-height: 1.5;">
2481
- <div x-show="event.data.verbose_details.architect.research_findings?.length" style="background: rgba(139, 92, 246, 0.1); padding: 0.75rem; border-radius: 6px; margin-bottom: 0.5rem; border-left: 3px solid #8b5cf6;">
2482
- <strong style="color: #8b5cf6;">Research Findings:</strong>
2483
- <ul style="margin: 0.5rem 0 0 1rem; color: var(--text-primary);">
2484
- <template x-for="finding in event.data.verbose_details.architect.research_findings">
2485
- <li x-text="finding" style="margin-bottom: 0.25rem;"></li>
2486
- </template>
2487
- </ul>
2488
- </div>
2489
-
2490
- <div x-show="event.data.verbose_details.architect.design_decisions?.length" style="background: rgba(245, 158, 11, 0.1); padding: 0.75rem; border-radius: 6px; margin-bottom: 0.5rem; border-left: 3px solid var(--accent-orange);">
2491
- <strong style="color: var(--accent-orange);">Design Decisions:</strong>
2492
- <ul style="margin: 0.5rem 0 0 1rem; color: var(--text-primary);">
2493
- <template x-for="decision in event.data.verbose_details.architect.design_decisions">
2494
- <li x-text="decision" style="margin-bottom: 0.25rem;"></li>
2495
- </template>
2496
- </ul>
2497
- </div>
2498
-
2499
- <div x-show="event.data.verbose_details.architect.architecture_patterns?.length" style="background: rgba(59, 130, 246, 0.1); padding: 0.75rem; border-radius: 6px; margin-bottom: 0.5rem; border-left: 3px solid var(--accent-blue);">
2500
- <strong style="color: var(--accent-blue);">Architecture Patterns:</strong>
2501
- <div style="margin: 0.5rem 0 0 0; color: var(--text-primary);">
2502
- <span x-text="event.data.verbose_details.architect.architecture_patterns.join(', ')"></span>
2503
- </div>
2504
- </div>
2505
- </div>
2506
-
2507
- </div>
2508
-
2509
- <!-- Task/Commit Info -->
2510
- <div x-show="event.task_id || event.commit_hash" style="display: flex; align-items: center; gap: 1rem; font-size: 0.75rem; color: var(--text-muted); margin-top: 0.5rem;">
2511
- <span x-show="event.task_id" x-text="'Task: ' + event.task_id"></span>
2512
- <span x-show="event.commit_hash" x-text="'Commit: ' + event.commit_hash.substring(0, 8)"></span>
2513
- </div>
2514
-
2515
- <!-- Files Modified - Compact Git Style (Enhanced Activities) -->
2516
- <div x-show="event.is_enhanced && event.files_changed && event.files_changed.length > 0" style="margin-top: 0.75rem;">
2517
- <div style="color: var(--text-muted); margin-bottom: 0.5rem; font-size: 0.75rem; font-weight: 600;">
2518
- 📁 <span x-text="event.files_changed.length + ' file' + (event.files_changed.length > 1 ? 's' : '') + ' changed'"></span>
2519
- </div>
2520
-
2521
- <!-- Compact File List - Git Style -->
2522
- <div style="font-family: 'Monaco', 'Menlo', monospace; font-size: 0.7rem; color: var(--text-muted); line-height: 1.6;">
2523
- <template x-for="(file, fileIdx) in event.files_changed" :key="event.id + '_file_' + fileIdx">
2524
- <div style="display: flex; align-items: center; gap: 0.5rem;">
2525
- <!-- Change Type (M/A/D) -->
2526
- <span style="font-weight: 700; min-width: 12px;"
2527
- :style="file.change_type === 'added' ? 'color: #22c55e;' :
2528
- file.change_type === 'deleted' ? 'color: #ef4444;' :
2529
- 'color: #f59e0b;'"
2530
- x-text="file.change_type === 'added' ? 'A' : file.change_type === 'deleted' ? 'D' : 'M'">
2531
- </span>
2532
-
2533
- <!-- File Path -->
2534
- <span style="color: var(--text-secondary);" x-text="file.path"></span>
2535
-
2536
- <!-- Git Diff Summary (+x / -y) -->
2537
- <span x-show="file.lines_added > 0 || file.lines_removed > 0" style="color: var(--text-muted);">
2538
- <span x-show="file.lines_added > 0" style="color: #22c55e;" x-text="'+' + file.lines_added"></span><span x-show="file.lines_added > 0 && file.lines_removed > 0"> / </span><span x-show="file.lines_removed > 0" style="color: #ef4444;" x-text="'-' + file.lines_removed"></span>
2539
- </span>
2540
- </div>
2541
- </template>
2542
- </div>
2543
- </div>
2544
-
2545
- <!-- Files Modified - Legacy Format (Fallback) -->
2546
- <div x-show="!event.is_enhanced && event.files_changed && event.files_changed.length > 0" style="margin-top: 0.5rem;">
2547
- <div style="color: var(--text-muted); margin-bottom: 0.25rem; font-size: 0.8rem;">
2548
- <span x-text="event.files_changed.length + ' file(s) changed'"></span>
2549
- </div>
2550
- </div>
2551
-
2552
- <!-- Branch Info -->
2553
- <div x-show="event.branch" style="margin-top: 0.5rem; font-size: 0.7rem; color: var(--text-muted); font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;">
2554
- <span x-text="'Branch: ' + event.branch"></span>
2555
- </div>
2556
- </div>
2557
- </div>
2558
- </div>
2559
- </template>
2560
-
2561
- <!-- Timeline line positioned after all items are rendered -->
2562
- <div class="timeline-line"></div>
2563
-
2564
- <div x-show="getActivityTimeline(selectedSpec).length === 0"
2565
- style="text-align: center; padding: 3rem; color: var(--text-muted); font-size: 0.875rem;">
2566
- <div style="font-size: 2rem; margin-bottom: 1rem;">📋</div>
2567
- <div>No activity recorded yet</div>
2568
- </div>
2569
- </div>
2570
- </div>
2571
-
2572
- <!-- Files Modified Section -->
2573
- <div x-show="activityTab==='files'" class="activity-content" style="padding: 1.5rem; flex: 1;">
2574
- <div style="max-height: 500px; overflow-y: auto; padding: 0.5rem 0;">
2575
- <!-- GitHub Desktop Style File List -->
2576
- <div style="background: rgba(255, 255, 255, 0.02); border-radius: 8px; border: 1px solid rgba(255, 255, 255, 0.05); overflow: visible; max-height: none;">
2577
-
2578
- <!-- Files from execution logs -->
2579
- <template x-for="(log, logIndex) in (selectedSpec?.execution_logs || selectedSpec?.execution_log || [])" :key="log.timestamp + 'exec'">
2580
- <template x-for="(file, fileIndex) in (log.files_changed || [])" :key="log.timestamp + file + fileIndex">
2581
- <div style="display: flex; align-items: center; padding: 0.75rem 1rem; border-bottom: 1px solid rgba(255, 255, 255, 0.05); transition: background 0.2s ease;"
2582
- @mouseenter="$el.style.background = 'rgba(255, 255, 255, 0.05)'"
2583
- @mouseleave="$el.style.background = 'transparent'">
2584
-
2585
- <!-- Status Indicator (GitHub Desktop style) -->
2586
- <div style="width: 18px; height: 18px; margin-right: 0.75rem; display: flex; align-items: center; justify-content: center; border-radius: 3px; background: rgba(34, 197, 94, 0.15); color: #22c55e; font-size: 0.7rem; font-weight: 600; font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;">
2587
- M
2588
- </div>
2589
-
2590
- <!-- File Icon and Name -->
2591
- <div style="flex: 1; min-width: 0; display: flex; align-items: center; gap: 0.5rem;">
2592
- <div style="font-size: 1rem;" x-text="getFileIcon(file)"></div>
2593
- <div style="min-width: 0; flex: 1;">
2594
- <div style="font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; font-size: 0.875rem; color: var(--text-primary); font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;" x-text="getFileName(file)"></div>
2595
- <div style="font-size: 0.75rem; color: var(--text-muted); margin-top: 0.125rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;" x-text="simplifyFilePath(file)"></div>
2596
- </div>
2597
- </div>
2598
-
2599
- <!-- Git Diff Statistics (GitHub Desktop style) -->
2600
- <div style="margin-left: auto; display: flex; align-items: center; gap: 0.5rem; font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; font-size: 0.75rem;"
2601
- x-data="{ gitDiffData: null, loading: true }"
2602
- x-init="
2603
- (async () => {
2604
- try {
2605
- const response = await fetch(`/api/git-diff?file=${encodeURIComponent(file)}`);
2606
- if (response.ok) {
2607
- gitDiffData = await response.json();
2608
- }
2609
- } catch (e) {
2610
- console.log('Could not fetch git diff for', file);
2611
- } finally {
2612
- loading = false;
2613
- }
2614
- })()
2615
- ">
2616
- <div x-show="loading" style="color: var(--text-muted); width: 60px; text-align: center; font-size: 0.65rem;">...</div>
2617
- <div x-show="!loading && gitDiffData && (gitDiffData.lines_added > 0 || gitDiffData.lines_removed > 0)" style="display: flex; align-items: center; gap: 0.375rem;">
2618
- <span x-show="gitDiffData.lines_added > 0" style="color: #22c55e; background: rgba(34, 197, 94, 0.1); padding: 0.125rem 0.375rem; border-radius: 12px; font-size: 0.7rem;" x-text="`+${gitDiffData.lines_added}`"></span>
2619
- <span x-show="gitDiffData.lines_removed > 0" style="color: #ef4444; background: rgba(239, 68, 68, 0.1); padding: 0.125rem 0.375rem; border-radius: 12px; font-size: 0.7rem;" x-text="`−${gitDiffData.lines_removed}`"></span>
2620
- </div>
2621
- <div x-show="!loading && (!gitDiffData || (gitDiffData.lines_added === 0 && gitDiffData.lines_removed === 0))" style="color: var(--text-muted); width: 60px; text-align: center; font-size: 0.65rem;">—</div>
2622
- </div>
2623
-
2624
- <!-- Timestamp -->
2625
- <div style="margin-left: 1rem; font-size: 0.7rem; color: var(--text-muted); min-width: 60px; text-align: right;" x-text="new Date(log.timestamp).toLocaleTimeString('en-US', {hour: '2-digit', minute: '2-digit'})"></div>
2626
- </div>
2627
- </template>
2628
- </template>
2629
-
2630
- <!-- Empty state -->
2631
- <div x-show="(!selectedSpec?.execution_logs || selectedSpec.execution_logs.length === 0) && (!selectedSpec?.execution_log || selectedSpec.execution_log.length === 0)" style="text-align: center; padding: 2rem; color: var(--text-muted); font-size: 0.875rem; display: flex; flex-direction: column; align-items: center; gap: 0.5rem;">
2632
- <div style="font-size: 1.5rem;">📁</div>
2633
- <div>No files modified yet</div>
2634
- <div style="font-size: 0.75rem; opacity: 0.7;">File changes will appear here during implementation</div>
2635
- </div>
2636
- </div>
2637
-
2638
- </div>
2639
- </div>
2640
-
2641
- <!-- Insights Section -->
2642
- <div x-show="activityTab==='insights'" class="activity-content" style="padding: 1.5rem; flex: 1;">
2643
- <div style="max-height: 500px; overflow-y: auto; padding: 0.5rem 0;">
2644
-
2645
- <!-- Compliance Overview -->
2646
- <div x-show="selectedSpec?.change_validation" style="background: rgba(34, 197, 94, 0.05); border-radius: 8px; padding: 1rem; margin-bottom: 1rem; border: 1px solid rgba(34, 197, 94, 0.2);">
2647
- <h4 style="margin: 0 0 0.75rem 0; color: #22c55e; font-size: 0.9rem; display: flex; align-items: center; gap: 0.5rem;">
2648
- <span>🔍</span>
2649
- <span>Compliance Status</span>
2650
- </h4>
2651
- <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 0.5rem; margin-bottom: 1rem;">
2652
- <div style="text-align: center; padding: 0.5rem; background: rgba(255, 255, 255, 0.05); border-radius: 6px;">
2653
- <div style="font-size: 1.5rem; margin-bottom: 0.25rem;" x-text="selectedSpec?.change_validation?.overall_compliance === 'compliant' ? '✅' : selectedSpec?.change_validation?.overall_compliance === 'partial' ? '⚠️' : '❌'"></div>
2654
- <div style="font-size: 0.8rem; color: var(--text-secondary);" x-text="selectedSpec?.change_validation?.overall_compliance || 'Unknown'"></div>
2655
- </div>
2656
- </div>
2657
- <div x-show="selectedSpec?.change_validation?.recommendations?.length > 0">
2658
- <div style="font-size: 0.85rem; color: var(--text-secondary); margin-bottom: 0.5rem;">Recommendations:</div>
2659
- <template x-for="rec in selectedSpec?.change_validation?.recommendations" :key="rec">
2660
- <div style="font-size: 0.8rem; color: var(--text-primary); margin-bottom: 0.25rem; padding-left: 1rem;" x-text="rec"></div>
2661
- </template>
2662
- </div>
2663
- </div>
2664
-
2665
- <!-- Implementation Summary -->
2666
- <div x-show="selectedSpec?.implementation_summary" style="background: rgba(99, 102, 241, 0.05); border-radius: 8px; padding: 1rem; margin-bottom: 1rem; border: 1px solid rgba(99, 102, 241, 0.2);">
2667
- <h4 style="margin: 0 0 0.75rem 0; color: #818cf8; font-size: 0.9rem; display: flex; align-items: center; gap: 0.5rem;">
2668
- <span>📊</span>
2669
- <span>Implementation Progress</span>
2670
- </h4>
2671
-
2672
- <!-- Progress Stats -->
2673
- <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(80px, 1fr)); gap: 0.5rem; margin-bottom: 1rem;">
2674
- <div style="text-align: center; padding: 0.5rem; background: rgba(255, 255, 255, 0.05); border-radius: 6px;">
2675
- <div style="font-size: 1.2rem; font-weight: 600; color: var(--text-primary);" x-text="selectedSpec?.implementation_summary?.files_changed_count || 0"></div>
2676
- <div style="font-size: 0.7rem; color: var(--text-secondary);">Files</div>
2677
- </div>
2678
- <div style="text-align: center; padding: 0.5rem; background: rgba(255, 255, 255, 0.05); border-radius: 6px;">
2679
- <div style="font-size: 1.2rem; font-weight: 600; color: var(--text-primary);" x-text="selectedSpec?.implementation_summary?.git_commits_count || 0"></div>
2680
- <div style="font-size: 0.7rem; color: var(--text-secondary);">Commits</div>
2681
- </div>
2682
- <div style="text-align: center; padding: 0.5rem; background: rgba(255, 255, 255, 0.05); border-radius: 6px;">
2683
- <div style="font-size: 1.2rem; font-weight: 600;" :style="`color: ${selectedSpec?.implementation_summary?.compliance_overview?.overall_status === 'compliant' ? '#22c55e' : selectedSpec?.implementation_summary?.compliance_overview?.overall_status === 'partial' ? '#f59e0b' : '#ef4444'}`" x-text="Math.round((selectedSpec?.implementation_summary?.compliance_overview?.compliant_files || 0) / Math.max(selectedSpec?.implementation_summary?.compliance_overview?.total_files || 1, 1) * 100) + '%'"></div>
2684
- <div style="font-size: 0.7rem; color: var(--text-secondary);">Compliant</div>
2685
- </div>
2686
- </div>
2687
-
2688
- <!-- Implementation Notes -->
2689
- <div x-show="selectedSpec?.implementation_summary?.implementation_notes?.length > 0">
2690
- <div style="font-size: 0.85rem; color: var(--text-secondary); margin-bottom: 0.5rem;">Recent Progress:</div>
2691
- <template x-for="note in selectedSpec?.implementation_summary?.implementation_notes.slice(0, 3)" :key="note">
2692
- <div style="font-size: 0.8rem; color: var(--text-primary); margin-bottom: 0.5rem; padding: 0.5rem; background: rgba(255, 255, 255, 0.02); border-radius: 4px;" x-text="note"></div>
2693
- </template>
2694
- </div>
2695
-
2696
- <!-- Actionable Insights -->
2697
- <div x-show="selectedSpec?.implementation_summary?.actionable_insights?.length > 0">
2698
- <div style="font-size: 0.85rem; color: var(--text-secondary); margin-bottom: 0.5rem;">Actionable Insights:</div>
2699
- <template x-for="insight in selectedSpec?.implementation_summary?.actionable_insights" :key="insight">
2700
- <div style="font-size: 0.8rem; color: var(--accent-blue); margin-bottom: 0.25rem; padding-left: 1rem;" x-text="insight"></div>
2701
- </template>
2702
- </div>
2703
- </div>
2704
-
2705
- <!-- Empty State -->
2706
- <div x-show="!selectedSpec?.implementation_summary && !selectedSpec?.change_validation" style="text-align: center; padding: 3rem; color: var(--text-muted); font-size: 0.875rem;">
2707
- <div style="font-size: 2rem; margin-bottom: 1rem;">🔍</div>
2708
- <div>No insights available yet</div>
2709
- <div style="font-size: 0.8rem; margin-top: 0.5rem;">Implementation summaries and validation results will appear here</div>
2710
- </div>
2711
- </div>
2712
- </div>
2713
-
2714
- </div>
2715
- </div>
2716
- </div>
2717
- </div>
2718
-
2719
- <!-- Activity Log Modal -->
2720
- <div class="modal-overlay"
2721
- x-show="showActivityLog"
2722
- x-transition:enter="transition ease-out duration-300"
2723
- x-transition:enter-start="opacity-0"
2724
- x-transition:enter-end="opacity-100"
2725
- x-transition:leave="transition ease-in duration-200"
2726
- x-transition:leave-start="opacity-100"
2727
- x-transition:leave-end="opacity-0"
2728
- @click.self="showActivityLog=false">
2729
-
2730
- <div class="modal-content"
2731
- x-show="showActivityLog"
2732
- x-transition:enter="transition ease-out duration-300"
2733
- x-transition:enter-start="opacity-0 scale-95"
2734
- x-transition:enter-end="opacity-100 scale-100"
2735
- x-transition:leave="transition ease-in duration-200"
2736
- x-transition:leave-start="opacity-100 scale-100"
2737
- x-transition:leave-end="opacity-0 scale-95"
2738
- style="grid-template-columns: 1fr; grid-template-rows: auto 1fr;">
2739
-
2740
- <!-- Activity Log Header -->
2741
- <div class="modal-header">
2742
- <h2 class="heading" style="margin: 0; font-size: 1.5rem;">📊 Activity Log</h2>
2743
- <div style="display: flex; gap: 1rem;">
2744
- <button class="btn btn-secondary" @click="showActivityLog=false">
2745
- ✕ Close
2746
- </button>
2747
- </div>
2748
- </div>
2749
-
2750
- <!-- Activity Log Content -->
2751
- <div class="modal-main">
2752
- <div style="margin-bottom: 1.5rem;">
2753
- <h3 class="heading" style="margin: 0 0 1rem 0;">Recent Activity</h3>
2754
- <div style="background: rgba(255, 255, 255, 0.03); padding: 1.5rem; border-radius: 12px; border: 1px solid rgba(255, 255, 255, 0.05);">
2755
-
2756
- <template x-for="activity in activities" :key="activity.timestamp">
2757
- <div style="border-bottom: 1px solid rgba(255, 255, 255, 0.05); padding: 1rem 0;">
2758
- <div style="display: flex; justify-content: between; align-items: start; margin-bottom: 0.5rem;">
2759
- <div>
2760
- <span class="status-badge"
2761
- :class="{
2762
- 'status-completed': activity.type === 'spec_created',
2763
- 'status-in-progress': activity.type === 'worktree_created',
2764
- 'status-planning': activity.type === 'commit',
2765
- 'status-draft': activity.type === 'spec_updated'
2766
- }"
2767
- x-text="activity.type.replace('_', ' ')"></span>
2768
- <span style="margin-left: 1rem; color: var(--text-secondary); font-size: 0.875rem;"
2769
- x-text="new Date(activity.timestamp).toLocaleString()"></span>
2770
- </div>
2771
- </div>
2772
- <div style="color: var(--text-primary); margin-bottom: 0.5rem;"
2773
- x-text="activity.message"></div>
2774
- <div style="color: var(--text-secondary); font-size: 0.875rem;">
2775
- <span>by </span>
2776
- <span style="color: var(--accent-blue);" x-text="activity.author"></span>
2777
- <template x-if="activity.files">
2778
- <span>
2779
- • Modified: <span x-text="activity.files.join(', ')"></span>
2780
- </span>
2781
- </template>
2782
- </div>
2783
- </div>
2784
- </template>
2785
-
2786
- <div x-show="activities.length === 0" style="text-align: center; padding: 2rem; color: var(--text-muted);">
2787
- No recent activity
2788
- </div>
2789
- </div>
2790
- </div>
2791
- </div>
2792
- </div>
2793
- </div>
2794
-
2795
- <!-- Worktree Modal -->
2796
- <div class="modal-overlay"
2797
- x-show="showWorktreeModal"
2798
- x-transition:enter="transition ease-out duration-300"
2799
- x-transition:enter-start="opacity-0"
2800
- x-transition:enter-end="opacity-100"
2801
- x-transition:leave="transition ease-in duration-200"
2802
- x-transition:leave-start="opacity-100"
2803
- x-transition:leave-end="opacity-0"
2804
- @click.self="showWorktreeModal=false">
2805
- <div class="modal-content"
2806
- x-show="showWorktreeModal"
2807
- x-transition:enter="transition ease-out duration-300"
2808
- x-transition:enter-start="opacity-0 scale-95"
2809
- x-transition:enter-end="opacity-100 scale-100"
2810
- x-transition:leave="transition ease-in duration-200"
2811
- x-transition:leave-start="opacity-100 scale-100"
2812
- x-transition:leave-end="opacity-0 scale-95"
2813
- style="width: 99vw; height: 98vh; max-width: none; min-width: 1440px; min-height: 960px; display: grid; grid-template-columns: 1fr; grid-template-rows: auto 1fr; overflow: hidden;">
2814
- <!-- Worktree Modal Header -->
2815
- <div class="modal-header" style="background: linear-gradient(135deg, rgba(245, 158, 11, 0.15) 0%, rgba(245, 158, 11, 0.08) 100%); border-bottom: 1px solid rgba(245, 158, 11, 0.2);">
2816
- <h2 class="heading" style="margin: 0; font-size: 1.5rem; color: var(--text-primary); display: flex; align-items: center; gap: 0.75rem;">
2817
- <span style="color: var(--accent-orange); font-size: 1.75rem;">🏗️</span>
2818
- <span>Active Worktrees</span>
2819
- <span style="color: var(--text-muted); font-size: 0.9rem; font-weight: 400;">(Construction Zones)</span>
2820
- </h2>
2821
- <button class="btn btn-secondary" @click="showWorktreeModal=false"
2822
- style="background: rgba(245, 158, 11, 0.1); border: 1px solid rgba(245, 158, 11, 0.3); color: var(--accent-orange);"
2823
- @mouseenter="$el.style.background = 'rgba(245, 158, 11, 0.2)'"
2824
- @mouseleave="$el.style.background = 'rgba(245, 158, 11, 0.1)'">
2825
- 🔧 Close
2826
- </button>
2827
- </div>
2828
-
2829
- <!-- Worktree Modal Content -->
2830
- <div class="modal-main" x-data="{
2831
- worktreeDetails: [],
2832
- async loadWorktreeDetails() {
2833
- try {
2834
- const response = await fetch('/api/tools/bob.worktree.list');
2835
- const data = await response.json();
2836
- this.worktreeDetails = [
2837
- ...data.committed?.map(item => ({ name: item.name, status: 'committed', color: 'var(--accent-green)', icon: '✅' })) || [],
2838
- ...data.dirty?.map(item => ({ name: item.name, status: 'dirty', color: 'var(--accent-yellow)', icon: '⚠️' })) || [],
2839
- ...data.clean?.map(item => ({ name: item.name, status: 'clean', color: 'var(--text-secondary)', icon: '💤' })) || []
2840
- ];
2841
- } catch (error) {
2842
- console.error('Failed to load worktree details:', error);
2843
- }
2844
- }
2845
- }" x-init="loadWorktreeDetails()">
2846
-
2847
- <template x-if="worktreeDetails.length > 0">
2848
- <div>
2849
- <div style="display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));">
2850
- <template x-for="(worktree, index) in worktreeDetails" :key="index">
2851
- <div style="background: linear-gradient(135deg, rgba(245, 158, 11, 0.08) 0%, rgba(245, 158, 11, 0.04) 100%); border-radius: 12px; padding: 1.5rem; border: 1px solid rgba(245, 158, 11, 0.2); transition: all 0.2s ease; box-shadow: 0 2px 8px rgba(245, 158, 11, 0.1);"
2852
- @mouseenter="$el.style.background = 'linear-gradient(135deg, rgba(245, 158, 11, 0.12) 0%, rgba(245, 158, 11, 0.06) 100%)'; $el.style.borderColor = 'rgba(245, 158, 11, 0.3)'"
2853
- @mouseleave="$el.style.background = 'linear-gradient(135deg, rgba(245, 158, 11, 0.08) 0%, rgba(245, 158, 11, 0.04) 100%)'; $el.style.borderColor = 'rgba(245, 158, 11, 0.2)'"
2854
-
2855
- <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 1rem;">
2856
- <h3 style="margin: 0; font-size: 1rem; font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; color: var(--text-primary);"
2857
- x-text="worktree.name"></h3>
2858
- <span style="font-size: 1.4rem; color: var(--accent-orange);">🏗️</span>
2859
- </div>
2860
-
2861
- <div style="display: flex; align-items: center; gap: 0.5rem; margin-bottom: 1rem;">
2862
- <span class="status-badge"
2863
- :class="{
2864
- 'status-completed': worktree.status === 'committed',
2865
- 'status-in-progress': worktree.status === 'dirty',
2866
- 'status-draft': worktree.status === 'clean'
2867
- }"
2868
- x-text="worktree.status"></span>
2869
- </div>
2870
-
2871
- <div style="font-size: 0.875rem; color: var(--text-muted);">
2872
- <div>Branch: <span style="font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;" x-text="worktree.name"></span></div>
2873
- <div style="margin-top: 0.5rem;">
2874
- Status: <span :style="'color: ' + worktree.color" x-text="worktree.status"></span>
2875
- </div>
2876
- </div>
2877
- </div>
2878
- </template>
2879
- </div>
2880
- </div>
2881
- </template>
2882
-
2883
- <template x-if="worktreeDetails.length === 0">
2884
- <div style="text-align: center; padding: 3rem; color: var(--text-muted);">
2885
- <div style="font-size: 3rem; margin-bottom: 1rem; color: var(--accent-orange);">🏗️</div>
2886
- <h3 style="color: var(--text-primary); font-family: var(--font-heading);">No Active Construction Zones</h3>
2887
- <p style="color: var(--text-secondary);">All workshop activity is currently on the main branch.</p>
2888
- <div style="margin-top: 1.5rem; font-size: 0.9rem; color: var(--text-muted);">
2889
- <span style="color: var(--accent-orange);">🔧</span> Ready for new projects
2890
- </div>
2891
- </div>
2892
- </template>
2893
- </div>
2894
- </div>
2895
- </div>
2896
-
2897
-
2898
-
2899
- <!-- Delete Confirmation Modal - Compact Top-Right -->
2900
- <div x-show="confirmDelete"
2901
- x-transition:enter="transition ease-out duration-300"
2902
- x-transition:enter-start="opacity-0 translate-x-4"
2903
- x-transition:enter-end="opacity-100 translate-x-0"
2904
- x-transition:leave="transition ease-in duration-200"
2905
- x-transition:leave-start="opacity-100 translate-x-0"
2906
- x-transition:leave-end="opacity-0 translate-x-4"
2907
- style="position: fixed; top: 1rem; right: 1rem; z-index: 1000; max-width: 400px;">
2908
- <div class="glass"
2909
- style="background: rgba(0, 0, 0, 0.95); border: 2px solid var(--accent-red); padding: 1.5rem; border-radius: 16px; backdrop-filter: blur(20px);">
2910
- <div>
2911
- <div style="display: flex; align-items: center; gap: 1rem; margin-bottom: 1rem;">
2912
- <div style="font-size: 1.5rem; color: var(--accent-red);">⚠️</div>
2913
- <h3 class="heading" style="color: var(--text-primary); margin: 0; font-size: 1rem;">Confirm Deletion</h3>
2914
- </div>
2915
- <p style="color: var(--text-secondary); margin-bottom: 1.5rem; font-size: 0.9rem; line-height: 1.4;">
2916
- Delete "<span style="color: var(--text-primary); font-weight: 600;" x-text="confirmDelete?.title"></span>"?
2917
- <br>
2918
- <strong style="color: var(--accent-red); font-size: 0.85rem;">This cannot be undone.</strong>
2919
- </p>
2920
-
2921
- <div style="display: flex; gap: 0.75rem; justify-content: flex-end;">
2922
- <button class="btn btn-secondary"
2923
- style="padding: 0.5rem 1rem; font-size: 0.85rem;"
2924
- @click="cancelDelete()">
2925
- Cancel
2926
- </button>
2927
- <button class="btn"
2928
- style="padding: 0.5rem 1rem; font-size: 0.85rem; background: var(--accent-red); border: 1px solid var(--accent-red); color: white;"
2929
- @click="deleteSpec()">
2930
- 🗑 Delete
2931
- </button>
2932
- </div>
2933
- </div>
2934
- </div>
2935
- </div>
2936
-
2937
- <!-- Toast Notifications -->
2938
- <div class="toast-container"
2939
- style="position: fixed; top: 1rem; right: 1rem; z-index: 9998; max-width: 400px; padding-top: 0;"
2940
- :style="confirmDelete ? 'padding-top: 200px;' : 'padding-top: 0;'">
2941
- <template x-for="toast in toasts" :key="toast.id">
2942
- <div x-show="toast.visible"
2943
- x-transition:enter="transition ease-out duration-300 transform"
2944
- x-transition:enter-start="translate-x-full opacity-0"
2945
- x-transition:enter-end="translate-x-0 opacity-100"
2946
- x-transition:leave="transition ease-in duration-200 transform"
2947
- x-transition:leave-start="translate-x-0 opacity-100"
2948
- x-transition:leave-end="translate-x-full opacity-0"
2949
- class="toast-notification"
2950
- :class="{
2951
- 'toast-success': toast.type === 'success',
2952
- 'toast-error': toast.type === 'error',
2953
- 'toast-info': toast.type === 'info'
2954
- }"
2955
- style="margin-bottom: 0.75rem; padding: 1rem 1.5rem; border-radius: 8px; backdrop-filter: blur(10px); border: 1px solid; box-shadow: 0 4px 20px rgba(0,0,0,0.15); cursor: pointer; font-family: var(--font-body); font-size: 0.9rem; line-height: 1.4; display: flex; align-items: center; justify-content: space-between; min-width: 300px;"
2956
- @click="removeToast(toast.id)">
2957
- <span x-text="toast.message" style="flex: 1; margin-right: 0.75rem;"></span>
2958
- <button @click.stop="removeToast(toast.id)"
2959
- style="background: none; border: none; color: inherit; opacity: 0.7; font-size: 1.1em; cursor: pointer; padding: 0; margin: 0; line-height: 1;"
2960
- @mouseover="$el.style.opacity = '1'"
2961
- @mouseout="$el.style.opacity = '0.7'">×</button>
2962
- </div>
2963
- </template>
2964
- </div>
2965
-
2966
- <style>
2967
- /* Modern Toast Notifications */
2968
- .toast-notification {
2969
- font-weight: 500;
2970
- font-size: 0.875rem;
2971
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
2972
- box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
2973
- backdrop-filter: blur(12px);
2974
- -webkit-backdrop-filter: blur(12px);
2975
- }
2976
-
2977
- .toast-success {
2978
- background: linear-gradient(135deg, rgba(34, 197, 94, 0.95) 0%, rgba(21, 128, 61, 0.95) 100%) !important;
2979
- border: 1px solid rgba(34, 197, 94, 0.4) !important;
2980
- color: white !important;
2981
- box-shadow: 0 8px 32px rgba(34, 197, 94, 0.3) !important;
2982
- }
2983
-
2984
- .toast-error {
2985
- background: linear-gradient(135deg, rgba(239, 68, 68, 0.95) 0%, rgba(185, 28, 28, 0.95) 100%) !important;
2986
- border: 1px solid rgba(239, 68, 68, 0.4) !important;
2987
- color: white !important;
2988
- box-shadow: 0 8px 32px rgba(239, 68, 68, 0.3) !important;
2989
- }
2990
-
2991
- .toast-info {
2992
- background: linear-gradient(135deg, rgba(59, 130, 246, 0.95) 0%, rgba(29, 78, 216, 0.95) 100%) !important;
2993
- border: 1px solid rgba(59, 130, 246, 0.4) !important;
2994
- color: white !important;
2995
- box-shadow: 0 8px 32px rgba(59, 130, 246, 0.3) !important;
2996
- }
2997
-
2998
- .toast-notification:hover {
2999
- transform: translateY(-2px);
3000
- box-shadow: 0 15px 35px -5px rgba(0, 0, 0, 0.15), 0 15px 15px -5px rgba(0, 0, 0, 0.06);
3001
- }
3002
- </style>
3003
-
3004
- <!-- Toast Notification Container -->
3005
- <div style="position: fixed; top: 1rem; right: 1rem; z-index: 9999; display: flex; flex-direction: column; gap: 0.5rem; max-width: 400px;">
3006
- <template x-for="toast in toasts" :key="toast.id">
3007
- <div x-show="true"
3008
- x-transition:enter="transform transition ease-out duration-300"
3009
- x-transition:enter-start="translate-x-full opacity-0"
3010
- x-transition:enter-end="translate-x-0 opacity-100"
3011
- x-transition:leave="transform transition ease-in duration-200"
3012
- x-transition:leave-start="translate-x-0 opacity-100"
3013
- x-transition:leave-end="translate-x-full opacity-0"
3014
- :style="`background: ${getToastColor(toast.type)}; backdrop-filter: blur(10px); border-radius: 8px; padding: 1rem; box-shadow: 0 4px 12px rgba(0,0,0,0.15); border: 1px solid rgba(255,255,255,0.2); color: white; font-weight: 500; display: flex; align-items: center; gap: 0.75rem; cursor: pointer;`"
3015
- @click="removeToast(toast.id)">
3016
- <span x-text="getToastIcon(toast.type)" style="font-size: 1.1rem;"></span>
3017
- <span x-text="toast.message" style="flex: 1; line-height: 1.4;"></span>
3018
- <button @click.stop="removeToast(toast.id)"
3019
- style="background: rgba(255,255,255,0.2); border: none; color: white; border-radius: 4px; padding: 0.25rem 0.5rem; font-size: 0.8rem; cursor: pointer; opacity: 0.8; transition: opacity 0.2s;"
3020
- @mouseenter="$el.style.opacity = '1'"
3021
- @mouseleave="$el.style.opacity = '0.8'">
3022
-
3023
- </button>
3024
- </div>
3025
- </template>
3026
- </div>
3027
-
3028
- <!-- Global copy functionality and enhanced code block styles -->
3029
- <script>
3030
- // Global function for copying code blocks
3031
- function copyToClipboard(text, buttonId) {
3032
- navigator.clipboard.writeText(text).then(() => {
3033
- const button = document.getElementById(buttonId);
3034
- if (button) {
3035
- const originalText = button.innerHTML;
3036
- button.innerHTML = '✅ Copied!';
3037
- button.style.background = 'rgba(16, 185, 129, 0.2)';
3038
- button.style.borderColor = 'rgba(16, 185, 129, 0.4)';
3039
-
3040
- setTimeout(() => {
3041
- button.innerHTML = originalText;
3042
- button.style.background = '';
3043
- button.style.borderColor = '';
3044
- }, 2000);
3045
- }
3046
-
3047
- // Also show toast notification if available
3048
- try {
3049
- window.Alpine.store('dashboard').addToast('📋 Command copied to clipboard!', 'success');
3050
- } catch (e) {
3051
- console.log('📋 Command copied to clipboard!');
3052
- }
3053
- }).catch(err => {
3054
- console.error('Failed to copy:', err);
3055
- try {
3056
- window.Alpine.store('dashboard').addToast('❌ Failed to copy command', 'error');
3057
- } catch (e) {
3058
- console.error('Failed to copy command');
3059
- }
3060
- });
3061
- }
3062
- </script>
3063
-
3064
- <style>
3065
- /* Enhanced code block styles with copy buttons */
3066
- .code-block-container {
3067
- position: relative;
3068
- margin: 1.5rem 0;
3069
- background: rgba(0, 0, 0, 0.6);
3070
- border-radius: 8px;
3071
- border: 1px solid rgba(255, 255, 255, 0.15);
3072
- overflow: hidden;
3073
- }
3074
-
3075
- .code-block-header {
3076
- display: flex;
3077
- justify-content: space-between;
3078
- align-items: center;
3079
- padding: 0.75rem 1rem;
3080
- background: rgba(255, 255, 255, 0.05);
3081
- border-bottom: 1px solid rgba(255, 255, 255, 0.1);
3082
- }
3083
-
3084
- .code-lang {
3085
- font-size: 0.8rem;
3086
- color: rgba(245, 158, 11, 0.9);
3087
- font-weight: 500;
3088
- text-transform: uppercase;
3089
- letter-spacing: 0.5px;
3090
- }
3091
-
3092
- .copy-code-btn {
3093
- background: rgba(255, 255, 255, 0.1);
3094
- border: 1px solid rgba(255, 255, 255, 0.2);
3095
- color: var(--text-primary);
3096
- padding: 0.4rem 0.8rem;
3097
- border-radius: 4px;
3098
- font-size: 0.8rem;
3099
- cursor: pointer;
3100
- transition: all 0.2s ease;
3101
- display: flex;
3102
- align-items: center;
3103
- gap: 0.3rem;
3104
- }
3105
-
3106
- .copy-code-btn:hover {
3107
- background: rgba(255, 255, 255, 0.15);
3108
- border-color: rgba(255, 255, 255, 0.3);
3109
- transform: translateY(-1px);
3110
- }
3111
-
3112
- .code-block-container pre {
3113
- margin: 0;
3114
- padding: 1.2rem;
3115
- background: transparent;
3116
- border: none;
3117
- overflow-x: auto;
3118
- }
3119
-
3120
- .code-block-container code {
3121
- background: transparent;
3122
- color: var(--text-primary);
3123
- font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
3124
- font-size: 0.9rem;
3125
- line-height: 1.5;
3126
- }
3127
-
3128
- .inline-code {
3129
- background: rgba(255, 255, 255, 0.1) !important;
3130
- padding: 0.2rem 0.4rem !important;
3131
- border-radius: 3px !important;
3132
- font-family: 'Monaco', 'Menlo', 'Consolas', monospace !important;
3133
- font-size: 0.85em !important;
3134
- color: rgba(245, 158, 11, 0.9) !important;
3135
- border: 1px solid rgba(255, 255, 255, 0.15) !important;
3136
- }
3137
-
3138
- /* Quick Access section styling */
3139
- .spec-content h3 {
3140
- color: rgba(34, 197, 94, 0.9);
3141
- border-bottom: 2px solid rgba(34, 197, 94, 0.3);
3142
- padding-bottom: 0.5rem;
3143
- margin-bottom: 1.5rem;
3144
- }
3145
-
3146
- .spec-content ul li {
3147
- margin: 0.5rem 0;
3148
- color: var(--text-secondary);
3149
- }
3150
-
3151
- .spec-content ul li strong {
3152
- color: var(--text-primary);
3153
- }
3154
- </style>
3155
-
3156
- </body>
3157
- </html>