Skip to content

Commit b16e37e

Browse files
committed
Remove Gemini API test button and Whisper mic test
- Remove Test connection button from onboarding API key screen - Remove Whisper 5-second mic test panel and state from model-download screen - Remove unused test-whisper-recording IPC handler, preload bridge, and speech.service method - Keep model-download choice (now/later) and actual turbo weight download intact - Downloaded turbo model is used for live speech without requiring user verification
1 parent e5305a3 commit b16e37e

5 files changed

Lines changed: 11 additions & 204 deletions

File tree

main.js

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -379,15 +379,6 @@ class ApplicationController {
379379
return speechService.isAvailable ? speechService.isAvailable() : false;
380380
});
381381

382-
ipcMain.handle("test-whisper-recording", async () => {
383-
try {
384-
const result = await speechService.testWhisperRecording(5000);
385-
return result;
386-
} catch (error) {
387-
return { ok: false, error: error.message };
388-
}
389-
});
390-
391382
ipcMain.handle("start-speech-recognition", () => {
392383
speechService.startRecording();
393384
return speechService.getStatus();

onboarding.html

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -774,11 +774,6 @@ <h1>Connect Google Gemini</h1>
774774
</div>
775775
</div>
776776

777-
<div class="field">
778-
<button class="btn" id="testKeyBtn" type="button">
779-
<i class="fas fa-plug"></i> Test connection
780-
</button>
781-
</div>
782777
</section>
783778

784779
<!-- SCREEN 3: Speech -->
@@ -909,21 +904,6 @@ <h1>Whisper Model Download</h1>
909904
</div>
910905

911906
<div class="install-log" id="modelDownloadLog"></div>
912-
913-
<!-- Whisper test panel (shown after model is downloaded) -->
914-
<div class="install-card" id="whisperTestCard" style="display:none; margin-top: 14px;">
915-
<div class="install-title">
916-
<i class="fas fa-microphone"></i>
917-
<span>Test Whisper</span>
918-
</div>
919-
<p style="font-size: 12px; color: var(--text-dim); margin-bottom: 12px;">
920-
Click the button below and speak a short sentence to confirm the microphone and Whisper model are working.
921-
</p>
922-
<button class="btn primary" id="testWhisperBtn" type="button">
923-
<i class="fas fa-microphone-lines"></i> Record 5-second test
924-
</button>
925-
<div id="whisperTestResult" style="margin-top: 12px; font-size: 12px; color: var(--text-dim);"></div>
926-
</div>
927907
</section>
928908

929909
<!-- SCREEN 6: Finish (star prompt + summary) -->

onboarding.js

Lines changed: 11 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,6 @@
4040
modelDownloadChoice: null, // 'now' | 'later'
4141
modelDownloading: false,
4242
modelDownloaded: false,
43-
whisperTestPassed: false,
44-
whisperTesting: false,
4543
finished: false,
4644
};
4745

@@ -76,12 +74,8 @@
7674
}
7775
refreshStepper();
7876
backBtn.style.visibility = state.step === 0 ? 'hidden' : 'visible';
79-
// Reset next button state unless we're actively downloading a model or awaiting a test
80-
const awaitingTest = name === 'model-download' &&
81-
state.modelDownloadChoice === 'now' &&
82-
state.modelDownloaded &&
83-
!state.whisperTestPassed;
84-
if (name !== 'model-download' || (!state.modelDownloading && !state.whisperTesting && !awaitingTest)) {
77+
// Reset next button state unless we're actively downloading a model
78+
if (name !== 'model-download' || !state.modelDownloading) {
8579
nextBtn.disabled = false;
8680
nextBtn.classList.remove('success');
8781
nextBtn.classList.add('primary');
@@ -138,10 +132,7 @@
138132
// Allow advancing whether whisper is detected OR user skipped
139133
return state.whisperDetected || state.skippingWhisper;
140134
case 'model-download':
141-
if (!state.modelDownloadChoice || state.modelDownloading) return false;
142-
// If user chose to download now, require a successful mic test before continuing.
143-
if (state.modelDownloadChoice === 'now' && state.modelDownloaded && !state.whisperTestPassed) return false;
144-
return true;
135+
return !!state.modelDownloadChoice && !state.modelDownloading;
145136
case 'finish':
146137
return true;
147138
default:
@@ -153,7 +144,6 @@
153144
const geminiInput = $('#geminiKey');
154145
const toggleVis = $('#toggleVis');
155146
const keyStatus = $('#keyStatus');
156-
const testKeyBtn = $('#testKeyBtn');
157147

158148
function setKeyStatus(state_, text) {
159149
keyStatus.className = `status-pill ${state_}`;
@@ -179,7 +169,7 @@
179169
} else if (keyStatus.classList.contains('success')) {
180170
// Keep success state — they had a valid key, may be editing
181171
} else {
182-
setKeyStatus('idle', 'Key entered — click Test connection');
172+
setKeyStatus('idle', 'Key entered');
183173
}
184174
});
185175

@@ -191,37 +181,6 @@
191181
: '<i class="fas fa-eye-slash"></i>';
192182
});
193183

194-
testKeyBtn.addEventListener('click', async () => {
195-
const key = geminiInput.value.trim();
196-
if (!key) {
197-
setKeyStatus('error', 'Enter a key first');
198-
return;
199-
}
200-
if (!window.electronAPI) {
201-
setKeyStatus('error', 'Bridge unavailable');
202-
return;
203-
}
204-
testKeyBtn.disabled = true;
205-
testKeyBtn.innerHTML = '<span class="spinner"></span> Testing…';
206-
setKeyStatus('testing', 'Testing connection to Google Gemini…');
207-
try {
208-
// saveSettings writes the key into .env (atomic via persistEnvUpdates).
209-
await window.electronAPI.saveSettings({ geminiKey: key });
210-
const result = await window.electronAPI.testGeminiConnection();
211-
if (result && result.success) {
212-
state.geminiKey = key;
213-
setKeyStatus('success', `Connected — ${result.model || 'Gemini ready'}`);
214-
} else {
215-
setKeyStatus('error', (result && result.error) || 'Connection failed');
216-
}
217-
} catch (e) {
218-
setKeyStatus('error', `Error: ${e.message || e}`);
219-
} finally {
220-
testKeyBtn.disabled = false;
221-
testKeyBtn.innerHTML = '<i class="fas fa-plug"></i> Test connection';
222-
}
223-
});
224-
225184
// ── Wire up: Speech choices ───────────────────────────────────────
226185
$$('#speechChoices .choice-card').forEach((card) => {
227186
card.addEventListener('click', () => {
@@ -418,43 +377,20 @@
418377
// Start downloading the model immediately
419378
startModelDownload();
420379
} else {
421-
// 'later' doesn't need a test
422380
nextBtn.disabled = false;
423381
}
424382
});
425383
});
426-
427-
// Wire up the Whisper test button
428-
const testBtn = document.getElementById('testWhisperBtn');
429-
if (testBtn) {
430-
testBtn.addEventListener('click', runWhisperTest);
431-
}
432384
}
433385

434386
// Restore selection state when navigating back
435387
$$('#modelDownloadChoices .choice-card').forEach((card) => {
436388
card.classList.toggle('selected', card.dataset.value === state.modelDownloadChoice);
437389
});
438390

439-
// Restore test panel visibility and state
440-
const testCard = document.getElementById('whisperTestCard');
441-
if (testCard && state.modelDownloadChoice === 'now' && state.modelDownloaded) {
442-
testCard.style.display = 'block';
443-
}
444-
const testBtn = document.getElementById('testWhisperBtn');
445-
if (testBtn && state.whisperTestPassed) {
446-
testBtn.innerHTML = '<i class="fas fa-check-circle"></i> Test passed';
447-
testBtn.classList.remove('primary');
448-
testBtn.classList.add('success');
449-
}
450-
451-
// Re-enable continue button if a choice has been made and (if applicable) test passed
452-
if (state.modelDownloadChoice && !state.modelDownloading && !state.whisperTesting) {
453-
if (state.modelDownloadChoice === 'now' && state.modelDownloaded && !state.whisperTestPassed) {
454-
nextBtn.disabled = true;
455-
} else {
456-
nextBtn.disabled = false;
457-
}
391+
// Re-enable continue button if a choice has been made and not actively downloading
392+
if (state.modelDownloadChoice && !state.modelDownloading) {
393+
nextBtn.disabled = false;
458394
}
459395
}
460396

@@ -477,11 +413,10 @@
477413
if (r.ok) {
478414
state.modelDownloaded = true;
479415
appendModelLog(`\n✓ Model downloaded successfully: ${r.path}`);
480-
// Show the Whisper mic test panel
481-
const testCard = document.getElementById('whisperTestCard');
482-
if (testCard) testCard.style.display = 'block';
483-
// Keep Continue disabled until test passes
484-
nextBtn.disabled = true;
416+
nextBtn.disabled = false;
417+
nextBtn.classList.remove('primary');
418+
nextBtn.classList.add('success');
419+
nextBtn.innerHTML = '<i class="fas fa-check-circle"></i> Continue';
485420
} else {
486421
appendModelLog(`\n✗ Download failed: ${r.message}`);
487422
// Let user continue anyway; they'll download on first use
@@ -498,48 +433,6 @@
498433
}
499434
}
500435

501-
async function runWhisperTest() {
502-
const btn = document.getElementById('testWhisperBtn');
503-
const resultEl = document.getElementById('whisperTestResult');
504-
if (!btn || !window.electronAPI || !window.electronAPI.testWhisperRecording) return;
505-
506-
state.whisperTesting = true;
507-
btn.disabled = true;
508-
btn.innerHTML = '<span class="spinner"></span> Listening…';
509-
if (resultEl) resultEl.textContent = 'Speak now…';
510-
511-
try {
512-
const r = await window.electronAPI.testWhisperRecording();
513-
if (r.ok) {
514-
state.whisperTestPassed = true;
515-
if (resultEl) resultEl.innerHTML = `<span style="color: var(--success);">✓ Heard:</span> “${escapeHtml(r.text)}”`;
516-
btn.innerHTML = '<i class="fas fa-check-circle"></i> Test passed';
517-
btn.classList.remove('primary');
518-
btn.classList.add('success');
519-
nextBtn.disabled = false;
520-
nextBtn.classList.remove('primary');
521-
nextBtn.classList.add('success');
522-
nextBtn.innerHTML = '<i class="fas fa-check-circle"></i> Continue';
523-
} else {
524-
if (resultEl) resultEl.innerHTML = `<span style="color: var(--error);">✗ ${escapeHtml(r.error || 'Test failed')}</span>`;
525-
btn.disabled = false;
526-
btn.innerHTML = '<i class="fas fa-redo"></i> Retry test';
527-
}
528-
} catch (e) {
529-
if (resultEl) resultEl.innerHTML = `<span style="color: var(--error);">✗ ${escapeHtml(e.message || e)}</span>`;
530-
btn.disabled = false;
531-
btn.innerHTML = '<i class="fas fa-redo"></i> Retry test';
532-
} finally {
533-
state.whisperTesting = false;
534-
}
535-
}
536-
537-
function escapeHtml(text) {
538-
const div = document.createElement('div');
539-
div.textContent = text;
540-
return div.innerHTML;
541-
}
542-
543436
// ── Wire up: Finish screen ────────────────────────────────────────
544437
function populateSummary() {
545438
const rows = [];

preload.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ contextBridge.exposeInMainWorld('electronAPI', {
4949
detectWhisper: () => ipcRenderer.invoke('detect-whisper'),
5050
installWhisper: () => ipcRenderer.invoke('install-whisper'),
5151
downloadWhisperModel: (modelName) => ipcRenderer.invoke('download-whisper-model', modelName),
52-
testWhisperRecording: () => ipcRenderer.invoke('test-whisper-recording'),
5352
onInstallProgress: (callback) => {
5453
const wrapped = (_event, line) => {
5554
try { callback(line); } catch (e) { console.error('onInstallProgress error:', e); }

src/services/speech.service.js

Lines changed: 0 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -818,62 +818,6 @@ class SpeechService extends EventEmitter {
818818
this._audioDataLogged = false;
819819
}
820820

821-
/**
822-
* Record a short audio clip and return the transcription.
823-
* Used by the onboarding wizard to verify the Whisper model works.
824-
*/
825-
async testWhisperRecording(durationMs = 5000) {
826-
return new Promise((resolve, reject) => {
827-
if (!this.isAvailable()) {
828-
reject(new Error('Speech recognition is not available'));
829-
return;
830-
}
831-
832-
let timeoutId = null;
833-
let transcriptionReceived = false;
834-
835-
const onTranscription = (text) => {
836-
if (transcriptionReceived) return;
837-
transcriptionReceived = true;
838-
cleanup();
839-
resolve({ ok: true, text: text.trim() });
840-
};
841-
842-
const onError = (error) => {
843-
if (transcriptionReceived) return;
844-
cleanup();
845-
reject(new Error(typeof error === 'string' ? error : error?.message || 'Speech test failed'));
846-
};
847-
848-
const cleanup = () => {
849-
if (timeoutId) clearTimeout(timeoutId);
850-
this.off('transcription', onTranscription);
851-
this.off('error', onError);
852-
};
853-
854-
this.once('transcription', onTranscription);
855-
this.once('error', onError);
856-
857-
try {
858-
this.startRecording();
859-
timeoutId = setTimeout(() => {
860-
if (transcriptionReceived) return;
861-
this.stopRecording();
862-
// Give Whisper a short window to emit transcription after stop
863-
setTimeout(() => {
864-
if (!transcriptionReceived) {
865-
cleanup();
866-
reject(new Error('No transcription received. Speak louder or check your microphone.'));
867-
}
868-
}, 3000);
869-
}, durationMs);
870-
} catch (error) {
871-
cleanup();
872-
reject(error);
873-
}
874-
});
875-
}
876-
877821
async recognizeFromFile(audioFilePath) {
878822
if (this.provider === 'azure') {
879823
if (!this.speechConfig) {

0 commit comments

Comments
 (0)