https://github.com/ktsu-dev/keybinding
https://github.com/ktsu-dev/keybinding
Last synced: 4 months ago
JSON representation
- Host: GitHub
- URL: https://github.com/ktsu-dev/keybinding
- Owner: ktsu-dev
- License: mit
- Created: 2025-05-29T02:19:03.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2026-02-14T01:50:50.000Z (4 months ago)
- Last Synced: 2026-02-14T08:51:29.755Z (4 months ago)
- Language: C#
- Size: 505 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE.md
- Authors: AUTHORS.md
- Copyright: COPYRIGHT.md
Awesome Lists containing this project
README
# Keybinding Management Library
A comprehensive .NET library for managing keyboard shortcuts and keybindings with support for multiple profiles, command registration, and persistent storage.
## Features
- **Multi-Profile Support**: Create and manage multiple keybinding profiles
- **Command Registry**: Register and organize commands with categories
- **Flexible Key Combinations**: Support for complex key combinations with multiple modifiers
- **Persistent Storage**: Automatic saving and loading of profiles and commands
- **Thread-Safe Operations**: Concurrent access support for multi-threaded applications
- **SOLID Architecture**: Clean, extensible design following SOLID principles
- **Comprehensive API**: Full-featured interfaces for all operations
## Quick Start
### Installation
Add a reference to the `Keybinding.Core` project in your application.
### Basic Usage
```csharp
using ktsu.Keybinding.Core;
using ktsu.Keybinding.Core.Models;
// Initialize the keybinding manager
var manager = new KeybindingManager("./keybinding-data");
await manager.InitializeAsync();
// Create a default profile if none exist
manager.CreateDefaultProfile();
// Register some commands
var commands = new[]
{
new Command("file.new", "New File", "Create a new file", "File"),
new Command("file.save", "Save File", "Save the current file", "File"),
new Command("edit.copy", "Copy", "Copy selected text", "Edit")
};
manager.RegisterCommands(commands);
// Set chord bindings
manager.Keybindings.BindChord("file.new", manager.Keybindings.ParseChord("Ctrl+N"));
manager.Keybindings.BindChord("file.save", manager.Keybindings.ParseChord("Ctrl+S"));
manager.Keybindings.BindChord("edit.copy", manager.Keybindings.ParseChord("Ctrl+C"));
// Find commands by chord
var chord = manager.Keybindings.ParseChord("Ctrl+S");
var commandId = manager.Keybindings.FindCommandByChord(chord);
// Save changes
await manager.SaveAsync();
```
## Core Concepts
### Commands
Commands represent actions that can be bound to keyboard shortcuts:
```csharp
var command = new Command(
id: "file.save", // Unique identifier
name: "Save File", // Display name
description: "Save the current file", // Optional description
category: "File" // Optional category for organization
);
```
### Chords (Key Combinations)
Chords represent musical-style key combinations using the Note and Chord classes:
```csharp
// Parse from string
var chord = manager.Keybindings.ParseChord("Ctrl+Alt+S");
// Create programmatically
var chord = new Chord([
new Note("CTRL"),
new Note("ALT"),
new Note("S")
]);
// Supported modifiers: Ctrl, Alt, Shift, Meta (Windows/Cmd key)
```
### Profiles
Profiles allow different keybinding configurations:
```csharp
// Create a new profile
var profile = new Profile("gaming", "Gaming Profile", "Keybindings for gaming");
manager.Profiles.CreateProfile(profile);
// Switch active profile
manager.Profiles.SetActiveProfile("gaming");
// Duplicate a profile
var newProfile = manager.Profiles.DuplicateProfile("default", "custom", "Custom Profile");
```
## Architecture
The library follows a clean architecture with clear separation of concerns:
### Models
- **`Command`**: Represents a named action that can be bound to keys
- **`Chord`**: Represents a keyboard shortcut with modifiers and primary key using musical paradigm
- **`Note`**: Represents individual keys in a chord
- **`Phrase`**: Represents sequences of chords for complex key combinations
- **`Profile`**: Contains a set of chord bindings for commands
### Contracts (Interfaces)
- **`ICommandRegistry`**: Command management operations
- **`IProfileManager`**: Profile management operations
- **`IKeybindingService`**: Keybinding coordination and validation
- **`IKeybindingRepository`**: Persistence operations
### Services
- **`CommandRegistry`**: Thread-safe command storage and retrieval
- **`ProfileManager`**: Profile lifecycle management
- **`KeybindingService`**: Coordinates keybinding operations across profiles and commands
- **`JsonKeybindingRepository`**: JSON-based persistence implementation
### Main Facade
- **`KeybindingManager`**: Main entry point that coordinates all services
## Advanced Usage
### Custom Repository
Implement your own storage mechanism:
```csharp
public class DatabaseKeybindingRepository : IKeybindingRepository
{
// Implement async persistence methods
public async Task SaveProfileAsync(Profile profile) { /* ... */ }
public async Task> LoadAllProfilesAsync() { /* ... */ }
// ... other methods
}
// Use with custom repository
var manager = new KeybindingManager(
new CommandRegistry(),
new ProfileManager(),
new DatabaseKeybindingRepository()
);
```
### Batch Operations
```csharp
// Register multiple commands at once
var commands = new[]
{
new Command("edit.cut", "Cut", "Cut to clipboard", "Edit"),
new Command("edit.copy", "Copy", "Copy to clipboard", "Edit"),
new Command("edit.paste", "Paste", "Paste from clipboard", "Edit")
};
int registered = manager.RegisterCommands(commands);
// Set multiple chord bindings
var chords = new Dictionary
{
{ "edit.cut", manager.Keybindings.ParseChord("Ctrl+X") },
{ "edit.copy", manager.Keybindings.ParseChord("Ctrl+C") },
{ "edit.paste", manager.Keybindings.ParseChord("Ctrl+V") }
};
int set = manager.SetChords(chords);
```
### Profile Management
```csharp
// Create specialized profiles
var vimProfile = new Profile("vim", "Vim Emulation", "Vim-style keybindings");
manager.Profiles.CreateProfile(vimProfile);
// Switch between profiles
manager.Profiles.SetActiveProfile("vim");
// ... set vim-style keybindings
manager.Profiles.SetActiveProfile("default");
// ... back to default keybindings
// Duplicate and customize
var customProfile = manager.Profiles.DuplicateProfile("default", "custom", "My Custom Profile");
```
### Command Organization
```csharp
// Search commands by name
var fileCommands = manager.Commands.SearchCommands("file");
// Filter by category
var editCommands = manager.Commands.GetCommandsByCategory("Edit");
// Check if command exists
if (manager.Commands.CommandExists("file.save"))
{
// Command is registered
}
```
## Sample Application
### Interactive Demo (`Keybinding.Demo`)
An interactive console application demonstrating all library features:
```bash
dotnet run --project Keybinding.Demo
```
The demo includes:
- Status overview
- Profile management
- Command registration
- Keybinding configuration
- Phrase binding and lookup
- Profile switching demonstration
## Data Storage
By default, the library stores data in JSON format in the specified directory:
```
data-directory/
├── commands.json # Registered commands
├── active-profile.json # Currently active profile ID
└── profiles/
├── default.json # Default profile keybindings
├── gaming.json # Gaming profile keybindings
└── custom.json # Custom profile keybindings
```
## Thread Safety
All services are designed to be thread-safe:
- `CommandRegistry` uses `ConcurrentDictionary` for command storage
- `ProfileManager` uses locking for profile operations
- `KeybindingService` coordinates safely across services
## Error Handling
The library provides comprehensive error handling:
- Invalid key combinations are rejected during parsing
- Duplicate command IDs are prevented
- Profile operations validate existence and constraints
- Repository operations handle I/O errors gracefully
## Testing
The solution includes comprehensive unit tests in the `Keybinding.Test` project:
```bash
dotnet test
```
Tests cover:
- Key combination parsing and validation
- Command registration and retrieval
- Profile management operations
- Keybinding service coordination
- Repository persistence operations
## Contributing
1. Fork the repository
2. Create a feature branch
3. Add tests for new functionality
4. Ensure all tests pass
5. Submit a pull request
## License
This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details.
## Architecture Principles
This library follows SOLID principles:
- **Single Responsibility**: Each class has a focused purpose
- **Open/Closed**: Extensible through interfaces without modification
- **Liskov Substitution**: Implementations are interchangeable
- **Interface Segregation**: Focused, specific interfaces
- **Dependency Inversion**: Depends on abstractions, not concretions
The design also follows DRY (Don't Repeat Yourself) principles with consistent patterns across the codebase.