@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.
- package/dist/bin.js +108 -28
- 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
|
|
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
|
|
1020
|
-
Notification.
|
|
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
|
-
|
|
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 === "
|
|
1376
|
-
sessions.delete(session_id);
|
|
1445
|
+
if (hook_event_name === "Ping") {
|
|
1377
1446
|
return null;
|
|
1378
1447
|
}
|
|
1379
|
-
if (hook_event_name === "
|
|
1448
|
+
if (hook_event_name === "SessionEnd") {
|
|
1449
|
+
sessions.delete(session_id);
|
|
1380
1450
|
return null;
|
|
1381
1451
|
}
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
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 =
|
|
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:
|
|
1490
|
+
lastEvent: displayEvent,
|
|
1411
1491
|
updatedAt: now,
|
|
1412
1492
|
startedAt: now
|
|
1413
1493
|
};
|