mirror of
https://github.com/wassname/chatGPTBox.git
synced 2026-08-13 12:00:22 +08:00
This commit is contained in:
@@ -11,6 +11,7 @@ import { render } from 'preact'
|
||||
import FloatingToolbar from '../FloatingToolbar'
|
||||
import { useClampWindowSize } from '../../hooks/use-clamp-window-size'
|
||||
import { defaultConfig, getUserConfig } from '../../config/index.mjs'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const logo = Browser.runtime.getURL('logo.png')
|
||||
|
||||
@@ -29,6 +30,7 @@ class ConversationItemData extends Object {
|
||||
}
|
||||
|
||||
function ConversationCard(props) {
|
||||
const { t } = useTranslation()
|
||||
const [isReady, setIsReady] = useState(!props.question)
|
||||
const [port, setPort] = useState(() => Browser.runtime.connect())
|
||||
const [session, setSession] = useState(props.session)
|
||||
@@ -44,7 +46,7 @@ function ConversationCard(props) {
|
||||
return [
|
||||
new ConversationItemData(
|
||||
'answer',
|
||||
'<p class="gpt-loading">Waiting for response...</p>',
|
||||
`<p class="gpt-loading">${t(`Waiting for response...`)}</p>`,
|
||||
),
|
||||
]
|
||||
else return []
|
||||
@@ -131,20 +133,26 @@ function ConversationCard(props) {
|
||||
switch (msg.error) {
|
||||
case 'UNAUTHORIZED':
|
||||
UpdateAnswer(
|
||||
`UNAUTHORIZED<br>Please login at https://chat.openai.com first${
|
||||
isSafari() ? '<br>Then open https://chat.openai.com/api/auth/session' : ''
|
||||
}<br>And refresh this page or type you question again` +
|
||||
`<br><br>Consider creating an api key at https://platform.openai.com/account/api-keys<hr>`,
|
||||
`${t('UNAUTHORIZED')}<br>${t('Please login at https://chat.openai.com first')}${
|
||||
isSafari() ? `<br>${t('Then open https://chat.openai.com/api/auth/session')}` : ''
|
||||
}<br>${t('And refresh this page or type you question again')}` +
|
||||
`<br><br>${t(
|
||||
'Consider creating an api key at https://platform.openai.com/account/api-keys',
|
||||
)}<hr>`,
|
||||
false,
|
||||
'error',
|
||||
)
|
||||
break
|
||||
case 'CLOUDFLARE':
|
||||
UpdateAnswer(
|
||||
`OpenAI Security Check Required<br>Please open ${
|
||||
isSafari() ? 'https://chat.openai.com/api/auth/session' : 'https://chat.openai.com'
|
||||
}<br>And refresh this page or type you question again` +
|
||||
`<br><br>Consider creating an api key at https://platform.openai.com/account/api-keys<hr>`,
|
||||
`${t('OpenAI Security Check Required')}<br>${
|
||||
isSafari()
|
||||
? t('Please open https://chat.openai.com/api/auth/session')
|
||||
: t('Please open https://chat.openai.com')
|
||||
}<br>${t('And refresh this page or type you question again')}` +
|
||||
`<br><br>${t(
|
||||
'Consider creating an api key at https://platform.openai.com/account/api-keys',
|
||||
)}<hr>`,
|
||||
false,
|
||||
'error',
|
||||
)
|
||||
@@ -172,7 +180,7 @@ function ConversationCard(props) {
|
||||
<XLg
|
||||
className="gpt-util-icon"
|
||||
style="margin:5px 15px 0px;"
|
||||
title="Close the Window"
|
||||
title={t('Close the Window')}
|
||||
size={16}
|
||||
onClick={() => {
|
||||
if (props.onClose) props.onClose()
|
||||
@@ -182,7 +190,7 @@ function ConversationCard(props) {
|
||||
<Pin
|
||||
className="gpt-util-icon"
|
||||
style="margin:5px 15px 0px;"
|
||||
title="Pin the Window"
|
||||
title={t('Pin the Window')}
|
||||
size={16}
|
||||
onClick={() => {
|
||||
if (props.onDock) props.onDock()
|
||||
@@ -196,7 +204,7 @@ function ConversationCard(props) {
|
||||
) : (
|
||||
<WindowDesktop
|
||||
className="gpt-util-icon"
|
||||
title="Float the Window"
|
||||
title={t('Float the Window')}
|
||||
size={16}
|
||||
onClick={() => {
|
||||
const position = { x: window.innerWidth / 2 - 300, y: window.innerHeight / 2 - 200 }
|
||||
@@ -216,7 +224,7 @@ function ConversationCard(props) {
|
||||
/>
|
||||
)}
|
||||
<span
|
||||
title="Save Conversation"
|
||||
title={t('Save Conversation')}
|
||||
className="gpt-util-icon"
|
||||
style="margin:15px;"
|
||||
onClick={() => {
|
||||
@@ -254,7 +262,7 @@ function ConversationCard(props) {
|
||||
const newQuestion = new ConversationItemData('question', question + '\n<hr/>')
|
||||
const newAnswer = new ConversationItemData(
|
||||
'answer',
|
||||
'<p class="gpt-loading">Waiting for response...</p>',
|
||||
`<p class="gpt-loading">${t('Waiting for response...')}</p>`,
|
||||
)
|
||||
setConversationItemData([...conversationItemData, newQuestion, newAnswer])
|
||||
setIsReady(false)
|
||||
|
||||
@@ -4,8 +4,10 @@ import { ChevronDownIcon, LinkExternalIcon, XCircleIcon } from '@primer/octicons
|
||||
import CopyButton from '../CopyButton'
|
||||
import PropTypes from 'prop-types'
|
||||
import MarkdownRender from '../MarkdownRender/markdown.jsx'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
export function ConversationItem({ type, content, session, done, port }) {
|
||||
const { t } = useTranslation()
|
||||
const [collapsed, setCollapsed] = useState(false)
|
||||
|
||||
switch (type) {
|
||||
@@ -13,15 +15,23 @@ export function ConversationItem({ type, content, session, done, port }) {
|
||||
return (
|
||||
<div className={type} dir="auto">
|
||||
<div className="gpt-header">
|
||||
<p>You:</p>
|
||||
<p>{t('You:')}</p>
|
||||
<div style="display: flex; gap: 15px;">
|
||||
<CopyButton contentFn={() => content} size={14} />
|
||||
{!collapsed ? (
|
||||
<span title="Collapse" className="gpt-util-icon" onClick={() => setCollapsed(true)}>
|
||||
<span
|
||||
title={t('Collapse')}
|
||||
className="gpt-util-icon"
|
||||
onClick={() => setCollapsed(true)}
|
||||
>
|
||||
<XCircleIcon size={14} />
|
||||
</span>
|
||||
) : (
|
||||
<span title="Expand" className="gpt-util-icon" onClick={() => setCollapsed(false)}>
|
||||
<span
|
||||
title={t('Expand')}
|
||||
className="gpt-util-icon"
|
||||
onClick={() => setCollapsed(false)}
|
||||
>
|
||||
<ChevronDownIcon size={14} />
|
||||
</span>
|
||||
)}
|
||||
@@ -35,7 +45,7 @@ export function ConversationItem({ type, content, session, done, port }) {
|
||||
<div className={type} dir="auto">
|
||||
<div className="gpt-header">
|
||||
<p style="white-space: nowrap;">
|
||||
{session && session.aiName ? `${session.aiName}:` : 'Loading...'}
|
||||
{session && session.aiName ? `${t(session.aiName)}:` : t('Loading...')}
|
||||
</p>
|
||||
<div style="display: flex; gap: 15px; align-items: center; white-space: nowrap;">
|
||||
{!done && (
|
||||
@@ -46,7 +56,7 @@ export function ConversationItem({ type, content, session, done, port }) {
|
||||
port.postMessage({ stop: true })
|
||||
}}
|
||||
>
|
||||
Stop
|
||||
{t('Stop')}
|
||||
</button>
|
||||
)}
|
||||
{done && session && session.conversationId && (
|
||||
@@ -57,7 +67,7 @@ export function ConversationItem({ type, content, session, done, port }) {
|
||||
)}
|
||||
{session && session.conversationId && (
|
||||
<a
|
||||
title="Continue on official website"
|
||||
title={t('Continue on official website')}
|
||||
href={'https://chat.openai.com/chat/' + session.conversationId}
|
||||
target="_blank"
|
||||
rel="nofollow noopener noreferrer"
|
||||
@@ -69,11 +79,19 @@ export function ConversationItem({ type, content, session, done, port }) {
|
||||
)}
|
||||
{session && <CopyButton contentFn={() => content} size={14} />}
|
||||
{!collapsed ? (
|
||||
<span title="Collapse" className="gpt-util-icon" onClick={() => setCollapsed(true)}>
|
||||
<span
|
||||
title={t('Collapse')}
|
||||
className="gpt-util-icon"
|
||||
onClick={() => setCollapsed(true)}
|
||||
>
|
||||
<XCircleIcon size={14} />
|
||||
</span>
|
||||
) : (
|
||||
<span title="Expand" className="gpt-util-icon" onClick={() => setCollapsed(false)}>
|
||||
<span
|
||||
title={t('Expand')}
|
||||
className="gpt-util-icon"
|
||||
onClick={() => setCollapsed(false)}
|
||||
>
|
||||
<ChevronDownIcon size={14} />
|
||||
</span>
|
||||
)}
|
||||
@@ -86,15 +104,23 @@ export function ConversationItem({ type, content, session, done, port }) {
|
||||
return (
|
||||
<div className={type} dir="auto">
|
||||
<div className="gpt-header">
|
||||
<p>Error:</p>
|
||||
<p>{t('Error:')}</p>
|
||||
<div style="display: flex; gap: 15px;">
|
||||
<CopyButton contentFn={() => content} size={14} />
|
||||
{!collapsed ? (
|
||||
<span title="Collapse" className="gpt-util-icon" onClick={() => setCollapsed(true)}>
|
||||
<span
|
||||
title={t('Collapse')}
|
||||
className="gpt-util-icon"
|
||||
onClick={() => setCollapsed(true)}
|
||||
>
|
||||
<XCircleIcon size={14} />
|
||||
</span>
|
||||
) : (
|
||||
<span title="Expand" className="gpt-util-icon" onClick={() => setCollapsed(false)}>
|
||||
<span
|
||||
title={t('Expand')}
|
||||
className="gpt-util-icon"
|
||||
onClick={() => setCollapsed(false)}
|
||||
>
|
||||
<ChevronDownIcon size={14} />
|
||||
</span>
|
||||
)}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState } from 'react'
|
||||
import { CheckIcon, CopyIcon } from '@primer/octicons-react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
CopyButton.propTypes = {
|
||||
contentFn: PropTypes.func.isRequired,
|
||||
@@ -9,6 +10,7 @@ CopyButton.propTypes = {
|
||||
}
|
||||
|
||||
function CopyButton({ className, contentFn, size }) {
|
||||
const { t } = useTranslation()
|
||||
const [copied, setCopied] = useState(false)
|
||||
|
||||
const onClick = () => {
|
||||
@@ -23,7 +25,11 @@ function CopyButton({ className, contentFn, size }) {
|
||||
}
|
||||
|
||||
return (
|
||||
<span title="Copy" className={`gpt-util-icon ${className ? className : ''}`} onClick={onClick}>
|
||||
<span
|
||||
title={t('Copy')}
|
||||
className={`gpt-util-icon ${className ? className : ''}`}
|
||||
onClick={onClick}
|
||||
>
|
||||
{copied ? <CheckIcon size={size} /> : <CopyIcon size={size} />}
|
||||
</span>
|
||||
)
|
||||
|
||||
@@ -5,8 +5,10 @@ import ConversationCard from '../ConversationCard'
|
||||
import { defaultConfig, getUserConfig } from '../../config/index.mjs'
|
||||
import Browser from 'webextension-polyfill'
|
||||
import { getPossibleElementByQuerySelector, endsWithQuestionMark } from '../../utils'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
function DecisionCard(props) {
|
||||
const { t } = useTranslation()
|
||||
const [triggered, setTriggered] = useState(false)
|
||||
const [config, setConfig] = useState(defaultConfig)
|
||||
const [render, setRender] = useState(false)
|
||||
@@ -103,7 +105,7 @@ function DecisionCard(props) {
|
||||
className="gpt-inner manual-btn icon-and-text"
|
||||
onClick={() => setTriggered(true)}
|
||||
>
|
||||
<SearchIcon size="small" /> Ask ChatGPT
|
||||
<SearchIcon size="small" /> {t('Ask ChatGPT')}
|
||||
</p>
|
||||
)
|
||||
case 'questionMark':
|
||||
@@ -118,14 +120,14 @@ function DecisionCard(props) {
|
||||
className="gpt-inner manual-btn icon-and-text"
|
||||
onClick={() => setTriggered(true)}
|
||||
>
|
||||
<SearchIcon size="small" /> Ask ChatGPT
|
||||
<SearchIcon size="small" /> {t('Ask ChatGPT')}
|
||||
</p>
|
||||
)
|
||||
}
|
||||
else
|
||||
return (
|
||||
<p className="gpt-inner icon-and-text">
|
||||
<LightBulbIcon size="small" /> No Input Found
|
||||
<LightBulbIcon size="small" /> {t('No Input Found')}
|
||||
</p>
|
||||
)
|
||||
})()}
|
||||
|
||||
@@ -2,8 +2,10 @@ import PropTypes from 'prop-types'
|
||||
import { memo, useCallback, useState } from 'react'
|
||||
import { ThumbsupIcon, ThumbsdownIcon } from '@primer/octicons-react'
|
||||
import Browser from 'webextension-polyfill'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const FeedbackForChatGPTWeb = (props) => {
|
||||
const { t } = useTranslation()
|
||||
const [action, setAction] = useState(null)
|
||||
|
||||
const clickThumbsUp = useCallback(async () => {
|
||||
@@ -39,7 +41,7 @@ const FeedbackForChatGPTWeb = (props) => {
|
||||
}, [props, action])
|
||||
|
||||
return (
|
||||
<div title="Feedback" className="gpt-feedback">
|
||||
<div title={t('Feedback')} className="gpt-feedback">
|
||||
<span
|
||||
onClick={clickThumbsUp}
|
||||
className={action === 'thumbsUp' ? 'gpt-feedback-selected gpt-util-icon' : 'gpt-util-icon'}
|
||||
|
||||
@@ -7,10 +7,12 @@ import { config as toolsConfig } from '../../content-script/selection-tools'
|
||||
import { getClientPosition, isMobile, setElementPositionInViewport } from '../../utils'
|
||||
import Draggable from 'react-draggable'
|
||||
import { useClampWindowSize } from '../../hooks/use-clamp-window-size'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const logo = Browser.runtime.getURL('logo.png')
|
||||
|
||||
function FloatingToolbar(props) {
|
||||
const { t } = useTranslation()
|
||||
const [selection, setSelection] = useState(props.selection)
|
||||
const [prompt, setPrompt] = useState(props.prompt)
|
||||
const [triggered, setTriggered] = useState(props.triggered)
|
||||
@@ -127,7 +129,7 @@ function FloatingToolbar(props) {
|
||||
cloneElement(toolConfig.icon, {
|
||||
size: 20,
|
||||
className: 'chatgptbox-selection-toolbar-button',
|
||||
title: toolConfig.label,
|
||||
title: t(toolConfig.label),
|
||||
onClick: async () => {
|
||||
const p = getClientPosition(props.container)
|
||||
props.container.style.position = 'fixed'
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { updateRefHeight } from '../../utils'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
export function InputBox({ onSubmit, enabled }) {
|
||||
const { t } = useTranslation()
|
||||
const [value, setValue] = useState('')
|
||||
const inputRef = useRef(null)
|
||||
|
||||
@@ -28,8 +30,8 @@ export function InputBox({ onSubmit, enabled }) {
|
||||
className="interact-input"
|
||||
placeholder={
|
||||
enabled
|
||||
? 'Type your question here\nEnter to send, shift + enter to break line'
|
||||
: 'Wait for the answer to finish and then continue here'
|
||||
? t('Type your question here\nEnter to send, shift + enter to break line')
|
||||
: t('Wait for the answer to finish and then continue here')
|
||||
}
|
||||
value={value}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
|
||||
Reference in New Issue
Block a user