Вложенные Дела, folder metadata, плагин workspace-folders
- Nested workspace model: маркеры .verstak/workspace.json на любой глубине - Folder metadata API: иконка, цвет, порядок (.verstak/folder-metadata/) - MoveWorkspace, GetFolderMetadata, SetFolderMetadata Wails биндинги - Contribution point workspaceTree — singleton в registry - Sidebar.svelte монтирует PluginBundleHost при наличии workspaceTree - VerstakPluginAPI: workspaces.* и folders.* методы - WorkspaceTree.svelte: рекурсивное дерево, выбор родительской папки - i18n: строки EN+RU, release notes v0.1.0-alpha.8
This commit is contained in:
+60
-8
@@ -979,6 +979,13 @@ type ContributionSummary struct {
|
||||
FileActions []FlatAction `json:"fileActions"`
|
||||
NoteActions []FlatAction `json:"noteActions"`
|
||||
ContextMenuEntries []FlatContextMenuEntry `json:"contextMenuEntries"`
|
||||
WorkspaceTree *FlatWorkspaceTree `json:"workspaceTree,omitempty"`
|
||||
}
|
||||
|
||||
// FlatWorkspaceTree is the summary for the workspaceTree singleton contribution.
|
||||
type FlatWorkspaceTree struct {
|
||||
PluginID string `json:"pluginId"`
|
||||
Component string `json:"component"`
|
||||
}
|
||||
|
||||
// buildContributionSummary creates a ContributionSummary from the registry.
|
||||
@@ -1053,7 +1060,12 @@ func buildContributionSummary(r *contribution.Registry) ContributionSummary {
|
||||
for i, v := range regContextMenus {
|
||||
contextMenus[i] = FlatContextMenuEntry{PluginID: v.PluginID, ID: v.Item.ID, Label: v.Item.Label, Context: v.Item.Context, Group: v.Item.Group, Capability: v.Item.Capability, Handler: v.Item.Handler}
|
||||
}
|
||||
return ContributionSummary{Views: views, Commands: cmds, SearchProviders: searchProviders, SettingsPanels: panels, SidebarItems: sidebar, StatusBarItems: statusBarItems, OpenProviders: openProviders, WorkspaceItems: workspaceItems, FileActions: fileActions, NoteActions: noteActions, ContextMenuEntries: contextMenus}
|
||||
var wsTree *FlatWorkspaceTree
|
||||
regWSTree := r.WorkspaceTree()
|
||||
if regWSTree != nil {
|
||||
wsTree = &FlatWorkspaceTree{PluginID: regWSTree.PluginID, Component: regWSTree.Component}
|
||||
}
|
||||
return ContributionSummary{Views: views, Commands: cmds, SearchProviders: searchProviders, SettingsPanels: panels, SidebarItems: sidebar, StatusBarItems: statusBarItems, OpenProviders: openProviders, WorkspaceItems: workspaceItems, FileActions: fileActions, NoteActions: noteActions, ContextMenuEntries: contextMenus, WorkspaceTree: wsTree}
|
||||
}
|
||||
|
||||
// GetContributions returns all registered contributions flattened for the frontend.
|
||||
@@ -2591,7 +2603,7 @@ func (a *App) UpdateWorkspaceMetadata(name string, patch workspace.MetadataPatch
|
||||
return meta, ""
|
||||
}
|
||||
|
||||
// GetCurrentWorkspace returns the currently selected top-level workspace.
|
||||
// GetCurrentWorkspace returns the currently selected workspace.
|
||||
func (a *App) GetCurrentWorkspace() map[string]interface{} {
|
||||
if a.workspace == nil {
|
||||
return map[string]interface{}{"status": "not initialized"}
|
||||
@@ -2600,7 +2612,7 @@ func (a *App) GetCurrentWorkspace() map[string]interface{} {
|
||||
if err != nil {
|
||||
return map[string]interface{}{"error": err.Error()}
|
||||
}
|
||||
identity, err := a.workspace.GetWorkspaceIdentity(node.Name)
|
||||
identity, err := a.workspace.GetWorkspaceIdentity(node.Path)
|
||||
if err != nil {
|
||||
return map[string]interface{}{"error": err.Error()}
|
||||
}
|
||||
@@ -2609,25 +2621,65 @@ func (a *App) GetCurrentWorkspace() map[string]interface{} {
|
||||
"workspaceId": identity.WorkspaceID,
|
||||
"name": node.Name,
|
||||
"rootPath": node.RootPath,
|
||||
"path": node.Path,
|
||||
}
|
||||
}
|
||||
|
||||
// SetCurrentWorkspace stores the selected top-level workspace name as UI state.
|
||||
func (a *App) SetCurrentWorkspace(name string) string {
|
||||
// SetCurrentWorkspace stores the selected workspace path as UI state.
|
||||
func (a *App) SetCurrentWorkspace(path string) string {
|
||||
if a.workspace == nil {
|
||||
return "workspace not initialized"
|
||||
}
|
||||
if err := a.workspace.SetCurrentNode(name); err != nil {
|
||||
if err := a.workspace.SetCurrentNode(path); err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
node, err := a.workspace.GetCurrentNode()
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
a.publishWorkspaceLifecycleEvent(workspaceSelectedEventName, map[string]interface{}{
|
||||
"operation": "select",
|
||||
"workspaceRootPath": name,
|
||||
"workspaceName": name,
|
||||
"workspaceRootPath": node.RootPath,
|
||||
"workspaceName": node.Name,
|
||||
"workspacePath": node.Path,
|
||||
})
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetFolderMetadata returns stored metadata for a plain vault folder.
|
||||
func (a *App) GetFolderMetadata(path string) (workspace.FolderMetadata, string) {
|
||||
if a.workspace == nil {
|
||||
return workspace.FolderMetadata{}, "workspace not initialized"
|
||||
}
|
||||
meta, err := a.workspace.GetFolderMetadata(path)
|
||||
if err != nil {
|
||||
return workspace.FolderMetadata{}, err.Error()
|
||||
}
|
||||
return meta, ""
|
||||
}
|
||||
|
||||
// SetFolderMetadata updates metadata for a plain vault folder.
|
||||
func (a *App) SetFolderMetadata(path string, meta workspace.FolderMetadata) string {
|
||||
if a.workspace == nil {
|
||||
return "workspace not initialized"
|
||||
}
|
||||
if err := a.workspace.SetFolderMetadata(path, meta); err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// MoveWorkspace moves a workspace to another parent folder.
|
||||
func (a *App) MoveWorkspace(id, newParentID string) string {
|
||||
if a.workspace == nil {
|
||||
return "workspace not initialized"
|
||||
}
|
||||
if err := a.workspace.MoveNode(id, newParentID); err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Deprecated: compatibility wrapper over the flat top-level folder workspace
|
||||
// model. Prefer ListWorkspaces.
|
||||
func (a *App) GetWorkspaceTree() map[string]interface{} {
|
||||
|
||||
@@ -2624,9 +2624,6 @@ func TestSetCurrentVaultInitializesWorkspaceWhenMissingAtStartup(t *testing.T) {
|
||||
if len(nodes) == 0 {
|
||||
t.Fatal("workspace nodes should not be empty")
|
||||
}
|
||||
if nodes[0].Path != "" {
|
||||
t.Fatalf("compatibility node should not expose workspace path mapping: %+v", nodes[0])
|
||||
}
|
||||
if !app.capRegistry.Has("verstak/core/workspace/v1") {
|
||||
t.Fatal("workspace capability should be registered after SetCurrentVault")
|
||||
}
|
||||
@@ -2905,11 +2902,11 @@ func TestMoveWorkspaceNodeCompatibilityIsUnsupported(t *testing.T) {
|
||||
}
|
||||
|
||||
errStr := app.MoveWorkspaceNode("Project", "Test")
|
||||
if errStr == "" || !strings.Contains(errStr, "top-level only") {
|
||||
t.Fatalf("MoveWorkspaceNode error = %q, want top-level only", errStr)
|
||||
if errStr == "" || !strings.Contains(errStr, "parent-is-workspace") {
|
||||
t.Fatalf("MoveWorkspaceNode error = %q, want parent-is-workspace", errStr)
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(vaultDir, "Test", "Project")); !os.IsNotExist(err) {
|
||||
t.Fatalf("MoveWorkspaceNode created nested mapped workspace, stat err=%v", err)
|
||||
t.Fatalf("MoveWorkspaceNode created nested workspace inside another workspace, stat err=%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user