WebUI: Support starting apps from local client

This commit is contained in:
Yukino Song
2025-06-05 19:43:14 +08:00
parent f262852838
commit 0f9e7aa17f
5 changed files with 27 additions and 6 deletions

View File

@@ -541,6 +541,8 @@ namespace confighttp {
nlohmann::json file_tree = nlohmann::json::parse(content); nlohmann::json file_tree = nlohmann::json::parse(content);
file_tree["current_app"] = proc::proc.get_running_app_uuid(); file_tree["current_app"] = proc::proc.get_running_app_uuid();
file_tree["host_uuid"] = http::unique_id;
file_tree["host_name"] = config::nvhttp.sunshine_name;
send_response(response, file_tree); send_response(response, file_tree);
} catch (std::exception &e) { } catch (std::exception &e) {

View File

@@ -68,6 +68,10 @@
vertical-align: top; vertical-align: top;
} }
.actions-col {
min-width: 150px;
}
.pre-wrap { .pre-wrap {
white-space: pre-wrap; white-space: pre-wrap;
} }
@@ -91,7 +95,7 @@
<thead> <thead>
<tr> <tr>
<th scope="col">{{ $t('apps.name') }}</th> <th scope="col">{{ $t('apps.name') }}</th>
<th scope="col" class="text-end">{{ $t('apps.actions') }}</th> <th scope="col" class="text-end actions-col">{{ $t('apps.actions') }}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -109,10 +113,10 @@
> >
<td>{{app.name || ' '}}</td> <td>{{app.name || ' '}}</td>
<td v-if="app.uuid" class="text-end"> <td v-if="app.uuid" class="text-end">
<button class="btn btn-primary me-2" :disabled="actionDisabled" @click="editApp(app)"> <button class="btn btn-primary me-1" :disabled="actionDisabled" @click="editApp(app)">
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
</button> </button>
<button class="btn btn-danger me-2" :disabled="actionDisabled" @click="showDeleteForm(app)"> <button class="btn btn-danger me-1" :disabled="actionDisabled" @click="showDeleteForm(app)">
<i class="fas fa-trash"></i> <i class="fas fa-trash"></i>
</button> </button>
<button class="btn btn-warning" :disabled="actionDisabled" @click="closeApp()" v-if="currentApp === app.uuid"> <button class="btn btn-warning" :disabled="actionDisabled" @click="closeApp()" v-if="currentApp === app.uuid">
@@ -548,6 +552,8 @@
platform: "", platform: "",
currentApp: "", currentApp: "",
draggingApp: -1, draggingApp: -1,
hostName: "",
hostUUID: "",
listReordered: false listReordered: false
}; };
}, },
@@ -666,6 +672,8 @@
.then(r => { .then(r => {
this.apps = r.apps.filter(i => i.uuid).map(i => ({...i, launching: false, dragover: false})); this.apps = r.apps.filter(i => i.uuid).map(i => ({...i, launching: false, dragover: false}));
this.currentApp = r.current_app; this.currentApp = r.current_app;
this.hostName = r.host_name;
this.hostUUID = r.host_uuid;
this.listReordered = false; this.listReordered = false;
}); });
}, },
@@ -674,6 +682,15 @@
this.showEditForm = true; this.showEditForm = true;
}, },
launchApp(app) { launchApp(app) {
const isLocalHost = ['localhost', '127.0.0.1', '[::1]'].indexOf(location.hostname) >= 0
if (!isLocalHost && confirm(this.$t('apps.launch_local_client'))) {
const link = document.createElement('a');
link.href = `art://launch?host_uuid=${this.hostUUID}&host_name=${this.hostName}&app_uuid=${app.uuid}&app_name=${app.name}`;
link.click();
return;
}
if (confirm(this.$t('apps.launch_warning'))) { if (confirm(this.$t('apps.launch_warning'))) {
this.actionDisabled = true; this.actionDisabled = true;
fetch("./api/apps/launch", { fetch("./api/apps/launch", {

View File

@@ -471,7 +471,7 @@
this.otpStatus = 'success' this.otpStatus = 'success'
this.otpMessage = this.i18n.t('pin.otp_success') this.otpMessage = this.i18n.t('pin.otp_success')
const isLocalHost = ['localhost', '127.0.0.1', '[::1]'].indexOf(location.hostname) < 0 const isLocalHost = ['localhost', '127.0.0.1', '[::1]'].indexOf(location.hostname) >= 0
if (hostManuallySet) { if (hostManuallySet) {
Object.assign(this, hostInfoCache); Object.assign(this, hostInfoCache);
@@ -479,7 +479,7 @@
this.hostAddr = resp.ip this.hostAddr = resp.ip
this.hostPort = parseInt(location.port, 10) - 1 this.hostPort = parseInt(location.port, 10) - 1
if (isLocalHost) { if (!isLocalHost) {
this.hostAddr = location.hostname this.hostAddr = location.hostname
} }
@@ -498,7 +498,7 @@
resetOTPTimeout = null resetOTPTimeout = null
}, 3 * 60 * 1000) }, 3 * 60 * 1000)
if (isLocalHost) { if (!isLocalHost) {
setTimeout(() => { setTimeout(() => {
if (window.confirm(this.i18n.t('pin.otp_pair_now'))) { if (window.confirm(this.i18n.t('pin.otp_pair_now'))) {
window.open(this.deepLink); window.open(this.deepLink);

View File

@@ -96,6 +96,7 @@
"image": "Image", "image": "Image",
"image_desc": "Application icon/picture/image path that will be sent to client. Image must be a PNG file. If not set, Apollo will send default box image.", "image_desc": "Application icon/picture/image path that will be sent to client. Image must be a PNG file. If not set, Apollo will send default box image.",
"launch": "Launch", "launch": "Launch",
"launch_local_client": "Do you want to launch the app from local client on this device?",
"launch_warning": "Are you sure you want to launch this app? This will terminate the currently running app.", "launch_warning": "Are you sure you want to launch this app? This will terminate the currently running app.",
"launch_failed": "App launch failed: ", "launch_failed": "App launch failed: ",
"loading": "Loading...", "loading": "Loading...",

View File

@@ -96,6 +96,7 @@
"image": "图片", "image": "图片",
"image_desc": "发送到客户端的应用程序图标/图片/图像的路径。图片必须是 PNG 文件。如果未设置Apollo 将发送默认图片。", "image_desc": "发送到客户端的应用程序图标/图片/图像的路径。图片必须是 PNG 文件。如果未设置Apollo 将发送默认图片。",
"launch": "启动", "launch": "启动",
"launch_local_client": "要通过此设备上的客户端启动应用吗?",
"launch_warning": "确定要启动此应用吗?这将会终止当前已启动的应用。", "launch_warning": "确定要启动此应用吗?这将会终止当前已启动的应用。",
"launch_success": "应用启动成功!", "launch_success": "应用启动成功!",
"launch_failed": "应用启动失败:", "launch_failed": "应用启动失败:",