260 lines
5.8 KiB
Go
260 lines
5.8 KiB
Go
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"
|
|
}
|