mcp-voice-hooks 1.0.26 → 1.0.28-beta.1
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/bin/cli.js +59 -1
- package/package.json +1 -1
- package/public/app.js +72 -3
- package/public/index.html +6 -0
package/bin/cli.js
CHANGED
@@ -54,6 +54,9 @@ async function configureClaudeCodeSettings() {
|
|
54
54
|
const settingsPath = path.join(claudeDir, 'settings.local.json');
|
55
55
|
// This was used in versions <= v1.0.21.
|
56
56
|
const oldSettingsPath = path.join(claudeDir, 'settings.json');
|
57
|
+
|
58
|
+
// Detect if running on Windows
|
59
|
+
const isWindows = process.platform === 'win32';
|
57
60
|
|
58
61
|
console.log('⚙️ Configuring project Claude Code settings...');
|
59
62
|
|
@@ -103,7 +106,62 @@ async function configureClaudeCodeSettings() {
|
|
103
106
|
}
|
104
107
|
|
105
108
|
// Add hook configuration with inline commands
|
106
|
-
|
109
|
+
// Windows requires different command syntax
|
110
|
+
const hookConfig = isWindows ? {
|
111
|
+
// Windows-compatible commands using PowerShell environment variable syntax
|
112
|
+
"Stop": [
|
113
|
+
{
|
114
|
+
"matcher": "",
|
115
|
+
"hooks": [
|
116
|
+
{
|
117
|
+
"type": "command",
|
118
|
+
"command": "powershell -Command \"$port = if ($env:MCP_VOICE_HOOKS_PORT) { $env:MCP_VOICE_HOOKS_PORT } else { '5111' }; try { Invoke-RestMethod -Uri \\\"http://localhost:$port/api/hooks/stop\\\" -Method POST } catch { Write-Output '{\\\"decision\\\": \\\"approve\\\", \\\"reason\\\": \\\"voice-hooks unavailable\\\"}' }\""
|
119
|
+
}
|
120
|
+
]
|
121
|
+
}
|
122
|
+
],
|
123
|
+
"PreToolUse": [
|
124
|
+
{
|
125
|
+
"matcher": "^(?!mcp__voice-hooks__).*",
|
126
|
+
"hooks": [
|
127
|
+
{
|
128
|
+
"type": "command",
|
129
|
+
"command": "powershell -Command \"$port = if ($env:MCP_VOICE_HOOKS_PORT) { $env:MCP_VOICE_HOOKS_PORT } else { '5111' }; try { Invoke-RestMethod -Uri \\\"http://localhost:$port/api/hooks/pre-tool\\\" -Method POST } catch { Write-Output '{\\\"decision\\\": \\\"approve\\\", \\\"reason\\\": \\\"voice-hooks unavailable\\\"}' }\""
|
130
|
+
}
|
131
|
+
]
|
132
|
+
},
|
133
|
+
{
|
134
|
+
"matcher": "^mcp__voice-hooks__speak$",
|
135
|
+
"hooks": [
|
136
|
+
{
|
137
|
+
"type": "command",
|
138
|
+
"command": "powershell -Command \"$port = if ($env:MCP_VOICE_HOOKS_PORT) { $env:MCP_VOICE_HOOKS_PORT } else { '5111' }; try { Invoke-RestMethod -Uri \\\"http://localhost:$port/api/hooks/pre-speak\\\" -Method POST } catch { Write-Output '{\\\"decision\\\": \\\"approve\\\", \\\"reason\\\": \\\"voice-hooks unavailable\\\"}' }\""
|
139
|
+
}
|
140
|
+
]
|
141
|
+
},
|
142
|
+
{
|
143
|
+
"matcher": "^mcp__voice-hooks__wait_for_utterance$",
|
144
|
+
"hooks": [
|
145
|
+
{
|
146
|
+
"type": "command",
|
147
|
+
"command": "powershell -Command \"$port = if ($env:MCP_VOICE_HOOKS_PORT) { $env:MCP_VOICE_HOOKS_PORT } else { '5111' }; try { Invoke-RestMethod -Uri \\\"http://localhost:$port/api/hooks/pre-wait\\\" -Method POST } catch { Write-Output '{\\\"decision\\\": \\\"approve\\\", \\\"reason\\\": \\\"voice-hooks unavailable\\\"}' }\""
|
148
|
+
}
|
149
|
+
]
|
150
|
+
}
|
151
|
+
],
|
152
|
+
"PostToolUse": [
|
153
|
+
{
|
154
|
+
"matcher": "^(?!mcp__voice-hooks__).*",
|
155
|
+
"hooks": [
|
156
|
+
{
|
157
|
+
"type": "command",
|
158
|
+
"command": "powershell -Command \"$port = if ($env:MCP_VOICE_HOOKS_PORT) { $env:MCP_VOICE_HOOKS_PORT } else { '5111' }; try { Invoke-RestMethod -Uri \\\"http://localhost:$port/api/hooks/post-tool\\\" -Method POST } catch { Write-Output '{}' }\""
|
159
|
+
}
|
160
|
+
]
|
161
|
+
}
|
162
|
+
]
|
163
|
+
} : {
|
164
|
+
// Unix/Mac commands using curl with shell environment variable syntax
|
107
165
|
"Stop": [
|
108
166
|
{
|
109
167
|
"matcher": "",
|
package/package.json
CHANGED
package/public/app.js
CHANGED
@@ -25,6 +25,7 @@ class VoiceHooksClient {
|
|
25
25
|
this.initializeTTSEvents();
|
26
26
|
|
27
27
|
// TTS controls
|
28
|
+
this.languageSelect = document.getElementById('languageSelect');
|
28
29
|
this.voiceSelect = document.getElementById('voiceSelect');
|
29
30
|
this.speechRateSlider = document.getElementById('speechRate');
|
30
31
|
this.speechRateInput = document.getElementById('speechRateInput');
|
@@ -123,6 +124,15 @@ class VoiceHooksClient {
|
|
123
124
|
this.clearAllBtn.addEventListener('click', () => this.clearAllUtterances());
|
124
125
|
this.listenBtn.addEventListener('click', () => this.toggleListening());
|
125
126
|
|
127
|
+
// Language filter
|
128
|
+
if (this.languageSelect) {
|
129
|
+
this.languageSelect.addEventListener('change', () => {
|
130
|
+
// Save language preference
|
131
|
+
localStorage.setItem('selectedLanguage', this.languageSelect.value);
|
132
|
+
// Repopulate voice list with filtered voices
|
133
|
+
this.populateVoiceList();
|
134
|
+
});
|
135
|
+
}
|
126
136
|
|
127
137
|
// TTS controls
|
128
138
|
this.voiceSelect.addEventListener('change', (e) => {
|
@@ -389,9 +399,49 @@ class VoiceHooksClient {
|
|
389
399
|
};
|
390
400
|
}
|
391
401
|
|
402
|
+
populateLanguageFilter() {
|
403
|
+
if (!this.languageSelect || !this.voices) return;
|
404
|
+
|
405
|
+
// Get current selection
|
406
|
+
const currentSelection = this.languageSelect.value || 'en-US';
|
407
|
+
|
408
|
+
// Clear existing options
|
409
|
+
this.languageSelect.innerHTML = '';
|
410
|
+
|
411
|
+
// Add "All Languages" option
|
412
|
+
const allOption = document.createElement('option');
|
413
|
+
allOption.value = 'all';
|
414
|
+
allOption.textContent = 'All Languages';
|
415
|
+
this.languageSelect.appendChild(allOption);
|
416
|
+
|
417
|
+
// Collect unique language codes
|
418
|
+
const languageCodes = new Set();
|
419
|
+
this.voices.forEach(voice => {
|
420
|
+
languageCodes.add(voice.lang);
|
421
|
+
});
|
422
|
+
|
423
|
+
// Sort and add language codes
|
424
|
+
Array.from(languageCodes).sort().forEach(lang => {
|
425
|
+
const option = document.createElement('option');
|
426
|
+
option.value = lang;
|
427
|
+
option.textContent = lang;
|
428
|
+
this.languageSelect.appendChild(option);
|
429
|
+
});
|
430
|
+
|
431
|
+
// Restore selection
|
432
|
+
this.languageSelect.value = currentSelection;
|
433
|
+
if (this.languageSelect.value !== currentSelection) {
|
434
|
+
// If saved selection not available, default to en-US
|
435
|
+
this.languageSelect.value = 'en-US';
|
436
|
+
}
|
437
|
+
}
|
438
|
+
|
392
439
|
populateVoiceList() {
|
393
440
|
if (!this.voiceSelect || !this.localVoicesGroup || !this.cloudVoicesGroup) return;
|
394
441
|
|
442
|
+
// First populate the language filter
|
443
|
+
this.populateLanguageFilter();
|
444
|
+
|
395
445
|
// Clear existing browser voice options
|
396
446
|
this.localVoicesGroup.innerHTML = '';
|
397
447
|
this.cloudVoicesGroup.innerHTML = '';
|
@@ -408,10 +458,23 @@ class VoiceHooksClient {
|
|
408
458
|
'Fred', 'Junior', 'Kathy', 'Ralph'
|
409
459
|
];
|
410
460
|
|
411
|
-
//
|
461
|
+
// Get selected language filter
|
462
|
+
const selectedLanguage = this.languageSelect ? this.languageSelect.value : 'en-US';
|
463
|
+
|
464
|
+
// Filter voices based on selected language
|
412
465
|
this.voices.forEach((voice, index) => {
|
413
|
-
|
414
|
-
|
466
|
+
const voiceLang = voice.lang;
|
467
|
+
let shouldInclude = false;
|
468
|
+
|
469
|
+
if (selectedLanguage === 'all') {
|
470
|
+
// Include all languages
|
471
|
+
shouldInclude = true;
|
472
|
+
} else {
|
473
|
+
// Check if voice matches selected language/locale
|
474
|
+
shouldInclude = voiceLang === selectedLanguage;
|
475
|
+
}
|
476
|
+
|
477
|
+
if (shouldInclude) {
|
415
478
|
// Check if voice should be excluded
|
416
479
|
const voiceName = voice.name;
|
417
480
|
const isExcluded = excludedVoices.some(excluded =>
|
@@ -588,6 +651,12 @@ class VoiceHooksClient {
|
|
588
651
|
// Load selected voice (will be applied after voices load)
|
589
652
|
this.selectedVoice = localStorage.getItem('selectedVoice') || 'system';
|
590
653
|
|
654
|
+
// Load selected language
|
655
|
+
const savedLanguage = localStorage.getItem('selectedLanguage');
|
656
|
+
if (savedLanguage && this.languageSelect) {
|
657
|
+
this.languageSelect.value = savedLanguage;
|
658
|
+
}
|
659
|
+
|
591
660
|
// Update UI visibility
|
592
661
|
this.updateVoiceOptionsVisibility();
|
593
662
|
|
package/public/index.html
CHANGED
@@ -442,6 +442,12 @@
|
|
442
442
|
</label>
|
443
443
|
</div>
|
444
444
|
<div class="tts-controls" id="voiceOptions" style="display: none;">
|
445
|
+
<div class="tts-control">
|
446
|
+
<label for="languageSelect">Language:</label>
|
447
|
+
<select id="languageSelect">
|
448
|
+
<option value="en-US">Loading languages...</option>
|
449
|
+
</select>
|
450
|
+
</div>
|
445
451
|
<div class="tts-control">
|
446
452
|
<label for="voiceSelect">Voice:</label>
|
447
453
|
<select id="voiceSelect">
|