diff options
Diffstat (limited to 'modules/core/module-searxng.js')
| -rw-r--r-- | modules/core/module-searxng.js | 73 |
1 files changed, 73 insertions, 0 deletions
diff --git a/modules/core/module-searxng.js b/modules/core/module-searxng.js new file mode 100644 index 0000000..f2c2e72 --- /dev/null +++ b/modules/core/module-searxng.js @@ -0,0 +1,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'); + } + } +} |