account: polish Wolf card

This commit is contained in:
codex 2026-05-22 03:38:29 -03:00
parent 9ff34e7661
commit 8126bd3c96
2 changed files with 132 additions and 14 deletions

View File

@ -1,7 +1,10 @@
<template> <template>
<div class="card module wolf-card" :style="{ order: 0 }"> <div class="card module wolf-card" :style="{ order: 0 }">
<div class="module-head"> <div class="module-head">
<h2>Wolf</h2> <div>
<h2>Wolf</h2>
<p class="muted wolf-subtitle">Game streaming access for Moonlight.</p>
</div>
<span <span
class="pill mono" class="pill mono"
:class=" :class="
@ -17,36 +20,54 @@
{{ wolf.loading ? "loading..." : wolf.status }} {{ wolf.loading ? "loading..." : wolf.status }}
</span> </span>
</div> </div>
<div class="kv"> <div class="wolf-connection">
<div class="row"> <div>
<span class="k mono">Moonlight host</span> <span class="k mono">Moonlight host</span>
<span class="v mono">{{ wolf.moonlightHost }}</span> <div class="wolf-host mono">{{ wolf.moonlightHost }}</div>
</div> </div>
<button class="copy mono" type="button" @click="copyHost">
{{ copiedHost ? "copied" : "copy" }}
</button>
</div>
<div class="wolf-summary">
<div class="wolf-summary-item">
<span class="k mono">GPU</span>
<strong class="mono">{{ wolf.gpuPriority }}</strong>
</div>
<div class="wolf-summary-item">
<span class="k mono">Firewall</span>
<strong class="mono" :class="currentIpUnlocked ? 'ok-text' : 'warn-text'">
{{ currentIpUnlocked ? "open" : "locked" }}
</strong>
</div>
<div class="wolf-summary-item">
<span class="k mono">Sessions</span>
<strong class="mono">{{ wolf.sessions.length }}</strong>
</div>
</div>
<div class="kv wolf-detail">
<div class="row"> <div class="row">
<span class="k mono">Your IP</span> <span class="k mono">Current IP</span>
<span class="v mono">{{ wolf.sourceIp || "unknown" }}</span> <span class="v mono">{{ wolf.sourceIp || "unknown" }}</span>
</div> </div>
<div class="row"> <div class="row">
<span class="k mono">GPU priority</span> <span class="k mono">Active unlocks</span>
<span class="v mono">{{ wolf.gpuPriority }}</span>
</div>
<div class="row">
<span class="k mono">Firewall unlocks</span>
<span class="v mono">{{ wolf.activeUnlocks.length }}</span> <span class="v mono">{{ wolf.activeUnlocks.length }}</span>
</div> </div>
<div class="row"> <div class="row">
<span class="k mono">Paired devices</span> <span class="k mono">Paired devices</span>
<span class="v mono">{{ wolf.clients.map((client) => client.name).join(", ") || "none" }}</span> <span class="v mono">{{ pairedDeviceNames }}</span>
</div> </div>
</div> </div>
<div class="actions"> <div class="actions">
<button class="primary" type="button" :disabled="wolf.unlocking" @click="$emit('unlock')"> <button class="primary" type="button" :disabled="wolf.unlocking" @click="$emit('unlock')">
{{ wolf.unlocking ? "Unlocking..." : "Unlock Moonlight" }} {{ wolf.unlocking ? "Unlocking..." : currentIpUnlocked ? "Refresh unlock" : "Unlock Moonlight" }}
</button> </button>
<button class="copy mono" type="button" :disabled="wolf.loading" @click="$emit('refresh')">Refresh</button> <button class="copy mono" type="button" :disabled="wolf.loading" @click="$emit('refresh')">Refresh</button>
</div> </div>
<div class="hint mono">Use the host above in Moonlight after unlocking your current IP.</div>
<div v-if="wolf.pendingPairRequests.length" class="secret-box"> <div v-if="wolf.pendingPairRequests.length" class="secret-box">
<div class="secret-head"> <div class="secret-head">
@ -72,6 +93,7 @@
<div v-if="wolf.canControlGpu" class="secret-box"> <div v-if="wolf.canControlGpu" class="secret-box">
<div class="secret-head"> <div class="secret-head">
<div class="pill mono">Admin</div> <div class="pill mono">Admin</div>
<span class="hint mono">GPU priority</span>
</div> </div>
<div class="wolf-controls"> <div class="wolf-controls">
<select v-model="wolf.selectedGame" class="input mono"> <select v-model="wolf.selectedGame" class="input mono">
@ -105,7 +127,9 @@
</template> </template>
<script setup> <script setup>
defineProps({ import { computed, ref } from "vue";
const props = defineProps({
wolf: { wolf: {
type: Object, type: Object,
required: true, required: true,
@ -113,6 +137,31 @@ defineProps({
}); });
defineEmits(["refresh", "unlock", "pair", "game-mode", "admin-unlock"]); defineEmits(["refresh", "unlock", "pair", "game-mode", "admin-unlock"]);
const copiedHost = ref(false);
const currentIpUnlocked = computed(() => {
const sourceIp = props.wolf.sourceIp;
if (!sourceIp) return false;
return props.wolf.activeUnlocks.some((unlock) => unlock?.ip === sourceIp);
});
const pairedDeviceNames = computed(() => {
const names = props.wolf.clients.map((client) => client.name).filter(Boolean);
return names.length ? names.join(", ") : "none";
});
async function copyHost() {
try {
await navigator.clipboard?.writeText(props.wolf.moonlightHost || "moonlight.bstein.dev");
} catch {
return;
}
copiedHost.value = true;
window.setTimeout(() => {
copiedHost.value = false;
}, 1600);
}
</script> </script>
<style scoped src="../styles/account.css"></style> <style scoped src="../styles/account.css"></style>

View File

@ -88,6 +88,66 @@ h1 {
margin: 10px 0 0; margin: 10px 0 0;
} }
.wolf-subtitle {
margin-top: 4px;
}
.wolf-connection {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-top: 14px;
padding: 12px;
border: 1px solid rgba(125, 208, 255, 0.22);
border-radius: 12px;
background: rgba(125, 208, 255, 0.06);
}
.wolf-host {
margin-top: 4px;
color: var(--accent-cyan);
overflow-wrap: anywhere;
}
.wolf-summary {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 8px;
margin-top: 10px;
}
.wolf-summary-item {
min-width: 0;
padding: 10px;
border: 1px solid var(--card-border);
border-radius: 12px;
background: rgba(255, 255, 255, 0.025);
}
.wolf-summary-item span,
.wolf-summary-item strong {
display: block;
}
.wolf-summary-item strong {
margin-top: 4px;
color: var(--text-strong);
overflow-wrap: anywhere;
}
.wolf-detail {
margin-top: 10px;
}
.ok-text {
color: rgba(120, 255, 160, 0.95) !important;
}
.warn-text {
color: rgba(255, 207, 104, 0.95) !important;
}
.kv { .kv {
margin-top: 12px; margin-top: 12px;
border: 1px solid var(--card-border); border: 1px solid var(--card-border);
@ -273,6 +333,15 @@ button.primary {
align-items: stretch; align-items: stretch;
} }
.wolf-connection {
align-items: stretch;
flex-direction: column;
}
.wolf-summary {
grid-template-columns: 1fr;
}
.pair-row, .pair-row,
.wolf-controls, .wolf-controls,
.manual-unlock { .manual-unlock {