Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support for most search engines #31

Merged
merged 13 commits into from
Dec 9, 2022
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# ChatGPT for Google

A browser extension to display ChatGPT response alongside Google Search results, supports Chrome/Edge/Firefox
A browser extension to display ChatGPT response alongside Search Engine results, supports Chrome/Edge/Firefox

[<img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=369975&theme=light" />](https://www.producthunt.com/posts/chatgpt-for-google?utm_source=badge-featured)

Expand Down
97 changes: 97 additions & 0 deletions src/content-script/engine-match-config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/**
* @typedef {object} SiteConfig
* @property {string[]} inputName - input element name of search box
* @property {string[]} sidebarContainerId - prepend child to
* @property {string[]} sidebarContainerClass - if above ids not exist, prepend child to
* @property {string[]} appendContainerId - if above all not exist, append child to
* @property {string[]} appendContainerClass - if above all not exist, append child to
*/
/**
* @type {Object.<string,SiteConfig>}
*/
export const config = {
google: {
inputName: ["q"],
sidebarContainerId: ["rhs"],
sidebarContainerClass: [],
appendContainerId: ["rcnt"],
appendContainerClass: []
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we can combine id and class, by using querySelector

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, wait a minute

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wong2 finished

},
bing: {
inputName: ["q"],
sidebarContainerId: ["b_context"],
sidebarContainerClass: [],
appendContainerId: [],
appendContainerClass: []
},
yahoo: {
inputName: ["p"],
sidebarContainerId: ["right"],
sidebarContainerClass: ["Contents__inner Contents__inner--sub"],
appendContainerId: ["cols", "contents__wrap"], // and yahoo jp
appendContainerClass: []
},
duckduckgo: {
inputName: ["q"],
sidebarContainerId: [],
sidebarContainerClass: ["results--sidebar js-results-sidebar"],
appendContainerId: ["links_wrapper"],
appendContainerClass: []
},
startpage: {
inputName: ["query"],
sidebarContainerId: [],
sidebarContainerClass: ["layout-web__sidebar layout-web__sidebar--web"],
appendContainerId: [],
appendContainerClass: ["layout-web__body layout-web__body--desktop"]
},
baidu: {
inputName: ["wd"],
sidebarContainerId: ["content_right"],
sidebarContainerClass: [],
appendContainerId: ["container"],
appendContainerClass: []
},
kagi: {
inputName: ["q"],
sidebarContainerId: [],
sidebarContainerClass: ["right-content-box _0_right_sidebar"],
appendContainerId: ["_0_app_content"],
appendContainerClass: []
},
yandex: {
inputName: ["text"],
sidebarContainerId: ["search-result-aside"],
sidebarContainerClass: [],
appendContainerId: [],
appendContainerClass: []
},
naver: {
inputName: ["query"],
sidebarContainerId: ["sub_pack"],
sidebarContainerClass: [],
appendContainerId: ["content"],
appendContainerClass: []
},
brave: {
inputName: ["q"],
sidebarContainerId: ["side-right"],
sidebarContainerClass: [],
appendContainerId: [],
appendContainerClass: []
},
searx: {
inputName: ["q"],
sidebarContainerId: ["sidebar_results"],
sidebarContainerClass: [],
appendContainerId: [],
appendContainerClass: []
},
ecosia: {
inputName: ["q"],
sidebarContainerId: [],
sidebarContainerClass: ["sidebar web__sidebar"],
appendContainerId: ["main"],
appendContainerClass: []
}
}
20 changes: 16 additions & 4 deletions src/content-script/index.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import MarkdownIt from "markdown-it";
import Browser from "webextension-polyfill";
import {getPossibleElementByClassArray, getPossibleElementByIdArray, getPossibleElementByNameArray} from "./utils.mjs"
import {config} from "./engine-match-config.mjs"

async function run(question) {
const markdown = new MarkdownIt();
Expand All @@ -8,12 +10,18 @@ async function run(question) {
container.className = "chat-gpt-container";
container.innerHTML = '<p class="loading">Waiting for ChatGPT response...</p>';

const siderbarContainer = document.getElementById("rhs");
const siderbarContainer =
getPossibleElementByIdArray(config[siteName].sidebarContainerId)
|| getPossibleElementByClassArray(config[siteName].sidebarContainerClass);
if (siderbarContainer) {
siderbarContainer.prepend(container);
} else {
container.classList.add("sidebar-free");
document.getElementById("rcnt").appendChild(container);
const appendContainer =
getPossibleElementByIdArray(config[siteName].appendContainerId)
|| getPossibleElementByClassArray(config[siteName].appendContainerClass);
if (appendContainer)
appendContainer.appendChild(container);
}

const port = Browser.runtime.connect();
Expand All @@ -32,11 +40,15 @@ async function run(question) {
port.postMessage({ question });
}

const searchInput = document.getElementsByName("q")[0];
const matchedSites = Object.keys(config);
const siteRegex = new RegExp(`(${matchedSites.join('|')})`);
const siteName = document.location.hostname.match(siteRegex)[0];

const searchInput = getPossibleElementByNameArray(config[siteName].inputName);
if (searchInput && searchInput.value) {
// only run on first page
const startParam = new URL(location.href).searchParams.get("start") || "0";
if (startParam === "0") {
run(searchInput.value);
}
}
}
26 changes: 26 additions & 0 deletions src/content-script/utils.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
export function getPossibleElementByNameArray(elementNameArray) {
for (const elementName of elementNameArray) {
const element = document.getElementsByName(elementName)[0];
if (element) {
return element;
}
}
}

export function getPossibleElementByIdArray(elementIdArray) {
for (const elementId of elementIdArray) {
const element = document.getElementById(elementId);
if (element) {
return element;
}
}
}

export function getPossibleElementByClassArray(elementClassArray) {
for (const elementClass of elementClassArray) {
const element = document.getElementsByClassName(elementClass)[0];
if (element) {
return element;
}
}
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer using simple for...of loop for these functions

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I don't know what happened when I wrote this code, it does seem like a long and incomprehensible way 😂

24 changes: 19 additions & 5 deletions src/manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "ChatGPT for Google",
"description": "Display ChatGPT response alongside Google Search results",
"version": "1.2.0",
"name": "ChatGPT for Search Engine",
"description": "Display ChatGPT response alongside Search Engine results",
"version": "1.3.0",
"manifest_version": 3,
"icons": {
"16": "logo.png",
Expand All @@ -15,8 +15,22 @@
},
"content_scripts": [
{
"matches": ["https://*/search*"],
"include_globs": ["*.google.*/*"],
"matches": ["https://*/*"],
"include_globs": [
"https://*.google.*/*",
"https://kagi.*/*",
"https://*.bing.*/*",
"https://*.yahoo.*/*",
"https://*.naver.*/*",
"https://*.brave.*/*",
"https://*.ecosia.org/*",
"https://searx.be/*",
"https://www.searx.be/*",
"https://yandex.*/*",
"https://duckduckgo.*",
"https://*.startpage.*/*",
"https://*.baidu.*"
],
"js": ["content-script.js"],
"css": ["github-markdown.css", "styles.css"]
}
Expand Down
24 changes: 19 additions & 5 deletions src/manifest.v2.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "ChatGPT for Google",
"description": "Display ChatGPT response alongside Google Search results",
"version": "1.2.0",
"name": "ChatGPT for Search Engine",
"description": "Display ChatGPT response alongside Search Engine results",
"version": "1.3.0",
"manifest_version": 2,
"icons": {
"16": "logo.png",
Expand All @@ -15,8 +15,22 @@
},
"content_scripts": [
{
"matches": ["https://*/search*"],
"include_globs": ["*.google.*/*"],
"matches": ["https://*/*"],
"include_globs": [
"https://*.google.*/*",
"https://kagi.*/*",
"https://*.bing.*/*",
"https://*.yahoo.*/*",
"https://*.naver.*/*",
"https://*.brave.*/*",
"https://*.ecosia.org/*",
"https://searx.be/*",
"https://www.searx.be/*",
"https://yandex.*/*",
"https://duckduckgo.*",
"https://*.startpage.*/*",
"https://*.baidu.*"
],
"js": ["content-script.js"],
"css": ["github-markdown.css", "styles.css"]
}
Expand Down