feat: Enhance audiobook chapter processing UI with a retry failed button, granular cancel options for parsing and generating, and updated localizations.

This commit is contained in:
2026-03-11 14:28:27 +08:00
parent d96089a2aa
commit 22e4930d63
6 changed files with 29 additions and 9 deletions

View File

@@ -67,7 +67,9 @@
"chaptersProgress": "Chapters parsed: {{parsed}} / {{total}}", "chaptersProgress": "Chapters parsed: {{parsed}} / {{total}}",
"chaptersParsing": "{{count}} parsing", "chaptersParsing": "{{count}} parsing",
"chaptersError": "{{count}} error", "chaptersError": "{{count}} error",
"cancelBatch": "✖ Cancel Batch", "cancelParsing": "✖ Cancel Parsing",
"cancelGenerating": "✖ Cancel Generating",
"retryFailed": "Retry Failed",
"cancelledToast": "Cancel signal sent. Running tasks will finish then stop.", "cancelledToast": "Cancel signal sent. Running tasks will finish then stop.",
"characters": { "characters": {

View File

@@ -67,7 +67,9 @@
"chaptersProgress": "章解析:{{parsed}} / {{total}} 章", "chaptersProgress": "章解析:{{parsed}} / {{total}} 章",
"chaptersParsing": "{{count}} 解析中", "chaptersParsing": "{{count}} 解析中",
"chaptersError": "{{count}} エラー", "chaptersError": "{{count}} エラー",
"cancelBatch": "✖ バッチキャンセル", "cancelParsing": "✖ 解析をキャンセル",
"cancelGenerating": "✖ 生成をキャンセル",
"retryFailed": "失敗を再試行",
"cancelledToast": "キャンセルシグナルを送信しました。実行中のタスクは完了後停止します。", "cancelledToast": "キャンセルシグナルを送信しました。実行中のタスクは完了後停止します。",
"characters": { "characters": {

View File

@@ -67,7 +67,9 @@
"chaptersProgress": "챕터 파싱: {{parsed}} / {{total}} 챕터", "chaptersProgress": "챕터 파싱: {{parsed}} / {{total}} 챕터",
"chaptersParsing": "{{count}} 파싱 중", "chaptersParsing": "{{count}} 파싱 중",
"chaptersError": "{{count}} 오류", "chaptersError": "{{count}} 오류",
"cancelBatch": "✖ 일괄 취소", "cancelParsing": "✖ 파싱 취소",
"cancelGenerating": "✖ 생성 취소",
"retryFailed": "실패 재시도",
"cancelledToast": "취소 신호가 전송되었습니다. 실행 중인 작업은 완료 후 중지됩니다.", "cancelledToast": "취소 신호가 전송되었습니다. 실행 중인 작업은 완료 후 중지됩니다.",
"characters": { "characters": {

View File

@@ -67,7 +67,9 @@
"chaptersProgress": "章节解析:{{parsed}} / {{total}} 章", "chaptersProgress": "章节解析:{{parsed}} / {{total}} 章",
"chaptersParsing": "{{count}} 解析中", "chaptersParsing": "{{count}} 解析中",
"chaptersError": "{{count}} 出错", "chaptersError": "{{count}} 出错",
"cancelBatch": "✖ 取消批量操作", "cancelParsing": "✖ 取消解析",
"cancelGenerating": "✖ 取消生成",
"retryFailed": "重试失败",
"cancelledToast": "已发送取消信号,当前正在运行的任务会完成后停止", "cancelledToast": "已发送取消信号,当前正在运行的任务会完成后停止",
"characters": { "characters": {

View File

@@ -67,7 +67,9 @@
"chaptersProgress": "章節解析:{{parsed}} / {{total}} 章", "chaptersProgress": "章節解析:{{parsed}} / {{total}} 章",
"chaptersParsing": "{{count}} 解析中", "chaptersParsing": "{{count}} 解析中",
"chaptersError": "{{count}} 出錯", "chaptersError": "{{count}} 出錯",
"cancelBatch": "✖ 取消批量操作", "cancelParsing": "✖ 取消解析",
"cancelGenerating": "✖ 取消生成",
"retryFailed": "重試失敗",
"cancelledToast": "已發送取消信號,當前正在執行的任務會完成後停止", "cancelledToast": "已發送取消信號,當前正在執行的任務會完成後停止",
"characters": { "characters": {

View File

@@ -689,14 +689,19 @@ function ProjectCard({ project, onRefresh }: { project: AudiobookProject; onRefr
<div className="space-y-2"> <div className="space-y-2">
{(chaptersParsing > 0 || chaptersError > 0 || chaptersParsed < chaptersTotal) && ( {(chaptersParsing > 0 || chaptersError > 0 || chaptersParsed < chaptersTotal) && (
<div className="space-y-1"> <div className="space-y-1">
<div className="text-xs text-muted-foreground flex items-center gap-1"> <div className="text-xs text-muted-foreground flex items-center gap-1 flex-wrap">
<span>📝</span> <span>📝</span>
<span>{t('projectCard.chaptersProgress', { parsed: chaptersParsed, total: chaptersTotal })}</span> <span>{t('projectCard.chaptersProgress', { parsed: chaptersParsed, total: chaptersTotal })}</span>
{chaptersParsing > 0 && ( {chaptersParsing > 0 && (
<span className="text-primary">({t('projectCard.chaptersParsing', { count: chaptersParsing })})</span> <span className="text-primary">({t('projectCard.chaptersParsing', { count: chaptersParsing })})</span>
)} )}
{chaptersError > 0 && ( {chaptersError > 0 && (
<span className="text-destructive">({t('projectCard.chaptersError', { count: chaptersError })})</span> <>
<span className="text-destructive">({t('projectCard.chaptersError', { count: chaptersError })})</span>
<Button size="sm" variant="outline" className="h-5 text-[10px] px-1.5 text-destructive border-destructive/40" onClick={handleParseAll}>
{t('projectCard.retryFailed')}
</Button>
</>
)} )}
</div> </div>
<Progress value={chapterProgress} /> <Progress value={chapterProgress} />
@@ -711,9 +716,14 @@ function ProjectCard({ project, onRefresh }: { project: AudiobookProject; onRefr
<Progress value={progress} /> <Progress value={progress} />
</div> </div>
)} )}
{(chaptersParsing > 0 || hasGenerating) && ( {chaptersParsing > 0 && (
<Button size="sm" variant="outline" className="h-6 text-xs px-2 text-destructive border-destructive/40" onClick={handleCancelBatch}> <Button size="sm" variant="outline" className="h-6 text-xs px-2 text-destructive border-destructive/40" onClick={handleCancelBatch}>
{t('projectCard.cancelBatch')} {t('projectCard.cancelParsing')}
</Button>
)}
{!chaptersParsing && hasGenerating && (
<Button size="sm" variant="outline" className="h-6 text-xs px-2 text-destructive border-destructive/40" onClick={handleCancelBatch}>
{t('projectCard.cancelGenerating')}
</Button> </Button>
)} )}
</div> </div>