claude-code-templates 1.8.0 → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +246 -0
- package/package.json +26 -12
- package/src/analytics/core/ConversationAnalyzer.js +754 -0
- package/src/analytics/core/FileWatcher.js +285 -0
- package/src/analytics/core/ProcessDetector.js +242 -0
- package/src/analytics/core/SessionAnalyzer.js +597 -0
- package/src/analytics/core/StateCalculator.js +190 -0
- package/src/analytics/data/DataCache.js +550 -0
- package/src/analytics/notifications/NotificationManager.js +448 -0
- package/src/analytics/notifications/WebSocketServer.js +526 -0
- package/src/analytics/utils/PerformanceMonitor.js +455 -0
- package/src/analytics-web/assets/js/main.js +312 -0
- package/src/analytics-web/components/Charts.js +114 -0
- package/src/analytics-web/components/ConversationTable.js +437 -0
- package/src/analytics-web/components/Dashboard.js +573 -0
- package/src/analytics-web/components/SessionTimer.js +596 -0
- package/src/analytics-web/index.html +882 -49
- package/src/analytics-web/index.html.original +1939 -0
- package/src/analytics-web/services/DataService.js +357 -0
- package/src/analytics-web/services/StateService.js +276 -0
- package/src/analytics-web/services/WebSocketService.js +523 -0
- package/src/analytics.js +626 -2317
- package/src/analytics.log +0 -0
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>Claude Code Analytics - Terminal</title>
|
|
7
7
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js"></script>
|
|
8
|
+
<script src="components/SessionTimer.js"></script>
|
|
8
9
|
<style>
|
|
9
10
|
* {
|
|
10
11
|
margin: 0;
|
|
@@ -121,6 +122,65 @@
|
|
|
121
122
|
margin-top: 2px;
|
|
122
123
|
}
|
|
123
124
|
|
|
125
|
+
.token-popover, .claude-sessions-popover {
|
|
126
|
+
position: absolute;
|
|
127
|
+
top: 100%;
|
|
128
|
+
left: 50%;
|
|
129
|
+
transform: translateX(-50%);
|
|
130
|
+
margin-top: 8px;
|
|
131
|
+
padding: 12px;
|
|
132
|
+
background: #21262d;
|
|
133
|
+
border: 1px solid #30363d;
|
|
134
|
+
border-radius: 6px;
|
|
135
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
|
|
136
|
+
display: none;
|
|
137
|
+
z-index: 1000;
|
|
138
|
+
min-width: 200px;
|
|
139
|
+
white-space: nowrap;
|
|
140
|
+
pointer-events: auto;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.token-popover::before, .claude-sessions-popover::before {
|
|
144
|
+
content: '';
|
|
145
|
+
position: absolute;
|
|
146
|
+
bottom: 100%;
|
|
147
|
+
left: 50%;
|
|
148
|
+
transform: translateX(-50%);
|
|
149
|
+
border: 6px solid transparent;
|
|
150
|
+
border-bottom-color: #30363d;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.token-popover::after, .claude-sessions-popover::after {
|
|
154
|
+
content: '';
|
|
155
|
+
position: absolute;
|
|
156
|
+
bottom: 100%;
|
|
157
|
+
left: 50%;
|
|
158
|
+
transform: translateX(-50%);
|
|
159
|
+
border: 5px solid transparent;
|
|
160
|
+
border-bottom-color: #21262d;
|
|
161
|
+
margin-top: 1px;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.token-breakdown-item, .session-breakdown-item {
|
|
165
|
+
display: flex;
|
|
166
|
+
justify-content: space-between;
|
|
167
|
+
margin-bottom: 6px;
|
|
168
|
+
font-size: 0.85rem;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.token-breakdown-item:last-child, .session-breakdown-item:last-child {
|
|
172
|
+
margin-bottom: 0;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.token-type, .session-type {
|
|
176
|
+
color: #7d8590;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.token-value, .session-value {
|
|
180
|
+
color: #c9d1d9;
|
|
181
|
+
font-weight: 500;
|
|
182
|
+
}
|
|
183
|
+
|
|
124
184
|
.chart-controls {
|
|
125
185
|
display: flex;
|
|
126
186
|
align-items: center;
|
|
@@ -196,6 +256,458 @@
|
|
|
196
256
|
cursor: not-allowed;
|
|
197
257
|
}
|
|
198
258
|
|
|
259
|
+
/* Session Timer Accordion Styles */
|
|
260
|
+
.session-timer-section {
|
|
261
|
+
margin: 15px 0;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
.session-timer-accordion {
|
|
265
|
+
background: #21262d;
|
|
266
|
+
border: 1px solid #30363d;
|
|
267
|
+
border-radius: 6px;
|
|
268
|
+
margin-bottom: 15px;
|
|
269
|
+
position: relative;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.session-timer-header {
|
|
273
|
+
display: flex;
|
|
274
|
+
justify-content: space-between;
|
|
275
|
+
align-items: center;
|
|
276
|
+
padding: 12px 16px;
|
|
277
|
+
cursor: pointer;
|
|
278
|
+
border-bottom: 1px solid #30363d;
|
|
279
|
+
transition: background-color 0.2s ease;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.session-timer-header:hover {
|
|
283
|
+
background: #30363d;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
.session-timer-title-section {
|
|
287
|
+
display: flex;
|
|
288
|
+
align-items: center;
|
|
289
|
+
gap: 8px;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.session-timer-chevron {
|
|
293
|
+
font-size: 0.75rem;
|
|
294
|
+
color: #7d8590;
|
|
295
|
+
transition: transform 0.2s ease;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
.session-timer-title {
|
|
299
|
+
color: #d57455;
|
|
300
|
+
font-size: 0.9rem;
|
|
301
|
+
font-weight: 500;
|
|
302
|
+
margin: 0;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
.session-timer-status-inline {
|
|
306
|
+
display: flex;
|
|
307
|
+
align-items: center;
|
|
308
|
+
gap: 6px;
|
|
309
|
+
font-size: 0.75rem;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.session-timer-status-dot {
|
|
313
|
+
width: 6px;
|
|
314
|
+
height: 6px;
|
|
315
|
+
border-radius: 50%;
|
|
316
|
+
animation: pulse 2s infinite;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
.session-timer-status-dot.active {
|
|
320
|
+
background: #3fb950;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
.session-timer-status-dot.warning {
|
|
324
|
+
background: #f97316;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
.session-timer-status-dot.inactive {
|
|
328
|
+
background: #7d8590;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
.session-timer-status-text {
|
|
332
|
+
color: #c9d1d9;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.session-timer-content {
|
|
336
|
+
padding: 12px 16px;
|
|
337
|
+
border-top: none;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
.session-loading-state {
|
|
341
|
+
display: flex;
|
|
342
|
+
align-items: center;
|
|
343
|
+
justify-content: center;
|
|
344
|
+
gap: 8px;
|
|
345
|
+
padding: 20px 0;
|
|
346
|
+
color: #7d8590;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.session-spinner {
|
|
350
|
+
width: 16px;
|
|
351
|
+
height: 16px;
|
|
352
|
+
border: 2px solid #30363d;
|
|
353
|
+
border-top: 2px solid #d57455;
|
|
354
|
+
border-radius: 50%;
|
|
355
|
+
animation: spin 1s linear infinite;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
@keyframes spin {
|
|
359
|
+
0% { transform: rotate(0deg); }
|
|
360
|
+
100% { transform: rotate(360deg); }
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
.session-timer-empty {
|
|
364
|
+
text-align: center;
|
|
365
|
+
padding: 20px;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.session-timer-empty-text {
|
|
369
|
+
color: #c9d1d9;
|
|
370
|
+
font-size: 0.9rem;
|
|
371
|
+
margin-bottom: 4px;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.session-timer-empty-subtext {
|
|
375
|
+
color: #7d8590;
|
|
376
|
+
font-size: 0.8rem;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
.session-timer-compact {
|
|
380
|
+
display: flex;
|
|
381
|
+
flex-direction: column;
|
|
382
|
+
gap: 12px;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
.session-timer-row {
|
|
386
|
+
display: flex;
|
|
387
|
+
gap: 20px;
|
|
388
|
+
align-items: center;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
.session-timer-time-compact {
|
|
392
|
+
flex: 0 0 auto;
|
|
393
|
+
text-align: center;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
.session-timer-time-value {
|
|
397
|
+
font-size: 1.5rem;
|
|
398
|
+
font-weight: 700;
|
|
399
|
+
color: #d57455;
|
|
400
|
+
line-height: 1;
|
|
401
|
+
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.session-timer-time-label {
|
|
405
|
+
color: #7d8590;
|
|
406
|
+
font-size: 0.75rem;
|
|
407
|
+
margin-top: 2px;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
.session-timer-progress-compact {
|
|
411
|
+
flex: 1;
|
|
412
|
+
display: flex;
|
|
413
|
+
flex-direction: column;
|
|
414
|
+
gap: 8px;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
.session-timer-progress-item {
|
|
418
|
+
display: flex;
|
|
419
|
+
flex-direction: column;
|
|
420
|
+
gap: 4px;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
.session-timer-progress-header {
|
|
424
|
+
display: flex;
|
|
425
|
+
justify-content: space-between;
|
|
426
|
+
align-items: center;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
.session-timer-progress-label {
|
|
430
|
+
color: #c9d1d9;
|
|
431
|
+
font-size: 0.75rem;
|
|
432
|
+
font-weight: 500;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
.session-timer-progress-value {
|
|
436
|
+
color: #7d8590;
|
|
437
|
+
font-size: 0.75rem;
|
|
438
|
+
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
|
439
|
+
display: inline-flex;
|
|
440
|
+
align-items: center;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
.session-timer-progress-bar {
|
|
444
|
+
width: 100%;
|
|
445
|
+
height: 4px;
|
|
446
|
+
background: #30363d;
|
|
447
|
+
border-radius: 2px;
|
|
448
|
+
overflow: hidden;
|
|
449
|
+
position: relative;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
.session-timer-progress-fill {
|
|
453
|
+
height: 100%;
|
|
454
|
+
background: #3fb950;
|
|
455
|
+
transition: width 0.3s ease, background-color 0.3s ease;
|
|
456
|
+
border-radius: 2px;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
.session-timer-stats-row {
|
|
460
|
+
display: flex;
|
|
461
|
+
gap: 12px;
|
|
462
|
+
padding-top: 8px;
|
|
463
|
+
border-top: 1px solid #30363d;
|
|
464
|
+
flex-wrap: wrap;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
.session-timer-stat-compact {
|
|
468
|
+
display: flex;
|
|
469
|
+
align-items: center;
|
|
470
|
+
gap: 4px;
|
|
471
|
+
flex: 1;
|
|
472
|
+
min-width: 0;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
.session-timer-stat-label {
|
|
476
|
+
color: #7d8590;
|
|
477
|
+
font-size: 0.75rem;
|
|
478
|
+
white-space: nowrap;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
.session-timer-stat-value {
|
|
482
|
+
color: #c9d1d9;
|
|
483
|
+
font-weight: 500;
|
|
484
|
+
font-size: 0.75rem;
|
|
485
|
+
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
|
486
|
+
white-space: nowrap;
|
|
487
|
+
overflow: hidden;
|
|
488
|
+
text-overflow: ellipsis;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
.session-timer-error {
|
|
492
|
+
text-align: center;
|
|
493
|
+
padding: 20px;
|
|
494
|
+
color: #ffa198;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
.session-timer-error-text {
|
|
498
|
+
margin-bottom: 12px;
|
|
499
|
+
font-size: 0.85rem;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
.session-timer-retry-btn {
|
|
503
|
+
background: #f85149;
|
|
504
|
+
border: 1px solid #f85149;
|
|
505
|
+
color: #ffffff;
|
|
506
|
+
padding: 6px 12px;
|
|
507
|
+
border-radius: 4px;
|
|
508
|
+
cursor: pointer;
|
|
509
|
+
font-size: 0.75rem;
|
|
510
|
+
transition: all 0.2s ease;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
.session-timer-retry-btn:hover {
|
|
514
|
+
background: #da3633;
|
|
515
|
+
border-color: #da3633;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
.session-warnings {
|
|
519
|
+
margin-top: 8px;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
.warnings-list {
|
|
523
|
+
display: flex;
|
|
524
|
+
flex-direction: column;
|
|
525
|
+
gap: 4px;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
.session-warning {
|
|
529
|
+
background: rgba(248, 81, 73, 0.1);
|
|
530
|
+
border: 1px solid rgba(248, 81, 73, 0.3);
|
|
531
|
+
border-radius: 4px;
|
|
532
|
+
padding: 8px;
|
|
533
|
+
color: #ffa198;
|
|
534
|
+
font-size: 0.75rem;
|
|
535
|
+
display: flex;
|
|
536
|
+
align-items: center;
|
|
537
|
+
gap: 6px;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
.session-warning.error {
|
|
541
|
+
background: rgba(248, 81, 73, 0.15);
|
|
542
|
+
border-color: rgba(248, 81, 73, 0.5);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
.session-warning.warning {
|
|
546
|
+
background: rgba(249, 115, 22, 0.1);
|
|
547
|
+
border-color: rgba(249, 115, 22, 0.3);
|
|
548
|
+
color: #fbbf24;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
.session-warning.info {
|
|
552
|
+
background: rgba(59, 130, 246, 0.1);
|
|
553
|
+
border-color: rgba(59, 130, 246, 0.3);
|
|
554
|
+
color: #93c5fd;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/* Session Timer Info Icon and Popover */
|
|
558
|
+
.session-timer-label-with-info {
|
|
559
|
+
display: inline-flex;
|
|
560
|
+
align-items: center;
|
|
561
|
+
gap: 4px;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
.session-timer-info-icon {
|
|
565
|
+
color: #7d8590;
|
|
566
|
+
font-size: 0.7rem;
|
|
567
|
+
cursor: pointer;
|
|
568
|
+
transition: all 0.2s ease;
|
|
569
|
+
display: inline-flex;
|
|
570
|
+
align-items: center;
|
|
571
|
+
margin-left: 4px;
|
|
572
|
+
opacity: 0.6;
|
|
573
|
+
line-height: 1;
|
|
574
|
+
vertical-align: middle;
|
|
575
|
+
padding: 2px;
|
|
576
|
+
border-radius: 2px;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
.session-timer-info-icon:hover {
|
|
580
|
+
color: #58a6ff;
|
|
581
|
+
opacity: 1;
|
|
582
|
+
background-color: rgba(88, 166, 255, 0.1);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
.session-timer-info-icon:active {
|
|
586
|
+
background-color: rgba(88, 166, 255, 0.2);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
.session-timer-tooltip {
|
|
590
|
+
position: absolute;
|
|
591
|
+
z-index: 1000;
|
|
592
|
+
background: #161b22;
|
|
593
|
+
border: 1px solid #30363d;
|
|
594
|
+
border-radius: 6px;
|
|
595
|
+
padding: 0;
|
|
596
|
+
box-shadow: 0 16px 32px rgba(1, 4, 9, 0.85);
|
|
597
|
+
max-width: 300px;
|
|
598
|
+
font-size: 0.75rem;
|
|
599
|
+
line-height: 1.4;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
.session-timer-tooltip-content {
|
|
603
|
+
padding: 12px;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
.session-timer-tooltip-content h4 {
|
|
607
|
+
margin: 0 0 8px 0;
|
|
608
|
+
color: #f0f6fc;
|
|
609
|
+
font-size: 0.8rem;
|
|
610
|
+
font-weight: 600;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
.session-timer-tooltip-content p {
|
|
614
|
+
margin: 0 0 8px 0;
|
|
615
|
+
color: #c9d1d9;
|
|
616
|
+
line-height: 1.5;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
.session-timer-tooltip-content p:last-of-type {
|
|
620
|
+
margin-bottom: 12px;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
.session-timer-tooltip-link {
|
|
624
|
+
border-top: 1px solid #30363d;
|
|
625
|
+
padding-top: 8px;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
.session-timer-tooltip-link a {
|
|
629
|
+
color: #58a6ff;
|
|
630
|
+
text-decoration: none;
|
|
631
|
+
display: inline-flex;
|
|
632
|
+
align-items: center;
|
|
633
|
+
gap: 4px;
|
|
634
|
+
transition: color 0.2s ease;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
.session-timer-tooltip-link a:hover {
|
|
638
|
+
color: #79c0ff;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
.session-timer-tooltip-link i {
|
|
642
|
+
font-size: 0.65rem;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/* Claude Session Info Styles */
|
|
646
|
+
.claude-session-info {
|
|
647
|
+
background: #161b22;
|
|
648
|
+
border: 1px solid #30363d;
|
|
649
|
+
border-radius: 6px;
|
|
650
|
+
padding: 12px;
|
|
651
|
+
margin: 12px 0;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
.claude-session-info-row {
|
|
655
|
+
margin-bottom: 12px;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
.claude-session-info-compact {
|
|
659
|
+
padding: 8px 12px;
|
|
660
|
+
background: #0d1117;
|
|
661
|
+
border: 1px solid #21262d;
|
|
662
|
+
border-radius: 4px;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
.claude-session-header {
|
|
666
|
+
color: #d57455;
|
|
667
|
+
font-size: 0.8rem;
|
|
668
|
+
margin-bottom: 8px;
|
|
669
|
+
text-transform: uppercase;
|
|
670
|
+
letter-spacing: 0.5px;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
.claude-session-details {
|
|
674
|
+
display: flex;
|
|
675
|
+
flex-direction: column;
|
|
676
|
+
gap: 4px;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
.claude-session-stats {
|
|
680
|
+
display: flex;
|
|
681
|
+
gap: 12px;
|
|
682
|
+
flex-wrap: wrap;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
.claude-session-stat {
|
|
686
|
+
display: flex;
|
|
687
|
+
align-items: center;
|
|
688
|
+
gap: 4px;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
.claude-session-label {
|
|
692
|
+
color: #7d8590;
|
|
693
|
+
font-size: 0.75rem;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
.claude-session-value {
|
|
697
|
+
color: #c9d1d9;
|
|
698
|
+
font-size: 0.75rem;
|
|
699
|
+
font-weight: 600;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
.claude-session-value.expired {
|
|
703
|
+
color: #f85149;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
.session-timer-status-dot.expired {
|
|
707
|
+
background: #f85149;
|
|
708
|
+
animation: pulse 2s infinite;
|
|
709
|
+
}
|
|
710
|
+
|
|
199
711
|
.charts-container {
|
|
200
712
|
display: grid;
|
|
201
713
|
grid-template-columns: 2fr 1fr;
|
|
@@ -229,6 +741,7 @@
|
|
|
229
741
|
.filter-bar {
|
|
230
742
|
display: flex;
|
|
231
743
|
align-items: center;
|
|
744
|
+
justify-content: space-between;
|
|
232
745
|
gap: 16px;
|
|
233
746
|
margin: 20px 0;
|
|
234
747
|
padding: 12px 0;
|
|
@@ -236,6 +749,12 @@
|
|
|
236
749
|
border-bottom: 1px solid #21262d;
|
|
237
750
|
}
|
|
238
751
|
|
|
752
|
+
.filter-section {
|
|
753
|
+
display: flex;
|
|
754
|
+
align-items: center;
|
|
755
|
+
gap: 16px;
|
|
756
|
+
}
|
|
757
|
+
|
|
239
758
|
.filter-label {
|
|
240
759
|
color: #7d8590;
|
|
241
760
|
font-size: 0.875rem;
|
|
@@ -246,6 +765,11 @@
|
|
|
246
765
|
gap: 8px;
|
|
247
766
|
}
|
|
248
767
|
|
|
768
|
+
.filter-actions {
|
|
769
|
+
display: flex;
|
|
770
|
+
align-items: center;
|
|
771
|
+
}
|
|
772
|
+
|
|
249
773
|
.filter-btn {
|
|
250
774
|
background: none;
|
|
251
775
|
border: 1px solid #30363d;
|
|
@@ -272,6 +796,7 @@
|
|
|
272
796
|
.sessions-table {
|
|
273
797
|
width: 100%;
|
|
274
798
|
border-collapse: collapse;
|
|
799
|
+
table-layout: fixed;
|
|
275
800
|
}
|
|
276
801
|
|
|
277
802
|
.sessions-table th {
|
|
@@ -283,10 +808,34 @@
|
|
|
283
808
|
border-bottom: 1px solid #30363d;
|
|
284
809
|
}
|
|
285
810
|
|
|
811
|
+
.sessions-table th:nth-child(1) { width: 20%; } /* conversation id */
|
|
812
|
+
.sessions-table th:nth-child(2) { width: 15%; } /* project */
|
|
813
|
+
.sessions-table th:nth-child(3) { width: 10%; } /* model */
|
|
814
|
+
.sessions-table th:nth-child(4) { width: 8%; } /* messages */
|
|
815
|
+
.sessions-table th:nth-child(5) { width: 10%; } /* tokens */
|
|
816
|
+
.sessions-table th:nth-child(6) { width: 12%; } /* last activity */
|
|
817
|
+
.sessions-table th:nth-child(7) { width: 15%; } /* conversation state - FIXED WIDTH */
|
|
818
|
+
.sessions-table th:nth-child(8) { width: 10%; } /* status */
|
|
819
|
+
|
|
286
820
|
.sessions-table td {
|
|
287
821
|
padding: 8px 12px;
|
|
288
822
|
font-size: 0.875rem;
|
|
289
823
|
border-bottom: 1px solid #21262d;
|
|
824
|
+
overflow: hidden;
|
|
825
|
+
text-overflow: ellipsis;
|
|
826
|
+
white-space: nowrap;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
/* Allow status squares to wrap and flex */
|
|
830
|
+
.sessions-table td:nth-child(8) {
|
|
831
|
+
white-space: normal;
|
|
832
|
+
overflow: visible;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
/* Allow conversation id to wrap for long IDs */
|
|
836
|
+
.sessions-table td:nth-child(1) {
|
|
837
|
+
white-space: normal;
|
|
838
|
+
overflow: visible;
|
|
290
839
|
}
|
|
291
840
|
|
|
292
841
|
.sessions-table tr:hover {
|
|
@@ -621,6 +1170,20 @@
|
|
|
621
1170
|
position: relative;
|
|
622
1171
|
}
|
|
623
1172
|
|
|
1173
|
+
.message-metadata {
|
|
1174
|
+
margin: 8px 0;
|
|
1175
|
+
padding: 4px 8px;
|
|
1176
|
+
background: #161b22;
|
|
1177
|
+
border-radius: 4px;
|
|
1178
|
+
border-left: 2px solid #30363d;
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
.message-id {
|
|
1182
|
+
margin-top: 8px;
|
|
1183
|
+
text-align: right;
|
|
1184
|
+
opacity: 0.7;
|
|
1185
|
+
}
|
|
1186
|
+
|
|
624
1187
|
.message:last-child {
|
|
625
1188
|
border-bottom: none;
|
|
626
1189
|
}
|
|
@@ -772,7 +1335,7 @@
|
|
|
772
1335
|
<div class="terminal-header">
|
|
773
1336
|
<div class="terminal-title">
|
|
774
1337
|
<span class="status-dot"></span>
|
|
775
|
-
claude-code-analytics
|
|
1338
|
+
claude-code-analytics <span id="app-version">1.8.0</span>
|
|
776
1339
|
</div>
|
|
777
1340
|
<div class="terminal-subtitle">real-time monitoring dashboard</div>
|
|
778
1341
|
<div class="terminal-subtitle" id="lastUpdate"></div>
|
|
@@ -797,14 +1360,49 @@
|
|
|
797
1360
|
<span class="stat-label">conversations:</span>
|
|
798
1361
|
<span class="stat-value" id="totalConversations">0</span>
|
|
799
1362
|
</div>
|
|
800
|
-
<div class="stat">
|
|
801
|
-
<span class="stat-label">
|
|
802
|
-
<span class="stat-value" id="claudeSessions"
|
|
803
|
-
|
|
1363
|
+
<div class="stat" style="position: relative;">
|
|
1364
|
+
<span class="stat-label">sessions:</span>
|
|
1365
|
+
<span class="stat-value" id="claudeSessions"
|
|
1366
|
+
onmouseenter="showClaudeSessionsPopover(event)"
|
|
1367
|
+
onmouseleave="hideClaudeSessionsPopover()"
|
|
1368
|
+
onclick="showClaudeSessionsPopover(event)"
|
|
1369
|
+
style="cursor: help;">0</span>
|
|
1370
|
+
<div class="claude-sessions-popover" id="claudeSessionsPopover">
|
|
1371
|
+
<div class="session-breakdown-item">
|
|
1372
|
+
<span class="session-type">This Month:</span>
|
|
1373
|
+
<span class="session-value" id="claudeSessionsMonth">0</span>
|
|
1374
|
+
</div>
|
|
1375
|
+
<div class="session-breakdown-item">
|
|
1376
|
+
<span class="session-type">This Week:</span>
|
|
1377
|
+
<span class="session-value" id="claudeSessionsWeek">0</span>
|
|
1378
|
+
</div>
|
|
1379
|
+
</div>
|
|
804
1380
|
</div>
|
|
805
|
-
<div class="stat">
|
|
1381
|
+
<div class="stat" style="position: relative;">
|
|
806
1382
|
<span class="stat-label">tokens:</span>
|
|
807
|
-
<span class="stat-value" id="totalTokens"
|
|
1383
|
+
<span class="stat-value" id="totalTokens"
|
|
1384
|
+
onmouseenter="showTokenPopover(event)"
|
|
1385
|
+
onmouseleave="hideTokenPopover()"
|
|
1386
|
+
onclick="showTokenPopover(event)"
|
|
1387
|
+
style="cursor: help;">0</span>
|
|
1388
|
+
<div class="token-popover" id="tokenPopover">
|
|
1389
|
+
<div class="token-breakdown-item">
|
|
1390
|
+
<span class="token-type">Input:</span>
|
|
1391
|
+
<span class="token-value" id="inputTokens">0</span>
|
|
1392
|
+
</div>
|
|
1393
|
+
<div class="token-breakdown-item">
|
|
1394
|
+
<span class="token-type">Output:</span>
|
|
1395
|
+
<span class="token-value" id="outputTokens">0</span>
|
|
1396
|
+
</div>
|
|
1397
|
+
<div class="token-breakdown-item">
|
|
1398
|
+
<span class="token-type">Cache Creation:</span>
|
|
1399
|
+
<span class="token-value" id="cacheCreationTokens">0</span>
|
|
1400
|
+
</div>
|
|
1401
|
+
<div class="token-breakdown-item">
|
|
1402
|
+
<span class="token-type">Cache Read:</span>
|
|
1403
|
+
<span class="token-value" id="cacheReadTokens">0</span>
|
|
1404
|
+
</div>
|
|
1405
|
+
</div>
|
|
808
1406
|
</div>
|
|
809
1407
|
<div class="stat">
|
|
810
1408
|
<span class="stat-label">projects:</span>
|
|
@@ -816,6 +1414,13 @@
|
|
|
816
1414
|
</div>
|
|
817
1415
|
</div>
|
|
818
1416
|
|
|
1417
|
+
<!-- Session Timer Section -->
|
|
1418
|
+
<div class="session-timer-section">
|
|
1419
|
+
<div id="session-timer-container">
|
|
1420
|
+
<!-- SessionTimer component will be mounted here -->
|
|
1421
|
+
</div>
|
|
1422
|
+
</div>
|
|
1423
|
+
|
|
819
1424
|
<div class="chart-controls">
|
|
820
1425
|
<div class="chart-controls-left">
|
|
821
1426
|
<div class="date-control">
|
|
@@ -828,9 +1433,6 @@
|
|
|
828
1433
|
</div>
|
|
829
1434
|
</div>
|
|
830
1435
|
<div class="chart-controls-right">
|
|
831
|
-
<button class="refresh-btn" onclick="toggleNotifications()" id="notificationBtn">
|
|
832
|
-
enable notifications
|
|
833
|
-
</button>
|
|
834
1436
|
<button class="refresh-btn" onclick="refreshCharts()" id="refreshBtn">
|
|
835
1437
|
refresh charts
|
|
836
1438
|
</button>
|
|
@@ -854,12 +1456,19 @@
|
|
|
854
1456
|
</div>
|
|
855
1457
|
|
|
856
1458
|
<div class="filter-bar">
|
|
857
|
-
<
|
|
858
|
-
|
|
859
|
-
<
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
1459
|
+
<div class="filter-section">
|
|
1460
|
+
<span class="filter-label">filter conversations:</span>
|
|
1461
|
+
<div class="filter-buttons">
|
|
1462
|
+
<button class="filter-btn active" data-filter="active">active</button>
|
|
1463
|
+
<button class="filter-btn" data-filter="recent">recent</button>
|
|
1464
|
+
<button class="filter-btn" data-filter="inactive">inactive</button>
|
|
1465
|
+
<button class="filter-btn" data-filter="all">all</button>
|
|
1466
|
+
</div>
|
|
1467
|
+
</div>
|
|
1468
|
+
<div class="filter-actions">
|
|
1469
|
+
<button class="refresh-btn" onclick="toggleNotifications()" id="notificationBtn">
|
|
1470
|
+
enable notifications
|
|
1471
|
+
</button>
|
|
863
1472
|
</div>
|
|
864
1473
|
</div>
|
|
865
1474
|
|
|
@@ -924,6 +1533,22 @@
|
|
|
924
1533
|
let allData = null;
|
|
925
1534
|
let notificationsEnabled = false;
|
|
926
1535
|
let previousConversationStates = new Map();
|
|
1536
|
+
let sessionTimerComponent = null;
|
|
1537
|
+
|
|
1538
|
+
async function loadVersion() {
|
|
1539
|
+
try {
|
|
1540
|
+
const response = await fetch('/api/version');
|
|
1541
|
+
const data = await response.json();
|
|
1542
|
+
|
|
1543
|
+
const versionElement = document.getElementById('app-version');
|
|
1544
|
+
if (versionElement) {
|
|
1545
|
+
versionElement.textContent = data.version;
|
|
1546
|
+
}
|
|
1547
|
+
} catch (error) {
|
|
1548
|
+
console.error('Error loading version:', error);
|
|
1549
|
+
// Keep fallback version if API fails
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
927
1552
|
|
|
928
1553
|
async function loadData() {
|
|
929
1554
|
try {
|
|
@@ -938,7 +1563,7 @@
|
|
|
938
1563
|
// Update timestamp
|
|
939
1564
|
document.getElementById('lastUpdate').textContent = `last update: ${data.lastUpdate}`;
|
|
940
1565
|
|
|
941
|
-
updateStats(data.summary);
|
|
1566
|
+
updateStats(data.summary, data.detailedTokenUsage, data);
|
|
942
1567
|
allConversations = data.conversations;
|
|
943
1568
|
allData = data; // Store data globally for access
|
|
944
1569
|
window.allData = data; // Keep for backward compatibility
|
|
@@ -951,6 +1576,9 @@
|
|
|
951
1576
|
updateCharts(data);
|
|
952
1577
|
updateSessionsTable();
|
|
953
1578
|
|
|
1579
|
+
// Initialize or update SessionTimer
|
|
1580
|
+
await initializeSessionTimer();
|
|
1581
|
+
|
|
954
1582
|
// Check for conversation state changes and send notifications
|
|
955
1583
|
checkForNotifications(data.conversations);
|
|
956
1584
|
|
|
@@ -964,15 +1592,32 @@
|
|
|
964
1592
|
// Function to only update conversation data without refreshing charts
|
|
965
1593
|
async function loadConversationData() {
|
|
966
1594
|
try {
|
|
967
|
-
const response = await fetch('/api/
|
|
1595
|
+
const response = await fetch('/api/fast-update');
|
|
968
1596
|
const data = await response.json();
|
|
969
1597
|
|
|
970
|
-
|
|
1598
|
+
// Only log state changes, not every refresh
|
|
1599
|
+
let hasStateChanges = false;
|
|
1600
|
+
|
|
1601
|
+
// Log conversation state changes
|
|
1602
|
+
if (data.conversations && allConversations) {
|
|
1603
|
+
data.conversations.forEach(conv => {
|
|
1604
|
+
const prevConv = allConversations.find(c => c.id === conv.id);
|
|
1605
|
+
if (prevConv && prevConv.conversationState !== conv.conversationState) {
|
|
1606
|
+
console.log('🔄 State change: ' + conv.project + ' from "' + prevConv.conversationState + '" to "' + conv.conversationState + '"');
|
|
1607
|
+
hasStateChanges = true;
|
|
1608
|
+
}
|
|
1609
|
+
});
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
// Only log refresh timestamp if there were actual changes
|
|
1613
|
+
if (hasStateChanges) {
|
|
1614
|
+
console.log('⚡ Update completed at:', new Date().toLocaleTimeString());
|
|
1615
|
+
}
|
|
971
1616
|
|
|
972
1617
|
// Update timestamp
|
|
973
1618
|
document.getElementById('lastUpdate').textContent = `last update: ${data.lastUpdate}`;
|
|
974
1619
|
|
|
975
|
-
updateStats(data.summary);
|
|
1620
|
+
updateStats(data.summary, data.detailedTokenUsage, data);
|
|
976
1621
|
allConversations = data.conversations;
|
|
977
1622
|
allData = data; // Store data globally for access
|
|
978
1623
|
window.allData = data; // Keep for backward compatibility
|
|
@@ -988,6 +1633,40 @@
|
|
|
988
1633
|
}
|
|
989
1634
|
}
|
|
990
1635
|
|
|
1636
|
+
// NEW: Function to update ONLY conversation states (ultra-fast)
|
|
1637
|
+
async function updateConversationStatesOnly() {
|
|
1638
|
+
try {
|
|
1639
|
+
const response = await fetch('/api/conversation-state');
|
|
1640
|
+
const data = await response.json();
|
|
1641
|
+
|
|
1642
|
+
// Update only the conversation state fields in the UI
|
|
1643
|
+
data.activeStates.forEach(stateInfo => {
|
|
1644
|
+
// Update in sessions table
|
|
1645
|
+
const sessionRow = document.querySelector('tr[data-session-id="' + stateInfo.id + '"]');
|
|
1646
|
+
if (sessionRow) {
|
|
1647
|
+
const stateCell = sessionRow.querySelector('.conversation-state');
|
|
1648
|
+
if (stateCell && stateCell.textContent !== stateInfo.state) {
|
|
1649
|
+
console.log('⚡ INSTANT State Update: ' + stateInfo.project + ' → "' + stateInfo.state + '"');
|
|
1650
|
+
stateCell.textContent = stateInfo.state;
|
|
1651
|
+
stateCell.className = 'conversation-state ' + getStateClass(stateInfo.state);
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
// Update in session detail if visible
|
|
1656
|
+
if (currentSession && currentSession.id === stateInfo.id) {
|
|
1657
|
+
const detailStateElement = document.querySelector('#sessionDetail .conversation-state');
|
|
1658
|
+
if (detailStateElement && detailStateElement.textContent !== stateInfo.state) {
|
|
1659
|
+
detailStateElement.textContent = stateInfo.state;
|
|
1660
|
+
detailStateElement.className = 'conversation-state ' + getStateClass(stateInfo.state);
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
});
|
|
1664
|
+
|
|
1665
|
+
} catch (error) {
|
|
1666
|
+
// Silently fail - don't interfere with main data flow
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
|
|
991
1670
|
// Notification functions
|
|
992
1671
|
async function requestNotificationPermission() {
|
|
993
1672
|
if (!('Notification' in window)) {
|
|
@@ -1044,16 +1723,13 @@
|
|
|
1044
1723
|
|
|
1045
1724
|
// Check if conversation state changed to "Awaiting user input..."
|
|
1046
1725
|
if (prevState && prevState !== currentState) {
|
|
1047
|
-
console.log(`State change detected: ${conv.project} from "${prevState}" to "${currentState}"`);
|
|
1048
|
-
|
|
1049
1726
|
if (currentState === 'Awaiting user input...' ||
|
|
1050
1727
|
currentState === 'User may be typing...' ||
|
|
1051
1728
|
currentState === 'Awaiting response...') {
|
|
1052
1729
|
|
|
1053
|
-
const title = '
|
|
1730
|
+
const title = 'Claude is waiting for you!';
|
|
1054
1731
|
const body = `Project: ${conv.project} - Claude needs your input`;
|
|
1055
1732
|
|
|
1056
|
-
console.log('Sending notification for state:', currentState);
|
|
1057
1733
|
sendNotification(title, body, conv.id);
|
|
1058
1734
|
}
|
|
1059
1735
|
}
|
|
@@ -1093,20 +1769,94 @@
|
|
|
1093
1769
|
}
|
|
1094
1770
|
}
|
|
1095
1771
|
|
|
1096
|
-
function updateStats(summary) {
|
|
1772
|
+
function updateStats(summary, detailedTokenUsage, data = null) {
|
|
1097
1773
|
document.getElementById('totalConversations').textContent = summary.totalConversations.toLocaleString();
|
|
1098
|
-
|
|
1774
|
+
|
|
1775
|
+
// Use detailed token usage for accurate totals
|
|
1776
|
+
const totalTokens = detailedTokenUsage ? detailedTokenUsage.total : summary.totalTokens;
|
|
1777
|
+
document.getElementById('totalTokens').textContent = totalTokens.toLocaleString();
|
|
1778
|
+
|
|
1779
|
+
// Update detailed token breakdown
|
|
1780
|
+
if (detailedTokenUsage) {
|
|
1781
|
+
document.getElementById('inputTokens').textContent = detailedTokenUsage.inputTokens.toLocaleString();
|
|
1782
|
+
document.getElementById('outputTokens').textContent = detailedTokenUsage.outputTokens.toLocaleString();
|
|
1783
|
+
document.getElementById('cacheCreationTokens').textContent = detailedTokenUsage.cacheCreationTokens.toLocaleString();
|
|
1784
|
+
document.getElementById('cacheReadTokens').textContent = detailedTokenUsage.cacheReadTokens.toLocaleString();
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1099
1787
|
document.getElementById('activeProjects').textContent = summary.activeProjects;
|
|
1100
1788
|
document.getElementById('dataSize').textContent = summary.totalFileSize;
|
|
1101
1789
|
|
|
1102
1790
|
// Update Claude sessions
|
|
1103
1791
|
if (summary.claudeSessions) {
|
|
1104
1792
|
document.getElementById('claudeSessions').textContent = summary.claudeSessions.total.toLocaleString();
|
|
1105
|
-
|
|
1106
|
-
|
|
1793
|
+
|
|
1794
|
+
// Update Claude sessions popover
|
|
1795
|
+
document.getElementById('claudeSessionsMonth').textContent = summary.claudeSessions.currentMonth;
|
|
1796
|
+
document.getElementById('claudeSessionsWeek').textContent = summary.claudeSessions.thisWeek;
|
|
1107
1797
|
}
|
|
1108
1798
|
}
|
|
1109
1799
|
|
|
1800
|
+
let popoverTimeout;
|
|
1801
|
+
|
|
1802
|
+
function showTokenPopover(event) {
|
|
1803
|
+
clearTimeout(popoverTimeout);
|
|
1804
|
+
const popover = document.getElementById('tokenPopover');
|
|
1805
|
+
if (popover) {
|
|
1806
|
+
popover.style.display = 'block';
|
|
1807
|
+
popover.style.visibility = 'visible';
|
|
1808
|
+
popover.style.opacity = '1';
|
|
1809
|
+
|
|
1810
|
+
// Add event listeners to popover to keep it open when hovering over it
|
|
1811
|
+
popover.addEventListener('mouseenter', () => {
|
|
1812
|
+
clearTimeout(popoverTimeout);
|
|
1813
|
+
});
|
|
1814
|
+
|
|
1815
|
+
popover.addEventListener('mouseleave', () => {
|
|
1816
|
+
hideTokenPopover();
|
|
1817
|
+
});
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
|
|
1821
|
+
function hideTokenPopover() {
|
|
1822
|
+
popoverTimeout = setTimeout(() => {
|
|
1823
|
+
const popover = document.getElementById('tokenPopover');
|
|
1824
|
+
if (popover) {
|
|
1825
|
+
popover.style.display = 'none';
|
|
1826
|
+
}
|
|
1827
|
+
}, 100); // Small delay to allow moving to popover
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1830
|
+
let claudeSessionsPopoverTimeout;
|
|
1831
|
+
|
|
1832
|
+
function showClaudeSessionsPopover(event) {
|
|
1833
|
+
clearTimeout(claudeSessionsPopoverTimeout);
|
|
1834
|
+
const popover = document.getElementById('claudeSessionsPopover');
|
|
1835
|
+
if (popover) {
|
|
1836
|
+
popover.style.display = 'block';
|
|
1837
|
+
popover.style.visibility = 'visible';
|
|
1838
|
+
popover.style.opacity = '1';
|
|
1839
|
+
|
|
1840
|
+
// Add event listeners to popover to keep it open when hovering over it
|
|
1841
|
+
popover.addEventListener('mouseenter', () => {
|
|
1842
|
+
clearTimeout(claudeSessionsPopoverTimeout);
|
|
1843
|
+
});
|
|
1844
|
+
|
|
1845
|
+
popover.addEventListener('mouseleave', () => {
|
|
1846
|
+
hideClaudeSessionsPopover();
|
|
1847
|
+
});
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
function hideClaudeSessionsPopover() {
|
|
1852
|
+
claudeSessionsPopoverTimeout = setTimeout(() => {
|
|
1853
|
+
const popover = document.getElementById('claudeSessionsPopover');
|
|
1854
|
+
if (popover) {
|
|
1855
|
+
popover.style.display = 'none';
|
|
1856
|
+
}
|
|
1857
|
+
}, 100); // Small delay to allow moving to popover
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1110
1860
|
function initializeDateInputs() {
|
|
1111
1861
|
const today = new Date();
|
|
1112
1862
|
const sevenDaysAgo = new Date(today);
|
|
@@ -1165,7 +1915,7 @@
|
|
|
1165
1915
|
console.error('Error refreshing charts:', error);
|
|
1166
1916
|
} finally {
|
|
1167
1917
|
refreshBtn.classList.remove('loading');
|
|
1168
|
-
refreshBtn.textContent = '
|
|
1918
|
+
refreshBtn.textContent = 'refresh charts';
|
|
1169
1919
|
}
|
|
1170
1920
|
}
|
|
1171
1921
|
|
|
@@ -1416,7 +2166,7 @@
|
|
|
1416
2166
|
noSessionsDiv.style.display = 'none';
|
|
1417
2167
|
|
|
1418
2168
|
tableBody.innerHTML = filteredConversations.map(conv => `
|
|
1419
|
-
<tr onclick="showSessionDetail('${conv.id}')" style="cursor: pointer;">
|
|
2169
|
+
<tr data-session-id="${conv.id}" onclick="showSessionDetail('${conv.id}')" style="cursor: pointer;">
|
|
1420
2170
|
<td>
|
|
1421
2171
|
<div class="session-id-container">
|
|
1422
2172
|
<div class="session-id">
|
|
@@ -1516,26 +2266,38 @@
|
|
|
1516
2266
|
tooltip: 'User input'
|
|
1517
2267
|
};
|
|
1518
2268
|
} else if (message.role === 'assistant') {
|
|
1519
|
-
|
|
2269
|
+
// Use the new metadata fields to determine type more accurately
|
|
1520
2270
|
|
|
1521
|
-
if
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
2271
|
+
// Check if message has tool use based on stop_reason or content blocks
|
|
2272
|
+
if (message.stop_reason === 'tool_use' || message.hasToolUse) {
|
|
2273
|
+
return {
|
|
2274
|
+
type: 'tool',
|
|
2275
|
+
tooltip: 'Tool execution'
|
|
2276
|
+
};
|
|
2277
|
+
}
|
|
2278
|
+
|
|
2279
|
+
// Check if message has tool results
|
|
2280
|
+
if (message.hasToolResult) {
|
|
2281
|
+
return {
|
|
2282
|
+
type: 'tool',
|
|
2283
|
+
tooltip: 'Tool result'
|
|
2284
|
+
};
|
|
2285
|
+
}
|
|
2286
|
+
|
|
2287
|
+
// Check for actual errors based on stop_reason or specific error patterns
|
|
2288
|
+
if (message.stop_reason === 'error' ||
|
|
2289
|
+
(message.content && message.content.includes('I apologize') && message.content.includes('error'))) {
|
|
2290
|
+
return {
|
|
2291
|
+
type: 'error',
|
|
2292
|
+
tooltip: 'Error in response'
|
|
2293
|
+
};
|
|
1538
2294
|
}
|
|
2295
|
+
|
|
2296
|
+
// Normal successful response
|
|
2297
|
+
return {
|
|
2298
|
+
type: 'success',
|
|
2299
|
+
tooltip: 'Successful response'
|
|
2300
|
+
};
|
|
1539
2301
|
}
|
|
1540
2302
|
|
|
1541
2303
|
return {
|
|
@@ -1686,6 +2448,24 @@
|
|
|
1686
2448
|
container.innerHTML = reversedMessages.map((message, index) => {
|
|
1687
2449
|
const messageType = getMessageType(message);
|
|
1688
2450
|
const messageNum = filteredMessages.length - index;
|
|
2451
|
+
|
|
2452
|
+
// Build additional info display
|
|
2453
|
+
const additionalInfo = [];
|
|
2454
|
+
if (message.model) additionalInfo.push(`Model: ${message.model}`);
|
|
2455
|
+
if (message.stop_reason) additionalInfo.push(`Stop: ${message.stop_reason}`);
|
|
2456
|
+
if (message.usage) {
|
|
2457
|
+
const usage = message.usage;
|
|
2458
|
+
if (usage.input_tokens) additionalInfo.push(`In: ${usage.input_tokens}t`);
|
|
2459
|
+
if (usage.output_tokens) additionalInfo.push(`Out: ${usage.output_tokens}t`);
|
|
2460
|
+
if (usage.cache_creation_input_tokens) additionalInfo.push(`Cache: ${usage.cache_creation_input_tokens}t`);
|
|
2461
|
+
if (usage.service_tier) additionalInfo.push(`Tier: ${usage.service_tier}`);
|
|
2462
|
+
}
|
|
2463
|
+
if (message.contentBlocks && message.contentBlocks.length > 0) {
|
|
2464
|
+
const blockTypes = message.contentBlocks.map(b => b.type).join(', ');
|
|
2465
|
+
additionalInfo.push(`Blocks: ${blockTypes}`);
|
|
2466
|
+
}
|
|
2467
|
+
if (message.version) additionalInfo.push(`v${message.version}`);
|
|
2468
|
+
|
|
1689
2469
|
return `
|
|
1690
2470
|
<div class="message">
|
|
1691
2471
|
<div class="message-type-indicator ${messageType.type}" data-tooltip="${messageType.tooltip}"></div>
|
|
@@ -1695,7 +2475,13 @@
|
|
|
1695
2475
|
#${messageNum} • ${message.timestamp ? formatMessageTime(message.timestamp) : 'unknown time'}
|
|
1696
2476
|
</div>
|
|
1697
2477
|
</div>
|
|
2478
|
+
${additionalInfo.length > 0 ? `
|
|
2479
|
+
<div class="message-metadata">
|
|
2480
|
+
<small style="color: #7d8590; font-size: 0.8rem;">${additionalInfo.join(' • ')}</small>
|
|
2481
|
+
</div>
|
|
2482
|
+
` : ''}
|
|
1698
2483
|
<div class="message-content">${truncateContent(message.content || 'no content')}</div>
|
|
2484
|
+
${message.message_id ? `<div class="message-id"><small style="color: #7d8590; font-size: 0.7rem;">ID: ${message.message_id}</small></div>` : ''}
|
|
1699
2485
|
</div>
|
|
1700
2486
|
`;
|
|
1701
2487
|
}).join('');
|
|
@@ -1836,12 +2622,18 @@
|
|
|
1836
2622
|
function initWhenReady() {
|
|
1837
2623
|
if (typeof Chart !== 'undefined') {
|
|
1838
2624
|
console.log('Chart.js loaded successfully');
|
|
2625
|
+
loadVersion();
|
|
1839
2626
|
loadData();
|
|
1840
2627
|
|
|
1841
|
-
//
|
|
2628
|
+
// Regular refresh for conversation data every 1 second (slower)
|
|
1842
2629
|
setInterval(() => {
|
|
1843
2630
|
loadConversationData();
|
|
1844
2631
|
}, 1000);
|
|
2632
|
+
|
|
2633
|
+
// NEW: Ultra-fast refresh ONLY for conversation states (every 100ms)
|
|
2634
|
+
setInterval(() => {
|
|
2635
|
+
updateConversationStatesOnly();
|
|
2636
|
+
}, 100);
|
|
1845
2637
|
} else {
|
|
1846
2638
|
console.log('Waiting for Chart.js to load...');
|
|
1847
2639
|
setTimeout(initWhenReady, 100);
|
|
@@ -1858,6 +2650,47 @@
|
|
|
1858
2650
|
updateNotificationButtonState();
|
|
1859
2651
|
});
|
|
1860
2652
|
|
|
2653
|
+
// Initialize SessionTimer component
|
|
2654
|
+
async function initializeSessionTimer() {
|
|
2655
|
+
if (!window.SessionTimer) {
|
|
2656
|
+
console.warn('SessionTimer component not available');
|
|
2657
|
+
return;
|
|
2658
|
+
}
|
|
2659
|
+
|
|
2660
|
+
const container = document.getElementById('session-timer-container');
|
|
2661
|
+
if (!container) {
|
|
2662
|
+
console.warn('Session timer container not found');
|
|
2663
|
+
return;
|
|
2664
|
+
}
|
|
2665
|
+
|
|
2666
|
+
// Create mock services for the SessionTimer
|
|
2667
|
+
const mockDataService = {
|
|
2668
|
+
getSessionData: async () => {
|
|
2669
|
+
try {
|
|
2670
|
+
const response = await fetch('/api/session/data');
|
|
2671
|
+
return await response.json();
|
|
2672
|
+
} catch (error) {
|
|
2673
|
+
console.error('Error fetching session data:', error);
|
|
2674
|
+
return null;
|
|
2675
|
+
}
|
|
2676
|
+
}
|
|
2677
|
+
};
|
|
2678
|
+
|
|
2679
|
+
const mockStateService = {
|
|
2680
|
+
subscribe: (callback) => {
|
|
2681
|
+
// Mock state service subscription
|
|
2682
|
+
return () => {}; // Unsubscribe function
|
|
2683
|
+
}
|
|
2684
|
+
};
|
|
2685
|
+
|
|
2686
|
+
// Initialize SessionTimer if not already initialized
|
|
2687
|
+
if (!sessionTimerComponent) {
|
|
2688
|
+
sessionTimerComponent = new SessionTimer(container, mockDataService, mockStateService);
|
|
2689
|
+
await sessionTimerComponent.initialize();
|
|
2690
|
+
console.log('✅ SessionTimer initialized successfully');
|
|
2691
|
+
}
|
|
2692
|
+
}
|
|
2693
|
+
|
|
1861
2694
|
function updateNotificationButtonState() {
|
|
1862
2695
|
const btn = document.getElementById('notificationBtn');
|
|
1863
2696
|
if (!btn) return;
|