|
40 | 40 | modelDownloadChoice: null, // 'now' | 'later' |
41 | 41 | modelDownloading: false, |
42 | 42 | modelDownloaded: false, |
43 | | - whisperTestPassed: false, |
44 | | - whisperTesting: false, |
45 | 43 | finished: false, |
46 | 44 | }; |
47 | 45 |
|
|
76 | 74 | } |
77 | 75 | refreshStepper(); |
78 | 76 | 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) { |
85 | 79 | nextBtn.disabled = false; |
86 | 80 | nextBtn.classList.remove('success'); |
87 | 81 | nextBtn.classList.add('primary'); |
|
138 | 132 | // Allow advancing whether whisper is detected OR user skipped |
139 | 133 | return state.whisperDetected || state.skippingWhisper; |
140 | 134 | 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; |
145 | 136 | case 'finish': |
146 | 137 | return true; |
147 | 138 | default: |
|
153 | 144 | const geminiInput = $('#geminiKey'); |
154 | 145 | const toggleVis = $('#toggleVis'); |
155 | 146 | const keyStatus = $('#keyStatus'); |
156 | | - const testKeyBtn = $('#testKeyBtn'); |
157 | 147 |
|
158 | 148 | function setKeyStatus(state_, text) { |
159 | 149 | keyStatus.className = `status-pill ${state_}`; |
|
179 | 169 | } else if (keyStatus.classList.contains('success')) { |
180 | 170 | // Keep success state — they had a valid key, may be editing |
181 | 171 | } else { |
182 | | - setKeyStatus('idle', 'Key entered — click Test connection'); |
| 172 | + setKeyStatus('idle', 'Key entered'); |
183 | 173 | } |
184 | 174 | }); |
185 | 175 |
|
|
191 | 181 | : '<i class="fas fa-eye-slash"></i>'; |
192 | 182 | }); |
193 | 183 |
|
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 | | - |
225 | 184 | // ── Wire up: Speech choices ─────────────────────────────────────── |
226 | 185 | $$('#speechChoices .choice-card').forEach((card) => { |
227 | 186 | card.addEventListener('click', () => { |
|
418 | 377 | // Start downloading the model immediately |
419 | 378 | startModelDownload(); |
420 | 379 | } else { |
421 | | - // 'later' doesn't need a test |
422 | 380 | nextBtn.disabled = false; |
423 | 381 | } |
424 | 382 | }); |
425 | 383 | }); |
426 | | - |
427 | | - // Wire up the Whisper test button |
428 | | - const testBtn = document.getElementById('testWhisperBtn'); |
429 | | - if (testBtn) { |
430 | | - testBtn.addEventListener('click', runWhisperTest); |
431 | | - } |
432 | 384 | } |
433 | 385 |
|
434 | 386 | // Restore selection state when navigating back |
435 | 387 | $$('#modelDownloadChoices .choice-card').forEach((card) => { |
436 | 388 | card.classList.toggle('selected', card.dataset.value === state.modelDownloadChoice); |
437 | 389 | }); |
438 | 390 |
|
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; |
458 | 394 | } |
459 | 395 | } |
460 | 396 |
|
|
477 | 413 | if (r.ok) { |
478 | 414 | state.modelDownloaded = true; |
479 | 415 | 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'; |
485 | 420 | } else { |
486 | 421 | appendModelLog(`\n✗ Download failed: ${r.message}`); |
487 | 422 | // Let user continue anyway; they'll download on first use |
|
498 | 433 | } |
499 | 434 | } |
500 | 435 |
|
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 | | - |
543 | 436 | // ── Wire up: Finish screen ──────────────────────────────────────── |
544 | 437 | function populateSummary() { |
545 | 438 | const rows = []; |
|
0 commit comments