mirror of
https://github.com/wassname/chatGPTBox.git
synced 2026-07-18 12:20:15 +08:00
@@ -32,6 +32,10 @@ async function runWebpack(isWithoutKatex, isWithoutTiktoken, callback) {
|
||||
import: './src/popup/index.jsx',
|
||||
dependOn: 'shared',
|
||||
},
|
||||
IndependentPanel: {
|
||||
import: './src/pages/IndependentPanel/index.jsx',
|
||||
dependOn: 'shared',
|
||||
},
|
||||
shared: [
|
||||
'preact',
|
||||
'webextension-polyfill',
|
||||
@@ -238,14 +242,21 @@ async function copyFiles(entryPoints, targetDir) {
|
||||
|
||||
async function finishOutput(outputDirSuffix) {
|
||||
const commonFiles = [
|
||||
{ src: 'src/logo.png', dst: 'logo.png' },
|
||||
|
||||
{ src: 'build/shared.js', dst: 'shared.js' },
|
||||
{ src: 'build/content-script.css', dst: 'content-script.css' }, // shared
|
||||
|
||||
{ src: 'build/content-script.js', dst: 'content-script.js' },
|
||||
{ src: 'build/content-script.css', dst: 'content-script.css' },
|
||||
|
||||
{ src: 'build/background.js', dst: 'background.js' },
|
||||
|
||||
{ src: 'build/popup.js', dst: 'popup.js' },
|
||||
{ src: 'build/popup.css', dst: 'popup.css' },
|
||||
{ src: 'src/popup/index.html', dst: 'popup.html' },
|
||||
{ src: 'src/logo.png', dst: 'logo.png' },
|
||||
|
||||
{ src: 'build/IndependentPanel.js', dst: 'IndependentPanel.js' },
|
||||
{ src: 'src/pages/IndependentPanel/index.html', dst: 'IndependentPanel.html' },
|
||||
]
|
||||
|
||||
// chromium
|
||||
|
||||
@@ -87,5 +87,10 @@
|
||||
"Clear Conversation": "Clear Conversation",
|
||||
"Retry": "Retry",
|
||||
"Exceeded maximum context length": "Exceeded maximum context length, please clear the conversation and try again",
|
||||
"Regenerate the answer after switching model": "Regenerate the answer after switching model"
|
||||
"Regenerate the answer after switching model": "Regenerate the answer after switching model",
|
||||
"Pin": "Pin",
|
||||
"Unpin": "Unpin",
|
||||
"Delete Conversation": "Delete Conversation",
|
||||
"Clear conversations": "Clear conversations",
|
||||
"Settings": "Settings"
|
||||
}
|
||||
|
||||
@@ -4,7 +4,4 @@ import { resources } from './resources'
|
||||
i18n.init({
|
||||
resources,
|
||||
fallbackLng: 'en',
|
||||
interpolation: {
|
||||
escapeValue: false, // not needed for react as it escapes by default
|
||||
},
|
||||
})
|
||||
|
||||
@@ -87,5 +87,10 @@
|
||||
"Clear Conversation": "清理对话",
|
||||
"Retry": "重试",
|
||||
"Exceeded maximum context length": "超出最大上下文长度, 请清理对话并重试",
|
||||
"Regenerate the answer after switching model": "快捷切换模型时自动重新生成回答"
|
||||
"Regenerate the answer after switching model": "快捷切换模型时自动重新生成回答",
|
||||
"Pin": "固定侧边",
|
||||
"Unpin": "收缩侧边",
|
||||
"Delete Conversation": "删除对话",
|
||||
"Clear conversations": "清空记录",
|
||||
"Settings": "设置"
|
||||
}
|
||||
|
||||
@@ -87,5 +87,10 @@
|
||||
"Clear Conversation": "清理對話",
|
||||
"Retry": "重試",
|
||||
"Exceeded maximum context length": "超出最大上下文長度, 請清理對話並重試",
|
||||
"Regenerate the answer after switching model": "快捷切換模型時自動重新生成回答"
|
||||
"Regenerate the answer after switching model": "快捷切換模型時自動重新生成回答",
|
||||
"Pin": "固定側邊",
|
||||
"Unpin": "收縮側邊",
|
||||
"Delete Conversation": "刪除對話",
|
||||
"Clear conversations": "清空記錄",
|
||||
"Settings": "設置"
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ export async function generateAnswersWithChatgptWebApi(port, question, session,
|
||||
port.onDisconnect.addListener(() => {
|
||||
console.debug('port disconnected')
|
||||
controller.abort()
|
||||
deleteConversation(accessToken, session.conversationId)
|
||||
if (session.autoClean) deleteConversation(accessToken, session.conversationId)
|
||||
})
|
||||
|
||||
const models = await getModels(accessToken).catch(() => {
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
ConfirmButton.propTypes = {
|
||||
onConfirm: PropTypes.func.isRequired,
|
||||
text: PropTypes.string.isRequired,
|
||||
}
|
||||
|
||||
function ConfirmButton({ onConfirm, text }) {
|
||||
const { t } = useTranslation()
|
||||
const [waitConfirm, setWaitConfirm] = useState(false)
|
||||
const confirmRef = useRef(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (waitConfirm) confirmRef.current.focus()
|
||||
}, [waitConfirm])
|
||||
|
||||
return (
|
||||
<span>
|
||||
<button
|
||||
ref={confirmRef}
|
||||
type="button"
|
||||
className="normal-button"
|
||||
style={{
|
||||
...(waitConfirm ? {} : { display: 'none' }),
|
||||
}}
|
||||
onBlur={() => {
|
||||
setWaitConfirm(false)
|
||||
}}
|
||||
onClick={() => {
|
||||
setWaitConfirm(false)
|
||||
onConfirm()
|
||||
}}
|
||||
>
|
||||
{t('Confirm')}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="normal-button"
|
||||
style={{
|
||||
...(waitConfirm ? { display: 'none' } : {}),
|
||||
}}
|
||||
onClick={() => {
|
||||
setWaitConfirm(true)
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</button>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
export default ConfirmButton
|
||||
@@ -65,8 +65,8 @@ function ConversationCard(props) {
|
||||
const config = useConfig()
|
||||
|
||||
useEffect(() => {
|
||||
if (props.onUpdate) props.onUpdate()
|
||||
})
|
||||
if (props.onUpdate) props.onUpdate(port, session, conversationItemData)
|
||||
}, [session, conversationItemData])
|
||||
|
||||
useEffect(() => {
|
||||
bodyRef.current.scrollTop = bodyRef.current.scrollHeight
|
||||
@@ -201,6 +201,7 @@ function ConversationCard(props) {
|
||||
title={t('Close the Window')}
|
||||
size={16}
|
||||
onClick={() => {
|
||||
port.disconnect()
|
||||
if (props.onClose) props.onClose()
|
||||
}}
|
||||
/>
|
||||
@@ -217,7 +218,7 @@ function ConversationCard(props) {
|
||||
<img src={logo} style="user-select:none;width:20px;height:20px;" />
|
||||
)}
|
||||
<select
|
||||
style={{ width: windowSize[0] * 0.05 + 'px' }}
|
||||
style={props.notClampSize ? {} : { width: windowSize[0] * 0.05 + 'px' }}
|
||||
className="normal-button"
|
||||
required
|
||||
onChange={(e) => {
|
||||
@@ -275,6 +276,7 @@ function ConversationCard(props) {
|
||||
)}
|
||||
<DeleteButton
|
||||
size={16}
|
||||
text={t('Clear Conversation')}
|
||||
onConfirm={() => {
|
||||
port.postMessage({ stop: true })
|
||||
Browser.runtime.sendMessage({
|
||||
@@ -309,7 +311,11 @@ function ConversationCard(props) {
|
||||
<div
|
||||
ref={bodyRef}
|
||||
className="markdown-body"
|
||||
style={{ maxHeight: windowSize[1] * 0.75 + 'px' }}
|
||||
style={
|
||||
props.notClampSize
|
||||
? { flexGrow: 1 }
|
||||
: { maxHeight: windowSize[1] * 0.75 + 'px', resize: 'vertical' }
|
||||
}
|
||||
>
|
||||
{conversationItemData.map((data, idx) => (
|
||||
<ConversationItem
|
||||
@@ -356,6 +362,7 @@ ConversationCard.propTypes = {
|
||||
onClose: PropTypes.func,
|
||||
dockable: PropTypes.bool,
|
||||
onDock: PropTypes.func,
|
||||
notClampSize: PropTypes.bool,
|
||||
}
|
||||
|
||||
export default memo(ConversationCard)
|
||||
|
||||
@@ -80,11 +80,10 @@ function DecisionCard(props) {
|
||||
return <ConversationCard session={props.session} question={question} />
|
||||
}
|
||||
return (
|
||||
<p
|
||||
className="gpt-inner manual-btn icon-and-text"
|
||||
onClick={() => setTriggered(true)}
|
||||
>
|
||||
<SearchIcon size="small" /> {t('Ask ChatGPT')}
|
||||
<p className="gpt-inner manual-btn" onClick={() => setTriggered(true)}>
|
||||
<span className="icon-and-text">
|
||||
<SearchIcon size="small" /> {t('Ask ChatGPT')}
|
||||
</span>
|
||||
</p>
|
||||
)
|
||||
case 'questionMark':
|
||||
@@ -95,18 +94,19 @@ function DecisionCard(props) {
|
||||
return <ConversationCard session={props.session} question={question} />
|
||||
}
|
||||
return (
|
||||
<p
|
||||
className="gpt-inner manual-btn icon-and-text"
|
||||
onClick={() => setTriggered(true)}
|
||||
>
|
||||
<SearchIcon size="small" /> {t('Ask ChatGPT')}
|
||||
<p className="gpt-inner manual-btn" onClick={() => setTriggered(true)}>
|
||||
<span className="icon-and-text">
|
||||
<SearchIcon size="small" /> {t('Ask ChatGPT')}
|
||||
</span>
|
||||
</p>
|
||||
)
|
||||
}
|
||||
else
|
||||
return (
|
||||
<p className="gpt-inner icon-and-text">
|
||||
<LightBulbIcon size="small" /> {t('No Input Found')}
|
||||
<p className="gpt-inner">
|
||||
<span className="icon-and-text">
|
||||
<LightBulbIcon size="small" /> {t('No Input Found')}
|
||||
</span>
|
||||
</p>
|
||||
)
|
||||
})()}
|
||||
|
||||
@@ -6,9 +6,10 @@ import { TrashIcon } from '@primer/octicons-react'
|
||||
DeleteButton.propTypes = {
|
||||
onConfirm: PropTypes.func.isRequired,
|
||||
size: PropTypes.number.isRequired,
|
||||
text: PropTypes.string.isRequired,
|
||||
}
|
||||
|
||||
function DeleteButton({ onConfirm, size }) {
|
||||
function DeleteButton({ onConfirm, size, text }) {
|
||||
const { t } = useTranslation()
|
||||
const [waitConfirm, setWaitConfirm] = useState(false)
|
||||
const confirmRef = useRef(null)
|
||||
@@ -38,7 +39,7 @@ function DeleteButton({ onConfirm, size }) {
|
||||
{t('Confirm')}
|
||||
</button>
|
||||
<span
|
||||
title={t('Clear Conversation')}
|
||||
title={text}
|
||||
className="gpt-util-icon"
|
||||
style={waitConfirm ? { display: 'none' } : {}}
|
||||
onClick={() => {
|
||||
|
||||
@@ -139,18 +139,19 @@ export function getNavigatorLanguage() {
|
||||
return navigator.language.substring(0, 2)
|
||||
}
|
||||
|
||||
export function isUsingApiKey(config) {
|
||||
export function isUsingApiKey(configOrSession) {
|
||||
return (
|
||||
gptApiModelKeys.includes(config.modelName) || chatgptApiModelKeys.includes(config.modelName)
|
||||
gptApiModelKeys.includes(configOrSession.modelName) ||
|
||||
chatgptApiModelKeys.includes(configOrSession.modelName)
|
||||
)
|
||||
}
|
||||
|
||||
export function isUsingMultiModeModel(config) {
|
||||
return bingWebModelKeys.includes(config.modelName)
|
||||
export function isUsingMultiModeModel(configOrSession) {
|
||||
return bingWebModelKeys.includes(configOrSession.modelName)
|
||||
}
|
||||
|
||||
export function isUsingCustomModel(config) {
|
||||
return customApiModelKeys.includes(config.modelName)
|
||||
export function isUsingCustomModel(configOrSession) {
|
||||
return customApiModelKeys.includes(configOrSession.modelName)
|
||||
}
|
||||
|
||||
export async function getPreferredLanguageKey() {
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import Browser from 'webextension-polyfill'
|
||||
import { initSession } from '../utils/index.mjs'
|
||||
import { getUserConfig } from './index.mjs'
|
||||
|
||||
export const initDefaultSession = async () => {
|
||||
const config = await getUserConfig()
|
||||
const modelName = config.modelName
|
||||
return initSession({
|
||||
sessionName: new Date().toLocaleString(),
|
||||
modelName: modelName,
|
||||
autoClean: false,
|
||||
})
|
||||
}
|
||||
|
||||
export const createSession = async (session) => {
|
||||
const currentSessions = await getSessions()
|
||||
if (!session) session = await initDefaultSession()
|
||||
currentSessions.unshift(session)
|
||||
await Browser.storage.local.set({ sessions: currentSessions })
|
||||
return { session, currentSessions }
|
||||
}
|
||||
|
||||
export const deleteSession = async (sessionId) => {
|
||||
const currentSessions = await getSessions()
|
||||
const index = currentSessions.findIndex((session) => session.sessionId === sessionId)
|
||||
currentSessions.splice(index, 1)
|
||||
if (currentSessions.length > 0) {
|
||||
await Browser.storage.local.set({ sessions: currentSessions })
|
||||
return currentSessions
|
||||
}
|
||||
return await resetSessions()
|
||||
}
|
||||
|
||||
export const getSession = async (sessionId) => {
|
||||
const currentSessions = await getSessions()
|
||||
return {
|
||||
session: currentSessions.find((session) => session.sessionId === sessionId),
|
||||
currentSessions,
|
||||
}
|
||||
}
|
||||
|
||||
export const updateSession = async (newSession) => {
|
||||
const currentSessions = await getSessions()
|
||||
currentSessions[
|
||||
currentSessions.findIndex((session) => session.sessionId === newSession.sessionId)
|
||||
] = newSession
|
||||
await Browser.storage.local.set({ sessions: currentSessions })
|
||||
return currentSessions
|
||||
}
|
||||
|
||||
export const resetSessions = async () => {
|
||||
const currentSessions = [await initDefaultSession()]
|
||||
await Browser.storage.local.set({ sessions: currentSessions })
|
||||
return currentSessions
|
||||
}
|
||||
|
||||
export const getSessions = async () => {
|
||||
const { sessions } = await Browser.storage.local.get('sessions')
|
||||
if (sessions && sessions.length > 0) return sessions
|
||||
return await resetSessions()
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
@import '../fonts/styles.css';
|
||||
@import '../pages/styles.scss';
|
||||
|
||||
[data-theme='auto'] {
|
||||
@import 'github-markdown-css/github-markdown.css';
|
||||
@@ -54,6 +55,9 @@
|
||||
margin-bottom: 20px;
|
||||
|
||||
.gpt-inner {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: 8px;
|
||||
border: 1px solid;
|
||||
overflow: hidden;
|
||||
@@ -73,7 +77,6 @@
|
||||
padding: 5px 15px 10px;
|
||||
background-color: var(--theme-color);
|
||||
color: var(--font-color);
|
||||
resize: vertical;
|
||||
overflow-y: auto;
|
||||
|
||||
ul,
|
||||
@@ -206,6 +209,7 @@
|
||||
background-color: var(--theme-color);
|
||||
color: var(--font-color);
|
||||
resize: none;
|
||||
min-height: 70px;
|
||||
max-height: 240px;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ export function useConfig(initFn) {
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
const listener = (changes) => {
|
||||
if (Object.keys(changes).length === 1 && 'sessions' in changes) return
|
||||
|
||||
const changedItems = Object.keys(changes)
|
||||
let newConfig = {}
|
||||
for (const key of changedItems) {
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
import {
|
||||
createSession,
|
||||
resetSessions,
|
||||
getSessions,
|
||||
updateSession,
|
||||
getSession,
|
||||
deleteSession,
|
||||
} from '../../config/localSession'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import './styles.scss'
|
||||
import { useConfig } from '../../hooks/use-config.mjs'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import ConfirmButton from '../../components/ConfirmButton'
|
||||
import ConversationCard from '../../components/ConversationCard'
|
||||
import DeleteButton from '../../components/DeleteButton'
|
||||
|
||||
function App() {
|
||||
const { t } = useTranslation()
|
||||
const [collapsed, setCollapsed] = useState(false)
|
||||
const config = useConfig()
|
||||
const [sessions, setSessions] = useState([])
|
||||
const [sessionId, setSessionId] = useState(null)
|
||||
const [currentSession, setCurrentSession] = useState(null)
|
||||
const [renderContent, setRenderContent] = useState(false)
|
||||
const currentPort = useRef(null)
|
||||
|
||||
const setSessionIdSafe = async (sessionId) => {
|
||||
if (currentPort.current) {
|
||||
try {
|
||||
currentPort.current.postMessage({ stop: true })
|
||||
currentPort.current.disconnect()
|
||||
} catch (e) {
|
||||
/* empty */
|
||||
}
|
||||
currentPort.current = null
|
||||
}
|
||||
const { session, currentSessions } = await getSession(sessionId)
|
||||
if (session) setSessionId(sessionId)
|
||||
else if (currentSessions.length > 0) setSessionId(currentSessions[0].sessionId)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
document.documentElement.dataset.theme = config.themeMode
|
||||
}, [config.themeMode])
|
||||
|
||||
useEffect(() => {
|
||||
// eslint-disable-next-line
|
||||
;(async () => {
|
||||
const sessions = await getSessions()
|
||||
setSessions(sessions)
|
||||
await setSessionIdSafe(sessions[0].sessionId)
|
||||
})()
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
// eslint-disable-next-line
|
||||
;(async () => {
|
||||
if (sessions.length > 0) {
|
||||
setCurrentSession((await getSession(sessionId)).session)
|
||||
setRenderContent(false)
|
||||
setTimeout(() => {
|
||||
setRenderContent(true)
|
||||
})
|
||||
}
|
||||
})()
|
||||
}, [sessionId])
|
||||
|
||||
const toggleSidebar = () => {
|
||||
setCollapsed(!collapsed)
|
||||
}
|
||||
|
||||
const createNewChat = async () => {
|
||||
const { session, currentSessions } = await createSession()
|
||||
setSessions(currentSessions)
|
||||
await setSessionIdSafe(session.sessionId)
|
||||
}
|
||||
|
||||
const clearConversations = async () => {
|
||||
const sessions = await resetSessions()
|
||||
setSessions(sessions)
|
||||
await setSessionIdSafe(sessions[0].sessionId)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="IndependentPanel">
|
||||
<div className="chat-container">
|
||||
<div className={`chat-sidebar ${collapsed ? 'collapsed' : ''}`}>
|
||||
<div className="chat-sidebar-button-group">
|
||||
<button className="normal-button" onClick={toggleSidebar}>
|
||||
{collapsed ? t('Pin') : t('Unpin')}
|
||||
</button>
|
||||
<button className="normal-button" onClick={createNewChat}>
|
||||
{t('New Chat')}
|
||||
</button>
|
||||
</div>
|
||||
<hr />
|
||||
<div className="chat-list">
|
||||
{sessions.map(
|
||||
(
|
||||
session,
|
||||
index, // TODO editable session name
|
||||
) => (
|
||||
<button
|
||||
key={index}
|
||||
className={`normal-button ${sessionId === session.sessionId ? 'active' : ''}`}
|
||||
style="display: flex; align-items: center; justify-content: space-between;"
|
||||
onClick={() => {
|
||||
setSessionIdSafe(session.sessionId)
|
||||
}}
|
||||
>
|
||||
{session.sessionName}
|
||||
<span className="gpt-util-group">
|
||||
<DeleteButton
|
||||
size={14}
|
||||
text={t('Delete Conversation')}
|
||||
onConfirm={() =>
|
||||
deleteSession(session.sessionId).then((sessions) => {
|
||||
setSessions(sessions)
|
||||
setSessionIdSafe(sessions[0].sessionId)
|
||||
})
|
||||
}
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
<hr />
|
||||
<div className="chat-sidebar-button-group">
|
||||
<ConfirmButton text={t('Clear conversations')} onConfirm={clearConversations} />
|
||||
<button className="normal-button">{t('Settings')}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="chat-content">
|
||||
{renderContent && currentSession && currentSession.conversationRecords && (
|
||||
<div className="chatgptbox-container" style="height:100%;">
|
||||
<ConversationCard
|
||||
session={currentSession}
|
||||
notClampSize={true}
|
||||
onUpdate={(port, session, cData) => {
|
||||
currentPort.current = port
|
||||
if (cData.length > 0 && cData[cData.length - 1].done) {
|
||||
updateSession(session).then(setSessions)
|
||||
setCurrentSession(session)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
@@ -0,0 +1,13 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>ChatGPTBox</title>
|
||||
<link rel="stylesheet" href="content-script.css" />
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script src="shared.js"></script>
|
||||
<script src="IndependentPanel.js"></script>
|
||||
<script src="content-script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,19 @@
|
||||
import { render } from 'preact'
|
||||
import '../../_locales/i18n-react'
|
||||
import App from './App'
|
||||
import Browser from 'webextension-polyfill'
|
||||
import { changeLanguage } from 'i18next'
|
||||
import { getPreferredLanguageKey } from '../../config/index.mjs'
|
||||
|
||||
document.body.style.margin = 0
|
||||
document.body.style.overflow = 'hidden'
|
||||
getPreferredLanguageKey().then((lang) => {
|
||||
changeLanguage(lang)
|
||||
})
|
||||
Browser.runtime.onMessage.addListener(async (message) => {
|
||||
if (message.type === 'CHANGE_LANG') {
|
||||
const data = message.data
|
||||
changeLanguage(data.lang)
|
||||
}
|
||||
})
|
||||
render(<App />, document.getElementById('app'))
|
||||
@@ -0,0 +1,131 @@
|
||||
[data-theme='auto'] {
|
||||
@media screen and (prefers-color-scheme: dark) {
|
||||
--font-color: #c9d1d9;
|
||||
--font-active-color: #ffffff;
|
||||
--theme-color: #202124;
|
||||
--theme-border-color: #3c4043;
|
||||
--active-color: #3c4043;
|
||||
}
|
||||
@media screen and (prefers-color-scheme: light) {
|
||||
--font-color: #24292f;
|
||||
--font-active-color: #cc3333;
|
||||
--theme-color: #ffffff;
|
||||
--theme-border-color: #aeafb2;
|
||||
--active-color: #d0d4da;
|
||||
}
|
||||
}
|
||||
|
||||
[data-theme='dark'] {
|
||||
--font-color: #c9d1d9;
|
||||
--font-active-color: #ffffff;
|
||||
--theme-color: #202124;
|
||||
--theme-border-color: #3c4043;
|
||||
--active-color: #3c4043;
|
||||
}
|
||||
|
||||
[data-theme='light'] {
|
||||
--font-color: #24292f;
|
||||
--font-active-color: #cc3333;
|
||||
--theme-color: #ffffff;
|
||||
--theme-border-color: #aeafb2;
|
||||
--active-color: #d0d4da;
|
||||
}
|
||||
|
||||
.IndependentPanel * {
|
||||
font-family: 'Cairo', sans-serif;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.IndependentPanel {
|
||||
.chat-container {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.chat-sidebar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 250px;
|
||||
background-color: var(--theme-color);
|
||||
transition: width 0.3s;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.chat-sidebar.collapsed {
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.chat-sidebar:hover,
|
||||
.chat-sidebar:not(.collapsed) {
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
.chat-sidebar-button-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0;
|
||||
background-color: var(--theme-color);
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.chat-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
padding: 0;
|
||||
background-color: var(--theme-color);
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.chat-content {
|
||||
flex-grow: 1;
|
||||
border: 1px solid var(--theme-border-color);
|
||||
background-color: var(--theme-color);
|
||||
}
|
||||
|
||||
.normal-button {
|
||||
width: 100%;
|
||||
min-height: 40px;
|
||||
padding: 1px 6px;
|
||||
border: 1px solid;
|
||||
border-color: var(--theme-border-color);
|
||||
background-color: var(--theme-color);
|
||||
color: var(--font-color);
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.gpt-util-group {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.gpt-util-icon {
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
align-items: center;
|
||||
color: var(--font-color);
|
||||
}
|
||||
.gpt-util-icon:hover {
|
||||
color: var(--font-active-color);
|
||||
}
|
||||
|
||||
.normal-button.active,
|
||||
.normal-button:hover {
|
||||
background-color: var(--active-color);
|
||||
}
|
||||
|
||||
hr {
|
||||
height: 1px;
|
||||
background-color: var(--theme-border-color);
|
||||
border: none;
|
||||
margin: 15px 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
@import 'IndependentPanel/styles.scss';
|
||||
@@ -1,3 +1,5 @@
|
||||
import { Models } from '../config/index.mjs'
|
||||
|
||||
/**
|
||||
* @typedef {object} BingWeb
|
||||
* @property {string|null} conversationSignature
|
||||
@@ -9,9 +11,12 @@
|
||||
* @typedef {object} Session
|
||||
* @property {string|null} question
|
||||
* @property {Object[]|null} conversationRecords
|
||||
* @property {boolean} isRetry
|
||||
* @property {string|null} sessionName
|
||||
* @property {string|null} sessionId
|
||||
* @property {string|null} aiName
|
||||
* @property {string|null} modelName
|
||||
* @property {boolean|null} autoClean
|
||||
* @property {boolean} isRetry
|
||||
* @property {string|null} conversationId - chatGPT web mode
|
||||
* @property {string|null} messageId - chatGPT web mode
|
||||
* @property {string|null} parentMessageId - chatGPT web mode
|
||||
@@ -20,16 +25,31 @@
|
||||
/**
|
||||
* @param {string|null} question
|
||||
* @param {Object[]|null} conversationRecords
|
||||
* @param {string|null} sessionName
|
||||
* @param {string|null} modelName
|
||||
* @param {boolean|null} autoClean
|
||||
* @returns {Session}
|
||||
*/
|
||||
export function initSession({ question = null, conversationRecords = [] } = {}) {
|
||||
export function initSession({
|
||||
question = null,
|
||||
conversationRecords = [],
|
||||
sessionName = null,
|
||||
modelName = null,
|
||||
autoClean = true,
|
||||
} = {}) {
|
||||
return {
|
||||
// common
|
||||
question,
|
||||
conversationRecords,
|
||||
|
||||
sessionName,
|
||||
sessionId: crypto.randomUUID(),
|
||||
|
||||
aiName: modelName ? Models[modelName].desc : null,
|
||||
modelName,
|
||||
|
||||
autoClean,
|
||||
isRetry: false,
|
||||
aiName: null,
|
||||
modelName: null,
|
||||
|
||||
// chatgpt-web
|
||||
conversationId: null,
|
||||
|
||||
Reference in New Issue
Block a user