mirror of
https://github.com/carey314/mio-plugin-clipper.git
synced 2026-08-10 07:44:32 +00:00
76 lines
2.6 KiB
Swift
76 lines
2.6 KiB
Swift
|
|
//
|
||
|
|
// 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
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|