claude-session-insights 0.3.1 → 0.3.2

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 CHANGED
@@ -18,6 +18,12 @@ Think "Spotify Wrapped" for your Claude Code usage — scores, summaries, badges
18
18
  - **Auto-Refresh** — optional 15-second polling to keep the dashboard current while you work
19
19
  - **Account Info** — displays your subscription type, org, and email from `claude auth status`
20
20
 
21
+ ## Screenshots
22
+ <img width="1080" height="880" alt="image" src="https://github.com/user-attachments/assets/d9914527-4ea5-49ed-aa8a-09a2896b1c67" />
23
+ <img width="1080" height="845" alt="image" src="https://github.com/user-attachments/assets/20799313-617c-4bc6-a999-76a0d95e2d8e" />
24
+ <img width="1080" height="823" alt="image" src="https://github.com/user-attachments/assets/249b4c78-8849-4db6-91bf-5ac87b4defbc" />
25
+
26
+
21
27
  ## Quick Start
22
28
 
23
29
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-session-insights",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Analyze Claude Code sessions for efficiency insights, scores, and team metrics",
5
5
  "type": "module",
6
6
  "bin": {
package/public/index.html CHANGED
@@ -698,10 +698,43 @@
698
698
  color: var(--text3); cursor: pointer; font-size: 18px; line-height: 1; padding: 4px;
699
699
  }
700
700
  .share-close:hover { color: var(--text); }
701
+
702
+ /* Session drawer */
703
+ .session-drawer-overlay {
704
+ position: fixed; inset: 0; z-index: 500;
705
+ background: rgba(0,0,0,0.4); backdrop-filter: blur(2px);
706
+ opacity: 0; pointer-events: none; transition: opacity 0.25s;
707
+ }
708
+ .session-drawer-overlay.open { opacity: 1; pointer-events: auto; }
709
+ .session-drawer {
710
+ position: absolute; top: 0; right: 0; bottom: 0;
711
+ width: min(840px, 92vw);
712
+ background: var(--bg); overflow-y: auto;
713
+ padding: 24px 32px;
714
+ transform: translateX(100%);
715
+ transition: transform 0.28s cubic-bezier(0.4, 0, 0.2, 1);
716
+ box-shadow: -4px 0 40px rgba(0,0,0,0.18);
717
+ }
718
+ .session-drawer-overlay.open .session-drawer { transform: translateX(0); }
719
+ .drawer-topbar {
720
+ display: flex; justify-content: space-between; align-items: center;
721
+ margin-bottom: 20px; padding-bottom: 14px; border-bottom: 1px solid var(--border);
722
+ }
723
+ .drawer-topbar-label { font-size: 11px; color: var(--text3); font-weight: 500; }
724
+ .drawer-close-btn {
725
+ background: var(--surface2); border: 1px solid var(--border); color: var(--text2);
726
+ width: 30px; height: 30px; border-radius: 7px; cursor: pointer;
727
+ display: flex; align-items: center; justify-content: center;
728
+ font-size: 17px; line-height: 1; padding: 0; transition: all 0.15s; flex-shrink: 0;
729
+ }
730
+ .drawer-close-btn:hover { background: var(--surface3); color: var(--text); border-color: var(--border-hover); }
701
731
  </style>
702
732
  </head>
703
733
  <body>
704
734
  <div id="app" class="loading">Loading sessions...</div>
735
+ <div class="session-drawer-overlay" id="session-drawer-overlay" onclick="if(event.target===this)closeSessionDrawer()">
736
+ <div class="session-drawer" id="session-drawer"></div>
737
+ </div>
705
738
 
706
739
  <script>
707
740
  const $ = (s) => document.querySelector(s);
@@ -1202,7 +1235,12 @@ function renderDashboard(data) {
1202
1235
 
1203
1236
  async function openSession(id) {
1204
1237
  currentView = 'session';
1205
- $('#app').innerHTML = '<div class="loading">Loading session...</div>';
1238
+ const overlay = document.getElementById('session-drawer-overlay');
1239
+ const drawer = document.getElementById('session-drawer');
1240
+ drawer.innerHTML = '<div class="loading">Loading session...</div>';
1241
+ overlay.classList.add('open');
1242
+ document.body.style.overflow = 'hidden';
1243
+ if (autoRefreshActive) pauseAutoRefresh();
1206
1244
  const s = await fetchSession(id);
1207
1245
  renderSession(s);
1208
1246
  }
@@ -1218,8 +1256,11 @@ function renderSession(s) {
1218
1256
 
1219
1257
  let cumCost = 0;
1220
1258
 
1221
- $('#app').innerHTML = `
1222
- <button class="back-btn" onclick="goBack()">&larr; Back to dashboard</button>
1259
+ document.getElementById('session-drawer').innerHTML = `
1260
+ <div class="drawer-topbar">
1261
+ <span class="drawer-topbar-label">Session Detail</span>
1262
+ <button class="drawer-close-btn" onclick="closeSessionDrawer()" title="Close (Esc)">&times;</button>
1263
+ </div>
1223
1264
 
1224
1265
  <div class="session-header">
1225
1266
  <h2>${escHtml(s.title)}</h2>
@@ -1300,13 +1341,20 @@ function renderSession(s) {
1300
1341
  if (costCanvas) drawCostPerTurn(costCanvas, s.turns);
1301
1342
  }
1302
1343
 
1344
+ function closeSessionDrawer() {
1345
+ document.getElementById('session-drawer-overlay').classList.remove('open');
1346
+ document.body.style.overflow = '';
1347
+ currentView = 'dashboard';
1348
+ resumeAutoRefreshIfPaused();
1349
+ }
1350
+
1303
1351
  function toggleScoreBreakdown() {
1304
1352
  const el = document.getElementById('score-breakdown-overlay');
1305
1353
  if (el) el.classList.toggle('open');
1306
1354
  }
1307
1355
 
1308
1356
  function goBack() {
1309
- if (currentData) { renderDashboard(currentData); loadCachedAIInsights(); }
1357
+ closeSessionDrawer();
1310
1358
  }
1311
1359
 
1312
1360
  // --- AI Insights ---
@@ -1314,6 +1362,7 @@ function goBack() {
1314
1362
  let aiModels = [];
1315
1363
  let aiDefaultModel = null;
1316
1364
  let aiDefaultModelLabel = null;
1365
+ let cachedAIRender = null; // persists AI insights across renderDashboard calls
1317
1366
 
1318
1367
  function renderMarkdown(text) {
1319
1368
  return text
@@ -1374,7 +1423,16 @@ document.addEventListener('click', () => {
1374
1423
  document.querySelectorAll('.ai-model-picker.open').forEach(p => p.classList.remove('open'));
1375
1424
  });
1376
1425
 
1426
+ function applyAIState() {
1427
+ if (cachedAIRender) {
1428
+ renderAIComplete(cachedAIRender.content, cachedAIRender.generatedAt, cachedAIRender.model);
1429
+ } else {
1430
+ renderModelPicker();
1431
+ }
1432
+ }
1433
+
1377
1434
  function renderAIComplete(content, generatedAt, model) {
1435
+ cachedAIRender = { content, generatedAt, model };
1378
1436
  const body = document.getElementById('ai-insights-body');
1379
1437
  if (!body) return;
1380
1438
  body.innerHTML = `
@@ -1393,6 +1451,7 @@ function renderAIComplete(content, generatedAt, model) {
1393
1451
  }
1394
1452
 
1395
1453
  function clearAIInsights() {
1454
+ cachedAIRender = null;
1396
1455
  fetch('/api/ai-analyze', { method: 'DELETE' }).catch(() => {});
1397
1456
  const body = document.getElementById('ai-insights-body');
1398
1457
  if (!body) return;
@@ -1871,6 +1930,7 @@ async function doRefresh() {
1871
1930
  btn.classList.add('spinning');
1872
1931
  currentData = await fetchData(true);
1873
1932
  renderDashboard(currentData);
1933
+ applyAIState();
1874
1934
  btn.classList.remove('spinning');
1875
1935
  }
1876
1936
 
@@ -1897,6 +1957,7 @@ function startAutoRefresh() {
1897
1957
  if (btn) btn.classList.add('spinning');
1898
1958
  currentData = await fetchData(true);
1899
1959
  renderDashboard(currentData);
1960
+ applyAIState();
1900
1961
  if (btn) btn.classList.remove('spinning');
1901
1962
  updateCountdownLabel();
1902
1963
  })();
@@ -1935,13 +1996,14 @@ function resumeAutoRefreshIfPaused() {
1935
1996
  }
1936
1997
  }
1937
1998
 
1938
- // Clear AI insights on fresh page load (not on in-app refresh)
1939
- fetch('/api/ai-analyze', { method: 'DELETE' }).catch(() => {});
1999
+ document.addEventListener('keydown', e => {
2000
+ if (e.key === 'Escape' && currentView === 'session') closeSessionDrawer();
2001
+ });
1940
2002
 
1941
2003
  fetchData().then(data => {
1942
2004
  currentData = data;
1943
2005
  renderDashboard(data);
1944
- loadCachedAIInsights(); // loads model list (cache already cleared above)
2006
+ loadCachedAIInsights();
1945
2007
  if (autoRefreshActive) startAutoRefresh();
1946
2008
  });
1947
2009