package input import ( "fmt" "strings" ) // Tentacle bindings are runtime registrations, never persisted core overrides. // The native input owner arbitrates them again whenever core settings change. func ValidateTentacleBindings(bindings []BindingOverride) error { if len(bindings) > 16*32*4 { return fmt.Errorf("too many tentacle bindings") } for _, binding := range bindings { if !strings.HasPrefix(string(binding.Command), "tentacle/") || len(binding.Command) > 400 || (binding.Layout != DesktopKeyboardLayout && binding.Layout != LaptopKeyboardLayout) || (binding.Scope != "global" && binding.Scope != "browse") || len(binding.Keys) != 1 { return fmt.Errorf("invalid tentacle binding") } if _, err := ParseCommandChord(binding.Keys[0]); err != nil { return err } } return nil } // MergeTentacleBindings preserves core bindings and registration order. A // conflicting suggestion stays unbound. Callers validate both inputs first. func MergeTentacleBindings(core, suggestions []BindingOverride) []BindingOverride { result := CloneBindingOverrides(core) for _, layout := range []KeyboardLayout{DesktopKeyboardLayout, LaptopKeyboardLayout} { occupied, _ := ResolveCommandBindings(layout, true, core) for _, suggestion := range suggestions { if suggestion.Layout != layout { continue } chord, _ := ParseCommandChord(suggestion.Keys[0]) if occupied[chord] != "" { continue } occupied[chord] = suggestion.Command result = append(result, CloneBindingOverrides([]BindingOverride{suggestion})[0]) } } return result }