Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/renderer/i18n/locales/en-us.json
Original file line number Diff line number Diff line change
Expand Up @@ -4434,7 +4434,7 @@
},
"generate_failed": "Failed to generate image",
"generated_image": "Generated Image",
"generating": "Generating...",
"generating": "Drawing in progress. Please do not leave this page.",
"go_to_settings": "Go to Settings",
"guidance_scale": "Guidance Scale",
"guidance_scale_tip": "Classifier Free Guidance ({{min}}-{{max}}). How close you want the model to stick to your prompt when looking for a related image to show you",
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/i18n/locales/zh-cn.json
Original file line number Diff line number Diff line change
Expand Up @@ -4434,7 +4434,7 @@
},
"generate_failed": "生成图像失败",
"generated_image": "生成图片",
"generating": "生成中...",
"generating": "绘图进行中,请不要离开页面",
"go_to_settings": "去设置",
"guidance_scale": "引导比例",
"guidance_scale_tip": "无分类器指导 ({{min}}-{{max}})。控制模型在寻找相关图像时对提示词的遵循程度",
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/i18n/locales/zh-tw.json
Original file line number Diff line number Diff line change
Expand Up @@ -4434,7 +4434,7 @@
},
"generate_failed": "無法生成圖片",
"generated_image": "產生的圖片",
"generating": "生成中...",
"generating": "繪圖進行中,請不要離開頁面",
"go_to_settings": "前往設定",
"guidance_scale": "引導比例",
"guidance_scale_tip": "無分類器指導 ({{min}}-{{max}})。控制模型在尋找相關影像時對提示詞的遵循程度",
Expand Down
34 changes: 16 additions & 18 deletions src/renderer/pages/paintings/components/Artboard.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Button, Tooltip } from '@cherrystudio/ui'
import ImageViewer from '@renderer/components/ImageViewer'
import { ImageDown, ImageUp, RefreshCcw, RotateCcwSquare, RotateCwSquare, ZoomIn, ZoomOut } from 'lucide-react'
import { motion } from 'motion/react'
import {
type FC,
type PointerEvent,
Expand Down Expand Up @@ -40,27 +39,26 @@ export interface ArtboardProps {
loadText?: ReactNode
}

const LoadingStateCard: FC<{ text: ReactNode; onCancel: () => void; cancelLabel: string }> = ({
const InlineLoadingState: FC<{ text: ReactNode; onCancel: () => void; cancelLabel: string }> = ({
text,
onCancel,
cancelLabel
}) => {
Comment thread
kangfenmao marked this conversation as resolved.
const progressLabel = typeof text === 'string' ? text : undefined

return (
<div className="flex min-w-56 flex-col items-center gap-4 rounded-[18px] border border-border-subtle bg-card/96 px-10 py-10 shadow-2xl backdrop-blur-sm">
<div className="relative h-12 w-12">
<motion.div
className="absolute inset-0 rounded-full border-2 border-border"
animate={{ rotate: 360 }}
transition={{ duration: 2, repeat: Number.POSITIVE_INFINITY, ease: 'linear' }}
/>
<motion.div
className="absolute inset-1 rounded-full border-2 border-primary border-r-transparent border-b-transparent"
animate={{ rotate: -360 }}
transition={{ duration: 1.5, repeat: Number.POSITIVE_INFINITY, ease: 'linear' }}
/>
<div
className="flex w-full max-w-90 flex-col items-center gap-3 rounded-md bg-card px-5 py-4 text-card-foreground"
role="status"
aria-live="polite">
<div className="text-center font-medium text-[13px] text-foreground leading-5">{text}</div>
<div
className="relative h-1.5 w-full overflow-hidden rounded-full bg-muted"
role="progressbar"
aria-label={progressLabel}>
<span className="animation-migration-backup-progress-indeterminate absolute inset-y-0 left-0 w-1/3 min-w-20 rounded-full bg-linear-to-r from-primary/0 via-primary to-primary/0" />
Comment thread
kangfenmao marked this conversation as resolved.
</div>
Comment thread
kangfenmao marked this conversation as resolved.
<div className="text-center font-medium text-[13px] text-foreground/85">{text}</div>
<Button variant="outline" size="sm" onClick={onCancel} className="mt-1 min-w-20">
<Button type="button" variant="outline" size="sm" onClick={onCancel} className="min-w-20">
{cancelLabel}
</Button>
</div>
Expand Down Expand Up @@ -253,8 +251,8 @@ const Artboard: FC<ArtboardProps> = ({ painting, isLoading, onCancel, imageCover
) : null}

{isLoading && (
<div className="-translate-x-1/2 -translate-y-1/2 absolute top-1/2 left-1/2 z-30">
<LoadingStateCard text={loadingText} onCancel={onCancel} cancelLabel={t('common.cancel')} />
<div className="-translate-y-1/2 absolute inset-x-4 top-1/2 z-30 flex justify-center">
<InlineLoadingState text={loadingText} onCancel={onCancel} cancelLabel={t('common.cancel')} />
</div>
)}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import { beforeAll, describe, expect, it, vi } from 'vitest'
import type { PaintingData } from '../../model/types/paintingData'

vi.mock('react-i18next', () => ({
useTranslation: () => ({ t: (key: string) => key })
useTranslation: () => ({
t: (key: string) => (key === 'paintings.generating' ? '绘图进行中,请不要离开页面' : key)
})
}))

vi.mock('@renderer/utils/image', () => ({
Expand Down Expand Up @@ -53,6 +55,31 @@ describe('Artboard', () => {
HTMLElement.prototype.hasPointerCapture ??= vi.fn(() => true)
})

it('renders an inline loading bar and cancel action while generating', () => {
const onCancel = vi.fn()

render(<Artboard painting={makePainting()} isLoading={true} onCancel={onCancel} />)

const loadingStatus = screen.getByRole('status')
expect(loadingStatus).toHaveTextContent('绘图进行中,请不要离开页面')
expect(loadingStatus.className).not.toContain('border')

Comment thread
kangfenmao marked this conversation as resolved.
const loadingBar = screen.getByRole('progressbar', { name: '绘图进行中,请不要离开页面' })
expect(loadingBar).toBeInTheDocument()
expect(loadingBar.firstElementChild).toHaveClass('animation-migration-backup-progress-indeterminate')
expect(loadingBar.firstElementChild).toHaveClass('bg-linear-to-r')

Comment thread
kangfenmao marked this conversation as resolved.
Comment thread
kangfenmao marked this conversation as resolved.
fireEvent.click(screen.getByRole('button', { name: 'common.cancel' }))

expect(onCancel).toHaveBeenCalledTimes(1)
})

it('does not render the loading bar when idle', () => {
render(<Artboard painting={makePainting()} isLoading={false} onCancel={vi.fn()} />)

expect(screen.queryByRole('progressbar')).not.toBeInTheDocument()
})

it('resets image transform when switching generated images', () => {
render(<Artboard painting={makePainting()} isLoading={false} onCancel={vi.fn()} />)

Expand Down
Loading