aboutsummaryrefslogtreecommitdiff
path: root/modules/core/module-searxng.js
diff options
context:
space:
mode:
Diffstat (limited to 'modules/core/module-searxng.js')
-rw-r--r--modules/core/module-searxng.js73
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');
+ }
+ }
+}