package platform import ( "encoding/json" "errors" "os" "path/filepath" ) // DesktopConfig is the desktop-only preference set, persisted as JSON under the // user config dir. json tags are the contract with the WebView settings page. type DesktopConfig struct { ClipboardSyncEnabled bool `json:"clipboard_sync_enabled"` LaunchAtLogin bool `json:"launch_at_login"` DeviceName string `json:"device_name"` // empty = use the hostname DownloadDir string `json:"download_dir"` // empty = system Downloads dir } // ResolveDeviceName returns the persisted device name, or the hostname when the // user hasn't renamed it. Persisting it Go-side is required because the WebView's // localStorage doesn't survive restart on the wails:// scheme (see session.go). func ResolveDeviceName() string { cfg, _ := LoadConfig() if cfg.DeviceName != "" { return cfg.DeviceName } if h, err := os.Hostname(); err == nil && h != "" { return h } return "cdrop-desktop" } // DefaultConfig is what a fresh install gets: clipboard sync on, no autostart. func DefaultConfig() DesktopConfig { return DesktopConfig{ClipboardSyncEnabled: true, LaunchAtLogin: false} } // ResolveDownloadDir returns the directory received files land in: the user's // configured override, or the system Downloads dir when unset. func ResolveDownloadDir() string { cfg, _ := LoadConfig() if cfg.DownloadDir != "" { return cfg.DownloadDir } return DefaultDownloadDir() } // DefaultDownloadDir is ~/Downloads (the OS default download folder; its path is // stable across locales on macOS/Windows). Falls back to the cwd if the home // dir can't be resolved. func DefaultDownloadDir() string { if home, err := os.UserHomeDir(); err == nil && home != "" { return filepath.Join(home, "Downloads") } return "." } // configPath is ~/Library/Application Support/cdrop/config.json on macOS // (os.UserConfigDir resolves the per-OS base). func configPath() (string, error) { dir, err := os.UserConfigDir() if err != nil { return "", err } return filepath.Join(dir, "cdrop", "config.json"), nil } // LoadConfig reads the persisted config, falling back to defaults when the file // is absent or unreadable so the app always has a usable preference set. func LoadConfig() (DesktopConfig, error) { p, err := configPath() if err != nil { return DefaultConfig(), err } data, err := os.ReadFile(p) if errors.Is(err, os.ErrNotExist) { return DefaultConfig(), nil } if err != nil { return DefaultConfig(), err } cfg := DefaultConfig() if err := json.Unmarshal(data, &cfg); err != nil { return DefaultConfig(), err } return cfg, nil } // SaveConfig writes the config atomically-ish (mkdir + write) under the user // config dir. func SaveConfig(cfg DesktopConfig) error { p, err := configPath() if err != nil { return err } if err := os.MkdirAll(filepath.Dir(p), 0o755); err != nil { return err } data, err := json.MarshalIndent(cfg, "", " ") if err != nil { return err } return os.WriteFile(p, data, 0o644) }