feat: add hide-dock setting and fix about text handling (#3)

- Add "hideDockIcon" setting label and description to English locale,
  enabling users to remove VidBee from the macOS Dock and rely on the
  menu bar/tray icon.
- Apply dock visibility changes when settings change, when settings
  are loaded, and after settings are reset by calling
  applyDockVisibility from the settings service.
- Update About page to use about.description for share text and display
  content, replacing the removed tagline key usage.
- Remove unused "tagline" entries from multiple locale files (en, fr,
  it, pt, ja, zh) to keep translations in sync with UI text changes.

These changes add OS integration for hiding the dock icon and ensure UI
copy is consistent and correctly internationalized.
This commit is contained in:
Nexmoe
2025-10-29 20:32:03 +08:00
committed by GitHub
parent 06132a0704
commit a1307bdd0b
14 changed files with 69 additions and 16 deletions

View File

@@ -10,6 +10,7 @@ import { downloadEngine } from './lib/download-engine'
import { ytdlpManager } from './lib/ytdlp-manager'
import { settingsManager } from './settings'
import { createTray, destroyTray } from './tray'
import { applyDockVisibility } from './utils/dock'
// Initialize electron-log for main process
log.initialize()
@@ -27,6 +28,7 @@ export function createWindow(): void {
height: 800,
show: false,
titleBarStyle: 'hidden', // Hide title bar on macOS
trafficLightPosition: { x: 12.5, y: 10 },
autoHideMenuBar: true,
icon: appIcon, // Set application icon
frame: false,
@@ -183,6 +185,8 @@ app.whenReady().then(async () => {
log.error('Failed to initialize yt-dlp:', error)
}
applyDockVisibility(settingsManager.get('hideDockIcon'))
createWindow()
initAutoUpdater()

View File

@@ -2,6 +2,7 @@ import { type IpcContext, IpcMethod, IpcService } from 'electron-ipc-decorator'
import type { AppSettings } from '../../../shared/types'
import { settingsManager } from '../../settings'
import { updateTrayMenu } from '../../tray'
import { applyDockVisibility } from '../../utils/dock'
class SettingsService extends IpcService {
static readonly groupName = 'settings'
@@ -18,6 +19,10 @@ class SettingsService extends IpcService {
if (key === 'language') {
updateTrayMenu()
}
if (key === 'hideDockIcon') {
applyDockVisibility(value as AppSettings['hideDockIcon'])
}
}
@IpcMethod()
@@ -32,11 +37,16 @@ class SettingsService extends IpcService {
if (settings.language) {
updateTrayMenu()
}
if (typeof settings.hideDockIcon === 'boolean') {
applyDockVisibility(settings.hideDockIcon)
}
}
@IpcMethod()
reset(_context: IpcContext): void {
settingsManager.reset()
applyDockVisibility(settingsManager.get('hideDockIcon'))
}
}

16
src/main/utils/dock.ts Normal file
View File

@@ -0,0 +1,16 @@
import { app } from 'electron'
/**
* Apply Dock visibility preference on macOS.
*/
export function applyDockVisibility(hideDockIcon: boolean): void {
if (process.platform !== 'darwin' || !app.dock) {
return
}
if (hideDockIcon) {
app.dock.hide()
} else {
app.dock.show()
}
}

View File

@@ -60,7 +60,6 @@
"shareSupport": "Recommend VidBee to your friends to support our growth and updates.",
"shareTitle": "Spread the word",
"sourceCode": "Source Code is available",
"tagline": "An AI-friendly download helper for every creator",
"title": "About",
"version": "Version",
"versionLabel": "v{{version}}",
@@ -281,6 +280,8 @@
"general": "General",
"language": "Language",
"light": "Light",
"hideDockIcon": "Hide Dock icon",
"hideDockIconDescription": "Remove VidBee from the macOS Dock. Use the menu bar or tray icon to reopen the app.",
"maxConcurrentDownloads": "Maximum number of active downloads",
"maxConcurrentDownloadsDescription": "Maximum number of simultaneous downloads",
"none": "None",

View File

@@ -60,7 +60,6 @@
"shareSupport": "Recommandez VidBee à vos amis pour soutenir notre croissance et nos mises à jour.",
"shareTitle": "Faites passer le mot",
"sourceCode": "Le code source est disponible",
"tagline": "Un assistant de téléchargement convivial pour l'IA pour chaque créateur",
"title": "À propos",
"version": "Version",
"versionLabel": "v{{version}}",

View File

@@ -60,7 +60,6 @@
"shareSupport": "Raccomanda VidBee ai tuoi amici per sostenere la nostra crescita e aggiornamenti.",
"shareTitle": "Passa parola",
"sourceCode": "Il codice sorgente è disponibile",
"tagline": "Un assistente di download amichevole per l'IA per ogni creatore",
"title": "Informazioni",
"version": "Versione",
"versionLabel": "v{{version}}",

View File

@@ -60,7 +60,6 @@
"shareSupport": "私たちの成長とアップデートをサポートするために、友達にVidBeeを推奨してください。",
"shareTitle": "口コミを広める",
"sourceCode": "ソースコードが利用可能",
"tagline": "すべてのクリエイターのためのAIフレンドリーなダウンロードアシスタント",
"title": "について",
"version": "バージョン",
"versionLabel": "v{{version}}",

View File

@@ -60,7 +60,6 @@
"shareSupport": "우리의 성장과 업데이트를 지원하기 위해 친구들에게 VidBee를 추천하세요.",
"shareTitle": "소문을 퍼뜨리세요",
"sourceCode": "소스 코드 사용 가능",
"tagline": "모든 크리에이터를 위한 AI 친화적 다운로드 도우미",
"title": "정보",
"version": "버전",
"versionLabel": "v{{version}}",

View File

@@ -60,7 +60,6 @@
"shareSupport": "Recomende VidBee aos seus amigos para apoiar nosso crescimento e atualizações.",
"shareTitle": "Espalhe a palavra",
"sourceCode": "Código fonte disponível",
"tagline": "Um assistente de download amigável à IA para cada criador",
"title": "Sobre",
"version": "Versão",
"versionLabel": "v{{version}}",

View File

@@ -60,7 +60,6 @@
"shareSupport": "向您的朋友推薦 VidBee 以支援我們的成長和更新。",
"shareTitle": "廣為宣傳",
"sourceCode": "原始碼已開放",
"tagline": "面向每位創作者的 AI 友善下載助手",
"title": "關於",
"version": "版本",
"versionLabel": "v{{version}}",

View File

@@ -52,7 +52,6 @@
"resourcesDescription": "了解 VidBee 并保持关注的实用链接。",
"resourcesTitle": "资源",
"sourceCode": "源代码已开放",
"tagline": "面向每位创作者的 AI 友好下载助手",
"title": "关于",
"version": "版本",
"versionLabel": "v{{version}}",

View File

@@ -114,7 +114,7 @@ export function About() {
const shareLinks = useMemo(() => {
const encodedUrl = encodeURIComponent(shareTargetUrl)
const encodedText = encodeURIComponent(t('about.tagline'))
const encodedText = encodeURIComponent(t('about.description'))
return {
facebook: `https://www.facebook.com/sharer/sharer.php?u=${encodedUrl}`,
@@ -207,11 +207,6 @@ export function About() {
return (
<div className="h-full bg-background">
<div className="container mx-auto max-w-5xl p-6 space-y-6">
<div className="space-y-2">
<h1 className="text-3xl font-bold tracking-tight">{t('about.title')}</h1>
<p className="text-muted-foreground">{t('about.description')}</p>
</div>
<Card>
<CardContent className="pt-6">
<div className="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
@@ -220,7 +215,7 @@ export function About() {
<div className="space-y-2">
<div>
<h2 className="text-2xl font-semibold leading-tight">{t('about.appName')}</h2>
<p className="text-sm text-muted-foreground">{t('about.tagline')}</p>
<p className="text-sm text-muted-foreground">{t('about.description')}</p>
</div>
<div className="flex flex-wrap items-center gap-2">
<Badge variant="secondary">

View File

@@ -21,7 +21,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@renderer/components/u
import type { OneClickQualityPreset } from '@shared/types'
import { useAtom, useSetAtom } from 'jotai'
import { useTheme } from 'next-themes'
import { useEffect } from 'react'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { toast } from 'sonner'
import { loadSettingsAtom, saveSettingAtom, settingsAtom } from '../store/settings'
@@ -32,11 +32,26 @@ export function Settings() {
const [settings, _setSettings] = useAtom(settingsAtom)
const loadSettings = useSetAtom(loadSettingsAtom)
const saveSetting = useSetAtom(saveSettingAtom)
const [platform, setPlatform] = useState<string>('')
useEffect(() => {
loadSettings()
}, [loadSettings])
useEffect(() => {
const fetchPlatform = async () => {
try {
const { ipcServices } = await import('../lib/ipc')
const platformInfo = await ipcServices.app.getPlatform()
setPlatform(platformInfo)
} catch (error) {
console.error('Failed to get platform info:', error)
}
}
fetchPlatform()
}, [])
const handleSettingChange = async (
key: keyof typeof settings,
value: (typeof settings)[keyof typeof settings]
@@ -263,6 +278,23 @@ export function Settings() {
</TabsContent>
<TabsContent value="advanced" className="space-y-4 mt-2">
{platform === 'darwin' && (
<ItemGroup>
<Item variant="muted">
<ItemContent>
<ItemTitle>{t('settings.hideDockIcon')}</ItemTitle>
<ItemDescription>{t('settings.hideDockIconDescription')}</ItemDescription>
</ItemContent>
<ItemActions>
<Switch
checked={settings.hideDockIcon}
onCheckedChange={(value) => handleSettingChange('hideDockIcon', value)}
/>
</ItemActions>
</Item>
</ItemGroup>
)}
<ItemGroup>
<Item variant="muted">
<ItemContent>

View File

@@ -156,6 +156,7 @@ export interface AppSettings {
oneClickDownloadType: 'video' | 'audio'
oneClickQuality: OneClickQualityPreset
closeToTray: boolean
hideDockIcon: boolean
autoUpdate: boolean
}
@@ -174,5 +175,6 @@ export const defaultSettings: AppSettings = {
oneClickDownloadType: 'video',
oneClickQuality: 'auto',
closeToTray: false,
hideDockIcon: false,
autoUpdate: true
}