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"`