Skip to content

Commit e3155c9

Browse files
committed
feat!: replace primary_template with unified templates list
Dissolve primary_template into a single templates array with a default: true marker. Add optional label field for TUI display and short slug names. Wire up ApplyDefaults for safe settings defaults.
1 parent 396ad81 commit e3155c9

File tree

11 files changed

+156
-134
lines changed

11 files changed

+156
-134
lines changed

cmd/create.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99

1010
"github.com/Pairadux/muxly/internal/forms"
1111
"github.com/Pairadux/muxly/internal/fzf"
12-
"github.com/Pairadux/muxly/internal/models"
1312
"github.com/Pairadux/muxly/internal/selector"
1413
"github.com/Pairadux/muxly/internal/tmux"
1514
"github.com/Pairadux/muxly/internal/utility"
@@ -22,15 +21,13 @@ var createCmd = &cobra.Command{
2221
Short: "Create a session from a template",
2322
Long: "Create a session from a template\n\nSelect a template, then choose a directory to create the session in.",
2423
RunE: func(cmd *cobra.Command, args []string) error {
25-
allTemplates := append([]models.SessionTemplate{cfg.PrimaryTemplate}, cfg.Templates...)
26-
2724
var selectedIdx int
28-
form := forms.TemplateSelectForm(allTemplates, &selectedIdx)
25+
form := forms.TemplateSelectForm(cfg.Templates, &selectedIdx)
2926
if err := form.Run(); err != nil {
3027
return fmt.Errorf("template selection failed: %w", err)
3128
}
3229

33-
tmpl := allTemplates[selectedIdx]
30+
tmpl := cfg.Templates[selectedIdx]
3431

3532
var sessionPath string
3633
if tmpl.Path != "" {

cmd/kill.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ var killCmd = &cobra.Command{
2121
2222
If SESSION is provided, the current session is killed and the client switches to SESSION.
2323
Otherwise, a picker list of active sessions is displayed to choose a replacement.
24-
If no other sessions exist, a new session is created from the primary template or the tmux server is killed.`,
24+
If no other sessions exist, a new session is created from the default template or the tmux server is killed.`,
2525
Args: cobra.MaximumNArgs(1),
2626
RunE: func(cmd *cobra.Command, args []string) error {
2727
if !tmux.IsTmuxServerRunning() {
@@ -79,18 +79,18 @@ If no other sessions exist, a new session is created from the primary template o
7979
}
8080

8181
var createFromTemplate bool
82-
form := forms.ConfirmationForm("Create session from primary template?", "Declining will kill the tmux server.", &createFromTemplate)
82+
form := forms.ConfirmationForm("Create session from default template?", "Declining will kill the tmux server.", &createFromTemplate)
8383

8484
if err := form.Run(); err != nil {
8585
return fmt.Errorf("failed to run confirmation form: %w", err)
8686
}
8787

8888
if createFromTemplate {
89-
if err := tmux.CreateSessionFromPrimaryTemplate(&cfg); err != nil {
89+
if err := tmux.CreateSessionFromDefaultTemplate(&cfg); err != nil {
9090
if errors.Is(err, tmux.ErrGracefulExit) {
9191
return nil
9292
}
93-
return fmt.Errorf("failed to create session from primary template: %w", err)
93+
return fmt.Errorf("failed to create session from default template: %w", err)
9494
}
9595
if err := tmux.KillSession(currentSession); err != nil {
9696
return fmt.Errorf("failed to kill session: %w", err)

cmd/root.go

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ var rootCmd = &cobra.Command{
5353
if err := validateConfig(); err != nil {
5454
return err
5555
}
56-
warnOnConfigIssues()
5756

5857
return nil
5958
},
@@ -75,7 +74,6 @@ var rootCmd = &cobra.Command{
7574
fmt.Printf("scan_dirs: %v\n", cfg.ScanDirs)
7675
fmt.Printf("entry_dirs: %v\n", cfg.EntryDirs)
7776
fmt.Printf("ignore_dirs: %v\n", cfg.IgnoreDirs)
78-
fmt.Printf("primary_template: %v\n", cfg.PrimaryTemplate)
7977
fmt.Printf("templates: %v\n", cfg.Templates)
8078
fmt.Printf("tmux_base: %v\n", cfg.Settings.TmuxBase)
8179
fmt.Printf("default_depth: %v\n", cfg.Settings.DefaultDepth)
@@ -138,7 +136,9 @@ var rootCmd = &cobra.Command{
138136
}
139137
}
140138
if len(sessionLayout.Windows) == 0 {
141-
sessionLayout = models.SessionLayout{Windows: cfg.PrimaryTemplate.Windows}
139+
if dflt, found := config.DefaultTemplate(&cfg); found {
140+
sessionLayout = models.SessionLayout{Windows: dflt.Windows}
141+
}
142142
}
143143

144144
sess := models.Session{
@@ -227,6 +227,8 @@ func initConfig() {
227227
os.Exit(1)
228228
}
229229

230+
config.ApplyDefaults(&cfg)
231+
230232
// Sync cfgFilePath with the actual config file that was loaded
231233
// This ensures 'muxly config edit' opens the correct file
232234
if viper.ConfigFileUsed() != "" {
@@ -258,9 +260,3 @@ func validateConfig() error {
258260

259261
return config.Validate(&cfg)
260262
}
261-
262-
func warnOnConfigIssues() {
263-
if cfg.Settings.Editor == "" {
264-
fmt.Fprintf(os.Stderr, "Warning: editor not set, defaulting to '%s'\n", config.DefaultEditor)
265-
}
266-
}

internal/checks/config.go

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"os"
66

7+
"github.com/Pairadux/muxly/internal/config"
78
"github.com/Pairadux/muxly/internal/models"
89
)
910

@@ -84,38 +85,36 @@ func ValidateConfig(cfg *models.Config) []CheckResult {
8485
})
8586
}
8687

87-
if cfg.PrimaryTemplate.Name == "" {
88+
dflt, hasDefault := config.DefaultTemplate(cfg)
89+
if !hasDefault {
8890
results = append(results, CheckResult{
89-
Name: "primary_template",
91+
Name: "default_template",
9092
Status: StatusError,
91-
Message: "Primary template name not set",
92-
Hint: "Set primary_template.name in config",
93+
Message: "No default template set",
94+
Hint: "Set default: true on one template in the templates list",
9395
})
94-
}
95-
96-
windowCount := len(cfg.PrimaryTemplate.Windows)
97-
if windowCount == 0 {
96+
} else if len(dflt.Windows) == 0 {
9897
results = append(results, CheckResult{
99-
Name: "primary_template",
98+
Name: "default_template",
10099
Status: StatusError,
101-
Message: "Primary template has no windows",
102-
Hint: "Add at least one window to primary_template.windows",
100+
Message: "Default template has no windows",
101+
Hint: "Add at least one window to the default template",
103102
})
104103
} else {
105104
results = append(results, CheckResult{
106-
Name: "primary_template",
105+
Name: "default_template",
107106
Status: StatusOK,
108-
Message: "Primary template",
109-
Detail: fmt.Sprintf("(%d window(s))", windowCount),
107+
Message: "Default template",
108+
Detail: fmt.Sprintf("(%d window(s))", len(dflt.Windows)),
110109
})
111110
}
112111

113-
if len(cfg.Templates) > 0 {
112+
if len(cfg.Templates) > 1 {
114113
results = append(results, CheckResult{
115114
Name: "templates",
116115
Status: StatusOK,
117116
Message: "Additional templates",
118-
Detail: fmt.Sprintf("(%d)", len(cfg.Templates)),
117+
Detail: fmt.Sprintf("(%d)", len(cfg.Templates)-1),
119118
})
120119
}
121120

internal/config/templates.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,19 @@ package config
22

33
import "github.com/Pairadux/muxly/internal/models"
44

5-
func FindTemplateByName(cfg *models.Config, name string) (models.SessionTemplate, bool) {
6-
if cfg.PrimaryTemplate.Name == name {
7-
return cfg.PrimaryTemplate, true
5+
// DefaultTemplate returns the template marked as default in the config.
6+
// Returns false if no template has Default set.
7+
func DefaultTemplate(cfg *models.Config) (models.SessionTemplate, bool) {
8+
for _, tmpl := range cfg.Templates {
9+
if tmpl.Default {
10+
return tmpl, true
11+
}
812
}
13+
return models.SessionTemplate{}, false
14+
}
15+
16+
// FindTemplateByName returns the first template matching the given name.
17+
func FindTemplateByName(cfg *models.Config, name string) (models.SessionTemplate, bool) {
918
for _, tmpl := range cfg.Templates {
1019
if tmpl.Name == name {
1120
return tmpl, true

internal/config/validation.go

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,18 @@ import (
1010

1111
// Validate ensures that the application configuration is valid and complete.
1212
// It checks that at least one directory is configured for scanning and that
13-
// the session layout has at least one window.
13+
// exactly one template is marked as default with at least one window.
1414
func Validate(cfg *models.Config) error {
1515
if len(cfg.ScanDirs) == 0 && len(cfg.EntryDirs) == 0 {
1616
return fmt.Errorf("no directories configured for scanning (scan_dirs or entry_dirs required)")
1717
}
1818

19-
if cfg.PrimaryTemplate.Name == "" {
20-
return fmt.Errorf("primary_template.name is required")
21-
}
22-
if len(cfg.PrimaryTemplate.Windows) == 0 {
23-
return fmt.Errorf("primary_template must have at least one window")
19+
if len(cfg.Templates) == 0 {
20+
return fmt.Errorf("at least one template is required")
2421
}
2522

26-
seenNames := map[string]bool{cfg.PrimaryTemplate.Name: true}
23+
seenNames := make(map[string]bool)
24+
defaultCount := 0
2725
for _, tmpl := range cfg.Templates {
2826
if tmpl.Name == "" {
2927
return fmt.Errorf("all templates must have a name")
@@ -35,6 +33,16 @@ func Validate(cfg *models.Config) error {
3533
if len(tmpl.Windows) == 0 {
3634
return fmt.Errorf("template %q must have at least one window", tmpl.Name)
3735
}
36+
if tmpl.Default {
37+
defaultCount++
38+
}
39+
}
40+
41+
if defaultCount == 0 {
42+
return fmt.Errorf("exactly one template must have default: true")
43+
}
44+
if defaultCount > 1 {
45+
return fmt.Errorf("only one template can have default: true, found %d", defaultCount)
3846
}
3947

4048
seenAliases := make(map[string]string)

0 commit comments

Comments
 (0)