claude-task-viewer 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -32
- package/package.json +1 -1
- package/public/index.html +1294 -334
- package/server.js +36 -0
package/public/index.html
CHANGED
|
@@ -3,218 +3,1118 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>Claude
|
|
7
|
-
<
|
|
6
|
+
<title>Claude Tasks</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
|
+
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600&family=Playfair+Display:wght@400;500;600&display=swap" rel="stylesheet">
|
|
8
10
|
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
|
9
|
-
<script>
|
|
10
|
-
tailwind.config = {
|
|
11
|
-
darkMode: 'class',
|
|
12
|
-
theme: {
|
|
13
|
-
extend: {
|
|
14
|
-
colors: {
|
|
15
|
-
claude: {
|
|
16
|
-
orange: '#E86F33',
|
|
17
|
-
cream: '#F5F0E8'
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
</script>
|
|
24
|
-
<style id="theme-styles">
|
|
25
|
-
/* Light mode overrides */
|
|
26
|
-
body.light { background: #f8fafc !important; color: #1e293b !important; }
|
|
27
|
-
.light .bg-gray-900 { background: #ffffff !important; }
|
|
28
|
-
.light .bg-gray-950 { background: #f8fafc !important; }
|
|
29
|
-
.light .bg-gray-900\/50 { background: rgba(255,255,255,0.9) !important; }
|
|
30
|
-
.light .bg-gray-800 { background: #f1f5f9 !important; }
|
|
31
|
-
.light .bg-gray-800\/50 { background: rgba(241,245,249,0.7) !important; }
|
|
32
|
-
.light .bg-gray-700 { background: #e2e8f0 !important; }
|
|
33
|
-
.light .bg-gray-700\/50 { background: rgba(226,232,240,0.5) !important; }
|
|
34
|
-
.light .border-gray-800 { border-color: #e2e8f0 !important; }
|
|
35
|
-
.light .border-gray-700 { border-color: #cbd5e1 !important; }
|
|
36
|
-
.light .text-gray-100 { color: #1e293b !important; }
|
|
37
|
-
.light .text-gray-200 { color: #334155 !important; }
|
|
38
|
-
.light .text-gray-300 { color: #475569 !important; }
|
|
39
|
-
.light .text-gray-400 { color: #64748b !important; }
|
|
40
|
-
.light .text-gray-500 { color: #64748b !important; }
|
|
41
|
-
.light .text-gray-600 { color: #94a3b8 !important; }
|
|
42
|
-
.light .hover\:text-gray-300:hover { color: #334155 !important; }
|
|
43
|
-
.light .hover\:text-gray-400:hover { color: #475569 !important; }
|
|
44
|
-
.light .hover\:bg-gray-800:hover { background: #f1f5f9 !important; }
|
|
45
|
-
.light .hover\:bg-gray-800\/50:hover { background: rgba(241,245,249,0.5) !important; }
|
|
46
|
-
.light .border-green-500\/30 { border-color: rgba(34,197,94,0.4) !important; }
|
|
47
|
-
.light .bg-green-500\/10 { background: rgba(34,197,94,0.15) !important; }
|
|
48
|
-
.light .bg-green-500\/20 { background: rgba(34,197,94,0.2) !important; }
|
|
49
|
-
.light .border-claude-orange\/30 { border-color: rgba(232,111,51,0.4) !important; }
|
|
50
|
-
.light .bg-claude-orange\/10 { background: rgba(232,111,51,0.12) !important; }
|
|
51
|
-
.light .bg-claude-orange\/20 { background: rgba(232,111,51,0.2) !important; }
|
|
52
|
-
.light .border-yellow-500\/20 { border-color: rgba(234,179,8,0.4) !important; }
|
|
53
|
-
.light .bg-yellow-500\/10 { background: rgba(234,179,8,0.15) !important; }
|
|
54
|
-
.light .bg-yellow-500\/20 { background: rgba(234,179,8,0.25) !important; }
|
|
55
|
-
.light .border-blue-500\/20 { border-color: rgba(59,130,246,0.4) !important; }
|
|
56
|
-
.light .bg-blue-500\/10 { background: rgba(59,130,246,0.15) !important; }
|
|
57
|
-
.light .prose pre { background: #f1f5f9 !important; }
|
|
58
|
-
.light .prose code { background: #e2e8f0 !important; }
|
|
59
|
-
.light ::-webkit-scrollbar-thumb { background: #e2e8f0 !important; }
|
|
60
|
-
.light ::-webkit-scrollbar-thumb:hover { background: #cbd5e1 !important; }
|
|
61
|
-
body.light { scrollbar-color: #e2e8f0 transparent; }
|
|
62
|
-
</style>
|
|
11
|
+
<script src="https://cdn.jsdelivr.net/npm/dompurify@3/dist/purify.min.js"></script>
|
|
63
12
|
<style>
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
13
|
+
:root {
|
|
14
|
+
--bg-deep: #08090a;
|
|
15
|
+
--bg-surface: #0d0e10;
|
|
16
|
+
--bg-elevated: #131416;
|
|
17
|
+
--bg-hover: #1a1b1e;
|
|
18
|
+
--border: #1e2023;
|
|
19
|
+
--text-primary: #e8e8e8;
|
|
20
|
+
--text-secondary: #8b8d91;
|
|
21
|
+
--text-tertiary: #5a5c60;
|
|
22
|
+
--text-muted: #3d3f42;
|
|
23
|
+
--accent: #E86F33;
|
|
24
|
+
--accent-dim: rgba(232, 111, 51, 0.15);
|
|
25
|
+
--accent-glow: rgba(232, 111, 51, 0.4);
|
|
26
|
+
--success: #3ecf8e;
|
|
27
|
+
--success-dim: rgba(62, 207, 142, 0.12);
|
|
28
|
+
--warning: #f0b429;
|
|
29
|
+
--warning-dim: rgba(240, 180, 41, 0.12);
|
|
30
|
+
--mono: 'IBM Plex Mono', monospace;
|
|
31
|
+
--serif: 'Playfair Display', serif;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
35
|
+
|
|
36
|
+
body {
|
|
37
|
+
font-family: var(--mono);
|
|
38
|
+
font-size: 13px;
|
|
39
|
+
background: var(--bg-deep);
|
|
40
|
+
color: var(--text-primary);
|
|
41
|
+
line-height: 1.5;
|
|
42
|
+
min-height: 100vh;
|
|
43
|
+
-webkit-font-smoothing: antialiased;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* Subtle scan-line texture */
|
|
47
|
+
body::before {
|
|
48
|
+
content: '';
|
|
49
|
+
position: fixed;
|
|
50
|
+
inset: 0;
|
|
51
|
+
background: repeating-linear-gradient(
|
|
52
|
+
0deg,
|
|
53
|
+
transparent,
|
|
54
|
+
transparent 2px,
|
|
55
|
+
rgba(0,0,0,0.03) 2px,
|
|
56
|
+
rgba(0,0,0,0.03) 4px
|
|
57
|
+
);
|
|
58
|
+
pointer-events: none;
|
|
59
|
+
z-index: 9999;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* Scrollbar */
|
|
63
|
+
::-webkit-scrollbar { width: 6px; height: 6px; }
|
|
73
64
|
::-webkit-scrollbar-track { background: transparent; }
|
|
74
|
-
::-webkit-scrollbar-thumb { background: var(--
|
|
75
|
-
::-webkit-scrollbar-thumb:hover { background: var(--
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
65
|
+
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
|
|
66
|
+
::-webkit-scrollbar-thumb:hover { background: var(--text-muted); }
|
|
67
|
+
|
|
68
|
+
/* Layout */
|
|
69
|
+
.app { display: flex; height: 100vh; }
|
|
70
|
+
|
|
71
|
+
/* Sidebar */
|
|
72
|
+
.sidebar {
|
|
73
|
+
width: 280px;
|
|
74
|
+
background: var(--bg-surface);
|
|
75
|
+
border-right: 1px solid var(--border);
|
|
76
|
+
display: flex;
|
|
77
|
+
flex-direction: column;
|
|
78
|
+
flex-shrink: 0;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.sidebar-header {
|
|
82
|
+
padding: 20px;
|
|
83
|
+
border-bottom: 1px solid var(--border);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.logo {
|
|
87
|
+
display: flex;
|
|
88
|
+
align-items: center;
|
|
89
|
+
gap: 10px;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.logo-mark {
|
|
93
|
+
width: 24px;
|
|
94
|
+
height: 24px;
|
|
95
|
+
background: var(--accent);
|
|
96
|
+
border-radius: 4px;
|
|
97
|
+
display: flex;
|
|
98
|
+
align-items: center;
|
|
99
|
+
justify-content: center;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.logo-mark svg {
|
|
103
|
+
width: 14px;
|
|
104
|
+
height: 14px;
|
|
105
|
+
color: white;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.logo-text {
|
|
109
|
+
font-family: var(--serif);
|
|
110
|
+
font-size: 16px;
|
|
111
|
+
font-weight: 500;
|
|
112
|
+
letter-spacing: -0.02em;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.connection {
|
|
116
|
+
display: flex;
|
|
117
|
+
align-items: center;
|
|
118
|
+
gap: 6px;
|
|
119
|
+
margin-top: 12px;
|
|
120
|
+
font-size: 11px;
|
|
121
|
+
color: var(--text-tertiary);
|
|
122
|
+
text-transform: uppercase;
|
|
123
|
+
letter-spacing: 0.05em;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.connection-dot {
|
|
127
|
+
width: 6px;
|
|
128
|
+
height: 6px;
|
|
129
|
+
border-radius: 50%;
|
|
130
|
+
background: var(--warning);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.connection-dot.live {
|
|
134
|
+
background: var(--success);
|
|
135
|
+
box-shadow: 0 0 8px var(--success);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.connection-dot.error {
|
|
139
|
+
background: #ef4444;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/* Nav item */
|
|
143
|
+
.nav-item {
|
|
144
|
+
display: flex;
|
|
145
|
+
align-items: center;
|
|
146
|
+
gap: 10px;
|
|
147
|
+
width: 100%;
|
|
148
|
+
padding: 10px 12px;
|
|
149
|
+
background: transparent;
|
|
150
|
+
border: none;
|
|
151
|
+
border-radius: 6px;
|
|
152
|
+
color: var(--text-secondary);
|
|
153
|
+
font-family: var(--mono);
|
|
154
|
+
font-size: 12px;
|
|
155
|
+
cursor: pointer;
|
|
156
|
+
transition: all 0.15s ease;
|
|
157
|
+
text-align: left;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.nav-item:hover {
|
|
161
|
+
background: var(--bg-hover);
|
|
162
|
+
color: var(--text-primary);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.nav-item.active {
|
|
166
|
+
background: var(--bg-elevated);
|
|
167
|
+
color: var(--text-primary);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.nav-item svg {
|
|
171
|
+
width: 16px;
|
|
172
|
+
height: 16px;
|
|
173
|
+
opacity: 0.6;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/* Sidebar sections */
|
|
177
|
+
.sidebar-section {
|
|
178
|
+
display: flex;
|
|
179
|
+
flex-direction: column;
|
|
180
|
+
border-bottom: 1px solid var(--border);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.sidebar-section.flex-1 {
|
|
184
|
+
flex: 1;
|
|
185
|
+
border-bottom: none;
|
|
186
|
+
overflow: hidden;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.section-header {
|
|
190
|
+
display: flex;
|
|
191
|
+
align-items: center;
|
|
192
|
+
justify-content: space-between;
|
|
193
|
+
padding: 12px 16px;
|
|
194
|
+
font-size: 10px;
|
|
195
|
+
font-weight: 600;
|
|
196
|
+
text-transform: uppercase;
|
|
197
|
+
letter-spacing: 0.1em;
|
|
198
|
+
color: var(--text-muted);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.filter-toggle {
|
|
202
|
+
display: flex;
|
|
203
|
+
align-items: center;
|
|
204
|
+
gap: 6px;
|
|
205
|
+
font-weight: 400;
|
|
206
|
+
color: var(--text-tertiary);
|
|
207
|
+
cursor: pointer;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.filter-toggle input[type="checkbox"] {
|
|
211
|
+
appearance: none;
|
|
212
|
+
width: 10px;
|
|
213
|
+
height: 10px;
|
|
214
|
+
border: 1px solid var(--text-muted);
|
|
215
|
+
border-radius: 2px;
|
|
216
|
+
cursor: pointer;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.filter-toggle input[type="checkbox"]:checked {
|
|
220
|
+
background: var(--accent);
|
|
221
|
+
border-color: var(--accent);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/* Live Updates */
|
|
225
|
+
.live-updates {
|
|
226
|
+
padding: 0 12px 12px;
|
|
227
|
+
max-height: 180px;
|
|
228
|
+
overflow-y: auto;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.live-empty {
|
|
232
|
+
padding: 16px;
|
|
233
|
+
text-align: center;
|
|
234
|
+
font-size: 11px;
|
|
235
|
+
color: var(--text-muted);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.live-item {
|
|
239
|
+
display: flex;
|
|
240
|
+
align-items: flex-start;
|
|
241
|
+
gap: 10px;
|
|
242
|
+
padding: 10px 12px;
|
|
243
|
+
background: var(--bg-elevated);
|
|
244
|
+
border: 1px solid var(--border);
|
|
245
|
+
border-radius: 6px;
|
|
246
|
+
margin-bottom: 6px;
|
|
247
|
+
cursor: pointer;
|
|
248
|
+
transition: all 0.15s ease;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.live-item:hover {
|
|
252
|
+
border-color: var(--text-muted);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.live-item .pulse {
|
|
256
|
+
width: 8px;
|
|
257
|
+
height: 8px;
|
|
258
|
+
margin-top: 4px;
|
|
259
|
+
background: var(--accent);
|
|
260
|
+
border-radius: 50%;
|
|
261
|
+
flex-shrink: 0;
|
|
262
|
+
animation: pulse 2s ease-in-out infinite;
|
|
263
|
+
box-shadow: 0 0 12px var(--accent-glow);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.live-item-content {
|
|
267
|
+
flex: 1;
|
|
268
|
+
min-width: 0;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
.live-item-action {
|
|
272
|
+
font-size: 12px;
|
|
273
|
+
color: var(--text-primary);
|
|
274
|
+
white-space: nowrap;
|
|
275
|
+
overflow: hidden;
|
|
276
|
+
text-overflow: ellipsis;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
.live-item-session {
|
|
280
|
+
font-size: 10px;
|
|
281
|
+
color: var(--text-tertiary);
|
|
282
|
+
margin-top: 2px;
|
|
283
|
+
white-space: nowrap;
|
|
284
|
+
overflow: hidden;
|
|
285
|
+
text-overflow: ellipsis;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/* Sessions */
|
|
289
|
+
.sessions-list {
|
|
290
|
+
flex: 1;
|
|
291
|
+
overflow-y: auto;
|
|
292
|
+
padding: 0 12px 12px;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
.sessions-list .nav-item {
|
|
296
|
+
margin-bottom: 8px;
|
|
297
|
+
border-bottom: 1px solid var(--border);
|
|
298
|
+
padding-bottom: 12px;
|
|
299
|
+
border-radius: 0;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.sessions-list .nav-item:hover {
|
|
303
|
+
background: transparent;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.session-item {
|
|
307
|
+
display: block;
|
|
308
|
+
width: 100%;
|
|
309
|
+
padding: 12px;
|
|
310
|
+
margin-bottom: 4px;
|
|
311
|
+
background: transparent;
|
|
312
|
+
border: 1px solid transparent;
|
|
313
|
+
border-radius: 6px;
|
|
314
|
+
text-align: left;
|
|
315
|
+
cursor: pointer;
|
|
316
|
+
transition: all 0.15s ease;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
.session-item:hover {
|
|
320
|
+
background: var(--bg-hover);
|
|
321
|
+
border-color: var(--border);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
.session-item.active {
|
|
325
|
+
background: var(--bg-elevated);
|
|
326
|
+
border-color: var(--border);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
.session-name {
|
|
330
|
+
display: flex;
|
|
331
|
+
align-items: center;
|
|
332
|
+
justify-content: space-between;
|
|
333
|
+
gap: 8px;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
.session-name span {
|
|
337
|
+
font-size: 13px;
|
|
338
|
+
color: var(--text-primary);
|
|
339
|
+
white-space: nowrap;
|
|
340
|
+
overflow: hidden;
|
|
341
|
+
text-overflow: ellipsis;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.session-name .pulse {
|
|
345
|
+
width: 8px;
|
|
346
|
+
height: 8px;
|
|
347
|
+
background: var(--accent);
|
|
348
|
+
border-radius: 50%;
|
|
349
|
+
animation: pulse 2s ease-in-out infinite;
|
|
350
|
+
box-shadow: 0 0 12px var(--accent-glow);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
@keyframes pulse {
|
|
354
|
+
0%, 100% { opacity: 1; transform: scale(1); }
|
|
355
|
+
50% { opacity: 0.6; transform: scale(0.9); }
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
.session-project {
|
|
359
|
+
font-size: 11px;
|
|
360
|
+
color: var(--text-tertiary);
|
|
361
|
+
margin-top: 4px;
|
|
362
|
+
white-space: nowrap;
|
|
363
|
+
overflow: hidden;
|
|
364
|
+
text-overflow: ellipsis;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.session-progress {
|
|
368
|
+
display: flex;
|
|
369
|
+
align-items: center;
|
|
370
|
+
gap: 8px;
|
|
371
|
+
margin-top: 8px;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.progress-bar {
|
|
375
|
+
flex: 1;
|
|
376
|
+
height: 2px;
|
|
377
|
+
background: var(--border);
|
|
378
|
+
border-radius: 1px;
|
|
379
|
+
overflow: hidden;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
.progress-fill {
|
|
383
|
+
height: 100%;
|
|
384
|
+
background: var(--accent);
|
|
385
|
+
transition: width 0.3s ease;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
.progress-text {
|
|
389
|
+
font-size: 10px;
|
|
390
|
+
color: var(--text-muted);
|
|
391
|
+
font-variant-numeric: tabular-nums;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
.session-time {
|
|
395
|
+
font-size: 10px;
|
|
396
|
+
color: var(--text-muted);
|
|
397
|
+
margin-top: 6px;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/* Footer */
|
|
401
|
+
.sidebar-footer {
|
|
402
|
+
padding: 12px 20px;
|
|
403
|
+
border-top: 1px solid var(--border);
|
|
404
|
+
font-size: 10px;
|
|
405
|
+
color: var(--text-muted);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
.sidebar-footer a {
|
|
409
|
+
color: var(--text-tertiary);
|
|
410
|
+
text-decoration: none;
|
|
411
|
+
transition: color 0.15s;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
.sidebar-footer a:hover {
|
|
415
|
+
color: var(--text-secondary);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/* Main */
|
|
419
|
+
.main {
|
|
420
|
+
flex: 1;
|
|
421
|
+
display: flex;
|
|
422
|
+
flex-direction: column;
|
|
423
|
+
overflow: hidden;
|
|
424
|
+
background: var(--bg-deep);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/* Empty state */
|
|
428
|
+
.empty-state {
|
|
429
|
+
flex: 1;
|
|
430
|
+
display: flex;
|
|
431
|
+
align-items: center;
|
|
432
|
+
justify-content: center;
|
|
433
|
+
flex-direction: column;
|
|
434
|
+
gap: 16px;
|
|
435
|
+
color: var(--text-muted);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
.empty-state svg {
|
|
439
|
+
width: 48px;
|
|
440
|
+
height: 48px;
|
|
441
|
+
opacity: 0.3;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
.empty-state p {
|
|
445
|
+
font-size: 13px;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/* Session view */
|
|
449
|
+
.session-view {
|
|
450
|
+
flex: 1;
|
|
451
|
+
display: none;
|
|
452
|
+
flex-direction: column;
|
|
453
|
+
overflow: hidden;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
.session-view.visible {
|
|
457
|
+
display: flex;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/* Header */
|
|
461
|
+
.view-header {
|
|
462
|
+
padding: 16px 24px;
|
|
463
|
+
border-bottom: 1px solid var(--border);
|
|
464
|
+
background: var(--bg-surface);
|
|
465
|
+
display: flex;
|
|
466
|
+
align-items: center;
|
|
467
|
+
justify-content: space-between;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
.view-title {
|
|
471
|
+
font-family: var(--serif);
|
|
472
|
+
font-size: 20px;
|
|
473
|
+
font-weight: 400;
|
|
474
|
+
letter-spacing: -0.02em;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
.view-meta {
|
|
478
|
+
font-size: 11px;
|
|
479
|
+
color: var(--text-tertiary);
|
|
480
|
+
margin-top: 4px;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
.view-actions {
|
|
484
|
+
display: flex;
|
|
485
|
+
align-items: center;
|
|
486
|
+
gap: 16px;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
.view-progress {
|
|
490
|
+
display: flex;
|
|
491
|
+
align-items: center;
|
|
492
|
+
gap: 10px;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
.view-progress .progress-bar {
|
|
496
|
+
width: 120px;
|
|
497
|
+
height: 3px;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
.view-progress .progress-text {
|
|
501
|
+
font-size: 13px;
|
|
502
|
+
font-weight: 500;
|
|
503
|
+
color: var(--accent);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
.icon-btn {
|
|
507
|
+
width: 32px;
|
|
508
|
+
height: 32px;
|
|
509
|
+
display: flex;
|
|
510
|
+
align-items: center;
|
|
511
|
+
justify-content: center;
|
|
512
|
+
background: transparent;
|
|
513
|
+
border: 1px solid var(--border);
|
|
514
|
+
border-radius: 6px;
|
|
515
|
+
color: var(--text-tertiary);
|
|
516
|
+
cursor: pointer;
|
|
517
|
+
transition: all 0.15s ease;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
.icon-btn:hover {
|
|
521
|
+
background: var(--bg-hover);
|
|
522
|
+
color: var(--text-primary);
|
|
523
|
+
border-color: var(--text-muted);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
.icon-btn svg {
|
|
527
|
+
width: 16px;
|
|
528
|
+
height: 16px;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/* Kanban */
|
|
532
|
+
.kanban {
|
|
533
|
+
flex: 1;
|
|
534
|
+
display: flex;
|
|
535
|
+
gap: 24px;
|
|
536
|
+
padding: 24px;
|
|
537
|
+
overflow-x: auto;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
.kanban-column {
|
|
541
|
+
width: 320px;
|
|
542
|
+
flex-shrink: 0;
|
|
543
|
+
display: flex;
|
|
544
|
+
flex-direction: column;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
.column-header {
|
|
548
|
+
display: flex;
|
|
549
|
+
align-items: center;
|
|
550
|
+
gap: 10px;
|
|
551
|
+
padding-bottom: 16px;
|
|
552
|
+
border-bottom: 1px solid var(--border);
|
|
553
|
+
margin-bottom: 16px;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
.column-dot {
|
|
557
|
+
width: 8px;
|
|
558
|
+
height: 8px;
|
|
559
|
+
border-radius: 50%;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
.column-dot.pending { background: var(--text-muted); }
|
|
563
|
+
.column-dot.in-progress {
|
|
564
|
+
background: var(--accent);
|
|
565
|
+
box-shadow: 0 0 12px var(--accent-glow);
|
|
566
|
+
animation: pulse 2s ease-in-out infinite;
|
|
567
|
+
}
|
|
568
|
+
.column-dot.completed { background: var(--success); }
|
|
569
|
+
|
|
570
|
+
.column-title {
|
|
571
|
+
font-size: 12px;
|
|
572
|
+
font-weight: 500;
|
|
573
|
+
text-transform: uppercase;
|
|
574
|
+
letter-spacing: 0.05em;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
.column-title.pending { color: var(--text-tertiary); }
|
|
578
|
+
.column-title.in-progress { color: var(--accent); }
|
|
579
|
+
.column-title.completed { color: var(--success); }
|
|
580
|
+
|
|
581
|
+
.column-count {
|
|
582
|
+
font-size: 10px;
|
|
583
|
+
padding: 2px 8px;
|
|
584
|
+
border-radius: 10px;
|
|
585
|
+
font-variant-numeric: tabular-nums;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
.column-count.pending {
|
|
589
|
+
background: var(--bg-elevated);
|
|
590
|
+
color: var(--text-muted);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
.column-count.in-progress {
|
|
594
|
+
background: var(--accent-dim);
|
|
595
|
+
color: var(--accent);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
.column-count.completed {
|
|
599
|
+
background: var(--success-dim);
|
|
600
|
+
color: var(--success);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
.column-tasks {
|
|
604
|
+
flex: 1;
|
|
605
|
+
overflow-y: auto;
|
|
606
|
+
display: flex;
|
|
607
|
+
flex-direction: column;
|
|
608
|
+
gap: 8px;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
.column-empty {
|
|
612
|
+
text-align: center;
|
|
613
|
+
padding: 32px 16px;
|
|
614
|
+
color: var(--text-muted);
|
|
615
|
+
font-size: 12px;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
/* Task card */
|
|
619
|
+
.task-card {
|
|
620
|
+
padding: 14px;
|
|
621
|
+
background: var(--bg-surface);
|
|
622
|
+
border: 1px solid var(--border);
|
|
623
|
+
border-radius: 8px;
|
|
624
|
+
cursor: pointer;
|
|
625
|
+
transition: all 0.15s ease;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
.task-card:hover {
|
|
629
|
+
background: var(--bg-elevated);
|
|
630
|
+
border-color: var(--text-muted);
|
|
631
|
+
transform: translateY(-1px);
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
.task-card.in-progress {
|
|
635
|
+
border-color: var(--accent);
|
|
636
|
+
box-shadow: 0 0 20px var(--accent-dim), inset 0 0 20px var(--accent-dim);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
.task-card.completed {
|
|
640
|
+
opacity: 0.6;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
.task-card.blocked {
|
|
644
|
+
opacity: 0.5;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
.task-id {
|
|
648
|
+
font-size: 10px;
|
|
649
|
+
color: var(--text-muted);
|
|
650
|
+
margin-bottom: 6px;
|
|
651
|
+
display: flex;
|
|
652
|
+
align-items: center;
|
|
653
|
+
gap: 8px;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
.task-badge {
|
|
657
|
+
font-size: 9px;
|
|
658
|
+
padding: 2px 6px;
|
|
659
|
+
border-radius: 3px;
|
|
660
|
+
text-transform: uppercase;
|
|
661
|
+
letter-spacing: 0.03em;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
.task-badge.blocked {
|
|
665
|
+
background: var(--warning-dim);
|
|
666
|
+
color: var(--warning);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
.task-title {
|
|
670
|
+
font-size: 13px;
|
|
671
|
+
color: var(--text-primary);
|
|
672
|
+
line-height: 1.4;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
.task-card.completed .task-title {
|
|
676
|
+
text-decoration: line-through;
|
|
677
|
+
color: var(--text-tertiary);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
.task-session {
|
|
681
|
+
font-size: 11px;
|
|
682
|
+
color: var(--accent);
|
|
683
|
+
margin-top: 6px;
|
|
684
|
+
opacity: 0.8;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
.task-active {
|
|
688
|
+
display: flex;
|
|
689
|
+
align-items: center;
|
|
690
|
+
gap: 6px;
|
|
691
|
+
margin-top: 10px;
|
|
692
|
+
padding-top: 10px;
|
|
693
|
+
border-top: 1px solid var(--border);
|
|
694
|
+
font-size: 11px;
|
|
695
|
+
color: var(--accent);
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
.task-active::before {
|
|
699
|
+
content: '';
|
|
700
|
+
width: 6px;
|
|
701
|
+
height: 6px;
|
|
702
|
+
background: var(--accent);
|
|
703
|
+
border-radius: 50%;
|
|
704
|
+
animation: pulse 2s ease-in-out infinite;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
.task-blocked {
|
|
708
|
+
font-size: 10px;
|
|
709
|
+
color: var(--text-muted);
|
|
710
|
+
margin-top: 8px;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
.task-desc {
|
|
714
|
+
font-size: 11px;
|
|
715
|
+
color: var(--text-tertiary);
|
|
716
|
+
margin-top: 8px;
|
|
717
|
+
display: -webkit-box;
|
|
718
|
+
-webkit-line-clamp: 2;
|
|
719
|
+
-webkit-box-orient: vertical;
|
|
720
|
+
overflow: hidden;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
/* Detail panel */
|
|
724
|
+
.detail-panel {
|
|
725
|
+
width: 400px;
|
|
726
|
+
background: var(--bg-surface);
|
|
727
|
+
border-left: 1px solid var(--border);
|
|
728
|
+
display: none;
|
|
729
|
+
flex-direction: column;
|
|
730
|
+
flex-shrink: 0;
|
|
731
|
+
overflow: hidden;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
.detail-panel.visible {
|
|
735
|
+
display: flex;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
.detail-header {
|
|
739
|
+
padding: 16px 20px;
|
|
740
|
+
border-bottom: 1px solid var(--border);
|
|
741
|
+
display: flex;
|
|
742
|
+
align-items: center;
|
|
743
|
+
justify-content: space-between;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
.detail-header h3 {
|
|
747
|
+
font-family: var(--serif);
|
|
748
|
+
font-size: 14px;
|
|
749
|
+
font-weight: 500;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
.detail-close {
|
|
753
|
+
width: 28px;
|
|
754
|
+
height: 28px;
|
|
755
|
+
display: flex;
|
|
756
|
+
align-items: center;
|
|
757
|
+
justify-content: center;
|
|
758
|
+
background: transparent;
|
|
759
|
+
border: none;
|
|
760
|
+
color: var(--text-muted);
|
|
761
|
+
cursor: pointer;
|
|
762
|
+
border-radius: 4px;
|
|
763
|
+
transition: all 0.15s;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
.detail-close:hover {
|
|
767
|
+
background: var(--bg-hover);
|
|
768
|
+
color: var(--text-primary);
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
.detail-close svg {
|
|
772
|
+
width: 16px;
|
|
773
|
+
height: 16px;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
.detail-content {
|
|
777
|
+
flex: 1;
|
|
778
|
+
overflow-y: auto;
|
|
779
|
+
padding: 20px;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
.detail-section {
|
|
783
|
+
margin-bottom: 20px;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
.detail-label {
|
|
787
|
+
font-size: 10px;
|
|
788
|
+
font-weight: 500;
|
|
789
|
+
text-transform: uppercase;
|
|
790
|
+
letter-spacing: 0.08em;
|
|
791
|
+
color: var(--text-muted);
|
|
792
|
+
margin-bottom: 8px;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
.detail-title {
|
|
796
|
+
font-family: var(--serif);
|
|
797
|
+
font-size: 18px;
|
|
798
|
+
line-height: 1.4;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
.detail-status {
|
|
802
|
+
display: inline-flex;
|
|
803
|
+
align-items: center;
|
|
804
|
+
gap: 6px;
|
|
805
|
+
font-size: 12px;
|
|
806
|
+
padding: 6px 10px;
|
|
807
|
+
border-radius: 4px;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
.detail-status.pending {
|
|
811
|
+
background: var(--bg-elevated);
|
|
812
|
+
color: var(--text-tertiary);
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
.detail-status.in_progress {
|
|
816
|
+
background: var(--accent-dim);
|
|
817
|
+
color: var(--accent);
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
.detail-status.completed {
|
|
821
|
+
background: var(--success-dim);
|
|
822
|
+
color: var(--success);
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
.detail-status .dot {
|
|
826
|
+
width: 6px;
|
|
827
|
+
height: 6px;
|
|
828
|
+
border-radius: 50%;
|
|
829
|
+
background: currentColor;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
.detail-status.in_progress .dot {
|
|
833
|
+
animation: pulse 2s ease-in-out infinite;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
.detail-box {
|
|
837
|
+
padding: 12px;
|
|
838
|
+
border-radius: 6px;
|
|
839
|
+
font-size: 12px;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
.detail-box.active {
|
|
843
|
+
background: var(--accent-dim);
|
|
844
|
+
border: 1px solid rgba(232, 111, 51, 0.2);
|
|
845
|
+
color: var(--accent);
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
.detail-box.blocked {
|
|
849
|
+
background: var(--warning-dim);
|
|
850
|
+
border: 1px solid rgba(240, 180, 41, 0.2);
|
|
851
|
+
color: var(--warning);
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
.detail-box.blocks {
|
|
855
|
+
background: rgba(96, 165, 250, 0.1);
|
|
856
|
+
border: 1px solid rgba(96, 165, 250, 0.2);
|
|
857
|
+
color: #60a5fa;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
.detail-desc {
|
|
861
|
+
font-size: 13px;
|
|
862
|
+
line-height: 1.7;
|
|
863
|
+
color: var(--text-secondary);
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
.detail-desc pre {
|
|
867
|
+
background: var(--bg-elevated);
|
|
868
|
+
padding: 12px;
|
|
869
|
+
border-radius: 6px;
|
|
870
|
+
overflow-x: auto;
|
|
871
|
+
margin: 12px 0;
|
|
872
|
+
font-size: 12px;
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
.detail-desc code {
|
|
876
|
+
background: var(--bg-elevated);
|
|
877
|
+
padding: 2px 6px;
|
|
878
|
+
border-radius: 3px;
|
|
879
|
+
font-size: 0.9em;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
.detail-desc pre code {
|
|
883
|
+
background: transparent;
|
|
884
|
+
padding: 0;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
.detail-desc hr {
|
|
888
|
+
border: none;
|
|
889
|
+
border-top: 1px solid var(--border);
|
|
890
|
+
margin: 16px 0;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
.detail-desc h4 {
|
|
894
|
+
font-size: 10px;
|
|
895
|
+
font-weight: 600;
|
|
896
|
+
text-transform: uppercase;
|
|
897
|
+
letter-spacing: 0.05em;
|
|
898
|
+
color: var(--accent);
|
|
899
|
+
margin: 0 0 8px 0;
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
.detail-desc p {
|
|
903
|
+
margin: 0 0 12px 0;
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
.detail-desc p:last-child {
|
|
907
|
+
margin-bottom: 0;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
/* Note form */
|
|
911
|
+
.note-section {
|
|
912
|
+
margin-top: 24px;
|
|
913
|
+
padding-top: 20px;
|
|
914
|
+
border-top: 1px solid var(--border);
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
.note-form {
|
|
918
|
+
display: flex;
|
|
919
|
+
flex-direction: column;
|
|
920
|
+
gap: 10px;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
.note-input {
|
|
924
|
+
width: 100%;
|
|
925
|
+
padding: 10px 12px;
|
|
926
|
+
background: var(--bg-elevated);
|
|
927
|
+
border: 1px solid var(--border);
|
|
928
|
+
border-radius: 6px;
|
|
929
|
+
color: var(--text-primary);
|
|
930
|
+
font-family: var(--mono);
|
|
931
|
+
font-size: 12px;
|
|
932
|
+
line-height: 1.5;
|
|
933
|
+
resize: vertical;
|
|
934
|
+
min-height: 60px;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
.note-input:focus {
|
|
938
|
+
outline: none;
|
|
939
|
+
border-color: var(--accent);
|
|
940
|
+
box-shadow: 0 0 0 2px var(--accent-dim);
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
.note-input::placeholder {
|
|
944
|
+
color: var(--text-muted);
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
.note-submit {
|
|
948
|
+
align-self: flex-end;
|
|
949
|
+
padding: 8px 16px;
|
|
950
|
+
background: var(--accent);
|
|
951
|
+
border: none;
|
|
952
|
+
border-radius: 5px;
|
|
953
|
+
color: white;
|
|
954
|
+
font-family: var(--mono);
|
|
955
|
+
font-size: 11px;
|
|
956
|
+
font-weight: 500;
|
|
957
|
+
cursor: pointer;
|
|
958
|
+
transition: all 0.15s ease;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
.note-submit:hover {
|
|
962
|
+
filter: brightness(1.1);
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
/* Light mode */
|
|
966
|
+
body.light {
|
|
967
|
+
--bg-deep: #fafafa;
|
|
968
|
+
--bg-surface: #ffffff;
|
|
969
|
+
--bg-elevated: #f5f5f5;
|
|
970
|
+
--bg-hover: #efefef;
|
|
971
|
+
--border: #e5e5e5;
|
|
972
|
+
--text-primary: #171717;
|
|
973
|
+
--text-secondary: #525252;
|
|
974
|
+
--text-tertiary: #737373;
|
|
975
|
+
--text-muted: #a3a3a3;
|
|
976
|
+
--accent-dim: rgba(232, 111, 51, 0.1);
|
|
977
|
+
--accent-glow: rgba(232, 111, 51, 0.3);
|
|
978
|
+
--success-dim: rgba(62, 207, 142, 0.1);
|
|
979
|
+
--warning-dim: rgba(240, 180, 41, 0.1);
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
body.light::before {
|
|
983
|
+
display: none;
|
|
984
|
+
}
|
|
79
985
|
</style>
|
|
80
986
|
</head>
|
|
81
|
-
<body
|
|
82
|
-
<div class="
|
|
987
|
+
<body>
|
|
988
|
+
<div class="app">
|
|
83
989
|
<!-- Sidebar -->
|
|
84
|
-
<aside class="
|
|
85
|
-
<header class="
|
|
86
|
-
<
|
|
87
|
-
<
|
|
88
|
-
<
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
990
|
+
<aside class="sidebar">
|
|
991
|
+
<header class="sidebar-header">
|
|
992
|
+
<div class="logo">
|
|
993
|
+
<div class="logo-mark">
|
|
994
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
|
|
995
|
+
<path d="M5 13l4 4L19 7"/>
|
|
996
|
+
</svg>
|
|
997
|
+
</div>
|
|
998
|
+
<span class="logo-text">Claude Tasks</span>
|
|
999
|
+
</div>
|
|
1000
|
+
<div id="connection-status" class="connection">
|
|
1001
|
+
<span class="connection-dot"></span>
|
|
1002
|
+
<span>Connecting</span>
|
|
1003
|
+
</div>
|
|
93
1004
|
</header>
|
|
94
1005
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
<span
|
|
1006
|
+
<!-- Live Updates -->
|
|
1007
|
+
<div class="sidebar-section">
|
|
1008
|
+
<div class="section-header">
|
|
1009
|
+
<span>Live Updates</span>
|
|
1010
|
+
</div>
|
|
1011
|
+
<div id="live-updates" class="live-updates">
|
|
1012
|
+
<div class="live-empty">No active tasks</div>
|
|
99
1013
|
</div>
|
|
100
1014
|
</div>
|
|
101
1015
|
|
|
102
|
-
<!--
|
|
103
|
-
<div class="
|
|
104
|
-
<
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
<
|
|
113
|
-
|
|
1016
|
+
<!-- Sessions -->
|
|
1017
|
+
<div class="sidebar-section flex-1">
|
|
1018
|
+
<div class="section-header">
|
|
1019
|
+
<span>Sessions</span>
|
|
1020
|
+
<label class="filter-toggle">
|
|
1021
|
+
<input type="checkbox" id="hide-inactive" onchange="toggleHideInactive()">
|
|
1022
|
+
<span>Active only</span>
|
|
1023
|
+
</label>
|
|
1024
|
+
</div>
|
|
1025
|
+
<div id="sessions-list" class="sessions-list">
|
|
1026
|
+
<button id="all-tasks-btn" class="nav-item" onclick="showAllTasks()">
|
|
1027
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1028
|
+
<path d="M4 6h16M4 12h16M4 18h16"/>
|
|
1029
|
+
</svg>
|
|
1030
|
+
<span>All Tasks</span>
|
|
1031
|
+
</button>
|
|
1032
|
+
</div>
|
|
114
1033
|
</div>
|
|
115
1034
|
|
|
116
|
-
<
|
|
117
|
-
<
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
<footer class="p-3 border-t border-gray-800 text-xs text-gray-600">
|
|
121
|
-
<a href="https://github.com/L1AD/claude-task-viewer" target="_blank" class="hover:text-gray-400">GitHub</a>
|
|
122
|
-
<span class="mx-2">·</span>
|
|
123
|
-
<a href="https://policylayer.com" target="_blank" class="hover:text-gray-400">PolicyLayer</a>
|
|
124
|
-
<span class="mx-2">·</span>
|
|
125
|
-
<span>v1.1.0</span>
|
|
1035
|
+
<footer class="sidebar-footer">
|
|
1036
|
+
<a href="https://github.com/L1AD/claude-task-viewer" target="_blank">GitHub</a>
|
|
1037
|
+
<span style="margin: 0 6px;">·</span>
|
|
1038
|
+
<a href="https://policylayer.com" target="_blank">PolicyLayer</a>
|
|
126
1039
|
</footer>
|
|
127
1040
|
</aside>
|
|
128
1041
|
|
|
129
|
-
<!-- Main
|
|
130
|
-
<main class="
|
|
131
|
-
<div id="no-session" class="
|
|
132
|
-
<
|
|
133
|
-
<
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
<p>Select a session to view tasks</p>
|
|
137
|
-
</div>
|
|
1042
|
+
<!-- Main -->
|
|
1043
|
+
<main class="main">
|
|
1044
|
+
<div id="no-session" class="empty-state">
|
|
1045
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
|
1046
|
+
<path d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"/>
|
|
1047
|
+
</svg>
|
|
1048
|
+
<p>Select a session to view tasks</p>
|
|
138
1049
|
</div>
|
|
139
1050
|
|
|
140
|
-
<div id="session-view" class="
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
<
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
<div class="w-32 h-2 bg-gray-800 rounded-full overflow-hidden">
|
|
151
|
-
<div id="progress-bar" class="h-full bg-gradient-to-r from-claude-orange to-orange-400 transition-all duration-500" style="width: 0%"></div>
|
|
152
|
-
</div>
|
|
153
|
-
<span id="progress-percent" class="text-sm font-medium text-claude-orange">0%</span>
|
|
1051
|
+
<div id="session-view" class="session-view">
|
|
1052
|
+
<header class="view-header">
|
|
1053
|
+
<div>
|
|
1054
|
+
<h1 id="session-title" class="view-title">Session</h1>
|
|
1055
|
+
<p id="session-meta" class="view-meta"></p>
|
|
1056
|
+
</div>
|
|
1057
|
+
<div class="view-actions">
|
|
1058
|
+
<div class="view-progress">
|
|
1059
|
+
<div class="progress-bar">
|
|
1060
|
+
<div id="progress-bar" class="progress-fill" style="width: 0%"></div>
|
|
154
1061
|
</div>
|
|
155
|
-
<
|
|
156
|
-
<svg id="theme-icon-dark" class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
157
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"/>
|
|
158
|
-
</svg>
|
|
159
|
-
<svg id="theme-icon-light" class="w-5 h-5 hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
160
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"/>
|
|
161
|
-
</svg>
|
|
162
|
-
</button>
|
|
1062
|
+
<span id="progress-percent" class="progress-text">0%</span>
|
|
163
1063
|
</div>
|
|
1064
|
+
<button id="theme-toggle" class="icon-btn" onclick="toggleTheme()" title="Toggle theme">
|
|
1065
|
+
<svg id="theme-icon-dark" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1066
|
+
<path d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"/>
|
|
1067
|
+
</svg>
|
|
1068
|
+
<svg id="theme-icon-light" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="display:none">
|
|
1069
|
+
<circle cx="12" cy="12" r="5"/>
|
|
1070
|
+
<path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/>
|
|
1071
|
+
</svg>
|
|
1072
|
+
</button>
|
|
164
1073
|
</div>
|
|
165
1074
|
</header>
|
|
166
1075
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
<
|
|
173
|
-
<span class="w-3 h-3 rounded-full bg-gray-500"></span>
|
|
174
|
-
<h3 class="font-medium text-gray-400">Pending</h3>
|
|
175
|
-
<span id="pending-count" class="text-xs text-gray-600 bg-gray-800 px-2 py-0.5 rounded-full">0</span>
|
|
176
|
-
</div>
|
|
177
|
-
<div id="pending-tasks" class="flex-1 space-y-3 kanban-column overflow-y-auto pr-1">
|
|
178
|
-
</div>
|
|
1076
|
+
<div class="kanban">
|
|
1077
|
+
<div class="kanban-column">
|
|
1078
|
+
<div class="column-header">
|
|
1079
|
+
<span class="column-dot pending"></span>
|
|
1080
|
+
<span class="column-title pending">Pending</span>
|
|
1081
|
+
<span id="pending-count" class="column-count pending">0</span>
|
|
179
1082
|
</div>
|
|
1083
|
+
<div id="pending-tasks" class="column-tasks"></div>
|
|
1084
|
+
</div>
|
|
180
1085
|
|
|
181
|
-
|
|
182
|
-
<div class="
|
|
183
|
-
<
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
<span id="in-progress-count" class="text-xs text-claude-orange/70 bg-claude-orange/20 px-2 py-0.5 rounded-full">0</span>
|
|
187
|
-
</div>
|
|
188
|
-
<div id="in-progress-tasks" class="flex-1 space-y-3 kanban-column overflow-y-auto pr-1">
|
|
189
|
-
</div>
|
|
1086
|
+
<div class="kanban-column">
|
|
1087
|
+
<div class="column-header">
|
|
1088
|
+
<span class="column-dot in-progress"></span>
|
|
1089
|
+
<span class="column-title in-progress">In Progress</span>
|
|
1090
|
+
<span id="in-progress-count" class="column-count in-progress">0</span>
|
|
190
1091
|
</div>
|
|
1092
|
+
<div id="in-progress-tasks" class="column-tasks"></div>
|
|
1093
|
+
</div>
|
|
191
1094
|
|
|
192
|
-
|
|
193
|
-
<div class="
|
|
194
|
-
<
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
<span id="completed-count" class="text-xs text-green-400/70 bg-green-500/20 px-2 py-0.5 rounded-full">0</span>
|
|
198
|
-
</div>
|
|
199
|
-
<div id="completed-tasks" class="flex-1 space-y-3 kanban-column overflow-y-auto pr-1">
|
|
200
|
-
</div>
|
|
1095
|
+
<div class="kanban-column">
|
|
1096
|
+
<div class="column-header">
|
|
1097
|
+
<span class="column-dot completed"></span>
|
|
1098
|
+
<span class="column-title completed">Completed</span>
|
|
1099
|
+
<span id="completed-count" class="column-count completed">0</span>
|
|
201
1100
|
</div>
|
|
1101
|
+
<div id="completed-tasks" class="column-tasks"></div>
|
|
202
1102
|
</div>
|
|
203
1103
|
</div>
|
|
204
1104
|
</div>
|
|
205
1105
|
</main>
|
|
206
1106
|
|
|
207
|
-
<!--
|
|
208
|
-
<aside id="detail-panel" class="
|
|
209
|
-
<header class="
|
|
210
|
-
<h3
|
|
211
|
-
<button id="close-detail" class="
|
|
212
|
-
<svg
|
|
213
|
-
<path
|
|
1107
|
+
<!-- Detail panel -->
|
|
1108
|
+
<aside id="detail-panel" class="detail-panel">
|
|
1109
|
+
<header class="detail-header">
|
|
1110
|
+
<h3>Task Details</h3>
|
|
1111
|
+
<button id="close-detail" class="detail-close">
|
|
1112
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1113
|
+
<path d="M6 18L18 6M6 6l12 12"/>
|
|
214
1114
|
</svg>
|
|
215
1115
|
</button>
|
|
216
1116
|
</header>
|
|
217
|
-
<div id="detail-content" class="
|
|
1117
|
+
<div id="detail-content" class="detail-content"></div>
|
|
218
1118
|
</aside>
|
|
219
1119
|
</div>
|
|
220
1120
|
|
|
@@ -223,9 +1123,10 @@
|
|
|
223
1123
|
let sessions = [];
|
|
224
1124
|
let currentSessionId = null;
|
|
225
1125
|
let currentTasks = [];
|
|
226
|
-
let viewMode = 'session';
|
|
1126
|
+
let viewMode = 'session';
|
|
1127
|
+
let hideInactive = localStorage.getItem('hideInactive') === 'true';
|
|
227
1128
|
|
|
228
|
-
// DOM
|
|
1129
|
+
// DOM
|
|
229
1130
|
const sessionsList = document.getElementById('sessions-list');
|
|
230
1131
|
const noSession = document.getElementById('no-session');
|
|
231
1132
|
const sessionView = document.getElementById('session-view');
|
|
@@ -243,18 +1144,52 @@
|
|
|
243
1144
|
const detailContent = document.getElementById('detail-content');
|
|
244
1145
|
const connectionStatus = document.getElementById('connection-status');
|
|
245
1146
|
|
|
246
|
-
// Fetch sessions
|
|
247
1147
|
async function fetchSessions() {
|
|
248
1148
|
try {
|
|
249
1149
|
const res = await fetch('/api/sessions');
|
|
250
1150
|
sessions = await res.json();
|
|
251
1151
|
renderSessions();
|
|
1152
|
+
fetchLiveUpdates();
|
|
252
1153
|
} catch (error) {
|
|
253
1154
|
console.error('Failed to fetch sessions:', error);
|
|
254
1155
|
}
|
|
255
1156
|
}
|
|
256
1157
|
|
|
257
|
-
|
|
1158
|
+
async function fetchLiveUpdates() {
|
|
1159
|
+
try {
|
|
1160
|
+
const res = await fetch('/api/tasks/all');
|
|
1161
|
+
const allTasks = await res.json();
|
|
1162
|
+
const activeTasks = allTasks.filter(t => t.status === 'in_progress');
|
|
1163
|
+
renderLiveUpdates(activeTasks);
|
|
1164
|
+
} catch (error) {
|
|
1165
|
+
console.error('Failed to fetch live updates:', error);
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
function renderLiveUpdates(activeTasks) {
|
|
1170
|
+
const container = document.getElementById('live-updates');
|
|
1171
|
+
|
|
1172
|
+
if (activeTasks.length === 0) {
|
|
1173
|
+
container.innerHTML = '<div class="live-empty">No active tasks</div>';
|
|
1174
|
+
return;
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
container.innerHTML = activeTasks.map(task => `
|
|
1178
|
+
<div class="live-item" onclick="openLiveTask('${task.sessionId}', '${task.id}')">
|
|
1179
|
+
<span class="pulse"></span>
|
|
1180
|
+
<div class="live-item-content">
|
|
1181
|
+
<div class="live-item-action">${escapeHtml(task.activeForm || task.subject)}</div>
|
|
1182
|
+
<div class="live-item-session">${escapeHtml(task.sessionName || task.sessionId.slice(0, 8))}</div>
|
|
1183
|
+
</div>
|
|
1184
|
+
</div>
|
|
1185
|
+
`).join('');
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
async function openLiveTask(sessionId, taskId) {
|
|
1189
|
+
await fetchTasks(sessionId);
|
|
1190
|
+
showTaskDetail(taskId, sessionId);
|
|
1191
|
+
}
|
|
1192
|
+
|
|
258
1193
|
async function fetchTasks(sessionId) {
|
|
259
1194
|
try {
|
|
260
1195
|
viewMode = 'session';
|
|
@@ -267,7 +1202,6 @@
|
|
|
267
1202
|
}
|
|
268
1203
|
}
|
|
269
1204
|
|
|
270
|
-
// Show all tasks across all sessions
|
|
271
1205
|
async function showAllTasks() {
|
|
272
1206
|
try {
|
|
273
1207
|
viewMode = 'all';
|
|
@@ -281,10 +1215,9 @@
|
|
|
281
1215
|
}
|
|
282
1216
|
}
|
|
283
1217
|
|
|
284
|
-
// Render all tasks view
|
|
285
1218
|
function renderAllTasks() {
|
|
286
|
-
noSession.
|
|
287
|
-
sessionView.classList.
|
|
1219
|
+
noSession.style.display = 'none';
|
|
1220
|
+
sessionView.classList.add('visible');
|
|
288
1221
|
|
|
289
1222
|
const totalTasks = currentTasks.length;
|
|
290
1223
|
const completed = currentTasks.filter(t => t.status === 'completed').length;
|
|
@@ -292,34 +1225,39 @@
|
|
|
292
1225
|
|
|
293
1226
|
sessionTitle.textContent = 'All Tasks';
|
|
294
1227
|
sessionMeta.textContent = `${totalTasks} tasks across ${sessions.length} sessions`;
|
|
295
|
-
|
|
296
1228
|
progressPercent.textContent = `${percent}%`;
|
|
297
1229
|
progressBar.style.width = `${percent}%`;
|
|
298
1230
|
|
|
299
1231
|
renderKanban();
|
|
300
1232
|
}
|
|
301
1233
|
|
|
302
|
-
// Render sessions sidebar
|
|
303
1234
|
function renderSessions() {
|
|
304
|
-
// Update all tasks button state
|
|
305
1235
|
const allTasksBtn = document.getElementById('all-tasks-btn');
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
1236
|
+
allTasksBtn.classList.toggle('active', viewMode === 'all');
|
|
1237
|
+
|
|
1238
|
+
const filteredSessions = hideInactive
|
|
1239
|
+
? sessions.filter(s => s.pending > 0 || s.inProgress > 0)
|
|
1240
|
+
: sessions;
|
|
1241
|
+
|
|
1242
|
+
// Get container for session items (after All Tasks button)
|
|
1243
|
+
let sessionsContainer = document.getElementById('sessions-items');
|
|
1244
|
+
if (!sessionsContainer) {
|
|
1245
|
+
sessionsContainer = document.createElement('div');
|
|
1246
|
+
sessionsContainer.id = 'sessions-items';
|
|
1247
|
+
sessionsList.appendChild(sessionsContainer);
|
|
310
1248
|
}
|
|
311
1249
|
|
|
312
|
-
if (
|
|
313
|
-
|
|
314
|
-
<div
|
|
315
|
-
<p
|
|
316
|
-
<p
|
|
1250
|
+
if (filteredSessions.length === 0) {
|
|
1251
|
+
sessionsContainer.innerHTML = `
|
|
1252
|
+
<div style="padding: 24px 12px; text-align: center; color: var(--text-muted); font-size: 12px;">
|
|
1253
|
+
<p>${hideInactive ? 'No active sessions' : 'No sessions found'}</p>
|
|
1254
|
+
<p style="margin-top: 8px; font-size: 11px;">${hideInactive ? 'Uncheck filter to see all' : 'Tasks appear when you use Claude Code'}</p>
|
|
317
1255
|
</div>
|
|
318
1256
|
`;
|
|
319
1257
|
return;
|
|
320
1258
|
}
|
|
321
1259
|
|
|
322
|
-
|
|
1260
|
+
sessionsContainer.innerHTML = filteredSessions.map(session => {
|
|
323
1261
|
const total = session.taskCount;
|
|
324
1262
|
const percent = total > 0 ? Math.round((session.completed / total) * 100) : 0;
|
|
325
1263
|
const isActive = session.id === currentSessionId && viewMode === 'session';
|
|
@@ -328,31 +1266,25 @@
|
|
|
328
1266
|
const projectName = session.project ? session.project.split('/').pop() : null;
|
|
329
1267
|
|
|
330
1268
|
return `
|
|
331
|
-
<button
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
<div class="flex items-center justify-between gap-2">
|
|
336
|
-
<span class="text-sm text-gray-200 truncate flex-1 ${session.name ? '' : 'font-mono text-xs text-gray-400'}">${escapeHtml(displayName)}</span>
|
|
337
|
-
${hasInProgress ? '<span class="w-2 h-2 rounded-full bg-claude-orange status-pulse flex-shrink-0"></span>' : ''}
|
|
1269
|
+
<button onclick="fetchTasks('${session.id}')" class="session-item ${isActive ? 'active' : ''}">
|
|
1270
|
+
<div class="session-name">
|
|
1271
|
+
<span>${escapeHtml(displayName)}</span>
|
|
1272
|
+
${hasInProgress ? '<span class="pulse"></span>' : ''}
|
|
338
1273
|
</div>
|
|
339
|
-
${projectName ? `<
|
|
340
|
-
<div class="
|
|
341
|
-
<div class="
|
|
342
|
-
|
|
343
|
-
</div>
|
|
344
|
-
<span class="text-xs text-gray-500">${session.completed}/${total}</span>
|
|
1274
|
+
${projectName ? `<div class="session-project">${escapeHtml(projectName)}</div>` : ''}
|
|
1275
|
+
<div class="session-progress">
|
|
1276
|
+
<div class="progress-bar"><div class="progress-fill" style="width: ${percent}%"></div></div>
|
|
1277
|
+
<span class="progress-text">${session.completed}/${total}</span>
|
|
345
1278
|
</div>
|
|
346
|
-
<
|
|
1279
|
+
<div class="session-time">${formatDate(session.modifiedAt)}</div>
|
|
347
1280
|
</button>
|
|
348
1281
|
`;
|
|
349
1282
|
}).join('');
|
|
350
1283
|
}
|
|
351
1284
|
|
|
352
|
-
// Render current session
|
|
353
1285
|
function renderSession() {
|
|
354
|
-
noSession.
|
|
355
|
-
sessionView.classList.
|
|
1286
|
+
noSession.style.display = 'none';
|
|
1287
|
+
sessionView.classList.add('visible');
|
|
356
1288
|
|
|
357
1289
|
const session = sessions.find(s => s.id === currentSessionId);
|
|
358
1290
|
if (!session) return;
|
|
@@ -360,7 +1292,7 @@
|
|
|
360
1292
|
const displayName = session.name || currentSessionId;
|
|
361
1293
|
sessionTitle.textContent = displayName;
|
|
362
1294
|
const projectName = session.project ? session.project.split('/').pop() : null;
|
|
363
|
-
sessionMeta.textContent = `${currentTasks.length} tasks${projectName ? ' · ' + projectName : ''} ·
|
|
1295
|
+
sessionMeta.textContent = `${currentTasks.length} tasks${projectName ? ' · ' + projectName : ''} · ${formatDate(session.modifiedAt)}`;
|
|
364
1296
|
|
|
365
1297
|
const completed = currentTasks.filter(t => t.status === 'completed').length;
|
|
366
1298
|
const percent = currentTasks.length > 0 ? Math.round((completed / currentTasks.length) * 100) : 0;
|
|
@@ -372,41 +1304,27 @@
|
|
|
372
1304
|
renderSessions();
|
|
373
1305
|
}
|
|
374
1306
|
|
|
375
|
-
// Render task card
|
|
376
1307
|
function renderTaskCard(task) {
|
|
377
1308
|
const isBlocked = task.blockedBy && task.blockedBy.length > 0;
|
|
378
|
-
const statusStyles = {
|
|
379
|
-
pending: 'border-gray-700 bg-gray-800/50',
|
|
380
|
-
in_progress: 'border-claude-orange/30 bg-claude-orange/10',
|
|
381
|
-
completed: 'border-green-500/30 bg-green-500/10'
|
|
382
|
-
};
|
|
383
1309
|
const taskId = viewMode === 'all' ? `${task.sessionId?.slice(0,4)}-${task.id}` : task.id;
|
|
384
1310
|
const sessionLabel = viewMode === 'all' && task.sessionName ? task.sessionName : null;
|
|
1311
|
+
const statusClass = task.status.replace('_', '-');
|
|
385
1312
|
|
|
386
1313
|
return `
|
|
387
|
-
<div
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
<div class="flex items-center gap-2 mb-2">
|
|
392
|
-
<span class="text-xs font-mono text-gray-500">#${taskId}</span>
|
|
393
|
-
${isBlocked ? '<span class="text-xs bg-yellow-500/20 text-yellow-400 px-1.5 py-0.5 rounded">blocked</span>' : ''}
|
|
1314
|
+
<div onclick="showTaskDetail('${task.id}', '${task.sessionId || ''}')" class="task-card ${statusClass} ${isBlocked ? 'blocked' : ''}">
|
|
1315
|
+
<div class="task-id">
|
|
1316
|
+
<span>#${taskId}</span>
|
|
1317
|
+
${isBlocked ? '<span class="task-badge blocked">Blocked</span>' : ''}
|
|
394
1318
|
</div>
|
|
395
|
-
<
|
|
396
|
-
${sessionLabel ? `<
|
|
397
|
-
${task.status === 'in_progress' && task.activeForm ?
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
${escapeHtml(task.activeForm)}
|
|
401
|
-
</p>
|
|
402
|
-
` : ''}
|
|
403
|
-
${isBlocked ? `<p class="text-xs text-gray-500 mt-2">Waiting on: ${task.blockedBy.map(id => '#' + id).join(', ')}</p>` : ''}
|
|
404
|
-
${task.description ? `<p class="text-xs text-gray-500 mt-2 line-clamp-2">${escapeHtml(task.description.split('\n')[0])}</p>` : ''}
|
|
1319
|
+
<div class="task-title">${escapeHtml(task.subject)}</div>
|
|
1320
|
+
${sessionLabel ? `<div class="task-session">${escapeHtml(sessionLabel)}</div>` : ''}
|
|
1321
|
+
${task.status === 'in_progress' && task.activeForm ? `<div class="task-active">${escapeHtml(task.activeForm)}</div>` : ''}
|
|
1322
|
+
${isBlocked ? `<div class="task-blocked">Waiting on ${task.blockedBy.map(id => '#' + id).join(', ')}</div>` : ''}
|
|
1323
|
+
${task.description ? `<div class="task-desc">${escapeHtml(task.description.split('\n')[0])}</div>` : ''}
|
|
405
1324
|
</div>
|
|
406
1325
|
`;
|
|
407
1326
|
}
|
|
408
1327
|
|
|
409
|
-
// Render kanban board
|
|
410
1328
|
function renderKanban() {
|
|
411
1329
|
const pending = currentTasks.filter(t => t.status === 'pending');
|
|
412
1330
|
const inProgress = currentTasks.filter(t => t.status === 'in_progress');
|
|
@@ -418,112 +1336,160 @@
|
|
|
418
1336
|
|
|
419
1337
|
pendingTasks.innerHTML = pending.length > 0
|
|
420
1338
|
? pending.map(renderTaskCard).join('')
|
|
421
|
-
: '<
|
|
1339
|
+
: '<div class="column-empty">No pending tasks</div>';
|
|
422
1340
|
|
|
423
1341
|
inProgressTasks.innerHTML = inProgress.length > 0
|
|
424
1342
|
? inProgress.map(renderTaskCard).join('')
|
|
425
|
-
: '<
|
|
1343
|
+
: '<div class="column-empty">No active tasks</div>';
|
|
426
1344
|
|
|
427
1345
|
completedTasks.innerHTML = completed.length > 0
|
|
428
1346
|
? completed.map(renderTaskCard).join('')
|
|
429
|
-
: '<
|
|
1347
|
+
: '<div class="column-empty">No completed tasks</div>';
|
|
430
1348
|
}
|
|
431
1349
|
|
|
432
|
-
// Show task detail
|
|
433
1350
|
function showTaskDetail(taskId, sessionId = null) {
|
|
434
|
-
const task = currentTasks.find(t =>
|
|
435
|
-
t.id === taskId && (!sessionId || t.sessionId === sessionId)
|
|
436
|
-
);
|
|
1351
|
+
const task = currentTasks.find(t => t.id === taskId && (!sessionId || t.sessionId === sessionId));
|
|
437
1352
|
if (!task) return;
|
|
438
1353
|
|
|
439
|
-
detailPanel.classList.
|
|
1354
|
+
detailPanel.classList.add('visible');
|
|
440
1355
|
|
|
441
1356
|
const statusLabels = {
|
|
442
|
-
completed: '<span class="
|
|
443
|
-
in_progress: '<span class="
|
|
444
|
-
pending: '<span class="
|
|
1357
|
+
completed: '<span class="detail-status completed"><span class="dot"></span>Completed</span>',
|
|
1358
|
+
in_progress: '<span class="detail-status in_progress"><span class="dot"></span>In Progress</span>',
|
|
1359
|
+
pending: '<span class="detail-status pending"><span class="dot"></span>Pending</span>'
|
|
445
1360
|
};
|
|
446
1361
|
|
|
447
1362
|
detailContent.innerHTML = `
|
|
448
|
-
<div class="
|
|
449
|
-
<div>
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
</div>
|
|
1363
|
+
<div class="detail-section">
|
|
1364
|
+
<div class="detail-label">Task #${task.id}</div>
|
|
1365
|
+
<h2 class="detail-title">${escapeHtml(task.subject)}</h2>
|
|
1366
|
+
</div>
|
|
453
1367
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
1368
|
+
<div class="detail-section">
|
|
1369
|
+
${statusLabels[task.status] || statusLabels.pending}
|
|
1370
|
+
</div>
|
|
457
1371
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
<
|
|
1372
|
+
${task.activeForm && task.status === 'in_progress' ? `
|
|
1373
|
+
<div class="detail-section">
|
|
1374
|
+
<div class="detail-box active">
|
|
1375
|
+
<strong>Currently:</strong> ${escapeHtml(task.activeForm)}
|
|
462
1376
|
</div>
|
|
463
|
-
|
|
1377
|
+
</div>
|
|
1378
|
+
` : ''}
|
|
464
1379
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
<
|
|
1380
|
+
${task.blockedBy && task.blockedBy.length > 0 ? `
|
|
1381
|
+
<div class="detail-section">
|
|
1382
|
+
<div class="detail-box blocked">
|
|
1383
|
+
<strong>Blocked by:</strong> ${task.blockedBy.map(id => '#' + id).join(', ')}
|
|
469
1384
|
</div>
|
|
470
|
-
|
|
1385
|
+
</div>
|
|
1386
|
+
` : ''}
|
|
471
1387
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
<
|
|
1388
|
+
${task.blocks && task.blocks.length > 0 ? `
|
|
1389
|
+
<div class="detail-section">
|
|
1390
|
+
<div class="detail-box blocks">
|
|
1391
|
+
<strong>Blocks:</strong> ${task.blocks.map(id => '#' + id).join(', ')}
|
|
476
1392
|
</div>
|
|
477
|
-
|
|
1393
|
+
</div>
|
|
1394
|
+
` : ''}
|
|
478
1395
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
1396
|
+
${task.description ? `
|
|
1397
|
+
<div class="detail-section">
|
|
1398
|
+
<div class="detail-label">Description</div>
|
|
1399
|
+
<div class="detail-desc">${DOMPurify.sanitize(marked.parse(task.description))}</div>
|
|
1400
|
+
</div>
|
|
1401
|
+
` : ''}
|
|
1402
|
+
|
|
1403
|
+
<div class="detail-section note-section">
|
|
1404
|
+
<div class="detail-label">Add Note</div>
|
|
1405
|
+
<form class="note-form" onsubmit="addNote(event, '${task.id}', '${task.sessionId || currentSessionId}')">
|
|
1406
|
+
<textarea id="note-input" class="note-input" placeholder="Add a note for Claude..." rows="3"></textarea>
|
|
1407
|
+
<button type="submit" class="note-submit">Add Note</button>
|
|
1408
|
+
</form>
|
|
487
1409
|
</div>
|
|
488
1410
|
`;
|
|
489
1411
|
}
|
|
490
1412
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
1413
|
+
async function addNote(event, taskId, sessionId) {
|
|
1414
|
+
event.preventDefault();
|
|
1415
|
+
const input = document.getElementById('note-input');
|
|
1416
|
+
const note = input.value.trim();
|
|
1417
|
+
if (!note) return;
|
|
1418
|
+
|
|
1419
|
+
try {
|
|
1420
|
+
const res = await fetch(`/api/tasks/${sessionId}/${taskId}/note`, {
|
|
1421
|
+
method: 'POST',
|
|
1422
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1423
|
+
body: JSON.stringify({ note })
|
|
1424
|
+
});
|
|
1425
|
+
|
|
1426
|
+
if (res.ok) {
|
|
1427
|
+
input.value = '';
|
|
1428
|
+
// Refresh to show updated description
|
|
1429
|
+
if (viewMode === 'all') {
|
|
1430
|
+
const tasksRes = await fetch('/api/tasks/all');
|
|
1431
|
+
currentTasks = await tasksRes.json();
|
|
1432
|
+
} else {
|
|
1433
|
+
await fetchTasks(sessionId);
|
|
1434
|
+
}
|
|
1435
|
+
showTaskDetail(taskId, sessionId);
|
|
1436
|
+
}
|
|
1437
|
+
} catch (error) {
|
|
1438
|
+
console.error('Failed to add note:', error);
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
function closeDetailPanel() {
|
|
1443
|
+
detailPanel.classList.remove('visible');
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
document.getElementById('close-detail').onclick = closeDetailPanel;
|
|
1447
|
+
|
|
1448
|
+
document.addEventListener('keydown', (e) => {
|
|
1449
|
+
if (e.key === 'Escape' && detailPanel.classList.contains('visible')) {
|
|
1450
|
+
closeDetailPanel();
|
|
1451
|
+
}
|
|
1452
|
+
});
|
|
495
1453
|
|
|
496
|
-
// Setup SSE for live updates
|
|
497
1454
|
function setupEventSource() {
|
|
498
|
-
|
|
1455
|
+
let retryDelay = 1000;
|
|
1456
|
+
let eventSource;
|
|
499
1457
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
<span class="w-2 h-2 rounded-full bg-green-500"></span>
|
|
503
|
-
<span class="text-gray-400">Live</span>
|
|
504
|
-
`;
|
|
505
|
-
};
|
|
1458
|
+
function connect() {
|
|
1459
|
+
eventSource = new EventSource('/api/events');
|
|
506
1460
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
1461
|
+
eventSource.onopen = () => {
|
|
1462
|
+
retryDelay = 1000; // Reset on successful connection
|
|
1463
|
+
connectionStatus.innerHTML = `
|
|
1464
|
+
<span class="connection-dot live"></span>
|
|
1465
|
+
<span>Connected</span>
|
|
1466
|
+
`;
|
|
1467
|
+
};
|
|
513
1468
|
|
|
514
|
-
|
|
515
|
-
|
|
1469
|
+
eventSource.onerror = () => {
|
|
1470
|
+
eventSource.close();
|
|
1471
|
+
connectionStatus.innerHTML = `
|
|
1472
|
+
<span class="connection-dot error"></span>
|
|
1473
|
+
<span>Reconnecting...</span>
|
|
1474
|
+
`;
|
|
1475
|
+
setTimeout(connect, retryDelay);
|
|
1476
|
+
retryDelay = Math.min(retryDelay * 2, 30000); // Max 30s
|
|
1477
|
+
};
|
|
516
1478
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
if (data.
|
|
520
|
-
|
|
1479
|
+
eventSource.onmessage = (event) => {
|
|
1480
|
+
const data = JSON.parse(event.data);
|
|
1481
|
+
if (data.type === 'update' || data.type === 'metadata-update') {
|
|
1482
|
+
fetchSessions();
|
|
1483
|
+
if (currentSessionId && data.sessionId === currentSessionId) {
|
|
1484
|
+
fetchTasks(currentSessionId);
|
|
1485
|
+
}
|
|
521
1486
|
}
|
|
522
|
-
}
|
|
523
|
-
}
|
|
1487
|
+
};
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
connect();
|
|
524
1491
|
}
|
|
525
1492
|
|
|
526
|
-
// Helpers
|
|
527
1493
|
function formatDate(dateStr) {
|
|
528
1494
|
const date = new Date(dateStr);
|
|
529
1495
|
const now = new Date();
|
|
@@ -541,43 +1507,37 @@
|
|
|
541
1507
|
return div.innerHTML;
|
|
542
1508
|
}
|
|
543
1509
|
|
|
544
|
-
|
|
1510
|
+
function toggleHideInactive() {
|
|
1511
|
+
hideInactive = document.getElementById('hide-inactive').checked;
|
|
1512
|
+
localStorage.setItem('hideInactive', hideInactive);
|
|
1513
|
+
renderSessions();
|
|
1514
|
+
}
|
|
1515
|
+
|
|
545
1516
|
function toggleTheme() {
|
|
546
|
-
const
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
body.classList.remove('light');
|
|
551
|
-
localStorage.setItem('theme', 'dark');
|
|
552
|
-
document.getElementById('theme-icon-dark').classList.remove('hidden');
|
|
553
|
-
document.getElementById('theme-icon-light').classList.add('hidden');
|
|
554
|
-
} else {
|
|
555
|
-
body.classList.add('light');
|
|
556
|
-
localStorage.setItem('theme', 'light');
|
|
557
|
-
document.getElementById('theme-icon-dark').classList.add('hidden');
|
|
558
|
-
document.getElementById('theme-icon-light').classList.remove('hidden');
|
|
559
|
-
}
|
|
1517
|
+
const isLight = document.body.classList.toggle('light');
|
|
1518
|
+
localStorage.setItem('theme', isLight ? 'light' : 'dark');
|
|
1519
|
+
document.getElementById('theme-icon-dark').style.display = isLight ? 'none' : 'block';
|
|
1520
|
+
document.getElementById('theme-icon-light').style.display = isLight ? 'block' : 'none';
|
|
560
1521
|
}
|
|
561
1522
|
|
|
562
|
-
// Load saved theme
|
|
563
1523
|
function loadTheme() {
|
|
564
|
-
|
|
565
|
-
if (saved === 'light') {
|
|
1524
|
+
if (localStorage.getItem('theme') === 'light') {
|
|
566
1525
|
document.body.classList.add('light');
|
|
567
|
-
document.getElementById('theme-icon-dark').
|
|
568
|
-
document.getElementById('theme-icon-light').
|
|
1526
|
+
document.getElementById('theme-icon-dark').style.display = 'none';
|
|
1527
|
+
document.getElementById('theme-icon-light').style.display = 'block';
|
|
569
1528
|
}
|
|
570
1529
|
}
|
|
571
1530
|
|
|
572
|
-
|
|
1531
|
+
function loadPreferences() {
|
|
1532
|
+
document.getElementById('hide-inactive').checked = hideInactive;
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1535
|
+
// Init
|
|
573
1536
|
loadTheme();
|
|
1537
|
+
loadPreferences();
|
|
574
1538
|
fetchSessions();
|
|
575
1539
|
setupEventSource();
|
|
576
|
-
|
|
577
|
-
// Default to All Tasks view
|
|
578
|
-
setTimeout(() => {
|
|
579
|
-
showAllTasks();
|
|
580
|
-
}, 500);
|
|
1540
|
+
setTimeout(showAllTasks, 500);
|
|
581
1541
|
</script>
|
|
582
1542
|
</body>
|
|
583
1543
|
</html>
|