- go.mod with Wails v2.9.2, x/crypto, go-sqlite3, go-keyring, uuid - cmd/app/main.go: Wails entry point (Nexus Terminal, 1400x900, #1a1a2e) - internal/app/app.go: lifecycle (Startup/Shutdown/DomReady) - internal/models/models.go: full model definitions (Profile, SSHConfig, TelnetConfig, RAWConfig, SessionInfo, KnownHost, AppPreferences, etc.) - internal/models/defaults.go: default values and NewProfile factory - internal/validation/validation.go: thorough field-level validation - frontend/embed.go: embedded frontend assets - Updated existing storage, secrets, config to match new model structure
108 lines
2.8 KiB
Go
108 lines
2.8 KiB
Go
// Package preferences manages application preferences with in-memory caching
|
|
// and persistence to a backing store.
|
|
package preferences
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
|
|
"github.com/copilot-ssh-client/app/internal/models"
|
|
)
|
|
|
|
// PreferenceStore is the persistence interface used by the Manager.
|
|
type PreferenceStore interface {
|
|
GetPreferences() (*models.AppPreferences, error)
|
|
SavePreferences(prefs *models.AppPreferences) error
|
|
}
|
|
|
|
// Manager handles application preferences with defaults and caching.
|
|
// It is safe for concurrent use.
|
|
type Manager struct {
|
|
store PreferenceStore
|
|
cache *models.AppPreferences
|
|
mu sync.RWMutex
|
|
}
|
|
|
|
// NewManager creates a preferences Manager backed by the given store.
|
|
// Call Load to populate the cache from disc.
|
|
func NewManager(store PreferenceStore) *Manager {
|
|
return &Manager{store: store}
|
|
}
|
|
|
|
// Load reads preferences from the backing store into memory. If no
|
|
// preferences have been saved yet, sensible defaults are applied.
|
|
func (m *Manager) Load() error {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
|
|
prefs, err := m.store.GetPreferences()
|
|
if err != nil {
|
|
return fmt.Errorf("preferences: failed to load: %w", err)
|
|
}
|
|
|
|
if prefs == nil {
|
|
defaults := models.DefaultAppPreferences()
|
|
m.cache = &defaults
|
|
} else {
|
|
m.cache = prefs
|
|
m.applyDefaults()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Get returns the current preferences. If Load has not been called, defaults
|
|
// are returned.
|
|
func (m *Manager) Get() *models.AppPreferences {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
|
|
if m.cache == nil {
|
|
defaults := models.DefaultAppPreferences()
|
|
return &defaults
|
|
}
|
|
// Return a copy to prevent callers mutating the cache.
|
|
cp := *m.cache
|
|
return &cp
|
|
}
|
|
|
|
// Save persists the given preferences to the backing store and updates the
|
|
// in-memory cache.
|
|
func (m *Manager) Save(prefs *models.AppPreferences) error {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
|
|
if err := m.store.SavePreferences(prefs); err != nil {
|
|
return fmt.Errorf("preferences: failed to save: %w", err)
|
|
}
|
|
cp := *prefs
|
|
m.cache = &cp
|
|
return nil
|
|
}
|
|
|
|
// applyDefaults fills in zero-valued fields from the default preferences.
|
|
// Caller must hold m.mu.
|
|
func (m *Manager) applyDefaults() {
|
|
defaults := models.DefaultAppPreferences()
|
|
if m.cache.Theme == "" {
|
|
m.cache.Theme = defaults.Theme
|
|
}
|
|
if m.cache.DefaultProtocol == "" {
|
|
m.cache.DefaultProtocol = defaults.DefaultProtocol
|
|
}
|
|
if m.cache.SidebarWidth == 0 {
|
|
m.cache.SidebarWidth = defaults.SidebarWidth
|
|
}
|
|
if m.cache.DefaultFontFamily == "" {
|
|
m.cache.DefaultFontFamily = defaults.DefaultFontFamily
|
|
}
|
|
if m.cache.DefaultFontSize == 0 {
|
|
m.cache.DefaultFontSize = defaults.DefaultFontSize
|
|
}
|
|
if m.cache.DefaultScrollback == 0 {
|
|
m.cache.DefaultScrollback = defaults.DefaultScrollback
|
|
}
|
|
if m.cache.DefaultCursorStyle == "" {
|
|
m.cache.DefaultCursorStyle = defaults.DefaultCursorStyle
|
|
}
|
|
}
|