完善一些提示性内容

This commit is contained in:
kura 2026-05-02 16:48:32 +08:00
parent 6eb2fc18b3
commit 5f5255830f
2 changed files with 76 additions and 10 deletions

View File

@ -67,17 +67,20 @@ const translationConfig = ref<TranslationConfig>({
apiBase: localStorage.getItem('llm.apiBase') ?? 'https://open.bigmodel.cn/api/paas/v4',
apiKey: localStorage.getItem('llm.apiKey') ?? '',
model: localStorage.getItem('llm.model') ?? 'GLM-4.7-Flash',
batchSize: Number(localStorage.getItem('llm.batchSize') ?? '60'),
batchSize: Number(localStorage.getItem('llm.batchSize') ?? '12'),
contextSize: Number(localStorage.getItem('llm.contextSize') ?? '5'),
})
const pending = ref(false)
const feedback = ref('')
const feedbackTone = ref<'normal' | 'error'>('normal')
const showAdvanced = ref(false)
const isDragging = ref(false)
const showApiKeyDialog = ref(false)
const apiKeyUrlCopied = ref(false)
const authorUrlCopied = ref(false)
let unlistenMenuAction: UnlistenFn | null = null
let dragDropUnlistenFn: UnlistenFn | null = null
let authorCopyTimer: ReturnType<typeof setTimeout> | null = null
const selectedTask = computed(() => taskStore.selectedTask)
const hasTranslationKey = computed(() => translationConfig.value.apiKey.trim().length > 0)
@ -98,10 +101,29 @@ onUnmounted(() => {
dragDropUnlistenFn()
dragDropUnlistenFn = null
}
if (authorCopyTimer) {
clearTimeout(authorCopyTimer)
authorCopyTimer = null
}
})
watch(locale, (newLocale) => {
localStorage.setItem('locale', newLocale)
if (outputMode.value === 'translate' && !hasTranslationKey.value) {
showMissingApiKeyFeedback()
}
})
watch([outputMode, hasTranslationKey], ([mode, hasKey]) => {
if (mode === 'translate' && !hasKey) {
showMissingApiKeyFeedback()
return
}
if (feedbackTone.value === 'error' && feedback.value === t('app.feedback.noApiKey')) {
feedbackTone.value = 'normal'
feedback.value = ''
}
})
function persistTranslationConfig() {
@ -115,9 +137,15 @@ function persistTranslationConfig() {
function resetModelPaths() {
whisperModelPath.value = defaultModelPaths.value?.whisperModelPath ?? ''
vadModelPath.value = defaultModelPaths.value?.vadModelPath ?? ''
feedbackTone.value = 'normal'
feedback.value = t('app.feedback.restoredDefaults')
}
function showMissingApiKeyFeedback() {
feedbackTone.value = 'error'
feedback.value = t('app.feedback.noApiKey')
}
function freeApiKeyHint() {
if (locale.value === 'zh-CN') {
return '开始翻译任务前需要先填写 LLM API Key。你可以选择下面任意一种方式。'
@ -127,7 +155,7 @@ function freeApiKeyHint() {
}
function openApiKeyDialog() {
feedback.value = t('app.feedback.noApiKey')
showMissingApiKeyFeedback()
apiKeyUrlCopied.value = false
showApiKeyDialog.value = true
}
@ -137,6 +165,18 @@ async function copyFreeApiKeyUrl() {
apiKeyUrlCopied.value = true
}
async function copyAuthorUrl() {
await navigator.clipboard.writeText('https://kuraa.cc')
authorUrlCopied.value = true
if (authorCopyTimer) {
clearTimeout(authorCopyTimer)
}
authorCopyTimer = setTimeout(() => {
authorUrlCopied.value = false
authorCopyTimer = null
}, 1400)
}
function acknowledgeApiKeyDialog() {
showApiKeyDialog.value = false
outputMode.value = 'source'
@ -202,6 +242,7 @@ async function submitFiles(filePaths: string[]) {
}
pending.value = true
feedbackTone.value = 'normal'
feedback.value = ''
try {
@ -227,6 +268,7 @@ async function submitFiles(filePaths: string[]) {
async function handlePickFiles() {
try {
feedbackTone.value = 'normal'
feedback.value = ''
persistTranslationConfig()
const selected = await open({
@ -336,11 +378,13 @@ async function handleExport(format: 'srt' | 'vtt' | 'ass') {
async function handleRetryTranslate(taskId: string) {
persistTranslationConfig()
if (!translationConfig.value.apiKey.trim()) {
feedbackTone.value = 'error'
feedback.value = t('app.feedback.noApiKey')
return
}
try {
await taskStore.retryTranslation(taskId, translationConfig.value)
feedbackTone.value = 'normal'
feedback.value = t('app.feedback.translationStarted')
} catch (error) {
feedback.value = error instanceof Error ? error.message : t('app.feedback.translationFailed')
@ -388,8 +432,8 @@ async function handleTranslateFromEditor() {
<span>
{{ locale === 'zh-CN' ? '推荐免费的' : 'Recommended free option:' }}
<button class="api-key-link" type="button" @click="copyFreeApiKeyUrl">GLM-4.7-Flash</button>
<span v-if="apiKeyUrlCopied" class="copy-hint">
{{ locale === 'zh-CN' ? '已复制链接' : 'Link copied' }}
<span class="copy-hint" :class="{ copied: apiKeyUrlCopied }">
{{ apiKeyUrlCopied ? (locale === 'zh-CN' ? '已复制链接' : 'Link copied') : (locale === 'zh-CN' ? '点击复制申请链接' : 'click to copy link') }}
</span>
</span>
</li>
@ -399,7 +443,7 @@ async function handleTranslateFromEditor() {
</div>
<div class="dialog-actions">
<button class="button" type="button" @click="acknowledgeApiKeyDialog">
{{ locale === 'zh-CN' ? '明白了' : 'Got it' }}
{{ locale === 'zh-CN' ? '明白了,先转原文' : 'Got it, use source mode' }}
</button>
</div>
</section>
@ -410,7 +454,8 @@ async function handleTranslateFromEditor() {
<div class="toolbar-title">
<strong>CrossSubtitle</strong>
<span class="credit-line">
by <a href="https://kuraa.cc" target="_blank" rel="noreferrer">kuraa</a>
by <button class="author-link" type="button" @click="copyAuthorUrl">kuraa</button>
<span v-if="authorUrlCopied" class="author-copy-hint">🎉</span>
</span>
</div>
<div class="toolbar-actions">
@ -460,7 +505,7 @@ async function handleTranslateFromEditor() {
</div>
</div>
<p v-if="feedback" class="feedback status-text">{{ feedback }}</p>
<p v-if="feedback" class="feedback status-text" :class="{ error: feedbackTone === 'error' }">{{ feedback }}</p>
</div>
<div v-if="showAdvanced" class="advanced-shell">

View File

@ -157,17 +157,27 @@ textarea {
align-items: center;
}
.credit-line a {
.credit-line a,
.author-link {
color: var(--c-text);
background: transparent;
border: 0;
padding: 0;
cursor: pointer;
text-decoration: none;
border-bottom: 1px solid transparent;
transition: border-color var(--transition);
}
.credit-line a:hover {
.credit-line a:hover,
.author-link:hover {
border-bottom-color: var(--c-text);
}
.author-copy-hint {
line-height: 1;
}
.workspace-toolbar,
.advanced-shell {
margin-top: 16px;
@ -332,6 +342,11 @@ textarea {
margin: 12px 0 0;
}
.status-text.error {
color: var(--c-error);
font-weight: 500;
}
.content-grid {
display: grid;
grid-template-columns: 300px minmax(0, 1fr);
@ -950,6 +965,8 @@ textarea {
.api-key-dialog {
width: min(520px, 100%);
max-height: calc(100vh - 40px);
overflow: auto;
padding: 20px;
border: 1px solid var(--c-border);
border-radius: var(--radius-lg);
@ -1109,10 +1126,14 @@ textarea {
}
.copy-hint {
color: var(--c-success);
color: var(--c-text-tertiary);
font-size: 12px;
}
.copy-hint.copied {
color: var(--c-success);
}
.dialog-actions {
display: flex;
justify-content: flex-end;