@delt/claude-alarm 0.3.6 → 0.3.8
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 +3 -3
- package/dist/dashboard/index.html +64 -17
- package/package.json +1 -1
- package/src/dashboard/index.html +64 -17
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ Monitor and interact with multiple Claude Code sessions from a web dashboard. Ge
|
|
|
7
7
|
## Architecture
|
|
8
8
|
|
|
9
9
|
<p align="center">
|
|
10
|
-
<img src="docs/architecture.svg" alt="Architecture" width="800">
|
|
10
|
+
<img src="https://raw.githubusercontent.com/delt96/delt-claude-alarm/main/docs/architecture.svg" alt="Architecture" width="800">
|
|
11
11
|
</p>
|
|
12
12
|
|
|
13
13
|
## Features
|
|
@@ -53,7 +53,7 @@ Open `http://127.0.0.1:7900` in your browser.
|
|
|
53
53
|
## Message Flow
|
|
54
54
|
|
|
55
55
|
<p align="center">
|
|
56
|
-
<img src="docs/message-flow.svg" alt="Message Flow" width="700">
|
|
56
|
+
<img src="https://raw.githubusercontent.com/delt96/delt-claude-alarm/main/docs/message-flow.svg" alt="Message Flow" width="700">
|
|
57
57
|
</p>
|
|
58
58
|
|
|
59
59
|
## Dashboard Layout
|
|
@@ -151,7 +151,7 @@ Config stored at `~/.claude-alarm/config.json`:
|
|
|
151
151
|
## Remote Access
|
|
152
152
|
|
|
153
153
|
<p align="center">
|
|
154
|
-
<img src="docs/remote-access.svg" alt="Remote Access" width="600">
|
|
154
|
+
<img src="https://raw.githubusercontent.com/delt96/delt-claude-alarm/main/docs/remote-access.svg" alt="Remote Access" width="600">
|
|
155
155
|
</p>
|
|
156
156
|
|
|
157
157
|
1. Set host to `0.0.0.0` in `~/.claude-alarm/config.json`
|
|
@@ -334,17 +334,7 @@
|
|
|
334
334
|
overflow-y: auto;
|
|
335
335
|
padding: 12px;
|
|
336
336
|
}
|
|
337
|
-
.notifications-panel
|
|
338
|
-
font-size: 13px;
|
|
339
|
-
text-transform: uppercase;
|
|
340
|
-
letter-spacing: 0.5px;
|
|
341
|
-
color: var(--text-dim);
|
|
342
|
-
padding: 8px 8px 12px;
|
|
343
|
-
position: sticky;
|
|
344
|
-
top: 0;
|
|
345
|
-
background: var(--bg);
|
|
346
|
-
z-index: 1;
|
|
347
|
-
}
|
|
337
|
+
.notifications-panel > .notif-header + #notifList { /* spacer */ }
|
|
348
338
|
.notif-item {
|
|
349
339
|
background: var(--surface);
|
|
350
340
|
border: 1px solid var(--border);
|
|
@@ -355,9 +345,43 @@
|
|
|
355
345
|
cursor: pointer;
|
|
356
346
|
}
|
|
357
347
|
.notif-item:hover { border-color: var(--accent); }
|
|
358
|
-
.notif-item .notif-
|
|
348
|
+
.notif-item:hover .notif-dismiss { opacity: 1; }
|
|
349
|
+
.notif-item .notif-title { font-weight: 500; margin-bottom: 2px; display: flex; align-items: center; }
|
|
350
|
+
.notif-item .notif-title-text { flex: 1; }
|
|
351
|
+
.notif-dismiss {
|
|
352
|
+
opacity: 0;
|
|
353
|
+
background: none;
|
|
354
|
+
border: none;
|
|
355
|
+
color: var(--text-dim);
|
|
356
|
+
cursor: pointer;
|
|
357
|
+
font-size: 14px;
|
|
358
|
+
padding: 0 2px;
|
|
359
|
+
transition: opacity 0.15s;
|
|
360
|
+
}
|
|
361
|
+
.notif-dismiss:hover { color: var(--red); }
|
|
359
362
|
.notif-item .notif-message { color: var(--text-dim); }
|
|
360
363
|
.notif-item .notif-time { font-size: 11px; color: var(--text-dim); margin-top: 4px; }
|
|
364
|
+
.notif-header {
|
|
365
|
+
display: flex;
|
|
366
|
+
align-items: center;
|
|
367
|
+
justify-content: space-between;
|
|
368
|
+
padding: 8px 8px 12px;
|
|
369
|
+
position: sticky;
|
|
370
|
+
top: 0;
|
|
371
|
+
background: var(--bg);
|
|
372
|
+
z-index: 1;
|
|
373
|
+
}
|
|
374
|
+
.notif-header h2 { font-size: 13px; text-transform: uppercase; letter-spacing: 0.5px; color: var(--text-dim); padding: 0; }
|
|
375
|
+
.notif-clear-all {
|
|
376
|
+
background: none;
|
|
377
|
+
border: 1px solid var(--border);
|
|
378
|
+
border-radius: 4px;
|
|
379
|
+
color: var(--text-dim);
|
|
380
|
+
cursor: pointer;
|
|
381
|
+
font-size: 11px;
|
|
382
|
+
padding: 2px 8px;
|
|
383
|
+
}
|
|
384
|
+
.notif-clear-all:hover { border-color: var(--red); color: var(--red); }
|
|
361
385
|
.notif-level {
|
|
362
386
|
display: inline-block;
|
|
363
387
|
width: 6px; height: 6px;
|
|
@@ -501,7 +525,10 @@
|
|
|
501
525
|
</div>
|
|
502
526
|
|
|
503
527
|
<div class="notifications-panel">
|
|
504
|
-
<
|
|
528
|
+
<div class="notif-header">
|
|
529
|
+
<h2>Notifications</h2>
|
|
530
|
+
<button class="notif-clear-all" id="notifClearAll">Clear all</button>
|
|
531
|
+
</div>
|
|
505
532
|
<div id="notifList">
|
|
506
533
|
<div class="empty-state">No notifications yet</div>
|
|
507
534
|
</div>
|
|
@@ -618,6 +645,8 @@
|
|
|
618
645
|
if (!state.messages[msg.sessionId]) state.messages[msg.sessionId] = [];
|
|
619
646
|
state.messages[msg.sessionId].push({ from: 'session', content: msg.content, time: msg.timestamp });
|
|
620
647
|
if (state.selectedSession === msg.sessionId) renderMessages();
|
|
648
|
+
state.notifications.unshift({ sessionId: msg.sessionId, title: 'Reply', message: msg.content.slice(0, 100), level: 'info', time: msg.timestamp });
|
|
649
|
+
renderNotifications();
|
|
621
650
|
break;
|
|
622
651
|
case 'notification':
|
|
623
652
|
state.notifications.unshift({ sessionId: msg.sessionId, title: msg.title, message: msg.message, level: msg.level || 'info', time: msg.timestamp });
|
|
@@ -732,26 +761,38 @@
|
|
|
732
761
|
|
|
733
762
|
function renderNotifications() {
|
|
734
763
|
const el = $('#notifList');
|
|
764
|
+
const clearBtn = $('#notifClearAll');
|
|
735
765
|
if (!state.notifications.length) {
|
|
736
766
|
el.innerHTML = '<div class="empty-state">No notifications yet</div>';
|
|
767
|
+
clearBtn.style.display = 'none';
|
|
737
768
|
return;
|
|
738
769
|
}
|
|
739
|
-
|
|
770
|
+
clearBtn.style.display = 'block';
|
|
771
|
+
el.innerHTML = state.notifications.slice(0, 50).map((n, i) => {
|
|
740
772
|
const timeStr = new Date(n.time).toLocaleTimeString();
|
|
741
773
|
const session = state.sessions[n.sessionId];
|
|
742
774
|
const sName = session ? session.name : n.sessionId.slice(0, 8);
|
|
743
|
-
return `<div class="notif-item" data-session="${n.sessionId}">
|
|
744
|
-
<div class="notif-title"><span class="notif-level ${n.level}"></span>${esc(n.title)}</div>
|
|
775
|
+
return `<div class="notif-item" data-session="${n.sessionId}" data-index="${i}">
|
|
776
|
+
<div class="notif-title"><span class="notif-level ${n.level}"></span><span class="notif-title-text">${esc(n.title)}</span><button class="notif-dismiss" data-index="${i}">×</button></div>
|
|
745
777
|
<div class="notif-message">${esc(n.message)}</div>
|
|
746
778
|
<div class="notif-time">${sName} · ${timeStr}</div>
|
|
747
779
|
</div>`;
|
|
748
780
|
}).join('');
|
|
749
781
|
el.querySelectorAll('.notif-item').forEach(item => {
|
|
750
|
-
item.addEventListener('click', () => {
|
|
782
|
+
item.addEventListener('click', (e) => {
|
|
783
|
+
if (e.target.classList.contains('notif-dismiss')) return;
|
|
751
784
|
const sid = item.dataset.session;
|
|
752
785
|
if (sid && state.sessions[sid]) selectSession(sid);
|
|
753
786
|
});
|
|
754
787
|
});
|
|
788
|
+
el.querySelectorAll('.notif-dismiss').forEach(btn => {
|
|
789
|
+
btn.addEventListener('click', (e) => {
|
|
790
|
+
e.stopPropagation();
|
|
791
|
+
const idx = parseInt(btn.dataset.index);
|
|
792
|
+
state.notifications.splice(idx, 1);
|
|
793
|
+
renderNotifications();
|
|
794
|
+
});
|
|
795
|
+
});
|
|
755
796
|
}
|
|
756
797
|
|
|
757
798
|
// --- Image handling ---
|
|
@@ -901,6 +942,12 @@
|
|
|
901
942
|
return html;
|
|
902
943
|
}
|
|
903
944
|
|
|
945
|
+
// Clear all notifications
|
|
946
|
+
$('#notifClearAll').addEventListener('click', () => {
|
|
947
|
+
state.notifications = [];
|
|
948
|
+
renderNotifications();
|
|
949
|
+
});
|
|
950
|
+
|
|
904
951
|
// Scroll to bottom button
|
|
905
952
|
const msgList = $('#messagesList');
|
|
906
953
|
const scrollBtn = $('#scrollBottom');
|
package/package.json
CHANGED
package/src/dashboard/index.html
CHANGED
|
@@ -334,17 +334,7 @@
|
|
|
334
334
|
overflow-y: auto;
|
|
335
335
|
padding: 12px;
|
|
336
336
|
}
|
|
337
|
-
.notifications-panel
|
|
338
|
-
font-size: 13px;
|
|
339
|
-
text-transform: uppercase;
|
|
340
|
-
letter-spacing: 0.5px;
|
|
341
|
-
color: var(--text-dim);
|
|
342
|
-
padding: 8px 8px 12px;
|
|
343
|
-
position: sticky;
|
|
344
|
-
top: 0;
|
|
345
|
-
background: var(--bg);
|
|
346
|
-
z-index: 1;
|
|
347
|
-
}
|
|
337
|
+
.notifications-panel > .notif-header + #notifList { /* spacer */ }
|
|
348
338
|
.notif-item {
|
|
349
339
|
background: var(--surface);
|
|
350
340
|
border: 1px solid var(--border);
|
|
@@ -355,9 +345,43 @@
|
|
|
355
345
|
cursor: pointer;
|
|
356
346
|
}
|
|
357
347
|
.notif-item:hover { border-color: var(--accent); }
|
|
358
|
-
.notif-item .notif-
|
|
348
|
+
.notif-item:hover .notif-dismiss { opacity: 1; }
|
|
349
|
+
.notif-item .notif-title { font-weight: 500; margin-bottom: 2px; display: flex; align-items: center; }
|
|
350
|
+
.notif-item .notif-title-text { flex: 1; }
|
|
351
|
+
.notif-dismiss {
|
|
352
|
+
opacity: 0;
|
|
353
|
+
background: none;
|
|
354
|
+
border: none;
|
|
355
|
+
color: var(--text-dim);
|
|
356
|
+
cursor: pointer;
|
|
357
|
+
font-size: 14px;
|
|
358
|
+
padding: 0 2px;
|
|
359
|
+
transition: opacity 0.15s;
|
|
360
|
+
}
|
|
361
|
+
.notif-dismiss:hover { color: var(--red); }
|
|
359
362
|
.notif-item .notif-message { color: var(--text-dim); }
|
|
360
363
|
.notif-item .notif-time { font-size: 11px; color: var(--text-dim); margin-top: 4px; }
|
|
364
|
+
.notif-header {
|
|
365
|
+
display: flex;
|
|
366
|
+
align-items: center;
|
|
367
|
+
justify-content: space-between;
|
|
368
|
+
padding: 8px 8px 12px;
|
|
369
|
+
position: sticky;
|
|
370
|
+
top: 0;
|
|
371
|
+
background: var(--bg);
|
|
372
|
+
z-index: 1;
|
|
373
|
+
}
|
|
374
|
+
.notif-header h2 { font-size: 13px; text-transform: uppercase; letter-spacing: 0.5px; color: var(--text-dim); padding: 0; }
|
|
375
|
+
.notif-clear-all {
|
|
376
|
+
background: none;
|
|
377
|
+
border: 1px solid var(--border);
|
|
378
|
+
border-radius: 4px;
|
|
379
|
+
color: var(--text-dim);
|
|
380
|
+
cursor: pointer;
|
|
381
|
+
font-size: 11px;
|
|
382
|
+
padding: 2px 8px;
|
|
383
|
+
}
|
|
384
|
+
.notif-clear-all:hover { border-color: var(--red); color: var(--red); }
|
|
361
385
|
.notif-level {
|
|
362
386
|
display: inline-block;
|
|
363
387
|
width: 6px; height: 6px;
|
|
@@ -501,7 +525,10 @@
|
|
|
501
525
|
</div>
|
|
502
526
|
|
|
503
527
|
<div class="notifications-panel">
|
|
504
|
-
<
|
|
528
|
+
<div class="notif-header">
|
|
529
|
+
<h2>Notifications</h2>
|
|
530
|
+
<button class="notif-clear-all" id="notifClearAll">Clear all</button>
|
|
531
|
+
</div>
|
|
505
532
|
<div id="notifList">
|
|
506
533
|
<div class="empty-state">No notifications yet</div>
|
|
507
534
|
</div>
|
|
@@ -618,6 +645,8 @@
|
|
|
618
645
|
if (!state.messages[msg.sessionId]) state.messages[msg.sessionId] = [];
|
|
619
646
|
state.messages[msg.sessionId].push({ from: 'session', content: msg.content, time: msg.timestamp });
|
|
620
647
|
if (state.selectedSession === msg.sessionId) renderMessages();
|
|
648
|
+
state.notifications.unshift({ sessionId: msg.sessionId, title: 'Reply', message: msg.content.slice(0, 100), level: 'info', time: msg.timestamp });
|
|
649
|
+
renderNotifications();
|
|
621
650
|
break;
|
|
622
651
|
case 'notification':
|
|
623
652
|
state.notifications.unshift({ sessionId: msg.sessionId, title: msg.title, message: msg.message, level: msg.level || 'info', time: msg.timestamp });
|
|
@@ -732,26 +761,38 @@
|
|
|
732
761
|
|
|
733
762
|
function renderNotifications() {
|
|
734
763
|
const el = $('#notifList');
|
|
764
|
+
const clearBtn = $('#notifClearAll');
|
|
735
765
|
if (!state.notifications.length) {
|
|
736
766
|
el.innerHTML = '<div class="empty-state">No notifications yet</div>';
|
|
767
|
+
clearBtn.style.display = 'none';
|
|
737
768
|
return;
|
|
738
769
|
}
|
|
739
|
-
|
|
770
|
+
clearBtn.style.display = 'block';
|
|
771
|
+
el.innerHTML = state.notifications.slice(0, 50).map((n, i) => {
|
|
740
772
|
const timeStr = new Date(n.time).toLocaleTimeString();
|
|
741
773
|
const session = state.sessions[n.sessionId];
|
|
742
774
|
const sName = session ? session.name : n.sessionId.slice(0, 8);
|
|
743
|
-
return `<div class="notif-item" data-session="${n.sessionId}">
|
|
744
|
-
<div class="notif-title"><span class="notif-level ${n.level}"></span>${esc(n.title)}</div>
|
|
775
|
+
return `<div class="notif-item" data-session="${n.sessionId}" data-index="${i}">
|
|
776
|
+
<div class="notif-title"><span class="notif-level ${n.level}"></span><span class="notif-title-text">${esc(n.title)}</span><button class="notif-dismiss" data-index="${i}">×</button></div>
|
|
745
777
|
<div class="notif-message">${esc(n.message)}</div>
|
|
746
778
|
<div class="notif-time">${sName} · ${timeStr}</div>
|
|
747
779
|
</div>`;
|
|
748
780
|
}).join('');
|
|
749
781
|
el.querySelectorAll('.notif-item').forEach(item => {
|
|
750
|
-
item.addEventListener('click', () => {
|
|
782
|
+
item.addEventListener('click', (e) => {
|
|
783
|
+
if (e.target.classList.contains('notif-dismiss')) return;
|
|
751
784
|
const sid = item.dataset.session;
|
|
752
785
|
if (sid && state.sessions[sid]) selectSession(sid);
|
|
753
786
|
});
|
|
754
787
|
});
|
|
788
|
+
el.querySelectorAll('.notif-dismiss').forEach(btn => {
|
|
789
|
+
btn.addEventListener('click', (e) => {
|
|
790
|
+
e.stopPropagation();
|
|
791
|
+
const idx = parseInt(btn.dataset.index);
|
|
792
|
+
state.notifications.splice(idx, 1);
|
|
793
|
+
renderNotifications();
|
|
794
|
+
});
|
|
795
|
+
});
|
|
755
796
|
}
|
|
756
797
|
|
|
757
798
|
// --- Image handling ---
|
|
@@ -901,6 +942,12 @@
|
|
|
901
942
|
return html;
|
|
902
943
|
}
|
|
903
944
|
|
|
945
|
+
// Clear all notifications
|
|
946
|
+
$('#notifClearAll').addEventListener('click', () => {
|
|
947
|
+
state.notifications = [];
|
|
948
|
+
renderNotifications();
|
|
949
|
+
});
|
|
950
|
+
|
|
904
951
|
// Scroll to bottom button
|
|
905
952
|
const msgList = $('#messagesList');
|
|
906
953
|
const scrollBtn = $('#scrollBottom');
|