Merge branch 'dev' into prod
This commit is contained in:
@@ -13,6 +13,7 @@ import {
|
|||||||
EyeOff,
|
EyeOff,
|
||||||
Clock,
|
Clock,
|
||||||
AlertCircle,
|
AlertCircle,
|
||||||
|
Download,
|
||||||
} from "lucide-react"
|
} from "lucide-react"
|
||||||
import { motion, AnimatePresence } from "motion/react"
|
import { motion, AnimatePresence } from "motion/react"
|
||||||
|
|
||||||
@@ -187,6 +188,27 @@ export function SettingsApiKeysTab() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleDownloadConfig = () => {
|
||||||
|
if (!createdKey?.key) return
|
||||||
|
const config = {
|
||||||
|
apiKey: createdKey.key,
|
||||||
|
exportPath: "/home/deck/Downloads",
|
||||||
|
baseUrl: "https://deckyvault.xyz",
|
||||||
|
hardwareSlug: null,
|
||||||
|
}
|
||||||
|
const blob = new Blob([JSON.stringify(config, null, 2)], {
|
||||||
|
type: "application/json",
|
||||||
|
})
|
||||||
|
const url = URL.createObjectURL(blob)
|
||||||
|
const a = document.createElement("a")
|
||||||
|
a.href = url
|
||||||
|
a.download = "deckyvault-config.json"
|
||||||
|
document.body.appendChild(a)
|
||||||
|
a.click()
|
||||||
|
document.body.removeChild(a)
|
||||||
|
URL.revokeObjectURL(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Show the created key modal
|
// Show the created key modal
|
||||||
@@ -261,6 +283,14 @@ export function SettingsApiKeysTab() {
|
|||||||
>
|
>
|
||||||
Done
|
Done
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={handleDownloadConfig}
|
||||||
|
className='mt-2 px-4 py-2 rounded-lg border border-border text-text/70 text-sm font-medium hover:border-primary/40 hover:text-primary transition-colors cursor-pointer flex items-center justify-center gap-2 w-full'
|
||||||
|
>
|
||||||
|
<Download className='h-4 w-4' />
|
||||||
|
Download Plugin Config
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -414,3 +414,37 @@ benchmark_percentiles=97,AVG,1,0.1
|
|||||||
return {"valid": False, "error": f"Network error: {str(e.reason)}"}
|
return {"valid": False, "error": f"Network error: {str(e.reason)}"}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return {"valid": False, "error": str(e)}
|
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)}
|
||||||
@@ -29,20 +29,20 @@ interface SessionFormProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const UPSCALER_OPTIONS = [
|
const UPSCALER_OPTIONS = [
|
||||||
{ label: "None", value: "none" },
|
{ label: "None", data: "none" },
|
||||||
{ label: "FSR", value: "fsr" },
|
{ label: "FSR", data: "fsr" },
|
||||||
{ label: "DLSS", value: "dlss" },
|
{ label: "DLSS", data: "dlss" },
|
||||||
{ label: "XeSS", value: "xess" },
|
{ label: "XeSS", data: "xess" },
|
||||||
{ label: "LSFG", value: "lsfg" },
|
{ label: "LSFG", data: "lsfg" },
|
||||||
{ label: "Other", value: "other" },
|
{ label: "Other", data: "other" },
|
||||||
]
|
]
|
||||||
|
|
||||||
const FRAME_GEN_OPTIONS = [
|
const FRAME_GEN_OPTIONS = [
|
||||||
{ label: "None", value: "none" },
|
{ label: "None", data: "none" },
|
||||||
{ label: "FSR FG", value: "fsr_fg" },
|
{ label: "FSR FG", data: "fsr_fg" },
|
||||||
{ label: "DLSS FG", value: "dlss_fg" },
|
{ label: "DLSS FG", data: "dlss_fg" },
|
||||||
{ label: "LSFG", value: "lsfg" },
|
{ label: "LSFG", data: "lsfg" },
|
||||||
{ label: "Other", value: "other" },
|
{ label: "Other", data: "other" },
|
||||||
]
|
]
|
||||||
|
|
||||||
export default function SessionForm({
|
export default function SessionForm({
|
||||||
|
|||||||
@@ -12,10 +12,14 @@ import {
|
|||||||
FaTimes,
|
FaTimes,
|
||||||
FaDownload,
|
FaDownload,
|
||||||
FaCog,
|
FaCog,
|
||||||
|
FaFileExport,
|
||||||
|
FaFileImport,
|
||||||
|
FaCopy,
|
||||||
|
FaSearch,
|
||||||
} from "react-icons/fa"
|
} from "react-icons/fa"
|
||||||
import type { PluginSettings } from "../lib/store"
|
import type { PluginSettings } from "../lib/store"
|
||||||
import { KNOWN_HARDWARE_SLUGS } from "@deckyvault/shared"
|
import { KNOWN_HARDWARE_SLUGS } from "@deckyvault/shared"
|
||||||
import { testApiKey, checkMangohud, writeMangohudConfig, getMangohudConfig } from "../lib/api"
|
import { testApiKey, checkMangohud, writeMangohudConfig, getMangohudConfig, exportConfig, importConfig } from "../lib/api"
|
||||||
|
|
||||||
interface SettingsPanelProps {
|
interface SettingsPanelProps {
|
||||||
settings: PluginSettings
|
settings: PluginSettings
|
||||||
@@ -26,8 +30,8 @@ interface SettingsPanelProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const HARDWARE_OPTIONS = [
|
const HARDWARE_OPTIONS = [
|
||||||
{ label: "Auto-detect", value: "" },
|
{ label: "Auto-detect", data: "" },
|
||||||
...KNOWN_HARDWARE_SLUGS.map((slug) => ({ label: slug, value: slug })),
|
...KNOWN_HARDWARE_SLUGS.map((slug) => ({ label: slug, data: slug })),
|
||||||
]
|
]
|
||||||
|
|
||||||
export default function SettingsPanel({
|
export default function SettingsPanel({
|
||||||
@@ -42,8 +46,14 @@ export default function SettingsPanel({
|
|||||||
path: string
|
path: string
|
||||||
version: string
|
version: string
|
||||||
}>({ checked: false, installed: false, path: "", version: "" })
|
}>({ checked: false, installed: false, path: "", version: "" })
|
||||||
const [showMangohudGuide, setShowMangohudGuide] = useState(false)
|
|
||||||
const [configWritten, setConfigWritten] = useState(false)
|
const [configWritten, setConfigWritten] = useState(false)
|
||||||
|
const [configStatus, setConfigStatus] = useState<{ message: string; isError: boolean } | null>(null)
|
||||||
|
const [configVerified, setConfigVerified] = useState<{
|
||||||
|
checked: boolean
|
||||||
|
valid: boolean
|
||||||
|
message: string
|
||||||
|
}>({ checked: false, valid: false, message: "" })
|
||||||
|
const [copiedLaunchOpt, setCopiedLaunchOpt] = useState(false)
|
||||||
|
|
||||||
async function handleTestKey() {
|
async function handleTestKey() {
|
||||||
if (!settings.apiKey) {
|
if (!settings.apiKey) {
|
||||||
@@ -78,6 +88,86 @@ export default function SettingsPanel({
|
|||||||
setConfigWritten(result.success)
|
setConfigWritten(result.success)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleVerifyConfig() {
|
||||||
|
const result = await getMangohudConfig()
|
||||||
|
if (!result.exists) {
|
||||||
|
setConfigVerified({
|
||||||
|
checked: true,
|
||||||
|
valid: false,
|
||||||
|
message: "No MangoHud config found. Write one first.",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const content = result.content
|
||||||
|
const hasOutputFolder = content.includes("output_folder=/tmp")
|
||||||
|
const hasOutputFile = content.includes("output_file=deckyvault-mangohud.log")
|
||||||
|
const hasFps = content.includes("fps")
|
||||||
|
const hasFrameTiming = content.includes("frame_timing")
|
||||||
|
const hasGpuPower = content.includes("gpu_power")
|
||||||
|
|
||||||
|
if (hasOutputFolder && hasOutputFile && hasFps) {
|
||||||
|
setConfigVerified({
|
||||||
|
checked: true,
|
||||||
|
valid: true,
|
||||||
|
message: "Config looks good — logging to /tmp/deckyvault-mangohud.log",
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
const missing: string[] = []
|
||||||
|
if (!hasOutputFolder) missing.push("output_folder=/tmp")
|
||||||
|
if (!hasOutputFile) missing.push("output_file=deckyvault-mangohud.log")
|
||||||
|
if (!hasFps) missing.push("fps")
|
||||||
|
if (!hasFrameTiming) missing.push("frame_timing")
|
||||||
|
if (!hasGpuPower) missing.push("gpu_power")
|
||||||
|
setConfigVerified({
|
||||||
|
checked: true,
|
||||||
|
valid: false,
|
||||||
|
message: `Missing: ${missing.join(", ")}. Write config again.`,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleCopyLaunchOption() {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText("mangohud %command%")
|
||||||
|
setCopiedLaunchOpt(true)
|
||||||
|
setTimeout(() => setCopiedLaunchOpt(false), 2000)
|
||||||
|
} catch {
|
||||||
|
// Fallback
|
||||||
|
const ta = document.createElement("textarea")
|
||||||
|
ta.value = "mangohud %command%"
|
||||||
|
document.body.appendChild(ta)
|
||||||
|
ta.select()
|
||||||
|
document.execCommand("copy")
|
||||||
|
document.body.removeChild(ta)
|
||||||
|
setCopiedLaunchOpt(true)
|
||||||
|
setTimeout(() => setCopiedLaunchOpt(false), 2000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* ── API Key ─────────────────────────────────────────────── */}
|
{/* ── API Key ─────────────────────────────────────────────── */}
|
||||||
@@ -152,6 +242,40 @@ export default function SettingsPanel({
|
|||||||
</PanelSectionRow>
|
</PanelSectionRow>
|
||||||
</PanelSection>
|
</PanelSection>
|
||||||
|
|
||||||
|
{/* ── Config Export/Import ────────────────────────────────── */}
|
||||||
|
<PanelSection title="Configuration">
|
||||||
|
<PanelSectionRow>
|
||||||
|
<ButtonItem layout="below" onClick={handleExportConfig}>
|
||||||
|
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
|
||||||
|
<FaFileExport />
|
||||||
|
Export Config to Downloads
|
||||||
|
</div>
|
||||||
|
</ButtonItem>
|
||||||
|
</PanelSectionRow>
|
||||||
|
<PanelSectionRow>
|
||||||
|
<ButtonItem layout="below" onClick={handleImportConfig}>
|
||||||
|
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
|
||||||
|
<FaFileImport />
|
||||||
|
Import Config from Downloads
|
||||||
|
</div>
|
||||||
|
</ButtonItem>
|
||||||
|
</PanelSectionRow>
|
||||||
|
{configStatus && (
|
||||||
|
<PanelSectionRow>
|
||||||
|
<div
|
||||||
|
className={staticClasses.Text}
|
||||||
|
style={{
|
||||||
|
fontSize: "12px",
|
||||||
|
padding: "4px 0",
|
||||||
|
color: configStatus.isError ? "#e74c3c" : "#2ecc71",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{configStatus.isError ? <FaTimes /> : <FaCheck />} {configStatus.message}
|
||||||
|
</div>
|
||||||
|
</PanelSectionRow>
|
||||||
|
)}
|
||||||
|
</PanelSection>
|
||||||
|
|
||||||
{/* ── MangoHud Setup ──────────────────────────────────────── */}
|
{/* ── MangoHud Setup ──────────────────────────────────────── */}
|
||||||
<PanelSection title="MangoHud Setup">
|
<PanelSection title="MangoHud Setup">
|
||||||
<PanelSectionRow>
|
<PanelSectionRow>
|
||||||
@@ -196,6 +320,39 @@ export default function SettingsPanel({
|
|||||||
</ButtonItem>
|
</ButtonItem>
|
||||||
</PanelSectionRow>
|
</PanelSectionRow>
|
||||||
|
|
||||||
|
<PanelSectionRow>
|
||||||
|
<ButtonItem layout="below" onClick={handleVerifyConfig}>
|
||||||
|
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
|
||||||
|
<FaSearch />
|
||||||
|
Verify Config
|
||||||
|
</div>
|
||||||
|
</ButtonItem>
|
||||||
|
</PanelSectionRow>
|
||||||
|
|
||||||
|
{configVerified.checked && (
|
||||||
|
<PanelSectionRow>
|
||||||
|
<div
|
||||||
|
className={staticClasses.Text}
|
||||||
|
style={{
|
||||||
|
fontSize: "12px",
|
||||||
|
padding: "4px 0",
|
||||||
|
color: configVerified.valid ? "#2ecc71" : "#e74c3c",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{configVerified.valid ? <FaCheck /> : <FaTimes />} {configVerified.message}
|
||||||
|
</div>
|
||||||
|
</PanelSectionRow>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<PanelSectionRow>
|
||||||
|
<ButtonItem layout="below" onClick={handleCopyLaunchOption}>
|
||||||
|
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
|
||||||
|
<FaCopy />
|
||||||
|
{copiedLaunchOpt ? "Copied!" : "Copy Launch Option"}
|
||||||
|
</div>
|
||||||
|
</ButtonItem>
|
||||||
|
</PanelSectionRow>
|
||||||
|
|
||||||
{configWritten && (
|
{configWritten && (
|
||||||
<PanelSectionRow>
|
<PanelSectionRow>
|
||||||
<div className={staticClasses.Text} style={{ fontSize: "12px", color: "#2ecc71", padding: "4px 0" }}>
|
<div className={staticClasses.Text} style={{ fontSize: "12px", color: "#2ecc71", padding: "4px 0" }}>
|
||||||
@@ -204,13 +361,6 @@ export default function SettingsPanel({
|
|||||||
</PanelSectionRow>
|
</PanelSectionRow>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<PanelSectionRow>
|
|
||||||
<ButtonItem layout="below" onClick={() => setShowMangohudGuide(!showMangohudGuide)}>
|
|
||||||
{showMangohudGuide ? "Hide Guide" : "Show Installation Guide"}
|
|
||||||
</ButtonItem>
|
|
||||||
</PanelSectionRow>
|
|
||||||
|
|
||||||
{showMangohudGuide && (
|
|
||||||
<PanelSectionRow>
|
<PanelSectionRow>
|
||||||
<div className={staticClasses.Text} style={{ fontSize: "12px", padding: "8px", lineHeight: "1.6" }}>
|
<div className={staticClasses.Text} style={{ fontSize: "12px", padding: "8px", lineHeight: "1.6" }}>
|
||||||
<strong>Steam Deck (SteamOS):</strong>
|
<strong>Steam Deck (SteamOS):</strong>
|
||||||
@@ -251,7 +401,6 @@ export default function SettingsPanel({
|
|||||||
• Not attaching? Try adding <code>mangohud %command%</code> to Steam launch options explicitly.
|
• Not attaching? Try adding <code>mangohud %command%</code> to Steam launch options explicitly.
|
||||||
</div>
|
</div>
|
||||||
</PanelSectionRow>
|
</PanelSectionRow>
|
||||||
)}
|
|
||||||
</PanelSection>
|
</PanelSection>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
definePlugin,
|
definePlugin,
|
||||||
} from "@decky/api"
|
} from "@decky/api"
|
||||||
import { FaDatabase } from "react-icons/fa"
|
import { FaChartLine } from "react-icons/fa"
|
||||||
import MainPanel from "./components/main-panel"
|
import MainPanel from "./components/main-panel"
|
||||||
import SettingsPanel from "./components/settings-panel"
|
import SettingsPanel from "./components/settings-panel"
|
||||||
import { useSettings, useSession } from "./lib/store"
|
import { useSettings, useSession } from "./lib/store"
|
||||||
@@ -163,12 +163,58 @@ function Content() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── DeckyVault icon (flat SVG) ────────────────────────────────
|
||||||
|
function DeckyVaultIcon() {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 2000 2000"
|
||||||
|
width="16"
|
||||||
|
height="16"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
style={{
|
||||||
|
fillRule: "evenodd",
|
||||||
|
clipRule: "evenodd",
|
||||||
|
strokeLinecap: "round",
|
||||||
|
strokeLinejoin: "round",
|
||||||
|
strokeMiterlimit: 1.5,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<g transform="matrix(1,0,0,1,0,37.09322)">
|
||||||
|
<g transform="matrix(0.861784,0,0,0.861784,133.476373,498.392836)">
|
||||||
|
<g transform="matrix(1.160384,0,0,1.160384,-154.883825,-621.369358)">
|
||||||
|
<path
|
||||||
|
d="M1729.5,1227.837L1729.5,1549.724C1729.5,1640.166 1656.072,1713.593 1565.63,1713.593L433.04,1713.593C343.332,1713.593 270.5,1640.762 270.5,1551.053L270.5,1228.241C270.5,1173.107 315.262,1128.345 370.396,1128.345L1630.008,1128.345C1684.919,1128.345 1729.5,1172.926 1729.5,1227.837ZM573.241,1362.037C573.241,1347.001 561.033,1334.793 545.996,1334.793L440.872,1334.793C425.835,1334.793 413.627,1347.001 413.627,1362.037L413.627,1467.162C413.627,1482.198 425.835,1494.406 440.872,1494.406L545.996,1494.406C561.033,1494.406 573.241,1482.198 573.241,1467.162L573.241,1362.037ZM506.173,1203.28C477.429,1203.28 454.093,1226.617 454.093,1255.361C454.093,1284.105 477.429,1307.441 506.173,1307.441C534.917,1307.441 558.253,1284.105 558.253,1255.361C558.253,1226.617 534.917,1203.28 506.173,1203.28ZM1371.919,1256.579C1371.919,1225.929 1347.036,1201.045 1316.386,1201.045L675.371,1201.045C644.721,1201.045 619.838,1225.929 619.838,1256.579L619.838,1593.602C619.838,1624.252 644.721,1649.136 675.371,1649.136L1316.386,1649.136C1347.036,1649.136 1371.919,1624.252 1371.919,1593.602L1371.919,1256.579ZM1629.368,1260.092C1619.752,1260.092 1611.946,1267.899 1611.946,1277.515C1611.946,1287.131 1619.752,1294.937 1629.368,1294.937C1638.984,1294.937 1646.791,1287.131 1646.791,1277.515C1646.791,1267.899 1638.984,1260.092 1629.368,1260.092ZM1629.368,1173.745C1619.752,1173.745 1611.946,1181.552 1611.946,1191.168C1611.946,1200.784 1619.752,1208.59 1629.368,1208.59C1638.984,1208.59 1646.791,1200.784 1646.791,1191.168C1646.791,1181.552 1638.984,1173.745 1629.368,1173.745ZM1671.673,1216.769C1662.057,1216.769 1654.25,1224.576 1654.25,1234.191C1654.25,1243.807 1662.057,1251.614 1671.673,1251.614C1681.288,1251.614 1689.095,1243.807 1689.095,1234.191C1689.095,1224.576 1681.288,1216.769 1671.673,1216.769ZM1586.56,1216.769C1576.944,1216.769 1569.137,1224.576 1569.137,1234.191C1569.137,1243.807 1576.944,1251.614 1586.56,1251.614C1596.176,1251.614 1603.983,1243.807 1603.983,1234.191C1603.983,1224.576 1596.176,1216.769 1586.56,1216.769ZM353.679,1252.925L353.679,1276.615C353.679,1282.358 358.341,1287.021 364.085,1287.021L380.656,1287.021C386.446,1287.021 391.147,1282.32 391.147,1276.53L391.147,1252.925L418.873,1252.925C422.388,1252.925 425.242,1250.071 425.242,1246.556L425.242,1221.853C425.242,1218.323 422.377,1215.457 418.847,1215.457L391.147,1215.457L391.147,1187.731C391.147,1184.216 388.293,1181.362 384.777,1181.362L360.074,1181.362C356.544,1181.362 353.679,1184.227 353.679,1187.757L353.679,1215.457L329.989,1215.457C324.246,1215.457 319.583,1220.12 319.583,1225.863L319.583,1242.434C319.583,1248.225 324.284,1252.925 330.074,1252.925L353.679,1252.925ZM1491.737,1203.28C1462.993,1203.28 1439.657,1226.617 1439.657,1255.361C1439.657,1284.105 1462.993,1307.441 1491.737,1307.441C1520.481,1307.441 1543.818,1284.105 1543.818,1255.361C1543.818,1226.617 1520.481,1203.28 1491.737,1203.28ZM1584.585,1362.037C1584.585,1347.001 1572.377,1334.793 1557.34,1334.793L1452.216,1334.793C1437.179,1334.793 1424.972,1347.001 1424.972,1362.037L1424.972,1467.162C1424.972,1482.198 1437.179,1494.406 1452.216,1494.406L1557.34,1494.406C1572.377,1494.406 1584.585,1482.198 1584.585,1467.162L1584.585,1362.037Z"
|
||||||
|
fill="white" stroke="white" strokeWidth="1"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.883612,0,0,0.904278,106.86691,103.779729)">
|
||||||
|
<path
|
||||||
|
d="M1975,773.67L1975,1186.72C1975,1302.777 1878.573,1397 1759.802,1397L272.452,1397C154.645,1397 59,1303.541 59,1188.426L59,774.189C59,703.439 117.783,646 190.187,646L1844.344,646C1916.455,646 1975,703.207 1975,773.67Z"
|
||||||
|
fill="none" stroke="white" strokeWidth="64.9"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1,0,0,1,-0,-24.728814)">
|
||||||
|
<path
|
||||||
|
d="M323,1000L323,891.398C323,841.926 363.195,801.76 412.704,801.76L640.543,801.76L479.436,640.773C444.428,605.791 444.428,548.988 479.436,514.006L563.141,430.363C598.149,395.381 654.994,395.381 690.002,430.363L851.108,591.351L851.108,363.68C851.108,314.208 891.303,274.042 940.812,274.042L1059.188,274.042C1108.697,274.042 1148.892,314.208 1148.892,363.68L1148.892,591.351L1309.998,430.363C1345.006,395.381 1401.851,395.381 1436.859,430.363L1520.564,514.006C1555.572,548.988 1555.572,605.791 1520.564,640.773L1359.457,801.76L1587.296,801.76C1636.805,801.76 1677,841.926 1677,891.398L1677,1000L1180.034,1000C1184.251,984.222 1186.5,967.643 1186.5,950.542C1186.5,844.971 1100.789,759.26 995.218,759.26C889.646,759.26 803.935,844.971 803.935,950.542C803.935,967.643 806.184,984.222 810.401,1000L323,1000Z"
|
||||||
|
fill="white" stroke="white" strokeWidth="50"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M323,1000L323,891.398C323,841.926 363.195,801.76 412.704,801.76L640.543,801.76L479.436,640.773C444.428,605.791 444.428,548.988 479.436,514.006L563.141,430.363C598.149,395.381 654.994,395.381 690.002,430.363L851.108,591.351L851.108,363.68C851.108,314.208 891.303,274.042 940.812,274.042L1059.188,274.042C1108.697,274.042 1148.892,314.208 1148.892,363.68L1148.892,591.351L1309.998,430.363C1345.006,395.381 1401.851,395.381 1436.859,430.363L1520.564,514.006C1555.572,548.988 1555.572,605.791 1520.564,640.773L1359.457,801.76L1587.296,801.76C1636.805,801.76 1677,841.926 1677,891.398L1677,1000L1180.034,1000C1184.251,984.222 1186.5,967.643 1186.5,950.542C1186.5,844.971 1100.789,759.26 995.218,759.26C889.646,759.26 803.935,844.971 803.935,950.542C803.935,967.643 806.184,984.222 810.401,1000L323,1000Z"
|
||||||
|
fill="none" stroke="white" strokeWidth="50"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default definePlugin(() => {
|
export default definePlugin(() => {
|
||||||
return {
|
return {
|
||||||
name: "DeckyVault",
|
name: "DeckyVault",
|
||||||
titleView: <div className={staticClasses.Title}>DeckyVault</div>,
|
titleView: <div className={staticClasses.Title}>DeckyVault</div>,
|
||||||
content: <Content />,
|
content: <Content />,
|
||||||
icon: <FaDatabase />,
|
icon: <DeckyVaultIcon />,
|
||||||
alwaysRender: false,
|
alwaysRender: false,
|
||||||
onDismount() {
|
onDismount() {
|
||||||
console.log("[DeckyVault] Plugin unloading")
|
console.log("[DeckyVault] Plugin unloading")
|
||||||
|
|||||||
@@ -74,3 +74,26 @@ export const testApiKey = callable<[apiKey: string, baseUrl?: string], {
|
|||||||
valid: boolean
|
valid: boolean
|
||||||
error?: string
|
error?: string
|
||||||
}>("test_api_key")
|
}>("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")
|
||||||
Reference in New Issue
Block a user