fix: vault/workspace lifecycle — CreateVault creates workspace, SetCurrentVault loads workspace, ReloadPlugins keeps workspace capability, recursive tree rendering
This commit is contained in:
+25
-2
@@ -159,6 +159,13 @@ func (a *App) ReloadPlugins() (int, string) {
|
||||
}
|
||||
}
|
||||
|
||||
// Re-register workspace capability if workspace is initialized
|
||||
if a.workspace != nil && a.workspace.IsInitialized() {
|
||||
if err := a.capRegistry.Register("verstak-desktop", []string{"verstak/core/workspace/v1"}); err != nil {
|
||||
log.Printf("[api] ReloadPlugins: failed to re-register workspace capability: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
plugins, errs := plugin.DiscoverPlugins(discoveryDirs)
|
||||
|
||||
// Plugin lifecycle: register capabilities + contributions
|
||||
@@ -407,6 +414,7 @@ func (a *App) UpdateAppSettings(patch map[string]interface{}) string {
|
||||
}
|
||||
|
||||
// SetCurrentVault sets the current vault path in app settings and re-opens the vault.
|
||||
// Loads workspace and registers vault + workspace capabilities.
|
||||
func (a *App) SetCurrentVault(path string) string {
|
||||
if a.appSettings == nil {
|
||||
return "app settings not initialized"
|
||||
@@ -418,8 +426,9 @@ func (a *App) SetCurrentVault(path string) string {
|
||||
if err := a.vault.OpenVault(path); err != nil {
|
||||
return fmt.Sprintf("failed to open vault: %v", err)
|
||||
}
|
||||
// Save to app settings
|
||||
if err := a.appSettings.SetCurrentVault(path); err != nil {
|
||||
// Save the actual vault path (normalized by OpenVault, includes VerstakVault/)
|
||||
vaultPath := a.vault.GetVaultPath()
|
||||
if err := a.appSettings.SetCurrentVault(vaultPath); err != nil {
|
||||
return fmt.Sprintf("failed to save app settings: %v", err)
|
||||
}
|
||||
// Load plugin state for the vault
|
||||
@@ -428,10 +437,24 @@ func (a *App) SetCurrentVault(path string) string {
|
||||
log.Printf("[api] SetCurrentVault: warning loading plugin state: %v", err)
|
||||
}
|
||||
}
|
||||
// Load workspace for the vault
|
||||
if a.workspace != nil {
|
||||
// Replace workspace manager with one pointing to the new vault
|
||||
a.workspace = workspace.NewManager(vaultPath)
|
||||
if err := a.workspace.Load(); err != nil {
|
||||
log.Printf("[api] SetCurrentVault: warning loading workspace: %v", err)
|
||||
}
|
||||
}
|
||||
// Register vault capability
|
||||
if err := a.capRegistry.Register("verstak-desktop", []string{"verstak/core/vault/v1"}); err != nil {
|
||||
log.Printf("[api] SetCurrentVault: failed to register vault capability: %v", err)
|
||||
}
|
||||
// Register workspace capability
|
||||
if a.workspace != nil && a.workspace.IsInitialized() {
|
||||
if err := a.capRegistry.Register("verstak-desktop", []string{"verstak/core/workspace/v1"}); err != nil {
|
||||
log.Printf("[api] SetCurrentVault: failed to register workspace capability: %v", err)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/verstak/verstak-desktop/internal/core/events"
|
||||
"github.com/verstak/verstak-desktop/internal/core/workspace"
|
||||
)
|
||||
|
||||
// VaultStatus represents the current state of a vault.
|
||||
@@ -114,6 +115,12 @@ func (v *Vault) CreateVault(path string) error {
|
||||
return fmt.Errorf("failed to write vault.json: %w", err)
|
||||
}
|
||||
|
||||
// Create workspace.json with root node
|
||||
wsMgr := workspace.NewManager(vaultDir)
|
||||
if err := wsMgr.Load(); err != nil {
|
||||
return fmt.Errorf("failed to create workspace: %w", err)
|
||||
}
|
||||
|
||||
v.mu.Lock()
|
||||
v.status = StatusOpen
|
||||
v.path = vaultDir
|
||||
|
||||
@@ -247,3 +247,113 @@ func TestVaultEvents_Published(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateVault_CreatesWorkspace(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
vaultPath := filepath.Join(dir, "testvault")
|
||||
|
||||
bus := events.NewBus()
|
||||
v := NewVault(bus)
|
||||
|
||||
if err := v.CreateVault(vaultPath); err != nil {
|
||||
t.Fatalf("CreateVault: %v", err)
|
||||
}
|
||||
|
||||
wsPath := filepath.Join(v.GetVaultPath(), ".verstak", "workspace.json")
|
||||
data, err := os.ReadFile(wsPath)
|
||||
if err != nil {
|
||||
t.Fatalf("workspace.json not found: %v", err)
|
||||
}
|
||||
|
||||
var ws struct {
|
||||
SchemaVersion int `json:"schemaVersion"`
|
||||
Nodes []struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Title string `json:"title"`
|
||||
Status string `json:"status"`
|
||||
ParentID string `json:"parentId"`
|
||||
} `json:"nodes"`
|
||||
CurrentNodeID string `json:"currentNodeId"`
|
||||
}
|
||||
if err := json.Unmarshal(data, &ws); err != nil {
|
||||
t.Fatalf("failed to parse workspace.json: %v", err)
|
||||
}
|
||||
|
||||
if ws.SchemaVersion != 1 {
|
||||
t.Errorf("schemaVersion: got %d, want 1", ws.SchemaVersion)
|
||||
}
|
||||
if len(ws.Nodes) != 1 {
|
||||
t.Fatalf("expected 1 root node, got %d", len(ws.Nodes))
|
||||
}
|
||||
if ws.Nodes[0].Type != "space" {
|
||||
t.Errorf("root type: got %q, want %q", ws.Nodes[0].Type, "space")
|
||||
}
|
||||
if ws.Nodes[0].Title != "My Workspace" {
|
||||
t.Errorf("root title: got %q, want %q", ws.Nodes[0].Title, "My Workspace")
|
||||
}
|
||||
if ws.Nodes[0].Status != "active" {
|
||||
t.Errorf("root status: got %q, want %q", ws.Nodes[0].Status, "active")
|
||||
}
|
||||
if ws.CurrentNodeID != ws.Nodes[0].ID {
|
||||
t.Errorf("currentNodeId should be root node id")
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenVault_WorkspaceLoads(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
vaultPath := filepath.Join(dir, "testvault")
|
||||
|
||||
bus := events.NewBus()
|
||||
v := NewVault(bus)
|
||||
|
||||
if err := v.CreateVault(vaultPath); err != nil {
|
||||
t.Fatalf("CreateVault: %v", err)
|
||||
}
|
||||
|
||||
v.CloseVault()
|
||||
|
||||
if err := v.OpenVault(vaultPath); err != nil {
|
||||
t.Fatalf("OpenVault: %v", err)
|
||||
}
|
||||
|
||||
wsPath := filepath.Join(v.GetVaultPath(), ".verstak", "workspace.json")
|
||||
data, err := os.ReadFile(wsPath)
|
||||
if err != nil {
|
||||
t.Fatalf("workspace.json not found after reopen: %v", err)
|
||||
}
|
||||
|
||||
var ws struct {
|
||||
Nodes []struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Title string `json:"title"`
|
||||
} `json:"nodes"`
|
||||
}
|
||||
if err := json.Unmarshal(data, &ws); err != nil {
|
||||
t.Fatalf("failed to parse workspace.json: %v", err)
|
||||
}
|
||||
if len(ws.Nodes) != 1 {
|
||||
t.Fatalf("expected 1 node after reopen, got %d", len(ws.Nodes))
|
||||
}
|
||||
if ws.Nodes[0].Type != "space" {
|
||||
t.Errorf("root type after reopen: got %q, want %q", ws.Nodes[0].Type, "space")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateVault_VaultPathNormalized(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
vaultPath := filepath.Join(dir, "testvault")
|
||||
|
||||
bus := events.NewBus()
|
||||
v := NewVault(bus)
|
||||
|
||||
if err := v.CreateVault(vaultPath); err != nil {
|
||||
t.Fatalf("CreateVault: %v", err)
|
||||
}
|
||||
|
||||
expectedPath := filepath.Join(vaultPath, "VerstakVault")
|
||||
if v.GetVaultPath() != expectedPath {
|
||||
t.Errorf("GetVaultPath: got %q, want %q", v.GetVaultPath(), expectedPath)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user