package main import ( "fmt" "os" "os/exec" "path/filepath" "verstak/internal/core/config" "verstak/internal/core/nodes" "verstak/internal/core/plugins" "verstak/internal/i18n" wailsruntime "github.com/wailsapp/wails/v2/pkg/runtime" ) // ===== Template management ===== // AllTemplates returns all registered templates with their enabled status. type TemplateWithStatus struct { ID string `json:"id"` Title string `json:"title"` Type string `json:"type"` Icon string `json:"icon,omitempty"` Enabled bool `json:"enabled"` } func (a *App) AllTemplates() ([]TemplateWithStatus, error) { if !a.IsReady() || a.templates == nil { return nil, fmt.Errorf("vault not ready") } appCfg, _ := config.LoadAppConfig() enabledSet := make(map[string]bool) if appCfg != nil { for _, id := range appCfg.EnabledTemplates { enabledSet[id] = true } } all := a.templates.All() result := make([]TemplateWithStatus, len(all)) for i, t := range all { // If config has explicit list, use it; otherwise default to true enabled := true if appCfg != nil && len(appCfg.EnabledTemplates) > 0 { enabled = enabledSet[t.ID] } result[i] = TemplateWithStatus{ ID: t.ID, Title: t.Title, Type: t.Type, Icon: t.Icon, Enabled: enabled, } } return result, nil } func (a *App) SetTemplateEnabled(templateID string, enabled bool) error { if !a.IsReady() || a.templates == nil { return fmt.Errorf("vault not ready") } appCfg, _ := config.LoadAppConfig() if appCfg == nil { appCfg = config.DefaultAppConfig() } // Update enabled templates list existing := make(map[string]bool) for _, id := range appCfg.EnabledTemplates { existing[id] = true } if enabled { existing[templateID] = true } else { delete(existing, templateID) } appCfg.EnabledTemplates = make([]string, 0, len(existing)) for id := range existing { appCfg.EnabledTemplates = append(appCfg.EnabledTemplates, id) } if err := config.SaveAppConfig(appCfg); err != nil { return err } // Update in-memory registry if enabled { _ = a.templates.Enable(templateID) } else { _ = a.templates.Disable(templateID) } return nil } func (a *App) ListTemplates() []TemplateDTO { if !a.IsReady() { return nil } templates := a.plugins.Templates() out := make([]TemplateDTO, 0, len(templates)) for _, t := range templates { out = append(out, TemplateDTO{ ID: t.Name, Title: t.Name, Type: t.RootType, Icon: t.Icon, }) } return out } func (a *App) FromTemplate(parentID, nodeType, title, section, template string) (*NodeDTO, error) { if err := a.requireVault(); err != nil { return nil, err } var tmpl *plugins.TemplateDefinition for _, t := range a.plugins.Templates() { if t.Name == template { tmpl = &t break } } if tmpl == nil { return nil, nil } root, err := a.nodes.Create(strPtr(parentID), tmpl.RootType, title, 0, "", "") if err != nil { return nil, err } var createTree func(parentID string, nodes []plugins.TreeNode) error createTree = func(parentID string, nodes []plugins.TreeNode) error { for _, tn := range nodes { child, err := a.nodes.Create(strPtr(parentID), tn.Type, tn.Title, 0, "", "") if err != nil { return err } if len(tn.Children) > 0 { if err := createTree(child.ID, tn.Children); err != nil { return err } } } return nil } if err := createTree(root.ID, tmpl.Tree); err != nil { return nil, err } dto := toNodeDTO(root) return &dto, nil } // ===== File picking ===== func (a *App) PickFile() (string, error) { return wailsruntime.OpenFileDialog(a.ctx, wailsruntime.OpenDialogOptions{ Title: i18n.TF("ru", "file.pickSingle"), }) } func (a *App) PickFiles() ([]string, error) { return wailsruntime.OpenMultipleFilesDialog(a.ctx, wailsruntime.OpenDialogOptions{ Title: i18n.TF("ru", "file.pickMultiple"), }) } func (a *App) PickDirectory() (string, error) { return wailsruntime.OpenDirectoryDialog(a.ctx, wailsruntime.OpenDialogOptions{ Title: i18n.TF("ru", "file.pickDirectory"), }) } func (a *App) OpenFile(fileID string) error { if err := a.requireVault(); err != nil { return err } return a.files.Open(fileID) } func (a *App) ReadFileText(fileID string) (string, error) { if err := a.requireVault(); err != nil { return "", err } return a.files.ReadText(fileID) } func (a *App) GetFileBase64(fileID string) (string, error) { if err := a.requireVault(); err != nil { return "", err } return a.files.ReadBase64(fileID) } func (a *App) OpenFolder(nodeID string) error { if err := a.requireVault(); err != nil { return err } n, err := a.nodes.GetActive(nodeID) if err != nil { return err } var target string if n.Type == nodes.TypeFile && n.FsPath == "" { records, _ := a.files.ListByNode(nodeID) if len(records) > 0 { target = filepath.Join(a.vault, records[0].Path) } if target == "" { target = a.vault } target = filepath.Dir(target) } else { target = filepath.Join(a.vault, n.FsPath) if n.Type == nodes.TypeFile { target = filepath.Dir(target) } } if _, err := os.Stat(target); os.IsNotExist(err) { target = a.vault } cmd := exec.Command("xdg-open", target) return cmd.Run() } func (a *App) OpenVaultFolder() error { if !a.IsReady() { return fmt.Errorf("vault not open") } cmd := exec.Command("xdg-open", a.vault) return cmd.Run() } func (a *App) Search(query string) ([]SearchResultDTO, error) { if err := a.requireVault(); err != nil { return nil, err } if query == "" { return []SearchResultDTO{}, nil } results, err := a.search.Search(query) if err != nil { return nil, err } out := make([]SearchResultDTO, len(results)) for i, r := range results { out[i] = SearchResultDTO{ NodeID: r.NodeID, Title: r.Title, Snippet: r.Snippet, Type: r.Type, } } return out, nil } func (a *App) VerstakVersion() string { return "verstak-gui/v2" }