From 7d5e789bb0a58ab4c4a5f557bc49bed086b42a6b Mon Sep 17 00:00:00 2001 From: AdingApkgg Date: Sat, 27 Dec 2025 06:54:54 +0800 Subject: [PATCH 1/6] Add service worker update notification and countdown feature - Implemented a toast notification to inform users of available service worker updates, enhancing user awareness of new versions. - Added a countdown timer for automatic updates, allowing users to manually trigger the update if desired. - Refactored service worker update handling in main.ts to integrate the new notification system, improving user experience during updates. --- src/api/search.ts | 26 +++++++++---- src/components/VndbPanel.vue | 1 + src/main.ts | 74 +++++++++++++++++++++++++++++++++++- 3 files changed, 91 insertions(+), 10 deletions(-) diff --git a/src/api/search.ts b/src/api/search.ts index feb2633..e980416 100644 --- a/src/api/search.ts +++ b/src/api/search.ts @@ -69,7 +69,9 @@ export interface VideoParseResult { * @param vndbId VNDB ID (如 "v12345") */ export async function fetchGameVideoUrl(vndbId: string): Promise { - if (!vndbId) return null + if (!vndbId) { + return null + } try { const response = await fetch(getVideoParseApiUrl(), { @@ -790,15 +792,14 @@ const COMBINED_PROMPT = `你是一名专业的视觉小说(Galgame/AVG)本 - 台词:保留情感色彩和语气,注意口语化 【输出格式】 -严格按以下格式输出,使用相同的分隔符: -===SECTION=== +严格按照输入的相同格式输出,使用 ===SECTION=== 分隔三部分: 翻译后的简介 ===SECTION=== 翻译后的标签(每行一个,与输入行数对应) ===SECTION=== 翻译后的台词(每行一条,与输入行数对应) -仅输出翻译结果,无需任何说明` +注意:输出不要以 ===SECTION=== 开头,直接输出翻译内容。仅输出翻译结果,无需任何说明。` /** * AI 翻译文本 @@ -962,7 +963,9 @@ export async function translateAllContent( }) if (!response.ok) { - if (attempt === maxRetries) return result + if (attempt === maxRetries) { + return result + } await new Promise((r) => setTimeout(r, 1000 * (attempt + 1))) continue } @@ -971,8 +974,13 @@ export async function translateAllContent( const content = data.choices?.[0]?.message?.content?.trim() if (content) { - // 解析返回结果 - 保留空字符串以维持索引对应关系 - const parts = content.split(/===SECTION===/).map((s: string) => s.trim()) + // 解析返回结果 + let parts = content.split(/===SECTION===/).map((s: string) => s.trim()) + + // 如果 AI 以 ===SECTION=== 开头,第一个元素会是空字符串,需要过滤掉 + if (parts[0] === '') { + parts = parts.slice(1) + } // 索引 0 = 描述, 索引 1 = 标签, 索引 2 = 名言 if (parts[0] && descText) { @@ -997,7 +1005,9 @@ export async function translateAllContent( return result } catch { - if (attempt === maxRetries) return result + if (attempt === maxRetries) { + return result + } await new Promise((r) => setTimeout(r, 1000 * (attempt + 1))) } } diff --git a/src/components/VndbPanel.vue b/src/components/VndbPanel.vue index d9e7f55..13b4248 100644 --- a/src/components/VndbPanel.vue +++ b/src/components/VndbPanel.vue @@ -623,6 +623,7 @@ const screenshotsReady = ref(false) // PV 视频状态 const pvVideoUrl = ref(null) const isPvLoading = ref(false) +// eslint-disable-next-line no-undef const pvVideoRef = ref(null) // 视频加载完成后暂停在第一帧 diff --git a/src/main.ts b/src/main.ts index f8d991c..04db4a4 100644 --- a/src/main.ts +++ b/src/main.ts @@ -59,6 +59,72 @@ app.mount('#app') // Service Worker 注册与更新 // ============================================ +// 显示更新提示 +function showUpdateToast(onUpdate: () => void) { + // 创建 toast 元素 + const toast = document.createElement('div') + toast.id = 'sw-update-toast' + toast.innerHTML = ` +
+ 🎉 发现新版本,5 秒后自动更新 + +
+ + ` + document.body.appendChild(toast) + + // 倒计时 + let countdown = 5 + const countdownEl = document.getElementById('sw-countdown') + const interval = setInterval(() => { + countdown-- + if (countdownEl) { + countdownEl.textContent = String(countdown) + } + if (countdown <= 0) { + clearInterval(interval) + onUpdate() + } + }, 1000) + + // 立即更新按钮 + document.getElementById('sw-update-now')?.addEventListener('click', () => { + clearInterval(interval) + onUpdate() + }) +} + if ('serviceWorker' in navigator) { window.addEventListener('load', async () => { try { @@ -75,8 +141,12 @@ if ('serviceWorker' in navigator) { worker.addEventListener('statechange', () => { // 新 SW 安装完成且有旧 SW 控制页面 = 有更新 if (worker.state === 'installed' && navigator.serviceWorker.controller) { - console.log('[SW] Update available, activating...') - worker.postMessage({ type: 'SKIP_WAITING' }) + console.log('[SW] Update available') + // 显示更新提示,5 秒后自动更新 + showUpdateToast(() => { + console.log('[SW] Activating update...') + worker.postMessage({ type: 'SKIP_WAITING' }) + }) } }) }) From ccd87d06be667cd45cc0a7e8fc9b195305b6da2d Mon Sep 17 00:00:00 2001 From: AdingApkgg Date: Sat, 27 Dec 2025 07:00:22 +0800 Subject: [PATCH 2/6] Enhance image loading and refactor screenshot handling in VndbPanel.vue - Added `referrerpolicy="no-referrer"` to image elements to improve privacy during image loading. - Removed unused `screenshotsReady` state and related logic, simplifying the component's state management. - Updated class names for consistency and improved styling of screenshot buttons. - Cleaned up comments for better clarity regarding screenshot loading behavior. --- src/components/VndbPanel.vue | 39 +++++++----------------------------- 1 file changed, 7 insertions(+), 32 deletions(-) diff --git a/src/components/VndbPanel.vue b/src/components/VndbPanel.vue index 13b4248..cb8ffc0 100644 --- a/src/components/VndbPanel.vue +++ b/src/components/VndbPanel.vue @@ -97,6 +97,7 @@ :alt="searchStore.vndbInfo.mainName" class="w-full h-auto rounded-2xl shadow-lg cursor-pointer hover:opacity-90 hover:scale-[1.02] transition-all" loading="lazy" + referrerpolicy="no-referrer" @error="handleImageError" /> @@ -377,6 +378,7 @@ :alt="char.name" class="absolute inset-0 w-full h-full object-cover" loading="lazy" + referrerpolicy="no-referrer" @load="($event.target as HTMLElement).parentElement?.querySelector('.skeleton')?.classList.add('hidden')" /> @@ -513,10 +515,9 @@ - +
@@ -527,14 +528,15 @@ @@ -548,7 +550,7 @@