Replaced fmt, strings, sort, os, io, sync, encoding/json, path/filepath, errors, log, reflect with core.Sprintf, core.E, core.Contains, core.Trim, core.Split, core.Join, core.JoinPath, slices.Sort, c.Fs(), c.Lock(), core.JSONMarshal, core.ReadAll and other CoreGO v0.8.0 primitives. Framework boundary exceptions preserved where stdlib types are required by external interfaces (Gin, net/http, CGo, Wails, bubbletea). Co-Authored-By: Virgil <virgil@lethean.io>
151 lines
3.7 KiB
Go
151 lines
3.7 KiB
Go
// pkg/window/layout.go
|
|
package window
|
|
|
|
import (
|
|
"os"
|
|
"sync"
|
|
"time"
|
|
|
|
corego "dappco.re/go/core"
|
|
)
|
|
|
|
// Layout is a named window arrangement.
|
|
// Use: layout := window.Layout{Name: "coding"}
|
|
type Layout struct {
|
|
Name string `json:"name"`
|
|
Windows map[string]WindowState `json:"windows"`
|
|
CreatedAt int64 `json:"createdAt"`
|
|
UpdatedAt int64 `json:"updatedAt"`
|
|
}
|
|
|
|
// LayoutInfo is a summary of a layout.
|
|
// Use: info := window.LayoutInfo{Name: "coding", WindowCount: 2}
|
|
type LayoutInfo struct {
|
|
Name string `json:"name"`
|
|
WindowCount int `json:"windowCount"`
|
|
CreatedAt int64 `json:"createdAt"`
|
|
UpdatedAt int64 `json:"updatedAt"`
|
|
}
|
|
|
|
// LayoutManager persists named window arrangements to ~/.config/Core/layouts.json.
|
|
// Use: lm := window.NewLayoutManager()
|
|
type LayoutManager struct {
|
|
configDir string
|
|
layouts map[string]Layout
|
|
mu sync.RWMutex
|
|
}
|
|
|
|
// NewLayoutManager creates a LayoutManager loading from the default config directory.
|
|
// Use: lm := window.NewLayoutManager()
|
|
func NewLayoutManager() *LayoutManager {
|
|
lm := &LayoutManager{
|
|
layouts: make(map[string]Layout),
|
|
}
|
|
configDir, err := os.UserConfigDir()
|
|
if err == nil {
|
|
lm.configDir = corego.JoinPath(configDir, "Core")
|
|
}
|
|
lm.loadLayouts()
|
|
return lm
|
|
}
|
|
|
|
// NewLayoutManagerWithDir creates a LayoutManager loading from a custom config directory.
|
|
// Useful for testing or when the default config directory is not appropriate.
|
|
// Use: lm := window.NewLayoutManagerWithDir(t.TempDir())
|
|
func NewLayoutManagerWithDir(configDir string) *LayoutManager {
|
|
lm := &LayoutManager{
|
|
configDir: configDir,
|
|
layouts: make(map[string]Layout),
|
|
}
|
|
lm.loadLayouts()
|
|
return lm
|
|
}
|
|
|
|
func (lm *LayoutManager) layoutsFilePath() string {
|
|
return corego.JoinPath(lm.configDir, "layouts.json")
|
|
}
|
|
|
|
func (lm *LayoutManager) loadLayouts() {
|
|
if lm.configDir == "" {
|
|
return
|
|
}
|
|
data, err := os.ReadFile(lm.layoutsFilePath())
|
|
if err != nil {
|
|
return
|
|
}
|
|
lm.mu.Lock()
|
|
defer lm.mu.Unlock()
|
|
_ = corego.JSONUnmarshal(data, &lm.layouts)
|
|
}
|
|
|
|
func (lm *LayoutManager) saveLayouts() {
|
|
if lm.configDir == "" {
|
|
return
|
|
}
|
|
lm.mu.RLock()
|
|
r := corego.JSONMarshal(lm.layouts)
|
|
lm.mu.RUnlock()
|
|
if !r.OK {
|
|
return
|
|
}
|
|
_ = os.MkdirAll(lm.configDir, 0o755)
|
|
_ = os.WriteFile(lm.layoutsFilePath(), r.Value.([]byte), 0o644)
|
|
}
|
|
|
|
// SaveLayout creates or updates a named layout.
|
|
// Use: _ = lm.SaveLayout("coding", windowStates)
|
|
func (lm *LayoutManager) SaveLayout(name string, windowStates map[string]WindowState) error {
|
|
if name == "" {
|
|
return corego.E("layout.save", "layout name cannot be empty", nil)
|
|
}
|
|
now := time.Now().UnixMilli()
|
|
lm.mu.Lock()
|
|
existing, exists := lm.layouts[name]
|
|
layout := Layout{
|
|
Name: name,
|
|
Windows: windowStates,
|
|
UpdatedAt: now,
|
|
}
|
|
if exists {
|
|
layout.CreatedAt = existing.CreatedAt
|
|
} else {
|
|
layout.CreatedAt = now
|
|
}
|
|
lm.layouts[name] = layout
|
|
lm.mu.Unlock()
|
|
lm.saveLayouts()
|
|
return nil
|
|
}
|
|
|
|
// GetLayout returns a layout by name.
|
|
// Use: layout, ok := lm.GetLayout("coding")
|
|
func (lm *LayoutManager) GetLayout(name string) (Layout, bool) {
|
|
lm.mu.RLock()
|
|
defer lm.mu.RUnlock()
|
|
l, ok := lm.layouts[name]
|
|
return l, ok
|
|
}
|
|
|
|
// ListLayouts returns info summaries for all layouts.
|
|
// Use: layouts := lm.ListLayouts()
|
|
func (lm *LayoutManager) ListLayouts() []LayoutInfo {
|
|
lm.mu.RLock()
|
|
defer lm.mu.RUnlock()
|
|
infos := make([]LayoutInfo, 0, len(lm.layouts))
|
|
for _, l := range lm.layouts {
|
|
infos = append(infos, LayoutInfo{
|
|
Name: l.Name, WindowCount: len(l.Windows),
|
|
CreatedAt: l.CreatedAt, UpdatedAt: l.UpdatedAt,
|
|
})
|
|
}
|
|
return infos
|
|
}
|
|
|
|
// DeleteLayout removes a layout by name.
|
|
// Use: lm.DeleteLayout("coding")
|
|
func (lm *LayoutManager) DeleteLayout(name string) {
|
|
lm.mu.Lock()
|
|
delete(lm.layouts, name)
|
|
lm.mu.Unlock()
|
|
lm.saveLayouts()
|
|
}
|