diff --git a/plugins/decky-vault/main.py b/plugins/decky-vault/main.py index 2a2990b..0e562b2 100644 --- a/plugins/decky-vault/main.py +++ b/plugins/decky-vault/main.py @@ -413,4 +413,38 @@ benchmark_percentiles=97,AVG,1,0.1 except urllib.error.URLError as e: return {"valid": False, "error": f"Network error: {str(e.reason)}"} except Exception as e: - return {"valid": False, "error": str(e)} \ No newline at end of file + return {"valid": False, "error": str(e)} + + async def export_config(self, settings: dict) -> dict: + """RPC: Export current settings to Downloads/deckyvault-config.json. + Returns {success: bool, path?: str, error?: str}.""" + try: + home = os.path.expanduser("~") + config_path = os.path.join(home, "Downloads", "deckyvault-config.json") + with open(config_path, 'w') as f: + json.dump(settings, f, indent=2) + return {"success": True, "path": config_path} + except Exception as e: + return {"success": False, "error": str(e)} + + async def import_config(self) -> dict: + """RPC: Import settings from the latest deckyvault-config.json in Downloads. + Returns {success: bool, settings?: dict, error?: str}.""" + try: + home = os.path.expanduser("~") + config_path = os.path.join(home, "Downloads", "deckyvault-config.json") + if not os.path.exists(config_path): + return {"success": False, "error": "No deckyvault-config.json found in Downloads"} + with open(config_path, 'r') as f: + settings = json.load(f) + return { + "success": True, + "settings": { + "apiKey": settings.get("apiKey", ""), + "exportPath": settings.get("exportPath", "/home/deck/Downloads"), + "baseUrl": settings.get("baseUrl", "https://deckyvault.xyz"), + "hardwareSlug": settings.get("hardwareSlug", None), + } + } + except Exception as e: + return {"success": False, "error": str(e)} \ No newline at end of file diff --git a/plugins/decky-vault/src/components/session-form.tsx b/plugins/decky-vault/src/components/session-form.tsx index 05c69de..5a7d39b 100644 --- a/plugins/decky-vault/src/components/session-form.tsx +++ b/plugins/decky-vault/src/components/session-form.tsx @@ -29,20 +29,20 @@ interface SessionFormProps { } const UPSCALER_OPTIONS = [ - { label: "None", value: "none" }, - { label: "FSR", value: "fsr" }, - { label: "DLSS", value: "dlss" }, - { label: "XeSS", value: "xess" }, - { label: "LSFG", value: "lsfg" }, - { label: "Other", value: "other" }, + { label: "None", data: "none" }, + { label: "FSR", data: "fsr" }, + { label: "DLSS", data: "dlss" }, + { label: "XeSS", data: "xess" }, + { label: "LSFG", data: "lsfg" }, + { label: "Other", data: "other" }, ] const FRAME_GEN_OPTIONS = [ - { label: "None", value: "none" }, - { label: "FSR FG", value: "fsr_fg" }, - { label: "DLSS FG", value: "dlss_fg" }, - { label: "LSFG", value: "lsfg" }, - { label: "Other", value: "other" }, + { label: "None", data: "none" }, + { label: "FSR FG", data: "fsr_fg" }, + { label: "DLSS FG", data: "dlss_fg" }, + { label: "LSFG", data: "lsfg" }, + { label: "Other", data: "other" }, ] export default function SessionForm({ diff --git a/plugins/decky-vault/src/components/settings-panel.tsx b/plugins/decky-vault/src/components/settings-panel.tsx index 0886fd9..08ee14b 100644 --- a/plugins/decky-vault/src/components/settings-panel.tsx +++ b/plugins/decky-vault/src/components/settings-panel.tsx @@ -12,10 +12,12 @@ import { FaTimes, FaDownload, FaCog, + FaFileExport, + FaFileImport, } from "react-icons/fa" import type { PluginSettings } from "../lib/store" import { KNOWN_HARDWARE_SLUGS } from "@deckyvault/shared" -import { testApiKey, checkMangohud, writeMangohudConfig, getMangohudConfig } from "../lib/api" +import { testApiKey, checkMangohud, writeMangohudConfig, exportConfig, importConfig } from "../lib/api" interface SettingsPanelProps { settings: PluginSettings @@ -26,8 +28,8 @@ interface SettingsPanelProps { } const HARDWARE_OPTIONS = [ - { label: "Auto-detect", value: "" }, - ...KNOWN_HARDWARE_SLUGS.map((slug) => ({ label: slug, value: slug })), + { label: "Auto-detect", data: "" }, + ...KNOWN_HARDWARE_SLUGS.map((slug) => ({ label: slug, data: slug })), ] export default function SettingsPanel({ @@ -42,8 +44,8 @@ export default function SettingsPanel({ path: string version: string }>({ checked: false, installed: false, path: "", version: "" }) - const [showMangohudGuide, setShowMangohudGuide] = useState(false) const [configWritten, setConfigWritten] = useState(false) + const [configStatus, setConfigStatus] = useState<{ message: string; isError: boolean } | null>(null) async function handleTestKey() { if (!settings.apiKey) { @@ -78,6 +80,30 @@ export default function SettingsPanel({ setConfigWritten(result.success) } + async function handleExportConfig() { + setConfigStatus(null) + const result = await exportConfig(settings) + if (result.success) { + setConfigStatus({ message: `Config saved to ${result.path}`, isError: false }) + } else { + setConfigStatus({ message: result.error || "Export failed", isError: true }) + } + } + + async function handleImportConfig() { + setConfigStatus(null) + const result = await importConfig() + if (result.success && result.settings) { + onUpdateSetting("apiKey", result.settings.apiKey || "") + onUpdateSetting("exportPath", result.settings.exportPath || "/home/deck/Downloads") + onUpdateSetting("baseUrl", result.settings.baseUrl || "https://deckyvault.xyz") + onUpdateSetting("hardwareSlug", result.settings.hardwareSlug || null) + setConfigStatus({ message: "Config imported from Downloads", isError: false }) + } else { + setConfigStatus({ message: result.error || "No config file found in Downloads", isError: true }) + } + } + return ( <> {/* ── API Key ─────────────────────────────────────────────── */} @@ -152,6 +178,40 @@ export default function SettingsPanel({ + {/* ── Config Export/Import ────────────────────────────────── */} + + + +
+ + Export Config to Downloads +
+
+
+ + +
+ + Import Config from Downloads +
+
+
+ {configStatus && ( + +
+ {configStatus.isError ? : } {configStatus.message} +
+
+ )} +
+ {/* ── MangoHud Setup ──────────────────────────────────────── */} @@ -205,53 +265,45 @@ export default function SettingsPanel({ )} - setShowMangohudGuide(!showMangohudGuide)}> - {showMangohudGuide ? "Hide Guide" : "Show Installation Guide"} - +
+ Steam Deck (SteamOS): +
+ MangoHud is pre-installed. Enable it per-game by adding + + mangohud %command% + + to the game's Steam launch options (right-click game → Properties → Launch Options). + +

+ Other Linux handhelds (ROG Ally, Legion Go): +
+ Install via package manager: + + sudo apt install mangohud + + or Flatpak: + + flatpak install flathub org.freedesktop.Platform.VulkanLayer.MangoHud + + +

+ Manual build: +
+ See{" "} + + github.com/flightlessmango/MangoHud + + +

+ Troubleshooting: +
+ • Log file empty? Check MangoHud is enabled for the game and the config was written. +
+ • Wrong path? Ensure the plugin can write to /tmp/. +
+ • Not attaching? Try adding mangohud %command% to Steam launch options explicitly. +
- - {showMangohudGuide && ( - -
- Steam Deck (SteamOS): -
- MangoHud is pre-installed. Enable it per-game by adding - - mangohud %command% - - to the game's Steam launch options (right-click game → Properties → Launch Options). - -

- Other Linux handhelds (ROG Ally, Legion Go): -
- Install via package manager: - - sudo apt install mangohud - - or Flatpak: - - flatpak install flathub org.freedesktop.Platform.VulkanLayer.MangoHud - - -

- Manual build: -
- See{" "} - - github.com/flightlessmango/MangoHud - - -

- Troubleshooting: -
- • Log file empty? Check MangoHud is enabled for the game and the config was written. -
- • Wrong path? Ensure the plugin can write to /tmp/. -
- • Not attaching? Try adding mangohud %command% to Steam launch options explicitly. -
-
- )}
) diff --git a/plugins/decky-vault/src/lib/api.ts b/plugins/decky-vault/src/lib/api.ts index c6db3e5..fe8dddd 100644 --- a/plugins/decky-vault/src/lib/api.ts +++ b/plugins/decky-vault/src/lib/api.ts @@ -73,4 +73,27 @@ export const uploadToDeckyvault = callable<[ export const testApiKey = callable<[apiKey: string, baseUrl?: string], { valid: boolean error?: string -}>("test_api_key") \ No newline at end of file +}>("test_api_key") + +// ── Config Export/Import ──────────────────────────────────────── +export const exportConfig = callable<[settings: { + apiKey: string + exportPath: string + baseUrl: string + hardwareSlug: string | null +}], { + success: boolean + path?: string + error?: string +}>("export_config") + +export const importConfig = callable<[], { + success: boolean + settings?: { + apiKey: string + exportPath: string + baseUrl: string + hardwareSlug: string | null + } + error?: string +}>("import_config") \ No newline at end of file