vibesurf 0.1.9a6__py3-none-any.whl → 0.1.10__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.
- vibe_surf/_version.py +2 -2
- vibe_surf/agents/vibe_surf_agent.py +25 -15
- vibe_surf/backend/api/browser.py +66 -0
- vibe_surf/backend/api/task.py +2 -1
- vibe_surf/backend/main.py +76 -1
- vibe_surf/backend/shared_state.py +2 -0
- vibe_surf/browser/agent_browser_session.py +312 -62
- vibe_surf/browser/browser_manager.py +57 -92
- vibe_surf/browser/watchdogs/dom_watchdog.py +43 -43
- vibe_surf/chrome_extension/background.js +84 -0
- vibe_surf/chrome_extension/manifest.json +3 -1
- vibe_surf/chrome_extension/scripts/file-manager.js +526 -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 +31 -8
- vibe_surf/chrome_extension/scripts/settings-manager.js +1214 -0
- vibe_surf/chrome_extension/scripts/ui-manager.js +770 -3186
- vibe_surf/chrome_extension/sidepanel.html +27 -4
- vibe_surf/chrome_extension/styles/activity.css +574 -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 +429 -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 +1 -0
- vibe_surf/controller/vibesurf_tools.py +0 -2
- {vibesurf-0.1.9a6.dist-info → vibesurf-0.1.10.dist-info}/METADATA +18 -2
- {vibesurf-0.1.9a6.dist-info → vibesurf-0.1.10.dist-info}/RECORD +39 -23
- vibe_surf/chrome_extension/styles/main.css +0 -2338
- vibe_surf/chrome_extension/styles/settings.css +0 -1100
- {vibesurf-0.1.9a6.dist-info → vibesurf-0.1.10.dist-info}/WHEEL +0 -0
- {vibesurf-0.1.9a6.dist-info → vibesurf-0.1.10.dist-info}/entry_points.txt +0 -0
- {vibesurf-0.1.9a6.dist-info → vibesurf-0.1.10.dist-info}/licenses/LICENSE +0 -0
- {vibesurf-0.1.9a6.dist-info → vibesurf-0.1.10.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
|
+
}
|