vibesurf 0.1.20__py3-none-any.whl → 0.1.22__py3-none-any.whl

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.

Potentially problematic release.


This version of vibesurf might be problematic. Click here for more details.

Files changed (43) hide show
  1. vibe_surf/_version.py +2 -2
  2. vibe_surf/agents/browser_use_agent.py +1 -1
  3. vibe_surf/agents/prompts/vibe_surf_prompt.py +1 -0
  4. vibe_surf/backend/api/task.py +1 -1
  5. vibe_surf/backend/api/voices.py +481 -0
  6. vibe_surf/backend/database/migrations/v004_add_voice_profiles.sql +35 -0
  7. vibe_surf/backend/database/models.py +38 -1
  8. vibe_surf/backend/database/queries.py +189 -1
  9. vibe_surf/backend/main.py +2 -0
  10. vibe_surf/backend/shared_state.py +1 -1
  11. vibe_surf/backend/voice_model_config.py +25 -0
  12. vibe_surf/browser/agen_browser_profile.py +2 -0
  13. vibe_surf/browser/agent_browser_session.py +5 -4
  14. vibe_surf/chrome_extension/background.js +271 -25
  15. vibe_surf/chrome_extension/content.js +147 -0
  16. vibe_surf/chrome_extension/permission-iframe.html +38 -0
  17. vibe_surf/chrome_extension/permission-request.html +104 -0
  18. vibe_surf/chrome_extension/scripts/api-client.js +61 -0
  19. vibe_surf/chrome_extension/scripts/file-manager.js +53 -12
  20. vibe_surf/chrome_extension/scripts/main.js +53 -12
  21. vibe_surf/chrome_extension/scripts/permission-iframe-request.js +188 -0
  22. vibe_surf/chrome_extension/scripts/permission-request.js +118 -0
  23. vibe_surf/chrome_extension/scripts/session-manager.js +30 -4
  24. vibe_surf/chrome_extension/scripts/settings-manager.js +690 -3
  25. vibe_surf/chrome_extension/scripts/ui-manager.js +961 -147
  26. vibe_surf/chrome_extension/scripts/user-settings-storage.js +422 -0
  27. vibe_surf/chrome_extension/scripts/voice-recorder.js +514 -0
  28. vibe_surf/chrome_extension/sidepanel.html +106 -29
  29. vibe_surf/chrome_extension/styles/components.css +35 -0
  30. vibe_surf/chrome_extension/styles/input.css +164 -1
  31. vibe_surf/chrome_extension/styles/layout.css +1 -1
  32. vibe_surf/chrome_extension/styles/settings-environment.css +138 -0
  33. vibe_surf/chrome_extension/styles/settings-forms.css +7 -7
  34. vibe_surf/chrome_extension/styles/variables.css +51 -0
  35. vibe_surf/tools/voice_asr.py +79 -8
  36. {vibesurf-0.1.20.dist-info → vibesurf-0.1.22.dist-info}/METADATA +9 -13
  37. {vibesurf-0.1.20.dist-info → vibesurf-0.1.22.dist-info}/RECORD +41 -34
  38. vibe_surf/chrome_extension/icons/convert-svg.js +0 -33
  39. vibe_surf/chrome_extension/icons/logo-preview.html +0 -187
  40. {vibesurf-0.1.20.dist-info → vibesurf-0.1.22.dist-info}/WHEEL +0 -0
  41. {vibesurf-0.1.20.dist-info → vibesurf-0.1.22.dist-info}/entry_points.txt +0 -0
  42. {vibesurf-0.1.20.dist-info → vibesurf-0.1.22.dist-info}/licenses/LICENSE +0 -0
  43. {vibesurf-0.1.20.dist-info → vibesurf-0.1.22.dist-info}/top_level.txt +0 -0
@@ -17,6 +17,13 @@ class VibeSurfUIManager {
17
17
  this.historyManager = null;
18
18
  this.fileManager = null;
19
19
  this.modalManager = null;
20
+ this.voiceRecorder = null;
21
+
22
+ // Initialize user settings storage
23
+ this.userSettingsStorage = null;
24
+
25
+ // Track if we're currently restoring selections to prevent override
26
+ this.isRestoringSelections = false;
20
27
 
21
28
  this.bindElements();
22
29
  this.initializeTabSelector(); // Initialize tab selector before binding events
@@ -51,6 +58,7 @@ class VibeSurfUIManager {
51
58
  agentModeSelect: document.getElementById('agent-mode-select'),
52
59
  taskInput: document.getElementById('task-input'),
53
60
  sendBtn: document.getElementById('send-btn'),
61
+ voiceRecordBtn: document.getElementById('voice-record-btn'),
54
62
 
55
63
  // Tab selector elements
56
64
  tabSelectorDropdown: document.getElementById('tab-selector-dropdown'),
@@ -77,6 +85,13 @@ class VibeSurfUIManager {
77
85
  // Initialize modal manager first (others may depend on it)
78
86
  this.modalManager = new VibeSurfModalManager();
79
87
 
88
+ // Initialize user settings storage
89
+ this.userSettingsStorage = new VibeSurfUserSettingsStorage();
90
+
91
+ // Initialize voice recorder
92
+ this.voiceRecorder = new VibeSurfVoiceRecorder(this.apiClient);
93
+ this.setupVoiceRecorderCallbacks();
94
+
80
95
  // Initialize other managers
81
96
  this.settingsManager = new VibeSurfSettingsManager(this.apiClient);
82
97
  this.historyManager = new VibeSurfHistoryManager(this.apiClient);
@@ -85,7 +100,7 @@ class VibeSurfUIManager {
85
100
  // Set up inter-manager communication
86
101
  this.setupManagerEvents();
87
102
 
88
- console.log('[UIManager] All specialized managers initialized');
103
+
89
104
  } catch (error) {
90
105
  console.error('[UIManager] Failed to initialize managers:', error);
91
106
  this.showNotification('Failed to initialize UI components', 'error');
@@ -95,7 +110,14 @@ class VibeSurfUIManager {
95
110
  setupManagerEvents() {
96
111
  // Settings Manager Events
97
112
  this.settingsManager.on('profilesUpdated', () => {
98
- this.updateLLMProfileSelect();
113
+ // Only update the profile select if we're not in the middle of restoring selections
114
+ // This prevents the profilesUpdated event from overriding user selections during initialization
115
+ if (!this.isRestoringSelections) {
116
+ this.updateLLMProfileSelect();
117
+ }
118
+
119
+ // Also update voice button state when profiles are updated (ASR profiles might have changed)
120
+ this.updateVoiceButtonState();
99
121
  });
100
122
 
101
123
  this.settingsManager.on('notification', (data) => {
@@ -213,6 +235,113 @@ class VibeSurfUIManager {
213
235
  });
214
236
  }
215
237
 
238
+ // Voice Recorder Callbacks
239
+ setupVoiceRecorderCallbacks() {
240
+ this.voiceRecorder.setCallbacks({
241
+ onRecordingStart: () => {
242
+ console.log('[UIManager] Voice recording started');
243
+ this.updateVoiceRecordingUI(true);
244
+ },
245
+ onRecordingStop: (audioBlob, duration) => {
246
+ console.log(`[UIManager] Voice recording stopped after ${duration}ms`);
247
+ this.updateVoiceRecordingUI(false);
248
+ },
249
+ onTranscriptionComplete: (transcribedText, result) => {
250
+ console.log(`[UIManager] Transcription completed: "${transcribedText}"`);
251
+ this.handleTranscriptionComplete(transcribedText);
252
+ },
253
+ onTranscriptionError: (errorMessage, errorType) => {
254
+ console.error(`[UIManager] Transcription error: ${errorMessage}`);
255
+ this.handleTranscriptionError(errorMessage, errorType);
256
+ },
257
+ onDurationUpdate: (formattedDuration, durationMs) => {
258
+ this.updateRecordingDuration(formattedDuration, durationMs);
259
+ }
260
+ });
261
+ }
262
+
263
+ // Update voice recording UI state
264
+ updateVoiceRecordingUI(isRecording) {
265
+ if (this.elements.voiceRecordBtn) {
266
+ if (isRecording) {
267
+ this.elements.voiceRecordBtn.classList.add('recording');
268
+ this.elements.voiceRecordBtn.setAttribute('title', 'Stop Recording');
269
+ this.elements.voiceRecordBtn.setAttribute('data-tooltip', 'Recording... Click to stop');
270
+ } else {
271
+ this.elements.voiceRecordBtn.classList.remove('recording');
272
+ this.elements.voiceRecordBtn.setAttribute('title', 'Voice Input');
273
+ this.elements.voiceRecordBtn.setAttribute('data-tooltip', 'Click to start voice recording');
274
+
275
+ // Clear duration display
276
+ this.updateRecordingDuration('0:00', 0);
277
+ }
278
+ }
279
+ }
280
+
281
+ // Update recording duration display
282
+ updateRecordingDuration(formattedDuration, durationMs) {
283
+ if (this.elements.voiceRecordBtn) {
284
+ // Update the tooltip with duration
285
+ this.elements.voiceRecordBtn.setAttribute('data-tooltip', `Recording... ${formattedDuration} - Click to stop`);
286
+
287
+ // Create or update duration display element
288
+ let durationElement = this.elements.voiceRecordBtn.querySelector('.recording-duration');
289
+ if (!durationElement) {
290
+ durationElement = document.createElement('div');
291
+ durationElement.className = 'recording-duration';
292
+ this.elements.voiceRecordBtn.appendChild(durationElement);
293
+ }
294
+
295
+ durationElement.textContent = formattedDuration;
296
+ }
297
+ }
298
+
299
+ // Handle transcription completion
300
+ handleTranscriptionComplete(transcribedText) {
301
+ if (!this.elements.taskInput) {
302
+ console.error('[UIManager] Task input element not found');
303
+ return;
304
+ }
305
+
306
+ // Insert transcribed text into the input field
307
+ const currentValue = this.elements.taskInput.value;
308
+ const cursorPosition = this.elements.taskInput.selectionStart;
309
+
310
+ // Insert at cursor position or append to end
311
+ const beforeCursor = currentValue.substring(0, cursorPosition);
312
+ const afterCursor = currentValue.substring(cursorPosition);
313
+ const newValue = beforeCursor + transcribedText + afterCursor;
314
+
315
+ this.elements.taskInput.value = newValue;
316
+
317
+ // Trigger input change event for validation and auto-resize
318
+ this.handleTaskInputChange({ target: this.elements.taskInput });
319
+
320
+ // Set cursor position after the inserted text
321
+ const newCursorPosition = cursorPosition + transcribedText.length;
322
+ this.elements.taskInput.setSelectionRange(newCursorPosition, newCursorPosition);
323
+ this.elements.taskInput.focus();
324
+
325
+ this.showNotification('Voice transcription completed', 'success');
326
+ }
327
+
328
+ // Handle transcription errors
329
+ handleTranscriptionError(errorMessage, errorType) {
330
+ let userMessage = 'Voice transcription failed';
331
+
332
+ if (errorType === 'recording') {
333
+ userMessage = `Recording failed: ${errorMessage}`;
334
+ } else if (errorType === 'transcription') {
335
+ if (errorMessage.includes('No active ASR profiles')) {
336
+ userMessage = 'No voice recognition profiles configured. Please set up an ASR profile in Settings > Voice.';
337
+ } else {
338
+ userMessage = `Transcription failed: ${errorMessage}`;
339
+ }
340
+ }
341
+
342
+ this.showNotification(userMessage, 'error');
343
+ }
344
+
216
345
  bindEvents() {
217
346
  // Header buttons
218
347
  this.elements.newSessionBtn?.addEventListener('click', this.handleNewSession.bind(this));
@@ -229,6 +358,7 @@ class VibeSurfUIManager {
229
358
 
230
359
  // Input handling
231
360
  this.elements.sendBtn?.addEventListener('click', this.handleSendTask.bind(this));
361
+ this.elements.voiceRecordBtn?.addEventListener('click', this.handleVoiceRecord.bind(this));
232
362
 
233
363
  // Task input handling
234
364
  this.elements.taskInput?.addEventListener('keydown', this.handleTaskInputKeydown.bind(this));
@@ -294,7 +424,7 @@ class VibeSurfUIManager {
294
424
  }
295
425
 
296
426
  handleTaskSubmitted(data) {
297
- console.log('[UIManager] Task submitted successfully, showing control panel');
427
+
298
428
  this.updateControlPanel('running');
299
429
  this.clearTaskInput();
300
430
  }
@@ -319,21 +449,21 @@ class VibeSurfUIManager {
319
449
  }
320
450
 
321
451
  handleTaskCompleted(data) {
322
- console.log('[UIManager] Task completed with status:', data.status);
452
+
323
453
 
324
454
  const message = data.status === 'done' ? 'Task completed successfully!' : 'Task completed with errors';
325
455
  const type = data.status === 'done' ? 'success' : 'error';
326
456
 
327
457
  // Check if we need to respect minimum visibility period
328
458
  if (this.controlPanelMinVisibilityActive) {
329
- console.log('[UIManager] Task completed during minimum visibility period, delaying control panel hide');
459
+
330
460
  const remainingTime = 1000;
331
461
  setTimeout(() => {
332
- console.log('[UIManager] Minimum visibility period respected, now hiding control panel');
462
+
333
463
  this.updateControlPanel('ready');
334
464
  }, remainingTime);
335
465
  } else {
336
- console.log('[UIManager] Task completed, hiding control panel immediately');
466
+
337
467
  this.updateControlPanel('ready');
338
468
  }
339
469
 
@@ -363,17 +493,17 @@ class VibeSurfUIManager {
363
493
 
364
494
  handleTaskError(data) {
365
495
  console.error('[UIManager] Task error:', data.error);
366
- console.log('[UIManager] Task error data structure:', JSON.stringify(data, null, 2));
496
+
367
497
 
368
498
  // Check if this is an LLM connection failure
369
499
  if (data.error && typeof data.error === 'object' && data.error.error === 'llm_connection_failed') {
370
- console.log('[UIManager] Detected LLM connection failure from object error');
500
+
371
501
  // Show LLM connection failed modal instead of generic notification
372
502
  this.showLLMConnectionFailedModal(data.error);
373
503
  this.updateControlPanel('ready'); // Reset UI since task failed to start
374
504
  return;
375
505
  } else if (data.error && typeof data.error === 'string' && data.error.includes('llm_connection_failed')) {
376
- console.log('[UIManager] Detected LLM connection failure from string error');
506
+
377
507
  // Handle case where error is a string containing the error type
378
508
  this.showLLMConnectionFailedModal({
379
509
  message: data.error,
@@ -392,10 +522,10 @@ class VibeSurfUIManager {
392
522
  setTimeout(() => {
393
523
  this.checkTaskStatus().then(status => {
394
524
  if (!status.isRunning) {
395
- console.log('[UIManager] Task confirmed stopped after error, hiding controls');
525
+
396
526
  this.updateControlPanel('ready');
397
527
  } else {
398
- console.log('[UIManager] Task still running after error, keeping controls visible');
528
+
399
529
  }
400
530
  }).catch(err => {
401
531
  console.warn('[UIManager] Could not verify task status after error:', err);
@@ -470,6 +600,23 @@ class VibeSurfUIManager {
470
600
  this.elements.agentModeSelect.disabled = isRunning && !isPaused;
471
601
  }
472
602
 
603
+ // Update voice record button state - disable during task execution unless paused
604
+ if (this.elements.voiceRecordBtn) {
605
+ const shouldDisableVoice = isRunning && !isPaused;
606
+ this.elements.voiceRecordBtn.disabled = shouldDisableVoice;
607
+ if (shouldDisableVoice) {
608
+ this.elements.voiceRecordBtn.classList.add('task-running-disabled');
609
+ this.elements.voiceRecordBtn.setAttribute('title', 'Voice input disabled while task is running');
610
+ } else {
611
+ this.elements.voiceRecordBtn.classList.remove('task-running-disabled');
612
+ if (this.elements.voiceRecordBtn.classList.contains('recording')) {
613
+ this.elements.voiceRecordBtn.setAttribute('title', 'Recording... Click to stop');
614
+ } else {
615
+ this.elements.voiceRecordBtn.setAttribute('title', 'Click to start voice recording');
616
+ }
617
+ }
618
+ }
619
+
473
620
  // Update file manager state - keep disabled during pause (as per requirement)
474
621
  this.fileManager.setEnabled(!isRunning);
475
622
 
@@ -496,7 +643,6 @@ class VibeSurfUIManager {
496
643
  }
497
644
 
498
645
  forceUpdateUIForPausedState() {
499
- console.log('[UIManager] Force updating UI for paused state');
500
646
 
501
647
  // Enable input during pause
502
648
  if (this.elements.taskInput) {
@@ -509,6 +655,13 @@ class VibeSurfUIManager {
509
655
  this.elements.sendBtn.disabled = !hasText;
510
656
  }
511
657
 
658
+ // Enable voice record button during pause
659
+ if (this.elements.voiceRecordBtn) {
660
+ this.elements.voiceRecordBtn.disabled = false;
661
+ this.elements.voiceRecordBtn.classList.remove('task-running-disabled');
662
+ this.elements.voiceRecordBtn.setAttribute('title', 'Click to start voice recording');
663
+ }
664
+
512
665
  // Keep LLM profile disabled during pause (user doesn't need to change it)
513
666
  if (this.elements.llmProfileSelect) {
514
667
  this.elements.llmProfileSelect.disabled = true;
@@ -522,7 +675,7 @@ class VibeSurfUIManager {
522
675
  // Keep file manager disabled during pause
523
676
  this.fileManager.setEnabled(false);
524
677
 
525
- // Keep header buttons disabled during pause (only input and send should be available)
678
+ // Keep header buttons disabled during pause (only input, send, and voice should be available)
526
679
  const headerButtons = [
527
680
  this.elements.newSessionBtn,
528
681
  this.elements.historyBtn,
@@ -533,13 +686,13 @@ class VibeSurfUIManager {
533
686
  if (button) {
534
687
  button.disabled = true;
535
688
  button.classList.add('task-running-disabled');
536
- button.setAttribute('title', 'Disabled during pause - only input and send are available');
689
+ button.setAttribute('title', 'Disabled during pause - only input, send, and voice input are available');
537
690
  }
538
691
  });
539
692
  }
540
693
 
541
694
  forceUpdateUIForRunningState() {
542
- console.log('[UIManager] Force updating UI for running state');
695
+
543
696
 
544
697
  // Disable input during running
545
698
  if (this.elements.taskInput) {
@@ -559,6 +712,13 @@ class VibeSurfUIManager {
559
712
  this.elements.agentModeSelect.disabled = true;
560
713
  }
561
714
 
715
+ // Disable voice record button during running
716
+ if (this.elements.voiceRecordBtn) {
717
+ this.elements.voiceRecordBtn.disabled = true;
718
+ this.elements.voiceRecordBtn.classList.add('task-running-disabled');
719
+ this.elements.voiceRecordBtn.setAttribute('title', 'Voice input disabled while task is running');
720
+ }
721
+
562
722
  // Update file manager state
563
723
  this.fileManager.setEnabled(false);
564
724
 
@@ -658,6 +818,26 @@ class VibeSurfUIManager {
658
818
  this.settingsManager.showSettings();
659
819
  }
660
820
 
821
+ async handleShowLLMSettings() {
822
+ // Enhanced task running check
823
+ const statusCheck = await this.checkTaskStatus();
824
+ if (statusCheck.isRunning) {
825
+ const canProceed = await this.showTaskRunningWarning('access settings');
826
+ if (!canProceed) return;
827
+ }
828
+
829
+ // Show settings and navigate directly to LLM profiles tab
830
+ this.settingsManager.showSettings();
831
+
832
+ // Switch to LLM profiles tab after settings are shown
833
+ setTimeout(() => {
834
+ const llmTab = document.querySelector('.settings-tab[data-tab="llm-profiles"]');
835
+ if (llmTab) {
836
+ llmTab.click();
837
+ }
838
+ }, 100);
839
+ }
840
+
661
841
  async handleCopySession() {
662
842
  const sessionId = this.sessionManager.getCurrentSessionId();
663
843
 
@@ -675,6 +855,111 @@ class VibeSurfUIManager {
675
855
  }
676
856
  }
677
857
 
858
+ // Voice Recording UI Handler
859
+ async handleVoiceRecord() {
860
+ // Check if voice recording is supported
861
+ if (!this.voiceRecorder.isSupported()) {
862
+ this.showNotification('Voice recording is not supported in your browser', 'error');
863
+ return;
864
+ }
865
+
866
+ // Check if ASR profiles are available before allowing recording
867
+ const isVoiceAvailable = await this.voiceRecorder.isVoiceRecordingAvailable();
868
+ if (!isVoiceAvailable) {
869
+ console.log('[UIManager] No ASR profiles available, showing configuration modal');
870
+ this.showVoiceProfileRequiredModal('configure');
871
+ return;
872
+ }
873
+
874
+ // Enhanced task status check - disable recording during task execution unless paused
875
+ const taskStatus = this.sessionManager.getTaskStatus();
876
+ const isTaskRunning = this.state.isTaskRunning;
877
+
878
+ if (isTaskRunning && taskStatus !== 'paused') {
879
+ this.showNotification('Cannot record voice while task is running. Stop the current task or wait for it to complete.', 'warning');
880
+ return;
881
+ }
882
+
883
+ // Additional check: if task is paused, allow voice input but show info message
884
+ if (isTaskRunning && taskStatus === 'paused') {
885
+ console.log('[UIManager] Task is paused, allowing voice input');
886
+ }
887
+
888
+ // Check if voice button is disabled due to missing ASR profiles
889
+ if (this.elements.voiceRecordBtn && this.elements.voiceRecordBtn.classList.contains('voice-disabled')) {
890
+ console.log('[UIManager] Voice button is disabled due to missing ASR profiles, showing modal');
891
+ this.showVoiceProfileRequiredModal('configure');
892
+ return;
893
+ }
894
+
895
+ try {
896
+ if (this.voiceRecorder.isCurrentlyRecording()) {
897
+ // Stop recording
898
+ console.log('[UIManager] Stopping voice recording');
899
+ await this.voiceRecorder.stopRecording();
900
+ } else {
901
+ // Start recording
902
+ console.log('[UIManager] Starting voice recording');
903
+
904
+ // Request microphone permission first
905
+ try {
906
+ // For Chrome extensions, ensure we have proper user gesture context
907
+ console.log('[UIManager] Requesting microphone permission with user gesture context');
908
+
909
+ // Add a small delay to ensure user gesture is properly registered
910
+ await new Promise(resolve => setTimeout(resolve, 100));
911
+
912
+ const hasPermission = await this.voiceRecorder.requestMicrophonePermission();
913
+ if (!hasPermission) {
914
+ this.showNotification('Microphone permission denied. Please allow microphone access to use voice input.', 'error');
915
+ return;
916
+ }
917
+ } catch (permissionError) {
918
+ console.error('[UIManager] Microphone permission error:', permissionError);
919
+
920
+ // Handle different types of permission errors with detailed guidance
921
+ let errorMessage = 'Microphone permission denied';
922
+ let detailedHelp = '';
923
+
924
+ if (permissionError.name === 'MicrophonePermissionError') {
925
+ errorMessage = permissionError.message;
926
+ if (permissionError.userAction) {
927
+ detailedHelp = permissionError.userAction;
928
+ }
929
+ } else if (permissionError.name === 'NotAllowedError') {
930
+ errorMessage = 'Microphone access was denied by your browser.';
931
+ detailedHelp = 'Please check your browser permissions and try again. You may need to allow microphone access in your browser settings.';
932
+ } else {
933
+ errorMessage = `Microphone access error: ${permissionError.message}`;
934
+ detailedHelp = 'Please ensure your browser allows microphone access and that a microphone is connected.';
935
+ }
936
+
937
+ // Show detailed error message
938
+ const fullMessage = detailedHelp ? `${errorMessage}\n\n${detailedHelp}` : errorMessage;
939
+ this.showNotification(fullMessage, 'error');
940
+
941
+ // Also log detailed error for debugging
942
+ console.error('[UIManager] Detailed microphone permission error:', {
943
+ name: permissionError.name,
944
+ message: permissionError.message,
945
+ userAction: permissionError.userAction,
946
+ originalError: permissionError.originalError
947
+ });
948
+
949
+ this.updateVoiceRecordingUI(false);
950
+ return;
951
+ }
952
+
953
+ // Start recording
954
+ await this.voiceRecorder.startRecording();
955
+ }
956
+ } catch (error) {
957
+ console.error('[UIManager] Voice recording error:', error);
958
+ this.showNotification(`Voice recording failed: ${error.message}`, 'error');
959
+ this.updateVoiceRecordingUI(false);
960
+ }
961
+ }
962
+
678
963
  async handleSendTask() {
679
964
  const taskDescription = this.elements.taskInput?.value.trim();
680
965
  const taskStatus = this.sessionManager.getTaskStatus();
@@ -689,7 +974,7 @@ class VibeSurfUIManager {
689
974
  try {
690
975
  if (isPaused) {
691
976
  // Handle adding new task to paused execution
692
- console.log('[UIManager] Adding new task to paused execution:', taskDescription);
977
+
693
978
  await this.sessionManager.addNewTaskToPaused(taskDescription);
694
979
 
695
980
  // Clear the input after successful addition
@@ -697,7 +982,7 @@ class VibeSurfUIManager {
697
982
  this.showNotification('Additional information added to the task', 'success');
698
983
 
699
984
  // Automatically resume the task after adding new information
700
- console.log('[UIManager] Auto-resuming task after adding new information');
985
+
701
986
  await this.sessionManager.resumeTask('Auto-resume after adding new task information');
702
987
 
703
988
  return;
@@ -742,17 +1027,17 @@ class VibeSurfUIManager {
742
1027
  const filePath = this.fileManager.getUploadedFilesForTask();
743
1028
  if (filePath) {
744
1029
  taskData.upload_files_path = filePath;
745
- console.log('[UIManager] Set upload_files_path to:', filePath);
1030
+
746
1031
  }
747
1032
 
748
1033
  // Add selected tabs information if any
749
1034
  const selectedTabsData = this.getSelectedTabsForTask();
750
1035
  if (selectedTabsData) {
751
1036
  taskData.selected_tabs = selectedTabsData;
752
- console.log('[UIManager] Set selected_tabs to:', selectedTabsData);
1037
+
753
1038
  }
754
1039
 
755
- console.log('[UIManager] Complete task data being submitted:', JSON.stringify(taskData, null, 2));
1040
+
756
1041
  await this.sessionManager.submitTask(taskData);
757
1042
 
758
1043
  // Clear uploaded files after successful task submission
@@ -879,14 +1164,64 @@ class VibeSurfUIManager {
879
1164
  return false; // Allow default behavior
880
1165
  }
881
1166
 
882
- handleLlmProfileChange(event) {
1167
+ async handleLlmProfileChange(event) {
1168
+ const selectedProfile = event.target.value;
1169
+
1170
+
1171
+
1172
+ try {
1173
+ // Save the selected LLM profile to user settings storage
1174
+ if (this.userSettingsStorage) {
1175
+
1176
+ await this.userSettingsStorage.setSelectedLlmProfile(selectedProfile);
1177
+
1178
+
1179
+ // Verify the save
1180
+ const verified = await this.userSettingsStorage.getSelectedLlmProfile();
1181
+
1182
+
1183
+ // Also save to localStorage as backup for browser restart scenarios
1184
+ localStorage.setItem('vibesurf-llm-profile-backup', selectedProfile);
1185
+
1186
+ } else {
1187
+ console.warn('[UIManager] userSettingsStorage not available for LLM profile save');
1188
+ }
1189
+ } catch (error) {
1190
+ console.error('[UIManager] Failed to save LLM profile selection:', error);
1191
+ }
1192
+
883
1193
  // Re-validate send button state when LLM profile changes
884
1194
  if (this.elements.taskInput) {
885
1195
  this.handleTaskInputChange({ target: this.elements.taskInput });
886
1196
  }
887
1197
  }
888
1198
 
889
- handleAgentModeChange(event) {
1199
+ async handleAgentModeChange(event) {
1200
+ const selectedMode = event.target.value;
1201
+
1202
+
1203
+
1204
+ try {
1205
+ // Save the selected agent mode to user settings storage
1206
+ if (this.userSettingsStorage) {
1207
+
1208
+ await this.userSettingsStorage.setSelectedAgentMode(selectedMode);
1209
+
1210
+
1211
+ // Verify the save
1212
+ const verified = await this.userSettingsStorage.getSelectedAgentMode();
1213
+
1214
+
1215
+ // Also save to localStorage as backup for browser restart scenarios
1216
+ localStorage.setItem('vibesurf-agent-mode-backup', selectedMode);
1217
+
1218
+ } else {
1219
+ console.warn('[UIManager] userSettingsStorage not available for agent mode save');
1220
+ }
1221
+ } catch (error) {
1222
+ console.error('[UIManager] Failed to save agent mode selection:', error);
1223
+ }
1224
+
890
1225
  // Re-validate send button state when agent mode changes
891
1226
  if (this.elements.taskInput) {
892
1227
  this.handleTaskInputChange({ target: this.elements.taskInput });
@@ -894,7 +1229,7 @@ class VibeSurfUIManager {
894
1229
  }
895
1230
 
896
1231
  handleTaskInputChange(event) {
897
- console.log('[UIManager] handleTaskInputChange called with value:', event.target.value);
1232
+
898
1233
 
899
1234
  const hasText = event.target.value.trim().length > 0;
900
1235
  const textarea = event.target;
@@ -938,10 +1273,10 @@ class VibeSurfUIManager {
938
1273
 
939
1274
  // UI Display Methods
940
1275
  updateSessionDisplay(sessionId) {
941
- console.log('[UIManager] Updating session display with ID:', sessionId);
1276
+
942
1277
  if (this.elements.sessionId) {
943
1278
  this.elements.sessionId.textContent = sessionId || '-';
944
- console.log('[UIManager] Session ID display updated to:', sessionId);
1279
+
945
1280
  } else {
946
1281
  console.error('[UIManager] Session ID element not found');
947
1282
  }
@@ -968,13 +1303,13 @@ class VibeSurfUIManager {
968
1303
 
969
1304
  switch (status) {
970
1305
  case 'ready':
971
- console.log('[UIManager] Setting control panel to ready (hidden)');
1306
+
972
1307
  panel.classList.add('hidden');
973
1308
  panel.classList.remove('error-state');
974
1309
  break;
975
1310
 
976
1311
  case 'running':
977
- console.log('[UIManager] Setting control panel to running (showing cancel button)');
1312
+
978
1313
  panel.classList.remove('hidden');
979
1314
  panel.classList.remove('error-state');
980
1315
  cancelBtn?.classList.remove('hidden');
@@ -986,7 +1321,7 @@ class VibeSurfUIManager {
986
1321
  break;
987
1322
 
988
1323
  case 'paused':
989
- console.log('[UIManager] Setting control panel to paused (showing resume/terminate buttons)');
1324
+
990
1325
  panel.classList.remove('hidden');
991
1326
  panel.classList.remove('error-state');
992
1327
  cancelBtn?.classList.add('hidden');
@@ -995,7 +1330,7 @@ class VibeSurfUIManager {
995
1330
  break;
996
1331
 
997
1332
  case 'error':
998
- console.log('[UIManager] Setting control panel to error (keeping cancel/terminate buttons visible)');
1333
+
999
1334
  panel.classList.remove('hidden');
1000
1335
  panel.classList.add('error-state');
1001
1336
  cancelBtn?.classList.remove('hidden');
@@ -1003,6 +1338,15 @@ class VibeSurfUIManager {
1003
1338
  terminateBtn?.classList.remove('hidden');
1004
1339
  break;
1005
1340
 
1341
+ case 'done':
1342
+ case 'completed':
1343
+ case 'finished':
1344
+ console.log(`[UIManager] Task completed with status: ${status}, hiding panel after delay`);
1345
+ // Treat as ready state
1346
+ panel.classList.add('hidden');
1347
+ panel.classList.remove('error-state');
1348
+ break;
1349
+
1006
1350
  default:
1007
1351
  console.log(`[UIManager] Unknown control panel status: ${status}, hiding panel`);
1008
1352
  panel.classList.add('hidden');
@@ -1017,7 +1361,7 @@ class VibeSurfUIManager {
1017
1361
  // Clear the flag after minimum visibility period (2 seconds)
1018
1362
  setTimeout(() => {
1019
1363
  this.controlPanelMinVisibilityActive = false;
1020
- console.log('[UIManager] Minimum control panel visibility period ended');
1364
+
1021
1365
  }, 2000);
1022
1366
  }
1023
1367
 
@@ -1108,9 +1452,21 @@ class VibeSurfUIManager {
1108
1452
  }
1109
1453
 
1110
1454
  logs.forEach(log => this.addActivityLog(log));
1455
+
1456
+ // Bind link click handlers to all existing activity items after loading
1457
+ this.bindLinkHandlersToAllActivityItems();
1458
+
1111
1459
  this.scrollActivityToBottom();
1112
1460
  }
1113
1461
 
1462
+ bindLinkHandlersToAllActivityItems() {
1463
+ // Bind link click handlers to all existing activity items
1464
+ const activityItems = this.elements.activityLog.querySelectorAll('.activity-item');
1465
+ activityItems.forEach(item => {
1466
+ this.bindLinkClickHandlers(item);
1467
+ });
1468
+ }
1469
+
1114
1470
  addActivityLog(activityData) {
1115
1471
  // Filter out "done" status messages from UI display only
1116
1472
  const agentStatus = activityData.agent_status || activityData.status || '';
@@ -1140,6 +1496,9 @@ class VibeSurfUIManager {
1140
1496
 
1141
1497
  // Bind copy button functionality
1142
1498
  this.bindCopyButtonEvent(activityItem, activityData);
1499
+
1500
+ // Bind link click handlers to prevent extension freezing
1501
+ this.bindLinkClickHandlers(activityItem);
1143
1502
  }
1144
1503
  }
1145
1504
  }
@@ -1218,7 +1577,7 @@ class VibeSurfUIManager {
1218
1577
  }
1219
1578
 
1220
1579
  handleSuggestionTaskClick(taskDescription) {
1221
- console.log('[UIManager] Suggestion task clicked:', taskDescription);
1580
+
1222
1581
 
1223
1582
  // First check if task is running
1224
1583
  if (this.state.isTaskRunning) {
@@ -1240,7 +1599,7 @@ class VibeSurfUIManager {
1240
1599
  return;
1241
1600
  }
1242
1601
 
1243
- console.log('[UIManager] Setting task description and submitting...');
1602
+
1244
1603
  this.elements.taskInput.value = taskDescription;
1245
1604
  this.elements.taskInput.focus();
1246
1605
 
@@ -1249,7 +1608,7 @@ class VibeSurfUIManager {
1249
1608
 
1250
1609
  // Auto-submit the task after a short delay
1251
1610
  setTimeout(() => {
1252
- console.log('[UIManager] Auto-submitting suggestion task...');
1611
+
1253
1612
  this.handleSendTask();
1254
1613
  }, 100);
1255
1614
  }
@@ -1342,6 +1701,147 @@ class VibeSurfUIManager {
1342
1701
  }
1343
1702
  }
1344
1703
 
1704
+ bindLinkClickHandlers(activityItem) {
1705
+ // Handle all link clicks within activity items to prevent extension freezing
1706
+ const links = activityItem.querySelectorAll('a');
1707
+
1708
+ links.forEach(link => {
1709
+ // Check if handler is already attached to prevent double binding
1710
+ if (link.hasAttribute('data-link-handler-attached')) {
1711
+ return;
1712
+ }
1713
+
1714
+ link.setAttribute('data-link-handler-attached', 'true');
1715
+
1716
+ link.addEventListener('click', async (e) => {
1717
+ console.log('[VibeSurf] Link click event detected:', e);
1718
+
1719
+ // Comprehensive event prevention
1720
+ e.preventDefault();
1721
+ e.stopPropagation();
1722
+ e.stopImmediatePropagation(); // Prevent any other handlers
1723
+
1724
+ // Remove href temporarily to prevent default browser behavior
1725
+ const originalHref = link.getAttribute('href');
1726
+ link.setAttribute('href', '#');
1727
+
1728
+ const href = originalHref;
1729
+ if (!href || href === '#') return;
1730
+
1731
+ // Debounce - prevent rapid repeated clicks
1732
+ if (link.hasAttribute('data-link-processing')) {
1733
+ console.log('[VibeSurf] Link already processing, ignoring duplicate click');
1734
+ return;
1735
+ }
1736
+
1737
+ link.setAttribute('data-link-processing', 'true');
1738
+
1739
+ try {
1740
+ console.log('[VibeSurf] Processing link:', href);
1741
+
1742
+ // Handle file:// links using existing logic
1743
+ if (href.startsWith('file://')) {
1744
+ await this.handleFileLinkClick(href);
1745
+ }
1746
+ // Handle HTTP/HTTPS links
1747
+ else if (href.startsWith('http://') || href.startsWith('https://')) {
1748
+ await this.handleHttpLinkClick(href);
1749
+ }
1750
+ // Handle other protocols or relative URLs
1751
+ else {
1752
+ await this.handleOtherLinkClick(href);
1753
+ }
1754
+
1755
+ console.log('[VibeSurf] Link processed successfully:', href);
1756
+ } catch (error) {
1757
+ console.error('[VibeSurf] Error handling link click:', error);
1758
+ this.showNotification(`Failed to open link: ${error.message}`, 'error');
1759
+ } finally {
1760
+ // Restore original href
1761
+ link.setAttribute('href', originalHref);
1762
+
1763
+ // Remove processing flag after a short delay
1764
+ setTimeout(() => {
1765
+ link.removeAttribute('data-link-processing');
1766
+ }, 1000);
1767
+ }
1768
+ });
1769
+ });
1770
+ }
1771
+
1772
+ async handleFileLinkClick(fileUrl) {
1773
+ console.log('[UIManager] Opening file URL:', fileUrl);
1774
+
1775
+ // Use the background script to handle file URLs
1776
+ const result = await chrome.runtime.sendMessage({
1777
+ type: 'OPEN_FILE_URL',
1778
+ data: { fileUrl }
1779
+ });
1780
+
1781
+ if (!result.success) {
1782
+ throw new Error(result.error || 'Failed to open file');
1783
+ }
1784
+ }
1785
+
1786
+ async handleHttpLinkClick(url) {
1787
+ console.log('[VibeSurf] Opening HTTP URL:', url);
1788
+
1789
+ try {
1790
+ // Open HTTP/HTTPS links in a new tab
1791
+ const result = await chrome.runtime.sendMessage({
1792
+ type: 'OPEN_FILE_URL',
1793
+ data: { fileUrl: url }
1794
+ });
1795
+
1796
+ console.log('[VibeSurf] Background script response:', result);
1797
+
1798
+ if (!result || !result.success) {
1799
+ throw new Error(result?.error || 'Failed to create tab for URL');
1800
+ }
1801
+
1802
+ console.log('[VibeSurf] Successfully opened tab:', result.tabId);
1803
+ } catch (error) {
1804
+ console.error('[VibeSurf] Error opening HTTP URL:', error);
1805
+ throw error;
1806
+ }
1807
+ }
1808
+
1809
+ async handleOtherLinkClick(url) {
1810
+ console.log('[UIManager] Opening other URL:', url);
1811
+
1812
+ // For relative URLs or other protocols, try to open in new tab
1813
+ try {
1814
+ // Use the background script to handle URL opening
1815
+ const result = await chrome.runtime.sendMessage({
1816
+ type: 'OPEN_FILE_URL',
1817
+ data: { fileUrl: url }
1818
+ });
1819
+
1820
+ if (!result.success) {
1821
+ throw new Error(result.error || 'Failed to open URL');
1822
+ }
1823
+ } catch (error) {
1824
+ // If the background script method fails, try to construct absolute URL
1825
+ if (!url.startsWith('http')) {
1826
+ try {
1827
+ const absoluteUrl = new URL(url, window.location.href).href;
1828
+ const result = await chrome.runtime.sendMessage({
1829
+ type: 'OPEN_FILE_URL',
1830
+ data: { fileUrl: absoluteUrl }
1831
+ });
1832
+
1833
+ if (!result.success) {
1834
+ throw new Error(result.error || 'Failed to open absolute URL');
1835
+ }
1836
+ } catch (urlError) {
1837
+ throw new Error(`Failed to open URL: ${urlError.message}`);
1838
+ }
1839
+ } else {
1840
+ throw error;
1841
+ }
1842
+ }
1843
+ }
1844
+
1345
1845
  async copyMessageToClipboard(activityData) {
1346
1846
  try {
1347
1847
  // Extract only the message content (no agent info or timestamps)
@@ -1370,9 +1870,9 @@ class VibeSurfUIManager {
1370
1870
  }
1371
1871
 
1372
1872
  // Check clipboard API availability
1373
- console.log('[UIManager] navigator.clipboard available:', !!navigator.clipboard);
1374
- console.log('[UIManager] writeText method available:', !!(navigator.clipboard && navigator.clipboard.writeText));
1375
- console.log('[UIManager] Document has focus:', document.hasFocus());
1873
+
1874
+
1875
+
1376
1876
 
1377
1877
  // Try multiple clipboard methods
1378
1878
  let copySuccess = false;
@@ -1380,7 +1880,7 @@ class VibeSurfUIManager {
1380
1880
 
1381
1881
  // Method 1: Chrome extension messaging approach
1382
1882
  try {
1383
- console.log('[UIManager] Trying Chrome extension messaging approach...');
1883
+
1384
1884
  await new Promise((resolve, reject) => {
1385
1885
  chrome.runtime.sendMessage({
1386
1886
  type: 'COPY_TO_CLIPBOARD',
@@ -1396,7 +1896,7 @@ class VibeSurfUIManager {
1396
1896
  });
1397
1897
  });
1398
1898
  copySuccess = true;
1399
- console.log('[UIManager] Copied using Chrome extension messaging');
1899
+
1400
1900
  } catch (extensionError) {
1401
1901
  console.warn('[UIManager] Chrome extension messaging failed:', extensionError);
1402
1902
  lastError = extensionError;
@@ -1405,10 +1905,10 @@ class VibeSurfUIManager {
1405
1905
  // Method 2: Modern clipboard API (if extension method failed)
1406
1906
  if (!copySuccess && navigator.clipboard && navigator.clipboard.writeText) {
1407
1907
  try {
1408
- console.log('[UIManager] Trying modern clipboard API...');
1908
+
1409
1909
  await navigator.clipboard.writeText(messageText);
1410
1910
  copySuccess = true;
1411
- console.log('[UIManager] Copied using modern clipboard API');
1911
+
1412
1912
  } catch (clipboardError) {
1413
1913
  console.warn('[UIManager] Modern clipboard API failed:', clipboardError);
1414
1914
  lastError = clipboardError;
@@ -1418,7 +1918,7 @@ class VibeSurfUIManager {
1418
1918
  // Method 3: Fallback using execCommand
1419
1919
  if (!copySuccess) {
1420
1920
  try {
1421
- console.log('[UIManager] Trying execCommand fallback...');
1921
+
1422
1922
  const textArea = document.createElement('textarea');
1423
1923
  textArea.value = messageText;
1424
1924
  textArea.style.position = 'fixed';
@@ -1435,7 +1935,7 @@ class VibeSurfUIManager {
1435
1935
 
1436
1936
  if (success) {
1437
1937
  copySuccess = true;
1438
- console.log('[UIManager] Copied using execCommand fallback');
1938
+
1439
1939
  } else {
1440
1940
  console.warn('[UIManager] execCommand returned false');
1441
1941
  }
@@ -1448,7 +1948,7 @@ class VibeSurfUIManager {
1448
1948
  if (copySuccess) {
1449
1949
  // Show visual feedback
1450
1950
  this.showCopyFeedback();
1451
- console.log('[UIManager] Copy operation completed successfully');
1951
+
1452
1952
  } else {
1453
1953
  throw new Error(`All clipboard methods failed. Last error: ${lastError?.message || 'Unknown error'}`);
1454
1954
  }
@@ -1668,9 +2168,12 @@ class VibeSurfUIManager {
1668
2168
  }
1669
2169
  }
1670
2170
 
1671
- updateLLMProfileSelect() {
2171
+ async updateLLMProfileSelect() {
1672
2172
  if (!this.elements.llmProfileSelect) return;
1673
2173
 
2174
+ // Preserve current user selection if any (to avoid overriding during profile updates)
2175
+ const currentSelection = this.elements.llmProfileSelect.value;
2176
+
1674
2177
  const profiles = this.settingsManager.getLLMProfiles();
1675
2178
  const select = this.elements.llmProfileSelect;
1676
2179
  select.innerHTML = '';
@@ -1691,16 +2194,50 @@ class VibeSurfUIManager {
1691
2194
  emptyOption.disabled = true;
1692
2195
  select.appendChild(emptyOption);
1693
2196
 
2197
+ // Determine selection priority: current selection > saved selection > default profile
2198
+ let targetSelection = currentSelection; // Preserve current selection first
2199
+
2200
+ // If no current selection, get saved selection
2201
+ if (!targetSelection) {
2202
+ try {
2203
+ if (this.userSettingsStorage) {
2204
+ targetSelection = await this.userSettingsStorage.getSelectedLlmProfile();
2205
+ }
2206
+ } catch (error) {
2207
+ console.error('[UIManager] Failed to get saved LLM profile selection:', error);
2208
+ }
2209
+ }
2210
+
1694
2211
  // Add actual profiles
2212
+ let hasSelectedProfile = false;
2213
+ let hasTargetProfile = false;
2214
+
1695
2215
  profiles.forEach(profile => {
1696
2216
  const option = document.createElement('option');
1697
2217
  option.value = profile.profile_name;
1698
2218
  option.textContent = profile.profile_name;
1699
- if (profile.is_default) {
1700
- option.selected = true;
2219
+
2220
+ // Check if this profile matches our target selection
2221
+ if (targetSelection && profile.profile_name === targetSelection) {
2222
+ hasTargetProfile = true;
1701
2223
  }
2224
+
1702
2225
  select.appendChild(option);
1703
2226
  });
2227
+
2228
+ // Apply selection based on priority
2229
+ if (hasTargetProfile) {
2230
+ select.value = targetSelection;
2231
+ hasSelectedProfile = true;
2232
+ } else {
2233
+ // Fall back to default profile if target not available
2234
+ const defaultProfile = profiles.find(p => p.is_default);
2235
+ if (defaultProfile) {
2236
+ select.value = defaultProfile.profile_name;
2237
+ hasSelectedProfile = true;
2238
+ }
2239
+ }
2240
+
1704
2241
  }
1705
2242
 
1706
2243
  // Update send button state if taskInput exists
@@ -1721,7 +2258,7 @@ class VibeSurfUIManager {
1721
2258
  confirmText: 'Open Settings',
1722
2259
  cancelText: 'Cancel',
1723
2260
  onConfirm: () => {
1724
- this.handleShowSettings();
2261
+ this.handleShowLLMSettings();
1725
2262
  }
1726
2263
  }
1727
2264
  : {
@@ -1731,15 +2268,61 @@ class VibeSurfUIManager {
1731
2268
  this.elements.llmProfileSelect?.focus();
1732
2269
  },
1733
2270
  onCancel: () => {
1734
- this.handleShowSettings();
2271
+ this.handleShowLLMSettings();
2272
+ }
2273
+ };
2274
+
2275
+ this.modalManager.showWarningModal(title, message, options);
2276
+ }
2277
+
2278
+ showVoiceProfileRequiredModal(action) {
2279
+ const isConfigureAction = action === 'configure';
2280
+ const title = isConfigureAction ? 'Voice Profile Required' : 'Please Select Voice Profile';
2281
+ const message = isConfigureAction
2282
+ ? 'No voice recognition (ASR) profiles are configured. You need to configure at least one voice profile before using voice input.'
2283
+ : 'Please configure a voice recognition profile to use voice input functionality.';
2284
+
2285
+ const options = isConfigureAction
2286
+ ? {
2287
+ confirmText: 'Open Voice Settings',
2288
+ cancelText: 'Cancel',
2289
+ onConfirm: () => {
2290
+ this.handleShowVoiceSettings();
2291
+ }
2292
+ }
2293
+ : {
2294
+ confirmText: 'Open Voice Settings',
2295
+ cancelText: 'Cancel',
2296
+ onConfirm: () => {
2297
+ this.handleShowVoiceSettings();
1735
2298
  }
1736
2299
  };
1737
2300
 
1738
2301
  this.modalManager.showWarningModal(title, message, options);
1739
2302
  }
1740
2303
 
2304
+ async handleShowVoiceSettings() {
2305
+ // Enhanced task running check
2306
+ const statusCheck = await this.checkTaskStatus();
2307
+ if (statusCheck.isRunning) {
2308
+ const canProceed = await this.showTaskRunningWarning('access voice settings');
2309
+ if (!canProceed) return;
2310
+ }
2311
+
2312
+ // Show settings and navigate directly to Voice profiles tab
2313
+ this.settingsManager.showSettings();
2314
+
2315
+ // Switch to Voice profiles tab after settings are shown
2316
+ setTimeout(() => {
2317
+ const voiceTab = document.querySelector('.settings-tab[data-tab="voice-profiles"]');
2318
+ if (voiceTab) {
2319
+ voiceTab.click();
2320
+ }
2321
+ }, 100);
2322
+ }
2323
+
1741
2324
  showLLMConnectionFailedModal(errorData) {
1742
- console.log('[UIManager] showLLMConnectionFailedModal called with:', errorData);
2325
+
1743
2326
 
1744
2327
  const llmProfile = errorData.llm_profile || 'unknown';
1745
2328
  const errorMessage = errorData.message || 'Cannot connect to LLM API';
@@ -1756,7 +2339,7 @@ class VibeSurfUIManager {
1756
2339
  }
1757
2340
  };
1758
2341
 
1759
- console.log('[UIManager] Showing LLM connection failed modal');
2342
+
1760
2343
  this.modalManager.showWarningModal(title, message, options);
1761
2344
  }
1762
2345
 
@@ -1825,20 +2408,132 @@ class VibeSurfUIManager {
1825
2408
  });
1826
2409
  }
1827
2410
 
2411
+ // Restore LLM profile selection from user settings storage
2412
+ async restoreLlmProfileSelection() {
2413
+ try {
2414
+ if (this.userSettingsStorage && this.elements.llmProfileSelect) {
2415
+ // Check current options available
2416
+ const availableOptions = Array.from(this.elements.llmProfileSelect.options).map(opt => opt.value);
2417
+
2418
+ const savedLlmProfile = await this.userSettingsStorage.getSelectedLlmProfile();
2419
+
2420
+ if (savedLlmProfile && savedLlmProfile.trim() !== '') {
2421
+ // Check if the saved profile exists in the current options
2422
+ const option = this.elements.llmProfileSelect.querySelector(`option[value="${savedLlmProfile}"]`);
2423
+
2424
+ if (option) {
2425
+ this.elements.llmProfileSelect.value = savedLlmProfile;
2426
+ } else {
2427
+ console.warn('[UIManager] Saved LLM profile not found in current options:', savedLlmProfile);
2428
+ }
2429
+ } else {
2430
+ // Check localStorage backup for browser restart scenarios
2431
+ const backupProfile = localStorage.getItem('vibesurf-llm-profile-backup');
2432
+
2433
+ if (backupProfile) {
2434
+ const option = this.elements.llmProfileSelect.querySelector(`option[value="${backupProfile}"]`);
2435
+ if (option) {
2436
+ this.elements.llmProfileSelect.value = backupProfile;
2437
+ // Also save it back to Chrome storage
2438
+ await this.userSettingsStorage.setSelectedLlmProfile(backupProfile);
2439
+ } else {
2440
+ console.warn('[UIManager] Backup profile not found in current options:', backupProfile);
2441
+ }
2442
+ }
2443
+ }
2444
+ } else {
2445
+ console.warn('[UIManager] Required components not available - userSettingsStorage:', !!this.userSettingsStorage, 'llmProfileSelect:', !!this.elements.llmProfileSelect);
2446
+ }
2447
+ } catch (error) {
2448
+ console.error('[UIManager] Failed to restore LLM profile selection:', error);
2449
+ }
2450
+ }
2451
+
2452
+ // Check and update voice button state based on ASR profile availability
2453
+ async updateVoiceButtonState() {
2454
+ if (!this.elements.voiceRecordBtn) return;
2455
+
2456
+ try {
2457
+ const isVoiceAvailable = await this.voiceRecorder.isVoiceRecordingAvailable();
2458
+
2459
+ if (!isVoiceAvailable) {
2460
+ // Add visual indication but keep button enabled for click handling
2461
+ this.elements.voiceRecordBtn.classList.add('voice-disabled');
2462
+ this.elements.voiceRecordBtn.setAttribute('title', 'Voice input disabled - No ASR profiles configured. Click to configure.');
2463
+ this.elements.voiceRecordBtn.setAttribute('data-tooltip', 'Voice input disabled - No ASR profiles configured. Click to configure.');
2464
+ } else {
2465
+ // Remove visual indication and restore normal tooltip
2466
+ this.elements.voiceRecordBtn.classList.remove('voice-disabled');
2467
+ if (!this.elements.voiceRecordBtn.classList.contains('recording')) {
2468
+ this.elements.voiceRecordBtn.setAttribute('title', 'Click to start voice recording');
2469
+ this.elements.voiceRecordBtn.setAttribute('data-tooltip', 'Click to start voice recording');
2470
+ }
2471
+ }
2472
+ } catch (error) {
2473
+ console.error('[UIManager] Error updating voice button state:', error);
2474
+ // Fallback: add visual indication but keep button enabled
2475
+ this.elements.voiceRecordBtn.classList.add('voice-disabled');
2476
+ this.elements.voiceRecordBtn.setAttribute('title', 'Voice input temporarily unavailable. Click for more info.');
2477
+ this.elements.voiceRecordBtn.setAttribute('data-tooltip', 'Voice input temporarily unavailable. Click for more info.');
2478
+ }
2479
+ }
2480
+
1828
2481
  // Initialization
1829
2482
  async initialize() {
1830
2483
  try {
2484
+ // Check for microphone permission parameter first (like Doubao AI)
2485
+ const urlParams = new URLSearchParams(window.location.search);
2486
+ const enterParam = urlParams.get('enter');
2487
+
2488
+ if (enterParam === 'mic-permission') {
2489
+ console.log('[UIManager] Detected microphone permission request parameter');
2490
+ this.showMicrophonePermissionRequest();
2491
+ return; // Skip normal initialization for permission request
2492
+ }
2493
+
1831
2494
  this.showLoading('Initializing VibeSurf...');
1832
2495
 
2496
+ // Initialize user settings storage first
2497
+ if (this.userSettingsStorage) {
2498
+
2499
+ await this.userSettingsStorage.initialize();
2500
+
2501
+ } else {
2502
+ console.error('[UIManager] userSettingsStorage not available during initialization');
2503
+ }
2504
+
1833
2505
  // Load settings data through settings manager
2506
+
1834
2507
  await this.settingsManager.loadSettingsData();
1835
2508
 
2509
+
2510
+ // Now restore user selections AFTER profiles are loaded but before any other initialization
2511
+
2512
+ this.isRestoringSelections = true;
2513
+
2514
+ try {
2515
+ // Restore LLM profile selection first
2516
+ await this.restoreLlmProfileSelection();
2517
+
2518
+ // Restore agent mode selection
2519
+ await this.restoreAgentModeSelection();
2520
+ } finally {
2521
+ this.isRestoringSelections = false;
2522
+ }
2523
+
2524
+ // Check and update voice button state based on ASR profile availability
2525
+ await this.updateVoiceButtonState();
2526
+
2527
+
1836
2528
  // Create initial session if none exists
2529
+
1837
2530
  if (!this.sessionManager.getCurrentSession()) {
2531
+
1838
2532
  await this.sessionManager.createSession();
1839
2533
  }
1840
2534
 
1841
2535
  this.hideLoading();
2536
+
1842
2537
  } catch (error) {
1843
2538
  this.hideLoading();
1844
2539
  console.error('[UIManager] Initialization failed:', error);
@@ -1846,47 +2541,204 @@ class VibeSurfUIManager {
1846
2541
  }
1847
2542
  }
1848
2543
 
1849
- // Cleanup
1850
- destroy() {
1851
- // Stop task status monitoring
1852
- this.stopTaskStatusMonitoring();
2544
+ // Show microphone permission request (like Doubao AI) - simplified approach
2545
+ showMicrophonePermissionRequest() {
2546
+ console.log('[UIManager] Showing simplified microphone permission request');
2547
+ console.log('[UIManager] Current URL:', window.location.href);
2548
+ console.log('[UIManager] URL params:', window.location.search);
1853
2549
 
1854
- // Cleanup managers
1855
- if (this.settingsManager) {
1856
- // Cleanup settings manager if it has destroy method
1857
- if (typeof this.settingsManager.destroy === 'function') {
1858
- this.settingsManager.destroy();
1859
- }
1860
- }
2550
+ // Simple approach: just try to get permission directly
2551
+ this.requestMicrophonePermissionDirectly();
2552
+ }
2553
+
2554
+ // Direct microphone permission request (simplified like Doubao AI)
2555
+ async requestMicrophonePermissionDirectly() {
2556
+ console.log('[UIManager] Requesting microphone permission directly');
1861
2557
 
1862
- if (this.historyManager) {
1863
- // Cleanup history manager if it has destroy method
1864
- if (typeof this.historyManager.destroy === 'function') {
1865
- this.historyManager.destroy();
1866
- }
2558
+ try {
2559
+ // Immediately try to get microphone permission
2560
+ console.log('[UIManager] Calling getUserMedia...');
2561
+ console.log('[UIManager] User agent:', navigator.userAgent);
2562
+ console.log('[UIManager] Location protocol:', window.location.protocol);
2563
+ console.log('[UIManager] Location href:', window.location.href);
2564
+
2565
+ const stream = await navigator.mediaDevices.getUserMedia({
2566
+ audio: true,
2567
+ video: false
2568
+ });
2569
+
2570
+ console.log('[UIManager] Microphone permission granted!');
2571
+
2572
+ // Stop the stream immediately (we just need permission)
2573
+ stream.getTracks().forEach(track => track.stop());
2574
+
2575
+ // Send success message back to original tab
2576
+ console.log('[UIManager] Sending success message...');
2577
+ chrome.runtime.sendMessage({
2578
+ type: 'MICROPHONE_PERMISSION_RESULT',
2579
+ granted: true
2580
+ }, (response) => {
2581
+ console.log('[UIManager] Success message response:', response);
2582
+ });
2583
+
2584
+ // Close this tab after a short delay
2585
+ setTimeout(() => {
2586
+ console.log('[UIManager] Closing tab...');
2587
+ window.close();
2588
+ }, 1000);
2589
+
2590
+ } catch (error) {
2591
+ console.error('[UIManager] Microphone permission denied:', error);
2592
+ console.log('[UIManager] Error name:', error.name);
2593
+ console.log('[UIManager] Error message:', error.message);
2594
+ console.log('[UIManager] Error stack:', error.stack);
2595
+
2596
+ // Send error message back to original tab
2597
+ console.log('[UIManager] Sending error message...');
2598
+ chrome.runtime.sendMessage({
2599
+ type: 'MICROPHONE_PERMISSION_RESULT',
2600
+ granted: false,
2601
+ error: error.message,
2602
+ errorName: error.name
2603
+ }, (response) => {
2604
+ console.log('[UIManager] Error message response:', response);
2605
+ });
2606
+
2607
+ // Close this tab after a short delay
2608
+ setTimeout(() => {
2609
+ console.log('[UIManager] Closing tab after error...');
2610
+ window.close();
2611
+ }, 2000);
1867
2612
  }
1868
-
1869
- if (this.fileManager) {
1870
- // Cleanup file manager if it has destroy method
1871
- if (typeof this.fileManager.destroy === 'function') {
1872
- this.fileManager.destroy();
2613
+ }
2614
+ // Restore agent mode selection from user settings storage
2615
+ async restoreAgentModeSelection() {
2616
+ try {
2617
+
2618
+ if (this.userSettingsStorage && this.elements.agentModeSelect) {
2619
+ // Check current options available
2620
+ const availableOptions = Array.from(this.elements.agentModeSelect.options).map(opt => opt.value);
2621
+
2622
+ const savedAgentMode = await this.userSettingsStorage.getSelectedAgentMode();
2623
+
2624
+ if (savedAgentMode && savedAgentMode.trim() !== '') {
2625
+ // Restore any saved agent mode, including 'thinking'
2626
+ this.elements.agentModeSelect.value = savedAgentMode;
2627
+ // Ensure the option is actually selected
2628
+ const option = this.elements.agentModeSelect.querySelector(`option[value="${savedAgentMode}"]`);
2629
+ if (option) {
2630
+ option.selected = true;
2631
+ console.log('[UIManager] Restored agent mode selection:', savedAgentMode);
2632
+ } else {
2633
+ console.warn('[UIManager] Agent mode option not found:', savedAgentMode);
2634
+ }
2635
+ } else {
2636
+ // Check localStorage backup for browser restart scenarios
2637
+ const backupMode = localStorage.getItem('vibesurf-agent-mode-backup');
2638
+
2639
+ if (backupMode) {
2640
+ const option = this.elements.agentModeSelect.querySelector(`option[value="${backupMode}"]`);
2641
+ if (option) {
2642
+ this.elements.agentModeSelect.value = backupMode;
2643
+ // Also save it back to Chrome storage
2644
+ await this.userSettingsStorage.setSelectedAgentMode(backupMode);
2645
+ } else {
2646
+ console.warn('[UIManager] Backup agent mode not found in current options:', backupMode);
2647
+ }
2648
+ }
2649
+ }
2650
+ } else {
2651
+ console.warn('[UIManager] Required components not available - userSettingsStorage:', !!this.userSettingsStorage, 'agentModeSelect:', !!this.elements.agentModeSelect);
1873
2652
  }
2653
+ } catch (error) {
2654
+ console.error('[UIManager] Failed to restore agent mode selection:', error);
2655
+ }
2656
+ }
2657
+
2658
+ // Cleanup
2659
+ destroy() {
2660
+ // Prevent multiple cleanup calls
2661
+ if (this.isDestroying) {
2662
+ console.log('[UIManager] Cleanup already in progress, skipping...');
2663
+ return;
1874
2664
  }
1875
2665
 
1876
- if (this.modalManager) {
1877
- // Cleanup modal manager if it has destroy method
1878
- if (typeof this.modalManager.destroy === 'function') {
1879
- this.modalManager.destroy();
2666
+ this.isDestroying = true;
2667
+ console.log('[UIManager] Destroying UI manager...');
2668
+
2669
+ try {
2670
+ // Stop task status monitoring
2671
+ this.stopTaskStatusMonitoring();
2672
+
2673
+ // Cleanup voice recorder
2674
+ if (this.voiceRecorder) {
2675
+ this.voiceRecorder.cleanup();
2676
+ this.voiceRecorder = null;
1880
2677
  }
2678
+
2679
+ // Cleanup managers with error handling
2680
+ if (this.settingsManager) {
2681
+ try {
2682
+ if (typeof this.settingsManager.destroy === 'function') {
2683
+ this.settingsManager.destroy();
2684
+ }
2685
+ } catch (error) {
2686
+ console.error('[UIManager] Error destroying settings manager:', error);
2687
+ }
2688
+ this.settingsManager = null;
2689
+ }
2690
+
2691
+ if (this.historyManager) {
2692
+ try {
2693
+ if (typeof this.historyManager.destroy === 'function') {
2694
+ this.historyManager.destroy();
2695
+ }
2696
+ } catch (error) {
2697
+ console.error('[UIManager] Error destroying history manager:', error);
2698
+ }
2699
+ this.historyManager = null;
2700
+ }
2701
+
2702
+ if (this.fileManager) {
2703
+ try {
2704
+ if (typeof this.fileManager.destroy === 'function') {
2705
+ this.fileManager.destroy();
2706
+ }
2707
+ } catch (error) {
2708
+ console.error('[UIManager] Error destroying file manager:', error);
2709
+ }
2710
+ this.fileManager = null;
2711
+ }
2712
+
2713
+ if (this.modalManager) {
2714
+ try {
2715
+ if (typeof this.modalManager.destroy === 'function') {
2716
+ this.modalManager.destroy();
2717
+ }
2718
+ } catch (error) {
2719
+ console.error('[UIManager] Error destroying modal manager:', error);
2720
+ }
2721
+ this.modalManager = null;
2722
+ }
2723
+
2724
+ // Clear state
2725
+ this.state = {
2726
+ isLoading: false,
2727
+ isTaskRunning: false,
2728
+ taskInfo: null
2729
+ };
2730
+
2731
+ console.log('[UIManager] UI manager cleanup complete');
2732
+ } catch (error) {
2733
+ console.error('[UIManager] Error during destroy:', error);
2734
+ } finally {
2735
+ this.isDestroying = false;
1881
2736
  }
1882
-
1883
- // Clear state
1884
- this.state.currentModal = null;
1885
2737
  }
1886
2738
 
1887
2739
  // Tab Selector Methods
1888
2740
  initializeTabSelector() {
1889
- console.log('[UIManager] Initializing tab selector...');
2741
+
1890
2742
 
1891
2743
  // Initialize tab selector state
1892
2744
  this.tabSelectorState = {
@@ -1896,20 +2748,15 @@ class VibeSurfUIManager {
1896
2748
  atPosition: -1 // Position where @ was typed
1897
2749
  };
1898
2750
 
1899
- console.log('[UIManager] Tab selector state initialized:', this.tabSelectorState);
2751
+
1900
2752
 
1901
2753
  // Bind tab selector events
1902
2754
  this.bindTabSelectorEvents();
1903
2755
  }
1904
2756
 
1905
2757
  bindTabSelectorEvents() {
1906
- console.log('[UIManager] Binding tab selector events...');
1907
- console.log('[UIManager] Available elements for binding:', {
1908
- tabSelectorCancel: !!this.elements.tabSelectorCancel,
1909
- tabSelectorConfirm: !!this.elements.tabSelectorConfirm,
1910
- selectAllTabs: !!this.elements.selectAllTabs,
1911
- tabSelectorDropdown: !!this.elements.tabSelectorDropdown
1912
- });
2758
+
2759
+
1913
2760
 
1914
2761
  // Select all radio button
1915
2762
  this.elements.selectAllTabs?.addEventListener('change', this.handleSelectAllTabs.bind(this));
@@ -1924,7 +2771,7 @@ class VibeSurfUIManager {
1924
2771
  }
1925
2772
  });
1926
2773
 
1927
- console.log('[UIManager] Tab selector events bound successfully');
2774
+
1928
2775
  }
1929
2776
 
1930
2777
  handleTabSelectorInput(event) {
@@ -1937,16 +2784,11 @@ class VibeSurfUIManager {
1937
2784
  const inputValue = event.target.value;
1938
2785
  const cursorPosition = event.target.selectionStart;
1939
2786
 
1940
- console.log('[UIManager] Tab selector input check:', {
1941
- inputValue,
1942
- cursorPosition,
1943
- charAtCursor: inputValue[cursorPosition - 1],
1944
- isAtSymbol: inputValue[cursorPosition - 1] === '@'
1945
- });
2787
+
1946
2788
 
1947
2789
  // Check if @ was just typed
1948
2790
  if (inputValue[cursorPosition - 1] === '@') {
1949
- console.log('[UIManager] @ detected, showing tab selector');
2791
+
1950
2792
  this.tabSelectorState.atPosition = cursorPosition - 1;
1951
2793
  this.showTabSelector();
1952
2794
  } else if (this.tabSelectorState.isVisible) {
@@ -1954,7 +2796,7 @@ class VibeSurfUIManager {
1954
2796
  if (this.tabSelectorState.atPosition >= 0 &&
1955
2797
  (this.tabSelectorState.atPosition >= inputValue.length ||
1956
2798
  inputValue[this.tabSelectorState.atPosition] !== '@')) {
1957
- console.log('[UIManager] @ deleted, hiding tab selector');
2799
+
1958
2800
  this.hideTabSelector();
1959
2801
  return;
1960
2802
  }
@@ -1962,19 +2804,15 @@ class VibeSurfUIManager {
1962
2804
  // Hide tab selector if user continues typing after @
1963
2805
  const textAfterAt = inputValue.substring(this.tabSelectorState.atPosition + 1, cursorPosition);
1964
2806
  if (textAfterAt.length > 0 && !textAfterAt.match(/^[\s]*$/)) {
1965
- console.log('[UIManager] Hiding tab selector due to continued typing');
2807
+
1966
2808
  this.hideTabSelector();
1967
2809
  }
1968
2810
  }
1969
2811
  }
1970
2812
 
1971
2813
  async showTabSelector() {
1972
- console.log('[UIManager] showTabSelector called');
1973
- console.log('[UIManager] Tab selector elements:', {
1974
- dropdown: !!this.elements.tabSelectorDropdown,
1975
- taskInput: !!this.elements.taskInput,
1976
- tabOptionsList: !!this.elements.tabOptionsList
1977
- });
2814
+
2815
+
1978
2816
 
1979
2817
  if (!this.elements.tabSelectorDropdown || !this.elements.taskInput) {
1980
2818
  console.error('[UIManager] Tab selector elements not found', {
@@ -1985,15 +2823,15 @@ class VibeSurfUIManager {
1985
2823
  }
1986
2824
 
1987
2825
  try {
1988
- console.log('[UIManager] Fetching tab data...');
2826
+
1989
2827
  // Fetch tab data from backend
1990
2828
  await this.populateTabSelector();
1991
2829
 
1992
- console.log('[UIManager] Positioning dropdown...');
2830
+
1993
2831
  // Position the dropdown relative to the input
1994
2832
  this.positionTabSelector();
1995
2833
 
1996
- console.log('[UIManager] Showing dropdown...');
2834
+
1997
2835
  // Show the dropdown with explicit visibility
1998
2836
  this.elements.tabSelectorDropdown.classList.remove('hidden');
1999
2837
  this.elements.tabSelectorDropdown.style.display = 'block';
@@ -2001,16 +2839,10 @@ class VibeSurfUIManager {
2001
2839
  this.elements.tabSelectorDropdown.style.opacity = '1';
2002
2840
  this.tabSelectorState.isVisible = true;
2003
2841
 
2004
- console.log('[UIManager] Tab selector shown successfully');
2005
- console.log('[UIManager] Classes:', this.elements.tabSelectorDropdown.className);
2006
- console.log('[UIManager] Computed styles:', {
2007
- display: getComputedStyle(this.elements.tabSelectorDropdown).display,
2008
- visibility: getComputedStyle(this.elements.tabSelectorDropdown).visibility,
2009
- opacity: getComputedStyle(this.elements.tabSelectorDropdown).opacity,
2010
- zIndex: getComputedStyle(this.elements.tabSelectorDropdown).zIndex,
2011
- position: getComputedStyle(this.elements.tabSelectorDropdown).position
2012
- });
2013
- console.log('[UIManager] Dropdown content HTML:', this.elements.tabSelectorDropdown.innerHTML);
2842
+
2843
+
2844
+
2845
+
2014
2846
  } catch (error) {
2015
2847
  console.error('[UIManager] Failed to show tab selector:', error);
2016
2848
  this.showNotification('Failed to load browser tabs', 'error');
@@ -2026,7 +2858,7 @@ class VibeSurfUIManager {
2026
2858
  this.tabSelectorState.selectedTabs = [];
2027
2859
  this.tabSelectorState.atPosition = -1;
2028
2860
 
2029
- console.log('[UIManager] Tab selector hidden');
2861
+
2030
2862
  }
2031
2863
 
2032
2864
  positionTabSelector() {
@@ -2035,10 +2867,7 @@ class VibeSurfUIManager {
2035
2867
  const inputRect = this.elements.taskInput.getBoundingClientRect();
2036
2868
  const dropdown = this.elements.tabSelectorDropdown;
2037
2869
 
2038
- console.log('[UIManager] Positioning dropdown:', {
2039
- inputRect,
2040
- dropdownElement: dropdown
2041
- });
2870
+
2042
2871
 
2043
2872
  // Calculate 90% width of input
2044
2873
  const dropdownWidth = inputRect.width * 0.9;
@@ -2052,47 +2881,32 @@ class VibeSurfUIManager {
2052
2881
  dropdown.style.maxHeight = '300px';
2053
2882
  dropdown.style.overflowY = 'auto';
2054
2883
 
2055
- console.log('[UIManager] Dropdown positioned with styles:', {
2056
- position: dropdown.style.position,
2057
- bottom: dropdown.style.bottom,
2058
- left: dropdown.style.left,
2059
- width: dropdown.style.width,
2060
- zIndex: dropdown.style.zIndex
2061
- });
2884
+
2062
2885
  }
2063
2886
 
2064
2887
  async populateTabSelector() {
2065
2888
  try {
2066
- console.log('[UIManager] Getting tab data from backend...');
2889
+
2067
2890
  // Get all tabs and active tab from backend
2068
2891
  const [allTabsResponse, activeTabResponse] = await Promise.all([
2069
2892
  this.apiClient.getAllBrowserTabs(),
2070
2893
  this.apiClient.getActiveBrowserTab()
2071
2894
  ]);
2072
2895
 
2073
- console.log('[UIManager] Raw API responses:', {
2074
- allTabsResponse: JSON.stringify(allTabsResponse, null, 2),
2075
- activeTabResponse: JSON.stringify(activeTabResponse, null, 2)
2076
- });
2896
+
2077
2897
 
2078
2898
  const allTabs = allTabsResponse.tabs || allTabsResponse || {};
2079
2899
  const activeTab = activeTabResponse.tab || activeTabResponse || {};
2080
2900
  const activeTabId = Object.keys(activeTab)[0];
2081
2901
 
2082
- console.log('[UIManager] Processed tab data:', {
2083
- allTabsCount: Object.keys(allTabs).length,
2084
- activeTabId,
2085
- allTabIds: Object.keys(allTabs),
2086
- allTabsData: allTabs,
2087
- activeTabData: activeTab
2088
- });
2902
+
2089
2903
 
2090
2904
  this.tabSelectorState.allTabs = allTabs;
2091
2905
 
2092
2906
  // Clear existing options
2093
2907
  if (this.elements.tabOptionsList) {
2094
2908
  this.elements.tabOptionsList.innerHTML = '';
2095
- console.log('[UIManager] Cleared existing tab options');
2909
+
2096
2910
  } else {
2097
2911
  console.error('[UIManager] tabOptionsList element not found!');
2098
2912
  return;
@@ -2109,7 +2923,7 @@ class VibeSurfUIManager {
2109
2923
 
2110
2924
  Object.entries(testTabs).forEach(([tabId, tabInfo]) => {
2111
2925
  const isActive = tabId === 'test-1';
2112
- console.log('[UIManager] Creating test tab option:', { tabId, title: tabInfo.title, isActive });
2926
+
2113
2927
  const option = this.createTabOption(tabId, tabInfo, isActive);
2114
2928
  this.elements.tabOptionsList.appendChild(option);
2115
2929
  });
@@ -2119,7 +2933,7 @@ class VibeSurfUIManager {
2119
2933
  // Add real tab options
2120
2934
  Object.entries(allTabs).forEach(([tabId, tabInfo]) => {
2121
2935
  const isActive = tabId === activeTabId;
2122
- console.log('[UIManager] Creating tab option:', { tabId, title: tabInfo.title, isActive });
2936
+
2123
2937
  const option = this.createTabOption(tabId, tabInfo, isActive);
2124
2938
  this.elements.tabOptionsList.appendChild(option);
2125
2939
  });
@@ -2130,7 +2944,7 @@ class VibeSurfUIManager {
2130
2944
  this.elements.selectAllTabs.checked = false;
2131
2945
  }
2132
2946
 
2133
- console.log('[UIManager] Tab selector populated with', Object.keys(this.tabSelectorState.allTabs).length, 'tabs');
2947
+
2134
2948
  } catch (error) {
2135
2949
  console.error('[UIManager] Failed to populate tab selector:', error);
2136
2950
  throw error;
@@ -2170,7 +2984,7 @@ class VibeSurfUIManager {
2170
2984
  // For radio buttons, replace the selected tabs array with just this tab
2171
2985
  this.tabSelectorState.selectedTabs = [tabId];
2172
2986
 
2173
- console.log('[UIManager] Selected tab:', tabId);
2987
+
2174
2988
 
2175
2989
  // Auto-confirm selection immediately
2176
2990
  this.confirmTabSelection();
@@ -2183,7 +2997,7 @@ class VibeSurfUIManager {
2183
2997
  const allTabIds = Object.keys(this.tabSelectorState.allTabs);
2184
2998
  this.tabSelectorState.selectedTabs = allTabIds;
2185
2999
 
2186
- console.log('[UIManager] Select all tabs:', allTabIds);
3000
+
2187
3001
 
2188
3002
  // Auto-confirm selection immediately
2189
3003
  this.confirmTabSelection();