Skip to content

Commit 27d87d6

Browse files
committed
feat: summarize any page by right-click menu (#55, #62, #78)
1 parent b6d74e0 commit 27d87d6

File tree

4 files changed

+90
-62
lines changed

4 files changed

+90
-62
lines changed

src/background/index.mjs

Lines changed: 47 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
gptApiModelKeys,
1717
} from '../config/index.mjs'
1818
import { isSafari } from '../utils/is-safari'
19-
import { isFirefox } from '../utils/is-firefox'
19+
import { config as menuConfig } from '../content-script/menu-tools'
2020

2121
const KEY_ACCESS_TOKEN = 'accessToken'
2222
const cache = new ExpiryMap(10 * 1000)
@@ -107,41 +107,58 @@ Browser.runtime.onMessage.addListener(async (message) => {
107107
}
108108
})
109109

110-
Browser.contextMenus.removeAll().then(() => {
111-
const menuId = 'ChatGPTBox-Menu'
112-
Browser.contextMenus.create({
113-
id: menuId,
114-
title: 'ChatGPTBox',
115-
contexts: ['all'],
116-
})
110+
function refreshMenu() {
111+
Browser.contextMenus.removeAll().then(() => {
112+
const menuId = 'ChatGPTBox-Menu'
113+
Browser.contextMenus.create({
114+
id: menuId,
115+
title: 'ChatGPTBox',
116+
contexts: ['all'],
117+
})
117118

118-
Browser.contextMenus.create({
119-
id: menuId + 'new',
120-
parentId: menuId,
121-
title: 'New Chat',
122-
contexts: [isFirefox() ? 'all' : 'selection'],
123-
})
124-
for (const index in defaultConfig.selectionTools) {
125-
const key = defaultConfig.selectionTools[index]
126-
const desc = defaultConfig.selectionToolsDesc[index]
119+
for (const [k, v] of Object.entries(menuConfig)) {
120+
Browser.contextMenus.create({
121+
id: menuId + k,
122+
parentId: menuId,
123+
title: v.label,
124+
contexts: ['all'],
125+
})
126+
}
127127
Browser.contextMenus.create({
128-
id: menuId + key,
128+
id: menuId + 'separator1',
129129
parentId: menuId,
130-
title: desc,
131130
contexts: ['selection'],
131+
type: 'separator',
132132
})
133-
}
134-
135-
Browser.contextMenus.onClicked.addListener((info, tab) => {
136-
const itemId = info.menuItemId === menuId ? 'new' : info.menuItemId.replace(menuId, '')
137-
const message = {
138-
itemId: itemId,
139-
selectionText: info.selectionText,
133+
for (const index in defaultConfig.selectionTools) {
134+
const key = defaultConfig.selectionTools[index]
135+
const desc = defaultConfig.selectionToolsDesc[index]
136+
Browser.contextMenus.create({
137+
id: menuId + key,
138+
parentId: menuId,
139+
title: desc,
140+
contexts: ['selection'],
141+
})
140142
}
141-
console.debug('menu clicked', message)
142-
Browser.tabs.sendMessage(tab.id, {
143-
type: 'MENU',
144-
data: message,
143+
144+
Browser.contextMenus.onClicked.addListener((info, tab) => {
145+
const message = {
146+
itemId: info.menuItemId.replace(menuId, ''),
147+
selectionText: info.selectionText,
148+
}
149+
console.debug('menu clicked', message)
150+
Browser.tabs.sendMessage(tab.id, {
151+
type: 'CREATE_MENU',
152+
data: message,
153+
})
145154
})
146155
})
156+
}
157+
158+
Browser.runtime.onMessage.addListener(async (message) => {
159+
if (message.type === 'REFRESH_MENU') {
160+
refreshMenu()
161+
}
147162
})
163+
164+
refreshMenu()

src/content-script/index.jsx

Lines changed: 25 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ import { render } from 'preact'
44
import DecisionCard from '../components/DecisionCard'
55
import { config as siteConfig } from './site-adapters'
66
import { config as toolsConfig } from './selection-tools'
7+
import { config as menuConfig } from './menu-tools'
78
import { clearOldAccessToken, getUserConfig, setAccessToken } from '../config/index.mjs'
89
import {
910
createElementAtPosition,
11+
cropText,
1012
getClientPosition,
1113
getPossibleElementByQuerySelector,
1214
initSession,
@@ -210,38 +212,31 @@ async function prepareForRightClickMenu() {
210212
})
211213

212214
Browser.runtime.onMessage.addListener(async (message) => {
213-
if (message.type === 'MENU') {
215+
if (message.type === 'CREATE_MENU') {
214216
const data = message.data
215-
if (data.itemId === 'new') {
216-
const position = { x: menuX, y: menuY }
217-
const container = createElementAtPosition(position.x, position.y)
218-
container.className = 'chatgptbox-toolbar-container-not-queryable'
219-
render(
220-
<FloatingToolbar
221-
session={initSession()}
222-
selection=""
223-
container={container}
224-
triggered={true}
225-
closeable={true}
226-
/>,
227-
container,
217+
let prompt = ''
218+
if (data.itemId in toolsConfig)
219+
prompt = await toolsConfig[data.itemId].genPrompt(data.selectionText)
220+
else if (data.itemId in menuConfig)
221+
prompt = cropText(
222+
`Reply in ${await getPreferredLanguage()}.\n` +
223+
(await menuConfig[data.itemId].genPrompt()),
228224
)
229-
} else {
230-
const position = { x: menuX, y: menuY }
231-
const container = createElementAtPosition(position.x, position.y)
232-
container.className = 'chatgptbox-toolbar-container-not-queryable'
233-
render(
234-
<FloatingToolbar
235-
session={initSession()}
236-
selection={data.selectionText}
237-
container={container}
238-
triggered={true}
239-
closeable={true}
240-
prompt={await toolsConfig[data.itemId].genPrompt(data.selectionText)}
241-
/>,
242-
container,
243-
)
244-
}
225+
226+
const position = { x: menuX, y: menuY }
227+
const container = createElementAtPosition(position.x, position.y)
228+
container.className = 'chatgptbox-toolbar-container-not-queryable'
229+
render(
230+
<FloatingToolbar
231+
session={initSession()}
232+
selection={data.selectionText}
233+
container={container}
234+
triggered={true}
235+
closeable={true}
236+
prompt={prompt}
237+
/>,
238+
container,
239+
)
245240
}
246241
})
247242
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { getCoreContentText } from '../../utils/get-core-content-text'
2+
3+
export const config = {
4+
newChat: {
5+
label: 'New Chat',
6+
genPrompt: async () => {
7+
return ''
8+
},
9+
},
10+
summarizePage: {
11+
label: 'Summarize Page',
12+
genPrompt: async () => {
13+
return `The following is the text content of a web page, analyze the core content and summarize:\n${getCoreContentText()}`
14+
},
15+
},
16+
}

src/utils/get-core-content-text.mjs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@ export function getCoreContentText() {
3535

3636
let ret
3737
if (secondLargestElement && getArea(secondLargestElement) > 0.5 * getArea(largestElement)) {
38-
ret = secondLargestElement.textContent
38+
ret = secondLargestElement.innerText || secondLargestElement.textContent
3939
console.log('use second')
4040
} else {
41-
ret = largestElement.textContent
41+
ret = largestElement.innerText || largestElement.textContent
4242
console.log('use first')
4343
}
4444
return ret.trim().replaceAll(' ', '').replaceAll('\n\n', '').replaceAll(',,', '')

0 commit comments

Comments
 (0)