1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
class SearXNGModule {
constructor() {
this.commands = {};
this.terminal = null;
}
getHelpInfo() {
return {
'search': 'search <query> [--summarize] [--fetch] — Search using SearXNG; --summarize/-s adds AI overview, --fetch/-f fetches page content',
's': 's <query> [--summarize] [--fetch] — Alias for search',
};
}
registerCommands() {
this.commands['search'] = (args) => this.search(args);
this.commands['s'] = (args) => this.search(args);
}
async search(args) {
if (!args[0]) {
this.terminal.print("Usage: search <query> [--summarize] [--fetch]", "warning");
this.terminal.print("Example: search quantum computing --summarize", "info");
return;
}
const argsList = args.slice();
const summarize = argsList.includes('--summarize') || argsList.includes('-s');
const fetchContent = argsList.includes('--fetch') || argsList.includes('-f');
const query = argsList.filter(a => !a.startsWith('-')).join(' ');
if (!query) { this.terminal.print('Search query is required', 'warning'); return; }
this.terminal.print(`🔍 Searching: ${query}`, 'info');
let params = 'q=' + encodeURIComponent(query);
if (summarize) params += '&summarize=true';
if (fetchContent) params += '&fetch_content=true';
try {
const resp = await fetch('/api/search?' + params);
if (!resp.ok) {
const err = await resp.json().catch(() => ({}));
this.terminal.print('Search failed: ' + (err.error || 'HTTP ' + resp.status), 'error');
return;
}
const data = await resp.json();
const results = data.results || [];
if (results.length === 0) {
this.terminal.print('No results found.', 'info');
return;
}
this.terminal.print(`\n=== ${results.length} results (via ${data.source || 'unknown'}) ===\n`, 'success');
if (data.ai_overview) {
this.terminal.print('🤖 AI Overview:', 'info');
this.terminal.print(data.ai_overview, 'ollama');
this.terminal.print('');
}
results.forEach((r, i) => {
const snippet = r.content || '';
const contentSnippet = r.content_snippet || '';
this.terminal.printHTML(`
<div class="search-result">
<strong>${i + 1}. <a href="${r.url}" target="_blank" style="color: #00aaff;">${r.title || 'No Title'}</a></strong><br/>
<span style="color: #888888; font-size: 0.85em;">${r.url || 'No URL'}</span><br/>
<span style="color: #00ff00; font-size: 0.85em;">${(snippet || '').substring(0, 200)}...</span>
${contentSnippet ? `<br/><span style="color: #aaaaaa; font-size: 0.8em;">📄 ${contentSnippet.substring(0, 150)}...</span>` : ''}
</div>
`);
});
} catch (e) {
this.terminal.print('Search failed: ' + e.message, 'error');
}
}
}
|