mirror of
https://github.com/HiMeditator/auto-caption.git
synced 2026-02-14 03:24:44 +08:00
feat(theme): 添加暗色主题支持
- 新增暗色主题选项和系统主题自动适配功能 - 调整了部分样式以适应暗色主题
This commit is contained in:
@@ -35,6 +35,7 @@
|
||||
|
||||
- 优化界面布局
|
||||
- 添加更多可保存和载入的配置项
|
||||
- 为字幕引擎添加更严格的状态限制,防止出现僵尸进程
|
||||
|
||||
### 新增文档
|
||||
|
||||
|
||||
1
TODO.md
1
TODO.md
@@ -1,4 +1,5 @@
|
||||
- [x] 添加英语和日语语言支持
|
||||
- [ ] 优化长字幕显示效果
|
||||
- [x] 添加暗色主题
|
||||
- [ ] 修复字幕引擎空置报错的问题
|
||||
- [ ] 添加更多字幕引擎
|
||||
|
||||
@@ -31,6 +31,19 @@
|
||||
- 发送:无数据
|
||||
- 接收:`FullConfig`
|
||||
|
||||
### `control.nativeTheme.get`
|
||||
|
||||
**介绍:**前端获取系统当前的主题
|
||||
|
||||
**发起方:**前端控制窗口
|
||||
|
||||
**接收方:**后端控制窗口实例
|
||||
|
||||
**数据类型:**
|
||||
|
||||
- 发送:无数据
|
||||
- 接收:`string`
|
||||
|
||||
## 前端 ==> 后端
|
||||
|
||||
### `control.uiLanguage.change`
|
||||
@@ -43,6 +56,16 @@
|
||||
|
||||
**数据类型:**`UILanguage`
|
||||
|
||||
### `control.uiTheme.change`
|
||||
|
||||
**介绍:**前端修改字界面主题,将修改同步给后端
|
||||
|
||||
**发起方:**前端控制窗口
|
||||
|
||||
**接收方:**后端控制窗口实例
|
||||
|
||||
**数据类型:**`UITheme`
|
||||
|
||||
### `control.leftBarWidth.change`
|
||||
|
||||
**介绍:**前端修改边栏宽度,将修改同步给后端
|
||||
@@ -165,7 +188,7 @@
|
||||
|
||||
## 后端 ==> 前端
|
||||
|
||||
### `caption.uiLanguage.set`
|
||||
### `control.uiLanguage.set`
|
||||
|
||||
**介绍:**后端将最新界面语言发送给前端,前端进行设置
|
||||
|
||||
@@ -175,6 +198,16 @@
|
||||
|
||||
**数据类型:**`UILanguage`
|
||||
|
||||
### `control.nativeTheme.change`
|
||||
|
||||
**介绍:**系统主题发生改变
|
||||
|
||||
**发起方:**后端
|
||||
|
||||
**接收方:**前端控制窗口
|
||||
|
||||
**数据类型:**`string`
|
||||
|
||||
### `control.engine.started`
|
||||
|
||||
**介绍:**引擎启动成功
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { shell, BrowserWindow, ipcMain } from 'electron'
|
||||
import { shell, BrowserWindow, ipcMain, nativeTheme } from 'electron'
|
||||
import path from 'path'
|
||||
import { is } from '@electron-toolkit/utils'
|
||||
import icon from '../../resources/icon.png?asset'
|
||||
@@ -50,17 +50,37 @@ class ControlWindow {
|
||||
}
|
||||
|
||||
public handleMessage() {
|
||||
nativeTheme.on('updated', () => {
|
||||
if(allConfig.uiTheme === 'system'){
|
||||
if(nativeTheme.shouldUseDarkColors && this.window){
|
||||
this.window.webContents.send('control.nativeTheme.change', 'dark')
|
||||
}
|
||||
else if(!nativeTheme.shouldUseDarkColors && this.window){
|
||||
this.window.webContents.send('control.nativeTheme.change', 'light')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.handle('both.window.mounted', () => {
|
||||
return allConfig.getFullConfig()
|
||||
})
|
||||
|
||||
ipcMain.handle('control.nativeTheme.get', () => {
|
||||
if(nativeTheme.shouldUseDarkColors) return 'dark'
|
||||
return 'light'
|
||||
})
|
||||
|
||||
ipcMain.on('control.uiLanguage.change', (_, args) => {
|
||||
allConfig.uiLanguage = args
|
||||
if(captionWindow.window){
|
||||
captionWindow.window.webContents.send('caption.uiLanguage.set', args)
|
||||
captionWindow.window.webContents.send('control.uiLanguage.set', args)
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('control.uiTheme.change', (_, args) => {
|
||||
allConfig.uiTheme = args
|
||||
})
|
||||
|
||||
ipcMain.on('control.leftBarWidth.change', (_, args) => {
|
||||
allConfig.leftBarWidth = args
|
||||
})
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
export type UILanguage = "zh" | "en" | "ja"
|
||||
|
||||
export type UITheme = "light" | "dark" | "system"
|
||||
|
||||
export interface Controls {
|
||||
engineEnabled: boolean,
|
||||
sourceLang: string,
|
||||
@@ -35,6 +37,7 @@ export interface CaptionItem {
|
||||
|
||||
export interface FullConfig {
|
||||
uiLanguage: UILanguage,
|
||||
uiTheme: UITheme,
|
||||
leftBarWidth: number,
|
||||
styles: Styles,
|
||||
controls: Controls,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {
|
||||
UILanguage, Styles, CaptionItem, Controls,
|
||||
FullConfig
|
||||
UILanguage, UITheme, Styles, Controls,
|
||||
CaptionItem, FullConfig
|
||||
} from '../types'
|
||||
import { app, BrowserWindow } from 'electron'
|
||||
import * as path from 'path'
|
||||
@@ -35,6 +35,7 @@ const defaultControls: Controls = {
|
||||
class AllConfig {
|
||||
uiLanguage: UILanguage = 'zh';
|
||||
leftBarWidth: number = 8;
|
||||
uiTheme: UITheme = 'system';
|
||||
styles: Styles = {...defaultStyles};
|
||||
controls: Controls = {...defaultControls};
|
||||
captionLog: CaptionItem[] = [];
|
||||
@@ -46,6 +47,7 @@ class AllConfig {
|
||||
if(fs.existsSync(configPath)){
|
||||
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'))
|
||||
if(config.uiLanguage) this.uiLanguage = config.uiLanguage
|
||||
if(config.uiTheme) this.uiTheme = config.uiTheme
|
||||
if(config.leftBarWidth) this.leftBarWidth = config.leftBarWidth
|
||||
if(config.styles) this.setStyles(config.styles)
|
||||
if(config.controls) this.setControls(config.controls)
|
||||
@@ -56,6 +58,7 @@ class AllConfig {
|
||||
public writeConfig() {
|
||||
const config = {
|
||||
uiLanguage: this.uiLanguage,
|
||||
uiTheme: this.uiTheme,
|
||||
leftBarWidth: this.leftBarWidth,
|
||||
controls: this.controls,
|
||||
styles: this.styles
|
||||
@@ -68,6 +71,7 @@ class AllConfig {
|
||||
public getFullConfig(): FullConfig {
|
||||
return {
|
||||
uiLanguage: this.uiLanguage,
|
||||
uiTheme: this.uiTheme,
|
||||
leftBarWidth: this.leftBarWidth,
|
||||
styles: this.styles,
|
||||
controls: this.controls,
|
||||
|
||||
@@ -4,16 +4,20 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { FullConfig } from './types'
|
||||
import { useCaptionLogStore } from './stores/captionLog'
|
||||
import { useCaptionStyleStore } from './stores/captionStyle'
|
||||
import { useEngineControlStore } from './stores/engineControl'
|
||||
import { useGeneralSettingStore } from './stores/generalSetting'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
onMounted(() => {
|
||||
console.log('Current route:', router.currentRoute.value.fullPath)
|
||||
window.electron.ipcRenderer.invoke('both.window.mounted').then((data: FullConfig) => {
|
||||
console.log(data)
|
||||
useGeneralSettingStore().uiLanguage = data.uiLanguage
|
||||
useGeneralSettingStore().uiTheme = data.uiTheme
|
||||
useGeneralSettingStore().leftBarWidth = data.leftBarWidth
|
||||
useCaptionStyleStore().setStyles(data.styles)
|
||||
useEngineControlStore().setControls(data.controls)
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/* :root {
|
||||
:root {
|
||||
--control-background: #fff;
|
||||
}
|
||||
|
||||
} */
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
<template>
|
||||
<div class="caption-list">
|
||||
<div class="caption-title">
|
||||
<span style="margin-right: 30px;">{{ $t('log.title') }}</span>
|
||||
<div>
|
||||
<a-app class="caption-title">
|
||||
<span style="margin-right: 30px;">{{ $t('log.title') }}</span>
|
||||
</a-app>
|
||||
<a-button
|
||||
type="primary"
|
||||
style="margin-right: 20px;"
|
||||
@@ -106,13 +108,13 @@ function clearCaptions() {
|
||||
|
||||
<style scoped>
|
||||
.caption-list {
|
||||
background: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.caption-title {
|
||||
display: inline-block;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
@@ -139,13 +141,11 @@ function clearCaptions() {
|
||||
|
||||
.caption-text {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.caption-translation {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
padding-left: 16px;
|
||||
border-left: 3px solid #1890ff;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,15 @@
|
||||
</a-radio-group>
|
||||
</div>
|
||||
|
||||
<div class="input-item">
|
||||
<span class="input-label">{{ $t('general.theme') }}</span>
|
||||
<a-radio-group v-model:value="uiTheme">
|
||||
<a-radio-button value="system">{{ $t('general.system') }}</a-radio-button>
|
||||
<a-radio-button value="light">{{ $t('general.light') }}</a-radio-button>
|
||||
<a-radio-button value="dark">{{ $t('general.dark') }}</a-radio-button>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
|
||||
<div class="input-item">
|
||||
<span class="input-label">{{ $t('general.barWidth') }}</span>
|
||||
<a-input
|
||||
@@ -37,7 +46,7 @@ import { useGeneralSettingStore } from '@renderer/stores/generalSetting'
|
||||
import { InfoCircleOutlined } from '@ant-design/icons-vue';
|
||||
|
||||
const generalSettingStore = useGeneralSettingStore()
|
||||
const { uiLanguage, leftBarWidth } = storeToRefs(generalSettingStore)
|
||||
const { uiLanguage, uiTheme, leftBarWidth } = storeToRefs(generalSettingStore)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
10
src/renderer/src/i18n/config/theme.ts
Normal file
10
src/renderer/src/i18n/config/theme.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { theme } from 'ant-design-vue';
|
||||
|
||||
export const antDesignTheme = {
|
||||
light: {
|
||||
token: {}
|
||||
},
|
||||
dark: {
|
||||
algorithm: theme.darkAlgorithm,
|
||||
}
|
||||
}
|
||||
@@ -16,3 +16,4 @@ export const i18n = createI18n({
|
||||
|
||||
export * from './config/engine'
|
||||
export * from './config/audio'
|
||||
export * from './config/theme'
|
||||
|
||||
@@ -26,7 +26,11 @@ export default {
|
||||
"title": "General Settings",
|
||||
"uiLanguage": "Language",
|
||||
"barWidth": "Width",
|
||||
"note": "General Settings take effect immediately. Please note that changes to the Caption Engine Settings and Caption Style Settings will only take effect after clicking Apply."
|
||||
"note": "General Settings take effect immediately. Please note that changes to the Caption Engine Settings and Caption Style Settings will only take effect after clicking Apply.",
|
||||
"theme": "theme",
|
||||
"light": "light",
|
||||
"dark": "dark",
|
||||
"system": "system"
|
||||
},
|
||||
engine: {
|
||||
"title": "Caption Engine Settings",
|
||||
|
||||
@@ -26,7 +26,11 @@ export default {
|
||||
"title": "一般設定",
|
||||
"uiLanguage": "言語設定",
|
||||
"barWidth": "左側の幅",
|
||||
"note": "一般設定はすぐに有効になります。字幕エンジンの設定と字幕スタイルの設定を変更した場合は、適用ボタンをクリックしてから有効になりますのでご注意ください。"
|
||||
"note": "一般設定はすぐに有効になります。字幕エンジンの設定と字幕スタイルの設定を変更した場合は、適用ボタンをクリックしてから有効になりますのでご注意ください。",
|
||||
"theme": "テーマ",
|
||||
"light": "明るい",
|
||||
"dark": "暗い",
|
||||
"system": "システム"
|
||||
},
|
||||
engine: {
|
||||
"title": "字幕エンジン設定",
|
||||
|
||||
@@ -28,7 +28,11 @@ export default {
|
||||
"title": "通用设置",
|
||||
"uiLanguage": "界面语言",
|
||||
"barWidth": "左侧宽度",
|
||||
"note": "通用设置修改后立即生效。注意字幕引擎设置和字幕样式的设置修改后需要点击应用后才会生效。"
|
||||
"note": "通用设置修改后立即生效。注意字幕引擎设置和字幕样式的设置修改后需要点击应用后才会生效。",
|
||||
"theme": "主题",
|
||||
"light": "浅色",
|
||||
"dark": "深色",
|
||||
"system": "系统"
|
||||
},
|
||||
engine: {
|
||||
"title": "字幕引擎设置",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import './assets/reset.css'
|
||||
import './assets/main.css'
|
||||
import { createApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
import { ref, watch } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
import { i18n } from '../i18n'
|
||||
import type { UILanguage } from '../types'
|
||||
import type { UILanguage, UITheme } from '../types'
|
||||
|
||||
import { engines, audioTypes } from '../i18n'
|
||||
import { engines, audioTypes, antDesignTheme } from '../i18n'
|
||||
import { useEngineControlStore } from './engineControl'
|
||||
|
||||
export const useGeneralSettingStore = defineStore('generalSetting', () => {
|
||||
const uiLanguage = ref<UILanguage>('zh')
|
||||
const uiTheme = ref<UITheme>('system')
|
||||
const leftBarWidth = ref<number>(8)
|
||||
|
||||
const antdTheme = ref<Object>(antDesignTheme['light'])
|
||||
|
||||
watch(uiLanguage, (newValue) => {
|
||||
i18n.global.locale.value = newValue
|
||||
useEngineControlStore().captionEngine = engines[newValue]
|
||||
@@ -17,16 +20,47 @@ export const useGeneralSettingStore = defineStore('generalSetting', () => {
|
||||
window.electron.ipcRenderer.send('control.uiLanguage.change', newValue)
|
||||
})
|
||||
|
||||
watch(uiTheme, (newValue) => {
|
||||
window.electron.ipcRenderer.send('control.uiTheme.change', newValue)
|
||||
if(newValue === 'system'){
|
||||
window.electron.ipcRenderer.invoke('control.nativeTheme.get').then((theme) => {
|
||||
if(theme === 'light') setLightTheme()
|
||||
else if(theme === 'dark') setDarkTheme()
|
||||
})
|
||||
}
|
||||
else if(newValue === 'light') setLightTheme()
|
||||
else if(newValue === 'dark') setDarkTheme()
|
||||
})
|
||||
|
||||
watch(leftBarWidth, (newValue) => {
|
||||
window.electron.ipcRenderer.send('control.leftBarWidth.change', newValue)
|
||||
})
|
||||
|
||||
window.electron.ipcRenderer.on('caption.uiLanguage.set', (_, args: UILanguage) => {
|
||||
window.electron.ipcRenderer.on('control.uiLanguage.set', (_, args: UILanguage) => {
|
||||
uiLanguage.value = args
|
||||
})
|
||||
|
||||
window.electron.ipcRenderer.on('control.nativeTheme.change', (_, args) => {
|
||||
if(args === 'light') setLightTheme()
|
||||
else if(args === 'dark') setDarkTheme()
|
||||
})
|
||||
|
||||
function setLightTheme(){
|
||||
antdTheme.value = antDesignTheme.light
|
||||
const root = document.documentElement
|
||||
root.style.setProperty('--control-background', '#fff')
|
||||
}
|
||||
|
||||
function setDarkTheme(){
|
||||
antdTheme.value = antDesignTheme.dark
|
||||
const root = document.documentElement
|
||||
root.style.setProperty('--control-background', '#000')
|
||||
}
|
||||
|
||||
return {
|
||||
uiLanguage,
|
||||
leftBarWidth
|
||||
uiTheme,
|
||||
leftBarWidth,
|
||||
antdTheme
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
export type UILanguage = "zh" | "en" | "ja"
|
||||
|
||||
export type UITheme = "light" | "dark" | "system"
|
||||
|
||||
export interface Controls {
|
||||
engineEnabled: boolean,
|
||||
sourceLang: string,
|
||||
@@ -35,6 +37,7 @@ export interface CaptionItem {
|
||||
|
||||
export interface FullConfig {
|
||||
uiLanguage: UILanguage,
|
||||
uiTheme: UITheme,
|
||||
leftBarWidth: number,
|
||||
styles: Styles,
|
||||
controls: Controls,
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
<template>
|
||||
<a-row>
|
||||
<a-col :span="leftBarWidth">
|
||||
<div class="caption-control">
|
||||
<GeneralSetting />
|
||||
<EngineControl />
|
||||
<CaptionStyle />
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="24 - leftBarWidth">
|
||||
<div class="caption-data">
|
||||
<EngineStatus />
|
||||
<CaptionLog />
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-config-provider :theme="antdTheme">
|
||||
<a-row class="control-container">
|
||||
<a-col :span="leftBarWidth">
|
||||
<div class="caption-control">
|
||||
<GeneralSetting />
|
||||
<EngineControl />
|
||||
<CaptionStyle />
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="24 - leftBarWidth">
|
||||
<div class="caption-data">
|
||||
<EngineStatus />
|
||||
<CaptionLog />
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-config-provider>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@@ -26,22 +28,30 @@ import { storeToRefs } from 'pinia'
|
||||
import { useGeneralSettingStore } from '@renderer/stores/generalSetting'
|
||||
|
||||
const generalSettingStore = useGeneralSettingStore()
|
||||
const { leftBarWidth } = storeToRefs(generalSettingStore)
|
||||
const { leftBarWidth, antdTheme } = storeToRefs(generalSettingStore)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.control-container {
|
||||
background-color: var(--control-background);
|
||||
}
|
||||
|
||||
|
||||
.caption-control {
|
||||
height: 100vh;
|
||||
border-right: 1px solid #7774;
|
||||
padding: 20px;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
.caption-data {
|
||||
height: 100vh;
|
||||
padding: 20px;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
.caption-control::-webkit-scrollbar,
|
||||
.caption-data::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user