@kosinal/claude-code-dashboard 0.0.5 → 0.0.6

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.
Files changed (2) hide show
  1. package/dist/bin.js +108 -28
  2. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -13,7 +13,6 @@ var MARKER_QUICK = "__claude_code_dashboard_quick__";
13
13
  var MARKER_INSTALL = "__claude_code_dashboard_install__";
14
14
  var MARKER_LEGACY = "__claude_code_dashboard__";
15
15
  var HOOK_EVENTS = ["SessionStart", "UserPromptSubmit", "Stop", "SessionEnd"];
16
- var INTERACTIVE_TOOLS_MATCHER = "ExitPlanMode|AskUserQuestion";
17
16
  function getConfigDir(configDir) {
18
17
  return configDir ?? path.join(os.homedir(), ".claude");
19
18
  }
@@ -104,7 +103,6 @@ function installHooks(port, configDir) {
104
103
  settings.hooks.PreToolUse = [];
105
104
  }
106
105
  settings.hooks.PreToolUse.push({
107
- matcher: INTERACTIVE_TOOLS_MATCHER,
108
106
  hooks: [
109
107
  {
110
108
  type: "command",
@@ -142,7 +140,6 @@ function installHooksWithCommand(command, configDir) {
142
140
  settings.hooks.PreToolUse = [];
143
141
  }
144
142
  settings.hooks.PreToolUse.push({
145
- matcher: INTERACTIVE_TOOLS_MATCHER,
146
143
  hooks: [
147
144
  {
148
145
  type: "command",
@@ -304,7 +301,7 @@ async function waitForServer(maxWaitMs = 10000) {
304
301
  let delay = 100;
305
302
  while (Date.now() - start < maxWaitMs) {
306
303
  try {
307
- await postHook('{"session_id":"ping","hook_event_name":"Ping"}');
304
+ await httpPing();
308
305
  return true;
309
306
  } catch {
310
307
  await new Promise(r => setTimeout(r, delay));
@@ -762,6 +759,43 @@ function getDashboardHtml() {
762
759
 
763
760
  .connection-dot.connected { background: #3fb950; }
764
761
 
762
+ .notif-banner {
763
+ display: flex;
764
+ align-items: center;
765
+ justify-content: space-between;
766
+ background: rgba(210, 153, 34, 0.15);
767
+ border: 1px solid #d29922;
768
+ border-radius: 6px;
769
+ padding: 10px 16px;
770
+ margin-bottom: 24px;
771
+ font-size: 13px;
772
+ color: #d29922;
773
+ animation: bannerFadeIn 0.2s ease-out;
774
+ }
775
+
776
+ .notif-banner button {
777
+ background: #d29922;
778
+ color: #0d1117;
779
+ border: none;
780
+ border-radius: 4px;
781
+ padding: 4px 12px;
782
+ font-size: 12px;
783
+ font-weight: 600;
784
+ cursor: pointer;
785
+ white-space: nowrap;
786
+ margin-left: 12px;
787
+ transition: background 0.2s;
788
+ }
789
+
790
+ .notif-banner button:hover {
791
+ background: #e3b341;
792
+ }
793
+
794
+ @keyframes bannerFadeIn {
795
+ from { opacity: 0; transform: translateY(-4px); }
796
+ to { opacity: 1; transform: translateY(0); }
797
+ }
798
+
765
799
  .overlay {
766
800
  position: fixed;
767
801
  top: 0;
@@ -985,6 +1019,7 @@ function getDashboardHtml() {
985
1019
  </div>
986
1020
  </div>
987
1021
  </header>
1022
+ <div id="notifBanner"></div>
988
1023
  <div id="overlayContainer"></div>
989
1024
  <main id="app">
990
1025
  <div class="empty-state">
@@ -1006,18 +1041,51 @@ function getDashboardHtml() {
1006
1041
  var btnStop = document.getElementById('btnStop');
1007
1042
  var btnRestart = document.getElementById('btnRestart');
1008
1043
  var notifToggle = document.getElementById('notifToggle');
1044
+ var notifBanner = document.getElementById('notifBanner');
1009
1045
  var notificationsEnabled = localStorage.getItem('notificationsEnabled') !== 'false';
1010
1046
  var sessions = [];
1011
1047
  var previousStatuses = {};
1012
1048
  var initialized = false;
1013
1049
  var es = null;
1050
+ var bannerTimer = null;
1051
+
1052
+ function showNotifBanner() {
1053
+ if (bannerTimer) { clearTimeout(bannerTimer); bannerTimer = null; }
1054
+ notifBanner.innerHTML =
1055
+ '<div class="notif-banner">' +
1056
+ '<span>Notifications enabled. Push button to test.</span>' +
1057
+ '<button id="btnTestNotif">Test</button>' +
1058
+ '</div>';
1059
+ document.getElementById('btnTestNotif').onclick = function() {
1060
+ if ('Notification' in window && Notification.permission === 'granted') {
1061
+ new Notification('Claude Code - Test', { body: 'Notifications are working!' });
1062
+ } else if ('Notification' in window && Notification.permission === 'default') {
1063
+ Notification.requestPermission().then(function(p) {
1064
+ if (p === 'granted') {
1065
+ new Notification('Claude Code - Test', { body: 'Notifications are working!' });
1066
+ }
1067
+ });
1068
+ }
1069
+ };
1070
+ bannerTimer = setTimeout(hideNotifBanner, 10000);
1071
+ }
1072
+
1073
+ function hideNotifBanner() {
1074
+ notifBanner.innerHTML = '';
1075
+ if (bannerTimer) { clearTimeout(bannerTimer); bannerTimer = null; }
1076
+ }
1014
1077
 
1015
1078
  notifToggle.checked = notificationsEnabled;
1016
1079
  notifToggle.addEventListener('change', function() {
1017
1080
  notificationsEnabled = notifToggle.checked;
1018
1081
  localStorage.setItem('notificationsEnabled', notificationsEnabled ? 'true' : 'false');
1019
- if (notificationsEnabled && 'Notification' in window && Notification.permission === 'default') {
1020
- Notification.requestPermission();
1082
+ if (notificationsEnabled) {
1083
+ if ('Notification' in window && Notification.permission === 'default') {
1084
+ Notification.requestPermission();
1085
+ }
1086
+ showNotifBanner();
1087
+ } else {
1088
+ hideNotifBanner();
1021
1089
  }
1022
1090
  });
1023
1091
 
@@ -1288,7 +1356,9 @@ data: ${data}
1288
1356
  return;
1289
1357
  }
1290
1358
  store.handleEvent(payload);
1291
- broadcast();
1359
+ if (payload.hook_event_name !== "Ping") {
1360
+ broadcast();
1361
+ }
1292
1362
  res.writeHead(200, { "Content-Type": "application/json" });
1293
1363
  res.end(JSON.stringify({ ok: true }));
1294
1364
  } catch {
@@ -1364,41 +1434,51 @@ data: ${initData}
1364
1434
  var EVENT_TO_STATUS = {
1365
1435
  SessionStart: "done",
1366
1436
  UserPromptSubmit: "running",
1367
- Stop: "done",
1368
- PreToolUse: "waiting"
1437
+ Stop: "done"
1369
1438
  };
1439
+ var INTERACTIVE_TOOLS = /* @__PURE__ */ new Set(["ExitPlanMode", "AskUserQuestion"]);
1370
1440
  function createStore() {
1371
1441
  const sessions = /* @__PURE__ */ new Map();
1372
1442
  return {
1373
1443
  handleEvent(payload) {
1374
1444
  const { session_id, hook_event_name, cwd } = payload;
1375
- if (hook_event_name === "SessionEnd") {
1376
- sessions.delete(session_id);
1445
+ if (hook_event_name === "Ping") {
1377
1446
  return null;
1378
1447
  }
1379
- if (hook_event_name === "Ping") {
1448
+ if (hook_event_name === "SessionEnd") {
1449
+ sessions.delete(session_id);
1380
1450
  return null;
1381
1451
  }
1382
- const status = EVENT_TO_STATUS[hook_event_name];
1383
- if (!status) {
1384
- const existing2 = sessions.get(session_id);
1385
- if (existing2) return existing2;
1386
- const session2 = {
1387
- sessionId: session_id,
1388
- status: "waiting",
1389
- cwd: cwd ?? "",
1390
- lastEvent: hook_event_name,
1391
- updatedAt: Date.now(),
1392
- startedAt: Date.now()
1393
- };
1394
- sessions.set(session_id, session2);
1395
- return session2;
1452
+ let status;
1453
+ let displayEvent;
1454
+ if (hook_event_name === "PreToolUse") {
1455
+ const toolName = typeof payload.tool_name === "string" ? payload.tool_name : "";
1456
+ displayEvent = toolName || hook_event_name;
1457
+ status = INTERACTIVE_TOOLS.has(toolName) ? "waiting" : "running";
1458
+ } else {
1459
+ const mapped = EVENT_TO_STATUS[hook_event_name];
1460
+ if (!mapped) {
1461
+ const existing2 = sessions.get(session_id);
1462
+ if (existing2) return existing2;
1463
+ const session2 = {
1464
+ sessionId: session_id,
1465
+ status: "waiting",
1466
+ cwd: cwd ?? "",
1467
+ lastEvent: hook_event_name,
1468
+ updatedAt: Date.now(),
1469
+ startedAt: Date.now()
1470
+ };
1471
+ sessions.set(session_id, session2);
1472
+ return session2;
1473
+ }
1474
+ status = mapped;
1475
+ displayEvent = hook_event_name;
1396
1476
  }
1397
1477
  const now = Date.now();
1398
1478
  const existing = sessions.get(session_id);
1399
1479
  if (existing) {
1400
1480
  existing.status = status;
1401
- existing.lastEvent = hook_event_name;
1481
+ existing.lastEvent = displayEvent;
1402
1482
  existing.updatedAt = now;
1403
1483
  if (cwd) existing.cwd = cwd;
1404
1484
  return existing;
@@ -1407,7 +1487,7 @@ function createStore() {
1407
1487
  sessionId: session_id,
1408
1488
  status,
1409
1489
  cwd: cwd ?? "",
1410
- lastEvent: hook_event_name,
1490
+ lastEvent: displayEvent,
1411
1491
  updatedAt: now,
1412
1492
  startedAt: now
1413
1493
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kosinal/claude-code-dashboard",
3
- "version": "0.0.5",
3
+ "version": "0.0.6",
4
4
  "type": "module",
5
5
  "description": "Real-time browser dashboard for Claude Code session states",
6
6
  "bin": {