diff --git a/build/bin/verstak-desktop b/build/bin/verstak-desktop index 9543260..20b0786 100755 Binary files a/build/bin/verstak-desktop and b/build/bin/verstak-desktop differ diff --git a/cmd/smoke-platform/main.go b/cmd/smoke-platform/main.go index 9a2d0b5..e8c894f 100644 --- a/cmd/smoke-platform/main.go +++ b/cmd/smoke-platform/main.go @@ -192,6 +192,14 @@ func main() { fmt.Printf(" ✅ registered vault capability\n") } + // Register workspace capability (core service — always present when vault is open) + if err := reg.Register("verstak-desktop", []string{"verstak/core/workspace/v1"}); err != nil { + fmt.Printf(" ❌ register workspace capability: %v\n", err) + allGood = false + } else { + fmt.Printf(" ✅ registered workspace capability\n") + } + // Register plugin capabilities for _, p := range m.Provides { if err := reg.Register(m.ID, []string{p}); err != nil { @@ -257,10 +265,10 @@ func main() { fmt.Printf("\n[capability count]\n") totalCaps := len(reg.List()) fmt.Printf(" total capabilities: %d\n", totalCaps) - if totalCaps >= 8 { - fmt.Printf(" ✅ total capabilities >= 8 (%d)\n", totalCaps) + if totalCaps >= 9 { + fmt.Printf(" ✅ total capabilities >= 9 (%d)\n", totalCaps) } else { - fmt.Printf(" ❌ total capabilities < 8 (got %d, expected >= 8)\n", totalCaps) + fmt.Printf(" ❌ total capabilities < 9 (got %d, expected >= 9)\n", totalCaps) allGood = false } @@ -649,6 +657,50 @@ func runWorkspaceTest(root string) { fmt.Printf(" ✅ workspace.json exists on disk\n") fmt.Printf(" content:\n%s\n", string(wsData)) + // ── Test 4-level deep tree ── + fmt.Printf("\n[4-level deep tree]\n") + // Create: root → folder1 → folder2 → case (4 levels) + folder1, err := ws.CreateNode(rootID, workspace.TypeFolder, "Level 1 Folder") + if err != nil { + fmt.Printf(" ❌ create folder1: %v\n", err) + exitCode = 1 + return + } + fmt.Printf(" ✅ created: %s\n", folder1.Title) + + folder2, err := ws.CreateNode(folder1.ID, workspace.TypeFolder, "Level 2 Folder") + if err != nil { + fmt.Printf(" ❌ create folder2: %v\n", err) + exitCode = 1 + return + } + fmt.Printf(" ✅ created: %s\n", folder2.Title) + + deepCase, err := ws.CreateNode(folder2.ID, workspace.TypeCase, "Deep Case") + if err != nil { + fmt.Printf(" ❌ create deep case: %v\n", err) + exitCode = 1 + return + } + fmt.Printf(" ✅ created: %s (depth 4)\n", deepCase.Title) + + tree = ws.GetTree() + if len(tree.Nodes) != 7 { // root + case + folder + nested + folder1 + folder2 + deepCase + fmt.Printf(" ❌ expected 7 nodes, got %d\n", len(tree.Nodes)) + exitCode = 1 + return + } + fmt.Printf(" ✅ tree has 7 nodes (4 levels deep)\n") + + // Verify deep case parent chain + deepNode, _ := ws.GetNode(deepCase.ID) + if deepNode.ParentID != folder2.ID { + fmt.Printf(" ❌ deep case parent mismatch\n") + exitCode = 1 + return + } + fmt.Printf(" ✅ deep case parent chain correct\n") + fmt.Printf("\n=== summary ===\n") fmt.Printf("✅ workspace test passed\n") } diff --git a/docs/PLUGIN_RUNTIME.md b/docs/PLUGIN_RUNTIME.md index 99b98ba..98aaa14 100644 --- a/docs/PLUGIN_RUNTIME.md +++ b/docs/PLUGIN_RUNTIME.md @@ -439,6 +439,7 @@ Workspace — центральная модель Верстака вокруг ### Lifecycle Events +**Planned (not yet implemented in runtime):** - `workspace.node.created` - `workspace.node.renamed` - `workspace.node.moved` diff --git a/frontend/src/lib/shell/WorkspaceTree.svelte b/frontend/src/lib/shell/WorkspaceTree.svelte index e3ddd72..b530556 100644 --- a/frontend/src/lib/shell/WorkspaceTree.svelte +++ b/frontend/src/lib/shell/WorkspaceTree.svelte @@ -2,11 +2,13 @@ import { onMount } from 'svelte'; import * as App from '../../../wailsjs/go/api/App'; - let nodes = []; - let currentNodeId = ''; + export let nodes = []; + export let currentNodeId = ''; + export let expandedNodes = {}; + export let depth = 0; + let loading = true; - let error = ''; - let expandedNodes = {}; + let localError = ''; let showCreate = false; let newNodeTitle = ''; let newNodeParentId = ''; @@ -14,12 +16,14 @@ let creating = false; onMount(async () => { - await loadTree(); + if (depth === 0) { + await loadTree(); + } }); async function loadTree() { loading = true; - error = ''; + localError = ''; try { const result = await App.GetWorkspaceTree(); if (result.status === 'not initialized') { @@ -32,7 +36,7 @@ if (root) expandedNodes[root.id] = true; } } catch (e) { - error = String(e); + localError = String(e); } loading = false; } @@ -63,7 +67,7 @@ async function selectNode(id) { const err = await App.SetCurrentWorkspaceNode(id); - if (err) { error = err; return; } + if (err) { localError = err; return; } currentNodeId = id; } @@ -78,12 +82,12 @@ if (!newNodeTitle.trim()) return; creating = true; const res = await App.CreateWorkspaceNode(newNodeParentId, newNodeType, newNodeTitle.trim()); - if (res.error) { error = res.error; creating = false; return; } + if (res.error) { localError = res.error; creating = false; return; } showCreate = false; creating = false; - await loadTree(); expandedNodes[newNodeParentId] = true; expandedNodes = expandedNodes; + await loadTree(); } function cancelCreate() { @@ -92,67 +96,62 @@ } -