@delt/claude-alarm 0.4.0 → 0.4.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.
@@ -92,17 +92,56 @@
92
92
  overflow-y: auto;
93
93
  padding: 12px;
94
94
  }
95
- .sessions-panel h2 {
96
- font-size: 13px;
97
- text-transform: uppercase;
98
- letter-spacing: 0.5px;
99
- color: var(--text-dim);
95
+ .sessions-header {
96
+ display: flex;
97
+ align-items: center;
98
+ justify-content: space-between;
100
99
  padding: 8px 8px 12px;
101
100
  position: sticky;
102
101
  top: 0;
103
102
  background: var(--bg);
104
103
  z-index: 1;
105
104
  }
105
+ .sessions-header h2 {
106
+ font-size: 13px;
107
+ text-transform: uppercase;
108
+ letter-spacing: 0.5px;
109
+ color: var(--text-dim);
110
+ }
111
+ .add-session-btn {
112
+ background: none;
113
+ border: 1px solid var(--border);
114
+ border-radius: 6px;
115
+ color: var(--text-dim);
116
+ cursor: pointer;
117
+ font-size: 16px;
118
+ width: 28px;
119
+ height: 28px;
120
+ display: flex;
121
+ align-items: center;
122
+ justify-content: center;
123
+ transition: border-color 0.15s, color 0.15s;
124
+ }
125
+ .add-session-btn:hover { border-color: var(--accent); color: var(--accent); }
126
+ .cmd-popup {
127
+ position: absolute;
128
+ top: 44px;
129
+ left: 12px;
130
+ right: 12px;
131
+ background: var(--surface);
132
+ border: 1px solid var(--border);
133
+ border-radius: 8px;
134
+ padding: 12px;
135
+ z-index: 10;
136
+ box-shadow: 0 4px 12px rgba(0,0,0,0.3);
137
+ display: none;
138
+ }
139
+ .cmd-popup.active { display: block; }
140
+ .cmd-popup-title {
141
+ font-size: 12px;
142
+ color: var(--text-dim);
143
+ margin-bottom: 8px;
144
+ }
106
145
  .session-card {
107
146
  background: var(--surface);
108
147
  border: 1px solid var(--border);
@@ -565,13 +604,13 @@
565
604
 
566
605
  <div class="token-overlay hidden" id="webhookOverlay">
567
606
  <div class="token-form" style="max-width:500px;text-align:left">
568
- <h2 style="text-align:center">Webhook Settings</h2>
569
- <p style="text-align:center">Add webhook URLs to receive notifications externally.</p>
570
- <div id="webhookList"></div>
571
- <button id="webhookAdd" style="width:100%;margin-top:8px;background:var(--surface);color:var(--text);border:1px dashed var(--border);border-radius:6px;padding:8px;cursor:pointer;font-size:13px">+ Add Webhook</button>
572
- <div style="display:flex;gap:8px;margin-top:16px">
573
- <button id="webhookSave" style="flex:1">Save</button>
574
- <button id="webhookCancel" style="flex:1;background:var(--surface);color:var(--text);border:1px solid var(--border)">Cancel</button>
607
+ <h2 style="text-align:center;margin-bottom:4px">Webhook Settings</h2>
608
+ <p style="text-align:center;margin-bottom:16px">Send notifications to Slack, Discord, or any webhook endpoint.</p>
609
+ <div id="webhookList" style="max-height:240px;overflow-y:auto"></div>
610
+ <button id="webhookAdd" style="width:100%;margin-top:10px;background:none;color:var(--text-dim);border:1px dashed var(--border);border-radius:6px;padding:10px;cursor:pointer;font-size:13px;transition:border-color 0.15s">+ Add Webhook</button>
611
+ <div style="display:flex;gap:8px;margin-top:20px">
612
+ <button id="webhookSave" style="flex:1;padding:10px">Save</button>
613
+ <button id="webhookCancel" style="flex:1;padding:10px;background:none;color:var(--text);border:1px solid var(--border);border-radius:6px;cursor:pointer;font-size:14px;font-weight:500">Cancel</button>
575
614
  </div>
576
615
  </div>
577
616
  </div>
@@ -582,8 +621,23 @@
582
621
  <button data-tab="notifications">Notifications</button>
583
622
  </div>
584
623
  <div class="container">
585
- <div class="sessions-panel">
586
- <h2>Sessions</h2>
624
+ <div class="sessions-panel" style="position:relative">
625
+ <div class="sessions-header">
626
+ <h2>Sessions</h2>
627
+ <button class="add-session-btn" id="addSessionBtn" title="Add session">+</button>
628
+ </div>
629
+ <div class="cmd-popup" id="cmdPopup">
630
+ <div class="cmd-popup-title">Run in terminal to connect:</div>
631
+ <div class="cmd-copy" id="cmdCopy1" title="Click to copy">
632
+ <span class="cmd-text">claude --dangerously-load-development-channels server:claude-alarm</span>
633
+ <span class="cmd-icon">&#128203;</span>
634
+ </div>
635
+ <div style="margin-top:6px;font-size:11px;color:var(--text-dim)">with auto-approve:</div>
636
+ <div class="cmd-copy" id="cmdCopy2" title="Click to copy">
637
+ <span class="cmd-text">claude --dangerously-load-development-channels server:claude-alarm --dangerously-skip-permissions</span>
638
+ <span class="cmd-icon">&#128203;</span>
639
+ </div>
640
+ </div>
587
641
  <div id="sessionsList"></div>
588
642
  </div>
589
643
 
@@ -745,30 +799,7 @@
745
799
  const el = $('#sessionsList');
746
800
  const ids = Object.keys(state.sessions);
747
801
  if (!ids.length) {
748
- el.innerHTML = `<div class="no-sessions">No active sessions.<br>Start Claude Code with the channel:
749
- <div class="cmd-copy" id="cmdCopy1" title="Click to copy">
750
- <span class="cmd-text">claude --dangerously-load-development-channels server:claude-alarm</span>
751
- <span class="cmd-icon">&#128203;</span>
752
- </div>
753
- <div style="margin-top:8px;font-size:11px;color:var(--text-dim)">or with auto-approve:</div>
754
- <div class="cmd-copy" id="cmdCopy2" title="Click to copy">
755
- <span class="cmd-text">claude --dangerously-load-development-channels server:claude-alarm --dangerously-skip-permissions</span>
756
- <span class="cmd-icon">&#128203;</span>
757
- </div>
758
- </div>`;
759
- el.querySelectorAll('.cmd-copy').forEach(btn => {
760
- btn.addEventListener('click', () => {
761
- const text = btn.querySelector('.cmd-text').textContent;
762
- navigator.clipboard.writeText(text).then(() => {
763
- btn.classList.add('copied');
764
- btn.querySelector('.cmd-icon').innerHTML = '&#10003;';
765
- setTimeout(() => {
766
- btn.classList.remove('copied');
767
- btn.querySelector('.cmd-icon').innerHTML = '&#128203;';
768
- }, 2000);
769
- });
770
- });
771
- });
802
+ el.innerHTML = '<div class="no-sessions">No active sessions.<br>Click + to see connection commands.</div>';
772
803
  return;
773
804
  }
774
805
  el.innerHTML = ids.map(id => {
@@ -1055,6 +1086,30 @@
1055
1086
  msgList.scrollTo({ top: msgList.scrollHeight, behavior: 'smooth' });
1056
1087
  });
1057
1088
 
1089
+ // --- Add session popup ---
1090
+ $('#addSessionBtn').addEventListener('click', (e) => {
1091
+ e.stopPropagation();
1092
+ $('#cmdPopup').classList.toggle('active');
1093
+ });
1094
+ document.addEventListener('click', (e) => {
1095
+ if (!e.target.closest('#cmdPopup') && !e.target.closest('#addSessionBtn')) {
1096
+ $('#cmdPopup').classList.remove('active');
1097
+ }
1098
+ });
1099
+ document.querySelectorAll('#cmdPopup .cmd-copy').forEach(btn => {
1100
+ btn.addEventListener('click', () => {
1101
+ const text = btn.querySelector('.cmd-text').textContent;
1102
+ navigator.clipboard.writeText(text).then(() => {
1103
+ btn.classList.add('copied');
1104
+ btn.querySelector('.cmd-icon').innerHTML = '&#10003;';
1105
+ setTimeout(() => {
1106
+ btn.classList.remove('copied');
1107
+ btn.querySelector('.cmd-icon').innerHTML = '&#128203;';
1108
+ }, 2000);
1109
+ });
1110
+ });
1111
+ });
1112
+
1058
1113
  // --- Webhook settings ---
1059
1114
  let webhookData = [];
1060
1115
  $('#webhookToggle').addEventListener('click', async () => {
@@ -1086,12 +1141,26 @@
1086
1141
  });
1087
1142
  function renderWebhooks() {
1088
1143
  const el = $('#webhookList');
1089
- el.innerHTML = webhookData.map((w, i) => `<div style="display:flex;gap:6px;margin-bottom:6px">
1090
- <input type="text" value="${esc(w.url)}" placeholder="https://hooks.slack.com/..." style="flex:1;background:var(--bg);border:1px solid var(--border);border-radius:6px;padding:8px;color:var(--text);font-size:13px;outline:none" data-idx="${i}">
1091
- <button onclick="this.parentElement.remove()" style="background:none;border:1px solid var(--border);border-radius:6px;color:var(--red);cursor:pointer;padding:4px 8px">&times;</button>
1144
+ if (!webhookData.length) {
1145
+ el.innerHTML = '<div style="text-align:center;color:var(--text-dim);padding:16px;font-size:13px">No webhooks configured</div>';
1146
+ return;
1147
+ }
1148
+ el.innerHTML = webhookData.map((w, i) => `<div style="display:flex;gap:8px;margin-bottom:8px;align-items:center">
1149
+ <input type="text" value="${esc(w.url)}" placeholder="https://hooks.slack.com/services/..." style="flex:1;background:var(--bg);border:1px solid var(--border);border-radius:6px;padding:10px 12px;color:var(--text);font-size:13px;outline:none;font-family:monospace" data-idx="${i}">
1150
+ <button style="background:none;border:1px solid var(--border);border-radius:6px;color:var(--text-dim);cursor:pointer;padding:6px 10px;font-size:14px;transition:color 0.15s" data-rm="${i}">&times;</button>
1092
1151
  </div>`).join('');
1093
1152
  el.querySelectorAll('input').forEach(inp => {
1094
1153
  inp.addEventListener('input', () => { webhookData[parseInt(inp.dataset.idx)].url = inp.value; });
1154
+ inp.addEventListener('focus', () => { inp.style.borderColor = 'var(--accent)'; });
1155
+ inp.addEventListener('blur', () => { inp.style.borderColor = 'var(--border)'; });
1156
+ });
1157
+ el.querySelectorAll('[data-rm]').forEach(btn => {
1158
+ btn.addEventListener('click', () => {
1159
+ webhookData.splice(parseInt(btn.dataset.rm), 1);
1160
+ renderWebhooks();
1161
+ });
1162
+ btn.addEventListener('mouseenter', () => { btn.style.color = 'var(--red)'; });
1163
+ btn.addEventListener('mouseleave', () => { btn.style.color = 'var(--text-dim)'; });
1095
1164
  });
1096
1165
  }
1097
1166
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@delt/claude-alarm",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
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",
@@ -92,17 +92,56 @@
92
92
  overflow-y: auto;
93
93
  padding: 12px;
94
94
  }
95
- .sessions-panel h2 {
96
- font-size: 13px;
97
- text-transform: uppercase;
98
- letter-spacing: 0.5px;
99
- color: var(--text-dim);
95
+ .sessions-header {
96
+ display: flex;
97
+ align-items: center;
98
+ justify-content: space-between;
100
99
  padding: 8px 8px 12px;
101
100
  position: sticky;
102
101
  top: 0;
103
102
  background: var(--bg);
104
103
  z-index: 1;
105
104
  }
105
+ .sessions-header h2 {
106
+ font-size: 13px;
107
+ text-transform: uppercase;
108
+ letter-spacing: 0.5px;
109
+ color: var(--text-dim);
110
+ }
111
+ .add-session-btn {
112
+ background: none;
113
+ border: 1px solid var(--border);
114
+ border-radius: 6px;
115
+ color: var(--text-dim);
116
+ cursor: pointer;
117
+ font-size: 16px;
118
+ width: 28px;
119
+ height: 28px;
120
+ display: flex;
121
+ align-items: center;
122
+ justify-content: center;
123
+ transition: border-color 0.15s, color 0.15s;
124
+ }
125
+ .add-session-btn:hover { border-color: var(--accent); color: var(--accent); }
126
+ .cmd-popup {
127
+ position: absolute;
128
+ top: 44px;
129
+ left: 12px;
130
+ right: 12px;
131
+ background: var(--surface);
132
+ border: 1px solid var(--border);
133
+ border-radius: 8px;
134
+ padding: 12px;
135
+ z-index: 10;
136
+ box-shadow: 0 4px 12px rgba(0,0,0,0.3);
137
+ display: none;
138
+ }
139
+ .cmd-popup.active { display: block; }
140
+ .cmd-popup-title {
141
+ font-size: 12px;
142
+ color: var(--text-dim);
143
+ margin-bottom: 8px;
144
+ }
106
145
  .session-card {
107
146
  background: var(--surface);
108
147
  border: 1px solid var(--border);
@@ -565,13 +604,13 @@
565
604
 
566
605
  <div class="token-overlay hidden" id="webhookOverlay">
567
606
  <div class="token-form" style="max-width:500px;text-align:left">
568
- <h2 style="text-align:center">Webhook Settings</h2>
569
- <p style="text-align:center">Add webhook URLs to receive notifications externally.</p>
570
- <div id="webhookList"></div>
571
- <button id="webhookAdd" style="width:100%;margin-top:8px;background:var(--surface);color:var(--text);border:1px dashed var(--border);border-radius:6px;padding:8px;cursor:pointer;font-size:13px">+ Add Webhook</button>
572
- <div style="display:flex;gap:8px;margin-top:16px">
573
- <button id="webhookSave" style="flex:1">Save</button>
574
- <button id="webhookCancel" style="flex:1;background:var(--surface);color:var(--text);border:1px solid var(--border)">Cancel</button>
607
+ <h2 style="text-align:center;margin-bottom:4px">Webhook Settings</h2>
608
+ <p style="text-align:center;margin-bottom:16px">Send notifications to Slack, Discord, or any webhook endpoint.</p>
609
+ <div id="webhookList" style="max-height:240px;overflow-y:auto"></div>
610
+ <button id="webhookAdd" style="width:100%;margin-top:10px;background:none;color:var(--text-dim);border:1px dashed var(--border);border-radius:6px;padding:10px;cursor:pointer;font-size:13px;transition:border-color 0.15s">+ Add Webhook</button>
611
+ <div style="display:flex;gap:8px;margin-top:20px">
612
+ <button id="webhookSave" style="flex:1;padding:10px">Save</button>
613
+ <button id="webhookCancel" style="flex:1;padding:10px;background:none;color:var(--text);border:1px solid var(--border);border-radius:6px;cursor:pointer;font-size:14px;font-weight:500">Cancel</button>
575
614
  </div>
576
615
  </div>
577
616
  </div>
@@ -582,8 +621,23 @@
582
621
  <button data-tab="notifications">Notifications</button>
583
622
  </div>
584
623
  <div class="container">
585
- <div class="sessions-panel">
586
- <h2>Sessions</h2>
624
+ <div class="sessions-panel" style="position:relative">
625
+ <div class="sessions-header">
626
+ <h2>Sessions</h2>
627
+ <button class="add-session-btn" id="addSessionBtn" title="Add session">+</button>
628
+ </div>
629
+ <div class="cmd-popup" id="cmdPopup">
630
+ <div class="cmd-popup-title">Run in terminal to connect:</div>
631
+ <div class="cmd-copy" id="cmdCopy1" title="Click to copy">
632
+ <span class="cmd-text">claude --dangerously-load-development-channels server:claude-alarm</span>
633
+ <span class="cmd-icon">&#128203;</span>
634
+ </div>
635
+ <div style="margin-top:6px;font-size:11px;color:var(--text-dim)">with auto-approve:</div>
636
+ <div class="cmd-copy" id="cmdCopy2" title="Click to copy">
637
+ <span class="cmd-text">claude --dangerously-load-development-channels server:claude-alarm --dangerously-skip-permissions</span>
638
+ <span class="cmd-icon">&#128203;</span>
639
+ </div>
640
+ </div>
587
641
  <div id="sessionsList"></div>
588
642
  </div>
589
643
 
@@ -745,30 +799,7 @@
745
799
  const el = $('#sessionsList');
746
800
  const ids = Object.keys(state.sessions);
747
801
  if (!ids.length) {
748
- el.innerHTML = `<div class="no-sessions">No active sessions.<br>Start Claude Code with the channel:
749
- <div class="cmd-copy" id="cmdCopy1" title="Click to copy">
750
- <span class="cmd-text">claude --dangerously-load-development-channels server:claude-alarm</span>
751
- <span class="cmd-icon">&#128203;</span>
752
- </div>
753
- <div style="margin-top:8px;font-size:11px;color:var(--text-dim)">or with auto-approve:</div>
754
- <div class="cmd-copy" id="cmdCopy2" title="Click to copy">
755
- <span class="cmd-text">claude --dangerously-load-development-channels server:claude-alarm --dangerously-skip-permissions</span>
756
- <span class="cmd-icon">&#128203;</span>
757
- </div>
758
- </div>`;
759
- el.querySelectorAll('.cmd-copy').forEach(btn => {
760
- btn.addEventListener('click', () => {
761
- const text = btn.querySelector('.cmd-text').textContent;
762
- navigator.clipboard.writeText(text).then(() => {
763
- btn.classList.add('copied');
764
- btn.querySelector('.cmd-icon').innerHTML = '&#10003;';
765
- setTimeout(() => {
766
- btn.classList.remove('copied');
767
- btn.querySelector('.cmd-icon').innerHTML = '&#128203;';
768
- }, 2000);
769
- });
770
- });
771
- });
802
+ el.innerHTML = '<div class="no-sessions">No active sessions.<br>Click + to see connection commands.</div>';
772
803
  return;
773
804
  }
774
805
  el.innerHTML = ids.map(id => {
@@ -1055,6 +1086,30 @@
1055
1086
  msgList.scrollTo({ top: msgList.scrollHeight, behavior: 'smooth' });
1056
1087
  });
1057
1088
 
1089
+ // --- Add session popup ---
1090
+ $('#addSessionBtn').addEventListener('click', (e) => {
1091
+ e.stopPropagation();
1092
+ $('#cmdPopup').classList.toggle('active');
1093
+ });
1094
+ document.addEventListener('click', (e) => {
1095
+ if (!e.target.closest('#cmdPopup') && !e.target.closest('#addSessionBtn')) {
1096
+ $('#cmdPopup').classList.remove('active');
1097
+ }
1098
+ });
1099
+ document.querySelectorAll('#cmdPopup .cmd-copy').forEach(btn => {
1100
+ btn.addEventListener('click', () => {
1101
+ const text = btn.querySelector('.cmd-text').textContent;
1102
+ navigator.clipboard.writeText(text).then(() => {
1103
+ btn.classList.add('copied');
1104
+ btn.querySelector('.cmd-icon').innerHTML = '&#10003;';
1105
+ setTimeout(() => {
1106
+ btn.classList.remove('copied');
1107
+ btn.querySelector('.cmd-icon').innerHTML = '&#128203;';
1108
+ }, 2000);
1109
+ });
1110
+ });
1111
+ });
1112
+
1058
1113
  // --- Webhook settings ---
1059
1114
  let webhookData = [];
1060
1115
  $('#webhookToggle').addEventListener('click', async () => {
@@ -1086,12 +1141,26 @@
1086
1141
  });
1087
1142
  function renderWebhooks() {
1088
1143
  const el = $('#webhookList');
1089
- el.innerHTML = webhookData.map((w, i) => `<div style="display:flex;gap:6px;margin-bottom:6px">
1090
- <input type="text" value="${esc(w.url)}" placeholder="https://hooks.slack.com/..." style="flex:1;background:var(--bg);border:1px solid var(--border);border-radius:6px;padding:8px;color:var(--text);font-size:13px;outline:none" data-idx="${i}">
1091
- <button onclick="this.parentElement.remove()" style="background:none;border:1px solid var(--border);border-radius:6px;color:var(--red);cursor:pointer;padding:4px 8px">&times;</button>
1144
+ if (!webhookData.length) {
1145
+ el.innerHTML = '<div style="text-align:center;color:var(--text-dim);padding:16px;font-size:13px">No webhooks configured</div>';
1146
+ return;
1147
+ }
1148
+ el.innerHTML = webhookData.map((w, i) => `<div style="display:flex;gap:8px;margin-bottom:8px;align-items:center">
1149
+ <input type="text" value="${esc(w.url)}" placeholder="https://hooks.slack.com/services/..." style="flex:1;background:var(--bg);border:1px solid var(--border);border-radius:6px;padding:10px 12px;color:var(--text);font-size:13px;outline:none;font-family:monospace" data-idx="${i}">
1150
+ <button style="background:none;border:1px solid var(--border);border-radius:6px;color:var(--text-dim);cursor:pointer;padding:6px 10px;font-size:14px;transition:color 0.15s" data-rm="${i}">&times;</button>
1092
1151
  </div>`).join('');
1093
1152
  el.querySelectorAll('input').forEach(inp => {
1094
1153
  inp.addEventListener('input', () => { webhookData[parseInt(inp.dataset.idx)].url = inp.value; });
1154
+ inp.addEventListener('focus', () => { inp.style.borderColor = 'var(--accent)'; });
1155
+ inp.addEventListener('blur', () => { inp.style.borderColor = 'var(--border)'; });
1156
+ });
1157
+ el.querySelectorAll('[data-rm]').forEach(btn => {
1158
+ btn.addEventListener('click', () => {
1159
+ webhookData.splice(parseInt(btn.dataset.rm), 1);
1160
+ renderWebhooks();
1161
+ });
1162
+ btn.addEventListener('mouseenter', () => { btn.style.color = 'var(--red)'; });
1163
+ btn.addEventListener('mouseleave', () => { btn.style.color = 'var(--text-dim)'; });
1095
1164
  });
1096
1165
  }
1097
1166