feat: add "Regenerate" in action window

This commit is contained in:
fullex 2025-05-29 19:26:44 +08:00 committed by 亢奋猫
parent d18436578d
commit 75d2eacf7c
8 changed files with 80 additions and 18 deletions

View File

@ -1849,9 +1849,10 @@
"original_show": "Show Original",
"original_hide": "Hide Original",
"original_copy": "Copy Original",
"esc_close": "Esc to Close",
"esc_stop": "Esc to Stop",
"c_copy": "C to Copy"
"esc_close": "Esc: Close",
"esc_stop": "Esc: Stop",
"c_copy": "C: Copy",
"r_regenerate": "R: Regenerate"
}
},
"settings": {

View File

@ -1851,7 +1851,8 @@
"original_copy": "原文をコピー",
"esc_close": "Escで閉じる",
"esc_stop": "Escで停止",
"c_copy": "Cでコピー"
"c_copy": "Cでコピー",
"r_regenerate": "Rで再生成"
}
},
"settings": {

View File

@ -1851,7 +1851,8 @@
"original_copy": "Копировать оригинал",
"esc_close": "Esc - закрыть",
"esc_stop": "Esc - остановить",
"c_copy": "C - копировать"
"c_copy": "C - копировать",
"r_regenerate": "R - перегенерировать"
}
},
"settings": {

View File

@ -1851,7 +1851,8 @@
"original_copy": "复制原文",
"esc_close": "Esc 关闭",
"esc_stop": "Esc 停止",
"c_copy": "C 复制"
"c_copy": "C 复制",
"r_regenerate": "R 重新生成"
}
},
"settings": {

View File

@ -1851,7 +1851,8 @@
"original_copy": "複製原文",
"esc_close": "Esc 關閉",
"esc_stop": "Esc 停止",
"c_copy": "C 複製"
"c_copy": "C 複製",
"r_regenerate": "R 重新生成"
}
},
"settings": {

View File

@ -220,6 +220,12 @@ const ActionGeneral: FC<Props> = React.memo(({ action, scrollToBottom }) => {
}
}
const handleRegenerate = () => {
setContentToCopy('')
setIsLoading(true)
fetchResult()
}
return (
<>
<Container>
@ -250,7 +256,7 @@ const ActionGeneral: FC<Props> = React.memo(({ action, scrollToBottom }) => {
{error && <ErrorMsg>{error}</ErrorMsg>}
</Container>
<FooterPadding />
<WindowFooter loading={isLoading} onPause={handlePause} content={contentToCopy} />
<WindowFooter loading={isLoading} onPause={handlePause} onRegenerate={handleRegenerate} content={contentToCopy} />
</>
)
})
@ -315,7 +321,7 @@ const OriginalContentCopyWrapper = styled.div`
`
const FooterPadding = styled.div`
min-height: 32px;
min-height: 12px;
`
const ErrorMsg = styled.div`

View File

@ -204,7 +204,7 @@ const OriginalContentCopyWrapper = styled.div`
`
const FooterPadding = styled.div`
min-height: 32px;
min-height: 12px;
`
const ErrorMsg = styled.div`

View File

@ -1,5 +1,5 @@
import { LoadingOutlined } from '@ant-design/icons'
import { CircleX, Copy, Pause } from 'lucide-react'
import { CircleX, Copy, Pause, RefreshCw } from 'lucide-react'
import { FC, useEffect, useState } from 'react'
import { useHotkeys } from 'react-hotkeys-hook'
import { useTranslation } from 'react-i18next'
@ -8,14 +8,22 @@ interface FooterProps {
content?: string
loading?: boolean
onPause?: () => void
onRegenerate?: () => void
}
const WindowFooter: FC<FooterProps> = ({ content = '', loading = false, onPause = () => {} }) => {
const WindowFooter: FC<FooterProps> = ({
content = '',
loading = false,
onPause = undefined,
onRegenerate = undefined
}) => {
const { t } = useTranslation()
const [isWindowFocus, setIsWindowFocus] = useState(true)
const [isCopyHovered, setIsCopyHovered] = useState(false)
const [isEscHovered, setIsEscHovered] = useState(false)
const [isRegenerateHovered, setIsRegenerateHovered] = useState(false)
const [isContainerHovered, setIsContainerHovered] = useState(false)
useEffect(() => {
window.addEventListener('focus', handleWindowFocus)
@ -31,6 +39,10 @@ const WindowFooter: FC<FooterProps> = ({ content = '', loading = false, onPause
handleCopy()
})
useHotkeys('r', () => {
handleRegenerate()
})
useHotkeys('esc', () => {
handleEsc()
})
@ -48,8 +60,26 @@ const WindowFooter: FC<FooterProps> = ({ content = '', loading = false, onPause
}
}
const handleRegenerate = () => {
setIsRegenerateHovered(true)
setTimeout(() => {
setIsRegenerateHovered(false)
}, 200)
if (loading && onPause) {
onPause()
}
if (onRegenerate) {
//wait for a little time
setTimeout(() => {
onRegenerate()
}, 200)
}
}
const handleCopy = () => {
if (!content) return
if (!content || loading) return
navigator.clipboard
.writeText(content)
@ -74,7 +104,10 @@ const WindowFooter: FC<FooterProps> = ({ content = '', loading = false, onPause
}
return (
<Container>
<Container
onMouseEnter={() => setIsContainerHovered(true)}
onMouseLeave={() => setIsContainerHovered(false)}
$isHovered={isContainerHovered}>
<OpButtonWrapper>
<OpButton onClick={handleEsc} $isWindowFocus={isWindowFocus} data-hovered={isEscHovered}>
{loading ? (
@ -96,6 +129,12 @@ const WindowFooter: FC<FooterProps> = ({ content = '', loading = false, onPause
</>
)}
</OpButton>
{onRegenerate && (
<OpButton onClick={handleRegenerate} $isWindowFocus={isWindowFocus} data-hovered={isRegenerateHovered}>
<RefreshCw size={14} className="btn-icon" />
{t('selection.action.window.r_regenerate')}
</OpButton>
)}
<OpButton onClick={handleCopy} $isWindowFocus={isWindowFocus && !!content} data-hovered={isCopyHovered}>
<Copy size={14} className="btn-icon" />
{t('selection.action.window.c_copy')}
@ -105,19 +144,28 @@ const WindowFooter: FC<FooterProps> = ({ content = '', loading = false, onPause
)
}
const Container = styled.div`
const Container = styled.div<{ $isHovered: boolean }>`
position: absolute;
bottom: 0;
left: 8px;
right: 8px;
left: 50%;
transform: translateX(-50%);
max-width: 480px;
min-width: min-content;
width: calc(100% - 16px);
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
padding: 5px 0;
padding: 5px 8px;
height: 32px;
backdrop-filter: blur(8px);
border-radius: 8px;
opacity: 0;
transition: all 0.3s ease;
&:hover {
opacity: 1;
}
`
const OpButtonWrapper = styled.div`
@ -144,6 +192,9 @@ const OpButton = styled.div<{ $isWindowFocus: boolean; $isHovered?: boolean }>`
opacity: ${(props) => (props.$isWindowFocus ? 1 : 0.2)};
transition: opacity 0.3s ease;
transition: color 0.2s ease;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
.btn-icon {
color: var(--color-text-secondary);