Introduces backend API endpoints for ViGEmBus status and installation, updates Windows build scripts to handle ViGEmBus versioning and installer download, and integrates ViGEmBus status and installation controls into the web UI. Removes legacy PowerShell scripts for gamepad driver management and related NSIS installer commands.
412 lines
14 KiB
HTML
412 lines
14 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en" data-bs-theme="auto">
|
|
|
|
<head>
|
|
<%- header %>
|
|
<style>
|
|
.troubleshooting-logs {
|
|
white-space: pre;
|
|
font-family: monospace;
|
|
overflow: auto;
|
|
max-height: 500px;
|
|
min-height: 500px;
|
|
font-size: 16px;
|
|
position: relative;
|
|
}
|
|
|
|
.copy-icon {
|
|
position: absolute;
|
|
top: 8px;
|
|
right: 8px;
|
|
padding: 8px;
|
|
cursor: pointer;
|
|
color: rgba(0, 0, 0, 1);
|
|
appearance: none;
|
|
border: none;
|
|
background: none;
|
|
}
|
|
|
|
.copy-icon:hover {
|
|
color: rgba(0, 0, 0, 0.75);
|
|
}
|
|
|
|
.copy-icon:active {
|
|
color: rgba(0, 0, 0, 1);
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body id="app" v-cloak>
|
|
<Navbar></Navbar>
|
|
<div class="container">
|
|
<h1 class="my-4">{{ $t('troubleshooting.troubleshooting') }}</h1>
|
|
<!-- ViGEmBus Installation -->
|
|
<div class="card p-2 my-4" v-if="platform === 'windows' && controllerEnabled">
|
|
<div class="card-body">
|
|
<h2 id="vigembus">{{ $t('troubleshooting.vigembus_install') }}</h2>
|
|
<br>
|
|
<p style="white-space: pre-line">{{ $t('troubleshooting.vigembus_desc') }}</p>
|
|
<div v-if="vigembus.installed && vigembus.version">
|
|
<p><strong>{{ $t('troubleshooting.vigembus_current_version') }}:</strong> {{ vigembus.version }}</p>
|
|
<div class="alert alert-success" v-if="vigembus.version_compatible">
|
|
{{ $t('troubleshooting.vigembus_compatible') }}
|
|
</div>
|
|
<div class="alert alert-danger" v-if="!vigembus.version_compatible">
|
|
{{ $t('troubleshooting.vigembus_incompatible') }}
|
|
</div>
|
|
</div>
|
|
<div class="alert alert-warning" v-else-if="!vigembus.installed">
|
|
{{ $t('troubleshooting.vigembus_not_installed') }}
|
|
</div>
|
|
<div class="alert alert-success" v-if="vigemBusInstallStatus === true">
|
|
{{ $t('troubleshooting.vigembus_install_success') }}
|
|
</div>
|
|
<div class="alert alert-danger" v-if="vigemBusInstallStatus === false">
|
|
{{ vigemBusInstallError || $t('troubleshooting.vigembus_install_error') }}
|
|
</div>
|
|
<div>
|
|
<button
|
|
:class="vigembus.installed && vigembus.version === vigembus.packaged_version ? 'btn btn-danger' : 'btn btn-primary'"
|
|
:disabled="vigemBusInstallPressed"
|
|
@click="installViGEmBus">
|
|
<template v-if="vigembus.installed && vigembus.version === vigembus.packaged_version">
|
|
{{ $t('troubleshooting.vigembus_force_reinstall_button', { version: vigembus.packaged_version }) }}
|
|
</template>
|
|
<template v-else>
|
|
{{ $t('troubleshooting.vigembus_install_button', { version: vigembus.packaged_version }) }}
|
|
</template>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- Force Close App -->
|
|
<div class="card p-2 my-4">
|
|
<div class="card-body">
|
|
<h2 id="close_apps">{{ $t('troubleshooting.force_close') }}</h2>
|
|
<br>
|
|
<p>{{ $t('troubleshooting.force_close_desc') }}</p>
|
|
<div class="alert alert-success" v-if="closeAppStatus === true">
|
|
{{ $t('troubleshooting.force_close_success') }}
|
|
</div>
|
|
<div class="alert alert-danger" v-if="closeAppStatus === false">
|
|
{{ $t('troubleshooting.force_close_error') }}
|
|
</div>
|
|
<div>
|
|
<button class="btn btn-warning" :disabled="closeAppPressed" @click="closeApp">
|
|
{{ $t('troubleshooting.force_close') }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- Restart Sunshine -->
|
|
<div class="card p-2 my-4">
|
|
<div class="card-body">
|
|
<h2 id="restart">{{ $t('troubleshooting.restart_sunshine') }}</h2>
|
|
<br>
|
|
<p>{{ $t('troubleshooting.restart_sunshine_desc') }}</p>
|
|
<div class="alert alert-success" v-if="restartPressed === true">
|
|
{{ $t('troubleshooting.restart_sunshine_success') }}
|
|
</div>
|
|
<div>
|
|
<button class="btn btn-warning" :disabled="restartPressed" @click="restart">
|
|
{{ $t('troubleshooting.restart_sunshine') }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- Reset persistent display device settings -->
|
|
<div class="card p-2 my-4" v-if="platform === 'windows'">
|
|
<div class="card-body">
|
|
<h2 id="dd_reset">{{ $t('troubleshooting.dd_reset') }}</h2>
|
|
<br>
|
|
<p style="white-space: pre-line">{{ $t('troubleshooting.dd_reset_desc') }}</p>
|
|
<div class="alert alert-success" v-if="ddResetStatus === true">
|
|
{{ $t('troubleshooting.dd_reset_success') }}
|
|
</div>
|
|
<div class="alert alert-danger" v-if="ddResetStatus === false">
|
|
{{ $t('troubleshooting.dd_reset_error') }}
|
|
</div>
|
|
<div>
|
|
<button class="btn btn-warning" :disabled="ddResetPressed" @click="ddResetPersistence">
|
|
{{ $t('troubleshooting.dd_reset') }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- Unpair Clients -->
|
|
<div class="card my-4">
|
|
<div class="card-body">
|
|
<div class="p-2">
|
|
<div class="d-flex justify-content-end align-items-center">
|
|
<h2 id="unpair" class="text-center me-auto">{{ $t('troubleshooting.unpair_title') }}</h2>
|
|
<button class="btn btn-danger" :disabled="unpairAllPressed" @click="unpairAll">
|
|
{{ $t('troubleshooting.unpair_all') }}
|
|
</button>
|
|
</div>
|
|
<br />
|
|
<p class="mb-0">{{ $t('troubleshooting.unpair_desc') }}</p>
|
|
<div id="apply-alert" class="alert alert-success d-flex align-items-center mt-3" :style="{ 'display': (showApplyMessage ? 'flex !important': 'none !important') }">
|
|
<div class="me-2"><b>{{ $t('_common.success') }}</b> {{ $t('troubleshooting.unpair_single_success') }}</div>
|
|
<button class="btn btn-success ms-auto apply" @click="clickedApplyBanner">{{ $t('_common.dismiss') }}</button>
|
|
</div>
|
|
<div class="alert alert-success mt-3" v-if="unpairAllStatus === true">
|
|
{{ $t('troubleshooting.unpair_all_success') }}
|
|
</div>
|
|
<div class="alert alert-danger mt-3" v-if="unpairAllStatus === false">
|
|
{{ $t('troubleshooting.unpair_all_error') }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<ul id="client-list" class="list-group list-group-flush list-group-item-light" v-if="clients && clients.length > 0">
|
|
<div v-for="client in clients" class="list-group-item d-flex">
|
|
<div class="p-2 flex-grow-1">{{ client.name !== "" ? client.name : $t('troubleshooting.unpair_single_unknown') }}</div>
|
|
<div class="me-2 ms-auto btn btn-danger" @click="unpairSingle(client.uuid)"><i class="fas fa-trash"></i></div>
|
|
</div>
|
|
</ul>
|
|
<ul v-else class="list-group list-group-flush list-group-item-light">
|
|
<div class="list-group-item p-3 text-center"><em>{{ $t('troubleshooting.unpair_single_no_devices') }}</em></div>
|
|
</ul>
|
|
|
|
</div>
|
|
<!-- Logs -->
|
|
<div class="card p-2 my-4">
|
|
<div class="card-body">
|
|
<h2 id="logs">{{ $t('troubleshooting.logs') }}</h2>
|
|
<br>
|
|
<div class="d-flex justify-content-between align-items-baseline py-2">
|
|
<p>{{ $t('troubleshooting.logs_desc') }}</p>
|
|
<input type="text" class="form-control" v-model="logFilter" :placeholder="$t('troubleshooting.logs_find')" style="width: 300px">
|
|
</div>
|
|
<div>
|
|
<div class="troubleshooting-logs">
|
|
<button class="copy-icon"><i class="fas fa-copy " @click="copyLogs"></i></button>{{actualLogs}}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script type="module">
|
|
import { createApp } from 'vue'
|
|
import { initApp } from './init'
|
|
import Navbar from './Navbar.vue'
|
|
|
|
const app = createApp({
|
|
components: {
|
|
Navbar
|
|
},
|
|
data() {
|
|
return {
|
|
clients: [],
|
|
closeAppPressed: false,
|
|
closeAppStatus: null,
|
|
ddResetPressed: false,
|
|
ddResetStatus: null,
|
|
logs: 'Loading...',
|
|
logFilter: null,
|
|
logInterval: null,
|
|
restartPressed: false,
|
|
showApplyMessage: false,
|
|
platform: "",
|
|
controllerEnabled: false,
|
|
unpairAllPressed: false,
|
|
unpairAllStatus: null,
|
|
vigembus: {
|
|
installed: false,
|
|
version: '',
|
|
version_compatible: false,
|
|
packaged_version: '',
|
|
},
|
|
vigemBusInstallPressed: false,
|
|
vigemBusInstallStatus: null,
|
|
vigemBusInstallError: null,
|
|
};
|
|
},
|
|
computed: {
|
|
actualLogs() {
|
|
if (!this.logFilter) return this.logs;
|
|
let lines = this.logs.split("\n");
|
|
lines = lines.filter(x => x.indexOf(this.logFilter) !== -1);
|
|
return lines.join("\n");
|
|
}
|
|
},
|
|
created() {
|
|
fetch("/api/config")
|
|
.then((r) => r.json())
|
|
.then((r) => {
|
|
this.platform = r.platform;
|
|
this.controllerEnabled = r.controller !== "disabled";
|
|
// Fetch ViGEmBus status only on Windows when gamepad is enabled
|
|
if (this.platform === 'windows' && this.controllerEnabled) {
|
|
this.refreshViGEmBusStatus();
|
|
}
|
|
});
|
|
|
|
this.logInterval = setInterval(() => {
|
|
this.refreshLogs();
|
|
}, 5000);
|
|
this.refreshLogs();
|
|
this.refreshClients();
|
|
},
|
|
beforeDestroy() {
|
|
clearInterval(this.logInterval);
|
|
},
|
|
methods: {
|
|
refreshLogs() {
|
|
fetch("./api/logs",)
|
|
.then((r) => r.text())
|
|
.then((r) => {
|
|
this.logs = r;
|
|
});
|
|
},
|
|
closeApp() {
|
|
this.closeAppPressed = true;
|
|
fetch("./api/apps/close", {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json"
|
|
} })
|
|
.then((r) => r.json())
|
|
.then((r) => {
|
|
this.closeAppPressed = false;
|
|
this.closeAppStatus = r.status;
|
|
setTimeout(() => {
|
|
this.closeAppStatus = null;
|
|
}, 5000);
|
|
});
|
|
},
|
|
unpairAll() {
|
|
this.unpairAllPressed = true;
|
|
fetch("./api/clients/unpair-all", {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json"
|
|
}
|
|
})
|
|
.then((r) => r.json())
|
|
.then((r) => {
|
|
this.unpairAllPressed = false;
|
|
this.unpairAllStatus = r.status;
|
|
setTimeout(() => {
|
|
this.unpairAllStatus = null;
|
|
}, 5000);
|
|
this.refreshClients();
|
|
});
|
|
},
|
|
unpairSingle(uuid) {
|
|
fetch("./api/clients/unpair", {
|
|
method: "POST",
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({ uuid })
|
|
}).then(() => {
|
|
this.showApplyMessage = true;
|
|
this.refreshClients();
|
|
});
|
|
},
|
|
refreshClients() {
|
|
fetch("./api/clients/list")
|
|
.then((response) => response.json())
|
|
.then((response) => {
|
|
const clientList = document.querySelector("#client-list");
|
|
if (response.status === true && response.named_certs && response.named_certs.length) {
|
|
this.clients = response.named_certs.sort((a, b) => {
|
|
return (a.name.toLowerCase() > b.name.toLowerCase() || a.name === "" ? 1 : -1)
|
|
});
|
|
} else {
|
|
this.clients = [];
|
|
}
|
|
});
|
|
},
|
|
clickedApplyBanner() {
|
|
this.showApplyMessage = false;
|
|
},
|
|
copyLogs() {
|
|
navigator.clipboard.writeText(this.actualLogs);
|
|
},
|
|
restart() {
|
|
this.restartPressed = true;
|
|
setTimeout(() => {
|
|
this.restartPressed = false;
|
|
}, 5000);
|
|
fetch("./api/restart", {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json"
|
|
}
|
|
});
|
|
},
|
|
ddResetPersistence() {
|
|
this.ddResetPressed = true;
|
|
fetch("/api/reset-display-device-persistence", {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json"
|
|
}
|
|
})
|
|
.then((r) => r.json())
|
|
.then((r) => {
|
|
this.ddResetPressed = false;
|
|
this.ddResetStatus = r.status;
|
|
setTimeout(() => {
|
|
this.ddResetStatus = null;
|
|
}, 5000);
|
|
});
|
|
},
|
|
refreshViGEmBusStatus() {
|
|
fetch("/api/vigembus/status")
|
|
.then((r) => r.json())
|
|
.then((r) => {
|
|
this.vigembus = {
|
|
installed: r.installed || false,
|
|
version: r.version || '',
|
|
version_compatible: r.version_compatible || false,
|
|
packaged_version: r.packaged_version,
|
|
};
|
|
})
|
|
.catch((err) => {
|
|
console.error("Failed to fetch ViGEmBus status:", err);
|
|
});
|
|
},
|
|
installViGEmBus() {
|
|
this.vigemBusInstallPressed = true;
|
|
this.vigemBusInstallStatus = null;
|
|
this.vigemBusInstallError = null;
|
|
fetch("/api/vigembus/install", {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json"
|
|
}
|
|
})
|
|
.then((r) => r.json())
|
|
.then((r) => {
|
|
this.vigemBusInstallPressed = false;
|
|
this.vigemBusInstallStatus = r.status;
|
|
if (!r.status && r.error) {
|
|
this.vigemBusInstallError = r.error;
|
|
}
|
|
setTimeout(() => {
|
|
this.vigemBusInstallStatus = null;
|
|
this.vigemBusInstallError = null;
|
|
}, 10000);
|
|
// Refresh status after installation attempt
|
|
this.refreshViGEmBusStatus();
|
|
})
|
|
.catch((err) => {
|
|
this.vigemBusInstallPressed = false;
|
|
this.vigemBusInstallStatus = false;
|
|
this.vigemBusInstallError = err.message;
|
|
setTimeout(() => {
|
|
this.vigemBusInstallStatus = null;
|
|
this.vigemBusInstallError = null;
|
|
}, 10000);
|
|
});
|
|
},
|
|
},
|
|
});
|
|
|
|
initApp(app);
|
|
</script>
|
|
|
|
</body>
|