wraith/frontend/src/layouts/UnlockLayout.vue
Vantz Stockwell 8a096d7f7b
Some checks failed
Build & Sign Wraith / Build Windows + Sign (push) Has been cancelled
Wraith v0.1.0 — Desktop SSH + RDP + SFTP Client
Go + Wails v3 + Vue 3 + SQLite + FreeRDP3 (purego)
183 tests, 76 source files, 9,910 lines of code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 08:19:29 -04:00

123 lines
4.1 KiB
Vue

<template>
<div class="h-screen w-screen flex items-center justify-center bg-[var(--wraith-bg-primary)]">
<div class="w-full max-w-sm px-6">
<!-- Branding -->
<div class="text-center mb-8">
<h1 class="text-4xl font-bold tracking-widest text-[var(--wraith-accent-blue)]">
WRAITH
</h1>
<p class="text-[var(--wraith-text-secondary)] mt-2 text-sm">
{{ appStore.isFirstRun ? "Create a master password" : "Enter your master password" }}
</p>
</div>
<!-- Card -->
<form
class="bg-[var(--wraith-bg-secondary)] border border-[var(--wraith-border)] rounded-lg p-6 space-y-4"
@submit.prevent="handleSubmit"
>
<!-- Error -->
<div
v-if="appStore.error"
class="text-sm text-[var(--wraith-accent-red)] bg-[var(--wraith-accent-red)]/10 border border-[var(--wraith-accent-red)]/20 rounded px-3 py-2"
>
{{ appStore.error }}
</div>
<!-- Password -->
<div>
<label class="block text-xs text-[var(--wraith-text-secondary)] mb-1.5">
Master Password
</label>
<input
ref="passwordInput"
v-model="password"
type="password"
autocomplete="current-password"
placeholder="Enter password..."
class="w-full px-3 py-2 text-sm rounded bg-[var(--wraith-bg-tertiary)] border border-[var(--wraith-border)] text-[var(--wraith-text-primary)] placeholder-[var(--wraith-text-muted)] outline-none focus:border-[var(--wraith-accent-blue)] transition-colors"
@input="appStore.clearError()"
/>
</div>
<!-- Confirm password (first run only) -->
<div v-if="appStore.isFirstRun">
<label class="block text-xs text-[var(--wraith-text-secondary)] mb-1.5">
Confirm Password
</label>
<input
v-model="confirmPassword"
type="password"
autocomplete="new-password"
placeholder="Confirm password..."
class="w-full px-3 py-2 text-sm rounded bg-[var(--wraith-bg-tertiary)] border border-[var(--wraith-border)] text-[var(--wraith-text-primary)] placeholder-[var(--wraith-text-muted)] outline-none focus:border-[var(--wraith-accent-blue)] transition-colors"
@input="appStore.clearError()"
/>
</div>
<!-- Submit -->
<button
type="submit"
:disabled="submitting"
class="w-full py-2 text-sm font-medium rounded bg-[var(--wraith-accent-blue)] text-white hover:opacity-90 disabled:opacity-50 transition-opacity cursor-pointer disabled:cursor-not-allowed"
>
<span v-if="submitting">{{ appStore.isFirstRun ? "Creating..." : "Unlocking..." }}</span>
<span v-else>{{ appStore.isFirstRun ? "Create Vault" : "Unlock" }}</span>
</button>
</form>
<!-- Version -->
<p class="text-center text-xs text-[var(--wraith-text-muted)] mt-4">
v1.0.0-alpha
</p>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from "vue";
import { useAppStore } from "@/stores/app.store";
const appStore = useAppStore();
const password = ref("");
const confirmPassword = ref("");
const submitting = ref(false);
const passwordInput = ref<HTMLInputElement | null>(null);
onMounted(() => {
passwordInput.value?.focus();
});
async function handleSubmit(): Promise<void> {
if (!password.value) {
appStore.error = "Password is required";
return;
}
if (appStore.isFirstRun) {
if (password.value.length < 8) {
appStore.error = "Password must be at least 8 characters";
return;
}
if (password.value !== confirmPassword.value) {
appStore.error = "Passwords do not match";
return;
}
}
submitting.value = true;
try {
if (appStore.isFirstRun) {
await appStore.createVault(password.value);
} else {
await appStore.unlock(password.value);
}
} catch {
// Error is set in the store
} finally {
submitting.value = false;
}
}
</script>