1package config
2
3const (
4 sectionWorkspace = "workspace"
5 sectionSession = "session"
6)
7
8// LegacyConfigTransformer implements port.ConfigTransformer.
9// It transforms old-format action bindings (slices) to new ActionBinding format.
10type LegacyConfigTransformer struct {
11 defaults *Config
12}
13
14// NewLegacyConfigTransformer creates a new transformer with defaults for description lookup.
15func NewLegacyConfigTransformer() *LegacyConfigTransformer {
16 return &LegacyConfigTransformer{defaults: DefaultConfig()}
17}
18
19// TransformLegacyActions converts old-format action bindings to new format.
20// This handles migration from:
21//
22// [workspace.pane_mode.actions]
23// "focus-left" = ["h", "arrowleft"]
24//
25// To:
26//
27// [workspace.pane_mode.actions.focus-left]
28// keys = ["h", "arrowleft"]
29// desc = "Focus pane to the left"
30func (t *LegacyConfigTransformer) TransformLegacyActions(rawConfig map[string]any) {
31 actionPaths := [][]string{
32 {"workspace", "pane_mode", "actions"},
33 {"workspace", "tab_mode", "actions"},
34 {"workspace", "resize_mode", "actions"},
35 {"workspace", "shortcuts", "actions"},
36 {"session", "session_mode", "actions"},
37 }
38
39 for _, path := range actionPaths {
40 t.transformActionSection(rawConfig, path)
41 }
42}
43
44func (t *LegacyConfigTransformer) transformActionSection(rawConfig map[string]any, path []string) {
45 // Navigate to the parent of the actions section
46 current := rawConfig
47 for _, key := range path[:len(path)-1] {
48 next, ok := current[key].(map[string]any)
49 if !ok {
50 return // Path doesn't exist
51 }
52 current = next
53 }
54
55 actionsKey := path[len(path)-1]
56 actionsRaw, ok := current[actionsKey]
57 if !ok {
58 return
59 }
60
61 actions, ok := actionsRaw.(map[string]any)
62 if !ok {
63 return
64 }
65
66 // Check each action and transform if needed
67 for actionName, actionValue := range actions {
68 // If value is a slice (old format), convert to ActionBinding format
69 if slice, ok := actionValue.([]any); ok {
70 keys := make([]string, 0, len(slice))
71 for _, v := range slice {
72 if s, ok := v.(string); ok {
73 keys = append(keys, s)
74 }
75 }
76
77 actions[actionName] = map[string]any{
78 "keys": keys,
79 "desc": t.getDefaultDesc(path, actionName),
80 }
81 }
82 }
83}
84
85func (t *LegacyConfigTransformer) getDefaultDesc(path []string, actionName string) string {
86 switch {
87 case path[0] == sectionWorkspace && path[1] == "pane_mode":
88 if b, ok := t.defaults.Workspace.PaneMode.Actions[actionName]; ok {
89 return b.Desc
90 }
91 case path[0] == sectionWorkspace && path[1] == "tab_mode":
92 if b, ok := t.defaults.Workspace.TabMode.Actions[actionName]; ok {
93 return b.Desc
94 }
95 case path[0] == sectionWorkspace && path[1] == "resize_mode":
96 if b, ok := t.defaults.Workspace.ResizeMode.Actions[actionName]; ok {
97 return b.Desc
98 }
99 case path[0] == sectionWorkspace && path[1] == "shortcuts":
100 if b, ok := t.defaults.Workspace.Shortcuts.Actions[actionName]; ok {
101 return b.Desc
102 }
103 case path[0] == sectionSession && path[1] == "session_mode":
104 if b, ok := t.defaults.Session.SessionMode.Actions[actionName]; ok {
105 return b.Desc
106 }
107 }
108 return ""
109}