tlc-claude-code 1.4.1 → 1.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dashboard/dist/App.js +229 -35
- package/dashboard/dist/components/AgentRegistryPane.d.ts +35 -0
- package/dashboard/dist/components/AgentRegistryPane.js +89 -0
- package/dashboard/dist/components/AgentRegistryPane.test.d.ts +1 -0
- package/dashboard/dist/components/AgentRegistryPane.test.js +200 -0
- package/dashboard/dist/components/RouterPane.d.ts +5 -0
- package/dashboard/dist/components/RouterPane.js +65 -0
- package/dashboard/dist/components/RouterPane.test.d.ts +1 -0
- package/dashboard/dist/components/RouterPane.test.js +176 -0
- package/dashboard/dist/components/accessibility.test.d.ts +1 -0
- package/dashboard/dist/components/accessibility.test.js +116 -0
- package/dashboard/dist/components/layout/MobileNav.d.ts +16 -0
- package/dashboard/dist/components/layout/MobileNav.js +31 -0
- package/dashboard/dist/components/layout/MobileNav.test.d.ts +1 -0
- package/dashboard/dist/components/layout/MobileNav.test.js +111 -0
- package/dashboard/dist/components/performance.test.d.ts +1 -0
- package/dashboard/dist/components/performance.test.js +114 -0
- package/dashboard/dist/components/responsive.test.d.ts +1 -0
- package/dashboard/dist/components/responsive.test.js +114 -0
- package/dashboard/dist/components/ui/Dropdown.d.ts +22 -0
- package/dashboard/dist/components/ui/Dropdown.js +109 -0
- package/dashboard/dist/components/ui/Dropdown.test.d.ts +1 -0
- package/dashboard/dist/components/ui/Dropdown.test.js +105 -0
- package/dashboard/dist/components/ui/Modal.d.ts +13 -0
- package/dashboard/dist/components/ui/Modal.js +25 -0
- package/dashboard/dist/components/ui/Modal.test.d.ts +1 -0
- package/dashboard/dist/components/ui/Modal.test.js +91 -0
- package/dashboard/dist/components/ui/Skeleton.d.ts +32 -0
- package/dashboard/dist/components/ui/Skeleton.js +48 -0
- package/dashboard/dist/components/ui/Skeleton.test.d.ts +1 -0
- package/dashboard/dist/components/ui/Skeleton.test.js +125 -0
- package/dashboard/dist/components/ui/Toast.d.ts +32 -0
- package/dashboard/dist/components/ui/Toast.js +21 -0
- package/dashboard/dist/components/ui/Toast.test.d.ts +1 -0
- package/dashboard/dist/components/ui/Toast.test.js +118 -0
- package/dashboard/dist/hooks/useTheme.d.ts +37 -0
- package/dashboard/dist/hooks/useTheme.js +96 -0
- package/dashboard/dist/hooks/useTheme.test.d.ts +1 -0
- package/dashboard/dist/hooks/useTheme.test.js +94 -0
- package/dashboard/dist/hooks/useWebSocket.d.ts +17 -0
- package/dashboard/dist/hooks/useWebSocket.js +100 -0
- package/dashboard/dist/hooks/useWebSocket.test.d.ts +1 -0
- package/dashboard/dist/hooks/useWebSocket.test.js +115 -0
- package/dashboard/dist/stores/projectStore.d.ts +44 -0
- package/dashboard/dist/stores/projectStore.js +76 -0
- package/dashboard/dist/stores/projectStore.test.d.ts +1 -0
- package/dashboard/dist/stores/projectStore.test.js +114 -0
- package/dashboard/dist/stores/uiStore.d.ts +29 -0
- package/dashboard/dist/stores/uiStore.js +72 -0
- package/dashboard/dist/stores/uiStore.test.d.ts +1 -0
- package/dashboard/dist/stores/uiStore.test.js +93 -0
- package/dashboard/package.json +3 -3
- package/docker-compose.dev.yml +6 -1
- package/package.json +5 -2
- package/server/dashboard/index.html +1336 -779
- package/server/index.js +178 -0
- package/server/lib/agent-cleanup.js +177 -0
- package/server/lib/agent-cleanup.test.js +359 -0
- package/server/lib/agent-hooks.js +126 -0
- package/server/lib/agent-hooks.test.js +303 -0
- package/server/lib/agent-metadata.js +179 -0
- package/server/lib/agent-metadata.test.js +383 -0
- package/server/lib/agent-persistence.js +191 -0
- package/server/lib/agent-persistence.test.js +475 -0
- package/server/lib/agent-registry-command.js +340 -0
- package/server/lib/agent-registry-command.test.js +334 -0
- package/server/lib/agent-registry.js +155 -0
- package/server/lib/agent-registry.test.js +239 -0
- package/server/lib/agent-state.js +236 -0
- package/server/lib/agent-state.test.js +375 -0
- package/server/lib/api-provider.js +186 -0
- package/server/lib/api-provider.test.js +336 -0
- package/server/lib/cli-detector.js +166 -0
- package/server/lib/cli-detector.test.js +269 -0
- package/server/lib/cli-provider.js +212 -0
- package/server/lib/cli-provider.test.js +349 -0
- package/server/lib/debug.test.js +62 -0
- package/server/lib/devserver-router-api.js +249 -0
- package/server/lib/devserver-router-api.test.js +426 -0
- package/server/lib/model-router.js +245 -0
- package/server/lib/model-router.test.js +313 -0
- package/server/lib/output-schemas.js +269 -0
- package/server/lib/output-schemas.test.js +307 -0
- package/server/lib/provider-interface.js +153 -0
- package/server/lib/provider-interface.test.js +394 -0
- package/server/lib/provider-queue.js +158 -0
- package/server/lib/provider-queue.test.js +315 -0
- package/server/lib/router-config.js +221 -0
- package/server/lib/router-config.test.js +237 -0
- package/server/lib/router-setup-command.js +419 -0
- package/server/lib/router-setup-command.test.js +375 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>TLC
|
|
6
|
+
<title>TLC Dashboard</title>
|
|
7
7
|
<style>
|
|
8
8
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
9
9
|
body {
|
|
@@ -14,303 +14,446 @@
|
|
|
14
14
|
overflow: hidden;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
/* Main Layout - Coolify Style */
|
|
18
|
+
.app {
|
|
19
|
+
display: flex;
|
|
20
|
+
flex-direction: column;
|
|
21
|
+
height: 100vh;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* Header */
|
|
17
25
|
.header {
|
|
18
26
|
background: #161b22;
|
|
19
|
-
padding:
|
|
27
|
+
padding: 8px 16px;
|
|
20
28
|
display: flex;
|
|
21
29
|
justify-content: space-between;
|
|
22
30
|
align-items: center;
|
|
23
31
|
border-bottom: 1px solid #30363d;
|
|
32
|
+
height: 48px;
|
|
24
33
|
}
|
|
25
|
-
.header
|
|
26
|
-
font-size: 18px;
|
|
27
|
-
color: #58a6ff;
|
|
34
|
+
.header-left {
|
|
28
35
|
display: flex;
|
|
29
36
|
align-items: center;
|
|
30
|
-
gap:
|
|
37
|
+
gap: 16px;
|
|
31
38
|
}
|
|
32
|
-
.
|
|
39
|
+
.logo {
|
|
33
40
|
font-family: monospace;
|
|
34
|
-
font-size:
|
|
35
|
-
color: #7ee787;
|
|
41
|
+
font-size: 18px;
|
|
36
42
|
font-weight: bold;
|
|
43
|
+
color: #7ee787;
|
|
44
|
+
}
|
|
45
|
+
.header-title {
|
|
46
|
+
font-size: 14px;
|
|
47
|
+
color: #8b949e;
|
|
48
|
+
}
|
|
49
|
+
.header-subtitle {
|
|
50
|
+
color: #58a6ff;
|
|
51
|
+
font-weight: 500;
|
|
37
52
|
}
|
|
38
|
-
.header
|
|
53
|
+
.header-right {
|
|
39
54
|
display: flex;
|
|
55
|
+
align-items: center;
|
|
40
56
|
gap: 12px;
|
|
57
|
+
}
|
|
58
|
+
.connection-status {
|
|
59
|
+
display: flex;
|
|
41
60
|
align-items: center;
|
|
61
|
+
gap: 6px;
|
|
62
|
+
font-size: 12px;
|
|
42
63
|
}
|
|
43
|
-
.
|
|
64
|
+
.status-dot {
|
|
44
65
|
width: 8px;
|
|
45
66
|
height: 8px;
|
|
46
67
|
border-radius: 50%;
|
|
47
68
|
}
|
|
48
|
-
.
|
|
49
|
-
.
|
|
50
|
-
.
|
|
51
|
-
padding: 4px 10px;
|
|
52
|
-
background: #238636;
|
|
53
|
-
border-radius: 12px;
|
|
69
|
+
.status-dot.connected { background: #3fb950; }
|
|
70
|
+
.status-dot.disconnected { background: #f85149; }
|
|
71
|
+
.version {
|
|
54
72
|
font-size: 12px;
|
|
55
|
-
|
|
73
|
+
color: #6e7681;
|
|
56
74
|
}
|
|
57
75
|
|
|
76
|
+
/* Main Container */
|
|
58
77
|
.container {
|
|
59
|
-
display: grid;
|
|
60
|
-
grid-template-columns: 1fr 300px;
|
|
61
|
-
height: calc(100vh - 54px);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
.main-content {
|
|
65
78
|
display: flex;
|
|
66
|
-
flex
|
|
79
|
+
flex: 1;
|
|
67
80
|
overflow: hidden;
|
|
68
81
|
}
|
|
69
82
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
83
|
+
/* Sidebar - Coolify Style */
|
|
84
|
+
.sidebar {
|
|
85
|
+
width: 200px;
|
|
73
86
|
background: #161b22;
|
|
87
|
+
border-right: 1px solid #30363d;
|
|
88
|
+
display: flex;
|
|
89
|
+
flex-direction: column;
|
|
90
|
+
transition: width 0.2s;
|
|
91
|
+
}
|
|
92
|
+
.sidebar.collapsed {
|
|
93
|
+
width: 56px;
|
|
94
|
+
}
|
|
95
|
+
.sidebar-header {
|
|
96
|
+
padding: 16px;
|
|
74
97
|
border-bottom: 1px solid #30363d;
|
|
75
98
|
}
|
|
76
|
-
.
|
|
77
|
-
padding: 12px 20px;
|
|
78
|
-
background: transparent;
|
|
79
|
-
border: none;
|
|
80
|
-
color: #8b949e;
|
|
81
|
-
cursor: pointer;
|
|
99
|
+
.sidebar-title {
|
|
82
100
|
font-size: 14px;
|
|
83
|
-
|
|
84
|
-
|
|
101
|
+
font-weight: 600;
|
|
102
|
+
color: #58a6ff;
|
|
85
103
|
}
|
|
86
|
-
.
|
|
87
|
-
|
|
88
|
-
background: #21262d;
|
|
104
|
+
.sidebar.collapsed .sidebar-title {
|
|
105
|
+
display: none;
|
|
89
106
|
}
|
|
90
|
-
.
|
|
91
|
-
|
|
92
|
-
|
|
107
|
+
.nav-items {
|
|
108
|
+
flex: 1;
|
|
109
|
+
padding: 8px;
|
|
110
|
+
overflow-y: auto;
|
|
93
111
|
}
|
|
94
|
-
.
|
|
95
|
-
|
|
112
|
+
.nav-item {
|
|
113
|
+
display: flex;
|
|
114
|
+
align-items: center;
|
|
115
|
+
gap: 12px;
|
|
116
|
+
padding: 10px 12px;
|
|
117
|
+
border-radius: 6px;
|
|
118
|
+
cursor: pointer;
|
|
119
|
+
color: #8b949e;
|
|
120
|
+
transition: all 0.15s;
|
|
121
|
+
margin-bottom: 2px;
|
|
122
|
+
}
|
|
123
|
+
.nav-item:hover {
|
|
124
|
+
background: #21262d;
|
|
125
|
+
color: #e6edf3;
|
|
126
|
+
}
|
|
127
|
+
.nav-item.active {
|
|
128
|
+
background: #1f6feb;
|
|
96
129
|
color: white;
|
|
130
|
+
}
|
|
131
|
+
.nav-item .icon {
|
|
132
|
+
font-size: 16px;
|
|
133
|
+
width: 24px;
|
|
134
|
+
text-align: center;
|
|
135
|
+
}
|
|
136
|
+
.nav-item .label {
|
|
137
|
+
flex: 1;
|
|
138
|
+
font-size: 13px;
|
|
139
|
+
}
|
|
140
|
+
.nav-item .shortcut {
|
|
141
|
+
font-size: 11px;
|
|
142
|
+
color: #6e7681;
|
|
97
143
|
padding: 2px 6px;
|
|
98
|
-
|
|
144
|
+
background: #21262d;
|
|
145
|
+
border-radius: 4px;
|
|
146
|
+
}
|
|
147
|
+
.nav-item.active .shortcut {
|
|
148
|
+
background: rgba(255,255,255,0.2);
|
|
149
|
+
color: rgba(255,255,255,0.8);
|
|
150
|
+
}
|
|
151
|
+
.sidebar.collapsed .nav-item .label,
|
|
152
|
+
.sidebar.collapsed .nav-item .shortcut {
|
|
153
|
+
display: none;
|
|
154
|
+
}
|
|
155
|
+
.sidebar-footer {
|
|
156
|
+
padding: 12px;
|
|
157
|
+
border-top: 1px solid #30363d;
|
|
99
158
|
font-size: 11px;
|
|
100
|
-
|
|
159
|
+
color: #6e7681;
|
|
160
|
+
}
|
|
161
|
+
.sidebar.collapsed .sidebar-footer span {
|
|
162
|
+
display: none;
|
|
101
163
|
}
|
|
102
164
|
|
|
103
|
-
|
|
165
|
+
/* Main Content */
|
|
166
|
+
.main {
|
|
104
167
|
flex: 1;
|
|
168
|
+
display: flex;
|
|
169
|
+
flex-direction: column;
|
|
105
170
|
overflow: hidden;
|
|
106
171
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
172
|
+
|
|
173
|
+
/* View Header */
|
|
174
|
+
.view-header {
|
|
175
|
+
padding: 12px 20px;
|
|
176
|
+
border-bottom: 1px solid #30363d;
|
|
177
|
+
background: #0d1117;
|
|
178
|
+
}
|
|
179
|
+
.view-title {
|
|
180
|
+
font-size: 16px;
|
|
181
|
+
font-weight: 600;
|
|
182
|
+
color: #58a6ff;
|
|
183
|
+
}
|
|
184
|
+
.breadcrumb {
|
|
185
|
+
color: #6e7681;
|
|
186
|
+
font-size: 14px;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/* View Content */
|
|
190
|
+
.view-content {
|
|
191
|
+
flex: 1;
|
|
110
192
|
overflow-y: auto;
|
|
111
193
|
padding: 20px;
|
|
112
194
|
}
|
|
113
|
-
|
|
114
|
-
|
|
195
|
+
|
|
196
|
+
/* Footer */
|
|
197
|
+
.footer {
|
|
198
|
+
padding: 8px 16px;
|
|
199
|
+
background: #161b22;
|
|
200
|
+
border-top: 1px solid #30363d;
|
|
201
|
+
display: flex;
|
|
202
|
+
justify-content: space-between;
|
|
203
|
+
font-size: 11px;
|
|
204
|
+
color: #6e7681;
|
|
115
205
|
}
|
|
116
206
|
|
|
117
|
-
/*
|
|
118
|
-
.
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
line-height: 1.6;
|
|
122
|
-
white-space: pre-wrap;
|
|
207
|
+
/* Panels - Hidden by default */
|
|
208
|
+
.panel {
|
|
209
|
+
display: none;
|
|
210
|
+
height: 100%;
|
|
123
211
|
}
|
|
124
|
-
.
|
|
125
|
-
|
|
126
|
-
color: #58a6ff;
|
|
127
|
-
margin: 20px 0 10px 0;
|
|
212
|
+
.panel.active {
|
|
213
|
+
display: block;
|
|
128
214
|
}
|
|
129
|
-
.plan-content h1 { font-size: 20px; }
|
|
130
|
-
.plan-content h2 { font-size: 16px; }
|
|
131
|
-
.plan-content h3 { font-size: 14px; }
|
|
132
|
-
.plan-content .task-done { color: #3fb950; }
|
|
133
|
-
.plan-content .task-working { color: #58a6ff; }
|
|
134
|
-
.plan-content .task-todo { color: #8b949e; }
|
|
135
215
|
|
|
136
|
-
/*
|
|
137
|
-
.
|
|
138
|
-
|
|
216
|
+
/* Projects View */
|
|
217
|
+
.project-card {
|
|
218
|
+
background: #161b22;
|
|
219
|
+
border: 1px solid #30363d;
|
|
220
|
+
border-radius: 8px;
|
|
221
|
+
padding: 20px;
|
|
222
|
+
margin-bottom: 16px;
|
|
139
223
|
}
|
|
140
|
-
.
|
|
141
|
-
|
|
142
|
-
color: #58a6ff;
|
|
143
|
-
margin-bottom: 15px;
|
|
144
|
-
padding-bottom: 8px;
|
|
145
|
-
border-bottom: 1px solid #30363d;
|
|
224
|
+
.project-card:hover {
|
|
225
|
+
border-color: #58a6ff;
|
|
146
226
|
}
|
|
147
|
-
.
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
border-radius: 6px;
|
|
227
|
+
.project-name {
|
|
228
|
+
font-size: 18px;
|
|
229
|
+
font-weight: 600;
|
|
151
230
|
margin-bottom: 8px;
|
|
152
|
-
display: flex;
|
|
153
|
-
align-items: center;
|
|
154
|
-
gap: 12px;
|
|
155
231
|
}
|
|
156
|
-
.
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
232
|
+
.project-desc {
|
|
233
|
+
color: #8b949e;
|
|
234
|
+
font-size: 14px;
|
|
235
|
+
margin-bottom: 16px;
|
|
236
|
+
}
|
|
237
|
+
.project-stats {
|
|
162
238
|
display: flex;
|
|
163
|
-
|
|
164
|
-
justify-content: center;
|
|
239
|
+
gap: 24px;
|
|
165
240
|
}
|
|
166
|
-
.
|
|
167
|
-
|
|
168
|
-
border-color: #238636;
|
|
241
|
+
.project-stat {
|
|
242
|
+
text-align: center;
|
|
169
243
|
}
|
|
170
|
-
.
|
|
171
|
-
|
|
172
|
-
|
|
244
|
+
.project-stat-value {
|
|
245
|
+
font-size: 24px;
|
|
246
|
+
font-weight: bold;
|
|
247
|
+
}
|
|
248
|
+
.project-stat-value.green { color: #3fb950; }
|
|
249
|
+
.project-stat-value.red { color: #f85149; }
|
|
250
|
+
.project-stat-value.blue { color: #58a6ff; }
|
|
251
|
+
.project-stat-label {
|
|
252
|
+
font-size: 11px;
|
|
253
|
+
color: #8b949e;
|
|
254
|
+
text-transform: uppercase;
|
|
255
|
+
}
|
|
256
|
+
.phase-badge {
|
|
257
|
+
display: inline-block;
|
|
258
|
+
padding: 4px 12px;
|
|
259
|
+
background: #238636;
|
|
260
|
+
border-radius: 12px;
|
|
173
261
|
font-size: 12px;
|
|
262
|
+
margin-bottom: 16px;
|
|
174
263
|
}
|
|
175
|
-
.
|
|
176
|
-
|
|
264
|
+
.progress-bar {
|
|
265
|
+
height: 8px;
|
|
266
|
+
background: #21262d;
|
|
267
|
+
border-radius: 4px;
|
|
268
|
+
overflow: hidden;
|
|
269
|
+
margin-top: 16px;
|
|
177
270
|
}
|
|
178
|
-
.
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
271
|
+
.progress-fill {
|
|
272
|
+
height: 100%;
|
|
273
|
+
background: linear-gradient(90deg, #238636, #3fb950);
|
|
274
|
+
border-radius: 4px;
|
|
275
|
+
transition: width 0.3s;
|
|
182
276
|
}
|
|
183
277
|
|
|
184
|
-
/*
|
|
185
|
-
.
|
|
186
|
-
|
|
278
|
+
/* Tasks View */
|
|
279
|
+
.task-board {
|
|
280
|
+
display: flex;
|
|
281
|
+
gap: 16px;
|
|
282
|
+
}
|
|
283
|
+
.task-column {
|
|
284
|
+
flex: 1;
|
|
285
|
+
min-width: 250px;
|
|
286
|
+
}
|
|
287
|
+
.task-column-header {
|
|
288
|
+
font-size: 12px;
|
|
289
|
+
font-weight: 600;
|
|
290
|
+
text-transform: uppercase;
|
|
291
|
+
color: #8b949e;
|
|
292
|
+
margin-bottom: 12px;
|
|
293
|
+
padding-bottom: 8px;
|
|
294
|
+
border-bottom: 2px solid #30363d;
|
|
295
|
+
}
|
|
296
|
+
.task-column-header.pending { border-color: #6e7681; }
|
|
297
|
+
.task-column-header.in-progress { border-color: #58a6ff; }
|
|
298
|
+
.task-column-header.completed { border-color: #3fb950; }
|
|
299
|
+
.task-item {
|
|
187
300
|
background: #161b22;
|
|
301
|
+
border: 1px solid #30363d;
|
|
188
302
|
border-radius: 6px;
|
|
189
|
-
|
|
190
|
-
|
|
303
|
+
padding: 12px;
|
|
304
|
+
margin-bottom: 8px;
|
|
305
|
+
cursor: pointer;
|
|
191
306
|
}
|
|
192
|
-
.
|
|
193
|
-
border-
|
|
194
|
-
opacity: 0.6;
|
|
307
|
+
.task-item:hover {
|
|
308
|
+
border-color: #58a6ff;
|
|
195
309
|
}
|
|
196
|
-
.
|
|
197
|
-
|
|
198
|
-
justify-content: space-between;
|
|
310
|
+
.task-title {
|
|
311
|
+
font-size: 14px;
|
|
199
312
|
margin-bottom: 8px;
|
|
200
313
|
}
|
|
201
|
-
.
|
|
202
|
-
font-
|
|
203
|
-
color: #
|
|
314
|
+
.task-meta {
|
|
315
|
+
font-size: 11px;
|
|
316
|
+
color: #6e7681;
|
|
204
317
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
318
|
+
|
|
319
|
+
/* Chat View */
|
|
320
|
+
.chat-container {
|
|
321
|
+
display: flex;
|
|
322
|
+
flex-direction: column;
|
|
323
|
+
height: 100%;
|
|
209
324
|
}
|
|
210
|
-
.
|
|
211
|
-
|
|
212
|
-
|
|
325
|
+
.chat-messages {
|
|
326
|
+
flex: 1;
|
|
327
|
+
overflow-y: auto;
|
|
328
|
+
padding: 16px;
|
|
213
329
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
background: #161b22;
|
|
217
|
-
padding: 20px;
|
|
218
|
-
border-radius: 8px;
|
|
219
|
-
margin-bottom: 20px;
|
|
330
|
+
.chat-message {
|
|
331
|
+
margin-bottom: 16px;
|
|
220
332
|
}
|
|
221
|
-
.
|
|
333
|
+
.chat-message.user .chat-bubble {
|
|
334
|
+
background: #1f6feb;
|
|
335
|
+
margin-left: 40px;
|
|
336
|
+
}
|
|
337
|
+
.chat-message.assistant .chat-bubble {
|
|
338
|
+
background: #21262d;
|
|
339
|
+
margin-right: 40px;
|
|
340
|
+
}
|
|
341
|
+
.chat-bubble {
|
|
342
|
+
padding: 12px 16px;
|
|
343
|
+
border-radius: 12px;
|
|
222
344
|
font-size: 14px;
|
|
223
|
-
|
|
224
|
-
color: #e6edf3;
|
|
345
|
+
line-height: 1.5;
|
|
225
346
|
}
|
|
226
|
-
.
|
|
347
|
+
.chat-input-container {
|
|
348
|
+
padding: 16px;
|
|
349
|
+
border-top: 1px solid #30363d;
|
|
350
|
+
}
|
|
351
|
+
.chat-input {
|
|
227
352
|
width: 100%;
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
background: #0d1117;
|
|
353
|
+
padding: 12px 16px;
|
|
354
|
+
background: #161b22;
|
|
231
355
|
border: 1px solid #30363d;
|
|
232
|
-
border-radius:
|
|
356
|
+
border-radius: 8px;
|
|
233
357
|
color: #e6edf3;
|
|
234
|
-
resize: vertical;
|
|
235
|
-
margin-bottom: 10px;
|
|
236
|
-
font-family: inherit;
|
|
237
358
|
font-size: 14px;
|
|
359
|
+
resize: none;
|
|
238
360
|
}
|
|
239
|
-
.
|
|
361
|
+
.chat-input:focus {
|
|
240
362
|
outline: none;
|
|
241
363
|
border-color: #58a6ff;
|
|
242
364
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
margin-right: 10px;
|
|
365
|
+
|
|
366
|
+
/* Agents View */
|
|
367
|
+
.agent-grid {
|
|
368
|
+
display: grid;
|
|
369
|
+
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
|
370
|
+
gap: 16px;
|
|
250
371
|
}
|
|
251
|
-
.
|
|
252
|
-
|
|
372
|
+
.agent-card {
|
|
373
|
+
background: #161b22;
|
|
374
|
+
border: 1px solid #30363d;
|
|
253
375
|
border-radius: 8px;
|
|
254
|
-
padding:
|
|
255
|
-
margin-bottom: 10px;
|
|
256
|
-
text-align: center;
|
|
257
|
-
cursor: pointer;
|
|
258
|
-
transition: all 0.2s;
|
|
259
|
-
min-height: 60px;
|
|
376
|
+
padding: 16px;
|
|
260
377
|
}
|
|
261
|
-
.
|
|
262
|
-
|
|
263
|
-
|
|
378
|
+
.agent-header {
|
|
379
|
+
display: flex;
|
|
380
|
+
justify-content: space-between;
|
|
381
|
+
align-items: center;
|
|
382
|
+
margin-bottom: 12px;
|
|
383
|
+
}
|
|
384
|
+
.agent-name {
|
|
385
|
+
font-weight: 600;
|
|
386
|
+
}
|
|
387
|
+
.agent-status {
|
|
388
|
+
font-size: 12px;
|
|
389
|
+
padding: 4px 8px;
|
|
390
|
+
border-radius: 4px;
|
|
391
|
+
}
|
|
392
|
+
.agent-status.running {
|
|
393
|
+
background: rgba(63, 185, 80, 0.2);
|
|
394
|
+
color: #3fb950;
|
|
264
395
|
}
|
|
265
|
-
.
|
|
396
|
+
.agent-status.idle {
|
|
397
|
+
background: rgba(139, 148, 158, 0.2);
|
|
266
398
|
color: #8b949e;
|
|
267
|
-
font-size: 13px;
|
|
268
399
|
}
|
|
269
|
-
.
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
margin-top: 10px;
|
|
400
|
+
.agent-task {
|
|
401
|
+
font-size: 13px;
|
|
402
|
+
color: #8b949e;
|
|
403
|
+
margin-bottom: 8px;
|
|
274
404
|
}
|
|
275
|
-
.
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
border-radius: 6px;
|
|
405
|
+
.agent-progress {
|
|
406
|
+
height: 4px;
|
|
407
|
+
background: #21262d;
|
|
408
|
+
border-radius: 2px;
|
|
280
409
|
overflow: hidden;
|
|
281
|
-
border: 1px solid #30363d;
|
|
282
410
|
}
|
|
283
|
-
.
|
|
284
|
-
width: 100%;
|
|
411
|
+
.agent-progress-fill {
|
|
285
412
|
height: 100%;
|
|
286
|
-
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
413
|
+
background: #58a6ff;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/* Preview View */
|
|
417
|
+
.preview-container {
|
|
418
|
+
height: 100%;
|
|
419
|
+
display: flex;
|
|
420
|
+
flex-direction: column;
|
|
421
|
+
}
|
|
422
|
+
.preview-toolbar {
|
|
423
|
+
display: flex;
|
|
424
|
+
gap: 8px;
|
|
425
|
+
padding: 12px 0;
|
|
426
|
+
border-bottom: 1px solid #30363d;
|
|
427
|
+
align-items: center;
|
|
428
|
+
}
|
|
429
|
+
.preview-url {
|
|
430
|
+
flex: 1;
|
|
431
|
+
text-align: right;
|
|
432
|
+
font-family: monospace;
|
|
299
433
|
font-size: 12px;
|
|
300
|
-
|
|
434
|
+
color: #8b949e;
|
|
301
435
|
}
|
|
302
|
-
.
|
|
303
|
-
|
|
436
|
+
.preview-iframe {
|
|
437
|
+
flex: 1;
|
|
438
|
+
border: 1px solid #30363d;
|
|
439
|
+
border-radius: 8px;
|
|
440
|
+
background: white;
|
|
441
|
+
margin-top: 12px;
|
|
304
442
|
}
|
|
305
443
|
|
|
306
|
-
/* Logs
|
|
307
|
-
.logs-
|
|
444
|
+
/* Logs View */
|
|
445
|
+
.logs-container {
|
|
446
|
+
height: 100%;
|
|
447
|
+
display: flex;
|
|
448
|
+
flex-direction: column;
|
|
449
|
+
}
|
|
450
|
+
.logs-filter {
|
|
308
451
|
display: flex;
|
|
309
452
|
gap: 8px;
|
|
310
|
-
margin-bottom:
|
|
453
|
+
margin-bottom: 16px;
|
|
311
454
|
}
|
|
312
|
-
.logs-
|
|
313
|
-
padding: 6px
|
|
455
|
+
.logs-filter button {
|
|
456
|
+
padding: 6px 12px;
|
|
314
457
|
background: #21262d;
|
|
315
458
|
border: 1px solid #30363d;
|
|
316
459
|
border-radius: 4px;
|
|
@@ -318,224 +461,408 @@
|
|
|
318
461
|
cursor: pointer;
|
|
319
462
|
font-size: 12px;
|
|
320
463
|
}
|
|
321
|
-
.logs-
|
|
464
|
+
.logs-filter button.active {
|
|
322
465
|
background: #30363d;
|
|
323
466
|
color: #e6edf3;
|
|
324
467
|
border-color: #58a6ff;
|
|
325
468
|
}
|
|
326
|
-
.logs-
|
|
469
|
+
.logs-output {
|
|
470
|
+
flex: 1;
|
|
327
471
|
background: #010409;
|
|
328
472
|
border-radius: 6px;
|
|
329
|
-
padding:
|
|
473
|
+
padding: 16px;
|
|
330
474
|
font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
|
|
331
475
|
font-size: 12px;
|
|
332
476
|
line-height: 1.6;
|
|
333
|
-
height: calc(100% - 60px);
|
|
334
477
|
overflow-y: auto;
|
|
335
478
|
}
|
|
336
|
-
.log-line { padding: 2px 0; white-space: pre-wrap;
|
|
479
|
+
.log-line { padding: 2px 0; white-space: pre-wrap; }
|
|
337
480
|
.log-line.error { color: #f85149; }
|
|
338
481
|
.log-line.success { color: #3fb950; }
|
|
339
482
|
.log-line.info { color: #58a6ff; }
|
|
340
483
|
.log-line.warn { color: #d29922; }
|
|
341
484
|
|
|
342
|
-
/*
|
|
343
|
-
.
|
|
344
|
-
|
|
485
|
+
/* GitHub View */
|
|
486
|
+
.github-section {
|
|
487
|
+
margin-bottom: 24px;
|
|
488
|
+
}
|
|
489
|
+
.github-section-title {
|
|
490
|
+
font-size: 14px;
|
|
491
|
+
font-weight: 600;
|
|
492
|
+
color: #58a6ff;
|
|
493
|
+
margin-bottom: 12px;
|
|
494
|
+
padding-bottom: 8px;
|
|
495
|
+
border-bottom: 1px solid #30363d;
|
|
496
|
+
}
|
|
497
|
+
.commit-item, .pr-item {
|
|
345
498
|
background: #161b22;
|
|
499
|
+
border: 1px solid #30363d;
|
|
346
500
|
border-radius: 6px;
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
gap: 15px;
|
|
501
|
+
padding: 12px;
|
|
502
|
+
margin-bottom: 8px;
|
|
350
503
|
}
|
|
351
|
-
.commit
|
|
504
|
+
.commit-hash {
|
|
352
505
|
font-family: monospace;
|
|
353
|
-
color: #58a6ff;
|
|
354
506
|
font-size: 12px;
|
|
507
|
+
color: #58a6ff;
|
|
355
508
|
}
|
|
356
|
-
.commit
|
|
357
|
-
|
|
509
|
+
.commit-message {
|
|
510
|
+
margin-top: 4px;
|
|
358
511
|
}
|
|
359
|
-
.commit
|
|
360
|
-
font-
|
|
512
|
+
.commit-meta {
|
|
513
|
+
font-size: 11px;
|
|
514
|
+
color: #6e7681;
|
|
515
|
+
margin-top: 8px;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/* Health View */
|
|
519
|
+
.health-grid {
|
|
520
|
+
display: grid;
|
|
521
|
+
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
|
522
|
+
gap: 16px;
|
|
523
|
+
}
|
|
524
|
+
.health-card {
|
|
525
|
+
background: #161b22;
|
|
526
|
+
border: 1px solid #30363d;
|
|
527
|
+
border-radius: 8px;
|
|
528
|
+
padding: 20px;
|
|
529
|
+
text-align: center;
|
|
530
|
+
}
|
|
531
|
+
.health-icon {
|
|
532
|
+
font-size: 32px;
|
|
533
|
+
margin-bottom: 12px;
|
|
534
|
+
}
|
|
535
|
+
.health-metric {
|
|
536
|
+
font-size: 28px;
|
|
537
|
+
font-weight: bold;
|
|
361
538
|
margin-bottom: 4px;
|
|
362
539
|
}
|
|
363
|
-
.
|
|
540
|
+
.health-metric.good { color: #3fb950; }
|
|
541
|
+
.health-metric.warning { color: #d29922; }
|
|
542
|
+
.health-metric.bad { color: #f85149; }
|
|
543
|
+
.health-label {
|
|
364
544
|
font-size: 12px;
|
|
365
545
|
color: #8b949e;
|
|
546
|
+
text-transform: uppercase;
|
|
366
547
|
}
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
548
|
+
.services-list {
|
|
549
|
+
margin-top: 24px;
|
|
550
|
+
}
|
|
551
|
+
.service-item {
|
|
552
|
+
display: flex;
|
|
553
|
+
justify-content: space-between;
|
|
554
|
+
align-items: center;
|
|
555
|
+
padding: 12px 16px;
|
|
370
556
|
background: #161b22;
|
|
371
|
-
border
|
|
557
|
+
border: 1px solid #30363d;
|
|
558
|
+
border-radius: 6px;
|
|
559
|
+
margin-bottom: 8px;
|
|
560
|
+
}
|
|
561
|
+
.service-name {
|
|
562
|
+
font-weight: 500;
|
|
563
|
+
}
|
|
564
|
+
.service-status {
|
|
372
565
|
display: flex;
|
|
373
|
-
|
|
566
|
+
align-items: center;
|
|
567
|
+
gap: 8px;
|
|
568
|
+
font-size: 12px;
|
|
374
569
|
}
|
|
570
|
+
.service-status.running { color: #3fb950; }
|
|
571
|
+
.service-status.stopped { color: #f85149; }
|
|
375
572
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
573
|
+
/* Router View */
|
|
574
|
+
.router-section {
|
|
575
|
+
margin-bottom: 24px;
|
|
576
|
+
}
|
|
577
|
+
.router-section-title {
|
|
578
|
+
font-size: 14px;
|
|
579
|
+
font-weight: 600;
|
|
580
|
+
color: #58a6ff;
|
|
581
|
+
margin-bottom: 12px;
|
|
582
|
+
}
|
|
583
|
+
.provider-row {
|
|
584
|
+
display: flex;
|
|
585
|
+
align-items: center;
|
|
586
|
+
gap: 12px;
|
|
587
|
+
padding: 12px 16px;
|
|
588
|
+
background: #161b22;
|
|
589
|
+
border: 1px solid #30363d;
|
|
590
|
+
border-radius: 6px;
|
|
591
|
+
margin-bottom: 8px;
|
|
592
|
+
}
|
|
593
|
+
.provider-indicator {
|
|
594
|
+
width: 10px;
|
|
595
|
+
height: 10px;
|
|
596
|
+
border-radius: 50%;
|
|
597
|
+
}
|
|
598
|
+
.provider-indicator.healthy { background: #3fb950; }
|
|
599
|
+
.provider-indicator.unhealthy { background: #f85149; }
|
|
600
|
+
.provider-name {
|
|
601
|
+
font-weight: 500;
|
|
602
|
+
min-width: 100px;
|
|
603
|
+
}
|
|
604
|
+
.provider-version {
|
|
605
|
+
color: #6e7681;
|
|
606
|
+
font-size: 12px;
|
|
607
|
+
min-width: 80px;
|
|
379
608
|
}
|
|
380
|
-
.
|
|
609
|
+
.provider-badge {
|
|
610
|
+
padding: 2px 8px;
|
|
611
|
+
border-radius: 4px;
|
|
381
612
|
font-size: 11px;
|
|
382
|
-
text-transform: uppercase;
|
|
383
|
-
color: #8b949e;
|
|
384
|
-
margin-bottom: 15px;
|
|
385
|
-
letter-spacing: 0.5px;
|
|
386
613
|
}
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
grid-template-columns: 1fr 1fr;
|
|
391
|
-
gap: 10px;
|
|
614
|
+
.provider-badge.local {
|
|
615
|
+
background: rgba(63, 185, 80, 0.2);
|
|
616
|
+
color: #3fb950;
|
|
392
617
|
}
|
|
393
|
-
.
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
618
|
+
.provider-badge.devserver {
|
|
619
|
+
background: rgba(210, 153, 34, 0.2);
|
|
620
|
+
color: #d29922;
|
|
621
|
+
}
|
|
622
|
+
.routing-table {
|
|
623
|
+
background: #161b22;
|
|
624
|
+
border: 1px solid #30363d;
|
|
397
625
|
border-radius: 6px;
|
|
626
|
+
overflow: hidden;
|
|
398
627
|
}
|
|
399
|
-
.
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
}
|
|
404
|
-
.
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
.
|
|
408
|
-
|
|
409
|
-
font-
|
|
628
|
+
.routing-row {
|
|
629
|
+
display: flex;
|
|
630
|
+
padding: 10px 16px;
|
|
631
|
+
border-bottom: 1px solid #30363d;
|
|
632
|
+
}
|
|
633
|
+
.routing-row:last-child {
|
|
634
|
+
border-bottom: none;
|
|
635
|
+
}
|
|
636
|
+
.routing-capability {
|
|
637
|
+
min-width: 120px;
|
|
638
|
+
font-weight: 500;
|
|
639
|
+
}
|
|
640
|
+
.routing-providers {
|
|
410
641
|
color: #8b949e;
|
|
411
|
-
text-transform: uppercase;
|
|
412
|
-
margin-top: 4px;
|
|
413
642
|
}
|
|
643
|
+
.routing-providers .local { color: #3fb950; }
|
|
644
|
+
.routing-providers .devserver { color: #d29922; }
|
|
414
645
|
|
|
415
|
-
|
|
646
|
+
/* Settings View */
|
|
647
|
+
.settings-section {
|
|
648
|
+
margin-bottom: 32px;
|
|
649
|
+
}
|
|
650
|
+
.settings-section-title {
|
|
651
|
+
font-size: 14px;
|
|
652
|
+
font-weight: 600;
|
|
653
|
+
margin-bottom: 16px;
|
|
654
|
+
padding-bottom: 8px;
|
|
655
|
+
border-bottom: 1px solid #30363d;
|
|
656
|
+
}
|
|
657
|
+
.settings-item {
|
|
416
658
|
display: flex;
|
|
417
|
-
|
|
418
|
-
|
|
659
|
+
justify-content: space-between;
|
|
660
|
+
align-items: center;
|
|
661
|
+
padding: 12px 0;
|
|
419
662
|
}
|
|
420
|
-
.
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
663
|
+
.settings-label {
|
|
664
|
+
color: #e6edf3;
|
|
665
|
+
}
|
|
666
|
+
.settings-desc {
|
|
667
|
+
font-size: 12px;
|
|
668
|
+
color: #6e7681;
|
|
669
|
+
margin-top: 4px;
|
|
670
|
+
}
|
|
671
|
+
.settings-value {
|
|
672
|
+
color: #8b949e;
|
|
673
|
+
font-family: monospace;
|
|
425
674
|
font-size: 13px;
|
|
426
|
-
|
|
675
|
+
}
|
|
676
|
+
.quick-links {
|
|
427
677
|
display: flex;
|
|
428
|
-
align-items: center;
|
|
429
678
|
gap: 8px;
|
|
430
|
-
|
|
431
|
-
}
|
|
432
|
-
.action-btn.primary {
|
|
433
|
-
background: #238636;
|
|
434
|
-
color: white;
|
|
679
|
+
flex-wrap: wrap;
|
|
435
680
|
}
|
|
436
|
-
.
|
|
437
|
-
|
|
681
|
+
.quick-link {
|
|
682
|
+
padding: 8px 16px;
|
|
438
683
|
background: #21262d;
|
|
439
|
-
color: #e6edf3;
|
|
440
684
|
border: 1px solid #30363d;
|
|
685
|
+
border-radius: 6px;
|
|
686
|
+
color: #8b949e;
|
|
687
|
+
text-decoration: none;
|
|
688
|
+
font-size: 13px;
|
|
689
|
+
cursor: pointer;
|
|
690
|
+
}
|
|
691
|
+
.quick-link:hover {
|
|
692
|
+
background: #30363d;
|
|
693
|
+
color: #e6edf3;
|
|
694
|
+
}
|
|
695
|
+
.quick-link kbd {
|
|
696
|
+
background: #161b22;
|
|
697
|
+
padding: 2px 6px;
|
|
698
|
+
border-radius: 3px;
|
|
699
|
+
margin-right: 8px;
|
|
441
700
|
}
|
|
442
|
-
.action-btn.secondary:hover { background: #30363d; }
|
|
443
|
-
.action-btn .icon { font-size: 16px; }
|
|
444
701
|
|
|
445
|
-
|
|
702
|
+
/* Command Palette */
|
|
703
|
+
.command-palette-overlay {
|
|
704
|
+
display: none;
|
|
705
|
+
position: fixed;
|
|
706
|
+
top: 0;
|
|
707
|
+
left: 0;
|
|
708
|
+
right: 0;
|
|
709
|
+
bottom: 0;
|
|
710
|
+
background: rgba(0, 0, 0, 0.5);
|
|
711
|
+
z-index: 1000;
|
|
712
|
+
align-items: flex-start;
|
|
713
|
+
justify-content: center;
|
|
714
|
+
padding-top: 100px;
|
|
715
|
+
}
|
|
716
|
+
.command-palette-overlay.active {
|
|
446
717
|
display: flex;
|
|
447
|
-
flex-direction: column;
|
|
448
|
-
gap: 8px;
|
|
449
718
|
}
|
|
450
|
-
.
|
|
719
|
+
.command-palette {
|
|
720
|
+
width: 600px;
|
|
721
|
+
max-width: 90vw;
|
|
722
|
+
background: #161b22;
|
|
723
|
+
border: 1px solid #30363d;
|
|
724
|
+
border-radius: 12px;
|
|
725
|
+
box-shadow: 0 16px 48px rgba(0, 0, 0, 0.4);
|
|
726
|
+
overflow: hidden;
|
|
727
|
+
}
|
|
728
|
+
.command-palette-input {
|
|
729
|
+
width: 100%;
|
|
730
|
+
padding: 16px 20px;
|
|
731
|
+
background: transparent;
|
|
732
|
+
border: none;
|
|
733
|
+
border-bottom: 1px solid #30363d;
|
|
734
|
+
color: #e6edf3;
|
|
735
|
+
font-size: 16px;
|
|
736
|
+
}
|
|
737
|
+
.command-palette-input:focus {
|
|
738
|
+
outline: none;
|
|
739
|
+
}
|
|
740
|
+
.command-palette-results {
|
|
741
|
+
max-height: 400px;
|
|
742
|
+
overflow-y: auto;
|
|
743
|
+
}
|
|
744
|
+
.command-item {
|
|
451
745
|
display: flex;
|
|
452
746
|
justify-content: space-between;
|
|
453
747
|
align-items: center;
|
|
454
|
-
padding:
|
|
455
|
-
|
|
456
|
-
border-radius: 6px;
|
|
457
|
-
text-decoration: none;
|
|
458
|
-
color: #e6edf3;
|
|
459
|
-
font-size: 13px;
|
|
748
|
+
padding: 12px 20px;
|
|
749
|
+
cursor: pointer;
|
|
460
750
|
}
|
|
461
|
-
.
|
|
751
|
+
.command-item:hover, .command-item.selected {
|
|
462
752
|
background: #21262d;
|
|
463
753
|
}
|
|
464
|
-
.
|
|
465
|
-
|
|
466
|
-
font-family: monospace;
|
|
467
|
-
font-size: 11px;
|
|
754
|
+
.command-name {
|
|
755
|
+
font-size: 14px;
|
|
468
756
|
}
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
padding: 40px;
|
|
473
|
-
color: #8b949e;
|
|
757
|
+
.command-desc {
|
|
758
|
+
font-size: 12px;
|
|
759
|
+
color: #6e7681;
|
|
474
760
|
}
|
|
475
|
-
.
|
|
476
|
-
font-size:
|
|
477
|
-
|
|
478
|
-
|
|
761
|
+
.command-shortcut {
|
|
762
|
+
font-size: 11px;
|
|
763
|
+
padding: 2px 6px;
|
|
764
|
+
background: #21262d;
|
|
765
|
+
border-radius: 4px;
|
|
766
|
+
color: #8b949e;
|
|
479
767
|
}
|
|
480
768
|
|
|
481
|
-
/*
|
|
482
|
-
.
|
|
769
|
+
/* Help Modal */
|
|
770
|
+
.help-overlay {
|
|
771
|
+
display: none;
|
|
772
|
+
position: fixed;
|
|
773
|
+
top: 0;
|
|
774
|
+
left: 0;
|
|
775
|
+
right: 0;
|
|
776
|
+
bottom: 0;
|
|
777
|
+
background: rgba(0, 0, 0, 0.5);
|
|
778
|
+
z-index: 1000;
|
|
779
|
+
align-items: center;
|
|
780
|
+
justify-content: center;
|
|
781
|
+
}
|
|
782
|
+
.help-overlay.active {
|
|
483
783
|
display: flex;
|
|
484
|
-
flex-direction: column;
|
|
485
|
-
height: 100%;
|
|
486
784
|
}
|
|
487
|
-
.
|
|
785
|
+
.help-modal {
|
|
786
|
+
width: 500px;
|
|
787
|
+
max-width: 90vw;
|
|
788
|
+
max-height: 80vh;
|
|
789
|
+
background: #161b22;
|
|
790
|
+
border: 1px solid #30363d;
|
|
791
|
+
border-radius: 12px;
|
|
792
|
+
overflow: hidden;
|
|
793
|
+
}
|
|
794
|
+
.help-header {
|
|
795
|
+
padding: 16px 20px;
|
|
796
|
+
border-bottom: 1px solid #30363d;
|
|
488
797
|
display: flex;
|
|
489
|
-
|
|
490
|
-
padding: 10px 0;
|
|
798
|
+
justify-content: space-between;
|
|
491
799
|
align-items: center;
|
|
492
|
-
border-bottom: 1px solid #30363d;
|
|
493
|
-
margin-bottom: 10px;
|
|
494
800
|
}
|
|
495
|
-
.
|
|
496
|
-
|
|
497
|
-
font-
|
|
498
|
-
|
|
801
|
+
.help-title {
|
|
802
|
+
font-size: 16px;
|
|
803
|
+
font-weight: 600;
|
|
804
|
+
color: #d29922;
|
|
805
|
+
}
|
|
806
|
+
.help-close {
|
|
807
|
+
background: none;
|
|
808
|
+
border: none;
|
|
499
809
|
color: #8b949e;
|
|
810
|
+
cursor: pointer;
|
|
811
|
+
font-size: 20px;
|
|
500
812
|
}
|
|
501
|
-
.
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
background: white;
|
|
506
|
-
width: 100%;
|
|
507
|
-
min-height: 500px;
|
|
813
|
+
.help-content {
|
|
814
|
+
padding: 20px;
|
|
815
|
+
overflow-y: auto;
|
|
816
|
+
max-height: calc(80vh - 60px);
|
|
508
817
|
}
|
|
509
|
-
|
|
510
|
-
/* Progress Bar */
|
|
511
|
-
.progress-container {
|
|
818
|
+
.help-section {
|
|
512
819
|
margin-bottom: 20px;
|
|
513
820
|
}
|
|
514
|
-
.
|
|
821
|
+
.help-section-title {
|
|
822
|
+
font-size: 12px;
|
|
823
|
+
text-transform: uppercase;
|
|
824
|
+
color: #8b949e;
|
|
825
|
+
margin-bottom: 12px;
|
|
826
|
+
}
|
|
827
|
+
.shortcut-row {
|
|
515
828
|
display: flex;
|
|
516
829
|
justify-content: space-between;
|
|
517
|
-
|
|
518
|
-
font-size: 13px;
|
|
519
|
-
}
|
|
520
|
-
.progress-header .phase-name {
|
|
521
|
-
color: #58a6ff;
|
|
522
|
-
font-weight: 500;
|
|
830
|
+
padding: 8px 0;
|
|
523
831
|
}
|
|
524
|
-
.
|
|
525
|
-
|
|
526
|
-
}
|
|
527
|
-
.progress-bar {
|
|
528
|
-
height: 8px;
|
|
832
|
+
.shortcut-keys {
|
|
833
|
+
font-family: monospace;
|
|
529
834
|
background: #21262d;
|
|
835
|
+
padding: 2px 8px;
|
|
530
836
|
border-radius: 4px;
|
|
531
|
-
|
|
837
|
+
font-size: 12px;
|
|
838
|
+
}
|
|
839
|
+
.shortcut-desc {
|
|
840
|
+
color: #8b949e;
|
|
841
|
+
font-size: 13px;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
/* Usage Stats Panel */
|
|
845
|
+
.usage-grid {
|
|
846
|
+
display: grid;
|
|
847
|
+
grid-template-columns: repeat(2, 1fr);
|
|
848
|
+
gap: 16px;
|
|
849
|
+
}
|
|
850
|
+
.usage-card {
|
|
851
|
+
background: #161b22;
|
|
852
|
+
border: 1px solid #30363d;
|
|
853
|
+
border-radius: 8px;
|
|
854
|
+
padding: 16px;
|
|
532
855
|
}
|
|
533
|
-
.
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
856
|
+
.usage-label {
|
|
857
|
+
font-size: 12px;
|
|
858
|
+
color: #8b949e;
|
|
859
|
+
margin-bottom: 8px;
|
|
860
|
+
}
|
|
861
|
+
.usage-value {
|
|
862
|
+
font-size: 24px;
|
|
863
|
+
font-weight: bold;
|
|
538
864
|
}
|
|
865
|
+
.usage-value.cost { color: #d29922; }
|
|
539
866
|
|
|
540
867
|
/* Buttons */
|
|
541
868
|
.btn {
|
|
@@ -545,6 +872,7 @@
|
|
|
545
872
|
cursor: pointer;
|
|
546
873
|
font-size: 13px;
|
|
547
874
|
font-weight: 500;
|
|
875
|
+
transition: all 0.15s;
|
|
548
876
|
}
|
|
549
877
|
.btn-primary {
|
|
550
878
|
background: #238636;
|
|
@@ -557,234 +885,635 @@
|
|
|
557
885
|
border: 1px solid #30363d;
|
|
558
886
|
}
|
|
559
887
|
.btn-secondary:hover { background: #30363d; }
|
|
888
|
+
|
|
889
|
+
/* Empty State */
|
|
890
|
+
.empty-state {
|
|
891
|
+
text-align: center;
|
|
892
|
+
padding: 60px 20px;
|
|
893
|
+
color: #6e7681;
|
|
894
|
+
}
|
|
895
|
+
.empty-state .icon {
|
|
896
|
+
font-size: 48px;
|
|
897
|
+
margin-bottom: 16px;
|
|
898
|
+
opacity: 0.5;
|
|
899
|
+
}
|
|
900
|
+
.empty-state p {
|
|
901
|
+
font-size: 14px;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
/* Split View for Router */
|
|
905
|
+
.split-view {
|
|
906
|
+
display: flex;
|
|
907
|
+
gap: 24px;
|
|
908
|
+
}
|
|
909
|
+
.split-view > div {
|
|
910
|
+
flex: 1;
|
|
911
|
+
}
|
|
560
912
|
</style>
|
|
561
913
|
</head>
|
|
562
914
|
<body>
|
|
563
|
-
<div class="
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
<
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
<button class="tab active" data-tab="app">🖥️ App</button>
|
|
579
|
-
<button class="tab" data-tab="plan">Plan</button>
|
|
580
|
-
<button class="tab" data-tab="tests">Tests</button>
|
|
581
|
-
<button class="tab" data-tab="bugs">Bugs <span class="badge" id="bugs-badge" style="display:none">0</span></button>
|
|
582
|
-
<button class="tab" data-tab="logs">Logs</button>
|
|
583
|
-
<button class="tab" data-tab="changelog">Changelog</button>
|
|
915
|
+
<div class="app">
|
|
916
|
+
<!-- Header -->
|
|
917
|
+
<header class="header">
|
|
918
|
+
<div class="header-left">
|
|
919
|
+
<span class="logo">TLC</span>
|
|
920
|
+
<span class="header-title">Dashboard</span>
|
|
921
|
+
<span class="header-subtitle" id="current-view-title">Projects</span>
|
|
922
|
+
</div>
|
|
923
|
+
<div class="header-right">
|
|
924
|
+
<div class="connection-status">
|
|
925
|
+
<span class="status-dot" id="status-dot"></span>
|
|
926
|
+
<span id="status-text">Connecting...</span>
|
|
927
|
+
</div>
|
|
928
|
+
<span style="color: #30363d">|</span>
|
|
929
|
+
<span class="version">v1.4.2</span>
|
|
584
930
|
</div>
|
|
931
|
+
</header>
|
|
585
932
|
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
</
|
|
596
|
-
<
|
|
933
|
+
<div class="container">
|
|
934
|
+
<!-- Sidebar -->
|
|
935
|
+
<nav class="sidebar" id="sidebar">
|
|
936
|
+
<div class="sidebar-header">
|
|
937
|
+
<span class="sidebar-title">TLC</span>
|
|
938
|
+
</div>
|
|
939
|
+
<div class="nav-items">
|
|
940
|
+
<div class="nav-item active" data-view="projects" onclick="switchView('projects')">
|
|
941
|
+
<span class="icon">📁</span>
|
|
942
|
+
<span class="label">Projects</span>
|
|
943
|
+
<span class="shortcut">1</span>
|
|
944
|
+
</div>
|
|
945
|
+
<div class="nav-item" data-view="tasks" onclick="switchView('tasks')">
|
|
946
|
+
<span class="icon">📋</span>
|
|
947
|
+
<span class="label">Tasks</span>
|
|
948
|
+
<span class="shortcut">2</span>
|
|
949
|
+
</div>
|
|
950
|
+
<div class="nav-item" data-view="chat" onclick="switchView('chat')">
|
|
951
|
+
<span class="icon">💬</span>
|
|
952
|
+
<span class="label">Chat</span>
|
|
953
|
+
<span class="shortcut">3</span>
|
|
954
|
+
</div>
|
|
955
|
+
<div class="nav-item" data-view="agents" onclick="switchView('agents')">
|
|
956
|
+
<span class="icon">🤖</span>
|
|
957
|
+
<span class="label">Agents</span>
|
|
958
|
+
<span class="shortcut">4</span>
|
|
959
|
+
</div>
|
|
960
|
+
<div class="nav-item" data-view="preview" onclick="switchView('preview')">
|
|
961
|
+
<span class="icon">👁</span>
|
|
962
|
+
<span class="label">Preview</span>
|
|
963
|
+
<span class="shortcut">5</span>
|
|
964
|
+
</div>
|
|
965
|
+
<div class="nav-item" data-view="logs" onclick="switchView('logs')">
|
|
966
|
+
<span class="icon">📜</span>
|
|
967
|
+
<span class="label">Logs</span>
|
|
968
|
+
<span class="shortcut">6</span>
|
|
969
|
+
</div>
|
|
970
|
+
<div class="nav-item" data-view="github" onclick="switchView('github')">
|
|
971
|
+
<span class="icon">🐙</span>
|
|
972
|
+
<span class="label">GitHub</span>
|
|
973
|
+
<span class="shortcut">7</span>
|
|
974
|
+
</div>
|
|
975
|
+
<div class="nav-item" data-view="health" onclick="switchView('health')">
|
|
976
|
+
<span class="icon">💚</span>
|
|
977
|
+
<span class="label">Health</span>
|
|
978
|
+
<span class="shortcut">8</span>
|
|
979
|
+
</div>
|
|
980
|
+
<div class="nav-item" data-view="router" onclick="switchView('router')">
|
|
981
|
+
<span class="icon">🔀</span>
|
|
982
|
+
<span class="label">Router</span>
|
|
983
|
+
<span class="shortcut">9</span>
|
|
984
|
+
</div>
|
|
985
|
+
<div class="nav-item" data-view="settings" onclick="switchView('settings')">
|
|
986
|
+
<span class="icon">⚙️</span>
|
|
987
|
+
<span class="label">Settings</span>
|
|
988
|
+
<span class="shortcut">0</span>
|
|
597
989
|
</div>
|
|
598
990
|
</div>
|
|
991
|
+
<div class="sidebar-footer">
|
|
992
|
+
<span>Ctrl+K: Commands | ?: Help</span>
|
|
993
|
+
</div>
|
|
994
|
+
</nav>
|
|
995
|
+
|
|
996
|
+
<!-- Main Content -->
|
|
997
|
+
<main class="main">
|
|
998
|
+
<div class="view-header">
|
|
999
|
+
<span class="view-title" id="view-title">📁 Projects</span>
|
|
1000
|
+
</div>
|
|
1001
|
+
<div class="view-content">
|
|
1002
|
+
|
|
1003
|
+
<!-- Projects Panel -->
|
|
1004
|
+
<div class="panel active" id="panel-projects">
|
|
1005
|
+
<div class="project-card" id="project-card">
|
|
1006
|
+
<div class="project-name" id="project-name">TLC</div>
|
|
1007
|
+
<div class="project-desc" id="project-desc">Test-Led Coding framework</div>
|
|
1008
|
+
<span class="phase-badge" id="phase-badge">Phase 33: Multi-Model Router</span>
|
|
1009
|
+
<div class="project-stats">
|
|
1010
|
+
<div class="project-stat">
|
|
1011
|
+
<div class="project-stat-value green" id="stat-passing">0</div>
|
|
1012
|
+
<div class="project-stat-label">Passing</div>
|
|
1013
|
+
</div>
|
|
1014
|
+
<div class="project-stat">
|
|
1015
|
+
<div class="project-stat-value red" id="stat-failing">0</div>
|
|
1016
|
+
<div class="project-stat-label">Failing</div>
|
|
1017
|
+
</div>
|
|
1018
|
+
<div class="project-stat">
|
|
1019
|
+
<div class="project-stat-value blue" id="stat-coverage">0%</div>
|
|
1020
|
+
<div class="project-stat-label">Coverage</div>
|
|
1021
|
+
</div>
|
|
1022
|
+
</div>
|
|
1023
|
+
<div class="progress-bar">
|
|
1024
|
+
<div class="progress-fill" id="progress-fill" style="width: 82%"></div>
|
|
1025
|
+
</div>
|
|
1026
|
+
</div>
|
|
1027
|
+
</div>
|
|
599
1028
|
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
1029
|
+
<!-- Tasks Panel -->
|
|
1030
|
+
<div class="panel" id="panel-tasks">
|
|
1031
|
+
<div class="task-board">
|
|
1032
|
+
<div class="task-column">
|
|
1033
|
+
<div class="task-column-header pending">Pending</div>
|
|
1034
|
+
<div id="tasks-pending"></div>
|
|
1035
|
+
</div>
|
|
1036
|
+
<div class="task-column">
|
|
1037
|
+
<div class="task-column-header in-progress">In Progress</div>
|
|
1038
|
+
<div id="tasks-in-progress"></div>
|
|
1039
|
+
</div>
|
|
1040
|
+
<div class="task-column">
|
|
1041
|
+
<div class="task-column-header completed">Completed</div>
|
|
1042
|
+
<div id="tasks-completed"></div>
|
|
1043
|
+
</div>
|
|
606
1044
|
</div>
|
|
607
1045
|
</div>
|
|
608
|
-
</div>
|
|
609
1046
|
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
1047
|
+
<!-- Chat Panel -->
|
|
1048
|
+
<div class="panel" id="panel-chat">
|
|
1049
|
+
<div class="chat-container">
|
|
1050
|
+
<div class="chat-messages" id="chat-messages">
|
|
1051
|
+
<div class="empty-state">
|
|
1052
|
+
<div class="icon">💬</div>
|
|
1053
|
+
<p>Chat with Claude about your project</p>
|
|
1054
|
+
</div>
|
|
1055
|
+
</div>
|
|
1056
|
+
<div class="chat-input-container">
|
|
1057
|
+
<textarea class="chat-input" placeholder="Type a message... (Enter to send)" rows="3" id="chat-input"></textarea>
|
|
1058
|
+
</div>
|
|
1059
|
+
</div>
|
|
615
1060
|
</div>
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
1061
|
+
|
|
1062
|
+
<!-- Agents Panel -->
|
|
1063
|
+
<div class="panel" id="panel-agents">
|
|
1064
|
+
<div class="agent-grid" id="agents-grid">
|
|
619
1065
|
<div class="empty-state">
|
|
620
|
-
<div class="icon"
|
|
621
|
-
<p>No
|
|
1066
|
+
<div class="icon">🤖</div>
|
|
1067
|
+
<p>No active agents</p>
|
|
622
1068
|
</div>
|
|
623
1069
|
</div>
|
|
624
1070
|
</div>
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
1071
|
+
|
|
1072
|
+
<!-- Preview Panel -->
|
|
1073
|
+
<div class="panel" id="panel-preview">
|
|
1074
|
+
<div class="preview-container">
|
|
1075
|
+
<div class="preview-toolbar">
|
|
1076
|
+
<button class="btn btn-secondary" onclick="reloadPreview()">🔄 Reload</button>
|
|
1077
|
+
<button class="btn btn-secondary" onclick="openInNewTab()">↗️ Open in New Tab</button>
|
|
1078
|
+
<button class="btn btn-primary" onclick="captureScreenshot()">📸 Screenshot</button>
|
|
1079
|
+
<span class="preview-url" id="preview-url">localhost:5001</span>
|
|
1080
|
+
</div>
|
|
1081
|
+
<iframe class="preview-iframe" id="preview-iframe" src="/app"></iframe>
|
|
629
1082
|
</div>
|
|
630
1083
|
</div>
|
|
631
|
-
</div>
|
|
632
1084
|
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
📷 Paste (Ctrl+V) or drop images here
|
|
1085
|
+
<!-- Logs Panel -->
|
|
1086
|
+
<div class="panel" id="panel-logs">
|
|
1087
|
+
<div class="logs-container">
|
|
1088
|
+
<div class="logs-filter">
|
|
1089
|
+
<button class="active" data-logtype="app" onclick="showLogs('app')">App</button>
|
|
1090
|
+
<button data-logtype="test" onclick="showLogs('test')">Tests</button>
|
|
1091
|
+
<button data-logtype="git" onclick="showLogs('git')">Git</button>
|
|
1092
|
+
<button data-logtype="system" onclick="showLogs('system')">System</button>
|
|
642
1093
|
</div>
|
|
643
|
-
<div class="
|
|
1094
|
+
<div class="logs-output" id="logs-output"></div>
|
|
1095
|
+
</div>
|
|
1096
|
+
</div>
|
|
1097
|
+
|
|
1098
|
+
<!-- GitHub Panel -->
|
|
1099
|
+
<div class="panel" id="panel-github">
|
|
1100
|
+
<div class="github-section">
|
|
1101
|
+
<div class="github-section-title">Recent Commits</div>
|
|
1102
|
+
<div id="commits-list"></div>
|
|
644
1103
|
</div>
|
|
645
|
-
<div>
|
|
646
|
-
<
|
|
647
|
-
|
|
648
|
-
<
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
<button class="btn" onclick="clearImages()" id="clear-images-btn" style="display:none">Clear Images</button>
|
|
1104
|
+
<div class="github-section">
|
|
1105
|
+
<div class="github-section-title">Pull Requests</div>
|
|
1106
|
+
<div id="prs-list">
|
|
1107
|
+
<div class="empty-state">
|
|
1108
|
+
<div class="icon">🔀</div>
|
|
1109
|
+
<p>No open pull requests</p>
|
|
1110
|
+
</div>
|
|
1111
|
+
</div>
|
|
654
1112
|
</div>
|
|
655
1113
|
</div>
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
1114
|
+
|
|
1115
|
+
<!-- Health Panel -->
|
|
1116
|
+
<div class="panel" id="panel-health">
|
|
1117
|
+
<div class="health-grid">
|
|
1118
|
+
<div class="health-card">
|
|
1119
|
+
<div class="health-icon">💚</div>
|
|
1120
|
+
<div class="health-metric good" id="health-status">Healthy</div>
|
|
1121
|
+
<div class="health-label">System Status</div>
|
|
1122
|
+
</div>
|
|
1123
|
+
<div class="health-card">
|
|
1124
|
+
<div class="health-icon">🧠</div>
|
|
1125
|
+
<div class="health-metric" id="health-memory">0 MB</div>
|
|
1126
|
+
<div class="health-label">Memory</div>
|
|
1127
|
+
</div>
|
|
1128
|
+
<div class="health-card">
|
|
1129
|
+
<div class="health-icon">⚡</div>
|
|
1130
|
+
<div class="health-metric" id="health-cpu">0%</div>
|
|
1131
|
+
<div class="health-label">CPU</div>
|
|
1132
|
+
</div>
|
|
1133
|
+
<div class="health-card">
|
|
1134
|
+
<div class="health-icon">💾</div>
|
|
1135
|
+
<div class="health-metric" id="health-disk">0%</div>
|
|
1136
|
+
<div class="health-label">Disk</div>
|
|
1137
|
+
</div>
|
|
1138
|
+
</div>
|
|
1139
|
+
<div class="services-list">
|
|
1140
|
+
<h3 style="margin-bottom: 16px; color: #58a6ff;">Services</h3>
|
|
1141
|
+
<div id="services-list"></div>
|
|
660
1142
|
</div>
|
|
661
1143
|
</div>
|
|
662
|
-
</div>
|
|
663
1144
|
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
1145
|
+
<!-- Router Panel -->
|
|
1146
|
+
<div class="panel" id="panel-router">
|
|
1147
|
+
<div class="split-view">
|
|
1148
|
+
<div>
|
|
1149
|
+
<div class="router-section">
|
|
1150
|
+
<div class="router-section-title">Providers</div>
|
|
1151
|
+
<div id="providers-list"></div>
|
|
1152
|
+
</div>
|
|
1153
|
+
<div class="router-section">
|
|
1154
|
+
<div class="router-section-title">Devserver</div>
|
|
1155
|
+
<div id="devserver-status">
|
|
1156
|
+
<div class="provider-row">
|
|
1157
|
+
<span class="provider-indicator unhealthy"></span>
|
|
1158
|
+
<span>Not configured - run </span>
|
|
1159
|
+
<code style="color: #58a6ff;">tlc router setup</code>
|
|
1160
|
+
</div>
|
|
1161
|
+
</div>
|
|
1162
|
+
</div>
|
|
1163
|
+
<div class="router-section">
|
|
1164
|
+
<div class="router-section-title">Routing Table</div>
|
|
1165
|
+
<div class="routing-table" id="routing-table"></div>
|
|
1166
|
+
</div>
|
|
1167
|
+
</div>
|
|
1168
|
+
<div>
|
|
1169
|
+
<div class="router-section">
|
|
1170
|
+
<div class="router-section-title">Usage Statistics</div>
|
|
1171
|
+
<div class="usage-grid">
|
|
1172
|
+
<div class="usage-card">
|
|
1173
|
+
<div class="usage-label">Requests Today</div>
|
|
1174
|
+
<div class="usage-value" id="usage-requests">0</div>
|
|
1175
|
+
</div>
|
|
1176
|
+
<div class="usage-card">
|
|
1177
|
+
<div class="usage-label">Tokens Used</div>
|
|
1178
|
+
<div class="usage-value" id="usage-tokens">0</div>
|
|
1179
|
+
</div>
|
|
1180
|
+
<div class="usage-card">
|
|
1181
|
+
<div class="usage-label">Local Requests</div>
|
|
1182
|
+
<div class="usage-value" style="color: #3fb950" id="usage-local">0</div>
|
|
1183
|
+
</div>
|
|
1184
|
+
<div class="usage-card">
|
|
1185
|
+
<div class="usage-label">Est. Cost Saved</div>
|
|
1186
|
+
<div class="usage-value cost" id="usage-saved">$0.00</div>
|
|
1187
|
+
</div>
|
|
1188
|
+
</div>
|
|
1189
|
+
</div>
|
|
1190
|
+
</div>
|
|
1191
|
+
</div>
|
|
670
1192
|
</div>
|
|
671
|
-
<div class="logs-content" id="logs-output"></div>
|
|
672
|
-
</div>
|
|
673
1193
|
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
<div class="
|
|
679
|
-
|
|
1194
|
+
<!-- Settings Panel -->
|
|
1195
|
+
<div class="panel" id="panel-settings">
|
|
1196
|
+
<div class="settings-section">
|
|
1197
|
+
<div class="settings-section-title">Project Configuration</div>
|
|
1198
|
+
<div class="settings-item">
|
|
1199
|
+
<div>
|
|
1200
|
+
<div class="settings-label">Project Name</div>
|
|
1201
|
+
<div class="settings-desc">Name from .tlc.json or package.json</div>
|
|
1202
|
+
</div>
|
|
1203
|
+
<span class="settings-value" id="settings-project">TLC</span>
|
|
1204
|
+
</div>
|
|
1205
|
+
<div class="settings-item">
|
|
1206
|
+
<div>
|
|
1207
|
+
<div class="settings-label">Test Framework</div>
|
|
1208
|
+
<div class="settings-desc">Primary test runner</div>
|
|
1209
|
+
</div>
|
|
1210
|
+
<span class="settings-value" id="settings-test-framework">vitest</span>
|
|
1211
|
+
</div>
|
|
1212
|
+
</div>
|
|
1213
|
+
<div class="settings-section">
|
|
1214
|
+
<div class="settings-section-title">Router Configuration</div>
|
|
1215
|
+
<div class="settings-item">
|
|
1216
|
+
<div>
|
|
1217
|
+
<div class="settings-label">Default Provider</div>
|
|
1218
|
+
<div class="settings-desc">Primary model provider</div>
|
|
1219
|
+
</div>
|
|
1220
|
+
<span class="settings-value" id="settings-provider">claude</span>
|
|
1221
|
+
</div>
|
|
1222
|
+
</div>
|
|
1223
|
+
<div class="settings-section">
|
|
1224
|
+
<div class="settings-section-title">Quick Links</div>
|
|
1225
|
+
<div class="quick-links">
|
|
1226
|
+
<a class="quick-link" onclick="switchView('health')"><kbd>U</kbd> Usage</a>
|
|
1227
|
+
<a class="quick-link" onclick="switchView('router')"><kbd>Q</kbd> Quality</a>
|
|
1228
|
+
<a class="quick-link" href="#" target="_blank"><kbd>D</kbd> Docs</a>
|
|
1229
|
+
<a class="quick-link" onclick="switchView('projects')"><kbd>W</kbd> Workspace</a>
|
|
1230
|
+
<a class="quick-link" onclick="switchView('logs')"><kbd>A</kbd> Audit</a>
|
|
1231
|
+
</div>
|
|
680
1232
|
</div>
|
|
681
1233
|
</div>
|
|
1234
|
+
|
|
682
1235
|
</div>
|
|
683
|
-
|
|
1236
|
+
|
|
1237
|
+
<!-- Footer -->
|
|
1238
|
+
<footer class="footer">
|
|
1239
|
+
<span>Tab: cycle | 1-0: jump | Ctrl+B: sidebar | Ctrl+K: commands | ?: help | Ctrl+Q: quit</span>
|
|
1240
|
+
<span id="footer-status">Ready</span>
|
|
1241
|
+
</footer>
|
|
1242
|
+
</main>
|
|
1243
|
+
</div>
|
|
1244
|
+
</div>
|
|
1245
|
+
|
|
1246
|
+
<!-- Command Palette -->
|
|
1247
|
+
<div class="command-palette-overlay" id="command-palette-overlay" onclick="closeCommandPalette(event)">
|
|
1248
|
+
<div class="command-palette" onclick="event.stopPropagation()">
|
|
1249
|
+
<input type="text" class="command-palette-input" id="command-input" placeholder="Type a command..." oninput="filterCommands()" onkeydown="handleCommandKey(event)">
|
|
1250
|
+
<div class="command-palette-results" id="command-results"></div>
|
|
684
1251
|
</div>
|
|
1252
|
+
</div>
|
|
685
1253
|
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
1254
|
+
<!-- Help Modal -->
|
|
1255
|
+
<div class="help-overlay" id="help-overlay" onclick="closeHelp(event)">
|
|
1256
|
+
<div class="help-modal" onclick="event.stopPropagation()">
|
|
1257
|
+
<div class="help-header">
|
|
1258
|
+
<span class="help-title">⌨️ Keyboard Shortcuts</span>
|
|
1259
|
+
<button class="help-close" onclick="toggleHelp()">×</button>
|
|
1260
|
+
</div>
|
|
1261
|
+
<div class="help-content">
|
|
1262
|
+
<div class="help-section">
|
|
1263
|
+
<div class="help-section-title">Global</div>
|
|
1264
|
+
<div class="shortcut-row">
|
|
1265
|
+
<span class="shortcut-keys">1-0</span>
|
|
1266
|
+
<span class="shortcut-desc">Jump to view</span>
|
|
693
1267
|
</div>
|
|
694
|
-
<div class="
|
|
695
|
-
<
|
|
1268
|
+
<div class="shortcut-row">
|
|
1269
|
+
<span class="shortcut-keys">Tab</span>
|
|
1270
|
+
<span class="shortcut-desc">Cycle views</span>
|
|
696
1271
|
</div>
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
<div class="sidebar-section">
|
|
701
|
-
<h3>Stats</h3>
|
|
702
|
-
<div class="stats-grid">
|
|
703
|
-
<div class="stat">
|
|
704
|
-
<div class="stat-value green" id="stat-pass">-</div>
|
|
705
|
-
<div class="stat-label">Passing</div>
|
|
1272
|
+
<div class="shortcut-row">
|
|
1273
|
+
<span class="shortcut-keys">Ctrl+K</span>
|
|
1274
|
+
<span class="shortcut-desc">Command palette</span>
|
|
706
1275
|
</div>
|
|
707
|
-
<div class="
|
|
708
|
-
<
|
|
709
|
-
<
|
|
1276
|
+
<div class="shortcut-row">
|
|
1277
|
+
<span class="shortcut-keys">Ctrl+B</span>
|
|
1278
|
+
<span class="shortcut-desc">Toggle sidebar</span>
|
|
710
1279
|
</div>
|
|
711
|
-
<div class="
|
|
712
|
-
<
|
|
713
|
-
<
|
|
1280
|
+
<div class="shortcut-row">
|
|
1281
|
+
<span class="shortcut-keys">?</span>
|
|
1282
|
+
<span class="shortcut-desc">Show help</span>
|
|
714
1283
|
</div>
|
|
715
|
-
<div class="
|
|
716
|
-
<
|
|
717
|
-
<
|
|
1284
|
+
<div class="shortcut-row">
|
|
1285
|
+
<span class="shortcut-keys">Esc</span>
|
|
1286
|
+
<span class="shortcut-desc">Go back / Close</span>
|
|
718
1287
|
</div>
|
|
719
1288
|
</div>
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
</button>
|
|
731
|
-
<button class="action-btn secondary" onclick="refreshAll()">
|
|
732
|
-
<span class="icon">🔄</span> Refresh All
|
|
733
|
-
</button>
|
|
734
|
-
</div>
|
|
735
|
-
</div>
|
|
736
|
-
|
|
737
|
-
<div class="sidebar-section">
|
|
738
|
-
<h3>Links</h3>
|
|
739
|
-
<div class="links">
|
|
740
|
-
<a class="link" id="link-app" href="#" target="_blank">
|
|
741
|
-
<span>App (Direct)</span>
|
|
742
|
-
<span class="url" id="link-app-port">:3000</span>
|
|
743
|
-
</a>
|
|
744
|
-
<a class="link" href="http://localhost:8080" target="_blank">
|
|
745
|
-
<span>Database Admin</span>
|
|
746
|
-
<span class="url">:8080</span>
|
|
747
|
-
</a>
|
|
748
|
-
<a class="link" href="#" onclick="event.preventDefault(); alert('Connect with: postgres://postgres:postgres@localhost:5433/app')">
|
|
749
|
-
<span>PostgreSQL</span>
|
|
750
|
-
<span class="url">:5433</span>
|
|
751
|
-
</a>
|
|
1289
|
+
<div class="help-section">
|
|
1290
|
+
<div class="help-section-title">Lists</div>
|
|
1291
|
+
<div class="shortcut-row">
|
|
1292
|
+
<span class="shortcut-keys">j/k</span>
|
|
1293
|
+
<span class="shortcut-desc">Navigate list</span>
|
|
1294
|
+
</div>
|
|
1295
|
+
<div class="shortcut-row">
|
|
1296
|
+
<span class="shortcut-keys">Enter</span>
|
|
1297
|
+
<span class="shortcut-desc">Select item</span>
|
|
1298
|
+
</div>
|
|
752
1299
|
</div>
|
|
753
1300
|
</div>
|
|
754
1301
|
</div>
|
|
755
1302
|
</div>
|
|
756
1303
|
|
|
757
1304
|
<script>
|
|
1305
|
+
// State
|
|
758
1306
|
let ws;
|
|
759
|
-
|
|
760
|
-
let
|
|
1307
|
+
let currentView = 'projects';
|
|
1308
|
+
let sidebarCollapsed = false;
|
|
761
1309
|
let reconnectAttempts = 0;
|
|
762
|
-
|
|
763
|
-
let
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
1310
|
+
const logs = { app: [], test: [], git: [], system: [] };
|
|
1311
|
+
let currentLogType = 'app';
|
|
1312
|
+
let appPort = 5001;
|
|
1313
|
+
|
|
1314
|
+
const views = ['projects', 'tasks', 'chat', 'agents', 'preview', 'logs', 'github', 'health', 'router', 'settings'];
|
|
1315
|
+
const viewTitles = {
|
|
1316
|
+
projects: '📁 Projects',
|
|
1317
|
+
tasks: '📋 Tasks',
|
|
1318
|
+
chat: '💬 Chat',
|
|
1319
|
+
agents: '🤖 Agents',
|
|
1320
|
+
preview: '👁 Preview',
|
|
1321
|
+
logs: '📜 Logs',
|
|
1322
|
+
github: '🐙 GitHub',
|
|
1323
|
+
health: '💚 Health',
|
|
1324
|
+
router: '🔀 Router',
|
|
1325
|
+
settings: '⚙️ Settings'
|
|
1326
|
+
};
|
|
1327
|
+
|
|
1328
|
+
const commands = [
|
|
1329
|
+
{ id: 'view:projects', name: 'Go to Projects', desc: 'View all projects', shortcut: '1' },
|
|
1330
|
+
{ id: 'view:tasks', name: 'Go to Tasks', desc: 'View task board', shortcut: '2' },
|
|
1331
|
+
{ id: 'view:chat', name: 'Go to Chat', desc: 'Open chat', shortcut: '3' },
|
|
1332
|
+
{ id: 'view:agents', name: 'Go to Agents', desc: 'View agents', shortcut: '4' },
|
|
1333
|
+
{ id: 'view:preview', name: 'Go to Preview', desc: 'App preview', shortcut: '5' },
|
|
1334
|
+
{ id: 'view:logs', name: 'Go to Logs', desc: 'View logs', shortcut: '6' },
|
|
1335
|
+
{ id: 'view:github', name: 'Go to GitHub', desc: 'View GitHub', shortcut: '7' },
|
|
1336
|
+
{ id: 'view:health', name: 'Go to Health', desc: 'System health', shortcut: '8' },
|
|
1337
|
+
{ id: 'view:router', name: 'Go to Router', desc: 'Model router', shortcut: '9' },
|
|
1338
|
+
{ id: 'view:settings', name: 'Go to Settings', desc: 'Settings', shortcut: '0' },
|
|
1339
|
+
{ id: 'cmd:run-tests', name: 'Run Tests', desc: 'Run test suite' },
|
|
1340
|
+
{ id: 'cmd:build', name: 'Build Project', desc: 'Build current project' },
|
|
1341
|
+
{ id: 'cmd:refresh', name: 'Refresh All', desc: 'Refresh all data' },
|
|
1342
|
+
];
|
|
1343
|
+
|
|
1344
|
+
// View Switching
|
|
1345
|
+
function switchView(view) {
|
|
1346
|
+
currentView = view;
|
|
1347
|
+
|
|
1348
|
+
// Update nav items
|
|
1349
|
+
document.querySelectorAll('.nav-item').forEach(item => {
|
|
1350
|
+
item.classList.toggle('active', item.dataset.view === view);
|
|
1351
|
+
});
|
|
1352
|
+
|
|
1353
|
+
// Update panels
|
|
1354
|
+
document.querySelectorAll('.panel').forEach(panel => {
|
|
1355
|
+
panel.classList.toggle('active', panel.id === `panel-${view}`);
|
|
772
1356
|
});
|
|
1357
|
+
|
|
1358
|
+
// Update titles
|
|
1359
|
+
document.getElementById('view-title').textContent = viewTitles[view];
|
|
1360
|
+
document.getElementById('current-view-title').textContent = viewTitles[view].split(' ').slice(1).join(' ');
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
// Keyboard Shortcuts
|
|
1364
|
+
document.addEventListener('keydown', (e) => {
|
|
1365
|
+
// Check if typing in input
|
|
1366
|
+
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') {
|
|
1367
|
+
if (e.key === 'Escape') {
|
|
1368
|
+
e.target.blur();
|
|
1369
|
+
}
|
|
1370
|
+
return;
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
// Ctrl+K - Command Palette
|
|
1374
|
+
if (e.ctrlKey && e.key === 'k') {
|
|
1375
|
+
e.preventDefault();
|
|
1376
|
+
toggleCommandPalette();
|
|
1377
|
+
return;
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
// Ctrl+B - Toggle Sidebar
|
|
1381
|
+
if (e.ctrlKey && e.key === 'b') {
|
|
1382
|
+
e.preventDefault();
|
|
1383
|
+
toggleSidebar();
|
|
1384
|
+
return;
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
// ? - Help
|
|
1388
|
+
if (e.key === '?') {
|
|
1389
|
+
e.preventDefault();
|
|
1390
|
+
toggleHelp();
|
|
1391
|
+
return;
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
// Escape - Close overlays
|
|
1395
|
+
if (e.key === 'Escape') {
|
|
1396
|
+
closeAllOverlays();
|
|
1397
|
+
return;
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
// Number keys 1-0 for views
|
|
1401
|
+
const num = parseInt(e.key);
|
|
1402
|
+
if (!isNaN(num)) {
|
|
1403
|
+
const viewIndex = num === 0 ? 9 : num - 1;
|
|
1404
|
+
if (viewIndex < views.length) {
|
|
1405
|
+
switchView(views[viewIndex]);
|
|
1406
|
+
}
|
|
1407
|
+
return;
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
// Tab - Cycle views
|
|
1411
|
+
if (e.key === 'Tab' && !e.ctrlKey && !e.shiftKey) {
|
|
1412
|
+
e.preventDefault();
|
|
1413
|
+
const currentIndex = views.indexOf(currentView);
|
|
1414
|
+
const nextIndex = (currentIndex + 1) % views.length;
|
|
1415
|
+
switchView(views[nextIndex]);
|
|
1416
|
+
}
|
|
773
1417
|
});
|
|
774
1418
|
|
|
1419
|
+
// Command Palette
|
|
1420
|
+
function toggleCommandPalette() {
|
|
1421
|
+
const overlay = document.getElementById('command-palette-overlay');
|
|
1422
|
+
const isActive = overlay.classList.toggle('active');
|
|
1423
|
+
if (isActive) {
|
|
1424
|
+
document.getElementById('command-input').focus();
|
|
1425
|
+
renderCommands();
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
function closeCommandPalette(e) {
|
|
1430
|
+
if (e.target === e.currentTarget) {
|
|
1431
|
+
document.getElementById('command-palette-overlay').classList.remove('active');
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
function filterCommands() {
|
|
1436
|
+
renderCommands();
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
function renderCommands() {
|
|
1440
|
+
const query = document.getElementById('command-input').value.toLowerCase();
|
|
1441
|
+
const filtered = commands.filter(cmd =>
|
|
1442
|
+
cmd.name.toLowerCase().includes(query) ||
|
|
1443
|
+
cmd.desc.toLowerCase().includes(query)
|
|
1444
|
+
);
|
|
1445
|
+
|
|
1446
|
+
document.getElementById('command-results').innerHTML = filtered.map((cmd, i) => `
|
|
1447
|
+
<div class="command-item ${i === 0 ? 'selected' : ''}" onclick="executeCommand('${cmd.id}')">
|
|
1448
|
+
<div>
|
|
1449
|
+
<div class="command-name">${cmd.name}</div>
|
|
1450
|
+
<div class="command-desc">${cmd.desc}</div>
|
|
1451
|
+
</div>
|
|
1452
|
+
${cmd.shortcut ? `<span class="command-shortcut">${cmd.shortcut}</span>` : ''}
|
|
1453
|
+
</div>
|
|
1454
|
+
`).join('');
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
function handleCommandKey(e) {
|
|
1458
|
+
if (e.key === 'Enter') {
|
|
1459
|
+
const selected = document.querySelector('.command-item.selected');
|
|
1460
|
+
if (selected) {
|
|
1461
|
+
selected.click();
|
|
1462
|
+
}
|
|
1463
|
+
} else if (e.key === 'Escape') {
|
|
1464
|
+
document.getElementById('command-palette-overlay').classList.remove('active');
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
function executeCommand(id) {
|
|
1469
|
+
document.getElementById('command-palette-overlay').classList.remove('active');
|
|
1470
|
+
document.getElementById('command-input').value = '';
|
|
1471
|
+
|
|
1472
|
+
if (id.startsWith('view:')) {
|
|
1473
|
+
switchView(id.replace('view:', ''));
|
|
1474
|
+
} else if (id === 'cmd:run-tests') {
|
|
1475
|
+
runTests();
|
|
1476
|
+
} else if (id === 'cmd:refresh') {
|
|
1477
|
+
refreshAll();
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
// Sidebar Toggle
|
|
1482
|
+
function toggleSidebar() {
|
|
1483
|
+
sidebarCollapsed = !sidebarCollapsed;
|
|
1484
|
+
document.getElementById('sidebar').classList.toggle('collapsed', sidebarCollapsed);
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
// Help Modal
|
|
1488
|
+
function toggleHelp() {
|
|
1489
|
+
document.getElementById('help-overlay').classList.toggle('active');
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
function closeHelp(e) {
|
|
1493
|
+
if (e.target === e.currentTarget) {
|
|
1494
|
+
document.getElementById('help-overlay').classList.remove('active');
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
function closeAllOverlays() {
|
|
1499
|
+
document.getElementById('command-palette-overlay').classList.remove('active');
|
|
1500
|
+
document.getElementById('help-overlay').classList.remove('active');
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
// WebSocket Connection
|
|
775
1504
|
function connect() {
|
|
776
1505
|
ws = new WebSocket(`ws://${location.host}`);
|
|
777
1506
|
|
|
778
1507
|
ws.onopen = () => {
|
|
779
1508
|
console.log('Connected to TLC server');
|
|
780
1509
|
reconnectAttempts = 0;
|
|
781
|
-
|
|
782
|
-
addLog('
|
|
1510
|
+
updateConnectionStatus(true);
|
|
1511
|
+
addLog('system', 'Connected to TLC Dashboard', 'success');
|
|
783
1512
|
};
|
|
784
1513
|
|
|
785
1514
|
ws.onclose = () => {
|
|
786
1515
|
console.log('Disconnected');
|
|
787
|
-
|
|
1516
|
+
updateConnectionStatus(false);
|
|
788
1517
|
if (reconnectAttempts < 10) {
|
|
789
1518
|
setTimeout(connect, Math.min(1000 * Math.pow(2, reconnectAttempts), 30000));
|
|
790
1519
|
reconnectAttempts++;
|
|
@@ -801,15 +1530,13 @@
|
|
|
801
1530
|
switch(msg.type) {
|
|
802
1531
|
case 'init':
|
|
803
1532
|
Object.assign(logs, msg.data.logs || {});
|
|
804
|
-
if (msg.data.appPort)
|
|
805
|
-
updateAppPort(msg.data.appPort);
|
|
806
|
-
}
|
|
1533
|
+
if (msg.data.appPort) updateAppPort(msg.data.appPort);
|
|
807
1534
|
renderLogs();
|
|
808
1535
|
break;
|
|
809
1536
|
case 'app-start':
|
|
810
1537
|
if (msg.data.port) {
|
|
811
1538
|
updateAppPort(msg.data.port);
|
|
812
|
-
|
|
1539
|
+
reloadPreview();
|
|
813
1540
|
}
|
|
814
1541
|
break;
|
|
815
1542
|
case 'app-log':
|
|
@@ -817,8 +1544,6 @@
|
|
|
817
1544
|
break;
|
|
818
1545
|
case 'test-output':
|
|
819
1546
|
addLog('test', msg.data.data, msg.data.stream === 'stderr' ? 'error' : '');
|
|
820
|
-
document.getElementById('test-results').innerHTML +=
|
|
821
|
-
`<div class="log-line ${msg.data.stream === 'stderr' ? 'error' : ''}">${escapeHtml(msg.data.data)}</div>`;
|
|
822
1547
|
break;
|
|
823
1548
|
case 'test-complete':
|
|
824
1549
|
const result = msg.data.exitCode === 0 ? 'passed' : 'failed';
|
|
@@ -827,23 +1552,19 @@
|
|
|
827
1552
|
break;
|
|
828
1553
|
case 'git-activity':
|
|
829
1554
|
addLog('git', msg.data.entry, 'info');
|
|
830
|
-
|
|
831
|
-
break;
|
|
832
|
-
case 'bug-created':
|
|
833
|
-
addLog('app', `Bug ${msg.data.bugId} created`, 'warn');
|
|
834
|
-
refreshBugs();
|
|
835
|
-
refreshStats();
|
|
1555
|
+
refreshGitHub();
|
|
836
1556
|
break;
|
|
837
1557
|
}
|
|
838
1558
|
}
|
|
839
1559
|
|
|
840
|
-
function
|
|
1560
|
+
function updateConnectionStatus(connected) {
|
|
841
1561
|
const dot = document.getElementById('status-dot');
|
|
842
1562
|
const text = document.getElementById('status-text');
|
|
843
|
-
dot.className = 'dot ' + (connected ? '
|
|
844
|
-
text.textContent = connected ? '
|
|
1563
|
+
dot.className = 'status-dot ' + (connected ? 'connected' : 'disconnected');
|
|
1564
|
+
text.textContent = connected ? 'Connected' : 'Disconnected';
|
|
845
1565
|
}
|
|
846
1566
|
|
|
1567
|
+
// Logs
|
|
847
1568
|
function detectLogLevel(text) {
|
|
848
1569
|
if (/error|fail|exception/i.test(text)) return 'error';
|
|
849
1570
|
if (/warn/i.test(text)) return 'warn';
|
|
@@ -873,7 +1594,7 @@
|
|
|
873
1594
|
|
|
874
1595
|
function showLogs(type) {
|
|
875
1596
|
currentLogType = type;
|
|
876
|
-
document.querySelectorAll('.logs-
|
|
1597
|
+
document.querySelectorAll('.logs-filter button').forEach(b => {
|
|
877
1598
|
b.classList.toggle('active', b.dataset.logtype === type);
|
|
878
1599
|
});
|
|
879
1600
|
renderLogs();
|
|
@@ -885,8 +1606,8 @@
|
|
|
885
1606
|
return div.innerHTML;
|
|
886
1607
|
}
|
|
887
1608
|
|
|
1609
|
+
// API Functions
|
|
888
1610
|
async function runTests() {
|
|
889
|
-
document.getElementById('test-results').innerHTML = '<div class="log-line info">Running tests...</div>';
|
|
890
1611
|
addLog('test', '--- Running tests ---', 'info');
|
|
891
1612
|
try {
|
|
892
1613
|
await fetch('/api/test', { method: 'POST' });
|
|
@@ -895,328 +1616,164 @@
|
|
|
895
1616
|
}
|
|
896
1617
|
}
|
|
897
1618
|
|
|
898
|
-
async function
|
|
899
|
-
document.getElementById('test-results').innerHTML = '<div class="log-line info">Running Playwright tests...</div>';
|
|
900
|
-
addLog('test', '--- Running Playwright ---', 'info');
|
|
901
|
-
try {
|
|
902
|
-
await fetch('/api/playwright', { method: 'POST' });
|
|
903
|
-
} catch (e) {
|
|
904
|
-
addLog('test', 'Playwright not configured. Add playwright container to docker-compose.', 'warn');
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
// Bug image handling
|
|
909
|
-
let bugImages = [];
|
|
910
|
-
|
|
911
|
-
function setupImageHandlers() {
|
|
912
|
-
const dropZone = document.getElementById('image-drop-zone');
|
|
913
|
-
|
|
914
|
-
// Paste handler (global)
|
|
915
|
-
document.addEventListener('paste', (e) => {
|
|
916
|
-
const items = e.clipboardData?.items;
|
|
917
|
-
if (!items) return;
|
|
918
|
-
|
|
919
|
-
for (const item of items) {
|
|
920
|
-
if (item.type.startsWith('image/')) {
|
|
921
|
-
e.preventDefault();
|
|
922
|
-
const file = item.getAsFile();
|
|
923
|
-
if (file) addImage(file);
|
|
924
|
-
}
|
|
925
|
-
}
|
|
926
|
-
});
|
|
927
|
-
|
|
928
|
-
// Drag & drop
|
|
929
|
-
dropZone.addEventListener('dragover', (e) => {
|
|
930
|
-
e.preventDefault();
|
|
931
|
-
dropZone.classList.add('dragover');
|
|
932
|
-
});
|
|
933
|
-
dropZone.addEventListener('dragleave', () => {
|
|
934
|
-
dropZone.classList.remove('dragover');
|
|
935
|
-
});
|
|
936
|
-
dropZone.addEventListener('drop', (e) => {
|
|
937
|
-
e.preventDefault();
|
|
938
|
-
dropZone.classList.remove('dragover');
|
|
939
|
-
const files = e.dataTransfer.files;
|
|
940
|
-
for (const file of files) {
|
|
941
|
-
if (file.type.startsWith('image/')) addImage(file);
|
|
942
|
-
}
|
|
943
|
-
});
|
|
944
|
-
}
|
|
945
|
-
|
|
946
|
-
function handleImageSelect(e) {
|
|
947
|
-
const files = e.target.files;
|
|
948
|
-
for (const file of files) {
|
|
949
|
-
if (file.type.startsWith('image/')) addImage(file);
|
|
950
|
-
}
|
|
951
|
-
e.target.value = ''; // Reset for re-selection
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
function addImage(file) {
|
|
955
|
-
const reader = new FileReader();
|
|
956
|
-
reader.onload = (e) => {
|
|
957
|
-
bugImages.push({ name: file.name, data: e.target.result });
|
|
958
|
-
renderImagePreviews();
|
|
959
|
-
};
|
|
960
|
-
reader.readAsDataURL(file);
|
|
961
|
-
}
|
|
962
|
-
|
|
963
|
-
function removeImage(index) {
|
|
964
|
-
bugImages.splice(index, 1);
|
|
965
|
-
renderImagePreviews();
|
|
966
|
-
}
|
|
967
|
-
|
|
968
|
-
function clearImages() {
|
|
969
|
-
bugImages = [];
|
|
970
|
-
renderImagePreviews();
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
function renderImagePreviews() {
|
|
974
|
-
const container = document.getElementById('image-previews');
|
|
975
|
-
const hint = document.getElementById('drop-hint');
|
|
976
|
-
const clearBtn = document.getElementById('clear-images-btn');
|
|
977
|
-
|
|
978
|
-
if (bugImages.length === 0) {
|
|
979
|
-
container.innerHTML = '';
|
|
980
|
-
hint.style.display = 'block';
|
|
981
|
-
clearBtn.style.display = 'none';
|
|
982
|
-
return;
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
hint.style.display = 'none';
|
|
986
|
-
clearBtn.style.display = 'inline-block';
|
|
987
|
-
container.innerHTML = bugImages.map((img, i) => `
|
|
988
|
-
<div class="image-preview">
|
|
989
|
-
<img src="${img.data}" alt="${img.name}" title="${img.name}">
|
|
990
|
-
<button class="remove-btn" onclick="event.stopPropagation(); removeImage(${i})">×</button>
|
|
991
|
-
</div>
|
|
992
|
-
`).join('');
|
|
993
|
-
}
|
|
994
|
-
|
|
995
|
-
async function submitBug() {
|
|
996
|
-
const desc = document.getElementById('bug-desc').value.trim();
|
|
997
|
-
const severity = document.getElementById('bug-severity').value;
|
|
998
|
-
if (!desc) { alert('Please describe the bug'); return; }
|
|
999
|
-
|
|
1619
|
+
async function refreshStats() {
|
|
1000
1620
|
try {
|
|
1001
|
-
const res = await fetch('/api/
|
|
1002
|
-
method: 'POST',
|
|
1003
|
-
headers: { 'Content-Type': 'application/json' },
|
|
1004
|
-
body: JSON.stringify({
|
|
1005
|
-
description: desc,
|
|
1006
|
-
severity,
|
|
1007
|
-
images: bugImages.map(img => img.data)
|
|
1008
|
-
})
|
|
1009
|
-
});
|
|
1621
|
+
const res = await fetch('/api/status');
|
|
1010
1622
|
const data = await res.json();
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
refreshBugs();
|
|
1623
|
+
document.getElementById('stat-passing').textContent = data.testsPass || 0;
|
|
1624
|
+
document.getElementById('stat-failing').textContent = data.testsFail || 0;
|
|
1625
|
+
if (data.coverage) {
|
|
1626
|
+
document.getElementById('stat-coverage').textContent = data.coverage + '%';
|
|
1016
1627
|
}
|
|
1017
1628
|
} catch (e) {
|
|
1018
|
-
|
|
1629
|
+
console.error('Failed to load stats:', e);
|
|
1019
1630
|
}
|
|
1020
1631
|
}
|
|
1021
1632
|
|
|
1022
|
-
async function
|
|
1633
|
+
async function refreshTasks() {
|
|
1023
1634
|
try {
|
|
1024
|
-
const res = await fetch('/api/
|
|
1635
|
+
const res = await fetch('/api/tasks');
|
|
1025
1636
|
const data = await res.json();
|
|
1026
1637
|
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
.
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
<div class="
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1638
|
+
const pending = data.filter(t => t.status === 'pending');
|
|
1639
|
+
const inProgress = data.filter(t => t.status === 'in_progress');
|
|
1640
|
+
const completed = data.filter(t => t.status === 'completed');
|
|
1641
|
+
|
|
1642
|
+
document.getElementById('tasks-pending').innerHTML = pending.map(t => `
|
|
1643
|
+
<div class="task-item">
|
|
1644
|
+
<div class="task-title">${escapeHtml(t.title)}</div>
|
|
1645
|
+
<div class="task-meta">${t.owner || 'Unassigned'}</div>
|
|
1646
|
+
</div>
|
|
1647
|
+
`).join('') || '<div class="empty-state"><p>No pending tasks</p></div>';
|
|
1648
|
+
|
|
1649
|
+
document.getElementById('tasks-in-progress').innerHTML = inProgress.map(t => `
|
|
1650
|
+
<div class="task-item">
|
|
1651
|
+
<div class="task-title">${escapeHtml(t.title)}</div>
|
|
1652
|
+
<div class="task-meta">@${t.owner || 'unknown'}</div>
|
|
1653
|
+
</div>
|
|
1654
|
+
`).join('') || '<div class="empty-state"><p>No tasks in progress</p></div>';
|
|
1655
|
+
|
|
1656
|
+
document.getElementById('tasks-completed').innerHTML = completed.map(t => `
|
|
1657
|
+
<div class="task-item">
|
|
1658
|
+
<div class="task-title">${escapeHtml(t.title)}</div>
|
|
1659
|
+
<div class="task-meta">@${t.owner || 'unknown'}</div>
|
|
1660
|
+
</div>
|
|
1661
|
+
`).join('') || '<div class="empty-state"><p>No completed tasks</p></div>';
|
|
1046
1662
|
} catch (e) {
|
|
1047
|
-
console.error('Failed to load
|
|
1663
|
+
console.error('Failed to load tasks:', e);
|
|
1048
1664
|
}
|
|
1049
1665
|
}
|
|
1050
1666
|
|
|
1051
|
-
async function
|
|
1667
|
+
async function refreshGitHub() {
|
|
1052
1668
|
try {
|
|
1053
|
-
const res = await fetch('/api/
|
|
1669
|
+
const res = await fetch('/api/changelog');
|
|
1054
1670
|
const data = await res.json();
|
|
1055
1671
|
|
|
1056
|
-
if (data.
|
|
1057
|
-
document.getElementById('
|
|
1058
|
-
<div class="
|
|
1059
|
-
<
|
|
1060
|
-
<
|
|
1672
|
+
if (data.commits?.length) {
|
|
1673
|
+
document.getElementById('commits-list').innerHTML = data.commits.slice(0, 10).map(c => `
|
|
1674
|
+
<div class="commit-item">
|
|
1675
|
+
<span class="commit-hash">${c.hash?.slice(0, 7) || '---'}</span>
|
|
1676
|
+
<div class="commit-message">${escapeHtml(c.message || '')}</div>
|
|
1677
|
+
<div class="commit-meta">${c.author || ''} • ${c.date || ''}</div>
|
|
1061
1678
|
</div>
|
|
1062
1679
|
`).join('');
|
|
1063
1680
|
}
|
|
1064
1681
|
} catch (e) {
|
|
1065
|
-
console.error('Failed to load
|
|
1682
|
+
console.error('Failed to load GitHub data:', e);
|
|
1066
1683
|
}
|
|
1067
1684
|
}
|
|
1068
1685
|
|
|
1069
|
-
async function
|
|
1686
|
+
async function refreshRouter() {
|
|
1070
1687
|
try {
|
|
1071
|
-
const res = await fetch('/api/
|
|
1688
|
+
const res = await fetch('/api/router/status');
|
|
1072
1689
|
const data = await res.json();
|
|
1073
1690
|
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1691
|
+
if (data.providers) {
|
|
1692
|
+
document.getElementById('providers-list').innerHTML = Object.entries(data.providers).map(([name, p]) => {
|
|
1693
|
+
const isLocal = p.type === 'cli' && p.detected;
|
|
1694
|
+
return `
|
|
1695
|
+
<div class="provider-row">
|
|
1696
|
+
<span class="provider-indicator ${p.healthy !== false ? 'healthy' : 'unhealthy'}"></span>
|
|
1697
|
+
<span class="provider-name">${name}</span>
|
|
1698
|
+
<span class="provider-version">${p.version || ''}</span>
|
|
1699
|
+
<span class="provider-badge ${isLocal ? 'local' : 'devserver'}">${isLocal ? 'local' : 'devserver'}</span>
|
|
1700
|
+
</div>
|
|
1701
|
+
`;
|
|
1702
|
+
}).join('');
|
|
1081
1703
|
}
|
|
1082
1704
|
|
|
1083
|
-
if (data.
|
|
1084
|
-
document.getElementById('
|
|
1085
|
-
<div class="
|
|
1086
|
-
<
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1705
|
+
if (data.capabilities) {
|
|
1706
|
+
document.getElementById('routing-table').innerHTML = Object.entries(data.capabilities).map(([cap, info]) => `
|
|
1707
|
+
<div class="routing-row">
|
|
1708
|
+
<span class="routing-capability">${cap}</span>
|
|
1709
|
+
<span class="routing-providers">→ ${info.providers.map(p => {
|
|
1710
|
+
const provider = data.providers[p];
|
|
1711
|
+
const isLocal = provider?.type === 'cli' && provider?.detected;
|
|
1712
|
+
return `<span class="${isLocal ? 'local' : 'devserver'}">${p}</span>`;
|
|
1713
|
+
}).join(', ')}</span>
|
|
1091
1714
|
</div>
|
|
1092
1715
|
`).join('');
|
|
1093
1716
|
}
|
|
1094
1717
|
} catch (e) {
|
|
1095
|
-
console.error('Failed to load
|
|
1718
|
+
console.error('Failed to load router status:', e);
|
|
1096
1719
|
}
|
|
1097
1720
|
}
|
|
1098
1721
|
|
|
1099
|
-
async function
|
|
1722
|
+
async function refreshHealth() {
|
|
1100
1723
|
try {
|
|
1101
|
-
const res = await fetch('/api/
|
|
1724
|
+
const res = await fetch('/api/health');
|
|
1102
1725
|
const data = await res.json();
|
|
1103
1726
|
|
|
1104
|
-
if (data.
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
</
|
|
1727
|
+
if (data.memory) document.getElementById('health-memory').textContent = Math.round(data.memory / 1024 / 1024) + ' MB';
|
|
1728
|
+
if (data.cpu) document.getElementById('health-cpu').textContent = data.cpu + '%';
|
|
1729
|
+
if (data.disk) document.getElementById('health-disk').textContent = data.disk + '%';
|
|
1730
|
+
|
|
1731
|
+
if (data.services) {
|
|
1732
|
+
document.getElementById('services-list').innerHTML = data.services.map(s => `
|
|
1733
|
+
<div class="service-item">
|
|
1734
|
+
<span class="service-name">${s.name}</span>
|
|
1735
|
+
<span class="service-status ${s.state}">${s.state === 'running' ? '● Running' : '○ Stopped'} ${s.port ? ':' + s.port : ''}</span>
|
|
1112
1736
|
</div>
|
|
1113
1737
|
`).join('');
|
|
1114
1738
|
}
|
|
1115
1739
|
} catch (e) {
|
|
1116
|
-
console.error('Failed to load
|
|
1740
|
+
console.error('Failed to load health:', e);
|
|
1117
1741
|
}
|
|
1118
1742
|
}
|
|
1119
1743
|
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
document.getElementById('stat-pass').textContent = data.testsPass || 0;
|
|
1126
|
-
document.getElementById('stat-fail').textContent = data.testsFail || 0;
|
|
1127
|
-
document.getElementById('stat-tasks').textContent = data.tasks || 0;
|
|
1128
|
-
document.getElementById('stat-bugs').textContent = data.bugsOpen || 0;
|
|
1129
|
-
} catch (e) {
|
|
1130
|
-
console.error('Failed to load stats:', e);
|
|
1131
|
-
}
|
|
1132
|
-
}
|
|
1133
|
-
|
|
1134
|
-
function refreshAll() {
|
|
1135
|
-
refreshPlan();
|
|
1136
|
-
refreshTests();
|
|
1137
|
-
refreshBugs();
|
|
1138
|
-
refreshChangelog();
|
|
1139
|
-
refreshStats();
|
|
1140
|
-
refreshProgress();
|
|
1744
|
+
// Preview Functions
|
|
1745
|
+
function updateAppPort(port) {
|
|
1746
|
+
appPort = port;
|
|
1747
|
+
document.getElementById('preview-url').textContent = `localhost:${port}`;
|
|
1141
1748
|
}
|
|
1142
1749
|
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
const iframe = document.getElementById('app-iframe');
|
|
1750
|
+
function reloadPreview() {
|
|
1751
|
+
const iframe = document.getElementById('preview-iframe');
|
|
1146
1752
|
iframe.src = iframe.src;
|
|
1147
1753
|
}
|
|
1148
1754
|
|
|
1149
|
-
function
|
|
1755
|
+
function openInNewTab() {
|
|
1150
1756
|
window.open(`http://localhost:${appPort}`, '_blank');
|
|
1151
1757
|
}
|
|
1152
1758
|
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
// Use html2canvas-like approach via the iframe
|
|
1156
|
-
const iframe = document.getElementById('app-iframe');
|
|
1157
|
-
|
|
1158
|
-
// For cross-origin iframes, we can't capture directly
|
|
1159
|
-
// Instead, prompt user to use browser screenshot or provide manual upload
|
|
1160
|
-
const desc = prompt('Describe the bug (screenshot will be from current app view):');
|
|
1161
|
-
if (!desc) return;
|
|
1162
|
-
|
|
1163
|
-
// Create bug with current URL
|
|
1164
|
-
const res = await fetch('/api/bug', {
|
|
1165
|
-
method: 'POST',
|
|
1166
|
-
headers: { 'Content-Type': 'application/json' },
|
|
1167
|
-
body: JSON.stringify({
|
|
1168
|
-
description: desc,
|
|
1169
|
-
severity: 'medium',
|
|
1170
|
-
url: iframe.src
|
|
1171
|
-
})
|
|
1172
|
-
});
|
|
1173
|
-
const data = await res.json();
|
|
1174
|
-
if (data.success) {
|
|
1175
|
-
alert(`Bug ${data.bugId} created!\n\nTip: Use browser's screenshot tool (Cmd+Shift+4 on Mac, Win+Shift+S on Windows) to capture and attach manually.`);
|
|
1176
|
-
refreshBugs();
|
|
1177
|
-
}
|
|
1178
|
-
} catch (e) {
|
|
1179
|
-
alert('Failed to create bug: ' + e.message);
|
|
1180
|
-
}
|
|
1181
|
-
}
|
|
1182
|
-
|
|
1183
|
-
async function refreshProgress() {
|
|
1184
|
-
try {
|
|
1185
|
-
const res = await fetch('/api/progress');
|
|
1186
|
-
const data = await res.json();
|
|
1187
|
-
|
|
1188
|
-
const phaseName = `Phase ${data.phase || '?'}: ${data.phaseName || 'Unknown'}`;
|
|
1189
|
-
const pct = data.progress || 0;
|
|
1190
|
-
|
|
1191
|
-
document.getElementById('progress-phase').textContent = phaseName;
|
|
1192
|
-
document.getElementById('progress-pct').textContent = `${pct}%`;
|
|
1193
|
-
document.getElementById('progress-fill').style.width = `${pct}%`;
|
|
1194
|
-
} catch (e) {
|
|
1195
|
-
// Fallback to stats-based progress
|
|
1196
|
-
try {
|
|
1197
|
-
const res = await fetch('/api/status');
|
|
1198
|
-
const data = await res.json();
|
|
1199
|
-
const phaseName = `Phase ${data.phase || '?'}`;
|
|
1200
|
-
document.getElementById('progress-phase').textContent = phaseName;
|
|
1201
|
-
} catch (e2) {
|
|
1202
|
-
console.error('Failed to load progress:', e2);
|
|
1203
|
-
}
|
|
1204
|
-
}
|
|
1759
|
+
function captureScreenshot() {
|
|
1760
|
+
alert('Use your browser\'s screenshot tool (Cmd+Shift+4 on Mac, Win+Shift+S on Windows)');
|
|
1205
1761
|
}
|
|
1206
1762
|
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1763
|
+
// Refresh All
|
|
1764
|
+
function refreshAll() {
|
|
1765
|
+
refreshStats();
|
|
1766
|
+
refreshTasks();
|
|
1767
|
+
refreshGitHub();
|
|
1768
|
+
refreshRouter();
|
|
1769
|
+
refreshHealth();
|
|
1212
1770
|
}
|
|
1213
1771
|
|
|
1214
1772
|
// Initialize
|
|
1215
1773
|
connect();
|
|
1216
1774
|
refreshAll();
|
|
1217
|
-
setupImageHandlers();
|
|
1218
1775
|
setInterval(refreshStats, 30000);
|
|
1219
|
-
setInterval(
|
|
1776
|
+
setInterval(refreshHealth, 60000);
|
|
1220
1777
|
</script>
|
|
1221
1778
|
</body>
|
|
1222
1779
|
</html>
|