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.

Files changed (69) hide show
  1. vibe_surf/_version.py +2 -2
  2. vibe_surf/agents/browser_use_agent.py +68 -45
  3. vibe_surf/agents/prompts/report_writer_prompt.py +73 -0
  4. vibe_surf/agents/prompts/vibe_surf_prompt.py +85 -172
  5. vibe_surf/agents/report_writer_agent.py +380 -226
  6. vibe_surf/agents/vibe_surf_agent.py +878 -814
  7. vibe_surf/agents/views.py +130 -0
  8. vibe_surf/backend/api/activity.py +3 -1
  9. vibe_surf/backend/api/browser.py +70 -0
  10. vibe_surf/backend/api/config.py +8 -5
  11. vibe_surf/backend/api/files.py +59 -50
  12. vibe_surf/backend/api/models.py +2 -2
  13. vibe_surf/backend/api/task.py +47 -13
  14. vibe_surf/backend/database/manager.py +24 -18
  15. vibe_surf/backend/database/queries.py +199 -192
  16. vibe_surf/backend/database/schemas.py +1 -1
  17. vibe_surf/backend/main.py +80 -3
  18. vibe_surf/backend/shared_state.py +30 -35
  19. vibe_surf/backend/utils/encryption.py +3 -1
  20. vibe_surf/backend/utils/llm_factory.py +41 -36
  21. vibe_surf/browser/agent_browser_session.py +308 -62
  22. vibe_surf/browser/browser_manager.py +71 -100
  23. vibe_surf/browser/utils.py +5 -3
  24. vibe_surf/browser/watchdogs/dom_watchdog.py +0 -45
  25. vibe_surf/chrome_extension/background.js +88 -0
  26. vibe_surf/chrome_extension/manifest.json +3 -1
  27. vibe_surf/chrome_extension/scripts/api-client.js +13 -0
  28. vibe_surf/chrome_extension/scripts/file-manager.js +482 -0
  29. vibe_surf/chrome_extension/scripts/history-manager.js +658 -0
  30. vibe_surf/chrome_extension/scripts/modal-manager.js +487 -0
  31. vibe_surf/chrome_extension/scripts/session-manager.js +52 -11
  32. vibe_surf/chrome_extension/scripts/settings-manager.js +1214 -0
  33. vibe_surf/chrome_extension/scripts/ui-manager.js +1530 -3163
  34. vibe_surf/chrome_extension/sidepanel.html +47 -7
  35. vibe_surf/chrome_extension/styles/activity.css +934 -0
  36. vibe_surf/chrome_extension/styles/base.css +76 -0
  37. vibe_surf/chrome_extension/styles/history-modal.css +791 -0
  38. vibe_surf/chrome_extension/styles/input.css +568 -0
  39. vibe_surf/chrome_extension/styles/layout.css +186 -0
  40. vibe_surf/chrome_extension/styles/responsive.css +454 -0
  41. vibe_surf/chrome_extension/styles/settings-environment.css +165 -0
  42. vibe_surf/chrome_extension/styles/settings-forms.css +389 -0
  43. vibe_surf/chrome_extension/styles/settings-modal.css +141 -0
  44. vibe_surf/chrome_extension/styles/settings-profiles.css +244 -0
  45. vibe_surf/chrome_extension/styles/settings-responsive.css +144 -0
  46. vibe_surf/chrome_extension/styles/settings-utilities.css +25 -0
  47. vibe_surf/chrome_extension/styles/variables.css +54 -0
  48. vibe_surf/cli.py +5 -22
  49. vibe_surf/common.py +35 -0
  50. vibe_surf/llm/openai_compatible.py +148 -93
  51. vibe_surf/logger.py +99 -0
  52. vibe_surf/{controller/vibesurf_tools.py → tools/browser_use_tools.py} +233 -221
  53. vibe_surf/tools/file_system.py +415 -0
  54. vibe_surf/{controller → tools}/mcp_client.py +4 -3
  55. vibe_surf/tools/report_writer_tools.py +21 -0
  56. vibe_surf/tools/vibesurf_tools.py +657 -0
  57. vibe_surf/tools/views.py +120 -0
  58. {vibesurf-0.1.9a6.dist-info → vibesurf-0.1.11.dist-info}/METADATA +23 -3
  59. vibesurf-0.1.11.dist-info/RECORD +93 -0
  60. vibe_surf/chrome_extension/styles/main.css +0 -2338
  61. vibe_surf/chrome_extension/styles/settings.css +0 -1100
  62. vibe_surf/controller/file_system.py +0 -53
  63. vibe_surf/controller/views.py +0 -37
  64. vibesurf-0.1.9a6.dist-info/RECORD +0 -71
  65. /vibe_surf/{controller → tools}/__init__.py +0 -0
  66. {vibesurf-0.1.9a6.dist-info → vibesurf-0.1.11.dist-info}/WHEEL +0 -0
  67. {vibesurf-0.1.9a6.dist-info → vibesurf-0.1.11.dist-info}/entry_points.txt +0 -0
  68. {vibesurf-0.1.9a6.dist-info → vibesurf-0.1.11.dist-info}/licenses/LICENSE +0 -0
  69. {vibesurf-0.1.9a6.dist-info → vibesurf-0.1.11.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,658 @@
1
+ // History Manager - Handles history modal, recent tasks, and session history
2
+ // Manages history display, pagination, search, and filtering
3
+
4
+ class VibeSurfHistoryManager {
5
+ constructor(apiClient) {
6
+ this.apiClient = apiClient;
7
+ this.state = {
8
+ historyMode: 'recent', // 'recent' or 'all'
9
+ currentPage: 1,
10
+ totalPages: 1,
11
+ pageSize: 10,
12
+ searchQuery: '',
13
+ statusFilter: 'all',
14
+ recentTasks: [],
15
+ allSessions: []
16
+ };
17
+ this.elements = {};
18
+ this.eventListeners = new Map();
19
+
20
+ this.bindElements();
21
+ this.bindEvents();
22
+ }
23
+
24
+ bindElements() {
25
+ this.elements = {
26
+ // History Modal
27
+ historyModal: document.getElementById('history-modal'),
28
+
29
+ // Recent Tasks Section
30
+ recentTasksList: document.getElementById('recent-tasks-list'),
31
+ viewMoreTasksBtn: document.getElementById('view-more-tasks-btn'),
32
+
33
+ // All Sessions Section
34
+ allSessionsSection: document.getElementById('all-sessions-section'),
35
+ backToRecentBtn: document.getElementById('back-to-recent-btn'),
36
+ sessionSearch: document.getElementById('session-search'),
37
+ sessionFilter: document.getElementById('session-filter'),
38
+ allSessionsList: document.getElementById('all-sessions-list'),
39
+
40
+ // Pagination
41
+ prevPageBtn: document.getElementById('prev-page-btn'),
42
+ nextPageBtn: document.getElementById('next-page-btn'),
43
+ pageInfo: document.getElementById('page-info')
44
+ };
45
+ }
46
+
47
+ bindEvents() {
48
+ // History modal close button
49
+ const historyModalClose = this.elements.historyModal?.querySelector('.modal-close');
50
+ if (historyModalClose) {
51
+ historyModalClose.addEventListener('click', this.hideModal.bind(this));
52
+ }
53
+
54
+ // History modal overlay click to close
55
+ const historyModalOverlay = this.elements.historyModal?.querySelector('.modal-overlay');
56
+ if (historyModalOverlay) {
57
+ historyModalOverlay.addEventListener('click', (event) => {
58
+ if (event.target === historyModalOverlay) {
59
+ this.hideModal();
60
+ }
61
+ });
62
+ }
63
+
64
+ // View More Tasks button
65
+ this.elements.viewMoreTasksBtn?.addEventListener('click', this.handleViewMoreTasks.bind(this));
66
+
67
+ // Back to Recent button
68
+ this.elements.backToRecentBtn?.addEventListener('click', this.handleBackToRecent.bind(this));
69
+
70
+ // Search and filter
71
+ this.elements.sessionSearch?.addEventListener('input', this.handleSessionSearch.bind(this));
72
+ this.elements.sessionFilter?.addEventListener('change', this.handleSessionFilter.bind(this));
73
+
74
+ // Pagination
75
+ this.elements.prevPageBtn?.addEventListener('click', this.handlePrevPage.bind(this));
76
+ this.elements.nextPageBtn?.addEventListener('click', this.handleNextPage.bind(this));
77
+
78
+ // Global keyboard shortcuts for this modal
79
+ document.addEventListener('keydown', this.handleKeydown.bind(this));
80
+ }
81
+
82
+ handleKeydown(event) {
83
+ // Close history modal on Escape key
84
+ if (event.key === 'Escape') {
85
+ if (this.elements.historyModal && !this.elements.historyModal.classList.contains('hidden')) {
86
+ this.hideModal();
87
+ }
88
+ }
89
+ }
90
+
91
+ // Event system for communicating with main UI manager
92
+ on(event, callback) {
93
+ if (!this.eventListeners.has(event)) {
94
+ this.eventListeners.set(event, []);
95
+ }
96
+ this.eventListeners.get(event).push(callback);
97
+ }
98
+
99
+ emit(event, data) {
100
+ if (this.eventListeners.has(event)) {
101
+ this.eventListeners.get(event).forEach(callback => {
102
+ try {
103
+ callback(data);
104
+ } catch (error) {
105
+ console.error(`[HistoryManager] Event callback error for ${event}:`, error);
106
+ }
107
+ });
108
+ }
109
+ }
110
+
111
+ // Public interface for showing history
112
+ async showHistory() {
113
+ try {
114
+ this.emit('loading', { message: 'Loading recent tasks...' });
115
+
116
+ // Reset to recent tasks view
117
+ this.state.historyMode = 'recent';
118
+ await this.loadRecentTasks();
119
+ this.displayHistoryModal();
120
+
121
+ this.emit('loading', { hide: true });
122
+ } catch (error) {
123
+ this.emit('loading', { hide: true });
124
+ this.emit('error', { message: `Failed to load history: ${error.message}` });
125
+ }
126
+ }
127
+
128
+ // Event Handlers
129
+ async handleViewMoreTasks() {
130
+ try {
131
+ console.log('[HistoryManager] View More Tasks clicked');
132
+ this.emit('loading', { message: 'Loading all sessions...' });
133
+
134
+ // Switch to all sessions view
135
+ this.state.historyMode = 'all';
136
+ console.log('[HistoryManager] Set history mode to "all"');
137
+
138
+ await this.loadAllSessions();
139
+ console.log('[HistoryManager] All sessions loaded, switching view');
140
+
141
+ this.displayAllSessionsView();
142
+ console.log('[HistoryManager] All sessions view displayed');
143
+
144
+ this.emit('loading', { hide: true });
145
+ } catch (error) {
146
+ this.emit('loading', { hide: true });
147
+ console.error('[HistoryManager] Error in handleViewMoreTasks:', error);
148
+ this.emit('error', { message: `Failed to load sessions: ${error.message}` });
149
+ }
150
+ }
151
+
152
+ handleBackToRecent() {
153
+ this.state.historyMode = 'recent';
154
+ this.displayRecentTasksView();
155
+ }
156
+
157
+ handleSessionSearch(event) {
158
+ this.state.searchQuery = event.target.value.trim().toLowerCase();
159
+ this.filterAndDisplaySessions();
160
+ }
161
+
162
+ handleSessionFilter(event) {
163
+ this.state.statusFilter = event.target.value;
164
+ this.filterAndDisplaySessions();
165
+ }
166
+
167
+ handlePrevPage() {
168
+ if (this.state.currentPage > 1) {
169
+ this.state.currentPage--;
170
+ this.filterAndDisplaySessions();
171
+ }
172
+ }
173
+
174
+ handleNextPage() {
175
+ if (this.state.currentPage < this.state.totalPages) {
176
+ this.state.currentPage++;
177
+ this.filterAndDisplaySessions();
178
+ }
179
+ }
180
+
181
+ // Data Loading Methods
182
+ async loadRecentTasks() {
183
+ try {
184
+ console.log('[HistoryManager] Loading recent tasks...');
185
+ const response = await this.apiClient.getRecentTasks();
186
+
187
+ // Handle API response structure: { tasks: [...], total_count: ..., limit: ... }
188
+ let tasks = [];
189
+ if (response && response.tasks && Array.isArray(response.tasks)) {
190
+ tasks = response.tasks;
191
+ } else if (response && Array.isArray(response)) {
192
+ tasks = response;
193
+ } else if (response && response.data && Array.isArray(response.data)) {
194
+ tasks = response.data;
195
+ }
196
+
197
+ // Take only the first 3 most recent tasks
198
+ this.state.recentTasks = tasks.slice(0, 3);
199
+ console.log('[HistoryManager] Recent tasks loaded:', this.state.recentTasks.length);
200
+
201
+ return this.state.recentTasks;
202
+ } catch (error) {
203
+ console.error('[HistoryManager] Failed to load recent tasks:', error);
204
+ this.state.recentTasks = [];
205
+ throw error;
206
+ }
207
+ }
208
+
209
+ async loadAllSessions() {
210
+ try {
211
+ console.log('[HistoryManager] Loading all sessions...');
212
+ const response = await this.apiClient.getAllSessions();
213
+
214
+ // Handle API response structure: { sessions: [...], total_count: ..., limit: ..., offset: ... }
215
+ let sessions = [];
216
+ if (response && response.sessions && Array.isArray(response.sessions)) {
217
+ sessions = response.sessions;
218
+ } else if (response && Array.isArray(response)) {
219
+ sessions = response;
220
+ } else if (response && response.data && Array.isArray(response.data)) {
221
+ sessions = response.data;
222
+ }
223
+
224
+ this.state.allSessions = sessions;
225
+ console.log('[HistoryManager] All sessions loaded:', this.state.allSessions.length);
226
+
227
+ return this.state.allSessions;
228
+ } catch (error) {
229
+ console.error('[HistoryManager] Failed to load all sessions:', error);
230
+ this.state.allSessions = [];
231
+ throw error;
232
+ }
233
+ }
234
+
235
+ // Display Methods
236
+ displayHistoryModal() {
237
+ if (this.state.historyMode === 'recent') {
238
+ this.displayRecentTasksView();
239
+ } else {
240
+ this.displayAllSessionsView();
241
+ }
242
+ this.showModal();
243
+ }
244
+
245
+ displayRecentTasksView() {
246
+ console.log('[HistoryManager] Switching to recent tasks view');
247
+
248
+ // Show recent tasks section and hide all sessions section
249
+ if (this.elements.recentTasksList && this.elements.allSessionsSection) {
250
+ const recentParent = this.elements.recentTasksList.parentElement;
251
+ if (recentParent) {
252
+ recentParent.classList.remove('hidden');
253
+ recentParent.style.display = 'block';
254
+ console.log('[HistoryManager] Showed recent tasks section');
255
+ }
256
+ this.elements.allSessionsSection.classList.add('hidden');
257
+ this.elements.allSessionsSection.style.display = 'none';
258
+ console.log('[HistoryManager] Hidden all sessions section');
259
+ }
260
+
261
+ this.renderRecentTasks();
262
+ }
263
+
264
+ displayAllSessionsView() {
265
+ console.log('[HistoryManager] Switching to all sessions view');
266
+ console.log('[HistoryManager] Elements check:', {
267
+ recentTasksList: !!this.elements.recentTasksList,
268
+ allSessionsSection: !!this.elements.allSessionsSection,
269
+ recentTasksParent: !!this.elements.recentTasksList?.parentElement
270
+ });
271
+
272
+ // Hide recent tasks section and show all sessions section
273
+ if (this.elements.recentTasksList && this.elements.allSessionsSection) {
274
+ const recentParent = this.elements.recentTasksList.parentElement;
275
+ if (recentParent) {
276
+ recentParent.style.display = 'none';
277
+ recentParent.classList.add('hidden');
278
+ console.log('[HistoryManager] Hidden recent tasks section');
279
+ }
280
+
281
+ // Remove hidden class and set display block
282
+ this.elements.allSessionsSection.classList.remove('hidden');
283
+ this.elements.allSessionsSection.style.display = 'block';
284
+ console.log('[HistoryManager] Showed all sessions section - removed hidden class and set display block');
285
+
286
+ // Debug: Check computed styles
287
+ const computedStyle = window.getComputedStyle(this.elements.allSessionsSection);
288
+ console.log('[HistoryManager] All sessions section computed display:', computedStyle.display);
289
+ console.log('[HistoryManager] All sessions section classList:', this.elements.allSessionsSection.classList.toString());
290
+
291
+ } else {
292
+ console.error('[HistoryManager] Missing elements for view switching:', {
293
+ recentTasksList: !!this.elements.recentTasksList,
294
+ allSessionsSection: !!this.elements.allSessionsSection
295
+ });
296
+ }
297
+
298
+ // Reset search and filter
299
+ this.state.currentPage = 1;
300
+ this.state.searchQuery = '';
301
+ this.state.statusFilter = 'all';
302
+
303
+ if (this.elements.sessionSearch) {
304
+ this.elements.sessionSearch.value = '';
305
+ }
306
+ if (this.elements.sessionFilter) {
307
+ this.elements.sessionFilter.value = 'all';
308
+ }
309
+
310
+ console.log('[HistoryManager] About to filter and display sessions');
311
+ this.filterAndDisplaySessions();
312
+ }
313
+
314
+ renderRecentTasks() {
315
+ if (!this.elements.recentTasksList) return;
316
+
317
+ this.elements.recentTasksList.innerHTML = '';
318
+
319
+ if (this.state.recentTasks.length === 0) {
320
+ this.elements.recentTasksList.innerHTML = `
321
+ <div class="empty-state">
322
+ <div class="empty-state-icon">📝</div>
323
+ <div class="empty-state-title">No Recent Tasks</div>
324
+ <div class="empty-state-description">Start a new task to see it here.</div>
325
+ </div>
326
+ `;
327
+ return;
328
+ }
329
+
330
+ this.state.recentTasks.forEach(task => {
331
+ const taskItem = this.createTaskItem(task);
332
+ this.elements.recentTasksList.appendChild(taskItem);
333
+ });
334
+ }
335
+
336
+ filterAndDisplaySessions() {
337
+ if (!this.elements.allSessionsList) {
338
+ console.error('[HistoryManager] allSessionsList element not found');
339
+ return;
340
+ }
341
+
342
+ console.log('[HistoryManager] Filtering sessions. Total sessions:', this.state.allSessions.length);
343
+ console.log('[HistoryManager] Search query:', this.state.searchQuery);
344
+ console.log('[HistoryManager] Status filter:', this.state.statusFilter);
345
+
346
+ let filteredSessions = [...this.state.allSessions]; // Create copy
347
+
348
+ // Apply search filter
349
+ if (this.state.searchQuery) {
350
+ filteredSessions = filteredSessions.filter(session =>
351
+ session.session_id.toLowerCase().includes(this.state.searchQuery) ||
352
+ (session.description && session.description.toLowerCase().includes(this.state.searchQuery))
353
+ );
354
+ }
355
+
356
+ // Apply status filter
357
+ if (this.state.statusFilter !== 'all') {
358
+ filteredSessions = filteredSessions.filter(session =>
359
+ (session.status || 'active').toLowerCase() === this.state.statusFilter.toLowerCase()
360
+ );
361
+ }
362
+
363
+ console.log('[HistoryManager] Filtered sessions count:', filteredSessions.length);
364
+
365
+ // Calculate pagination
366
+ const totalSessions = filteredSessions.length;
367
+ this.state.totalPages = Math.ceil(totalSessions / this.state.pageSize);
368
+
369
+ // Ensure current page is valid
370
+ if (this.state.currentPage > this.state.totalPages) {
371
+ this.state.currentPage = Math.max(1, this.state.totalPages);
372
+ }
373
+
374
+ // Get sessions for current page
375
+ const startIndex = (this.state.currentPage - 1) * this.state.pageSize;
376
+ const endIndex = startIndex + this.state.pageSize;
377
+ const paginatedSessions = filteredSessions.slice(startIndex, endIndex);
378
+
379
+ console.log('[HistoryManager] Paginated sessions for display:', paginatedSessions.length);
380
+
381
+ // Render sessions
382
+ this.renderSessionsList(paginatedSessions);
383
+
384
+ // Update pagination controls
385
+ this.updatePaginationControls();
386
+ }
387
+
388
+ renderSessionsList(sessions) {
389
+ if (!this.elements.allSessionsList) {
390
+ console.error('[HistoryManager] allSessionsList element not found for rendering');
391
+ return;
392
+ }
393
+
394
+ console.log('[HistoryManager] Rendering sessions list with', sessions.length, 'sessions');
395
+
396
+ this.elements.allSessionsList.innerHTML = '';
397
+
398
+ if (sessions.length === 0) {
399
+ console.log('[HistoryManager] No sessions to display, showing empty state');
400
+ this.elements.allSessionsList.innerHTML = `
401
+ <div class="empty-state">
402
+ <div class="empty-state-icon">🔍</div>
403
+ <div class="empty-state-title">No Sessions Found</div>
404
+ <div class="empty-state-description">Try adjusting your search or filter criteria.</div>
405
+ </div>
406
+ `;
407
+ return;
408
+ }
409
+
410
+ sessions.forEach((session, index) => {
411
+ console.log(`[HistoryManager] Creating session item ${index + 1}:`, session.session_id);
412
+ const sessionItem = this.createSessionItem(session);
413
+ this.elements.allSessionsList.appendChild(sessionItem);
414
+ });
415
+
416
+ console.log('[HistoryManager] Sessions list rendered successfully');
417
+ }
418
+
419
+ updatePaginationControls() {
420
+ // Update pagination buttons
421
+ if (this.elements.prevPageBtn) {
422
+ this.elements.prevPageBtn.disabled = this.state.currentPage <= 1;
423
+ }
424
+
425
+ if (this.elements.nextPageBtn) {
426
+ this.elements.nextPageBtn.disabled = this.state.currentPage >= this.state.totalPages;
427
+ }
428
+
429
+ // Update page info
430
+ if (this.elements.pageInfo) {
431
+ if (this.state.totalPages === 0) {
432
+ this.elements.pageInfo.textContent = 'No results';
433
+ } else {
434
+ this.elements.pageInfo.textContent = `Page ${this.state.currentPage} of ${this.state.totalPages}`;
435
+ }
436
+ }
437
+ }
438
+
439
+ // Item Creation Methods
440
+ createTaskItem(task) {
441
+ const item = document.createElement('div');
442
+ item.className = 'recent-task-item';
443
+
444
+ const sessionId = task.session_id || 'Unknown';
445
+ const taskDesc = task.description || task.task_description || 'No description';
446
+ const timestamp = new Date(task.created_at || task.timestamp || Date.now()).toLocaleString();
447
+ const status = task.status || 'completed';
448
+
449
+ item.innerHTML = `
450
+ <div class="task-item-header">
451
+ <div class="task-session-id">${sessionId}</div>
452
+ <div class="task-timestamp">${timestamp}</div>
453
+ </div>
454
+ <div class="task-description">${this.truncateText(taskDesc, 100)}</div>
455
+ <div class="task-status">
456
+ <span class="status-dot ${status}"></span>
457
+ <span class="status-text">${status}</span>
458
+ </div>
459
+ `;
460
+
461
+ item.addEventListener('click', () => {
462
+ this.handleTaskItemClick(task);
463
+ });
464
+
465
+ return item;
466
+ }
467
+
468
+ createSessionItem(session) {
469
+ const item = document.createElement('div');
470
+ item.className = 'session-item';
471
+
472
+ const sessionId = session.session_id || 'Unknown';
473
+ const createdAt = new Date(session.created_at || session.timestamp || Date.now()).toLocaleString();
474
+ const lastActivity = session.last_activity ? new Date(session.last_activity).toLocaleString() : 'No activity';
475
+ const taskCount = session.task_count || 0;
476
+ const status = session.status || 'active';
477
+
478
+ item.innerHTML = `
479
+ <div class="session-item-header">
480
+ <div class="session-id">${sessionId}</div>
481
+ <div class="session-timestamp">${createdAt}</div>
482
+ </div>
483
+ <div class="session-details">
484
+ <div class="session-info">
485
+ <span class="session-task-count">${taskCount} task(s)</span>
486
+ <span class="session-last-activity">Last: ${lastActivity}</span>
487
+ </div>
488
+ <div class="session-status">
489
+ <span class="status-dot ${status}"></span>
490
+ <span class="status-text">${status}</span>
491
+ </div>
492
+ </div>
493
+ `;
494
+
495
+ // Add enhanced click handler with debugging
496
+ item.addEventListener('click', (event) => {
497
+ event.preventDefault();
498
+ event.stopPropagation();
499
+ this.handleSessionItemClick(session);
500
+ });
501
+
502
+ // Add visual feedback for clickability
503
+ item.style.cursor = 'pointer';
504
+ item.setAttribute('title', `Click to load session: ${sessionId}`);
505
+
506
+ return item;
507
+ }
508
+
509
+ // Click Handlers
510
+ async handleTaskItemClick(task) {
511
+ try {
512
+ console.log('[HistoryManager] Task item clicked:', task.session_id);
513
+ const sessionId = task.session_id;
514
+ if (!sessionId) {
515
+ console.error('[HistoryManager] No session ID found in task data:', task);
516
+ this.emit('error', { message: 'Invalid task - no session ID found' });
517
+ return;
518
+ }
519
+
520
+ // Close the modal first
521
+ this.hideModal();
522
+
523
+ // Emit event to load session
524
+ this.emit('loadSession', { sessionId });
525
+
526
+ } catch (error) {
527
+ console.error('[HistoryManager] Error in handleTaskItemClick:', error);
528
+ this.emit('error', { message: `Failed to load task session: ${error.message}` });
529
+ }
530
+ }
531
+
532
+ async handleSessionItemClick(session) {
533
+ try {
534
+ console.log('[HistoryManager] Session item clicked:', session.session_id);
535
+ const sessionId = session.session_id;
536
+ if (!sessionId) {
537
+ console.error('[HistoryManager] No session ID found in session data:', session);
538
+ this.emit('error', { message: 'Invalid session - no session ID found' });
539
+ return;
540
+ }
541
+
542
+ // Close the modal first
543
+ this.hideModal();
544
+
545
+ // Emit event to load session
546
+ this.emit('loadSession', { sessionId });
547
+
548
+ } catch (error) {
549
+ console.error('[HistoryManager] Error in handleSessionItemClick:', error);
550
+ this.emit('error', { message: `Failed to load session: ${error.message}` });
551
+ }
552
+ }
553
+
554
+ // Utility Methods
555
+ truncateText(text, maxLength) {
556
+ if (!text) return '';
557
+ if (text.length <= maxLength) return text;
558
+ return text.substring(0, maxLength) + '...';
559
+ }
560
+
561
+ escapeHtml(text) {
562
+ if (typeof text !== 'string') return '';
563
+ const div = document.createElement('div');
564
+ div.textContent = text;
565
+ return div.innerHTML;
566
+ }
567
+
568
+ // Public interface
569
+ getState() {
570
+ return { ...this.state };
571
+ }
572
+
573
+ showModal() {
574
+ if (this.elements.historyModal) {
575
+ this.elements.historyModal.classList.remove('hidden');
576
+ this.elements.historyModal.classList.add('scale-in');
577
+ }
578
+ }
579
+
580
+ hideModal() {
581
+ if (this.elements.historyModal) {
582
+ this.elements.historyModal.classList.add('hidden');
583
+ this.elements.historyModal.classList.remove('scale-in');
584
+ }
585
+ }
586
+
587
+ // Reset state when modal is closed
588
+ reset() {
589
+ this.state.historyMode = 'recent';
590
+ this.state.currentPage = 1;
591
+ this.state.searchQuery = '';
592
+ this.state.statusFilter = 'all';
593
+
594
+ if (this.elements.sessionSearch) {
595
+ this.elements.sessionSearch.value = '';
596
+ }
597
+ if (this.elements.sessionFilter) {
598
+ this.elements.sessionFilter.value = 'all';
599
+ }
600
+ }
601
+
602
+ // Create a specific history item (can be overridden for custom history items)
603
+ createHistoryItem(session) {
604
+ const item = document.createElement('div');
605
+ item.className = 'history-item';
606
+
607
+ const createdAt = new Date(session.createdAt || session.lastUpdated).toLocaleString();
608
+ const taskCount = session.taskHistory?.length || 0;
609
+
610
+ item.innerHTML = `
611
+ <div class="history-item-header">
612
+ <span class="history-session-id">${session.sessionId}</span>
613
+ <span class="history-timestamp">${createdAt}</span>
614
+ </div>
615
+ <div class="history-task">${taskCount} task(s)</div>
616
+ <div class="history-status">
617
+ <span class="status-dot ${session.status || 'active'}"></span>
618
+ ${session.status || 'active'}
619
+ </div>
620
+ `;
621
+
622
+ item.addEventListener('click', () => {
623
+ this.emit('loadSession', { sessionId: session.sessionId });
624
+ this.hideModal();
625
+ });
626
+
627
+ return item;
628
+ }
629
+
630
+ // Display generic history list (backwards compatibility)
631
+ displayHistoryList(sessions) {
632
+ if (!this.elements.recentTasksList) return;
633
+
634
+ this.elements.recentTasksList.innerHTML = '';
635
+
636
+ if (sessions.length === 0) {
637
+ this.elements.recentTasksList.innerHTML = `
638
+ <div class="empty-state">
639
+ <div class="empty-state-icon">📝</div>
640
+ <div class="empty-state-title">No Sessions Found</div>
641
+ <div class="empty-state-description">Create a new session to get started.</div>
642
+ </div>
643
+ `;
644
+ } else {
645
+ sessions.forEach(session => {
646
+ const item = this.createHistoryItem(session);
647
+ this.elements.recentTasksList.appendChild(item);
648
+ });
649
+ }
650
+
651
+ this.showModal();
652
+ }
653
+ }
654
+
655
+ // Export for use in other modules
656
+ if (typeof window !== 'undefined') {
657
+ window.VibeSurfHistoryManager = VibeSurfHistoryManager;
658
+ }