clicodelog 0.1.0__py3-none-any.whl
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.
- clicodelog/__init__.py +6 -0
- clicodelog/__main__.py +6 -0
- clicodelog/app.py +996 -0
- clicodelog/cli.py +56 -0
- clicodelog/templates/index.html +1067 -0
- clicodelog-0.1.0.dist-info/METADATA +305 -0
- clicodelog-0.1.0.dist-info/RECORD +11 -0
- clicodelog-0.1.0.dist-info/WHEEL +5 -0
- clicodelog-0.1.0.dist-info/entry_points.txt +2 -0
- clicodelog-0.1.0.dist-info/licenses/LICENSE +21 -0
- clicodelog-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1067 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>AI Conversation History</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=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
|
10
|
+
<style>
|
|
11
|
+
/* Dark Theme (Default) */
|
|
12
|
+
:root {
|
|
13
|
+
--bg-primary: #0d1117;
|
|
14
|
+
--bg-secondary: #161b22;
|
|
15
|
+
--bg-tertiary: #21262d;
|
|
16
|
+
--border-color: #30363d;
|
|
17
|
+
--text-primary: #e6edf3;
|
|
18
|
+
--text-secondary: #8b949e;
|
|
19
|
+
--text-muted: #6e7681;
|
|
20
|
+
--accent-blue: #58a6ff;
|
|
21
|
+
--accent-purple: #a371f7;
|
|
22
|
+
--accent-green: #3fb950;
|
|
23
|
+
--accent-orange: #d29922;
|
|
24
|
+
--user-bg: #1f6feb22;
|
|
25
|
+
--assistant-bg: #238636;
|
|
26
|
+
--thinking-bg: #a371f722;
|
|
27
|
+
--shadow-color: rgba(0, 0, 0, 0.3);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/* Light Theme - Soft Blue */
|
|
31
|
+
[data-theme="light"] {
|
|
32
|
+
--bg-primary: #f0f6fc;
|
|
33
|
+
--bg-secondary: #ffffff;
|
|
34
|
+
--bg-tertiary: #e8f1fb;
|
|
35
|
+
--border-color: #c8d8e8;
|
|
36
|
+
--text-primary: #1a3a5c;
|
|
37
|
+
--text-secondary: #4a6a8a;
|
|
38
|
+
--text-muted: #6b8aaa;
|
|
39
|
+
--accent-blue: #2563eb;
|
|
40
|
+
--accent-purple: #7c3aed;
|
|
41
|
+
--accent-green: #16a34a;
|
|
42
|
+
--accent-orange: #d97706;
|
|
43
|
+
--user-bg: #dbeafe;
|
|
44
|
+
--assistant-bg: #16a34a;
|
|
45
|
+
--thinking-bg: #ede9fe;
|
|
46
|
+
--shadow-color: rgba(37, 99, 235, 0.1);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
* {
|
|
50
|
+
box-sizing: border-box;
|
|
51
|
+
margin: 0;
|
|
52
|
+
padding: 0;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
body {
|
|
56
|
+
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
|
|
57
|
+
background: var(--bg-primary);
|
|
58
|
+
color: var(--text-primary);
|
|
59
|
+
line-height: 1.6;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.app {
|
|
63
|
+
display: grid;
|
|
64
|
+
grid-template-columns: 280px 320px 1fr;
|
|
65
|
+
height: 100vh;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.panel {
|
|
69
|
+
border-right: 1px solid var(--border-color);
|
|
70
|
+
overflow-y: auto;
|
|
71
|
+
display: flex;
|
|
72
|
+
flex-direction: column;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.panel-header {
|
|
76
|
+
padding: 16px;
|
|
77
|
+
border-bottom: 1px solid var(--border-color);
|
|
78
|
+
background: var(--bg-secondary);
|
|
79
|
+
position: sticky;
|
|
80
|
+
top: 0;
|
|
81
|
+
z-index: 10;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.panel-header h2 {
|
|
85
|
+
font-size: 14px;
|
|
86
|
+
font-weight: 600;
|
|
87
|
+
color: var(--text-secondary);
|
|
88
|
+
text-transform: uppercase;
|
|
89
|
+
letter-spacing: 0.5px;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.panel-content {
|
|
93
|
+
flex: 1;
|
|
94
|
+
overflow-y: auto;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.search-box {
|
|
98
|
+
padding: 12px 16px;
|
|
99
|
+
border-bottom: 1px solid var(--border-color);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.search-box input {
|
|
103
|
+
width: 100%;
|
|
104
|
+
padding: 8px 12px;
|
|
105
|
+
background: var(--bg-tertiary);
|
|
106
|
+
border: 1px solid var(--border-color);
|
|
107
|
+
border-radius: 6px;
|
|
108
|
+
color: var(--text-primary);
|
|
109
|
+
font-size: 13px;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.search-box input:focus {
|
|
113
|
+
outline: none;
|
|
114
|
+
border-color: var(--accent-blue);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.list-item {
|
|
118
|
+
padding: 12px 16px;
|
|
119
|
+
border-bottom: 1px solid var(--border-color);
|
|
120
|
+
cursor: pointer;
|
|
121
|
+
transition: background 0.15s;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.list-item:hover {
|
|
125
|
+
background: var(--bg-secondary);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.list-item.active {
|
|
129
|
+
background: var(--bg-tertiary);
|
|
130
|
+
border-left: 3px solid var(--accent-blue);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.list-item-title {
|
|
134
|
+
font-size: 14px;
|
|
135
|
+
font-weight: 500;
|
|
136
|
+
margin-bottom: 4px;
|
|
137
|
+
white-space: nowrap;
|
|
138
|
+
overflow: hidden;
|
|
139
|
+
text-overflow: ellipsis;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.list-item-meta {
|
|
143
|
+
font-size: 12px;
|
|
144
|
+
color: var(--text-muted);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.conversation-panel {
|
|
148
|
+
border-right: none;
|
|
149
|
+
background: var(--bg-primary);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.conversation-header {
|
|
153
|
+
padding: 16px 24px;
|
|
154
|
+
border-bottom: 1px solid var(--border-color);
|
|
155
|
+
background: var(--bg-secondary);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.conversation-header h1 {
|
|
159
|
+
font-size: 18px;
|
|
160
|
+
font-weight: 600;
|
|
161
|
+
margin-bottom: 8px;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.conversation-meta {
|
|
165
|
+
display: flex;
|
|
166
|
+
gap: 16px;
|
|
167
|
+
font-size: 13px;
|
|
168
|
+
color: var(--text-secondary);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.messages {
|
|
172
|
+
padding: 24px;
|
|
173
|
+
display: flex;
|
|
174
|
+
flex-direction: column;
|
|
175
|
+
gap: 24px;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.message {
|
|
179
|
+
max-width: 100%;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.message-header {
|
|
183
|
+
display: flex;
|
|
184
|
+
align-items: center;
|
|
185
|
+
gap: 8px;
|
|
186
|
+
margin-bottom: 8px;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.message-role {
|
|
190
|
+
font-size: 12px;
|
|
191
|
+
font-weight: 600;
|
|
192
|
+
text-transform: uppercase;
|
|
193
|
+
letter-spacing: 0.5px;
|
|
194
|
+
padding: 2px 8px;
|
|
195
|
+
border-radius: 4px;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.message.user .message-role {
|
|
199
|
+
background: var(--accent-blue);
|
|
200
|
+
color: white;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.message.assistant .message-role {
|
|
204
|
+
background: var(--accent-purple);
|
|
205
|
+
color: white;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.message-time {
|
|
209
|
+
font-size: 11px;
|
|
210
|
+
color: var(--text-muted);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.message-content {
|
|
214
|
+
padding: 16px;
|
|
215
|
+
border-radius: 8px;
|
|
216
|
+
font-size: 14px;
|
|
217
|
+
white-space: pre-wrap;
|
|
218
|
+
word-wrap: break-word;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.message.user .message-content {
|
|
222
|
+
background: var(--user-bg);
|
|
223
|
+
border: 1px solid var(--border-color);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.message.assistant .message-content {
|
|
227
|
+
background: var(--bg-secondary);
|
|
228
|
+
border: 1px solid var(--border-color);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.thinking-block {
|
|
232
|
+
margin-top: 12px;
|
|
233
|
+
padding: 12px;
|
|
234
|
+
background: var(--thinking-bg);
|
|
235
|
+
border-radius: 6px;
|
|
236
|
+
border-left: 3px solid var(--accent-purple);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.thinking-header {
|
|
240
|
+
font-size: 11px;
|
|
241
|
+
font-weight: 600;
|
|
242
|
+
color: var(--accent-purple);
|
|
243
|
+
text-transform: uppercase;
|
|
244
|
+
margin-bottom: 8px;
|
|
245
|
+
cursor: pointer;
|
|
246
|
+
display: flex;
|
|
247
|
+
align-items: center;
|
|
248
|
+
gap: 6px;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.thinking-header::before {
|
|
252
|
+
content: '▶';
|
|
253
|
+
font-size: 8px;
|
|
254
|
+
transition: transform 0.2s;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.thinking-block.expanded .thinking-header::before {
|
|
258
|
+
transform: rotate(90deg);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.thinking-content {
|
|
262
|
+
display: none;
|
|
263
|
+
font-size: 13px;
|
|
264
|
+
color: var(--text-secondary);
|
|
265
|
+
font-family: 'JetBrains Mono', monospace;
|
|
266
|
+
white-space: pre-wrap;
|
|
267
|
+
max-height: 400px;
|
|
268
|
+
overflow-y: auto;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
.thinking-block.expanded .thinking-content {
|
|
272
|
+
display: block;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
.tool-uses {
|
|
276
|
+
margin-top: 12px;
|
|
277
|
+
display: flex;
|
|
278
|
+
flex-direction: column;
|
|
279
|
+
gap: 8px;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.tool-use {
|
|
283
|
+
padding: 12px;
|
|
284
|
+
background: var(--bg-tertiary);
|
|
285
|
+
border-radius: 6px;
|
|
286
|
+
border-left: 3px solid var(--accent-orange);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
.tool-name {
|
|
290
|
+
font-size: 12px;
|
|
291
|
+
font-weight: 600;
|
|
292
|
+
color: var(--accent-orange);
|
|
293
|
+
margin-bottom: 6px;
|
|
294
|
+
font-family: 'JetBrains Mono', monospace;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.tool-input {
|
|
298
|
+
font-size: 12px;
|
|
299
|
+
font-family: 'JetBrains Mono', monospace;
|
|
300
|
+
color: var(--text-secondary);
|
|
301
|
+
white-space: pre-wrap;
|
|
302
|
+
max-height: 200px;
|
|
303
|
+
overflow-y: auto;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.empty-state {
|
|
307
|
+
display: flex;
|
|
308
|
+
flex-direction: column;
|
|
309
|
+
align-items: center;
|
|
310
|
+
justify-content: center;
|
|
311
|
+
height: 100%;
|
|
312
|
+
color: var(--text-muted);
|
|
313
|
+
text-align: center;
|
|
314
|
+
padding: 40px;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.empty-state-icon {
|
|
318
|
+
font-size: 48px;
|
|
319
|
+
margin-bottom: 16px;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
.summaries {
|
|
323
|
+
padding: 12px 16px;
|
|
324
|
+
background: var(--bg-tertiary);
|
|
325
|
+
border-bottom: 1px solid var(--border-color);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
.summary-tag {
|
|
329
|
+
display: inline-block;
|
|
330
|
+
padding: 4px 8px;
|
|
331
|
+
background: var(--accent-green);
|
|
332
|
+
color: white;
|
|
333
|
+
border-radius: 4px;
|
|
334
|
+
font-size: 11px;
|
|
335
|
+
margin: 2px;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
.model-badge {
|
|
339
|
+
font-size: 11px;
|
|
340
|
+
padding: 2px 6px;
|
|
341
|
+
background: var(--bg-tertiary);
|
|
342
|
+
border-radius: 4px;
|
|
343
|
+
color: var(--text-muted);
|
|
344
|
+
font-family: 'JetBrains Mono', monospace;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
.usage-info {
|
|
348
|
+
font-size: 11px;
|
|
349
|
+
color: var(--text-muted);
|
|
350
|
+
margin-left: auto;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
code {
|
|
354
|
+
font-family: 'JetBrains Mono', monospace;
|
|
355
|
+
background: var(--bg-tertiary);
|
|
356
|
+
padding: 2px 6px;
|
|
357
|
+
border-radius: 4px;
|
|
358
|
+
font-size: 0.9em;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
pre {
|
|
362
|
+
background: var(--bg-tertiary);
|
|
363
|
+
padding: 12px;
|
|
364
|
+
border-radius: 6px;
|
|
365
|
+
overflow-x: auto;
|
|
366
|
+
font-family: 'JetBrains Mono', monospace;
|
|
367
|
+
font-size: 13px;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
.loading {
|
|
371
|
+
display: flex;
|
|
372
|
+
align-items: center;
|
|
373
|
+
justify-content: center;
|
|
374
|
+
padding: 40px;
|
|
375
|
+
color: var(--text-muted);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
@keyframes spin {
|
|
379
|
+
to { transform: rotate(360deg); }
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
.spinner {
|
|
383
|
+
width: 20px;
|
|
384
|
+
height: 20px;
|
|
385
|
+
border: 2px solid var(--border-color);
|
|
386
|
+
border-top-color: var(--accent-blue);
|
|
387
|
+
border-radius: 50%;
|
|
388
|
+
animation: spin 0.8s linear infinite;
|
|
389
|
+
margin-right: 12px;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/* Top Controls */
|
|
393
|
+
.top-controls {
|
|
394
|
+
position: fixed;
|
|
395
|
+
top: 16px;
|
|
396
|
+
right: 16px;
|
|
397
|
+
z-index: 100;
|
|
398
|
+
display: flex;
|
|
399
|
+
gap: 8px;
|
|
400
|
+
align-items: center;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
.control-btn {
|
|
404
|
+
background: var(--bg-secondary);
|
|
405
|
+
border: 1px solid var(--border-color);
|
|
406
|
+
border-radius: 8px;
|
|
407
|
+
padding: 8px 12px;
|
|
408
|
+
cursor: pointer;
|
|
409
|
+
display: flex;
|
|
410
|
+
align-items: center;
|
|
411
|
+
gap: 8px;
|
|
412
|
+
font-size: 13px;
|
|
413
|
+
font-weight: 500;
|
|
414
|
+
color: var(--text-primary);
|
|
415
|
+
transition: all 0.2s ease;
|
|
416
|
+
box-shadow: 0 2px 8px var(--shadow-color);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
.control-btn:hover {
|
|
420
|
+
background: var(--bg-tertiary);
|
|
421
|
+
border-color: var(--accent-blue);
|
|
422
|
+
transform: translateY(-1px);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
.control-btn .icon {
|
|
426
|
+
font-size: 16px;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
.control-btn.syncing .icon {
|
|
430
|
+
animation: spin 1s linear infinite;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
.control-btn:disabled {
|
|
434
|
+
opacity: 0.4;
|
|
435
|
+
cursor: not-allowed;
|
|
436
|
+
transform: none;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
.control-btn:disabled:hover {
|
|
440
|
+
background: var(--bg-secondary);
|
|
441
|
+
border-color: var(--border-color);
|
|
442
|
+
transform: none;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
.sync-status {
|
|
446
|
+
font-size: 11px;
|
|
447
|
+
color: var(--text-muted);
|
|
448
|
+
background: var(--bg-secondary);
|
|
449
|
+
border: 1px solid var(--border-color);
|
|
450
|
+
border-radius: 6px;
|
|
451
|
+
padding: 6px 10px;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
/* Light theme specific adjustments */
|
|
456
|
+
[data-theme="light"] .message.user .message-content {
|
|
457
|
+
background: var(--user-bg);
|
|
458
|
+
border: 1px solid #bfdbfe;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
[data-theme="light"] .message.assistant .message-content {
|
|
462
|
+
background: var(--bg-secondary);
|
|
463
|
+
border: 1px solid var(--border-color);
|
|
464
|
+
box-shadow: 0 1px 3px var(--shadow-color);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
[data-theme="light"] .thinking-block {
|
|
468
|
+
background: var(--thinking-bg);
|
|
469
|
+
border-left: 3px solid var(--accent-purple);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
[data-theme="light"] .tool-use {
|
|
473
|
+
background: #fef3c7;
|
|
474
|
+
border-left: 3px solid var(--accent-orange);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
[data-theme="light"] .search-box input {
|
|
478
|
+
background: var(--bg-secondary);
|
|
479
|
+
border: 1px solid var(--border-color);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
[data-theme="light"] .search-box input:focus {
|
|
483
|
+
border-color: var(--accent-blue);
|
|
484
|
+
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
[data-theme="light"] .list-item:hover {
|
|
488
|
+
background: var(--bg-tertiary);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
[data-theme="light"] .list-item.active {
|
|
492
|
+
background: #dbeafe;
|
|
493
|
+
border-left: 3px solid var(--accent-blue);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
[data-theme="light"] .panel-header {
|
|
497
|
+
background: var(--bg-secondary);
|
|
498
|
+
box-shadow: 0 1px 2px var(--shadow-color);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
[data-theme="light"] .conversation-header {
|
|
502
|
+
background: var(--bg-secondary);
|
|
503
|
+
box-shadow: 0 1px 2px var(--shadow-color);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
[data-theme="light"] code {
|
|
507
|
+
background: #e0e7ff;
|
|
508
|
+
color: #3730a3;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
[data-theme="light"] pre {
|
|
512
|
+
background: #f1f5f9;
|
|
513
|
+
border: 1px solid var(--border-color);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
[data-theme="light"] .model-badge {
|
|
517
|
+
background: #e0e7ff;
|
|
518
|
+
color: #3730a3;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
[data-theme="light"] .summaries {
|
|
522
|
+
background: #ecfdf5;
|
|
523
|
+
border-bottom: 1px solid #a7f3d0;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
[data-theme="light"] .summary-tag {
|
|
527
|
+
background: var(--accent-green);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/* Source Selector Styles */
|
|
531
|
+
.source-selector {
|
|
532
|
+
display: flex;
|
|
533
|
+
align-items: center;
|
|
534
|
+
gap: 8px;
|
|
535
|
+
background: var(--bg-secondary);
|
|
536
|
+
border: 1px solid var(--border-color);
|
|
537
|
+
border-radius: 8px;
|
|
538
|
+
padding: 6px 12px;
|
|
539
|
+
box-shadow: 0 2px 8px var(--shadow-color);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
.source-selector label {
|
|
543
|
+
font-size: 12px;
|
|
544
|
+
color: var(--text-secondary);
|
|
545
|
+
font-weight: 500;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
.source-selector select {
|
|
549
|
+
background: var(--bg-tertiary);
|
|
550
|
+
border: 1px solid var(--border-color);
|
|
551
|
+
border-radius: 4px;
|
|
552
|
+
padding: 4px 8px;
|
|
553
|
+
color: var(--text-primary);
|
|
554
|
+
font-size: 13px;
|
|
555
|
+
font-weight: 500;
|
|
556
|
+
cursor: pointer;
|
|
557
|
+
min-width: 140px;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
.source-selector select:focus {
|
|
561
|
+
outline: none;
|
|
562
|
+
border-color: var(--accent-blue);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
.source-indicator {
|
|
566
|
+
width: 8px;
|
|
567
|
+
height: 8px;
|
|
568
|
+
border-radius: 50%;
|
|
569
|
+
background: var(--accent-green);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
.source-indicator.unavailable {
|
|
573
|
+
background: var(--text-muted);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
[data-theme="light"] .source-selector select {
|
|
577
|
+
background: var(--bg-secondary);
|
|
578
|
+
}
|
|
579
|
+
</style>
|
|
580
|
+
</head>
|
|
581
|
+
<body>
|
|
582
|
+
<!-- Top Controls -->
|
|
583
|
+
<div class="top-controls">
|
|
584
|
+
<div class="source-selector">
|
|
585
|
+
<label>Source:</label>
|
|
586
|
+
<select id="source-select" onchange="changeSource(this.value)">
|
|
587
|
+
<option value="claude-code">Claude Code</option>
|
|
588
|
+
<option value="codex">OpenAI Codex</option>
|
|
589
|
+
<option value="gemini">Google Gemini</option>
|
|
590
|
+
</select>
|
|
591
|
+
</div>
|
|
592
|
+
<span class="sync-status" id="sync-status" title="Last sync time">Syncing...</span>
|
|
593
|
+
<button class="control-btn" id="export-btn" onclick="exportConversation()" title="Export conversation as TXT" disabled>
|
|
594
|
+
<span class="icon">📥</span>
|
|
595
|
+
<span>Export</span>
|
|
596
|
+
</button>
|
|
597
|
+
<button class="control-btn" id="sync-btn" onclick="manualSync()" title="Sync data now">
|
|
598
|
+
<span class="icon">🔄</span>
|
|
599
|
+
<span>Sync</span>
|
|
600
|
+
</button>
|
|
601
|
+
<button class="control-btn" onclick="toggleTheme()" title="Toggle theme">
|
|
602
|
+
<span class="icon" id="theme-icon">🌙</span>
|
|
603
|
+
<span id="theme-label">Dark</span>
|
|
604
|
+
</button>
|
|
605
|
+
</div>
|
|
606
|
+
|
|
607
|
+
<div class="app">
|
|
608
|
+
<!-- Projects Panel -->
|
|
609
|
+
<div class="panel" id="projects-panel">
|
|
610
|
+
<div class="panel-header">
|
|
611
|
+
<h2>Projects</h2>
|
|
612
|
+
</div>
|
|
613
|
+
<div class="search-box">
|
|
614
|
+
<input type="text" id="project-search" placeholder="Search projects...">
|
|
615
|
+
</div>
|
|
616
|
+
<div class="panel-content" id="projects-list">
|
|
617
|
+
<div class="loading">
|
|
618
|
+
<div class="spinner"></div>
|
|
619
|
+
Loading projects...
|
|
620
|
+
</div>
|
|
621
|
+
</div>
|
|
622
|
+
</div>
|
|
623
|
+
|
|
624
|
+
<!-- Sessions Panel -->
|
|
625
|
+
<div class="panel" id="sessions-panel">
|
|
626
|
+
<div class="panel-header">
|
|
627
|
+
<h2>Sessions</h2>
|
|
628
|
+
</div>
|
|
629
|
+
<div class="panel-content" id="sessions-list">
|
|
630
|
+
<div class="empty-state">
|
|
631
|
+
<div class="empty-state-icon">📁</div>
|
|
632
|
+
<p>Select a project to view sessions</p>
|
|
633
|
+
</div>
|
|
634
|
+
</div>
|
|
635
|
+
</div>
|
|
636
|
+
|
|
637
|
+
<!-- Conversation Panel -->
|
|
638
|
+
<div class="panel conversation-panel" id="conversation-panel">
|
|
639
|
+
<div id="conversation-content">
|
|
640
|
+
<div class="empty-state">
|
|
641
|
+
<div class="empty-state-icon">💬</div>
|
|
642
|
+
<h3>AI Conversation History</h3>
|
|
643
|
+
<p>Select a session to view the conversation</p>
|
|
644
|
+
</div>
|
|
645
|
+
</div>
|
|
646
|
+
</div>
|
|
647
|
+
</div>
|
|
648
|
+
|
|
649
|
+
<script>
|
|
650
|
+
let currentProjectId = null;
|
|
651
|
+
let currentSessionId = null;
|
|
652
|
+
let currentSource = 'claude-code';
|
|
653
|
+
let projects = [];
|
|
654
|
+
let availableSources = [];
|
|
655
|
+
|
|
656
|
+
// Theme Management
|
|
657
|
+
function getPreferredTheme() {
|
|
658
|
+
const saved = localStorage.getItem('theme');
|
|
659
|
+
if (saved) return saved;
|
|
660
|
+
// Default to light theme as requested
|
|
661
|
+
return 'light';
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
function setTheme(theme) {
|
|
665
|
+
document.documentElement.setAttribute('data-theme', theme);
|
|
666
|
+
localStorage.setItem('theme', theme);
|
|
667
|
+
updateThemeToggle(theme);
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
function updateThemeToggle(theme) {
|
|
671
|
+
const icon = document.getElementById('theme-icon');
|
|
672
|
+
const label = document.getElementById('theme-label');
|
|
673
|
+
if (theme === 'light') {
|
|
674
|
+
icon.textContent = '☀️';
|
|
675
|
+
label.textContent = 'Light';
|
|
676
|
+
} else {
|
|
677
|
+
icon.textContent = '🌙';
|
|
678
|
+
label.textContent = 'Dark';
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
function toggleTheme() {
|
|
683
|
+
const current = document.documentElement.getAttribute('data-theme') || 'dark';
|
|
684
|
+
const newTheme = current === 'light' ? 'dark' : 'light';
|
|
685
|
+
setTheme(newTheme);
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
// Initialize theme on page load
|
|
689
|
+
(function() {
|
|
690
|
+
const theme = getPreferredTheme();
|
|
691
|
+
setTheme(theme);
|
|
692
|
+
})();
|
|
693
|
+
|
|
694
|
+
// Source Management
|
|
695
|
+
async function loadSources() {
|
|
696
|
+
try {
|
|
697
|
+
const response = await fetch('/api/sources');
|
|
698
|
+
const data = await response.json();
|
|
699
|
+
availableSources = data.sources;
|
|
700
|
+
currentSource = data.current;
|
|
701
|
+
|
|
702
|
+
const select = document.getElementById('source-select');
|
|
703
|
+
select.innerHTML = availableSources.map(source => `
|
|
704
|
+
<option value="${source.id}" ${source.id === currentSource ? 'selected' : ''} ${!source.available ? 'disabled' : ''}>
|
|
705
|
+
${source.name}${!source.available ? ' (not found)' : ''}
|
|
706
|
+
</option>
|
|
707
|
+
`).join('');
|
|
708
|
+
} catch (error) {
|
|
709
|
+
console.error('Error loading sources:', error);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
async function changeSource(sourceId) {
|
|
714
|
+
if (sourceId === currentSource) return;
|
|
715
|
+
|
|
716
|
+
currentSource = sourceId;
|
|
717
|
+
currentProjectId = null;
|
|
718
|
+
currentSessionId = null;
|
|
719
|
+
|
|
720
|
+
// Update UI
|
|
721
|
+
document.getElementById('export-btn').disabled = true;
|
|
722
|
+
|
|
723
|
+
// Reset panels
|
|
724
|
+
document.getElementById('sessions-list').innerHTML = `
|
|
725
|
+
<div class="empty-state">
|
|
726
|
+
<div class="empty-state-icon">📁</div>
|
|
727
|
+
<p>Select a project to view sessions</p>
|
|
728
|
+
</div>
|
|
729
|
+
`;
|
|
730
|
+
document.getElementById('conversation-content').innerHTML = `
|
|
731
|
+
<div class="empty-state">
|
|
732
|
+
<div class="empty-state-icon">💬</div>
|
|
733
|
+
<h3>AI Conversation History</h3>
|
|
734
|
+
<p>Select a session to view the conversation</p>
|
|
735
|
+
</div>
|
|
736
|
+
`;
|
|
737
|
+
|
|
738
|
+
// Load projects for new source
|
|
739
|
+
await loadProjects();
|
|
740
|
+
await fetchSyncStatus();
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
// Sync Management
|
|
744
|
+
async function fetchSyncStatus() {
|
|
745
|
+
try {
|
|
746
|
+
const response = await fetch(`/api/status?source=${currentSource}`);
|
|
747
|
+
const data = await response.json();
|
|
748
|
+
updateSyncStatus(data.last_sync);
|
|
749
|
+
} catch (error) {
|
|
750
|
+
console.error('Error fetching sync status:', error);
|
|
751
|
+
document.getElementById('sync-status').textContent = 'Status unavailable';
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
function updateSyncStatus(lastSync) {
|
|
756
|
+
const statusEl = document.getElementById('sync-status');
|
|
757
|
+
if (lastSync) {
|
|
758
|
+
const date = new Date(lastSync);
|
|
759
|
+
const timeStr = date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
|
760
|
+
statusEl.textContent = `Last sync: ${timeStr}`;
|
|
761
|
+
statusEl.title = `Last sync: ${date.toLocaleString()}`;
|
|
762
|
+
} else {
|
|
763
|
+
statusEl.textContent = 'Not synced';
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
async function manualSync() {
|
|
768
|
+
const btn = document.getElementById('sync-btn');
|
|
769
|
+
const statusEl = document.getElementById('sync-status');
|
|
770
|
+
|
|
771
|
+
btn.classList.add('syncing');
|
|
772
|
+
btn.disabled = true;
|
|
773
|
+
statusEl.textContent = 'Syncing...';
|
|
774
|
+
|
|
775
|
+
try {
|
|
776
|
+
const response = await fetch(`/api/sync?source=${currentSource}`, { method: 'POST' });
|
|
777
|
+
const data = await response.json();
|
|
778
|
+
|
|
779
|
+
if (data.status === 'success') {
|
|
780
|
+
updateSyncStatus(data.last_sync);
|
|
781
|
+
// Reload projects to show any new data
|
|
782
|
+
await loadProjects();
|
|
783
|
+
} else {
|
|
784
|
+
statusEl.textContent = 'Sync failed';
|
|
785
|
+
}
|
|
786
|
+
} catch (error) {
|
|
787
|
+
console.error('Error syncing:', error);
|
|
788
|
+
statusEl.textContent = 'Sync error';
|
|
789
|
+
} finally {
|
|
790
|
+
btn.classList.remove('syncing');
|
|
791
|
+
btn.disabled = false;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
// Fetch sync status on load and periodically
|
|
796
|
+
fetchSyncStatus();
|
|
797
|
+
setInterval(fetchSyncStatus, 60000); // Update status every minute
|
|
798
|
+
|
|
799
|
+
// Export conversation as text file
|
|
800
|
+
function exportConversation() {
|
|
801
|
+
if (!currentProjectId || !currentSessionId) return;
|
|
802
|
+
|
|
803
|
+
const url = `/api/projects/${currentProjectId}/sessions/${currentSessionId}/export?source=${currentSource}`;
|
|
804
|
+
|
|
805
|
+
// Create a temporary link and trigger download
|
|
806
|
+
const link = document.createElement('a');
|
|
807
|
+
link.href = url;
|
|
808
|
+
link.download = `${currentSessionId}.txt`;
|
|
809
|
+
document.body.appendChild(link);
|
|
810
|
+
link.click();
|
|
811
|
+
document.body.removeChild(link);
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
// Format timestamp
|
|
815
|
+
function formatTime(timestamp) {
|
|
816
|
+
if (!timestamp) return '';
|
|
817
|
+
const date = new Date(timestamp);
|
|
818
|
+
return date.toLocaleString();
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
// Format file size
|
|
822
|
+
function formatSize(bytes) {
|
|
823
|
+
if (bytes < 1024) return bytes + ' B';
|
|
824
|
+
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
|
|
825
|
+
return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
// Escape HTML
|
|
829
|
+
function escapeHtml(text) {
|
|
830
|
+
if (!text) return '';
|
|
831
|
+
const div = document.createElement('div');
|
|
832
|
+
div.textContent = text;
|
|
833
|
+
return div.innerHTML;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
// Format tool input
|
|
837
|
+
function formatToolInput(input) {
|
|
838
|
+
if (typeof input === 'string') return escapeHtml(input);
|
|
839
|
+
try {
|
|
840
|
+
return escapeHtml(JSON.stringify(input, null, 2));
|
|
841
|
+
} catch {
|
|
842
|
+
return escapeHtml(String(input));
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
// Load projects
|
|
847
|
+
async function loadProjects() {
|
|
848
|
+
try {
|
|
849
|
+
const response = await fetch(`/api/projects?source=${currentSource}`);
|
|
850
|
+
projects = await response.json();
|
|
851
|
+
renderProjects(projects);
|
|
852
|
+
} catch (error) {
|
|
853
|
+
console.error('Error loading projects:', error);
|
|
854
|
+
document.getElementById('projects-list').innerHTML = `
|
|
855
|
+
<div class="empty-state">
|
|
856
|
+
<p>Error loading projects</p>
|
|
857
|
+
</div>
|
|
858
|
+
`;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
// Render projects list
|
|
863
|
+
function renderProjects(projectsList) {
|
|
864
|
+
const container = document.getElementById('projects-list');
|
|
865
|
+
if (projectsList.length === 0) {
|
|
866
|
+
container.innerHTML = `
|
|
867
|
+
<div class="empty-state">
|
|
868
|
+
<p>No projects found</p>
|
|
869
|
+
</div>
|
|
870
|
+
`;
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
container.innerHTML = projectsList.map(project => `
|
|
875
|
+
<div class="list-item ${project.id === currentProjectId ? 'active' : ''}"
|
|
876
|
+
onclick="selectProject('${project.id}')">
|
|
877
|
+
<div class="list-item-title">${escapeHtml(project.name)}</div>
|
|
878
|
+
<div class="list-item-meta">${project.session_count} sessions</div>
|
|
879
|
+
</div>
|
|
880
|
+
`).join('');
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
// Select project
|
|
884
|
+
async function selectProject(projectId) {
|
|
885
|
+
currentProjectId = projectId;
|
|
886
|
+
currentSessionId = null;
|
|
887
|
+
renderProjects(projects);
|
|
888
|
+
|
|
889
|
+
// Disable export button when switching projects
|
|
890
|
+
document.getElementById('export-btn').disabled = true;
|
|
891
|
+
|
|
892
|
+
const sessionsContainer = document.getElementById('sessions-list');
|
|
893
|
+
sessionsContainer.innerHTML = `
|
|
894
|
+
<div class="loading">
|
|
895
|
+
<div class="spinner"></div>
|
|
896
|
+
Loading sessions...
|
|
897
|
+
</div>
|
|
898
|
+
`;
|
|
899
|
+
|
|
900
|
+
try {
|
|
901
|
+
const response = await fetch(`/api/projects/${projectId}/sessions?source=${currentSource}`);
|
|
902
|
+
const sessions = await response.json();
|
|
903
|
+
renderSessions(sessions);
|
|
904
|
+
} catch (error) {
|
|
905
|
+
console.error('Error loading sessions:', error);
|
|
906
|
+
sessionsContainer.innerHTML = `
|
|
907
|
+
<div class="empty-state">
|
|
908
|
+
<p>Error loading sessions</p>
|
|
909
|
+
</div>
|
|
910
|
+
`;
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
// Render sessions list
|
|
915
|
+
function renderSessions(sessions) {
|
|
916
|
+
const container = document.getElementById('sessions-list');
|
|
917
|
+
if (sessions.length === 0) {
|
|
918
|
+
container.innerHTML = `
|
|
919
|
+
<div class="empty-state">
|
|
920
|
+
<p>No sessions found</p>
|
|
921
|
+
</div>
|
|
922
|
+
`;
|
|
923
|
+
return;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
container.innerHTML = sessions.map(session => `
|
|
927
|
+
<div class="list-item ${session.id === currentSessionId ? 'active' : ''}"
|
|
928
|
+
onclick="selectSession('${session.id}')">
|
|
929
|
+
<div class="list-item-title">${escapeHtml(session.summary)}</div>
|
|
930
|
+
<div class="list-item-meta">
|
|
931
|
+
${session.message_count} messages • ${formatSize(session.size)}
|
|
932
|
+
<br>${formatTime(session.last_timestamp)}
|
|
933
|
+
</div>
|
|
934
|
+
</div>
|
|
935
|
+
`).join('');
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
// Select session
|
|
939
|
+
async function selectSession(sessionId) {
|
|
940
|
+
currentSessionId = sessionId;
|
|
941
|
+
// Re-render sessions to update active state
|
|
942
|
+
const sessionsResponse = await fetch(`/api/projects/${currentProjectId}/sessions?source=${currentSource}`);
|
|
943
|
+
const sessions = await sessionsResponse.json();
|
|
944
|
+
renderSessions(sessions);
|
|
945
|
+
|
|
946
|
+
const conversationContainer = document.getElementById('conversation-content');
|
|
947
|
+
conversationContainer.innerHTML = `
|
|
948
|
+
<div class="loading">
|
|
949
|
+
<div class="spinner"></div>
|
|
950
|
+
Loading conversation...
|
|
951
|
+
</div>
|
|
952
|
+
`;
|
|
953
|
+
|
|
954
|
+
try {
|
|
955
|
+
const response = await fetch(`/api/projects/${currentProjectId}/sessions/${sessionId}?source=${currentSource}`);
|
|
956
|
+
const conversation = await response.json();
|
|
957
|
+
renderConversation(conversation);
|
|
958
|
+
} catch (error) {
|
|
959
|
+
console.error('Error loading conversation:', error);
|
|
960
|
+
conversationContainer.innerHTML = `
|
|
961
|
+
<div class="empty-state">
|
|
962
|
+
<p>Error loading conversation</p>
|
|
963
|
+
</div>
|
|
964
|
+
`;
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
// Render conversation
|
|
969
|
+
function renderConversation(conversation) {
|
|
970
|
+
const container = document.getElementById('conversation-content');
|
|
971
|
+
|
|
972
|
+
let summariesHtml = '';
|
|
973
|
+
if (conversation.summaries && conversation.summaries.length > 0) {
|
|
974
|
+
summariesHtml = `
|
|
975
|
+
<div class="summaries">
|
|
976
|
+
<strong>Summaries:</strong><br>
|
|
977
|
+
${conversation.summaries.map(s => `<span class="summary-tag">${escapeHtml(s)}</span>`).join('')}
|
|
978
|
+
</div>
|
|
979
|
+
`;
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
const messagesHtml = conversation.messages.map(msg => {
|
|
983
|
+
let thinkingHtml = '';
|
|
984
|
+
if (msg.thinking) {
|
|
985
|
+
thinkingHtml = `
|
|
986
|
+
<div class="thinking-block" onclick="this.classList.toggle('expanded')">
|
|
987
|
+
<div class="thinking-header">Thinking</div>
|
|
988
|
+
<div class="thinking-content">${escapeHtml(msg.thinking)}</div>
|
|
989
|
+
</div>
|
|
990
|
+
`;
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
let toolUsesHtml = '';
|
|
994
|
+
if (msg.tool_uses && msg.tool_uses.length > 0) {
|
|
995
|
+
toolUsesHtml = `
|
|
996
|
+
<div class="tool-uses">
|
|
997
|
+
${msg.tool_uses.map(tool => `
|
|
998
|
+
<div class="tool-use">
|
|
999
|
+
<div class="tool-name">${escapeHtml(tool.name)}</div>
|
|
1000
|
+
<div class="tool-input">${formatToolInput(tool.input)}</div>
|
|
1001
|
+
</div>
|
|
1002
|
+
`).join('')}
|
|
1003
|
+
</div>
|
|
1004
|
+
`;
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
let usageHtml = '';
|
|
1008
|
+
if (msg.usage) {
|
|
1009
|
+
const tokens = msg.usage.input_tokens + (msg.usage.output_tokens || 0);
|
|
1010
|
+
usageHtml = `<span class="usage-info">${tokens.toLocaleString()} tokens</span>`;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
let modelHtml = '';
|
|
1014
|
+
if (msg.model) {
|
|
1015
|
+
modelHtml = `<span class="model-badge">${msg.model}</span>`;
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
return `
|
|
1019
|
+
<div class="message ${msg.role}">
|
|
1020
|
+
<div class="message-header">
|
|
1021
|
+
<span class="message-role">${msg.role}</span>
|
|
1022
|
+
${modelHtml}
|
|
1023
|
+
<span class="message-time">${formatTime(msg.timestamp)}</span>
|
|
1024
|
+
${usageHtml}
|
|
1025
|
+
</div>
|
|
1026
|
+
<div class="message-content">${escapeHtml(msg.content)}</div>
|
|
1027
|
+
${thinkingHtml}
|
|
1028
|
+
${toolUsesHtml}
|
|
1029
|
+
</div>
|
|
1030
|
+
`;
|
|
1031
|
+
}).join('');
|
|
1032
|
+
|
|
1033
|
+
container.innerHTML = `
|
|
1034
|
+
<div class="conversation-header">
|
|
1035
|
+
<h1>Session: ${conversation.session_id}</h1>
|
|
1036
|
+
<div class="conversation-meta">
|
|
1037
|
+
<span>${conversation.messages.length} messages</span>
|
|
1038
|
+
</div>
|
|
1039
|
+
</div>
|
|
1040
|
+
${summariesHtml}
|
|
1041
|
+
<div class="messages">
|
|
1042
|
+
${messagesHtml}
|
|
1043
|
+
</div>
|
|
1044
|
+
`;
|
|
1045
|
+
|
|
1046
|
+
// Enable export button
|
|
1047
|
+
document.getElementById('export-btn').disabled = false;
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
// Search projects
|
|
1051
|
+
document.getElementById('project-search').addEventListener('input', (e) => {
|
|
1052
|
+
const query = e.target.value.toLowerCase();
|
|
1053
|
+
const filtered = projects.filter(p =>
|
|
1054
|
+
p.name.toLowerCase().includes(query)
|
|
1055
|
+
);
|
|
1056
|
+
renderProjects(filtered);
|
|
1057
|
+
});
|
|
1058
|
+
|
|
1059
|
+
// Initialize
|
|
1060
|
+
async function init() {
|
|
1061
|
+
await loadSources();
|
|
1062
|
+
await loadProjects();
|
|
1063
|
+
}
|
|
1064
|
+
init();
|
|
1065
|
+
</script>
|
|
1066
|
+
</body>
|
|
1067
|
+
</html>
|