完善一些提示性内容
This commit is contained in:
parent
6eb2fc18b3
commit
5f5255830f
59
src/App.vue
59
src/App.vue
@ -67,17 +67,20 @@ const translationConfig = ref<TranslationConfig>({
|
|||||||
apiBase: localStorage.getItem('llm.apiBase') ?? 'https://open.bigmodel.cn/api/paas/v4',
|
apiBase: localStorage.getItem('llm.apiBase') ?? 'https://open.bigmodel.cn/api/paas/v4',
|
||||||
apiKey: localStorage.getItem('llm.apiKey') ?? '',
|
apiKey: localStorage.getItem('llm.apiKey') ?? '',
|
||||||
model: localStorage.getItem('llm.model') ?? 'GLM-4.7-Flash',
|
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'),
|
contextSize: Number(localStorage.getItem('llm.contextSize') ?? '5'),
|
||||||
})
|
})
|
||||||
const pending = ref(false)
|
const pending = ref(false)
|
||||||
const feedback = ref('')
|
const feedback = ref('')
|
||||||
|
const feedbackTone = ref<'normal' | 'error'>('normal')
|
||||||
const showAdvanced = ref(false)
|
const showAdvanced = ref(false)
|
||||||
const isDragging = ref(false)
|
const isDragging = ref(false)
|
||||||
const showApiKeyDialog = ref(false)
|
const showApiKeyDialog = ref(false)
|
||||||
const apiKeyUrlCopied = ref(false)
|
const apiKeyUrlCopied = ref(false)
|
||||||
|
const authorUrlCopied = ref(false)
|
||||||
let unlistenMenuAction: UnlistenFn | null = null
|
let unlistenMenuAction: UnlistenFn | null = null
|
||||||
let dragDropUnlistenFn: UnlistenFn | null = null
|
let dragDropUnlistenFn: UnlistenFn | null = null
|
||||||
|
let authorCopyTimer: ReturnType<typeof setTimeout> | null = null
|
||||||
|
|
||||||
const selectedTask = computed(() => taskStore.selectedTask)
|
const selectedTask = computed(() => taskStore.selectedTask)
|
||||||
const hasTranslationKey = computed(() => translationConfig.value.apiKey.trim().length > 0)
|
const hasTranslationKey = computed(() => translationConfig.value.apiKey.trim().length > 0)
|
||||||
@ -98,10 +101,29 @@ onUnmounted(() => {
|
|||||||
dragDropUnlistenFn()
|
dragDropUnlistenFn()
|
||||||
dragDropUnlistenFn = null
|
dragDropUnlistenFn = null
|
||||||
}
|
}
|
||||||
|
if (authorCopyTimer) {
|
||||||
|
clearTimeout(authorCopyTimer)
|
||||||
|
authorCopyTimer = null
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(locale, (newLocale) => {
|
watch(locale, (newLocale) => {
|
||||||
localStorage.setItem('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() {
|
function persistTranslationConfig() {
|
||||||
@ -115,9 +137,15 @@ function persistTranslationConfig() {
|
|||||||
function resetModelPaths() {
|
function resetModelPaths() {
|
||||||
whisperModelPath.value = defaultModelPaths.value?.whisperModelPath ?? ''
|
whisperModelPath.value = defaultModelPaths.value?.whisperModelPath ?? ''
|
||||||
vadModelPath.value = defaultModelPaths.value?.vadModelPath ?? ''
|
vadModelPath.value = defaultModelPaths.value?.vadModelPath ?? ''
|
||||||
|
feedbackTone.value = 'normal'
|
||||||
feedback.value = t('app.feedback.restoredDefaults')
|
feedback.value = t('app.feedback.restoredDefaults')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showMissingApiKeyFeedback() {
|
||||||
|
feedbackTone.value = 'error'
|
||||||
|
feedback.value = t('app.feedback.noApiKey')
|
||||||
|
}
|
||||||
|
|
||||||
function freeApiKeyHint() {
|
function freeApiKeyHint() {
|
||||||
if (locale.value === 'zh-CN') {
|
if (locale.value === 'zh-CN') {
|
||||||
return '开始翻译任务前需要先填写 LLM API Key。你可以选择下面任意一种方式。'
|
return '开始翻译任务前需要先填写 LLM API Key。你可以选择下面任意一种方式。'
|
||||||
@ -127,7 +155,7 @@ function freeApiKeyHint() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function openApiKeyDialog() {
|
function openApiKeyDialog() {
|
||||||
feedback.value = t('app.feedback.noApiKey')
|
showMissingApiKeyFeedback()
|
||||||
apiKeyUrlCopied.value = false
|
apiKeyUrlCopied.value = false
|
||||||
showApiKeyDialog.value = true
|
showApiKeyDialog.value = true
|
||||||
}
|
}
|
||||||
@ -137,6 +165,18 @@ async function copyFreeApiKeyUrl() {
|
|||||||
apiKeyUrlCopied.value = true
|
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() {
|
function acknowledgeApiKeyDialog() {
|
||||||
showApiKeyDialog.value = false
|
showApiKeyDialog.value = false
|
||||||
outputMode.value = 'source'
|
outputMode.value = 'source'
|
||||||
@ -202,6 +242,7 @@ async function submitFiles(filePaths: string[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pending.value = true
|
pending.value = true
|
||||||
|
feedbackTone.value = 'normal'
|
||||||
feedback.value = ''
|
feedback.value = ''
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -227,6 +268,7 @@ async function submitFiles(filePaths: string[]) {
|
|||||||
|
|
||||||
async function handlePickFiles() {
|
async function handlePickFiles() {
|
||||||
try {
|
try {
|
||||||
|
feedbackTone.value = 'normal'
|
||||||
feedback.value = ''
|
feedback.value = ''
|
||||||
persistTranslationConfig()
|
persistTranslationConfig()
|
||||||
const selected = await open({
|
const selected = await open({
|
||||||
@ -336,11 +378,13 @@ async function handleExport(format: 'srt' | 'vtt' | 'ass') {
|
|||||||
async function handleRetryTranslate(taskId: string) {
|
async function handleRetryTranslate(taskId: string) {
|
||||||
persistTranslationConfig()
|
persistTranslationConfig()
|
||||||
if (!translationConfig.value.apiKey.trim()) {
|
if (!translationConfig.value.apiKey.trim()) {
|
||||||
|
feedbackTone.value = 'error'
|
||||||
feedback.value = t('app.feedback.noApiKey')
|
feedback.value = t('app.feedback.noApiKey')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await taskStore.retryTranslation(taskId, translationConfig.value)
|
await taskStore.retryTranslation(taskId, translationConfig.value)
|
||||||
|
feedbackTone.value = 'normal'
|
||||||
feedback.value = t('app.feedback.translationStarted')
|
feedback.value = t('app.feedback.translationStarted')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
feedback.value = error instanceof Error ? error.message : t('app.feedback.translationFailed')
|
feedback.value = error instanceof Error ? error.message : t('app.feedback.translationFailed')
|
||||||
@ -388,8 +432,8 @@ async function handleTranslateFromEditor() {
|
|||||||
<span>
|
<span>
|
||||||
{{ locale === 'zh-CN' ? '推荐免费的' : 'Recommended free option:' }}
|
{{ locale === 'zh-CN' ? '推荐免费的' : 'Recommended free option:' }}
|
||||||
<button class="api-key-link" type="button" @click="copyFreeApiKeyUrl">GLM-4.7-Flash</button>
|
<button class="api-key-link" type="button" @click="copyFreeApiKeyUrl">GLM-4.7-Flash</button>
|
||||||
<span v-if="apiKeyUrlCopied" class="copy-hint">
|
<span class="copy-hint" :class="{ copied: apiKeyUrlCopied }">
|
||||||
{{ locale === 'zh-CN' ? '已复制链接' : 'Link copied' }}
|
{{ apiKeyUrlCopied ? (locale === 'zh-CN' ? '已复制链接' : 'Link copied') : (locale === 'zh-CN' ? '点击复制申请链接' : 'click to copy link') }}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
@ -399,7 +443,7 @@ async function handleTranslateFromEditor() {
|
|||||||
</div>
|
</div>
|
||||||
<div class="dialog-actions">
|
<div class="dialog-actions">
|
||||||
<button class="button" type="button" @click="acknowledgeApiKeyDialog">
|
<button class="button" type="button" @click="acknowledgeApiKeyDialog">
|
||||||
{{ locale === 'zh-CN' ? '明白了' : 'Got it' }}
|
{{ locale === 'zh-CN' ? '明白了,先转原文' : 'Got it, use source mode' }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@ -410,7 +454,8 @@ async function handleTranslateFromEditor() {
|
|||||||
<div class="toolbar-title">
|
<div class="toolbar-title">
|
||||||
<strong>CrossSubtitle</strong>
|
<strong>CrossSubtitle</strong>
|
||||||
<span class="credit-line">
|
<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>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="toolbar-actions">
|
<div class="toolbar-actions">
|
||||||
@ -460,7 +505,7 @@ async function handleTranslateFromEditor() {
|
|||||||
</div>
|
</div>
|
||||||
</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>
|
||||||
|
|
||||||
<div v-if="showAdvanced" class="advanced-shell">
|
<div v-if="showAdvanced" class="advanced-shell">
|
||||||
|
|||||||
@ -157,17 +157,27 @@ textarea {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.credit-line a {
|
.credit-line a,
|
||||||
|
.author-link {
|
||||||
color: var(--c-text);
|
color: var(--c-text);
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
cursor: pointer;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
border-bottom: 1px solid transparent;
|
border-bottom: 1px solid transparent;
|
||||||
transition: border-color var(--transition);
|
transition: border-color var(--transition);
|
||||||
}
|
}
|
||||||
|
|
||||||
.credit-line a:hover {
|
.credit-line a:hover,
|
||||||
|
.author-link:hover {
|
||||||
border-bottom-color: var(--c-text);
|
border-bottom-color: var(--c-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.author-copy-hint {
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.workspace-toolbar,
|
.workspace-toolbar,
|
||||||
.advanced-shell {
|
.advanced-shell {
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
@ -332,6 +342,11 @@ textarea {
|
|||||||
margin: 12px 0 0;
|
margin: 12px 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status-text.error {
|
||||||
|
color: var(--c-error);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
.content-grid {
|
.content-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 300px minmax(0, 1fr);
|
grid-template-columns: 300px minmax(0, 1fr);
|
||||||
@ -950,6 +965,8 @@ textarea {
|
|||||||
|
|
||||||
.api-key-dialog {
|
.api-key-dialog {
|
||||||
width: min(520px, 100%);
|
width: min(520px, 100%);
|
||||||
|
max-height: calc(100vh - 40px);
|
||||||
|
overflow: auto;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
border: 1px solid var(--c-border);
|
border: 1px solid var(--c-border);
|
||||||
border-radius: var(--radius-lg);
|
border-radius: var(--radius-lg);
|
||||||
@ -1109,10 +1126,14 @@ textarea {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.copy-hint {
|
.copy-hint {
|
||||||
color: var(--c-success);
|
color: var(--c-text-tertiary);
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.copy-hint.copied {
|
||||||
|
color: var(--c-success);
|
||||||
|
}
|
||||||
|
|
||||||
.dialog-actions {
|
.dialog-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user