feat(ui): 主题切换 + 语言持久化 + About 对话框
- 新增 light/dark 主题调色板与 Windows 注册表系统主题探测 - 偏好设置对话框:主题三选一 + 语言下拉(en/zh-CN/de),即时生效 - 持久化 Language/Theme/AutoLockMinutes/MinimizeToTray 至 YAML 顶层 - 启动时按 config 中保存的语言初始化 i18n - About 对话框展示 version/runtime/项目地址/许可证 - 全部硬编码颜色迁移到 themePalette,为深色模式做准备
This commit is contained in:
+43
-3
@@ -26,6 +26,13 @@ type store struct {
|
||||
passphrase []byte
|
||||
encrypted bool
|
||||
|
||||
// Top-level user preferences kept alongside entries so save() can
|
||||
// roundtrip them without requiring the UI to rebuild a full Config.
|
||||
language string
|
||||
theme string
|
||||
autoLockMinutes int
|
||||
minimizeToTray bool
|
||||
|
||||
// dirty signals a save is pending. The worker reads & resets it.
|
||||
dirty bool
|
||||
snapshotFn func() []config.Entry
|
||||
@@ -37,6 +44,27 @@ type store struct {
|
||||
onError func(error)
|
||||
}
|
||||
|
||||
// Preferences returns the persisted top-level prefs. UI code reads these
|
||||
// after Load to seed dialog defaults.
|
||||
func (s *store) Preferences() (language, theme string, autoLockMinutes int, minimizeToTray bool) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
return s.language, s.theme, s.autoLockMinutes, s.minimizeToTray
|
||||
}
|
||||
|
||||
// SetPreferences updates the persisted top-level prefs and schedules a
|
||||
// save. Callers pass the current value for each field — there is no
|
||||
// per-field "leave unchanged" sentinel.
|
||||
func (s *store) SetPreferences(language, theme string, autoLockMinutes int, minimizeToTray bool) {
|
||||
s.mu.Lock()
|
||||
s.language = language
|
||||
s.theme = theme
|
||||
s.autoLockMinutes = autoLockMinutes
|
||||
s.minimizeToTray = minimizeToTray
|
||||
s.mu.Unlock()
|
||||
s.Push()
|
||||
}
|
||||
|
||||
// newStore initializes a store and starts the background save worker.
|
||||
// snapshotFn is invoked whenever a save runs; it must return a freshly
|
||||
// copied entries slice (the worker holds no UI locks). onError is called
|
||||
@@ -86,6 +114,10 @@ func (s *store) Load(passphrase []byte) (*config.Config, error) {
|
||||
s.mu.Lock()
|
||||
s.passphrase = passphrase
|
||||
s.encrypted = cfg.Encrypted
|
||||
s.language = cfg.Language
|
||||
s.theme = cfg.Theme
|
||||
s.autoLockMinutes = cfg.AutoLockMinutes
|
||||
s.minimizeToTray = cfg.MinimizeToTray
|
||||
s.mu.Unlock()
|
||||
logger.WithField("entries", len(cfg.Entries)).Debug("config loaded into store")
|
||||
return cfg, nil
|
||||
@@ -148,13 +180,21 @@ func (s *store) run() {
|
||||
s.dirty = false
|
||||
pw := append([]byte(nil), s.passphrase...)
|
||||
enc := s.encrypted
|
||||
lang := s.language
|
||||
theme := s.theme
|
||||
autoLock := s.autoLockMinutes
|
||||
minToTray := s.minimizeToTray
|
||||
s.mu.Unlock()
|
||||
|
||||
entries := s.snapshotFn()
|
||||
cfg := &config.Config{
|
||||
Version: 1,
|
||||
Encrypted: enc,
|
||||
Entries: entries,
|
||||
Version: 1,
|
||||
Language: lang,
|
||||
Theme: theme,
|
||||
AutoLockMinutes: autoLock,
|
||||
MinimizeToTray: minToTray,
|
||||
Encrypted: enc,
|
||||
Entries: entries,
|
||||
}
|
||||
if err := config.SaveYAML(cfg, s.path, pw); err != nil {
|
||||
global.Log.WithField("func", fn).WithError(err).Error("save failed")
|
||||
|
||||
Reference in New Issue
Block a user