core: Milestone 7b — Files explorer and Default Editor improvements

- Files plugin: richer explorer with breadcrumbs, selection, toolbar actions,
  rename/trash, filter, sorting, hidden/reserved entries filtered
- Default Editor: line numbers, Ctrl+S, markdown toolbar, Edit/Preview/Split,
  markdown preview, Reload/Revert
- E2E tests: 39 passed for files + editor
- Workspace model: correction, naming alignment, compatibility wrappers
- Updated docs: NOTES_FILES_PLUGIN_PLAN.md, PLUGIN_RUNTIME.md
This commit is contained in:
2026-06-20 19:20:13 +08:00
parent 4de5a74a55
commit 0ac473d720
18 changed files with 1956 additions and 1161 deletions
+3 -2
View File
@@ -115,9 +115,10 @@ func (v *Vault) CreateVault(path string) error {
return fmt.Errorf("failed to write vault.json: %w", err)
}
// Create workspace.json with root node
// Create the initial physical workspace folder. Workspace listing is still
// sourced from top-level vault folders, not from .verstak metadata.
wsMgr := workspace.NewManager(vaultDir)
if err := wsMgr.Load(); err != nil {
if _, err := wsMgr.CreateWorkspace("Workspace", "default"); err != nil {
return fmt.Errorf("failed to create workspace: %w", err)
}
+10 -56
View File
@@ -259,44 +259,14 @@ func TestCreateVault_CreatesWorkspace(t *testing.T) {
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)
if _, err := os.Stat(filepath.Join(v.GetVaultPath(), "Workspace")); err != nil {
t.Fatalf("Workspace folder 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 := os.Stat(filepath.Join(v.GetVaultPath(), "Workspace", "Notes", "Overview.md")); err != nil {
t.Fatalf("default workspace overview not found: %v", err)
}
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")
if _, err := os.Stat(filepath.Join(v.GetVaultPath(), ".verstak", "workspace.json")); !os.IsNotExist(err) {
t.Fatalf("workspace.json should not be created as workspace source of truth, stat err=%v", err)
}
}
@@ -317,27 +287,11 @@ func TestOpenVault_WorkspaceLoads(t *testing.T) {
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)
if _, err := os.Stat(filepath.Join(v.GetVaultPath(), "Workspace")); err != nil {
t.Fatalf("Workspace folder should still exist 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")
if _, err := os.Stat(filepath.Join(v.GetVaultPath(), ".verstak", "workspace.json")); !os.IsNotExist(err) {
t.Fatalf("OpenVault should not create workspace.json, stat err=%v", err)
}
}