mcp-voice-hooks 1.0.8 → 1.0.13

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/public/index.html CHANGED
@@ -1,9 +1,10 @@
1
1
  <!DOCTYPE html>
2
2
  <html lang="en">
3
+
3
4
  <head>
4
5
  <meta charset="UTF-8">
5
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Voice Hooks - MCP POC</title>
7
+ <title>Voice Mode for Claude Code</title>
7
8
  <style>
8
9
  body {
9
10
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
@@ -12,34 +13,34 @@
12
13
  padding: 20px;
13
14
  background-color: #f5f5f5;
14
15
  }
15
-
16
+
16
17
  .container {
17
18
  background: white;
18
19
  border-radius: 12px;
19
20
  padding: 24px;
20
- box-shadow: 0 2px 12px rgba(0,0,0,0.1);
21
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
21
22
  }
22
-
23
+
23
24
  h1 {
24
25
  color: #333;
25
26
  margin-bottom: 8px;
26
27
  }
27
-
28
+
28
29
  .subtitle {
29
30
  color: #666;
30
31
  margin-bottom: 24px;
31
32
  }
32
-
33
+
33
34
  .input-section {
34
35
  margin-bottom: 24px;
35
36
  }
36
-
37
+
37
38
  .input-group {
38
39
  display: flex;
39
40
  gap: 12px;
40
41
  margin-bottom: 16px;
41
42
  }
42
-
43
+
43
44
  #utteranceInput {
44
45
  flex: 1;
45
46
  padding: 12px;
@@ -47,12 +48,12 @@
47
48
  border-radius: 8px;
48
49
  font-size: 16px;
49
50
  }
50
-
51
+
51
52
  #utteranceInput:focus {
52
53
  outline: none;
53
54
  border-color: #007AFF;
54
55
  }
55
-
56
+
56
57
  #sendBtn {
57
58
  background: #007AFF;
58
59
  color: white;
@@ -62,22 +63,22 @@
62
63
  font-size: 16px;
63
64
  cursor: pointer;
64
65
  }
65
-
66
+
66
67
  #sendBtn:hover {
67
68
  background: #0056CC;
68
69
  }
69
-
70
+
70
71
  #sendBtn:disabled {
71
72
  background: #ccc;
72
73
  cursor: not-allowed;
73
74
  }
74
-
75
+
75
76
  .status {
76
77
  display: flex;
77
78
  gap: 24px;
78
79
  margin-bottom: 24px;
79
80
  }
80
-
81
+
81
82
  .status-item {
82
83
  padding: 12px;
83
84
  background: #f8f9fa;
@@ -85,30 +86,30 @@
85
86
  text-align: center;
86
87
  flex: 1;
87
88
  }
88
-
89
+
89
90
  .status-number {
90
91
  font-size: 24px;
91
92
  font-weight: bold;
92
93
  color: #007AFF;
93
94
  }
94
-
95
+
95
96
  .status-label {
96
97
  font-size: 14px;
97
98
  color: #666;
98
99
  }
99
-
100
+
100
101
  .utterances-section h3 {
101
102
  color: #333;
102
103
  margin-bottom: 16px;
103
104
  }
104
-
105
+
105
106
  .utterances-list {
106
107
  max-height: 400px;
107
108
  overflow-y: auto;
108
109
  border: 1px solid #ddd;
109
110
  border-radius: 8px;
110
111
  }
111
-
112
+
112
113
  .utterance-item {
113
114
  padding: 12px 16px;
114
115
  border-bottom: 1px solid #eee;
@@ -116,22 +117,22 @@
116
117
  justify-content: space-between;
117
118
  align-items: center;
118
119
  }
119
-
120
+
120
121
  .utterance-item:last-child {
121
122
  border-bottom: none;
122
123
  }
123
-
124
+
124
125
  .utterance-text {
125
126
  flex: 1;
126
127
  margin-right: 12px;
127
128
  }
128
-
129
+
129
130
  .utterance-meta {
130
131
  font-size: 12px;
131
132
  color: #666;
132
133
  text-align: right;
133
134
  }
134
-
135
+
135
136
  .utterance-status {
136
137
  display: inline-block;
137
138
  padding: 2px 8px;
@@ -140,17 +141,17 @@
140
141
  font-weight: bold;
141
142
  margin-top: 4px;
142
143
  }
143
-
144
+
144
145
  .status-pending {
145
146
  background: #FFF3CD;
146
147
  color: #856404;
147
148
  }
148
-
149
+
149
150
  .status-delivered {
150
151
  background: #D1ECF1;
151
152
  color: #0C5460;
152
153
  }
153
-
154
+
154
155
  .refresh-btn {
155
156
  background: #6C757D;
156
157
  color: white;
@@ -161,25 +162,25 @@
161
162
  cursor: pointer;
162
163
  margin-left: 12px;
163
164
  }
164
-
165
+
165
166
  .refresh-btn:hover {
166
167
  background: #545B62;
167
168
  }
168
-
169
+
169
170
  .empty-state {
170
171
  text-align: center;
171
172
  color: #666;
172
173
  padding: 40px 20px;
173
174
  font-style: italic;
174
175
  }
175
-
176
+
176
177
  .voice-controls {
177
178
  display: flex;
178
179
  gap: 12px;
179
180
  align-items: center;
180
181
  margin-bottom: 16px;
181
182
  }
182
-
183
+
183
184
  #listenBtn {
184
185
  background: #28A745;
185
186
  color: white;
@@ -192,24 +193,24 @@
192
193
  align-items: center;
193
194
  gap: 8px;
194
195
  }
195
-
196
+
196
197
  #listenBtn:hover {
197
198
  background: #218838;
198
199
  }
199
-
200
+
200
201
  #listenBtn.listening {
201
202
  background: #DC3545;
202
203
  }
203
-
204
+
204
205
  #listenBtn.listening:hover {
205
206
  background: #C82333;
206
207
  }
207
-
208
+
208
209
  #listenBtn:disabled {
209
210
  background: #ccc;
210
211
  cursor: not-allowed;
211
212
  }
212
-
213
+
213
214
  .listening-indicator {
214
215
  display: none;
215
216
  align-items: center;
@@ -217,11 +218,11 @@
217
218
  color: #DC3545;
218
219
  font-weight: 500;
219
220
  }
220
-
221
+
221
222
  .listening-indicator.active {
222
223
  display: flex;
223
224
  }
224
-
225
+
225
226
  .listening-dot {
226
227
  width: 8px;
227
228
  height: 8px;
@@ -229,13 +230,21 @@
229
230
  border-radius: 50%;
230
231
  animation: pulse 1.5s infinite;
231
232
  }
232
-
233
+
233
234
  @keyframes pulse {
234
- 0% { opacity: 1; }
235
- 50% { opacity: 0.3; }
236
- 100% { opacity: 1; }
237
- }
238
-
235
+ 0% {
236
+ opacity: 1;
237
+ }
238
+
239
+ 50% {
240
+ opacity: 0.3;
241
+ }
242
+
243
+ 100% {
244
+ opacity: 1;
245
+ }
246
+ }
247
+
239
248
  .interim-text {
240
249
  display: none;
241
250
  padding: 12px;
@@ -246,28 +255,213 @@
246
255
  font-style: italic;
247
256
  color: #6C757D;
248
257
  }
249
-
258
+
250
259
  .interim-text.active {
251
260
  display: block;
252
261
  }
253
-
262
+
254
263
  .mic-icon {
255
264
  width: 16px;
256
265
  height: 16px;
257
266
  fill: currentColor;
258
267
  }
268
+
269
+ .tts-settings {
270
+ background: #f8f9fa;
271
+ padding: 12px;
272
+ border-radius: 8px;
273
+ margin-bottom: 24px;
274
+ }
275
+
276
+ .tts-settings h4 {
277
+ margin-top: 0;
278
+ margin-bottom: 0;
279
+ color: #333;
280
+ }
281
+
282
+ .tts-controls {
283
+ display: flex;
284
+ gap: 16px;
285
+ align-items: center;
286
+ flex-wrap: wrap;
287
+ }
288
+
289
+ .tts-control {
290
+ display: flex;
291
+ align-items: center;
292
+ gap: 8px;
293
+ }
294
+
295
+ .tts-control label {
296
+ font-size: 14px;
297
+ color: #666;
298
+ }
299
+
300
+ .tts-control select,
301
+ .tts-control input {
302
+ padding: 6px;
303
+ border: 1px solid #ddd;
304
+ border-radius: 4px;
305
+ font-size: 14px;
306
+ }
307
+
308
+ .tts-test-btn {
309
+ background: #6C757D;
310
+ color: white;
311
+ border: none;
312
+ padding: 6px 12px;
313
+ border-radius: 4px;
314
+ font-size: 14px;
315
+ cursor: pointer;
316
+ }
317
+
318
+ .tts-test-btn:hover {
319
+ background: #545B62;
320
+ }
321
+
322
+ .rate-warning,
323
+ .system-voice-info {
324
+ font-size: 12px;
325
+ padding: 8px 12px;
326
+ border-radius: 6px;
327
+ margin: 8px 0;
328
+ display: flex;
329
+ align-items: center;
330
+ gap: 8px;
331
+ }
332
+
333
+ .rate-warning {
334
+ background: #fff3cd;
335
+ color: #856404;
336
+ border: 1px solid #ffeaa7;
337
+ }
338
+
339
+ .system-voice-info {
340
+ background: #d1ecf1;
341
+ color: #0c5460;
342
+ border: 1px solid #bee5eb;
343
+ }
344
+
345
+ .system-voice-info a {
346
+ color: #0c5460;
347
+ font-weight: bold;
348
+ }
349
+
350
+ .warning-icon,
351
+ .info-icon {
352
+ font-size: 16px;
353
+ }
354
+
355
+ .info-message {
356
+ background: #d1ecf1;
357
+ border: 1px solid #bee5eb;
358
+ border-radius: 8px;
359
+ margin-bottom: 16px;
360
+ }
361
+
362
+ .info-message .empty-state {
363
+ color: #0c5460;
364
+ padding: 20px;
365
+ font-style: normal;
366
+ }
367
+
368
+ /* Toggle switch styles */
369
+ .switch {
370
+ position: relative;
371
+ display: inline-block;
372
+ width: 50px;
373
+ height: 26px;
374
+ }
375
+
376
+ .switch input {
377
+ opacity: 0;
378
+ width: 0;
379
+ height: 0;
380
+ }
381
+
382
+ .slider {
383
+ position: absolute;
384
+ cursor: pointer;
385
+ top: 0;
386
+ left: 0;
387
+ right: 0;
388
+ bottom: 0;
389
+ background-color: #ccc;
390
+ transition: .3s;
391
+ border-radius: 26px;
392
+ }
393
+
394
+ .slider:before {
395
+ position: absolute;
396
+ content: "";
397
+ height: 18px;
398
+ width: 18px;
399
+ left: 4px;
400
+ bottom: 4px;
401
+ background-color: white;
402
+ transition: .3s;
403
+ border-radius: 50%;
404
+ }
405
+
406
+ input:checked+.slider {
407
+ background-color: #007AFF;
408
+ }
409
+
410
+ input:checked+.slider:before {
411
+ transform: translateX(24px);
412
+ }
259
413
  </style>
260
414
  </head>
415
+
261
416
  <body>
262
417
  <div class="container">
263
- <h1>Voice Hooks - MCP POC</h1>
264
- <p class="subtitle">Speak or type utterances to test the MCP voice interaction system</p>
265
-
418
+ <h1>Voice Mode for Claude Code</h1>
419
+ <p class="subtitle">Speak or type to enqueue messages for Claude</p>
420
+
421
+ <div class="tts-settings">
422
+ <div style="display: flex; justify-content: space-between; align-items: center;">
423
+ <h4>Allow Claude to speak back to you</h4>
424
+ <label class="switch">
425
+ <input type="checkbox" id="voiceResponsesToggle">
426
+ <span class="slider"></span>
427
+ </label>
428
+ </div>
429
+ <div class="tts-controls" id="voiceOptions" style="display: none;">
430
+ <div class="tts-control" style="width: 100%;">
431
+ <label for="voiceSelect">Voice:</label>
432
+ <select id="voiceSelect">
433
+ <option value="system">Mac System Voice</option>
434
+ <optgroup label="Browser Voices (Cloud)" id="cloudVoicesGroup">
435
+ </optgroup>
436
+ <optgroup label="Browser Voices (Local)" id="localVoicesGroup">
437
+ </optgroup>
438
+ </select>
439
+ </div>
440
+ <div id="systemVoiceInfo" class="system-voice-info" style="display: none;">
441
+ <span class="info-icon">ℹ️</span>
442
+ <span class="info-text">You can download high quality voices in your Mac settings app. See the <a
443
+ href="https://github.com/johnmatthewtennant/mcp-voice-hooks?tab=readme-ov-file#voice-responses"
444
+ target="_blank">README</a></span>
445
+ </div>
446
+ <div id="rateWarning" class="rate-warning" style="display: none;">
447
+ <span class="warning-icon">⚠️</span>
448
+ <span class="warning-text">Google voices may not respond well to rate adjustments</span>
449
+ </div>
450
+ <div class="tts-control" style="width: 100%;">
451
+ <label for="speechRate">Speaking Rate:</label>
452
+ <input type="range" id="speechRate" min="0.5" max="5" step="0.1" value="1">
453
+ <input type="number" id="speechRateInput" min="0.5" max="5" step="0.1" value="1">
454
+ </div>
455
+ <button class="tts-test-btn" id="testTTSBtn">Test Voice</button>
456
+ </div>
457
+ </div>
458
+
266
459
  <div class="input-section">
267
460
  <div class="voice-controls">
268
461
  <button id="listenBtn">
269
462
  <svg class="mic-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
270
- <path d="M12 1C10.34 1 9 2.34 9 4V12C9 13.66 10.34 15 12 15C13.66 15 15 13.66 15 12V4C15 2.34 13.66 1 12 1ZM19 12C19 15.53 16.39 18.44 13 18.93V22H11V18.93C7.61 18.44 5 15.53 5 12H7C7 14.76 9.24 17 12 17C14.76 17 17 14.76 17 12H19Z"/>
463
+ <path
464
+ d="M12 1C10.34 1 9 2.34 9 4V12C9 13.66 10.34 15 12 15C13.66 15 15 13.66 15 12V4C15 2.34 13.66 1 12 1ZM19 12C19 15.53 16.39 18.44 13 18.93V22H11V18.93C7.61 18.44 5 15.53 5 12H7C7 14.76 9.24 17 12 17C14.76 17 17 14.76 17 12H19Z" />
271
465
  </svg>
272
466
  <span id="listenBtnText">Start Listening</span>
273
467
  </button>
@@ -276,20 +470,15 @@
276
470
  <span>Listening...</span>
277
471
  </div>
278
472
  </div>
279
-
473
+
280
474
  <div class="interim-text" id="interimText"></div>
281
-
475
+
282
476
  <div class="input-group">
283
- <input
284
- type="text"
285
- id="utteranceInput"
286
- placeholder="Type your utterance here..."
287
- autofocus
288
- >
477
+ <input type="text" id="utteranceInput" placeholder="Type your utterance here..." autofocus>
289
478
  <button id="sendBtn">Send</button>
290
479
  </div>
291
480
  </div>
292
-
481
+
293
482
  <div class="status">
294
483
  <div class="status-item">
295
484
  <div class="status-number" id="totalCount">0</div>
@@ -304,19 +493,24 @@
304
493
  <div class="status-label">Delivered</div>
305
494
  </div>
306
495
  </div>
307
-
496
+
308
497
  <div class="utterances-section">
309
498
  <h3>
310
499
  Recent Utterances
311
500
  <button class="refresh-btn" id="refreshBtn">Refresh</button>
312
- <button class="refresh-btn" id="clearAllBtn" style="background: #DC3545; margin-left: 8px;">Clear All</button>
501
+ <button class="refresh-btn" id="clearAllBtn" style="background: #DC3545; margin-left: 8px;">Clear
502
+ All</button>
313
503
  </h3>
504
+ <div class="info-message" id="infoMessage" style="display: none;">
505
+ <div class="empty-state">You need to send one message in the Claude code CLI to start voice interaction</div>
506
+ </div>
314
507
  <div class="utterances-list" id="utterancesList">
315
508
  <div class="empty-state">No utterances yet. Type something above to get started!</div>
316
509
  </div>
317
510
  </div>
318
511
  </div>
319
-
512
+
320
513
  <script src="app.js"></script>
321
514
  </body>
515
+
322
516
  </html>
@@ -1,3 +0,0 @@
1
- #!/bin/bash
2
- PORT="${MCP_VOICE_HOOKS_PORT:-5111}"
3
- curl -s -X POST http://localhost:${PORT}/api/hooks/pre-speak || echo '{"decision": "approve"}'
@@ -1,3 +0,0 @@
1
- #!/bin/bash
2
- PORT="${MCP_VOICE_HOOKS_PORT:-5111}"
3
- curl -s -X POST http://localhost:${PORT}/api/hooks/pre-tool || echo '{"decision": "approve"}'
@@ -1,3 +0,0 @@
1
- #!/bin/bash
2
- PORT="${MCP_VOICE_HOOKS_PORT:-5111}"
3
- curl -s -X POST http://localhost:${PORT}/api/hooks/pre-wait || echo '{"decision": "approve"}'
@@ -1,3 +0,0 @@
1
- #!/bin/bash
2
- PORT="${MCP_VOICE_HOOKS_PORT:-5111}"
3
- curl -s -X POST http://localhost:${PORT}/api/hooks/stop || echo '{"decision": "approve"}'
package/CLAUDE.local.md DELETED
@@ -1,25 +0,0 @@
1
- # To publish to npm
2
-
3
- ```bash
4
- # 1. Build the project first
5
- npm run build
6
-
7
- # 2. Update roadmap.md with version info and stage it
8
- git add roadmap.md
9
-
10
- # 3. Bump version (patch, minor, or major) - creates a commit and tag
11
- npm version patch --registry https://registry.npmjs.org/
12
-
13
- # Alternative: Bump version without creating a commit/tag
14
- # npm version patch --no-git-tag-version
15
- # Then manually commit the changes
16
-
17
- # 4. Publish to npm (this creates the .tgz file automatically)
18
- npm publish --registry https://registry.npmjs.org/
19
-
20
- # Note: It can take 1-5 minutes for the package to be available globally
21
- # Check availability at: https://www.npmjs.com/package/mcp-voice-hooks
22
-
23
- # Optional: Create .tgz file without publishing
24
- npm pack
25
- ```
@@ -1,12 +0,0 @@
1
- // src/debug.ts
2
- var DEBUG = process.env.DEBUG === "true" || process.env.VOICE_HOOKS_DEBUG === "true";
3
- function debugLog(...args) {
4
- if (DEBUG) {
5
- console.log(...args);
6
- }
7
- }
8
-
9
- export {
10
- debugLog
11
- };
12
- //# sourceMappingURL=chunk-IYGM5COW.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/debug.ts"],"sourcesContent":["const DEBUG = process.env.DEBUG === 'true' || process.env.VOICE_HOOKS_DEBUG === 'true';\n\nexport function debugLog(...args: any[]): void {\n if (DEBUG) {\n console.log(...args);\n }\n}"],"mappings":";AAAA,IAAM,QAAQ,QAAQ,IAAI,UAAU,UAAU,QAAQ,IAAI,sBAAsB;AAEzE,SAAS,YAAY,MAAmB;AAC7C,MAAI,OAAO;AACT,YAAQ,IAAI,GAAG,IAAI;AAAA,EACrB;AACF;","names":[]}
@@ -1,2 +0,0 @@
1
-
2
- export { }