Skip to content

Commit fc982e4

Browse files
committed
feat: option to regenerate the answer after switching model (#98)
1 parent bf71a13 commit fc982e4

File tree

9 files changed

+75
-82
lines changed

9 files changed

+75
-82
lines changed

src/_locales/en/main.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,5 +86,6 @@
8686
"Confirm": "Confirm",
8787
"Clear Conversation": "Clear Conversation",
8888
"Retry": "Retry",
89-
"Exceeded maximum context length": "Exceeded maximum context length, please clear the conversation and try again"
89+
"Exceeded maximum context length": "Exceeded maximum context length, please clear the conversation and try again",
90+
"Regenerate the answer after switching model": "Regenerate the answer after switching model"
9091
}

src/_locales/zh-hans/main.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,5 +86,6 @@
8686
"Confirm": "确认",
8787
"Clear Conversation": "清理对话",
8888
"Retry": "重试",
89-
"Exceeded maximum context length": "超出最大上下文长度, 请清理对话并重试"
89+
"Exceeded maximum context length": "超出最大上下文长度, 请清理对话并重试",
90+
"Regenerate the answer after switching model": "快捷切换模型时自动重新生成回答"
9091
}

src/_locales/zh-hant/main.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,5 +86,6 @@
8686
"Confirm": "確認",
8787
"Clear Conversation": "清理對話",
8888
"Retry": "重試",
89-
"Exceeded maximum context length": "超出最大上下文長度, 請清理對話並重試"
89+
"Exceeded maximum context length": "超出最大上下文長度, 請清理對話並重試",
90+
"Regenerate the answer after switching model": "快捷切換模型時自動重新生成回答"
9091
}

src/components/ConversationCard/index.jsx

Lines changed: 20 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ import FileSaver from 'file-saver'
1010
import { render } from 'preact'
1111
import FloatingToolbar from '../FloatingToolbar'
1212
import { useClampWindowSize } from '../../hooks/use-clamp-window-size'
13-
import { defaultConfig, getUserConfig, Models } from '../../config/index.mjs'
13+
import { Models } from '../../config/index.mjs'
1414
import { useTranslation } from 'react-i18next'
1515
import DeleteButton from '../DeleteButton'
16+
import { useConfig } from '../../hooks/use-config.mjs'
1617

1718
const logo = Browser.runtime.getURL('logo.png')
1819

@@ -61,11 +62,7 @@ function ConversationCard(props) {
6162
}
6263
})(),
6364
)
64-
const [config, setConfig] = useState(defaultConfig)
65-
66-
useEffect(() => {
67-
getUserConfig().then(setConfig)
68-
}, [])
65+
const config = useConfig()
6966

7067
useEffect(() => {
7168
if (props.onUpdate) props.onUpdate()
@@ -172,6 +169,20 @@ function ConversationCard(props) {
172169
}
173170
}, [conversationItemData])
174171

172+
const getRetryFn = (session) => () => {
173+
updateAnswer(`<p class="gpt-loading">${t('Waiting for response...')}</p>`, false, 'answer')
174+
setIsReady(false)
175+
176+
const newSession = { ...session, isRetry: true }
177+
setSession(newSession)
178+
try {
179+
port.postMessage({ stop: true })
180+
port.postMessage({ session: newSession })
181+
} catch (e) {
182+
updateAnswer(e, false, 'error')
183+
}
184+
}
185+
175186
return (
176187
<div className="gpt-inner">
177188
<div className="gpt-header" style="margin: 15px;">
@@ -203,7 +214,8 @@ function ConversationCard(props) {
203214
required
204215
onChange={(e) => {
205216
const modelName = e.target.value
206-
setSession({ ...session, modelName, aiName: t(Models[modelName].desc) })
217+
if (config.autoRegenAfterSwitchModel)
218+
getRetryFn({ ...session, modelName, aiName: t(Models[modelName].desc) })()
207219
}}
208220
>
209221
{Object.entries(Models).map(([key, model]) => {
@@ -298,27 +310,7 @@ function ConversationCard(props) {
298310
session={session}
299311
done={data.done}
300312
port={port}
301-
onRetry={
302-
idx === conversationItemData.length - 1
303-
? () => {
304-
updateAnswer(
305-
`<p class="gpt-loading">${t('Waiting for response...')}</p>`,
306-
false,
307-
'answer',
308-
)
309-
setIsReady(false)
310-
311-
const newSession = { ...session, isRetry: true }
312-
setSession(newSession)
313-
try {
314-
port.postMessage({ stop: true })
315-
port.postMessage({ session: newSession })
316-
} catch (e) {
317-
updateAnswer(e, false, 'error')
318-
}
319-
}
320-
: null
321-
}
313+
onRetry={idx === conversationItemData.length - 1 ? getRetryFn(session) : null}
322314
/>
323315
))}
324316
</div>

src/components/DecisionCard/index.jsx

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,41 +2,20 @@ import { LightBulbIcon, SearchIcon } from '@primer/octicons-react'
22
import { useState, useEffect } from 'react'
33
import PropTypes from 'prop-types'
44
import ConversationCard from '../ConversationCard'
5-
import { defaultConfig, getUserConfig } from '../../config/index.mjs'
6-
import Browser from 'webextension-polyfill'
75
import { getPossibleElementByQuerySelector, endsWithQuestionMark } from '../../utils'
86
import { useTranslation } from 'react-i18next'
7+
import { useConfig } from '../../hooks/use-config.mjs'
98

109
function DecisionCard(props) {
1110
const { t } = useTranslation()
1211
const [triggered, setTriggered] = useState(false)
13-
const [config, setConfig] = useState(defaultConfig)
1412
const [render, setRender] = useState(false)
13+
const config = useConfig(() => {
14+
setRender(true)
15+
})
1516

1617
const question = props.question
1718

18-
useEffect(() => {
19-
getUserConfig().then((config) => {
20-
setConfig(config)
21-
setRender(true)
22-
})
23-
}, [])
24-
25-
useEffect(() => {
26-
const listener = (changes) => {
27-
const changedItems = Object.keys(changes)
28-
let newConfig = {}
29-
for (const key of changedItems) {
30-
newConfig[key] = changes[key].newValue
31-
}
32-
setConfig({ ...config, ...newConfig })
33-
}
34-
Browser.storage.local.onChanged.addListener(listener)
35-
return () => {
36-
Browser.storage.local.onChanged.removeListener(listener)
37-
}
38-
}, [config])
39-
4019
const updatePosition = () => {
4120
if (!render) return
4221

src/components/FloatingToolbar/index.jsx

Lines changed: 5 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ import Browser from 'webextension-polyfill'
22
import { cloneElement, useEffect, useState } from 'react'
33
import ConversationCard from '../ConversationCard'
44
import PropTypes from 'prop-types'
5-
import { defaultConfig, getUserConfig } from '../../config/index.mjs'
65
import { config as toolsConfig } from '../../content-script/selection-tools'
76
import { getClientPosition, isMobile, setElementPositionInViewport } from '../../utils'
87
import Draggable from 'react-draggable'
98
import { useClampWindowSize } from '../../hooks/use-clamp-window-size'
109
import { useTranslation } from 'react-i18next'
10+
import { useConfig } from '../../hooks/use-config.mjs'
1111

1212
const logo = Browser.runtime.getURL('logo.png')
1313

@@ -16,36 +16,15 @@ function FloatingToolbar(props) {
1616
const [selection, setSelection] = useState(props.selection)
1717
const [prompt, setPrompt] = useState(props.prompt)
1818
const [triggered, setTriggered] = useState(props.triggered)
19-
const [config, setConfig] = useState(defaultConfig)
2019
const [render, setRender] = useState(false)
2120
const [closeable, setCloseable] = useState(props.closeable)
2221
const [position, setPosition] = useState(getClientPosition(props.container))
2322
const [virtualPosition, setVirtualPosition] = useState({ x: 0, y: 0 })
2423
const windowSize = useClampWindowSize([750, 1500], [0, Infinity])
25-
26-
useEffect(() => {
27-
getUserConfig().then((config) => {
28-
setConfig(config)
29-
setRender(true)
30-
31-
if (!triggered) props.container.style.position = 'absolute'
32-
})
33-
}, [])
34-
35-
useEffect(() => {
36-
const listener = (changes) => {
37-
const changedItems = Object.keys(changes)
38-
let newConfig = {}
39-
for (const key of changedItems) {
40-
newConfig[key] = changes[key].newValue
41-
}
42-
setConfig({ ...config, ...newConfig })
43-
}
44-
Browser.storage.local.onChanged.addListener(listener)
45-
return () => {
46-
Browser.storage.local.onChanged.removeListener(listener)
47-
}
48-
}, [config])
24+
const config = useConfig(() => {
25+
setRender(true)
26+
if (!triggered) props.container.style.position = 'absolute'
27+
})
4928

5029
useEffect(() => {
5130
if (isMobile()) {

src/config/index.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ export const defaultConfig = {
6666
preferredLanguage: getNavigatorLanguage(),
6767
insertAtTop: isMobile(),
6868
lockWhenAnswer: false,
69+
autoRegenAfterSwitchModel: false,
6970
customModelApiUrl: 'http://localhost:8000/chat/completions',
7071
customModelName: 'chatglm-6b-int4',
7172

src/hooks/use-config.mjs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { useEffect, useState } from 'react'
2+
import { defaultConfig, getUserConfig } from '../config/index.mjs'
3+
import Browser from 'webextension-polyfill'
4+
5+
export function useConfig(initFn) {
6+
const [config, setConfig] = useState(defaultConfig)
7+
useEffect(() => {
8+
getUserConfig().then((config) => {
9+
setConfig(config)
10+
if (initFn) initFn()
11+
})
12+
}, [])
13+
useEffect(() => {
14+
const listener = (changes) => {
15+
const changedItems = Object.keys(changes)
16+
let newConfig = {}
17+
for (const key of changedItems) {
18+
newConfig[key] = changes[key].newValue
19+
}
20+
setConfig({ ...config, ...newConfig })
21+
}
22+
Browser.storage.local.onChanged.addListener(listener)
23+
return () => {
24+
Browser.storage.local.onChanged.removeListener(listener)
25+
}
26+
}, [config])
27+
return config
28+
}

src/popup/Popup.jsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,17 @@ function GeneralPart({ config, updateConfig }) {
235235
/>
236236
{t('Lock scrollbar while answering')}
237237
</label>
238+
<label>
239+
<input
240+
type="checkbox"
241+
checked={config.autoRegenAfterSwitchModel}
242+
onChange={(e) => {
243+
const checked = e.target.checked
244+
updateConfig({ autoRegenAfterSwitchModel: checked })
245+
}}
246+
/>
247+
{t('Regenerate the answer after switching model')}
248+
</label>
238249
<br />
239250
</>
240251
)

0 commit comments

Comments
 (0)