diff --git a/build/bin/verstak-desktop b/build/bin/verstak-desktop index 2474682..def4f98 100755 Binary files a/build/bin/verstak-desktop and b/build/bin/verstak-desktop differ diff --git a/frontend/src/lib/shell/VaultSelection.svelte b/frontend/src/lib/shell/VaultSelection.svelte index 36cd03d..5a61a4a 100644 --- a/frontend/src/lib/shell/VaultSelection.svelte +++ b/frontend/src/lib/shell/VaultSelection.svelte @@ -16,41 +16,49 @@ appSettings = await App.GetAppSettings() || {}; recentVaults = appSettings.recentVaults || []; } catch (e) { - // App settings might fail if backend not ready — show selection anyway console.error('[VaultSelection] load settings:', e); } loading = false; }); + async function browseNewVault() { + const path = await App.SelectDirectory(); + if (path) { + newVaultPath = path; + } + } + + async function browseOpenVault() { + const path = await App.SelectVaultForOpen(); + if (path) { + openVaultPath = path; + } + } + async function createVault() { error = ''; if (!newVaultPath.trim()) { - error = 'Please enter a path for the new vault'; + error = 'Please enter or select a path for the new vault'; return; } creating = true; try { - // Step 1: Create the vault directory + metadata const createErr = await App.CreateVault(newVaultPath.trim()); if (createErr) { error = 'Create vault: ' + createErr; creating = false; return; } - // Step 2: Open it (registers capabilities, loads plugin state) const openErr = await App.OpenVault(newVaultPath.trim()); if (openErr) { error = 'Open vault: ' + openErr; creating = false; return; } - // Step 3: Save to app settings (set current + add to recent) const setErr = await App.SetCurrentVault(newVaultPath.trim()); if (setErr) { - // Vault is open but settings save failed — still proceed console.warn('[VaultSelection] SetCurrentVault:', setErr); } - // Success — notify app to transition to main UI window.dispatchEvent(new CustomEvent('verstak:vault-opened')); } catch (e) { error = String(e); @@ -61,19 +69,17 @@ async function openExistingVault() { error = ''; if (!openVaultPath.trim()) { - error = 'Please enter a path to an existing vault'; + error = 'Please enter or select a path to an existing vault'; return; } opening = true; try { - // Step 1: Open the vault const openErr = await App.OpenVault(openVaultPath.trim()); if (openErr) { error = 'Open vault: ' + openErr; opening = false; return; } - // Step 2: Save to app settings const setErr = await App.SetCurrentVault(openVaultPath.trim()); if (setErr) { console.warn('[VaultSelection] SetCurrentVault:', setErr); @@ -141,9 +147,14 @@ + + +
@@ -157,9 +168,14 @@ + +
+
@@ -196,7 +212,7 @@ padding: 2rem; } .vault-selection-inner { - max-width: 520px; + max-width: 560px; width: 100%; } .loading-text { @@ -257,6 +273,7 @@ .input-row { display: flex; gap: 0.5rem; + margin-bottom: 0.5rem; } .input-row input { flex: 1; @@ -274,16 +291,19 @@ .input-row input::placeholder { color: #666; } + .button-row { + display: flex; + justify-content: flex-end; + } .btn-primary { background: #4ecca3; color: #1a1a2e; border: none; - padding: 0.5rem 1rem; + padding: 0.5rem 1.25rem; border-radius: 6px; cursor: pointer; font-size: 0.85rem; font-weight: 600; - white-space: nowrap; } .btn-primary:hover:not(:disabled) { background: #3dbb92; @@ -292,6 +312,24 @@ opacity: 0.5; cursor: not-allowed; } + .btn-secondary { + background: #0f3460; + color: #a0a0b8; + border: 1px solid #1a3a5c; + padding: 0.5rem 0.75rem; + border-radius: 6px; + cursor: pointer; + font-size: 0.85rem; + white-space: nowrap; + } + .btn-secondary:hover:not(:disabled) { + background: #1a3a5c; + color: #e0e0f0; + } + .btn-secondary:disabled { + opacity: 0.5; + cursor: not-allowed; + } .recent-section { background: #16213e; border: 1px solid #0f3460; diff --git a/frontend/wailsjs/go/api/App.d.ts b/frontend/wailsjs/go/api/App.d.ts index 37676d2..806c87f 100755 --- a/frontend/wailsjs/go/api/App.d.ts +++ b/frontend/wailsjs/go/api/App.d.ts @@ -4,6 +4,7 @@ import {capability} from '../models'; import {api} from '../models'; import {permissions} from '../models'; import {plugin} from '../models'; +import {context} from '../models'; export function CloseVault():Promise; @@ -39,9 +40,13 @@ export function RecordDesiredPlugin(arg1:string,arg2:string,arg3:string):Promise export function ReloadPlugins():Promise; +export function SelectDirectory():Promise; + +export function SelectVaultForOpen():Promise; + export function SetCurrentVault(arg1:string):Promise; -export function Startup():Promise; +export function Startup(arg1:context.Context):Promise; export function UpdateAppSettings(arg1:Record):Promise; diff --git a/frontend/wailsjs/go/api/App.js b/frontend/wailsjs/go/api/App.js index 7bfb652..88bf554 100755 --- a/frontend/wailsjs/go/api/App.js +++ b/frontend/wailsjs/go/api/App.js @@ -70,12 +70,20 @@ export function ReloadPlugins() { return window['go']['api']['App']['ReloadPlugins'](); } +export function SelectDirectory() { + return window['go']['api']['App']['SelectDirectory'](); +} + +export function SelectVaultForOpen() { + return window['go']['api']['App']['SelectVaultForOpen'](); +} + export function SetCurrentVault(arg1) { return window['go']['api']['App']['SetCurrentVault'](arg1); } -export function Startup() { - return window['go']['api']['App']['Startup'](); +export function Startup(arg1) { + return window['go']['api']['App']['Startup'](arg1); } export function UpdateAppSettings(arg1) { diff --git a/internal/api/app.go b/internal/api/app.go index 3026b76..04ecb42 100644 --- a/internal/api/app.go +++ b/internal/api/app.go @@ -2,12 +2,15 @@ package api import ( + "context" "fmt" "log" "os" "path/filepath" "strings" + "github.com/wailsapp/wails/v2/pkg/runtime" + "github.com/verstak/verstak-desktop/internal/core/appsettings" "github.com/verstak/verstak-desktop/internal/core/capability" "github.com/verstak/verstak-desktop/internal/core/contribution" @@ -21,6 +24,7 @@ import ( // App is the main application struct exposed to the Wails frontend. type App struct { + ctx context.Context capRegistry *capability.Registry contribRegistry *contribution.Registry permRegistry *permissions.Registry @@ -57,10 +61,10 @@ func NewApp( } } -// Startup is called when the app starts. -func (a *App) Startup() error { +// Startup is called when the app starts. Sets the Wails context for dialogs. +func (a *App) Startup(ctx context.Context) { + a.ctx = ctx log.Printf("[api] App.Startup: initialized with %d plugins", len(a.plugins)) - return nil } // ─── Plugin Manager API ───────────────────────────────────── @@ -477,6 +481,39 @@ func (a *App) RecordDesiredPlugin(pluginID, version, source string) string { return "" } +// ─── Dialog API ───────────────────────────────────────────── + +// SelectDirectory opens a native directory picker dialog. +// Returns the selected path or empty string if cancelled. +func (a *App) SelectDirectory() string { + home, _ := os.UserHomeDir() + + selected, err := runtime.OpenDirectoryDialog(a.ctx, runtime.OpenDialogOptions{ + Title: "Select Vault Directory", + DefaultDirectory: home, + }) + if err != nil { + log.Printf("[api] SelectDirectory: %v", err) + return "" + } + return selected +} + +// SelectVaultForOpen opens a directory picker for opening an existing vault. +func (a *App) SelectVaultForOpen() string { + home, _ := os.UserHomeDir() + + selected, err := runtime.OpenDirectoryDialog(a.ctx, runtime.OpenDialogOptions{ + Title: "Open Existing Vault", + DefaultDirectory: home, + }) + if err != nil { + log.Printf("[api] SelectVaultForOpen: %v", err) + return "" + } + return selected +} + // ContributionSummary aggregates all contribution types for the frontend. type ContributionSummary struct { Views []contribution.ContributionView `json:"views"`