From d67e183d7225a3778f6814ccd858c9824ef67b03 Mon Sep 17 00:00:00 2001 From: Vantz Stockwell Date: Tue, 17 Mar 2026 06:32:10 -0400 Subject: [PATCH] feat: master password unlock UI with first-run vault creation Add the unlock screen that gates entry to the main app. Includes app store (unlocked state, firstRun flag), a centered dark-themed unlock card with WRAITH branding, password validation for first-run vault creation, and conditional rendering in App.vue. Co-Authored-By: Claude Opus 4.6 (1M context) --- frontend/src/App.vue | 30 ++++++- frontend/src/env.d.ts | 7 ++ frontend/src/layouts/UnlockLayout.vue | 122 ++++++++++++++++++++++++++ frontend/src/stores/app.store.ts | 77 ++++++++++++++++ frontend/vite.config.ts | 6 ++ 5 files changed, 238 insertions(+), 4 deletions(-) create mode 100644 frontend/src/env.d.ts create mode 100644 frontend/src/layouts/UnlockLayout.vue create mode 100644 frontend/src/stores/app.store.ts diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 64d898f..52affe7 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -1,8 +1,30 @@ + + diff --git a/frontend/src/env.d.ts b/frontend/src/env.d.ts new file mode 100644 index 0000000..cd39087 --- /dev/null +++ b/frontend/src/env.d.ts @@ -0,0 +1,7 @@ +/// + +declare module "*.vue" { + import type { DefineComponent } from "vue"; + const component: DefineComponent; + export default component; +} diff --git a/frontend/src/layouts/UnlockLayout.vue b/frontend/src/layouts/UnlockLayout.vue new file mode 100644 index 0000000..8931322 --- /dev/null +++ b/frontend/src/layouts/UnlockLayout.vue @@ -0,0 +1,122 @@ + + + diff --git a/frontend/src/stores/app.store.ts b/frontend/src/stores/app.store.ts new file mode 100644 index 0000000..6cc3d37 --- /dev/null +++ b/frontend/src/stores/app.store.ts @@ -0,0 +1,77 @@ +import { defineStore } from "pinia"; +import { ref } from "vue"; + +/** + * Wraith application store. + * Manages unlock state, first-run detection, and vault operations. + * + * Once Wails v3 bindings are generated, the mock calls below will be + * replaced with actual WraithApp.IsFirstRun(), CreateVault(), Unlock(), etc. + */ +export const useAppStore = defineStore("app", () => { + const isUnlocked = ref(false); + const isFirstRun = ref(true); + const isLoading = ref(true); + const error = ref(null); + + /** Check whether the vault has been created before. */ + async function checkFirstRun(): Promise { + try { + // TODO: replace with Wails binding — WraithApp.IsFirstRun() + isFirstRun.value = true; + } catch { + isFirstRun.value = true; + } finally { + isLoading.value = false; + } + } + + /** Create a new vault with the given master password. */ + async function createVault(password: string): Promise { + error.value = null; + try { + // TODO: replace with Wails binding — WraithApp.CreateVault(password) + void password; + isFirstRun.value = false; + isUnlocked.value = true; + } catch (e) { + error.value = e instanceof Error ? e.message : "Failed to create vault"; + throw e; + } + } + + /** Unlock an existing vault with the master password. */ + async function unlock(password: string): Promise { + error.value = null; + try { + // TODO: replace with Wails binding — WraithApp.Unlock(password) + void password; + isUnlocked.value = true; + } catch (e) { + error.value = e instanceof Error ? e.message : "Invalid master password"; + throw e; + } + } + + /** Lock the vault (return to unlock screen). */ + function lock(): void { + isUnlocked.value = false; + } + + /** Clear the current error message. */ + function clearError(): void { + error.value = null; + } + + return { + isUnlocked, + isFirstRun, + isLoading, + error, + checkFirstRun, + createVault, + unlock, + lock, + clearError, + }; +}); diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 111aca4..2896b7c 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -1,7 +1,13 @@ import { defineConfig } from "vite"; import vue from "@vitejs/plugin-vue"; import tailwindcss from "@tailwindcss/vite"; +import { resolve } from "path"; export default defineConfig({ plugins: [vue(), tailwindcss()], + resolve: { + alias: { + "@": resolve(__dirname, "src"), + }, + }, });