From 57c95d64fc382ea9252eee7578cae81dad7696a6 Mon Sep 17 00:00:00 2001 From: josc146 Date: Sat, 25 Mar 2023 20:48:59 +0800 Subject: [PATCH] feat: summarize any page by right-click menu (#55, #62, #78) --- src/background/index.mjs | 81 +++++++++++++++---------- src/content-script/index.jsx | 55 ++++++++--------- src/content-script/menu-tools/index.mjs | 16 +++++ src/utils/get-core-content-text.mjs | 4 +- 4 files changed, 92 insertions(+), 64 deletions(-) create mode 100644 src/content-script/menu-tools/index.mjs diff --git a/src/background/index.mjs b/src/background/index.mjs index 8e245b2..b767c40 100644 --- a/src/background/index.mjs +++ b/src/background/index.mjs @@ -16,7 +16,7 @@ import { gptApiModelKeys, } from '../config/index.mjs' import { isSafari } from '../utils/is-safari' -import { isFirefox } from '../utils/is-firefox' +import { config as menuConfig } from '../content-script/menu-tools' const KEY_ACCESS_TOKEN = 'accessToken' const cache = new ExpiryMap(10 * 1000) @@ -107,41 +107,58 @@ Browser.runtime.onMessage.addListener(async (message) => { } }) -Browser.contextMenus.removeAll().then(() => { - const menuId = 'ChatGPTBox-Menu' - Browser.contextMenus.create({ - id: menuId, - title: 'ChatGPTBox', - contexts: ['all'], - }) - - Browser.contextMenus.create({ - id: menuId + 'new', - parentId: menuId, - title: 'New Chat', - contexts: [isFirefox() ? 'all' : 'selection'], - }) - for (const index in defaultConfig.selectionTools) { - const key = defaultConfig.selectionTools[index] - const desc = defaultConfig.selectionToolsDesc[index] +function refreshMenu() { + Browser.contextMenus.removeAll().then(() => { + const menuId = 'ChatGPTBox-Menu' Browser.contextMenus.create({ - id: menuId + key, - parentId: menuId, - title: desc, - contexts: ['selection'], + id: menuId, + title: 'ChatGPTBox', + contexts: ['all'], }) - } - Browser.contextMenus.onClicked.addListener((info, tab) => { - const itemId = info.menuItemId === menuId ? 'new' : info.menuItemId.replace(menuId, '') - const message = { - itemId: itemId, - selectionText: info.selectionText, + for (const [k, v] of Object.entries(menuConfig)) { + Browser.contextMenus.create({ + id: menuId + k, + parentId: menuId, + title: v.label, + contexts: ['all'], + }) } - console.debug('menu clicked', message) - Browser.tabs.sendMessage(tab.id, { - type: 'MENU', - data: message, + Browser.contextMenus.create({ + id: menuId + 'separator1', + parentId: menuId, + contexts: ['selection'], + type: 'separator', + }) + for (const index in defaultConfig.selectionTools) { + const key = defaultConfig.selectionTools[index] + const desc = defaultConfig.selectionToolsDesc[index] + Browser.contextMenus.create({ + id: menuId + key, + parentId: menuId, + title: desc, + contexts: ['selection'], + }) + } + + Browser.contextMenus.onClicked.addListener((info, tab) => { + const message = { + itemId: info.menuItemId.replace(menuId, ''), + selectionText: info.selectionText, + } + console.debug('menu clicked', message) + Browser.tabs.sendMessage(tab.id, { + type: 'CREATE_MENU', + data: message, + }) }) }) +} + +Browser.runtime.onMessage.addListener(async (message) => { + if (message.type === 'REFRESH_MENU') { + refreshMenu() + } }) + +refreshMenu() diff --git a/src/content-script/index.jsx b/src/content-script/index.jsx index 32f64ac..6161654 100644 --- a/src/content-script/index.jsx +++ b/src/content-script/index.jsx @@ -4,9 +4,11 @@ import { render } from 'preact' import DecisionCard from '../components/DecisionCard' import { config as siteConfig } from './site-adapters' import { config as toolsConfig } from './selection-tools' +import { config as menuConfig } from './menu-tools' import { clearOldAccessToken, getUserConfig, setAccessToken } from '../config/index.mjs' import { createElementAtPosition, + cropText, getClientPosition, getPossibleElementByQuerySelector, initSession, @@ -210,38 +212,31 @@ async function prepareForRightClickMenu() { }) Browser.runtime.onMessage.addListener(async (message) => { - if (message.type === 'MENU') { + if (message.type === 'CREATE_MENU') { const data = message.data - if (data.itemId === 'new') { - const position = { x: menuX, y: menuY } - const container = createElementAtPosition(position.x, position.y) - container.className = 'chatgptbox-toolbar-container-not-queryable' - render( - , - container, + let prompt = '' + if (data.itemId in toolsConfig) + prompt = await toolsConfig[data.itemId].genPrompt(data.selectionText) + else if (data.itemId in menuConfig) + prompt = cropText( + `Reply in ${await getPreferredLanguage()}.\n` + + (await menuConfig[data.itemId].genPrompt()), ) - } else { - const position = { x: menuX, y: menuY } - const container = createElementAtPosition(position.x, position.y) - container.className = 'chatgptbox-toolbar-container-not-queryable' - render( - , - container, - ) - } + + const position = { x: menuX, y: menuY } + const container = createElementAtPosition(position.x, position.y) + container.className = 'chatgptbox-toolbar-container-not-queryable' + render( + , + container, + ) } }) } diff --git a/src/content-script/menu-tools/index.mjs b/src/content-script/menu-tools/index.mjs new file mode 100644 index 0000000..0ee810a --- /dev/null +++ b/src/content-script/menu-tools/index.mjs @@ -0,0 +1,16 @@ +import { getCoreContentText } from '../../utils/get-core-content-text' + +export const config = { + newChat: { + label: 'New Chat', + genPrompt: async () => { + return '' + }, + }, + summarizePage: { + label: 'Summarize Page', + genPrompt: async () => { + return `The following is the text content of a web page, analyze the core content and summarize:\n${getCoreContentText()}` + }, + }, +} diff --git a/src/utils/get-core-content-text.mjs b/src/utils/get-core-content-text.mjs index 9f1af09..5f618a1 100644 --- a/src/utils/get-core-content-text.mjs +++ b/src/utils/get-core-content-text.mjs @@ -35,10 +35,10 @@ export function getCoreContentText() { let ret if (secondLargestElement && getArea(secondLargestElement) > 0.5 * getArea(largestElement)) { - ret = secondLargestElement.textContent + ret = secondLargestElement.innerText || secondLargestElement.textContent console.log('use second') } else { - ret = largestElement.textContent + ret = largestElement.innerText || largestElement.textContent console.log('use first') } return ret.trim().replaceAll(' ', '').replaceAll('\n\n', '').replaceAll(',,', '')