// // ClipperConfig.swift // 划词侠 plugin // // Loads ~/.config/codeisland/clipper-config.json on demand. // Schema: // { "deepseek_api_key": "sk-...", // "default_translate_target": "zh" } // // Never bundles a key. If the file is missing/empty we surface a // state-E "请先填" screen with a helper that copies the path to the // clipboard so the user can `open -e` it. // import Foundation struct ClipperConfig { var deepseekApiKey: String var defaultTranslateTarget: String // "zh" | "en" | "ja" static let configPath: String = { let home = NSHomeDirectory() return "\(home)/.config/codeisland/clipper-config.json" }() static var configDir: String { let home = NSHomeDirectory() return "\(home)/.config/codeisland" } /// Best-effort load. If anything is missing we return nil so the UI /// can render the onboarding banner — never throws to the caller. static func load() -> ClipperConfig? { let url = URL(fileURLWithPath: configPath) guard let data = try? Data(contentsOf: url) else { ClipperDebugLog.write("config: file missing at \(configPath)") return nil } guard let obj = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else { ClipperDebugLog.write("config: malformed JSON") return nil } let key = (obj["deepseek_api_key"] as? String) ?? "" let target = (obj["default_translate_target"] as? String) ?? "zh" if key.trimmingCharacters(in: .whitespaces).isEmpty || key.trimmingCharacters(in: .whitespaces) == "TODO" { ClipperDebugLog.write("config: deepseek_api_key empty/TODO") return nil } return ClipperConfig(deepseekApiKey: key, defaultTranslateTarget: target) } /// Write a stub file the user can edit. Idempotent — won't clobber /// an existing real config. @discardableResult static func ensureStubExists() -> Bool { let fm = FileManager.default if fm.fileExists(atPath: configPath) { return false } try? fm.createDirectory(atPath: configDir, withIntermediateDirectories: true) let stub = """ { "deepseek_api_key": "TODO", "default_translate_target": "zh" } """ do { try stub.write(toFile: configPath, atomically: true, encoding: .utf8) ClipperDebugLog.write("config: wrote stub at \(configPath)") return true } catch { ClipperDebugLog.write("config: stub write failed: \(error)") return false } } }