vibesurf 0.1.12__py3-none-any.whl → 0.1.13__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.

vibe_surf/_version.py CHANGED
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.1.12'
32
- __version_tuple__ = version_tuple = (0, 1, 12)
31
+ __version__ = version = '0.1.13'
32
+ __version_tuple__ = version_tuple = (0, 1, 13)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -22,7 +22,6 @@ from ..shared_state import (
22
22
  is_task_running,
23
23
  get_active_task_info,
24
24
  clear_active_task,
25
- browser_manager
26
25
  )
27
26
 
28
27
  from vibe_surf.logger import get_logger
@@ -49,7 +48,7 @@ async def submit_task(
49
48
  ):
50
49
  """Submit new task for execution (single task mode)"""
51
50
  from ..database.queries import LLMProfileQueries
52
- from ..shared_state import workspace_dir, active_task
51
+ from ..shared_state import workspace_dir, active_task, llm, current_llm_profile_name
53
52
 
54
53
  # Check if task is already running
55
54
  if is_task_running():
@@ -74,6 +73,18 @@ async def submit_task(
74
73
  detail=f"LLM profile '{task_request.llm_profile_name}' not found"
75
74
  )
76
75
 
76
+ # Initialize LLM for this task if needed
77
+ if not current_llm_profile_name or current_llm_profile_name != task_request.llm_profile_name:
78
+ current_llm_profile_name = task_request.llm_profile_name
79
+ success, message = await _ensure_llm_initialized(llm_profile)
80
+ if not success:
81
+ active_task = None
82
+ return {
83
+ "success": False,
84
+ "error": "llm_connection_failed",
85
+ "message": f"Cannot connect to LLM API: {message}",
86
+ "llm_profile": task_request.llm_profile_name
87
+ }
77
88
  # Generate task ID
78
89
  task_id = uuid7str()
79
90
 
@@ -106,10 +117,6 @@ async def submit_task(
106
117
  )
107
118
  await db.commit()
108
119
 
109
- # Initialize LLM for this task if needed
110
- if not active_task or active_task["llm_profile_name"] != task_request.llm_profile_name:
111
- await _ensure_llm_initialized(llm_profile)
112
-
113
120
  # Add background task
114
121
  background_tasks.add_task(
115
122
  execute_task_background,
@@ -139,20 +146,46 @@ async def submit_task(
139
146
 
140
147
 
141
148
  async def _ensure_llm_initialized(llm_profile):
142
- """Ensure LLM is initialized with the specified profile"""
149
+ """Ensure LLM is initialized with the specified profile and test connectivity"""
143
150
  from ..utils.llm_factory import create_llm_from_profile
144
151
  from ..shared_state import vibesurf_agent
152
+ from browser_use.llm import UserMessage
145
153
 
146
154
  if not vibesurf_agent:
147
155
  raise HTTPException(status_code=503, detail="VibeSurf agent not initialized")
148
156
 
149
- # Always create new LLM instance to ensure we're using the right profile
150
- new_llm = create_llm_from_profile(llm_profile)
157
+ try:
158
+ # Always create new LLM instance to ensure we're using the right profile
159
+ new_llm = create_llm_from_profile(llm_profile)
151
160
 
152
- # Update vibesurf agent's LLM and register with token cost service
153
- if vibesurf_agent and vibesurf_agent.token_cost_service:
154
- vibesurf_agent.llm = vibesurf_agent.token_cost_service.register_llm(new_llm)
155
- logger.info(f"LLM updated and registered for token tracking for profile: {llm_profile['profile_name']}")
161
+ # Test LLM connectivity with a simple question
162
+ test_message = UserMessage(content='What is the capital of France? Answer in one word.')
163
+
164
+ logger.info(f"Testing LLM connectivity for profile: {llm_profile['profile_name']}")
165
+ response = await new_llm.ainvoke([test_message])
166
+
167
+ # Check if response contains expected answer
168
+ if not response or not hasattr(response, 'completion'):
169
+ return False, f"LLM response validation failed: No completion content received"
170
+
171
+ completion = response.completion.lower() if response.completion else ""
172
+ if 'paris' not in completion:
173
+ logger.warning(f"LLM connectivity test returned unexpected answer: {response.completion}")
174
+ # Still continue if we got some response, just log the warning
175
+
176
+ logger.info(f"LLM connectivity test successful for profile: {llm_profile['profile_name']}")
177
+
178
+ # Update vibesurf agent's LLM and register with token cost service
179
+ if vibesurf_agent and vibesurf_agent.token_cost_service:
180
+ vibesurf_agent.llm = vibesurf_agent.token_cost_service.register_llm(new_llm)
181
+ logger.info(f"LLM updated and registered for token tracking for profile: {llm_profile['profile_name']}")
182
+
183
+ return True, "LLM initialized and tested successfully"
184
+
185
+ except Exception as e:
186
+ error_msg = f"LLM connectivity test failed: {str(e)}"
187
+ logger.error(error_msg)
188
+ return False, error_msg
156
189
 
157
190
 
158
191
  @router.post("/pause")
@@ -33,6 +33,7 @@ browser_manager: Optional[BrowserManager] = None
33
33
  vibesurf_tools: Optional[VibeSurfTools] = None
34
34
  llm: Optional[BaseChatModel] = None
35
35
  db_manager: Optional['DatabaseManager'] = None
36
+ current_llm_profile_name: Optional[str] = None
36
37
 
37
38
  # Environment variables
38
39
  workspace_dir: str = ""
@@ -51,7 +52,7 @@ active_task: Optional[Dict[str, Any]] = None
51
52
 
52
53
  def get_all_components():
53
54
  """Get all components as a dictionary"""
54
- global vibesurf_agent, browser_manager, vibesurf_tools, llm, db_manager
55
+ global vibesurf_agent, browser_manager, vibesurf_tools, llm, db_manager, current_llm_profile_name
55
56
  global workspace_dir, browser_execution_path, browser_user_data, active_mcp_server, envs
56
57
 
57
58
  return {
@@ -65,13 +66,14 @@ def get_all_components():
65
66
  "browser_user_data": browser_user_data,
66
67
  "active_mcp_server": active_mcp_server,
67
68
  "active_task": active_task,
69
+ "current_llm_profile_name": current_llm_profile_name,
68
70
  "envs": envs
69
71
  }
70
72
 
71
73
 
72
74
  def set_components(**kwargs):
73
75
  """Update global components"""
74
- global vibesurf_agent, browser_manager, vibesurf_tools, llm, db_manager
76
+ global vibesurf_agent, browser_manager, vibesurf_tools, llm, db_manager, current_llm_profile_name
75
77
  global workspace_dir, browser_execution_path, browser_user_data, active_mcp_server, envs
76
78
 
77
79
  if "vibesurf_agent" in kwargs:
@@ -94,6 +96,8 @@ def set_components(**kwargs):
94
96
  active_mcp_server = kwargs["active_mcp_server"]
95
97
  if "envs" in kwargs:
96
98
  envs = kwargs["envs"]
99
+ if "current_llm_profile_name" in kwargs:
100
+ envs = kwargs["current_llm_profile_name"]
97
101
 
98
102
 
99
103
  async def execute_task_background(
@@ -105,9 +109,11 @@ async def execute_task_background(
105
109
  db_session=None
106
110
  ):
107
111
  """Background task execution function for single task with LLM profile support"""
108
- global vibesurf_agent, active_task
112
+ global vibesurf_agent, active_task, current_llm_profile_name
109
113
 
110
114
  try:
115
+ current_llm_profile_name = llm_profile_name
116
+
111
117
  # Check if MCP server configuration needs update
112
118
  await _check_and_update_mcp_servers(db_session)
113
119
 
@@ -314,7 +320,7 @@ async def _load_active_mcp_servers():
314
320
 
315
321
  async def initialize_vibesurf_components():
316
322
  """Initialize VibeSurf components from environment variables and default LLM profile"""
317
- global vibesurf_agent, browser_manager, vibesurf_tools, llm, db_manager
323
+ global vibesurf_agent, browser_manager, vibesurf_tools, llm, db_manager, current_llm_profile_name
318
324
  global workspace_dir, browser_execution_path, browser_user_data, envs
319
325
  from vibe_surf import common
320
326
 
@@ -463,7 +469,7 @@ async def initialize_vibesurf_components():
463
469
 
464
470
  async def _initialize_default_llm():
465
471
  """Initialize LLM from default profile or fallback to environment variables"""
466
- global db_manager
472
+ global db_manager, current_llm_profile_name
467
473
 
468
474
  try:
469
475
  # Try to get default LLM profile from database
@@ -482,6 +488,7 @@ async def _initialize_default_llm():
482
488
  )
483
489
  if profile_with_key:
484
490
  llm_instance = create_llm_from_profile(profile_with_key)
491
+ current_llm_profile_name = default_profile.profile_name
485
492
  logger.info(f"✅ LLM initialized from default profile: {default_profile.profile_name}")
486
493
  return llm_instance
487
494
  break
@@ -4,6 +4,15 @@ LLM Factory utilities for creating LLM instances from profiles
4
4
 
5
5
  from typing import Optional
6
6
  import logging
7
+ # Import LLM classes from browser_use and vibe_surf
8
+ from browser_use.llm import (
9
+ BaseChatModel,
10
+ ChatOpenAI, ChatAnthropic, ChatGoogle, ChatAzureOpenAI,
11
+ ChatGroq, ChatOllama, ChatOpenRouter, ChatDeepSeek,
12
+ ChatAWSBedrock, ChatAnthropicBedrock
13
+ )
14
+ from vibe_surf.llm import ChatOpenAICompatible
15
+
7
16
  from ..llm_config import get_supported_providers, is_provider_supported
8
17
 
9
18
  from vibe_surf.logger import get_logger
@@ -11,16 +20,9 @@ from vibe_surf.logger import get_logger
11
20
  logger = get_logger(__name__)
12
21
 
13
22
 
14
- def create_llm_from_profile(llm_profile):
23
+ def create_llm_from_profile(llm_profile) -> BaseChatModel:
15
24
  """Create LLM instance from LLMProfile database record (dict or object)"""
16
25
  try:
17
- # Import LLM classes from browser_use and vibe_surf
18
- from browser_use.llm import (
19
- ChatOpenAI, ChatAnthropic, ChatGoogle, ChatAzureOpenAI,
20
- ChatGroq, ChatOllama, ChatOpenRouter, ChatDeepSeek,
21
- ChatAWSBedrock, ChatAnthropicBedrock
22
- )
23
- from vibe_surf.llm import ChatOpenAICompatible
24
26
 
25
27
  # Handle both dict and object access patterns
26
28
  def get_attr(obj, key, default=None):
@@ -99,6 +99,12 @@ class VibeSurfAPIClient {
99
99
  if (error.status >= 400 && error.status < 500) {
100
100
  throw error; // Client errors shouldn't be retried
101
101
  }
102
+
103
+ // Don't retry on LLM connection failures
104
+ if (error.data && error.data.error === 'llm_connection_failed') {
105
+ console.log('[API] LLM connection failed - skipping retry');
106
+ throw error;
107
+ }
102
108
  }
103
109
 
104
110
  // Don't retry on timeout for the last attempt
@@ -61,20 +61,24 @@ class VibeSurfModalManager {
61
61
 
62
62
  const modalId = this.generateModalId();
63
63
 
64
+ // Add warning icon to title and always show cancel for warning modals
65
+ const warningTitle = `⚠️ ${title}`;
66
+ const shouldShowCancel = showCancel || true; // Always show cancel for warning modals
67
+
64
68
  const modalHTML = `
65
- <div class="modal-overlay" id="${modalId}-overlay">
69
+ <div class="modal-overlay dynamic-modal warning-modal" id="${modalId}-overlay">
66
70
  <div class="modal warning-modal ${className}" id="${modalId}">
67
- <div class="modal-header">
68
- <h3>${this.escapeHtml(title)}</h3>
69
- <button class="modal-close-btn" data-modal-id="${modalId}">×</button>
70
- </div>
71
- <div class="modal-body">
72
- <div class="warning-icon">⚠️</div>
73
- <p>${this.escapeHtml(message)}</p>
74
- </div>
75
- <div class="modal-footer">
76
- ${showCancel ? `<button class="btn-secondary modal-cancel-btn" data-modal-id="${modalId}">${this.escapeHtml(cancelText)}</button>` : ''}
77
- <button class="btn-primary modal-confirm-btn" data-modal-id="${modalId}">${this.escapeHtml(confirmText)}</button>
71
+ <div class="modal-content">
72
+ <div class="modal-header">
73
+ <h3>${this.escapeHtml(warningTitle)}</h3>
74
+ </div>
75
+ <div class="modal-body">
76
+ <p>${this.escapeHtml(message)}</p>
77
+ </div>
78
+ <div class="modal-footer">
79
+ ${shouldShowCancel ? `<button class="btn-secondary modal-cancel-btn" data-modal-id="${modalId}">${this.escapeHtml(cancelText)}</button>` : ''}
80
+ <button class="btn-primary modal-confirm-btn" data-modal-id="${modalId}">${this.escapeHtml(confirmText)}</button>
81
+ </div>
78
82
  </div>
79
83
  </div>
80
84
  </div>
@@ -121,20 +125,23 @@ class VibeSurfModalManager {
121
125
 
122
126
  const icon = iconMap[type] || iconMap.question;
123
127
 
128
+ // Add icon to title and remove close button
129
+ const iconTitle = `${icon} ${title}`;
130
+
124
131
  const modalHTML = `
125
- <div class="modal-overlay" id="${modalId}-overlay">
132
+ <div class="modal-overlay dynamic-modal" id="${modalId}-overlay">
126
133
  <div class="modal confirm-modal ${className}" id="${modalId}">
127
- <div class="modal-header">
128
- <h3>${this.escapeHtml(title)}</h3>
129
- <button class="modal-close-btn" data-modal-id="${modalId}">×</button>
130
- </div>
131
- <div class="modal-body">
132
- <div class="confirm-icon">${icon}</div>
133
- <p>${this.escapeHtml(message)}</p>
134
- </div>
135
- <div class="modal-footer">
136
- <button class="btn-secondary modal-cancel-btn" data-modal-id="${modalId}">${this.escapeHtml(cancelText)}</button>
137
- <button class="btn-primary modal-confirm-btn" data-modal-id="${modalId}" ${type === 'danger' ? 'data-danger="true"' : ''}>${this.escapeHtml(confirmText)}</button>
134
+ <div class="modal-content">
135
+ <div class="modal-header">
136
+ <h3>${this.escapeHtml(iconTitle)}</h3>
137
+ </div>
138
+ <div class="modal-body">
139
+ <p>${this.escapeHtml(message)}</p>
140
+ </div>
141
+ <div class="modal-footer">
142
+ <button class="btn-secondary modal-cancel-btn" data-modal-id="${modalId}">${this.escapeHtml(cancelText)}</button>
143
+ <button class="btn-primary modal-confirm-btn" data-modal-id="${modalId}" ${type === 'danger' ? 'data-danger="true"' : ''}>${this.escapeHtml(confirmText)}</button>
144
+ </div>
138
145
  </div>
139
146
  </div>
140
147
  </div>
@@ -174,16 +181,18 @@ class VibeSurfModalManager {
174
181
  const modalId = this.generateModalId();
175
182
 
176
183
  const modalHTML = `
177
- <div class="modal-overlay ${backdrop ? 'backdrop' : ''}" id="${modalId}-overlay">
184
+ <div class="modal-overlay dynamic-modal ${backdrop ? 'backdrop' : ''}" id="${modalId}-overlay">
178
185
  <div class="modal ${className}" id="${modalId}">
179
- ${title || showCloseButton ? `
180
- <div class="modal-header">
181
- ${title ? `<h3>${this.escapeHtml(title)}</h3>` : ''}
182
- ${showCloseButton ? `<button class="modal-close-btn" data-modal-id="${modalId}">×</button>` : ''}
186
+ <div class="modal-content">
187
+ ${title || showCloseButton ? `
188
+ <div class="modal-header">
189
+ ${title ? `<h3>${this.escapeHtml(title)}</h3>` : ''}
190
+ ${showCloseButton ? `<button class="modal-close-btn" data-modal-id="${modalId}">×</button>` : ''}
191
+ </div>
192
+ ` : ''}
193
+ <div class="modal-body">
194
+ ${content}
183
195
  </div>
184
- ` : ''}
185
- <div class="modal-body">
186
- ${content}
187
196
  </div>
188
197
  </div>
189
198
  </div>
@@ -221,6 +221,16 @@ class VibeSurfSessionManager {
221
221
 
222
222
  const response = await this.apiClient.submitTask(taskPayload);
223
223
 
224
+ // Check if the response indicates LLM connection failure
225
+ if (response && response.success === false && response.error === 'llm_connection_failed') {
226
+ console.log('[SessionManager] LLM connection failed, emitting taskError');
227
+ this.emit('taskError', {
228
+ error: response,
229
+ sessionId: this.currentSession.id
230
+ });
231
+ throw new Error(response.message || 'LLM connection failed');
232
+ }
233
+
224
234
  // Update current session with task info
225
235
  this.currentSession.currentTask = {
226
236
  taskId: response.task_id,
@@ -1206,6 +1206,38 @@ class VibeSurfSettingsManager {
1206
1206
  this.elements.backendUrl.value = url;
1207
1207
  }
1208
1208
  }
1209
+
1210
+ // Navigate to specific LLM profile for editing
1211
+ async navigateToLLMProfile(profileName) {
1212
+ console.log('[SettingsManager] Navigating to LLM profile:', profileName);
1213
+
1214
+ // First show the settings modal
1215
+ this.showSettings();
1216
+
1217
+ // Switch to LLM profiles tab
1218
+ const llmTab = document.querySelector('.settings-tab[data-tab="llm-profiles"]');
1219
+ if (llmTab) {
1220
+ llmTab.click(); // This will trigger handleTabSwitch
1221
+ }
1222
+
1223
+ // Wait a moment for tab switching to complete
1224
+ setTimeout(async () => {
1225
+ // Find the profile in the current state
1226
+ const profile = this.state.llmProfiles.find(p => p.profile_name === profileName);
1227
+
1228
+ if (profile) {
1229
+ // Show the edit form for this profile
1230
+ await this.showProfileForm('llm', profile);
1231
+ console.log('[SettingsManager] LLM profile edit form shown for:', profileName);
1232
+ } else {
1233
+ console.warn('[SettingsManager] Profile not found:', profileName);
1234
+ this.emit('notification', {
1235
+ message: `LLM profile "${profileName}" not found. Please check if it still exists.`,
1236
+ type: 'warning'
1237
+ });
1238
+ }
1239
+ }, 100);
1240
+ }
1209
1241
  }
1210
1242
 
1211
1243
  // Export for use in other modules
@@ -359,6 +359,27 @@ class VibeSurfUIManager {
359
359
 
360
360
  handleTaskError(data) {
361
361
  console.error('[UIManager] Task error:', data.error);
362
+ console.log('[UIManager] Task error data structure:', JSON.stringify(data, null, 2));
363
+
364
+ // Check if this is an LLM connection failure
365
+ if (data.error && typeof data.error === 'object' && data.error.error === 'llm_connection_failed') {
366
+ console.log('[UIManager] Detected LLM connection failure from object error');
367
+ // Show LLM connection failed modal instead of generic notification
368
+ this.showLLMConnectionFailedModal(data.error);
369
+ this.updateControlPanel('ready'); // Reset UI since task failed to start
370
+ return;
371
+ } else if (data.error && typeof data.error === 'string' && data.error.includes('llm_connection_failed')) {
372
+ console.log('[UIManager] Detected LLM connection failure from string error');
373
+ // Handle case where error is a string containing the error type
374
+ this.showLLMConnectionFailedModal({
375
+ message: data.error,
376
+ llm_profile: 'unknown'
377
+ });
378
+ this.updateControlPanel('ready'); // Reset UI since task failed to start
379
+ return;
380
+ }
381
+
382
+ // Default error handling for other types of errors
362
383
  this.showNotification(`Task error: ${data.error}`, 'error');
363
384
 
364
385
  this.updateControlPanel('error');
@@ -718,7 +739,12 @@ class VibeSurfUIManager {
718
739
  // Clear uploaded files after successful task submission
719
740
  this.fileManager.clearUploadedFiles();
720
741
  } catch (error) {
721
- this.showNotification(`Failed to submit task: ${error.message}`, 'error');
742
+ // Check if this is an LLM connection failure from API response
743
+ if (error.data && error.data.error === 'llm_connection_failed') {
744
+ this.showLLMConnectionFailedModal(error.data);
745
+ } else {
746
+ this.showNotification(`Failed to submit task: ${error.message}`, 'error');
747
+ }
722
748
  }
723
749
  }
724
750
 
@@ -1686,6 +1712,46 @@ class VibeSurfUIManager {
1686
1712
  this.modalManager.showWarningModal(title, message, options);
1687
1713
  }
1688
1714
 
1715
+ showLLMConnectionFailedModal(errorData) {
1716
+ console.log('[UIManager] showLLMConnectionFailedModal called with:', errorData);
1717
+
1718
+ const llmProfile = errorData.llm_profile || 'unknown';
1719
+ const errorMessage = errorData.message || 'Cannot connect to LLM API';
1720
+
1721
+ const title = 'LLM Connection Failed';
1722
+ const message = `${errorMessage}\n\nThe LLM profile "${llmProfile}" cannot be reached. Please check your LLM configuration and API credentials.`;
1723
+
1724
+ const options = {
1725
+ confirmText: 'Update LLM Profile',
1726
+ cancelText: 'Cancel',
1727
+ onConfirm: () => {
1728
+ // Navigate to the specific LLM profile edit page
1729
+ this.handleShowLLMProfileSettings(llmProfile);
1730
+ }
1731
+ };
1732
+
1733
+ console.log('[UIManager] Showing LLM connection failed modal');
1734
+ this.modalManager.showWarningModal(title, message, options);
1735
+ }
1736
+
1737
+ async handleShowLLMProfileSettings(profileName) {
1738
+ // Enhanced task running check
1739
+ const statusCheck = await this.checkTaskStatus();
1740
+ if (statusCheck.isRunning) {
1741
+ const canProceed = await this.showTaskRunningWarning('access settings');
1742
+ if (!canProceed) return;
1743
+ }
1744
+
1745
+ // Show settings and navigate to the specific LLM profile
1746
+ this.settingsManager.showSettings();
1747
+
1748
+ // Navigate to LLM profiles section and edit the specific profile
1749
+ // We'll add a method to settings manager to handle this
1750
+ if (this.settingsManager.navigateToLLMProfile) {
1751
+ this.settingsManager.navigateToLLMProfile(profileName);
1752
+ }
1753
+ }
1754
+
1689
1755
  // Loading and notifications
1690
1756
  showLoading(message = 'Loading...') {
1691
1757
  this.state.isLoading = true;
@@ -1,6 +1,6 @@
1
1
  /* Components CSS - Modals, Forms, and Complex Components */
2
2
 
3
- /* Modal Styles */
3
+ /* Modal Styles - Base modal container */
4
4
  .modal {
5
5
  position: fixed;
6
6
  top: 0;
@@ -14,6 +14,12 @@
14
14
  padding: var(--spacing-lg);
15
15
  }
16
16
 
17
+ /* Hidden state for static modals */
18
+ .modal.hidden {
19
+ display: none;
20
+ }
21
+
22
+ /* Legacy modal overlay for existing modals */
17
23
  .modal-overlay {
18
24
  position: absolute;
19
25
  top: 0;
@@ -24,6 +30,30 @@
24
30
  backdrop-filter: blur(4px);
25
31
  }
26
32
 
33
+ /* Dynamic modal overlay for modal-manager.js */
34
+ .modal-overlay.dynamic-modal {
35
+ position: fixed;
36
+ top: 0;
37
+ left: 0;
38
+ right: 0;
39
+ bottom: 0;
40
+ z-index: 10000;
41
+ display: flex;
42
+ align-items: center;
43
+ justify-content: center;
44
+ padding: var(--spacing-lg);
45
+ background-color: rgba(0, 0, 0, 0.5);
46
+ backdrop-filter: blur(4px);
47
+ opacity: 0;
48
+ visibility: hidden;
49
+ transition: all var(--transition-fast);
50
+ }
51
+
52
+ .modal-overlay.dynamic-modal.show {
53
+ opacity: 1;
54
+ visibility: visible;
55
+ }
56
+
27
57
  .modal-content {
28
58
  position: relative;
29
59
  width: 100%;
@@ -37,6 +67,40 @@
37
67
  overflow: hidden;
38
68
  }
39
69
 
70
+ /* Modal Body - for dynamic modals from modal-manager.js */
71
+ .modal-body {
72
+ flex: 1;
73
+ padding: var(--spacing-lg) var(--spacing-xl);
74
+ overflow-y: auto;
75
+ display: flex;
76
+ flex-direction: column;
77
+ align-items: center;
78
+ text-align: center;
79
+ gap: var(--spacing-md);
80
+ }
81
+
82
+ .modal-body .warning-icon {
83
+ font-size: 2rem;
84
+ margin-bottom: var(--spacing-sm);
85
+ }
86
+
87
+ .modal-body p {
88
+ font-size: var(--font-size-sm);
89
+ color: var(--text-primary);
90
+ line-height: 1.6;
91
+ margin: 0;
92
+ }
93
+
94
+ /* Modal Footer - for dynamic modals from modal-manager.js */
95
+ .modal-footer {
96
+ display: flex;
97
+ gap: var(--spacing-sm);
98
+ justify-content: center;
99
+ padding: var(--spacing-lg) var(--spacing-xl) var(--spacing-xl);
100
+ border-top: 1px solid var(--border-color);
101
+ background-color: var(--bg-secondary);
102
+ }
103
+
40
104
  .modal-header {
41
105
  display: flex;
42
106
  align-items: center;
@@ -70,6 +134,28 @@
70
134
  color: var(--text-primary);
71
135
  }
72
136
 
137
+ /* Modal Close Button - alternative class for dynamic modals */
138
+ .modal-close-btn {
139
+ display: flex;
140
+ align-items: center;
141
+ justify-content: center;
142
+ width: 32px;
143
+ height: 32px;
144
+ border: none;
145
+ border-radius: var(--radius-md);
146
+ background-color: transparent;
147
+ color: var(--text-secondary);
148
+ cursor: pointer;
149
+ transition: all var(--transition-fast);
150
+ font-size: 18px;
151
+ line-height: 1;
152
+ }
153
+
154
+ .modal-close-btn:hover {
155
+ background-color: var(--bg-hover);
156
+ color: var(--text-primary);
157
+ }
158
+
73
159
  /* History Modal */
74
160
  .history-list {
75
161
  flex: 1;
@@ -505,6 +591,47 @@
505
591
  color: var(--text-secondary);
506
592
  }
507
593
 
594
+ /* Button Styles for Dynamic Modals */
595
+ .btn-primary, .btn-secondary {
596
+ padding: 12px 24px;
597
+ border: none;
598
+ border-radius: var(--radius-md);
599
+ font-size: var(--font-size-sm);
600
+ font-weight: var(--font-weight-medium);
601
+ cursor: pointer;
602
+ transition: all var(--transition-fast);
603
+ min-width: 100px;
604
+ }
605
+
606
+ .btn-primary {
607
+ background-color: var(--primary-color);
608
+ color: white;
609
+ }
610
+
611
+ .btn-primary:hover {
612
+ background-color: var(--primary-hover);
613
+ transform: translateY(-1px);
614
+ box-shadow: 0 4px 16px rgba(0, 122, 204, 0.3);
615
+ }
616
+
617
+ .btn-secondary {
618
+ background-color: var(--bg-primary);
619
+ color: var(--text-primary);
620
+ border: 1px solid var(--border-color);
621
+ }
622
+
623
+ .btn-secondary:hover {
624
+ background-color: var(--bg-hover);
625
+ border-color: var(--border-hover);
626
+ }
627
+
628
+ .btn-primary:disabled, .btn-secondary:disabled {
629
+ opacity: 0.5;
630
+ cursor: not-allowed;
631
+ transform: none !important;
632
+ box-shadow: none !important;
633
+ }
634
+
508
635
  /* Tooltip */
509
636
  .tooltip {
510
637
  position: relative;
@@ -578,22 +705,53 @@
578
705
  max-width: 300px;
579
706
  }
580
707
 
581
- /* Warning Modal */
582
- .warning-modal {
583
- position: fixed;
584
- top: 0;
585
- left: 0;
586
- right: 0;
587
- bottom: 0;
588
- background-color: rgba(0, 0, 0, 0.6);
589
- backdrop-filter: blur(6px);
590
- display: flex;
591
- align-items: center;
592
- justify-content: center;
593
- z-index: 2100;
594
- padding: var(--spacing-lg);
708
+ /* Dynamic Modal Styles for modal-manager.js */
709
+ .modal-overlay.dynamic-modal .modal.warning-modal .modal-content {
710
+ border: 2px solid var(--warning-color);
711
+ animation: modalSlideIn 0.3s ease-out;
712
+ }
713
+
714
+ .modal-overlay.dynamic-modal .modal.warning-modal .modal-header {
715
+ background-color: rgba(255, 193, 7, 0.1);
716
+ border-bottom: 1px solid rgba(255, 193, 7, 0.2);
717
+ }
718
+
719
+ .modal-overlay.dynamic-modal .modal.warning-modal .modal-header h3 {
720
+ color: var(--warning-color);
721
+ }
722
+
723
+ .modal-overlay.dynamic-modal .modal.warning-modal .warning-icon {
724
+ color: var(--warning-color);
725
+ filter: drop-shadow(0 2px 4px rgba(255, 193, 7, 0.3));
726
+ }
727
+
728
+ /* Confirm Modal enhancements */
729
+ .modal-overlay.dynamic-modal .modal.confirm-modal .modal-content {
730
+ animation: modalSlideIn 0.3s ease-out;
731
+ }
732
+
733
+ /* Modal Animation */
734
+ @keyframes modalSlideIn {
735
+ from {
736
+ opacity: 0;
737
+ transform: translateY(-20px) scale(0.95);
738
+ }
739
+ to {
740
+ opacity: 1;
741
+ transform: translateY(0) scale(1);
742
+ }
743
+ }
744
+
745
+ /* Z-index management for different modal types */
746
+ .modal-overlay.dynamic-modal {
747
+ z-index: 10000; /* Higher than static modals */
748
+ }
749
+
750
+ .modal-overlay.dynamic-modal.warning-modal {
751
+ z-index: 15000; /* Highest priority for warning modals */
595
752
  }
596
753
 
754
+ /* Legacy warning modal classes - keeping for backward compatibility but not used by modal-manager.js */
597
755
  .warning-content {
598
756
  background-color: var(--bg-primary);
599
757
  border-radius: var(--radius-xl);
@@ -614,11 +772,6 @@
614
772
  border-bottom: 1px solid rgba(255, 193, 7, 0.2);
615
773
  }
616
774
 
617
- .warning-icon {
618
- font-size: 1.5rem;
619
- color: var(--warning-color);
620
- }
621
-
622
775
  .warning-header h3 {
623
776
  font-size: var(--font-size-lg);
624
777
  font-weight: var(--font-weight-semibold);
@@ -256,101 +256,7 @@ select.task-running-disabled {
256
256
  font-size: 14px;
257
257
  }
258
258
 
259
- /* Enhanced warning modal styles */
260
- .modal-overlay.warning-modal {
261
- z-index: 15000;
262
- }
263
-
264
- .warning-modal .modal-content {
265
- max-width: 500px;
266
- background: white;
267
- border-radius: 12px;
268
- box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
269
- }
270
-
271
- .warning-header {
272
- display: flex;
273
- align-items: center;
274
- gap: 12px;
275
- padding: 20px 20px 0;
276
- border-bottom: 1px solid #eee;
277
- margin-bottom: 16px;
278
- }
279
-
280
- .warning-icon {
281
- font-size: 24px;
282
- }
283
-
284
- .warning-header h3 {
285
- margin: 0;
286
- color: #dc3545;
287
- font-size: 18px;
288
- }
289
-
290
- .warning-body {
291
- padding: 0 20px 16px;
292
- }
293
-
294
- .warning-body p {
295
- margin: 0 0 12px;
296
- color: #666;
297
- line-height: 1.5;
298
- }
299
-
300
- .warning-details {
301
- background: #f8f9fa;
302
- border: 1px solid #e9ecef;
303
- border-radius: 4px;
304
- padding: 12px;
305
- font-family: monospace;
306
- font-size: 12px;
307
- color: #495057;
308
- white-space: pre-wrap;
309
- margin: 0;
310
- }
311
-
312
- .warning-actions {
313
- display: flex;
314
- gap: 8px;
315
- padding: 16px 20px 20px;
316
- justify-content: flex-end;
317
- }
318
-
319
- .warning-actions .btn {
320
- padding: 8px 16px;
321
- border: none;
322
- border-radius: 6px;
323
- font-size: 14px;
324
- cursor: pointer;
325
- transition: all 0.2s ease;
326
- }
327
-
328
- .warning-actions .btn-danger {
329
- background: #dc3545;
330
- color: white;
331
- }
332
-
333
- .warning-actions .btn-danger:hover {
334
- background: #c82333;
335
- }
336
-
337
- .warning-actions .btn-secondary {
338
- background: #6c757d;
339
- color: white;
340
- }
341
-
342
- .warning-actions .btn-secondary:hover {
343
- background: #545b62;
344
- }
345
-
346
- .warning-actions .btn-primary {
347
- background: #007bff;
348
- color: white;
349
- }
350
-
351
- .warning-actions .btn-primary:hover {
352
- background: #0056b3;
353
- }
259
+ /* Warning modal styles removed - consolidated in components.css to avoid conflicts */
354
260
 
355
261
  /* File Upload Styles */
356
262
  .uploaded-files-container {
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vibesurf
3
- Version: 0.1.12
3
+ Version: 0.1.13
4
4
  Summary: VibeSurf: A powerful browser assistant for vibe surfing
5
5
  Author: Shao Warm
6
6
  License: Apache-2.0
@@ -1,5 +1,5 @@
1
1
  vibe_surf/__init__.py,sha256=WtduuMFGauMD_9dpk4fnRnLTAP6ka9Lfu0feAFNzLfo,339
2
- vibe_surf/_version.py,sha256=cEPXLUpTV7EzqolnyXW8nf8Hr6IVyBji9CzB6Cq_Ar0,706
2
+ vibe_surf/_version.py,sha256=Xz5RLbyPcCHHXte393JYfUy4Dt7uaeWyrGVw9SmJ0eg,706
3
3
  vibe_surf/cli.py,sha256=pbep2dBeQqralZ8AggkH4h2nayBarbdN8lhZxo35gNU,16689
4
4
  vibe_surf/common.py,sha256=_WWMxen5wFwzUjEShn3yDVC1OBFUiJ6Vccadi6tuG6w,1215
5
5
  vibe_surf/logger.py,sha256=k53MFA96QX6t9OfcOf1Zws8PP0OOqjVJfhUD3Do9lKw,3043
@@ -14,14 +14,14 @@ vibe_surf/agents/prompts/vibe_surf_prompt.py,sha256=8bC7IJNbK7_jLbxjVsta3urZIi-F
14
14
  vibe_surf/backend/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
15
  vibe_surf/backend/llm_config.py,sha256=9V8Gg065TQALbOKQnOqFWd8RzOJjegOD8w6YOf90Q7Y,5036
16
16
  vibe_surf/backend/main.py,sha256=nZDs_FkYU7NdqSm1O4OwHJslYIJZL1Gr3CEKa8ByaEY,7178
17
- vibe_surf/backend/shared_state.py,sha256=cVZepa1A0vo4tzNJM_-8y2F4DkRTx6Qo7ZrKW6d7aVg,22860
17
+ vibe_surf/backend/shared_state.py,sha256=yEBWj6YI6WjVF2nuJ_nujo8yqHw2j8xikmxr0G8fnbs,23331
18
18
  vibe_surf/backend/api/__init__.py,sha256=XxF1jUOORpLYCfFuPrrnUGRnOrr6ClH0_MNPU-4RnSs,68
19
19
  vibe_surf/backend/api/activity.py,sha256=_cnHusqolt5Hf3KdAf6FK-3sBc-TSaadmb5dJxGI57A,9398
20
20
  vibe_surf/backend/api/browser.py,sha256=NXedyZG3NIVRIx5O7d9mHwVWX-Q4_KsX5mSgfKt8UEA,2122
21
21
  vibe_surf/backend/api/config.py,sha256=EwzxYvC6HlaVo2OFWjtBmBMjX4eW2q8hp7l2LO2GZV0,27124
22
22
  vibe_surf/backend/api/files.py,sha256=kJMG9MWECKXwGh64Q6xvAzNjeZGcLhIEnn65HiMZHKE,11762
23
23
  vibe_surf/backend/api/models.py,sha256=iyMBdpDkOfwx0h2yRkjmYjRdFyDErhO8XZcWTurdcmU,10347
24
- vibe_surf/backend/api/task.py,sha256=pe4CHxM7Xc7lzvbslRxxSF-TKRiFj3A6RXCjHpP8f_o,12343
24
+ vibe_surf/backend/api/task.py,sha256=xxZC-SoRBwzoAMpCDZc7YAxBlehkGC7_s8bMsfA0cs0,14058
25
25
  vibe_surf/backend/database/__init__.py,sha256=XhmcscnhgMhUyXML7m4SnuQIqkFpyY_zJ0D3yYa2RqQ,239
26
26
  vibe_surf/backend/database/manager.py,sha256=aC8XQc68qDsaryrux7VfCEl2hnEZL5kfnEkXWXPg2ig,4427
27
27
  vibe_surf/backend/database/models.py,sha256=mePuHsaSqJKA4TthvXbup_Ioann2_chxywiLKqAWyh4,7009
@@ -32,7 +32,7 @@ vibe_surf/backend/migrations/init_db.py,sha256=pY2Yq7K1vPxqT8r3jlAQcYEQWK-GGbb0F
32
32
  vibe_surf/backend/migrations/seed_data.py,sha256=L6Ll-u8P4cICAUlD5y9urQPSUld6M67erSBCEIdw8Uc,8239
33
33
  vibe_surf/backend/utils/__init__.py,sha256=V8leMFp7apAglUAoCHPZrNNcRHthSLYIudIJE5qwjb0,184
34
34
  vibe_surf/backend/utils/encryption.py,sha256=CjLNh_n0Luhfa-6BB-icfzkiiDqj5b4Gu6MADU3p2eM,3754
35
- vibe_surf/backend/utils/llm_factory.py,sha256=g1G_fkIBdDqZypTGuYGgnU5CCudpVE505n4AUdlSE5Q,8991
35
+ vibe_surf/backend/utils/llm_factory.py,sha256=mNy8o3sw7vYJ8gwiTsrgXbG7Ri_B11ylE4KGcfHULp8,8972
36
36
  vibe_surf/browser/__init__.py,sha256=_UToO2fZfSCrfjOcxhn4Qq7ZLbYeyPuUUEmqIva-Yv8,325
37
37
  vibe_surf/browser/agen_browser_profile.py,sha256=4H4qDjqLqHAfMi3niBI9fd0S1GlVDWYDUUOrEQoNkl8,5677
38
38
  vibe_surf/browser/agent_browser_session.py,sha256=rptz2MdXspS44zC21nJaLOmp5aEWsvBfvzpDk5Jr-mA,33884
@@ -52,21 +52,21 @@ vibe_surf/chrome_extension/icons/convert-svg.js,sha256=j137nZA7MJK35NtrwWff8yb3U
52
52
  vibe_surf/chrome_extension/icons/logo-preview.html,sha256=hrgU45uziKHKIb8be9P4ZrZJyGggWnm2M5oEu89V5sM,6962
53
53
  vibe_surf/chrome_extension/icons/logo.icns,sha256=ZzY1eIKF4dNhNW4CeE1UBQloxNVC7bQx3qcClo3CnMQ,1569615
54
54
  vibe_surf/chrome_extension/icons/logo.png,sha256=PLmv1E6sCGXUE5ZDxr-pFPQd9Gvaw_f1TnYmF8VIssU,566385
55
- vibe_surf/chrome_extension/scripts/api-client.js,sha256=EMxNiuyw76UZ6K7-qOP2qpZDmZGvvZ8xPFy5pwIPu0w,13595
55
+ vibe_surf/chrome_extension/scripts/api-client.js,sha256=YUnMZOKUcJaGFl8wCpGxgCSvBKqbwCtYbQLbI32otrY,13844
56
56
  vibe_surf/chrome_extension/scripts/file-manager.js,sha256=WcEaeoECq84jXqEo3Q2ywmqppwSUVm-0dtrbTYXL07g,15809
57
57
  vibe_surf/chrome_extension/scripts/history-manager.js,sha256=J_EiBKrRp2cTxuy0Sq5oCju5YuX0_2pGG6NWrJSjC24,22218
58
58
  vibe_surf/chrome_extension/scripts/main.js,sha256=WhmCGktQoSl7aaMl8a9ysJJiysAjf12bWGypMucCSVg,16913
59
59
  vibe_surf/chrome_extension/scripts/markdown-it.min.js,sha256=gZ3xe0BdCJplNiHWTKrm6AGZydPy34jJKZqFIf-7hIw,102948
60
- vibe_surf/chrome_extension/scripts/modal-manager.js,sha256=VjkbTfknzwoUHFoSiLj19J18eTZ6u78I0ujuOIDAKhU,13850
61
- vibe_surf/chrome_extension/scripts/session-manager.js,sha256=LGSiUHSeMgJwIWluk4TuoomSi_z6m_qDc08Vbg6SOWw,20680
62
- vibe_surf/chrome_extension/scripts/settings-manager.js,sha256=FE5rMm3HTY1M7dzyQ2jnmCRmff1PDtqy6GNDV3tJOVk,47435
63
- vibe_surf/chrome_extension/scripts/ui-manager.js,sha256=K5jLaCc1KpX1R74a_PVOemEn6008amChHgbQMjsLi9s,78695
60
+ vibe_surf/chrome_extension/scripts/modal-manager.js,sha256=9ekFaTmy8sk94KK9HZJ7739ObsZ6ssllmRLP9rQxUKU,14187
61
+ vibe_surf/chrome_extension/scripts/session-manager.js,sha256=sXh4j4dv3eKOGYeov8iUPt-DX5jKQLhcASDXRX9qW5Y,21126
62
+ vibe_surf/chrome_extension/scripts/settings-manager.js,sha256=rgstYooxZtV9QjqP8KRRQclHbeiZJozZZfv80A6M_P4,48601
63
+ vibe_surf/chrome_extension/scripts/ui-manager.js,sha256=_qMrS_3_jZn7lDlXkTlF4CwOZxdg3B3Enww95PaGsHA,81526
64
64
  vibe_surf/chrome_extension/styles/activity.css,sha256=aEFa_abskrDQvwsesPVOyJW3rUQUEBQpMKPHhY94CoA,19601
65
65
  vibe_surf/chrome_extension/styles/animations.css,sha256=TLAet_xXBxCA-H36BWP4xBGBIVjbDdAj7Q6OPxPsbE8,7891
66
66
  vibe_surf/chrome_extension/styles/base.css,sha256=d8nt7Wej1r57G6pnBIGKEVkepFxlq8Ets0isngCf5w8,1296
67
- vibe_surf/chrome_extension/styles/components.css,sha256=7K6khbJcONVAArfeS4qmPBUJxvGGs20-eEw62bD_7VI,14741
67
+ vibe_surf/chrome_extension/styles/components.css,sha256=cbpYgT1ujG11axRa9xnHizgm4wfrpSzq_nkUOdfNZ0A,18495
68
68
  vibe_surf/chrome_extension/styles/history-modal.css,sha256=xCg0EPz1N4kg47jnz9q9E1M4zGag8lJMt_msAOkZi3M,16583
69
- vibe_surf/chrome_extension/styles/input.css,sha256=ZEApufxeDOdeB5MowC3QQccrj_sKoj0-vxlgcf7rmWM,11986
69
+ vibe_surf/chrome_extension/styles/input.css,sha256=Sn2QulNV4jS2NJRByp7RVyZtCJbexw48AjLxgpB-XRw,10566
70
70
  vibe_surf/chrome_extension/styles/layout.css,sha256=ggh-lWpP6sHubFkshf9dcmA52LiXw7VJmQXcM5_EGcY,3528
71
71
  vibe_surf/chrome_extension/styles/responsive.css,sha256=a_SFX-PeOcenG4xMeoru5XFnnXdf0YhEyRzvTVVZHdI,8529
72
72
  vibe_surf/chrome_extension/styles/settings-environment.css,sha256=OzgezmfZZ_yD60Jy9Uiq83mDmMCfk2dPPcZISb9Eqd4,3390
@@ -85,9 +85,9 @@ vibe_surf/tools/mcp_client.py,sha256=OeCoTgyx4MoY7JxXndK6pGHIoyFOhf5r7XCbx25y1Ec
85
85
  vibe_surf/tools/report_writer_tools.py,sha256=sUqUFr-_Rs8RJ0Bs77Hrp07kNwRIvHv7ErzSPYSlbTg,705
86
86
  vibe_surf/tools/vibesurf_tools.py,sha256=uSsymefr-SrVAuDtvNGsB0Am4QeTuHuL-4KJABQW8dg,29880
87
87
  vibe_surf/tools/views.py,sha256=KwzzXXZJqyW1oyszA0Z4buDDqAm-6PXsCAB0nB7z_sQ,4218
88
- vibesurf-0.1.12.dist-info/licenses/LICENSE,sha256=czn6QYya0-jhLnStD9JqnMS-hwP5wRByipkrGTvoXLI,11355
89
- vibesurf-0.1.12.dist-info/METADATA,sha256=iF0JzXx8sKE_IC17rHVxvlM2jAPs04MIZk-QPifmjLI,5313
90
- vibesurf-0.1.12.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
91
- vibesurf-0.1.12.dist-info/entry_points.txt,sha256=UxqpvMocL-PR33S6vLF2OmXn-kVzM-DneMeZeHcPMM8,48
92
- vibesurf-0.1.12.dist-info/top_level.txt,sha256=VPZGHqSb6EEqcJ4ZX6bHIuWfon5f6HXl3c7BYpbRqnY,10
93
- vibesurf-0.1.12.dist-info/RECORD,,
88
+ vibesurf-0.1.13.dist-info/licenses/LICENSE,sha256=czn6QYya0-jhLnStD9JqnMS-hwP5wRByipkrGTvoXLI,11355
89
+ vibesurf-0.1.13.dist-info/METADATA,sha256=PzpfRoPGUlDTpJI898y49j8lsx-ibdQNmD32H8W_dhU,5313
90
+ vibesurf-0.1.13.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
91
+ vibesurf-0.1.13.dist-info/entry_points.txt,sha256=UxqpvMocL-PR33S6vLF2OmXn-kVzM-DneMeZeHcPMM8,48
92
+ vibesurf-0.1.13.dist-info/top_level.txt,sha256=VPZGHqSb6EEqcJ4ZX6bHIuWfon5f6HXl3c7BYpbRqnY,10
93
+ vibesurf-0.1.13.dist-info/RECORD,,