@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 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 h2 {
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-title { font-weight: 500; margin-bottom: 2px; }
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
- <h2>Notifications</h2>
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
- el.innerHTML = state.notifications.slice(0, 50).map(n => {
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}">&times;</button></div>
745
777
  <div class="notif-message">${esc(n.message)}</div>
746
778
  <div class="notif-time">${sName} &middot; ${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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@delt/claude-alarm",
3
- "version": "0.3.6",
3
+ "version": "0.3.8",
4
4
  "description": "Monitor and get notifications from multiple Claude Code sessions via MCP Channels",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -334,17 +334,7 @@
334
334
  overflow-y: auto;
335
335
  padding: 12px;
336
336
  }
337
- .notifications-panel h2 {
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-title { font-weight: 500; margin-bottom: 2px; }
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
- <h2>Notifications</h2>
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
- el.innerHTML = state.notifications.slice(0, 50).map(n => {
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}">&times;</button></div>
745
777
  <div class="notif-message">${esc(n.message)}</div>
746
778
  <div class="notif-time">${sName} &middot; ${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');