您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
在SearXNG搜索结果中直接显示Gemini AI的回答!API密钥本地保存,界面美观快速。
// ==UserScript== // @name SearXNGにGemini AIの回答を表示 ✨ // @name:ja SearXNGにGemini AIの回答を表示 ✨ // @name:en Show Gemini AI Answers in SearXNG ✨ // @name:zh-CN 在SearXNG中显示Gemini AI回答 ✨ // @name:zh-TW 在SearXNG中顯示Gemini AI回答 ✨ // @name:ko SearXNG에서 Gemini AI 답변 표시 ✨ // @name:fr Afficher les réponses Gemini AI dans SearXNG ✨ // @name:es Mostrar respuestas de Gemini AI en SearXNG ✨ // @name:de Gemini-AI-Antworten in SearXNG anzeigen ✨ // @name:pt-BR Exibir respostas do Gemini AI no SearXNG ✨ // @name:ru Показ ответов Gemini AI в SearXNG ✨ // @version 3.5.0 // @description SearXNG検索結果にGoogle GeminiのAI回答を直接表示!APIキーはローカル保存、スタイリッシュなUIで回答を即確認。 // @description:en Display Google Gemini AI answers directly in SearXNG search results! API key stored locally, fast and elegant UI. // @description:zh-CN 在SearXNG搜索结果中直接显示Gemini AI的回答!API密钥本地保存,界面美观快速。 // @description:zh-TW 在SearXNG搜尋結果中直接顯示Gemini AI的回答!API金鑰本地儲存,介面美觀且快速。 // @description:ko SearXNG 검색 결과에 Gemini AI 답변을 바로 표시! API 키는 로컬 저장, 세련된 UI로 빠르게 확인 가능. // @description:fr Affiche les réponses Gemini AI dans SearXNG. Clé API stockée localement, interface rapide et élégante. // @description:es Muestra respuestas de Gemini AI directamente en SearXNG. Clave API guardada localmente, interfaz elegante y rápida. // @description:de Zeigt Gemini AI-Antworten direkt in SearXNG an. API-Schlüssel wird lokal gespeichert, stilvolles UI. // @description:pt-BR Exibe respostas do Gemini AI diretamente no SearXNG! Chave API armazenada localmente, interface rápida e estilosa. // @description:ru Отображение ответов Gemini AI прямо в SearXNG! Ключ API хранится локально, стильный и быстрый интерфейс. // @namespace https://github.com/koyasi777/searxng-gemini-answer-injector // @author koyasi777 // @match *://*/searx/search* // @match *://*/searxng/search* // @match *://searx.*/* // @match *://*.searx.*/* // @match https://search.charleseroop.com/* // @grant none // @license MIT // @homepageURL https://github.com/koyasi777/searxng-gemini-answer-injector // @supportURL https://github.com/koyasi777/searxng-gemini-answer-injector/issues // @icon https://docs.searxng.org/_static/searxng-wordmark.svg // ==/UserScript== (async function() { 'use strict'; const log = (...args) => console.log('[SearxGemini]', ...args); // 🔽 <details> 自動展開処理(先に実行) function expandDetailsIfExists() { const idsToExpand = ['suggestions', 'engines_msg', 'search_url']; idsToExpand.forEach(id => { const container = document.getElementById(id); if (!container) return; const details = container.querySelector('details'); if (details) details.open = true; }); } // 🔄 MutationObserverを最初に仕掛ける(遅延読み込み対応) const observer = new MutationObserver(() => { expandDetailsIfExists(); }); observer.observe(document.body, { childList: true, subtree: true }); expandDetailsIfExists(); // 初回も実行しておく const MODEL_NAME = 'gemini-2.0-flash'; const GEMINI_API_URL_BASE = `https://generativelanguage.googleapis.com/v1/models/${MODEL_NAME}:generateContent`; // 🔐 APIキーの取得・保存 async function getApiKey(force = false) { if (force) localStorage.removeItem('GEMINI_API_KEY'); let key = localStorage.getItem('GEMINI_API_KEY'); if (!key) { alert( 'Gemini APIキーを取得するには、以下のGoogle公式ページにアクセスしてください:\n\n' + 'https://aistudio.google.com/app/apikey?hl=ja\n\n' + '取得したAPIキーを次の入力欄に貼り付けてください(ローカルに保存されます)' ); key = prompt('Gemini APIキーを入力してください(ローカルに保存されます)'); if (key) localStorage.setItem('GEMINI_API_KEY', key); } return key; } let GEMINI_API_KEY = await getApiKey(); const query = document.querySelector('input[name="q"]')?.value?.trim(); if (!query) return log('クエリ取得失敗'); // const resultsDiv = document.querySelector('#results'); // if (!resultsDiv) return log('結果DIVが見つかりません'); const sidebarDiv = document.querySelector('#sidebar'); if (!sidebarDiv || !sidebarDiv.firstChild) return log('sidebar DIVが見つかりません'); const isDark = () => window.matchMedia('(prefers-color-scheme: dark)').matches; const aiBox = document.createElement('div'); aiBox.className = 'gemini-ai-box'; aiBox.style.cssText = ` padding: 1em; border-radius: 12px; margin-bottom: 1.2em; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif; line-height: 1.6; background: ${isDark() ? '#1e1e1e' : '#ffffff'}; color: ${isDark() ? '#e0e0e0' : '#202124'}; box-shadow: 0 1px 2px rgba(0,0,0,0.06), 0 2px 8px rgba(0,0,0,0.05); transition: background 0.3s ease, color 0.3s ease; `; aiBox.innerHTML = ` <div style="margin-bottom: 0.4em;"> <span style=" font-size: 0.75em; background: ${isDark() ? '#333' : '#f1f3f4'}; color: ${isDark() ? '#aaa' : '#555'}; padding: 0.25em 0.6em; border-radius: 6px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; ">Gemini AI</span> </div> <div id="gemini-answer-content"> Gemini AIによる回答を取得中… </div> `; // resultsDiv.insertBefore(aiBox, resultsDiv.firstChild); sidebarDiv.insertBefore(aiBox, sidebarDiv.firstChild); function getCurrentLanguage() { const raw = document.getElementById("language")?.value?.trim().toLowerCase() || "ja"; return (raw === "all" || raw === "auto") ? "ja" : raw; } async function fetchGeminiAnswer() { const contentEl = document.getElementById('gemini-answer-content'); // ⬇️ 最終言語取得ロジック(auto/all → ja) const currentLang = getCurrentLanguage(); // ⬇️ プロンプト組み立て(言語指示付き) const prompt = [ `以下の検索クエリに対し、最新の正確な回答をHTML形式で出力してください(段落・改行・リンク含む)。`, `また、信頼できる出典URLを必ず1件以上含めてください。HTML本体だけを返してください。`, ``, `クエリ:「${query}」`, ``, `※ 出力の言語は「${currentLang}」にしてください。` ].join('\n'); try { const response = await fetch(`${GEMINI_API_URL_BASE}?key=${GEMINI_API_KEY}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ contents: [{ parts: [{ text: prompt }] }], }) }); if (response.status === 401 || response.status === 403) { contentEl.innerHTML = `❗ APIキーが無効または期限切れです。再入力してください。`; GEMINI_API_KEY = await getApiKey(true); // 強制再入力 return await fetchGeminiAnswer(); // 再試行 } const data = await response.json(); const rawText = data?.candidates?.[0]?.content?.parts?.[0]?.text; if (!rawText) { contentEl.innerHTML = `Gemini AIの回答を取得できませんでした。`; return log('API応答にテキストがありません:', data); } // コードブロック記法の除去(文中のものも含めて完全対応) const cleanedText = rawText .replace(/```html\s*/gi, '') .replace(/```/g, ''); contentEl.innerHTML = cleanedText; } catch (err) { document.getElementById('gemini-answer-content').innerHTML = `Gemini APIエラー: ${err.message}`; log('API呼び出しエラー:', err); } } await fetchGeminiAnswer(); function escapeHTML(str) { const div = document.createElement('div'); div.innerText = str; return div.innerHTML; } })();