vibesurf 0.1.9a6__py3-none-any.whl → 0.1.11__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 +2 -2
- vibe_surf/agents/browser_use_agent.py +68 -45
- vibe_surf/agents/prompts/report_writer_prompt.py +73 -0
- vibe_surf/agents/prompts/vibe_surf_prompt.py +85 -172
- vibe_surf/agents/report_writer_agent.py +380 -226
- vibe_surf/agents/vibe_surf_agent.py +878 -814
- vibe_surf/agents/views.py +130 -0
- vibe_surf/backend/api/activity.py +3 -1
- vibe_surf/backend/api/browser.py +70 -0
- vibe_surf/backend/api/config.py +8 -5
- vibe_surf/backend/api/files.py +59 -50
- vibe_surf/backend/api/models.py +2 -2
- vibe_surf/backend/api/task.py +47 -13
- vibe_surf/backend/database/manager.py +24 -18
- vibe_surf/backend/database/queries.py +199 -192
- vibe_surf/backend/database/schemas.py +1 -1
- vibe_surf/backend/main.py +80 -3
- vibe_surf/backend/shared_state.py +30 -35
- vibe_surf/backend/utils/encryption.py +3 -1
- vibe_surf/backend/utils/llm_factory.py +41 -36
- vibe_surf/browser/agent_browser_session.py +308 -62
- vibe_surf/browser/browser_manager.py +71 -100
- vibe_surf/browser/utils.py +5 -3
- vibe_surf/browser/watchdogs/dom_watchdog.py +0 -45
- vibe_surf/chrome_extension/background.js +88 -0
- vibe_surf/chrome_extension/manifest.json +3 -1
- vibe_surf/chrome_extension/scripts/api-client.js +13 -0
- vibe_surf/chrome_extension/scripts/file-manager.js +482 -0
- vibe_surf/chrome_extension/scripts/history-manager.js +658 -0
- vibe_surf/chrome_extension/scripts/modal-manager.js +487 -0
- vibe_surf/chrome_extension/scripts/session-manager.js +52 -11
- vibe_surf/chrome_extension/scripts/settings-manager.js +1214 -0
- vibe_surf/chrome_extension/scripts/ui-manager.js +1530 -3163
- vibe_surf/chrome_extension/sidepanel.html +47 -7
- vibe_surf/chrome_extension/styles/activity.css +934 -0
- vibe_surf/chrome_extension/styles/base.css +76 -0
- vibe_surf/chrome_extension/styles/history-modal.css +791 -0
- vibe_surf/chrome_extension/styles/input.css +568 -0
- vibe_surf/chrome_extension/styles/layout.css +186 -0
- vibe_surf/chrome_extension/styles/responsive.css +454 -0
- vibe_surf/chrome_extension/styles/settings-environment.css +165 -0
- vibe_surf/chrome_extension/styles/settings-forms.css +389 -0
- vibe_surf/chrome_extension/styles/settings-modal.css +141 -0
- vibe_surf/chrome_extension/styles/settings-profiles.css +244 -0
- vibe_surf/chrome_extension/styles/settings-responsive.css +144 -0
- vibe_surf/chrome_extension/styles/settings-utilities.css +25 -0
- vibe_surf/chrome_extension/styles/variables.css +54 -0
- vibe_surf/cli.py +5 -22
- vibe_surf/common.py +35 -0
- vibe_surf/llm/openai_compatible.py +148 -93
- vibe_surf/logger.py +99 -0
- vibe_surf/{controller/vibesurf_tools.py → tools/browser_use_tools.py} +233 -221
- vibe_surf/tools/file_system.py +415 -0
- vibe_surf/{controller → tools}/mcp_client.py +4 -3
- vibe_surf/tools/report_writer_tools.py +21 -0
- vibe_surf/tools/vibesurf_tools.py +657 -0
- vibe_surf/tools/views.py +120 -0
- {vibesurf-0.1.9a6.dist-info → vibesurf-0.1.11.dist-info}/METADATA +23 -3
- vibesurf-0.1.11.dist-info/RECORD +93 -0
- vibe_surf/chrome_extension/styles/main.css +0 -2338
- vibe_surf/chrome_extension/styles/settings.css +0 -1100
- vibe_surf/controller/file_system.py +0 -53
- vibe_surf/controller/views.py +0 -37
- vibesurf-0.1.9a6.dist-info/RECORD +0 -71
- /vibe_surf/{controller → tools}/__init__.py +0 -0
- {vibesurf-0.1.9a6.dist-info → vibesurf-0.1.11.dist-info}/WHEEL +0 -0
- {vibesurf-0.1.9a6.dist-info → vibesurf-0.1.11.dist-info}/entry_points.txt +0 -0
- {vibesurf-0.1.9a6.dist-info → vibesurf-0.1.11.dist-info}/licenses/LICENSE +0 -0
- {vibesurf-0.1.9a6.dist-info → vibesurf-0.1.11.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
// Modal Manager - Handles modal dialogs and user confirmations
|
|
2
|
+
// Manages warning modals, confirmation dialogs, and generic modal utilities
|
|
3
|
+
|
|
4
|
+
class VibeSurfModalManager {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.state = {
|
|
7
|
+
activeModals: new Set(),
|
|
8
|
+
modalCounter: 0
|
|
9
|
+
};
|
|
10
|
+
this.eventListeners = new Map();
|
|
11
|
+
|
|
12
|
+
this.bindGlobalEvents();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
bindGlobalEvents() {
|
|
16
|
+
// Handle escape key to close modals
|
|
17
|
+
document.addEventListener('keydown', (e) => {
|
|
18
|
+
if (e.key === 'Escape') {
|
|
19
|
+
this.closeTopModal();
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// Handle background clicks to close modals
|
|
24
|
+
document.addEventListener('click', (e) => {
|
|
25
|
+
if (e.target.classList.contains('modal-overlay')) {
|
|
26
|
+
this.closeModal(e.target.querySelector('.modal'));
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Event system for communicating with main UI manager
|
|
32
|
+
on(event, callback) {
|
|
33
|
+
if (!this.eventListeners.has(event)) {
|
|
34
|
+
this.eventListeners.set(event, []);
|
|
35
|
+
}
|
|
36
|
+
this.eventListeners.get(event).push(callback);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
emit(event, data) {
|
|
40
|
+
if (this.eventListeners.has(event)) {
|
|
41
|
+
this.eventListeners.get(event).forEach(callback => {
|
|
42
|
+
try {
|
|
43
|
+
callback(data);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error(`[ModalManager] Event callback error for ${event}:`, error);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Warning Modal
|
|
52
|
+
showWarningModal(title, message, options = {}) {
|
|
53
|
+
const {
|
|
54
|
+
confirmText = 'OK',
|
|
55
|
+
showCancel = false,
|
|
56
|
+
cancelText = 'Cancel',
|
|
57
|
+
onConfirm = null,
|
|
58
|
+
onCancel = null,
|
|
59
|
+
className = ''
|
|
60
|
+
} = options;
|
|
61
|
+
|
|
62
|
+
const modalId = this.generateModalId();
|
|
63
|
+
|
|
64
|
+
const modalHTML = `
|
|
65
|
+
<div class="modal-overlay" id="${modalId}-overlay">
|
|
66
|
+
<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>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
`;
|
|
82
|
+
|
|
83
|
+
// Add to DOM
|
|
84
|
+
document.body.insertAdjacentHTML('beforeend', modalHTML);
|
|
85
|
+
|
|
86
|
+
const modal = document.getElementById(modalId);
|
|
87
|
+
const overlay = document.getElementById(`${modalId}-overlay`);
|
|
88
|
+
|
|
89
|
+
// Track active modal
|
|
90
|
+
this.state.activeModals.add(modalId);
|
|
91
|
+
|
|
92
|
+
// Add event listeners
|
|
93
|
+
this.bindModalEvents(modalId, { onConfirm, onCancel });
|
|
94
|
+
|
|
95
|
+
// Show modal
|
|
96
|
+
requestAnimationFrame(() => {
|
|
97
|
+
overlay.classList.add('show');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
return modalId;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Confirmation Modal
|
|
104
|
+
showConfirmModal(title, message, options = {}) {
|
|
105
|
+
const {
|
|
106
|
+
confirmText = 'Confirm',
|
|
107
|
+
cancelText = 'Cancel',
|
|
108
|
+
onConfirm = null,
|
|
109
|
+
onCancel = null,
|
|
110
|
+
className = '',
|
|
111
|
+
type = 'question' // question, danger, info
|
|
112
|
+
} = options;
|
|
113
|
+
|
|
114
|
+
const modalId = this.generateModalId();
|
|
115
|
+
|
|
116
|
+
const iconMap = {
|
|
117
|
+
question: '❓',
|
|
118
|
+
danger: '🚨',
|
|
119
|
+
info: 'ℹ️'
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const icon = iconMap[type] || iconMap.question;
|
|
123
|
+
|
|
124
|
+
const modalHTML = `
|
|
125
|
+
<div class="modal-overlay" id="${modalId}-overlay">
|
|
126
|
+
<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>
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
`;
|
|
142
|
+
|
|
143
|
+
// Add to DOM
|
|
144
|
+
document.body.insertAdjacentHTML('beforeend', modalHTML);
|
|
145
|
+
|
|
146
|
+
const modal = document.getElementById(modalId);
|
|
147
|
+
const overlay = document.getElementById(`${modalId}-overlay`);
|
|
148
|
+
|
|
149
|
+
// Track active modal
|
|
150
|
+
this.state.activeModals.add(modalId);
|
|
151
|
+
|
|
152
|
+
// Add event listeners
|
|
153
|
+
this.bindModalEvents(modalId, { onConfirm, onCancel });
|
|
154
|
+
|
|
155
|
+
// Show modal
|
|
156
|
+
requestAnimationFrame(() => {
|
|
157
|
+
overlay.classList.add('show');
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
return modalId;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Generic Modal Creator
|
|
164
|
+
createModal(content, options = {}) {
|
|
165
|
+
const {
|
|
166
|
+
title = '',
|
|
167
|
+
className = '',
|
|
168
|
+
showCloseButton = true,
|
|
169
|
+
backdrop = true,
|
|
170
|
+
onShow = null,
|
|
171
|
+
onHide = null
|
|
172
|
+
} = options;
|
|
173
|
+
|
|
174
|
+
const modalId = this.generateModalId();
|
|
175
|
+
|
|
176
|
+
const modalHTML = `
|
|
177
|
+
<div class="modal-overlay ${backdrop ? 'backdrop' : ''}" id="${modalId}-overlay">
|
|
178
|
+
<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>` : ''}
|
|
183
|
+
</div>
|
|
184
|
+
` : ''}
|
|
185
|
+
<div class="modal-body">
|
|
186
|
+
${content}
|
|
187
|
+
</div>
|
|
188
|
+
</div>
|
|
189
|
+
</div>
|
|
190
|
+
`;
|
|
191
|
+
|
|
192
|
+
// Add to DOM
|
|
193
|
+
document.body.insertAdjacentHTML('beforeend', modalHTML);
|
|
194
|
+
|
|
195
|
+
const modal = document.getElementById(modalId);
|
|
196
|
+
const overlay = document.getElementById(`${modalId}-overlay`);
|
|
197
|
+
|
|
198
|
+
// Track active modal
|
|
199
|
+
this.state.activeModals.add(modalId);
|
|
200
|
+
|
|
201
|
+
// Add basic event listeners
|
|
202
|
+
this.bindModalEvents(modalId, { onShow, onHide });
|
|
203
|
+
|
|
204
|
+
// Show modal
|
|
205
|
+
requestAnimationFrame(() => {
|
|
206
|
+
overlay.classList.add('show');
|
|
207
|
+
if (onShow) onShow(modal);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
return {
|
|
211
|
+
modalId,
|
|
212
|
+
modal,
|
|
213
|
+
overlay,
|
|
214
|
+
close: () => this.closeModal(modal)
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Modal Event Binding
|
|
219
|
+
bindModalEvents(modalId, callbacks = {}) {
|
|
220
|
+
const { onConfirm, onCancel, onShow, onHide } = callbacks;
|
|
221
|
+
|
|
222
|
+
// Close button
|
|
223
|
+
const closeBtn = document.querySelector(`[data-modal-id="${modalId}"].modal-close-btn`);
|
|
224
|
+
if (closeBtn) {
|
|
225
|
+
closeBtn.addEventListener('click', () => {
|
|
226
|
+
this.closeModal(document.getElementById(modalId));
|
|
227
|
+
if (onCancel) onCancel();
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Confirm button
|
|
232
|
+
const confirmBtn = document.querySelector(`[data-modal-id="${modalId}"].modal-confirm-btn`);
|
|
233
|
+
if (confirmBtn) {
|
|
234
|
+
confirmBtn.addEventListener('click', () => {
|
|
235
|
+
if (onConfirm) {
|
|
236
|
+
const result = onConfirm();
|
|
237
|
+
// Only close if callback doesn't return false
|
|
238
|
+
if (result !== false) {
|
|
239
|
+
this.closeModal(document.getElementById(modalId));
|
|
240
|
+
}
|
|
241
|
+
} else {
|
|
242
|
+
this.closeModal(document.getElementById(modalId));
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Cancel button
|
|
248
|
+
const cancelBtn = document.querySelector(`[data-modal-id="${modalId}"].modal-cancel-btn`);
|
|
249
|
+
if (cancelBtn) {
|
|
250
|
+
cancelBtn.addEventListener('click', () => {
|
|
251
|
+
this.closeModal(document.getElementById(modalId));
|
|
252
|
+
if (onCancel) onCancel();
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Close Modal
|
|
258
|
+
closeModal(modal) {
|
|
259
|
+
if (!modal) return;
|
|
260
|
+
|
|
261
|
+
const modalId = modal.id;
|
|
262
|
+
const overlay = document.getElementById(`${modalId}-overlay`);
|
|
263
|
+
|
|
264
|
+
if (overlay) {
|
|
265
|
+
overlay.classList.remove('show');
|
|
266
|
+
|
|
267
|
+
// Remove from DOM after animation
|
|
268
|
+
setTimeout(() => {
|
|
269
|
+
if (overlay.parentNode) {
|
|
270
|
+
overlay.parentNode.removeChild(overlay);
|
|
271
|
+
}
|
|
272
|
+
this.state.activeModals.delete(modalId);
|
|
273
|
+
}, 300); // Match CSS transition duration
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Close the topmost modal
|
|
278
|
+
closeTopModal() {
|
|
279
|
+
const modalIds = Array.from(this.state.activeModals);
|
|
280
|
+
if (modalIds.length > 0) {
|
|
281
|
+
const topModalId = modalIds[modalIds.length - 1];
|
|
282
|
+
const modal = document.getElementById(topModalId);
|
|
283
|
+
if (modal) {
|
|
284
|
+
this.closeModal(modal);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Close all modals
|
|
290
|
+
closeAllModals() {
|
|
291
|
+
const modalIds = Array.from(this.state.activeModals);
|
|
292
|
+
modalIds.forEach(modalId => {
|
|
293
|
+
const modal = document.getElementById(modalId);
|
|
294
|
+
if (modal) {
|
|
295
|
+
this.closeModal(modal);
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Utility Methods
|
|
301
|
+
generateModalId() {
|
|
302
|
+
return `modal-${++this.state.modalCounter}-${Date.now()}`;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
escapeHtml(text) {
|
|
306
|
+
if (typeof text !== 'string') return '';
|
|
307
|
+
const div = document.createElement('div');
|
|
308
|
+
div.textContent = text;
|
|
309
|
+
return div.innerHTML;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Promise-based modal methods
|
|
313
|
+
showWarningModalAsync(title, message, options = {}) {
|
|
314
|
+
return new Promise((resolve) => {
|
|
315
|
+
this.showWarningModal(title, message, {
|
|
316
|
+
...options,
|
|
317
|
+
onConfirm: () => {
|
|
318
|
+
if (options.onConfirm) options.onConfirm();
|
|
319
|
+
resolve(true);
|
|
320
|
+
},
|
|
321
|
+
onCancel: () => {
|
|
322
|
+
if (options.onCancel) options.onCancel();
|
|
323
|
+
resolve(false);
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
showConfirmModalAsync(title, message, options = {}) {
|
|
330
|
+
return new Promise((resolve) => {
|
|
331
|
+
this.showConfirmModal(title, message, {
|
|
332
|
+
...options,
|
|
333
|
+
onConfirm: async () => {
|
|
334
|
+
try {
|
|
335
|
+
let result = true;
|
|
336
|
+
if (options.onConfirm) {
|
|
337
|
+
result = await options.onConfirm();
|
|
338
|
+
// If onConfirm returns false, don't resolve with true
|
|
339
|
+
if (result === false) {
|
|
340
|
+
resolve(false);
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
resolve(true);
|
|
345
|
+
} catch (error) {
|
|
346
|
+
console.error('[ModalManager] onConfirm error:', error);
|
|
347
|
+
resolve(false);
|
|
348
|
+
}
|
|
349
|
+
},
|
|
350
|
+
onCancel: async () => {
|
|
351
|
+
try {
|
|
352
|
+
if (options.onCancel) {
|
|
353
|
+
await options.onCancel();
|
|
354
|
+
}
|
|
355
|
+
resolve(false);
|
|
356
|
+
} catch (error) {
|
|
357
|
+
console.error('[ModalManager] onCancel error:', error);
|
|
358
|
+
resolve(false);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Modal state queries
|
|
366
|
+
hasActiveModals() {
|
|
367
|
+
return this.state.activeModals.size > 0;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
getActiveModalCount() {
|
|
371
|
+
return this.state.activeModals.size;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
isModalActive(modalId) {
|
|
375
|
+
return this.state.activeModals.has(modalId);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Quick notification modal (auto-close)
|
|
379
|
+
showNotificationModal(message, type = 'info', duration = 3000) {
|
|
380
|
+
const typeIcons = {
|
|
381
|
+
success: '✅',
|
|
382
|
+
error: '❌',
|
|
383
|
+
warning: '⚠️',
|
|
384
|
+
info: 'ℹ️'
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
const icon = typeIcons[type] || typeIcons.info;
|
|
388
|
+
const typeClass = `notification-${type}`;
|
|
389
|
+
|
|
390
|
+
const modalData = this.createModal(`
|
|
391
|
+
<div class="notification-content">
|
|
392
|
+
<div class="notification-icon">${icon}</div>
|
|
393
|
+
<div class="notification-message">${this.escapeHtml(message)}</div>
|
|
394
|
+
</div>
|
|
395
|
+
`, {
|
|
396
|
+
className: `notification-modal ${typeClass}`,
|
|
397
|
+
showCloseButton: false,
|
|
398
|
+
backdrop: false
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
// Auto-close after duration
|
|
402
|
+
if (duration > 0) {
|
|
403
|
+
setTimeout(() => {
|
|
404
|
+
modalData.close();
|
|
405
|
+
}, duration);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return modalData.modalId;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// Quick input modal
|
|
412
|
+
showInputModal(title, placeholder = '', defaultValue = '', options = {}) {
|
|
413
|
+
const {
|
|
414
|
+
inputType = 'text',
|
|
415
|
+
confirmText = 'OK',
|
|
416
|
+
cancelText = 'Cancel',
|
|
417
|
+
onConfirm = null,
|
|
418
|
+
onCancel = null,
|
|
419
|
+
validator = null
|
|
420
|
+
} = options;
|
|
421
|
+
|
|
422
|
+
return new Promise((resolve) => {
|
|
423
|
+
const inputId = `input-${Date.now()}`;
|
|
424
|
+
|
|
425
|
+
const modalData = this.createModal(`
|
|
426
|
+
<div class="input-modal-content">
|
|
427
|
+
<label for="${inputId}" class="input-label">${title}</label>
|
|
428
|
+
<input type="${inputType}" id="${inputId}" class="modal-input"
|
|
429
|
+
placeholder="${this.escapeHtml(placeholder)}"
|
|
430
|
+
value="${this.escapeHtml(defaultValue)}">
|
|
431
|
+
<div class="modal-footer">
|
|
432
|
+
<button class="btn-secondary input-cancel-btn">${this.escapeHtml(cancelText)}</button>
|
|
433
|
+
<button class="btn-primary input-confirm-btn">${this.escapeHtml(confirmText)}</button>
|
|
434
|
+
</div>
|
|
435
|
+
</div>
|
|
436
|
+
`, {
|
|
437
|
+
className: 'input-modal',
|
|
438
|
+
showCloseButton: true
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
const input = document.getElementById(inputId);
|
|
442
|
+
const confirmBtn = modalData.modal.querySelector('.input-confirm-btn');
|
|
443
|
+
const cancelBtn = modalData.modal.querySelector('.input-cancel-btn');
|
|
444
|
+
|
|
445
|
+
// Focus input
|
|
446
|
+
setTimeout(() => input.focus(), 100);
|
|
447
|
+
|
|
448
|
+
const handleConfirm = () => {
|
|
449
|
+
const value = input.value.trim();
|
|
450
|
+
|
|
451
|
+
if (validator && !validator(value)) {
|
|
452
|
+
return; // Don't close modal if validation fails
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
modalData.close();
|
|
456
|
+
if (onConfirm) onConfirm(value);
|
|
457
|
+
resolve(value);
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
const handleCancel = () => {
|
|
461
|
+
modalData.close();
|
|
462
|
+
if (onCancel) onCancel();
|
|
463
|
+
resolve(null);
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
confirmBtn.addEventListener('click', handleConfirm);
|
|
467
|
+
cancelBtn.addEventListener('click', handleCancel);
|
|
468
|
+
|
|
469
|
+
// Enter key to confirm
|
|
470
|
+
input.addEventListener('keydown', (e) => {
|
|
471
|
+
if (e.key === 'Enter') {
|
|
472
|
+
handleConfirm();
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// Get current state
|
|
479
|
+
getState() {
|
|
480
|
+
return { ...this.state };
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// Export for use in other modules
|
|
485
|
+
if (typeof window !== 'undefined') {
|
|
486
|
+
window.VibeSurfModalManager = VibeSurfModalManager;
|
|
487
|
+
}
|
|
@@ -7,7 +7,7 @@ class VibeSurfSessionManager {
|
|
|
7
7
|
this.currentSession = null;
|
|
8
8
|
this.activityLogs = [];
|
|
9
9
|
this.pollingInterval = null;
|
|
10
|
-
this.pollingFrequency =
|
|
10
|
+
this.pollingFrequency = 300; // 300ms for faster response
|
|
11
11
|
this.isPolling = false;
|
|
12
12
|
this.eventListeners = new Map();
|
|
13
13
|
|
|
@@ -201,8 +201,18 @@ class VibeSurfSessionManager {
|
|
|
201
201
|
}
|
|
202
202
|
|
|
203
203
|
try {
|
|
204
|
-
|
|
205
|
-
|
|
204
|
+
// Stop any existing polling before starting new task
|
|
205
|
+
this.stopActivityPolling();
|
|
206
|
+
|
|
207
|
+
// Reset activity logs for new task to ensure proper index synchronization
|
|
208
|
+
this.activityLogs = [];
|
|
209
|
+
|
|
210
|
+
// Sync with server logs to get the correct starting state
|
|
211
|
+
try {
|
|
212
|
+
await this.syncActivityLogsFromServer();
|
|
213
|
+
} catch (error) {
|
|
214
|
+
this.activityLogs = [];
|
|
215
|
+
}
|
|
206
216
|
|
|
207
217
|
const taskPayload = {
|
|
208
218
|
session_id: this.currentSession.id,
|
|
@@ -220,12 +230,12 @@ class VibeSurfSessionManager {
|
|
|
220
230
|
submittedAt: new Date().toISOString()
|
|
221
231
|
};
|
|
222
232
|
|
|
223
|
-
// Start activity polling
|
|
224
|
-
this.startActivityPolling();
|
|
225
|
-
|
|
226
233
|
// Store updated session
|
|
227
234
|
await this.storeSessionData();
|
|
228
235
|
|
|
236
|
+
// Start polling after task submission and sync
|
|
237
|
+
this.startActivityPolling();
|
|
238
|
+
|
|
229
239
|
this.emit('taskSubmitted', {
|
|
230
240
|
sessionId: this.currentSession.id,
|
|
231
241
|
task: this.currentSession.currentTask,
|
|
@@ -273,6 +283,13 @@ class VibeSurfSessionManager {
|
|
|
273
283
|
await this.storeSessionData();
|
|
274
284
|
}
|
|
275
285
|
|
|
286
|
+
// Sync activity logs before resuming polling to ensure index consistency
|
|
287
|
+
try {
|
|
288
|
+
await this.syncActivityLogsFromServer();
|
|
289
|
+
} catch (error) {
|
|
290
|
+
// Continue with existing logs if sync fails
|
|
291
|
+
}
|
|
292
|
+
|
|
276
293
|
// Restart polling when task is resumed
|
|
277
294
|
this.startActivityPolling();
|
|
278
295
|
|
|
@@ -298,6 +315,13 @@ class VibeSurfSessionManager {
|
|
|
298
315
|
|
|
299
316
|
// Stop polling when task is stopped
|
|
300
317
|
this.stopActivityPolling();
|
|
318
|
+
|
|
319
|
+
// Sync final activity logs to capture any termination messages
|
|
320
|
+
try {
|
|
321
|
+
await this.syncActivityLogsFromServer();
|
|
322
|
+
} catch (error) {
|
|
323
|
+
// Continue if sync fails
|
|
324
|
+
}
|
|
301
325
|
|
|
302
326
|
this.emit('taskStopped', { sessionId: this.currentSession?.id, response });
|
|
303
327
|
|
|
@@ -309,6 +333,24 @@ class VibeSurfSessionManager {
|
|
|
309
333
|
}
|
|
310
334
|
}
|
|
311
335
|
|
|
336
|
+
async addNewTaskToPaused(newTaskDescription) {
|
|
337
|
+
try {
|
|
338
|
+
const response = await this.apiClient.addNewTask(newTaskDescription);
|
|
339
|
+
|
|
340
|
+
this.emit('newTaskAdded', {
|
|
341
|
+
sessionId: this.currentSession?.id,
|
|
342
|
+
newTask: newTaskDescription,
|
|
343
|
+
response
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
return response;
|
|
347
|
+
} catch (error) {
|
|
348
|
+
console.error('[SessionManager] Add new task failed:', error);
|
|
349
|
+
this.emit('taskError', { error: error.message, action: 'add_new_task' });
|
|
350
|
+
throw error;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
312
354
|
// Activity polling
|
|
313
355
|
startActivityPolling() {
|
|
314
356
|
if (this.isPolling) {
|
|
@@ -358,15 +400,14 @@ class VibeSurfSessionManager {
|
|
|
358
400
|
|
|
359
401
|
if (response && activityLog) {
|
|
360
402
|
const prevActivityLog = this.activityLogs.length > 0 ? this.activityLogs[this.activityLogs.length - 1] : null;
|
|
361
|
-
|
|
362
|
-
// 检查是否为新的、不重复的activity log
|
|
403
|
+
|
|
363
404
|
const isNewLog = !prevActivityLog || !this.areLogsEqual(prevActivityLog, activityLog);
|
|
364
405
|
|
|
365
406
|
if (isNewLog) {
|
|
366
407
|
// New activity log received
|
|
367
408
|
const newLog = { ...activityLog };
|
|
368
409
|
|
|
369
|
-
// Add timestamp if not present
|
|
410
|
+
// Add timestamp if not present - this should now be handled by UI
|
|
370
411
|
if (!newLog.timestamp) {
|
|
371
412
|
newLog.timestamp = new Date().toISOString();
|
|
372
413
|
}
|
|
@@ -456,7 +497,7 @@ class VibeSurfSessionManager {
|
|
|
456
497
|
if (missingLogs.length > 0) {
|
|
457
498
|
|
|
458
499
|
for (const log of missingLogs) {
|
|
459
|
-
// Add timestamp if not present
|
|
500
|
+
// Add timestamp if not present - this should now be handled by UI
|
|
460
501
|
if (!log.timestamp) {
|
|
461
502
|
log.timestamp = new Date().toISOString();
|
|
462
503
|
}
|
|
@@ -492,7 +533,7 @@ class VibeSurfSessionManager {
|
|
|
492
533
|
// 完全同步:用服务器端的logs替换本地logs
|
|
493
534
|
const previousCount = this.activityLogs.length;
|
|
494
535
|
|
|
495
|
-
//
|
|
536
|
+
// Add timestamp to logs that don't have them
|
|
496
537
|
const processedLogs = serverLogs.map(log => ({
|
|
497
538
|
...log,
|
|
498
539
|
timestamp: log.timestamp || new Date().toISOString()
|