local-deep-research 0.1.26__py3-none-any.whl → 0.2.2__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.
- local_deep_research/__init__.py +23 -22
- local_deep_research/__main__.py +16 -0
- local_deep_research/advanced_search_system/__init__.py +7 -0
- local_deep_research/advanced_search_system/filters/__init__.py +8 -0
- local_deep_research/advanced_search_system/filters/base_filter.py +38 -0
- local_deep_research/advanced_search_system/filters/cross_engine_filter.py +200 -0
- local_deep_research/advanced_search_system/findings/base_findings.py +81 -0
- local_deep_research/advanced_search_system/findings/repository.py +452 -0
- local_deep_research/advanced_search_system/knowledge/__init__.py +1 -0
- local_deep_research/advanced_search_system/knowledge/base_knowledge.py +151 -0
- local_deep_research/advanced_search_system/knowledge/standard_knowledge.py +159 -0
- local_deep_research/advanced_search_system/questions/__init__.py +1 -0
- local_deep_research/advanced_search_system/questions/base_question.py +64 -0
- local_deep_research/advanced_search_system/questions/decomposition_question.py +445 -0
- local_deep_research/advanced_search_system/questions/standard_question.py +119 -0
- local_deep_research/advanced_search_system/repositories/__init__.py +7 -0
- local_deep_research/advanced_search_system/strategies/__init__.py +1 -0
- local_deep_research/advanced_search_system/strategies/base_strategy.py +118 -0
- local_deep_research/advanced_search_system/strategies/iterdrag_strategy.py +450 -0
- local_deep_research/advanced_search_system/strategies/parallel_search_strategy.py +312 -0
- local_deep_research/advanced_search_system/strategies/rapid_search_strategy.py +270 -0
- local_deep_research/advanced_search_system/strategies/standard_strategy.py +300 -0
- local_deep_research/advanced_search_system/tools/__init__.py +1 -0
- local_deep_research/advanced_search_system/tools/base_tool.py +100 -0
- local_deep_research/advanced_search_system/tools/knowledge_tools/__init__.py +1 -0
- local_deep_research/advanced_search_system/tools/question_tools/__init__.py +1 -0
- local_deep_research/advanced_search_system/tools/search_tools/__init__.py +1 -0
- local_deep_research/api/__init__.py +5 -5
- local_deep_research/api/research_functions.py +154 -160
- local_deep_research/app.py +8 -0
- local_deep_research/citation_handler.py +25 -16
- local_deep_research/{config.py → config/config_files.py} +102 -110
- local_deep_research/config/llm_config.py +472 -0
- local_deep_research/config/search_config.py +77 -0
- local_deep_research/defaults/__init__.py +10 -5
- local_deep_research/defaults/main.toml +2 -2
- local_deep_research/defaults/search_engines.toml +60 -34
- local_deep_research/main.py +121 -19
- local_deep_research/migrate_db.py +147 -0
- local_deep_research/report_generator.py +87 -45
- local_deep_research/search_system.py +153 -283
- local_deep_research/setup_data_dir.py +35 -0
- local_deep_research/test_migration.py +178 -0
- local_deep_research/utilities/__init__.py +0 -0
- local_deep_research/utilities/db_utils.py +49 -0
- local_deep_research/{utilties → utilities}/enums.py +2 -2
- local_deep_research/{utilties → utilities}/llm_utils.py +63 -29
- local_deep_research/utilities/search_utilities.py +242 -0
- local_deep_research/{utilties → utilities}/setup_utils.py +4 -2
- local_deep_research/web/__init__.py +0 -1
- local_deep_research/web/app.py +86 -1709
- local_deep_research/web/app_factory.py +289 -0
- local_deep_research/web/database/README.md +70 -0
- local_deep_research/web/database/migrate_to_ldr_db.py +289 -0
- local_deep_research/web/database/migrations.py +447 -0
- local_deep_research/web/database/models.py +117 -0
- local_deep_research/web/database/schema_upgrade.py +107 -0
- local_deep_research/web/models/database.py +294 -0
- local_deep_research/web/models/settings.py +94 -0
- local_deep_research/web/routes/api_routes.py +559 -0
- local_deep_research/web/routes/history_routes.py +354 -0
- local_deep_research/web/routes/research_routes.py +715 -0
- local_deep_research/web/routes/settings_routes.py +1583 -0
- local_deep_research/web/services/research_service.py +947 -0
- local_deep_research/web/services/resource_service.py +149 -0
- local_deep_research/web/services/settings_manager.py +669 -0
- local_deep_research/web/services/settings_service.py +187 -0
- local_deep_research/web/services/socket_service.py +210 -0
- local_deep_research/web/static/css/custom_dropdown.css +277 -0
- local_deep_research/web/static/css/settings.css +1223 -0
- local_deep_research/web/static/css/styles.css +525 -48
- local_deep_research/web/static/js/components/custom_dropdown.js +428 -0
- local_deep_research/web/static/js/components/detail.js +348 -0
- local_deep_research/web/static/js/components/fallback/formatting.js +122 -0
- local_deep_research/web/static/js/components/fallback/ui.js +215 -0
- local_deep_research/web/static/js/components/history.js +487 -0
- local_deep_research/web/static/js/components/logpanel.js +949 -0
- local_deep_research/web/static/js/components/progress.js +1107 -0
- local_deep_research/web/static/js/components/research.js +1865 -0
- local_deep_research/web/static/js/components/results.js +766 -0
- local_deep_research/web/static/js/components/settings.js +3981 -0
- local_deep_research/web/static/js/components/settings_sync.js +106 -0
- local_deep_research/web/static/js/main.js +226 -0
- local_deep_research/web/static/js/services/api.js +253 -0
- local_deep_research/web/static/js/services/audio.js +31 -0
- local_deep_research/web/static/js/services/formatting.js +119 -0
- local_deep_research/web/static/js/services/pdf.js +622 -0
- local_deep_research/web/static/js/services/socket.js +882 -0
- local_deep_research/web/static/js/services/ui.js +546 -0
- local_deep_research/web/templates/base.html +72 -0
- local_deep_research/web/templates/components/custom_dropdown.html +47 -0
- local_deep_research/web/templates/components/log_panel.html +32 -0
- local_deep_research/web/templates/components/mobile_nav.html +22 -0
- local_deep_research/web/templates/components/settings_form.html +299 -0
- local_deep_research/web/templates/components/sidebar.html +21 -0
- local_deep_research/web/templates/pages/details.html +73 -0
- local_deep_research/web/templates/pages/history.html +51 -0
- local_deep_research/web/templates/pages/progress.html +57 -0
- local_deep_research/web/templates/pages/research.html +139 -0
- local_deep_research/web/templates/pages/results.html +59 -0
- local_deep_research/web/templates/settings_dashboard.html +78 -192
- local_deep_research/web/utils/__init__.py +0 -0
- local_deep_research/web/utils/formatters.py +76 -0
- local_deep_research/web_search_engines/engines/full_search.py +18 -16
- local_deep_research/web_search_engines/engines/meta_search_engine.py +182 -131
- local_deep_research/web_search_engines/engines/search_engine_arxiv.py +224 -139
- local_deep_research/web_search_engines/engines/search_engine_brave.py +88 -71
- local_deep_research/web_search_engines/engines/search_engine_ddg.py +48 -39
- local_deep_research/web_search_engines/engines/search_engine_github.py +415 -204
- local_deep_research/web_search_engines/engines/search_engine_google_pse.py +123 -90
- local_deep_research/web_search_engines/engines/search_engine_guardian.py +210 -157
- local_deep_research/web_search_engines/engines/search_engine_local.py +532 -369
- local_deep_research/web_search_engines/engines/search_engine_local_all.py +42 -36
- local_deep_research/web_search_engines/engines/search_engine_pubmed.py +358 -266
- local_deep_research/web_search_engines/engines/search_engine_searxng.py +212 -160
- local_deep_research/web_search_engines/engines/search_engine_semantic_scholar.py +213 -170
- local_deep_research/web_search_engines/engines/search_engine_serpapi.py +84 -68
- local_deep_research/web_search_engines/engines/search_engine_wayback.py +186 -154
- local_deep_research/web_search_engines/engines/search_engine_wikipedia.py +115 -77
- local_deep_research/web_search_engines/search_engine_base.py +174 -99
- local_deep_research/web_search_engines/search_engine_factory.py +192 -102
- local_deep_research/web_search_engines/search_engines_config.py +22 -15
- {local_deep_research-0.1.26.dist-info → local_deep_research-0.2.2.dist-info}/METADATA +177 -97
- local_deep_research-0.2.2.dist-info/RECORD +135 -0
- {local_deep_research-0.1.26.dist-info → local_deep_research-0.2.2.dist-info}/WHEEL +1 -2
- {local_deep_research-0.1.26.dist-info → local_deep_research-0.2.2.dist-info}/entry_points.txt +3 -0
- local_deep_research/defaults/llm_config.py +0 -338
- local_deep_research/utilties/search_utilities.py +0 -114
- local_deep_research/web/static/js/app.js +0 -3763
- local_deep_research/web/templates/api_keys_config.html +0 -82
- local_deep_research/web/templates/collections_config.html +0 -90
- local_deep_research/web/templates/index.html +0 -348
- local_deep_research/web/templates/llm_config.html +0 -120
- local_deep_research/web/templates/main_config.html +0 -89
- local_deep_research/web/templates/search_engines_config.html +0 -154
- local_deep_research/web/templates/settings.html +0 -519
- local_deep_research-0.1.26.dist-info/RECORD +0 -61
- local_deep_research-0.1.26.dist-info/top_level.txt +0 -1
- /local_deep_research/{utilties → config}/__init__.py +0 -0
- {local_deep_research-0.1.26.dist-info → local_deep_research-0.2.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,487 @@
|
|
1
|
+
/**
|
2
|
+
* History Component
|
3
|
+
* Manages the display and interaction with research history
|
4
|
+
*/
|
5
|
+
(function() {
|
6
|
+
// DOM Elements
|
7
|
+
let historyContainer = null;
|
8
|
+
let searchInput = null;
|
9
|
+
let clearHistoryBtn = null;
|
10
|
+
let historyEmptyMessage = null;
|
11
|
+
|
12
|
+
// Component state
|
13
|
+
let historyItems = [];
|
14
|
+
let filteredItems = [];
|
15
|
+
|
16
|
+
// Fallback UI utilities in case main UI utils aren't loaded
|
17
|
+
const uiUtils = {
|
18
|
+
showSpinner: function(container, message) {
|
19
|
+
if (window.ui && window.ui.showSpinner) {
|
20
|
+
return window.ui.showSpinner(container, message);
|
21
|
+
}
|
22
|
+
|
23
|
+
// Fallback implementation
|
24
|
+
if (!container) container = document.body;
|
25
|
+
const spinnerHtml = `
|
26
|
+
<div class="loading-spinner centered">
|
27
|
+
<div class="spinner"></div>
|
28
|
+
${message ? `<div class="spinner-message">${message}</div>` : ''}
|
29
|
+
</div>
|
30
|
+
`;
|
31
|
+
container.innerHTML = spinnerHtml;
|
32
|
+
},
|
33
|
+
|
34
|
+
hideSpinner: function(container) {
|
35
|
+
if (window.ui && window.ui.hideSpinner) {
|
36
|
+
return window.ui.hideSpinner(container);
|
37
|
+
}
|
38
|
+
|
39
|
+
// Fallback implementation
|
40
|
+
if (!container) container = document.body;
|
41
|
+
const spinner = container.querySelector('.loading-spinner');
|
42
|
+
if (spinner) {
|
43
|
+
spinner.remove();
|
44
|
+
}
|
45
|
+
},
|
46
|
+
|
47
|
+
showError: function(message) {
|
48
|
+
if (window.ui && window.ui.showError) {
|
49
|
+
return window.ui.showError(message);
|
50
|
+
}
|
51
|
+
|
52
|
+
// Fallback implementation
|
53
|
+
console.error(message);
|
54
|
+
alert(message);
|
55
|
+
},
|
56
|
+
|
57
|
+
showMessage: function(message) {
|
58
|
+
if (window.ui && window.ui.showMessage) {
|
59
|
+
return window.ui.showMessage(message);
|
60
|
+
}
|
61
|
+
|
62
|
+
// Fallback implementation
|
63
|
+
console.log(message);
|
64
|
+
alert(message);
|
65
|
+
}
|
66
|
+
};
|
67
|
+
|
68
|
+
// Fallback API utilities
|
69
|
+
const apiUtils = {
|
70
|
+
getResearchHistory: async function() {
|
71
|
+
if (window.api && window.api.getResearchHistory) {
|
72
|
+
return window.api.getResearchHistory();
|
73
|
+
}
|
74
|
+
|
75
|
+
// Fallback implementation
|
76
|
+
try {
|
77
|
+
const response = await fetch('/research/api/history');
|
78
|
+
if (!response.ok) {
|
79
|
+
throw new Error(`API Error: ${response.status} ${response.statusText}`);
|
80
|
+
}
|
81
|
+
return await response.json();
|
82
|
+
} catch (error) {
|
83
|
+
console.error('API Error:', error);
|
84
|
+
throw error;
|
85
|
+
}
|
86
|
+
},
|
87
|
+
|
88
|
+
deleteResearch: async function(researchId) {
|
89
|
+
if (window.api && window.api.deleteResearch) {
|
90
|
+
return window.api.deleteResearch(researchId);
|
91
|
+
}
|
92
|
+
|
93
|
+
// Fallback implementation
|
94
|
+
try {
|
95
|
+
const response = await fetch(`/research/api/delete/${researchId}`, {
|
96
|
+
method: 'DELETE'
|
97
|
+
});
|
98
|
+
if (!response.ok) {
|
99
|
+
throw new Error(`API Error: ${response.status} ${response.statusText}`);
|
100
|
+
}
|
101
|
+
return await response.json();
|
102
|
+
} catch (error) {
|
103
|
+
console.error('API Error:', error);
|
104
|
+
throw error;
|
105
|
+
}
|
106
|
+
},
|
107
|
+
|
108
|
+
clearResearchHistory: async function() {
|
109
|
+
if (window.api && window.api.clearResearchHistory) {
|
110
|
+
return window.api.clearResearchHistory();
|
111
|
+
}
|
112
|
+
|
113
|
+
// Fallback implementation
|
114
|
+
try {
|
115
|
+
const response = await fetch('/research/api/clear_history', {
|
116
|
+
method: 'POST',
|
117
|
+
headers: {
|
118
|
+
'Content-Type': 'application/json'
|
119
|
+
},
|
120
|
+
body: JSON.stringify({})
|
121
|
+
});
|
122
|
+
if (!response.ok) {
|
123
|
+
throw new Error(`API Error: ${response.status} ${response.statusText}`);
|
124
|
+
}
|
125
|
+
return await response.json();
|
126
|
+
} catch (error) {
|
127
|
+
console.error('API Error:', error);
|
128
|
+
throw error;
|
129
|
+
}
|
130
|
+
}
|
131
|
+
};
|
132
|
+
|
133
|
+
/**
|
134
|
+
* Initialize the history component
|
135
|
+
*/
|
136
|
+
function initializeHistory() {
|
137
|
+
// Get DOM elements
|
138
|
+
historyContainer = document.getElementById('history-items');
|
139
|
+
searchInput = document.getElementById('history-search');
|
140
|
+
clearHistoryBtn = document.getElementById('clear-history-btn');
|
141
|
+
historyEmptyMessage = document.getElementById('history-empty-message');
|
142
|
+
|
143
|
+
if (!historyContainer) {
|
144
|
+
console.error('Required DOM elements not found for history component');
|
145
|
+
return;
|
146
|
+
}
|
147
|
+
|
148
|
+
// Set up event listeners
|
149
|
+
setupEventListeners();
|
150
|
+
|
151
|
+
// Load history data
|
152
|
+
loadHistoryData();
|
153
|
+
|
154
|
+
console.log('History component initialized');
|
155
|
+
}
|
156
|
+
|
157
|
+
/**
|
158
|
+
* Set up event listeners
|
159
|
+
*/
|
160
|
+
function setupEventListeners() {
|
161
|
+
// Search input
|
162
|
+
if (searchInput) {
|
163
|
+
searchInput.addEventListener('input', handleSearchInput);
|
164
|
+
}
|
165
|
+
|
166
|
+
// Clear history button
|
167
|
+
if (clearHistoryBtn) {
|
168
|
+
clearHistoryBtn.addEventListener('click', handleClearHistory);
|
169
|
+
}
|
170
|
+
|
171
|
+
// Delegation for history item clicks
|
172
|
+
if (historyContainer) {
|
173
|
+
historyContainer.addEventListener('click', function(e) {
|
174
|
+
// Handle delete button click
|
175
|
+
if (e.target && e.target.closest('.delete-item-btn')) {
|
176
|
+
const itemId = e.target.closest('.history-item').dataset.id;
|
177
|
+
handleDeleteItem(itemId);
|
178
|
+
}
|
179
|
+
});
|
180
|
+
}
|
181
|
+
}
|
182
|
+
|
183
|
+
/**
|
184
|
+
* Load history data from API
|
185
|
+
*/
|
186
|
+
async function loadHistoryData() {
|
187
|
+
// Show loading state
|
188
|
+
uiUtils.showSpinner(historyContainer, 'Loading research history...');
|
189
|
+
|
190
|
+
try {
|
191
|
+
// Get history items
|
192
|
+
const response = await apiUtils.getResearchHistory();
|
193
|
+
|
194
|
+
if (response && Array.isArray(response.items)) {
|
195
|
+
historyItems = response.items;
|
196
|
+
filteredItems = [...historyItems];
|
197
|
+
|
198
|
+
// Render history items
|
199
|
+
renderHistoryItems();
|
200
|
+
} else {
|
201
|
+
throw new Error('Invalid response format');
|
202
|
+
}
|
203
|
+
} catch (error) {
|
204
|
+
console.error('Error loading history:', error);
|
205
|
+
uiUtils.hideSpinner(historyContainer);
|
206
|
+
uiUtils.showError('Error loading history: ' + error.message);
|
207
|
+
}
|
208
|
+
}
|
209
|
+
|
210
|
+
/**
|
211
|
+
* Render history items
|
212
|
+
*/
|
213
|
+
function renderHistoryItems() {
|
214
|
+
// Hide spinner
|
215
|
+
uiUtils.hideSpinner(historyContainer);
|
216
|
+
|
217
|
+
// Clear container
|
218
|
+
historyContainer.innerHTML = '';
|
219
|
+
|
220
|
+
// Show empty message if no items
|
221
|
+
if (filteredItems.length === 0) {
|
222
|
+
if (historyEmptyMessage) {
|
223
|
+
historyEmptyMessage.style.display = 'block';
|
224
|
+
} else {
|
225
|
+
historyContainer.innerHTML = `
|
226
|
+
<div class="empty-state">
|
227
|
+
<i class="fas fa-history empty-icon"></i>
|
228
|
+
<p>No research history found.</p>
|
229
|
+
${searchInput && searchInput.value ? '<p>Try adjusting your search query.</p>' : ''}
|
230
|
+
</div>
|
231
|
+
`;
|
232
|
+
}
|
233
|
+
|
234
|
+
if (clearHistoryBtn) {
|
235
|
+
clearHistoryBtn.style.display = 'none';
|
236
|
+
}
|
237
|
+
return;
|
238
|
+
}
|
239
|
+
|
240
|
+
// Hide empty message
|
241
|
+
if (historyEmptyMessage) {
|
242
|
+
historyEmptyMessage.style.display = 'none';
|
243
|
+
}
|
244
|
+
|
245
|
+
// Show clear button
|
246
|
+
if (clearHistoryBtn) {
|
247
|
+
clearHistoryBtn.style.display = 'inline-block';
|
248
|
+
}
|
249
|
+
|
250
|
+
// Create items
|
251
|
+
filteredItems.forEach(item => {
|
252
|
+
const itemElement = createHistoryItemElement(item);
|
253
|
+
historyContainer.appendChild(itemElement);
|
254
|
+
});
|
255
|
+
}
|
256
|
+
|
257
|
+
/**
|
258
|
+
* Format date safely using the formatter if available
|
259
|
+
*/
|
260
|
+
function formatDate(dateStr) {
|
261
|
+
if (window.formatting && window.formatting.formatDate) {
|
262
|
+
return window.formatting.formatDate(dateStr);
|
263
|
+
}
|
264
|
+
|
265
|
+
// Simple fallback date formatting
|
266
|
+
try {
|
267
|
+
const date = new Date(dateStr);
|
268
|
+
return date.toLocaleDateString() + ' ' + date.toLocaleTimeString();
|
269
|
+
} catch (e) {
|
270
|
+
return dateStr;
|
271
|
+
}
|
272
|
+
}
|
273
|
+
|
274
|
+
/**
|
275
|
+
* Format status safely using the formatter if available
|
276
|
+
*/
|
277
|
+
function formatStatus(status) {
|
278
|
+
if (window.formatting && window.formatting.formatStatus) {
|
279
|
+
return window.formatting.formatStatus(status);
|
280
|
+
}
|
281
|
+
|
282
|
+
// Simple fallback formatting
|
283
|
+
const statusMap = {
|
284
|
+
'in_progress': 'In Progress',
|
285
|
+
'completed': 'Completed',
|
286
|
+
'failed': 'Failed',
|
287
|
+
'suspended': 'Suspended'
|
288
|
+
};
|
289
|
+
|
290
|
+
return statusMap[status] || status;
|
291
|
+
}
|
292
|
+
|
293
|
+
/**
|
294
|
+
* Format mode safely using the formatter if available
|
295
|
+
*/
|
296
|
+
function formatMode(mode) {
|
297
|
+
if (window.formatting && window.formatting.formatMode) {
|
298
|
+
return window.formatting.formatMode(mode);
|
299
|
+
}
|
300
|
+
|
301
|
+
// Simple fallback formatting
|
302
|
+
const modeMap = {
|
303
|
+
'quick': 'Quick Summary',
|
304
|
+
'detailed': 'Detailed Report'
|
305
|
+
};
|
306
|
+
|
307
|
+
return modeMap[mode] || mode;
|
308
|
+
}
|
309
|
+
|
310
|
+
/**
|
311
|
+
* Create a history item element
|
312
|
+
* @param {Object} item - The history item data
|
313
|
+
* @returns {HTMLElement} The history item element
|
314
|
+
*/
|
315
|
+
function createHistoryItemElement(item) {
|
316
|
+
const itemEl = document.createElement('div');
|
317
|
+
itemEl.className = 'history-item';
|
318
|
+
itemEl.dataset.id = item.id;
|
319
|
+
|
320
|
+
// Format date
|
321
|
+
const formattedDate = formatDate(item.created_at);
|
322
|
+
|
323
|
+
// Get a display title (use query if title is not available)
|
324
|
+
const displayTitle = item.title || formatTitleFromQuery(item.query);
|
325
|
+
|
326
|
+
// Status class - convert in_progress to in-progress for CSS
|
327
|
+
const statusClass = item.status ? item.status.replace('_', '-') : '';
|
328
|
+
|
329
|
+
// Create the HTML content
|
330
|
+
itemEl.innerHTML = `
|
331
|
+
<div class="history-item-header">
|
332
|
+
<div class="history-item-title">${displayTitle}</div>
|
333
|
+
<div class="history-item-status status-${statusClass}">${formatStatus(item.status)}</div>
|
334
|
+
</div>
|
335
|
+
<div class="history-item-meta">
|
336
|
+
<div class="history-item-date">${formattedDate}</div>
|
337
|
+
<div class="history-item-mode">${formatMode(item.mode)}</div>
|
338
|
+
</div>
|
339
|
+
<div class="history-item-actions">
|
340
|
+
${item.status === 'completed' ?
|
341
|
+
`<button class="btn btn-sm btn-outline view-btn">
|
342
|
+
<i class="fas fa-eye"></i> View
|
343
|
+
</button>` : ''}
|
344
|
+
<button class="btn btn-sm btn-outline delete-item-btn">
|
345
|
+
<i class="fas fa-trash-alt"></i>
|
346
|
+
</button>
|
347
|
+
</div>
|
348
|
+
`;
|
349
|
+
|
350
|
+
// Add event listeners
|
351
|
+
const viewBtn = itemEl.querySelector('.view-btn');
|
352
|
+
if (viewBtn) {
|
353
|
+
viewBtn.addEventListener('click', (e) => {
|
354
|
+
e.stopPropagation(); // Prevent item click
|
355
|
+
window.location.href = `/research/results/${item.id}`;
|
356
|
+
});
|
357
|
+
}
|
358
|
+
|
359
|
+
const deleteBtn = itemEl.querySelector('.delete-item-btn');
|
360
|
+
if (deleteBtn) {
|
361
|
+
deleteBtn.addEventListener('click', (e) => {
|
362
|
+
e.stopPropagation(); // Prevent item click
|
363
|
+
handleDeleteItem(item.id);
|
364
|
+
});
|
365
|
+
}
|
366
|
+
|
367
|
+
// Add click event to the whole item
|
368
|
+
itemEl.addEventListener('click', () => {
|
369
|
+
if (item.status === 'completed') {
|
370
|
+
window.location.href = `/research/results/${item.id}`;
|
371
|
+
} else {
|
372
|
+
window.location.href = `/research/progress/${item.id}`;
|
373
|
+
}
|
374
|
+
});
|
375
|
+
|
376
|
+
return itemEl;
|
377
|
+
}
|
378
|
+
|
379
|
+
/**
|
380
|
+
* Format a title from a query string
|
381
|
+
* Truncates long queries and adds ellipsis
|
382
|
+
* @param {string} query - The query string
|
383
|
+
* @returns {string} Formatted title
|
384
|
+
*/
|
385
|
+
function formatTitleFromQuery(query) {
|
386
|
+
if (!query) return 'Untitled Research';
|
387
|
+
|
388
|
+
// Truncate long queries
|
389
|
+
if (query.length > 60) {
|
390
|
+
return query.substring(0, 57) + '...';
|
391
|
+
}
|
392
|
+
|
393
|
+
return query;
|
394
|
+
}
|
395
|
+
|
396
|
+
/**
|
397
|
+
* Handle search input
|
398
|
+
*/
|
399
|
+
function handleSearchInput() {
|
400
|
+
const searchTerm = searchInput.value.trim().toLowerCase();
|
401
|
+
|
402
|
+
if (!searchTerm) {
|
403
|
+
// Reset to show all items
|
404
|
+
filteredItems = [...historyItems];
|
405
|
+
} else {
|
406
|
+
// Filter items based on search term
|
407
|
+
filteredItems = historyItems.filter(item => {
|
408
|
+
// Search in title if available, otherwise in query
|
409
|
+
const titleMatch = item.title ?
|
410
|
+
item.title.toLowerCase().includes(searchTerm) :
|
411
|
+
false;
|
412
|
+
|
413
|
+
// Always search in query
|
414
|
+
const queryMatch = item.query ?
|
415
|
+
item.query.toLowerCase().includes(searchTerm) :
|
416
|
+
false;
|
417
|
+
|
418
|
+
return titleMatch || queryMatch;
|
419
|
+
});
|
420
|
+
}
|
421
|
+
|
422
|
+
// Render filtered items
|
423
|
+
renderHistoryItems();
|
424
|
+
}
|
425
|
+
|
426
|
+
/**
|
427
|
+
* Handle delete item
|
428
|
+
* @param {string} itemId - The item ID to delete
|
429
|
+
*/
|
430
|
+
async function handleDeleteItem(itemId) {
|
431
|
+
if (!confirm('Are you sure you want to delete this research? This action cannot be undone.')) {
|
432
|
+
return;
|
433
|
+
}
|
434
|
+
|
435
|
+
try {
|
436
|
+
// Delete item via API
|
437
|
+
await apiUtils.deleteResearch(itemId);
|
438
|
+
|
439
|
+
// Remove from arrays
|
440
|
+
historyItems = historyItems.filter(item => item.id != itemId);
|
441
|
+
filteredItems = filteredItems.filter(item => item.id != itemId);
|
442
|
+
|
443
|
+
// Show success message
|
444
|
+
uiUtils.showMessage('Research deleted successfully');
|
445
|
+
|
446
|
+
// Re-render history items
|
447
|
+
renderHistoryItems();
|
448
|
+
} catch (error) {
|
449
|
+
console.error('Error deleting research:', error);
|
450
|
+
uiUtils.showError('Error deleting research: ' + error.message);
|
451
|
+
}
|
452
|
+
}
|
453
|
+
|
454
|
+
/**
|
455
|
+
* Handle clear history
|
456
|
+
*/
|
457
|
+
async function handleClearHistory() {
|
458
|
+
if (!confirm('Are you sure you want to clear all research history? This action cannot be undone.')) {
|
459
|
+
return;
|
460
|
+
}
|
461
|
+
|
462
|
+
try {
|
463
|
+
// Clear history via API
|
464
|
+
await apiUtils.clearResearchHistory();
|
465
|
+
|
466
|
+
// Clear arrays
|
467
|
+
historyItems = [];
|
468
|
+
filteredItems = [];
|
469
|
+
|
470
|
+
// Show success message
|
471
|
+
uiUtils.showMessage('Research history cleared successfully');
|
472
|
+
|
473
|
+
// Re-render history items
|
474
|
+
renderHistoryItems();
|
475
|
+
} catch (error) {
|
476
|
+
console.error('Error clearing history:', error);
|
477
|
+
uiUtils.showError('Error clearing history: ' + error.message);
|
478
|
+
}
|
479
|
+
}
|
480
|
+
|
481
|
+
// Initialize on DOM content loaded
|
482
|
+
if (document.readyState === 'loading') {
|
483
|
+
document.addEventListener('DOMContentLoaded', initializeHistory);
|
484
|
+
} else {
|
485
|
+
initializeHistory();
|
486
|
+
}
|
487
|
+
})();
|