mirror of
https://github.com/wassname/chatGPTBox.git
synced 2026-07-23 12:50:47 +08:00
first commit
This commit is contained in:
@@ -0,0 +1,220 @@
|
||||
import './styles.scss'
|
||||
import { render } from 'preact'
|
||||
import DecisionCard from '../components/DecisionCard'
|
||||
import { config as siteConfig } from './site-adapters'
|
||||
import { config as toolsConfig } from './selection-tools'
|
||||
import { clearOldAccessToken, getUserConfig, setAccessToken, getPreferredLanguage } from '../config'
|
||||
import {
|
||||
createElementAtPosition,
|
||||
getPossibleElementByQuerySelector,
|
||||
initSession,
|
||||
isSafari,
|
||||
} from '../utils'
|
||||
import FloatingToolbar from '../components/FloatingToolbar'
|
||||
import Browser from 'webextension-polyfill'
|
||||
|
||||
/**
|
||||
* @param {SiteConfig} siteConfig
|
||||
* @param {UserConfig} userConfig
|
||||
*/
|
||||
async function mountComponent(siteConfig, userConfig) {
|
||||
if (
|
||||
!getPossibleElementByQuerySelector(siteConfig.sidebarContainerQuery) &&
|
||||
!getPossibleElementByQuerySelector(siteConfig.appendContainerQuery) &&
|
||||
!getPossibleElementByQuerySelector(siteConfig.sidebarContainerQuery) &&
|
||||
!getPossibleElementByQuerySelector([userConfig.prependQuery]) &&
|
||||
!getPossibleElementByQuerySelector([userConfig.appendQuery])
|
||||
)
|
||||
return
|
||||
|
||||
document.querySelectorAll('.chat-gpt-container').forEach((e) => e.remove())
|
||||
|
||||
let question
|
||||
if (userConfig.inputQuery) question = await getInput([userConfig.inputQuery])
|
||||
if (!question && siteConfig) question = await getInput(siteConfig.inputQuery)
|
||||
|
||||
document.querySelectorAll('.chat-gpt-container').forEach((e) => e.remove())
|
||||
const container = document.createElement('div')
|
||||
container.className = 'chat-gpt-container'
|
||||
render(
|
||||
<DecisionCard
|
||||
session={initSession()}
|
||||
question={question}
|
||||
siteConfig={siteConfig}
|
||||
container={container}
|
||||
/>,
|
||||
container,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string[]|function} inputQuery
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
async function getInput(inputQuery) {
|
||||
if (typeof inputQuery === 'function') {
|
||||
const input = await inputQuery()
|
||||
if (input) return `Reply in ${await getPreferredLanguage()}.\n` + input
|
||||
return input
|
||||
}
|
||||
const searchInput = getPossibleElementByQuerySelector(inputQuery)
|
||||
if (searchInput && searchInput.value) {
|
||||
return searchInput.value
|
||||
}
|
||||
}
|
||||
|
||||
async function prepareForSafari() {
|
||||
await clearOldAccessToken()
|
||||
|
||||
if (location.hostname !== 'chat.openai.com' || location.pathname !== '/api/auth/session') return
|
||||
|
||||
const response = document.querySelector('pre').textContent
|
||||
|
||||
let data
|
||||
try {
|
||||
data = JSON.parse(response)
|
||||
} catch (error) {
|
||||
console.error('json error', error)
|
||||
return
|
||||
}
|
||||
if (data.accessToken) {
|
||||
await setAccessToken(data.accessToken)
|
||||
}
|
||||
}
|
||||
|
||||
let toolbarContainer
|
||||
|
||||
async function prepareForSelectionTools() {
|
||||
document.addEventListener('mouseup', (e) => {
|
||||
if (toolbarContainer && toolbarContainer.contains(e.target)) return
|
||||
if (
|
||||
toolbarContainer &&
|
||||
window.getSelection()?.rangeCount > 0 &&
|
||||
toolbarContainer.contains(window.getSelection()?.getRangeAt(0).endContainer.parentElement)
|
||||
)
|
||||
return
|
||||
|
||||
if (toolbarContainer) toolbarContainer.remove()
|
||||
setTimeout(() => {
|
||||
const selection = window.getSelection()?.toString()
|
||||
if (selection) {
|
||||
const position = { x: e.clientX + 15, y: e.clientY - 15 }
|
||||
toolbarContainer = createElementAtPosition(position.x, position.y)
|
||||
toolbarContainer.className = 'toolbar-container'
|
||||
render(
|
||||
<FloatingToolbar
|
||||
session={initSession()}
|
||||
selection={selection}
|
||||
position={position}
|
||||
container={toolbarContainer}
|
||||
/>,
|
||||
toolbarContainer,
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
document.addEventListener('mousedown', (e) => {
|
||||
if (toolbarContainer && toolbarContainer.contains(e.target)) return
|
||||
|
||||
document.querySelectorAll('.toolbar-container').forEach((e) => e.remove())
|
||||
})
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (
|
||||
toolbarContainer &&
|
||||
!toolbarContainer.contains(e.target) &&
|
||||
(e.target.nodeName === 'INPUT' || e.target.nodeName === 'TEXTAREA')
|
||||
) {
|
||||
setTimeout(() => {
|
||||
if (!window.getSelection()?.toString()) toolbarContainer.remove()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let menuX, menuY
|
||||
|
||||
async function prepareForRightClickMenu() {
|
||||
document.addEventListener('contextmenu', (e) => {
|
||||
menuX = e.clientX
|
||||
menuY = e.clientY
|
||||
})
|
||||
|
||||
Browser.runtime.onMessage.addListener(async (message) => {
|
||||
if (message.type === 'MENU') {
|
||||
const data = message.data
|
||||
if (data.itemId === 'new') {
|
||||
const position = { x: menuX, y: menuY }
|
||||
const container = createElementAtPosition(position.x, position.y)
|
||||
container.className = 'toolbar-container-not-queryable'
|
||||
render(
|
||||
<FloatingToolbar
|
||||
session={initSession()}
|
||||
selection=""
|
||||
position={position}
|
||||
container={container}
|
||||
triggered={true}
|
||||
closeable={true}
|
||||
onClose={() => container.remove()}
|
||||
/>,
|
||||
container,
|
||||
)
|
||||
} else {
|
||||
const position = { x: menuX, y: menuY }
|
||||
const container = createElementAtPosition(position.x, position.y)
|
||||
container.className = 'toolbar-container-not-queryable'
|
||||
render(
|
||||
<FloatingToolbar
|
||||
session={initSession()}
|
||||
selection={data.selectionText}
|
||||
position={position}
|
||||
container={container}
|
||||
triggered={true}
|
||||
closeable={true}
|
||||
onClose={() => container.remove()}
|
||||
prompt={await toolsConfig[data.itemId].genPrompt(data.selectionText)}
|
||||
/>,
|
||||
container,
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function prepareForStaticCard() {
|
||||
let siteRegex
|
||||
if (userConfig.userSiteRegexOnly) siteRegex = userConfig.siteRegex
|
||||
else
|
||||
siteRegex = new RegExp(
|
||||
(userConfig.siteRegex && userConfig.siteRegex + '|') + Object.keys(siteConfig).join('|'),
|
||||
)
|
||||
|
||||
const matches = location.hostname.match(siteRegex)
|
||||
if (matches) {
|
||||
const siteName = matches[0]
|
||||
if (siteName in siteConfig) {
|
||||
const siteAction = siteConfig[siteName].action
|
||||
if (siteAction && siteAction.init) {
|
||||
await siteAction.init(location.hostname, userConfig, getInput, mountComponent)
|
||||
}
|
||||
}
|
||||
if (
|
||||
userConfig.siteAdapters.includes(siteName) &&
|
||||
!userConfig.activeSiteAdapters.includes(siteName)
|
||||
)
|
||||
return
|
||||
|
||||
mountComponent(siteConfig[siteName], userConfig)
|
||||
}
|
||||
}
|
||||
|
||||
let userConfig
|
||||
|
||||
async function run() {
|
||||
userConfig = await getUserConfig()
|
||||
if (isSafari()) await prepareForSafari()
|
||||
prepareForSelectionTools()
|
||||
prepareForStaticCard()
|
||||
prepareForRightClickMenu()
|
||||
}
|
||||
|
||||
run()
|
||||
@@ -0,0 +1,60 @@
|
||||
import {
|
||||
CardHeading,
|
||||
CardList,
|
||||
EmojiSmile,
|
||||
Palette,
|
||||
QuestionCircle,
|
||||
Translate,
|
||||
} from 'react-bootstrap-icons'
|
||||
import { getPreferredLanguage } from '../../config.mjs'
|
||||
|
||||
export const config = {
|
||||
translate: {
|
||||
icon: <Translate />,
|
||||
label: 'Translate',
|
||||
genPrompt: async (selection) => {
|
||||
const preferredLanguage = await getPreferredLanguage()
|
||||
return (
|
||||
`Translate the following into ${preferredLanguage} and only show me the translated content.` +
|
||||
`If it is already in ${preferredLanguage},` +
|
||||
`translate it into English and only show me the translated content:\n"${selection}"`
|
||||
)
|
||||
},
|
||||
},
|
||||
summary: {
|
||||
icon: <CardHeading />,
|
||||
label: 'Summary',
|
||||
genPrompt: async (selection) => {
|
||||
const preferredLanguage = await getPreferredLanguage()
|
||||
return `Reply in ${preferredLanguage}.Summarize the following as concisely as possible:\n"${selection}"`
|
||||
},
|
||||
},
|
||||
polish: {
|
||||
icon: <Palette />,
|
||||
label: 'Polish',
|
||||
genPrompt: async (selection) =>
|
||||
`Check the following content for possible diction and grammar problems,and polish it carefully:\n"${selection}"`,
|
||||
},
|
||||
sentiment: {
|
||||
icon: <EmojiSmile />,
|
||||
label: 'Sentiment Analysis',
|
||||
genPrompt: async (selection) => {
|
||||
const preferredLanguage = await getPreferredLanguage()
|
||||
return `Reply in ${preferredLanguage}.Analyze the sentiments expressed in the following content and make a brief summary of the sentiments:\n"${selection}"`
|
||||
},
|
||||
},
|
||||
divide: {
|
||||
icon: <CardList />,
|
||||
label: 'Divide Paragraphs',
|
||||
genPrompt: async (selection) =>
|
||||
`Divide the following into paragraphs that are easy to read and understand:\n"${selection}"`,
|
||||
},
|
||||
ask: {
|
||||
icon: <QuestionCircle />,
|
||||
label: 'Ask',
|
||||
genPrompt: async (selection) => {
|
||||
const preferredLanguage = await getPreferredLanguage()
|
||||
return `Reply in ${preferredLanguage}.Analyze the following content and express your opinion,or give your answer:\n"${selection}"`
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
//TODO
|
||||
@@ -0,0 +1,26 @@
|
||||
import { config } from '../index'
|
||||
|
||||
export default {
|
||||
init: async (hostname, userConfig, getInput, mountComponent) => {
|
||||
try {
|
||||
const targetNode = document.getElementById('wrapper_wrapper')
|
||||
const observer = new MutationObserver(async (records) => {
|
||||
if (
|
||||
records.some(
|
||||
(record) =>
|
||||
record.type === 'childList' &&
|
||||
[...record.addedNodes].some((node) => node.id === 'container'),
|
||||
)
|
||||
) {
|
||||
const searchValue = await getInput(config.baidu.inputQuery)
|
||||
if (searchValue) {
|
||||
mountComponent(config.baidu, userConfig)
|
||||
}
|
||||
}
|
||||
})
|
||||
observer.observe(targetNode, { childList: true })
|
||||
} catch (e) {
|
||||
/* empty */
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import { cropText } from '../../../utils'
|
||||
import { config } from '../index.mjs'
|
||||
|
||||
export default {
|
||||
init: async (hostname, userConfig, getInput, mountComponent) => {
|
||||
try {
|
||||
let oldUrl = location.href
|
||||
const checkUrlChange = async () => {
|
||||
if (location.href !== oldUrl) {
|
||||
oldUrl = location.href
|
||||
mountComponent(config.bilibili, userConfig)
|
||||
}
|
||||
}
|
||||
window.setInterval(checkUrlChange, 500)
|
||||
} catch (e) {
|
||||
/* empty */
|
||||
}
|
||||
},
|
||||
inputQuery: async () => {
|
||||
try {
|
||||
const bvid = location.pathname.replace('video', '').replaceAll('/', '')
|
||||
const p = Number(new URLSearchParams(location.search).get('p') || 1) - 1
|
||||
|
||||
const pagelistResponse = await fetch(
|
||||
`https://api.bilibili.com/x/player/pagelist?bvid=${bvid}`,
|
||||
)
|
||||
const pagelistData = await pagelistResponse.json()
|
||||
const videoList = pagelistData.data
|
||||
const cid = videoList[p].cid
|
||||
const title = videoList[p].part
|
||||
|
||||
const infoResponse = await fetch(
|
||||
`https://api.bilibili.com/x/player/v2?bvid=${bvid}&cid=${cid}`,
|
||||
{
|
||||
credentials: 'include',
|
||||
},
|
||||
)
|
||||
const infoData = await infoResponse.json()
|
||||
const subtitleUrl = infoData.data.subtitle.subtitles[0].subtitle_url
|
||||
|
||||
const subtitleResponse = await fetch(subtitleUrl)
|
||||
const subtitleData = await subtitleResponse.json()
|
||||
const subtitles = subtitleData.body
|
||||
|
||||
let subtitleContent = ''
|
||||
for (let i = 0; i < subtitles.length; i++) {
|
||||
if (i === subtitles.length - 1) subtitleContent += subtitles[i].content
|
||||
else subtitleContent += subtitles[i].content + ','
|
||||
}
|
||||
|
||||
return cropText(
|
||||
`用尽量简练的语言,联系视频标题,对视频进行内容摘要,视频标题为:"${title}",字幕内容为:\n${subtitleContent}`,
|
||||
)
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import { cropText, limitedFetch } from '../../../utils'
|
||||
import { config } from '../index.mjs'
|
||||
|
||||
const getPatchUrl = async () => {
|
||||
const patchUrl = location.origin + location.pathname + '.patch'
|
||||
const response = await fetch(patchUrl, { method: 'HEAD' })
|
||||
if (response.ok) return patchUrl
|
||||
return ''
|
||||
}
|
||||
|
||||
const getPatchData = async (patchUrl) => {
|
||||
if (!patchUrl) return
|
||||
|
||||
let patchData = await limitedFetch(patchUrl, 1024 * 40)
|
||||
patchData = patchData.substring(patchData.indexOf('---'))
|
||||
return patchData
|
||||
}
|
||||
|
||||
export default {
|
||||
init: async (hostname, userConfig, getInput, mountComponent) => {
|
||||
try {
|
||||
const targetNode = document.querySelector('body')
|
||||
const observer = new MutationObserver(async (records) => {
|
||||
if (
|
||||
records.some(
|
||||
(record) =>
|
||||
record.type === 'childList' &&
|
||||
[...record.addedNodes].some((node) => node.classList.contains('page-responsive')),
|
||||
)
|
||||
) {
|
||||
const patchUrl = await getPatchUrl()
|
||||
if (patchUrl) {
|
||||
mountComponent(config.github, userConfig)
|
||||
}
|
||||
}
|
||||
})
|
||||
observer.observe(targetNode, { childList: true })
|
||||
} catch (e) {
|
||||
/* empty */
|
||||
}
|
||||
},
|
||||
inputQuery: async () => {
|
||||
try {
|
||||
const patchUrl = await getPatchUrl()
|
||||
const patchData = await getPatchData(patchUrl)
|
||||
if (!patchData) return
|
||||
|
||||
return cropText(
|
||||
`Analyze the contents of a git commit,provide a suitable commit message,and summarize the contents of the commit.` +
|
||||
`The patch contents of this commit are as follows:\n${patchData}`,
|
||||
)
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { cropText, limitedFetch } from '../../../utils'
|
||||
|
||||
const getPatchUrl = async () => {
|
||||
const patchUrl = location.origin + location.pathname + '.patch'
|
||||
const response = await fetch(patchUrl, { method: 'HEAD' })
|
||||
if (response.ok) return patchUrl
|
||||
return ''
|
||||
}
|
||||
|
||||
const getPatchData = async (patchUrl) => {
|
||||
if (!patchUrl) return
|
||||
|
||||
let patchData = await limitedFetch(patchUrl, 1024 * 40)
|
||||
patchData = patchData.substring(patchData.indexOf('---'))
|
||||
return patchData
|
||||
}
|
||||
|
||||
export default {
|
||||
inputQuery: async () => {
|
||||
try {
|
||||
const patchUrl = await getPatchUrl()
|
||||
const patchData = await getPatchData(patchUrl)
|
||||
if (!patchData) return
|
||||
|
||||
return cropText(
|
||||
`Analyze the contents of a git commit,provide a suitable commit message,and summarize the contents of the commit.` +
|
||||
`The patch contents of this commit are as follows:\n${patchData}`,
|
||||
)
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
import baidu from './baidu'
|
||||
import bilibili from './bilibili'
|
||||
import youtube from './youtube'
|
||||
import github from './github'
|
||||
import gitlab from './gitlab'
|
||||
import zhihu from './zhihu'
|
||||
import reddit from './reddit'
|
||||
import quora from './quora'
|
||||
|
||||
/**
|
||||
* @typedef {object} SiteConfigAction
|
||||
* @property {function} init
|
||||
*/
|
||||
/**
|
||||
* @typedef {object} SiteConfig
|
||||
* @property {string[]|function} inputQuery - for search box
|
||||
* @property {string[]} sidebarContainerQuery - prepend child to
|
||||
* @property {string[]} appendContainerQuery - if sidebarContainer not exists, append child to
|
||||
* @property {string[]} resultsContainerQuery - prepend child to if insertAtTop is true
|
||||
* @property {SiteConfigAction} action
|
||||
*/
|
||||
/**
|
||||
* @type {Object.<string,SiteConfig>}
|
||||
*/
|
||||
export const config = {
|
||||
google: {
|
||||
inputQuery: ["input[name='q']"],
|
||||
sidebarContainerQuery: ['#rhs'],
|
||||
appendContainerQuery: ['#rcnt'],
|
||||
resultsContainerQuery: ['#rso'],
|
||||
},
|
||||
bing: {
|
||||
inputQuery: ["[name='q']"],
|
||||
sidebarContainerQuery: ['#b_context'],
|
||||
appendContainerQuery: [],
|
||||
resultsContainerQuery: ['#b_results'],
|
||||
},
|
||||
yahoo: {
|
||||
inputQuery: ["input[name='p']"],
|
||||
sidebarContainerQuery: ['#right', '.Contents__inner.Contents__inner--sub'],
|
||||
appendContainerQuery: ['#cols', '#contents__wrap'],
|
||||
resultsContainerQuery: [
|
||||
'#main-algo',
|
||||
'.searchCenterMiddle',
|
||||
'.Contents__inner.Contents__inner--main',
|
||||
'#contents',
|
||||
],
|
||||
},
|
||||
duckduckgo: {
|
||||
inputQuery: ["input[name='q']"],
|
||||
sidebarContainerQuery: ['.results--sidebar.js-results-sidebar'],
|
||||
appendContainerQuery: ['#links_wrapper'],
|
||||
resultsContainerQuery: ['.results'],
|
||||
},
|
||||
startpage: {
|
||||
inputQuery: ["input[name='query']"],
|
||||
sidebarContainerQuery: ['.layout-web__sidebar.layout-web__sidebar--web'],
|
||||
appendContainerQuery: ['.layout-web__body.layout-web__body--desktop'],
|
||||
resultsContainerQuery: ['.mainline-results'],
|
||||
},
|
||||
baidu: {
|
||||
inputQuery: ["input[id='kw']"],
|
||||
sidebarContainerQuery: ['#content_right'],
|
||||
appendContainerQuery: ['#container'],
|
||||
resultsContainerQuery: ['#content_left', '#results'],
|
||||
action: {
|
||||
init: baidu.init,
|
||||
},
|
||||
},
|
||||
kagi: {
|
||||
inputQuery: ["input[name='q']"],
|
||||
sidebarContainerQuery: ['.right-content-box._0_right_sidebar'],
|
||||
appendContainerQuery: ['#_0_app_content'],
|
||||
resultsContainerQuery: ['#main', '#app'],
|
||||
},
|
||||
yandex: {
|
||||
inputQuery: ["input[name='text']"],
|
||||
sidebarContainerQuery: ['#search-result-aside'],
|
||||
appendContainerQuery: [],
|
||||
resultsContainerQuery: ['#search-result'],
|
||||
},
|
||||
naver: {
|
||||
inputQuery: ["input[name='query']"],
|
||||
sidebarContainerQuery: ['#sub_pack'],
|
||||
appendContainerQuery: ['#content'],
|
||||
resultsContainerQuery: ['#main_pack', '#ct'],
|
||||
},
|
||||
brave: {
|
||||
inputQuery: ["input[name='q']"],
|
||||
sidebarContainerQuery: ['#side-right'],
|
||||
appendContainerQuery: [],
|
||||
resultsContainerQuery: ['#results'],
|
||||
},
|
||||
searx: {
|
||||
inputQuery: ["input[name='q']"],
|
||||
sidebarContainerQuery: ['#sidebar_results', '#sidebar'],
|
||||
appendContainerQuery: [],
|
||||
resultsContainerQuery: ['#urls', '#main_results', '#results'],
|
||||
},
|
||||
ecosia: {
|
||||
inputQuery: ["input[name='q']"],
|
||||
sidebarContainerQuery: ['.sidebar.web__sidebar'],
|
||||
appendContainerQuery: ['#main'],
|
||||
resultsContainerQuery: ['.mainline'],
|
||||
},
|
||||
neeva: {
|
||||
inputQuery: ["input[name='q']"],
|
||||
sidebarContainerQuery: ['.result-group-layout__stickyContainer-iDIO8'],
|
||||
appendContainerQuery: ['.search-index__searchHeaderContainer-2JD6q'],
|
||||
resultsContainerQuery: ['.result-group-layout__component-1jzTe', '#search'],
|
||||
},
|
||||
bilibili: {
|
||||
inputQuery: bilibili.inputQuery,
|
||||
sidebarContainerQuery: ['#danmukuBox'],
|
||||
appendContainerQuery: [],
|
||||
resultsContainerQuery: [],
|
||||
action: {
|
||||
init: bilibili.init,
|
||||
},
|
||||
},
|
||||
youtube: {
|
||||
inputQuery: youtube.inputQuery,
|
||||
sidebarContainerQuery: ['#secondary'],
|
||||
appendContainerQuery: [],
|
||||
resultsContainerQuery: [],
|
||||
action: {
|
||||
init: youtube.init,
|
||||
},
|
||||
},
|
||||
github: {
|
||||
inputQuery: github.inputQuery,
|
||||
sidebarContainerQuery: ['#diff', '.commit'],
|
||||
appendContainerQuery: [],
|
||||
resultsContainerQuery: [],
|
||||
action: {
|
||||
init: github.init,
|
||||
},
|
||||
},
|
||||
gitlab: {
|
||||
inputQuery: gitlab.inputQuery,
|
||||
sidebarContainerQuery: ['.js-commit-box-info'],
|
||||
appendContainerQuery: [],
|
||||
resultsContainerQuery: [],
|
||||
},
|
||||
zhihu: {
|
||||
inputQuery: zhihu.inputQuery,
|
||||
sidebarContainerQuery: ['.Question-sideColumn', '.Post-Header'],
|
||||
appendContainerQuery: [],
|
||||
resultsContainerQuery: [],
|
||||
},
|
||||
reddit: {
|
||||
inputQuery: reddit.inputQuery,
|
||||
sidebarContainerQuery: ['.side .spacer .linkinfo'],
|
||||
appendContainerQuery: [],
|
||||
resultsContainerQuery: [],
|
||||
},
|
||||
quora: {
|
||||
inputQuery: quora.inputQuery,
|
||||
sidebarContainerQuery: ['.q-box.PageContentsLayout___StyledBox-d2uxks-0'],
|
||||
appendContainerQuery: [],
|
||||
resultsContainerQuery: [],
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { cropText } from '../../../utils'
|
||||
|
||||
export default {
|
||||
inputQuery: async () => {
|
||||
try {
|
||||
if (location.pathname === '/') return
|
||||
|
||||
const texts = document.querySelectorAll('.q-box.qu-userSelect--text')
|
||||
let title
|
||||
if (texts.length > 0) title = texts[0].textContent
|
||||
let answers = ''
|
||||
if (texts.length > 1)
|
||||
for (let i = 1; i < texts.length; i++) {
|
||||
answers += `answer${i}:${texts[i].textContent}|`
|
||||
}
|
||||
|
||||
return cropText(
|
||||
`Below is the content from a question and answer platform,giving the corresponding summary and your opinion on it.` +
|
||||
`The question is:'${title}',` +
|
||||
`Some answers are as follows:\n${answers}`,
|
||||
)
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { cropText } from '../../../utils'
|
||||
|
||||
export default {
|
||||
inputQuery: async () => {
|
||||
try {
|
||||
const title = document.querySelector('.entry .title').textContent
|
||||
const texts = document.querySelectorAll('.entry .usertext-body')
|
||||
let description
|
||||
if (texts.length > 0) description = texts[0].textContent
|
||||
let answers = ''
|
||||
if (texts.length > 1)
|
||||
for (let i = 1; i < texts.length; i++) {
|
||||
answers += `answer${i}:${texts[i].textContent}|`
|
||||
}
|
||||
|
||||
return cropText(
|
||||
`Below is the content from a social forum,giving the corresponding summary and your opinion on it.` +
|
||||
`The title is:'${title}',and the further description of the title is:'${description}'.` +
|
||||
`Some answers are as follows:\n${answers}`,
|
||||
)
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
//TODO
|
||||
@@ -0,0 +1,53 @@
|
||||
import { cropText } from '../../../utils'
|
||||
import { config } from '../index.mjs'
|
||||
|
||||
export default {
|
||||
init: async (hostname, userConfig, getInput, mountComponent) => {
|
||||
try {
|
||||
let oldUrl = location.href
|
||||
const checkUrlChange = async () => {
|
||||
if (location.href !== oldUrl) {
|
||||
oldUrl = location.href
|
||||
mountComponent(config.youtube, userConfig)
|
||||
}
|
||||
}
|
||||
window.setInterval(checkUrlChange, 500)
|
||||
} catch (e) {
|
||||
/* empty */
|
||||
}
|
||||
},
|
||||
inputQuery: async () => {
|
||||
try {
|
||||
const docText = await (
|
||||
await fetch(location.href, {
|
||||
credentials: 'include',
|
||||
})
|
||||
).text()
|
||||
|
||||
let subtitleUrl = docText.substring(docText.indexOf('https://www.youtube.com/api/timedtext'))
|
||||
subtitleUrl = subtitleUrl.substring(0, subtitleUrl.indexOf('"'))
|
||||
subtitleUrl = subtitleUrl.replaceAll('\\u0026', '&')
|
||||
|
||||
let title = docText.substring(docText.indexOf('"title":"') + '"title":"'.length)
|
||||
title = title.substring(0, title.indexOf('","'))
|
||||
|
||||
const subtitleResponse = await fetch(subtitleUrl)
|
||||
let subtitleData = await subtitleResponse.text()
|
||||
|
||||
let subtitleContent = ''
|
||||
while (subtitleData.indexOf('">') !== -1) {
|
||||
subtitleData = subtitleData.substring(subtitleData.indexOf('">') + 2)
|
||||
subtitleContent += subtitleData.substring(0, subtitleData.indexOf('<')) + ','
|
||||
}
|
||||
|
||||
await new Promise((r) => setTimeout(r, 1000))
|
||||
|
||||
return cropText(
|
||||
`Provide a brief summary of the video using concise language and incorporating the video title.` +
|
||||
`The video title is:"${title}".The subtitle content is as follows:\n${subtitleContent}`,
|
||||
)
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { cropText } from '../../../utils'
|
||||
|
||||
export default {
|
||||
inputQuery: async () => {
|
||||
try {
|
||||
const title = document.querySelector('.QuestionHeader-title')?.textContent
|
||||
if (title) {
|
||||
const description = document.querySelector('.QuestionRichText')?.textContent
|
||||
const answer = document.querySelector('.AnswerItem .RichText')?.textContent
|
||||
|
||||
return cropText(
|
||||
`以下是一个问答平台的提问与回答内容,给出相应的摘要,以及你对此的看法.问题是:"${title}",问题的进一步描述是:"${description}".` +
|
||||
`其中一个回答如下:\n${answer}`,
|
||||
)
|
||||
} else {
|
||||
const title = document.querySelector('.Post-Title')?.textContent
|
||||
const description = document.querySelector('.Post-RichText')?.textContent
|
||||
|
||||
if (title) {
|
||||
return cropText(
|
||||
`以下是一篇文章,给出相应的摘要,以及你对此的看法.标题是:"${title}",内容是:\n"${description}"`,
|
||||
)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
[data-theme='auto'] {
|
||||
@import 'github-markdown-css/github-markdown.css';
|
||||
@media screen and (prefers-color-scheme: dark) {
|
||||
@import 'highlight.js/scss/github-dark.scss';
|
||||
--font-color: #c9d1d9;
|
||||
--theme-color: #202124;
|
||||
--theme-border-color: #3c4043;
|
||||
--dragbar-color: #3c4043;
|
||||
}
|
||||
@media screen and (prefers-color-scheme: light) {
|
||||
@import 'highlight.js/scss/github.scss';
|
||||
--font-color: #24292f;
|
||||
--theme-color: #eaecf0;
|
||||
--theme-border-color: #aeafb2;
|
||||
--dragbar-color: #dfe0e1;
|
||||
}
|
||||
}
|
||||
|
||||
[data-theme='dark'] {
|
||||
@import 'highlight.js/scss/github-dark.scss';
|
||||
@import 'github-markdown-css/github-markdown-dark.css';
|
||||
|
||||
--font-color: #c9d1d9;
|
||||
--theme-color: #202124;
|
||||
--theme-border-color: #3c4043;
|
||||
--dragbar-color: #3c4043;
|
||||
}
|
||||
|
||||
[data-theme='light'] {
|
||||
@import 'highlight.js/scss/github.scss';
|
||||
@import 'github-markdown-css/github-markdown-light.css';
|
||||
|
||||
--font-color: #24292f;
|
||||
--theme-color: #eaecf0;
|
||||
--theme-border-color: #aeafb2;
|
||||
--dragbar-color: #ccced0;
|
||||
}
|
||||
|
||||
.sidebar-free {
|
||||
margin-left: 60px;
|
||||
}
|
||||
|
||||
.chat-gpt-container {
|
||||
width: 100%;
|
||||
flex-basis: 0;
|
||||
flex-grow: 1;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.gpt-inner {
|
||||
border-radius: 8px;
|
||||
border: 1px solid;
|
||||
overflow: hidden;
|
||||
border-color: var(--theme-border-color);
|
||||
background-color: var(--theme-color);
|
||||
margin: 0;
|
||||
|
||||
hr {
|
||||
height: 1px;
|
||||
background-color: var(--theme-border-color);
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.markdown-body {
|
||||
padding: 5px 15px 10px;
|
||||
background-color: var(--theme-color);
|
||||
color: var(--font-color);
|
||||
max-height: 800px;
|
||||
overflow-y: auto;
|
||||
|
||||
ul,
|
||||
ol {
|
||||
padding-left: 1.5em;
|
||||
}
|
||||
|
||||
ol {
|
||||
list-style: none;
|
||||
counter-reset: item;
|
||||
|
||||
li {
|
||||
counter-increment: item;
|
||||
|
||||
&:before {
|
||||
content: counter(item) '. ';
|
||||
margin-left: -0.75em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.icon-and-text {
|
||||
color: var(--font-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 15px;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.manual-btn {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.gpt-loading {
|
||||
color: var(--font-color);
|
||||
animation: gpt-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
||||
}
|
||||
|
||||
.code-copy-btn {
|
||||
color: inherit;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
:is(.answer, .question, .error) {
|
||||
font-size: 15px;
|
||||
line-height: 1.6;
|
||||
border-radius: 8px;
|
||||
word-break: break-all;
|
||||
|
||||
pre {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
& > p {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
code {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
border-radius: 8px;
|
||||
|
||||
.hljs {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.gpt-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 5px;
|
||||
color: var(--font-color);
|
||||
|
||||
p {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.gpt-feedback {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.gpt-feedback-selected {
|
||||
color: #f08080;
|
||||
}
|
||||
|
||||
.gpt-util-icon {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.error {
|
||||
color: #ec4336;
|
||||
}
|
||||
|
||||
.interact-input {
|
||||
box-sizing: border-box;
|
||||
padding: 5px 15px;
|
||||
border: 0;
|
||||
border-top: 1px solid var(--theme-border-color);
|
||||
width: 100%;
|
||||
background-color: var(--theme-color);
|
||||
color: var(--font-color);
|
||||
resize: none;
|
||||
max-height: 240px;
|
||||
}
|
||||
|
||||
.dragbar {
|
||||
cursor: move;
|
||||
width: 250px;
|
||||
height: 12px;
|
||||
border-radius: 10px;
|
||||
background-color: var(--dragbar-color);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes gpt-pulse {
|
||||
0%,
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
50% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
.gpt-selection-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: 15px;
|
||||
padding: 2px;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 4px 2px 4px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.gpt-selection-toolbar-button {
|
||||
margin-left: 2px;
|
||||
padding: 2px;
|
||||
border-radius: 30px;
|
||||
background-color: #ffffff;
|
||||
color: #24292f;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.gpt-selection-toolbar-button:hover {
|
||||
background-color: #d4d5da;
|
||||
}
|
||||
|
||||
.gpt-selection-window {
|
||||
width: 600px;
|
||||
height: auto;
|
||||
border-radius: 8px;
|
||||
background-color: var(--theme-color);
|
||||
box-shadow: 4px 4px 4px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
Reference in New Issue
Block a user