Skip to content

Latest commit

 

History

History
322 lines (243 loc) · 8 KB

File metadata and controls

322 lines (243 loc) · 8 KB

Contributing to bucklezig

Thank you for considering contributing to bucklezig! This document outlines how to contribute to the project.

Table of Contents

Code of Conduct

  • Be respectful and constructive in all interactions
  • Focus on what is best for the community and project
  • Show empathy towards other contributors
  • Accept constructive criticism gracefully

Getting Started

Prerequisites

  • Zig 0.11+: Install Zig
  • Go 1.21+: Install Go
  • Platform Requirements:
    • macOS: Xcode Command Line Tools (xcode-select --install)
    • Linux: ALSA development libraries (libasound2-dev)

Setup

  1. Fork the repository on GitHub
  2. Clone your fork:
    git clone https://github.com/YOUR_USERNAME/bucklezig.git
    cd bucklezig
  3. Create sound profiles (see Audio Profile Guidelines)
  4. Build the project:
    make build
  5. Run tests:
    make test

Development Workflow

We follow Test-Driven Development (TDD):

  1. Red: Write a failing test for the new feature/bugfix
  2. Green: Write minimal code to make the test pass
  3. Refactor: Improve code quality while keeping tests green

Branch Strategy

  • public - Main development branch (default)
  • Feature branches: feature/descriptive-name
  • Bugfix branches: fix/issue-description

Making Changes

# Create a feature branch
git checkout -b feature/my-new-feature

# Make changes, write tests
# Ensure tests pass
make test

# Commit with descriptive message
git commit -am "Add feature: brief description"

# Push to your fork
git push origin feature/my-new-feature

# Open a Pull Request on GitHub

Audio Profile Guidelines

Sourcing Audio Files

IMPORTANT: Only use audio files you have legal rights to distribute. Acceptable sources:

  • Public Domain: Freesound.org (CC0 license)
  • Creative Commons: Verify license allows redistribution (CC-BY, CC-BY-SA)
  • Self-recorded: Your own keyboard recordings
  • Royalty-free libraries: Check licensing terms carefully

DO NOT:

  • ❌ Rip audio from commercial products
  • ❌ Use copyrighted sounds without permission
  • ❌ Submit profiles with unclear licensing

Profile Structure

Each profile requires 256 WAV files in this structure:

profiles/
└── YourProfileName/
    ├── 1-down.wav      # Key 1 press
    ├── 1-up.wav        # Key 1 release
    ├── 2-down.wav
    ├── 2-up.wav
    ...
    ├── 128-down.wav
    └── 128-up.wav

Audio Specifications

  • Format: WAV (uncompressed PCM)
  • Sample Rate: 44100 Hz or 48000 Hz
  • Bit Depth: 16-bit or 24-bit
  • Channels: Mono or Stereo
  • File Size: Keep under 500KB per file for fast loading

Submitting Profiles

When submitting a new profile:

  1. Include a LICENSE.txt in the profile directory stating:

    • Sound source/author
    • License type (e.g., "CC0 Public Domain")
    • Attribution requirements (if any)
  2. In your PR description, include:

    • Profile name and description
    • Sound source URL
    • License confirmation
    • Recording setup (if original)

Example:

profiles/Cherry-MX-Blue/
├── LICENSE.txt         # "Sounds by John Doe, CC0 Public Domain"
├── 1-down.wav
├── 1-up.wav
...

Testing Requirements

Coverage Standards (Non-Negotiable)

  • Minimum: 80% overall coverage
  • Critical paths: 100% coverage required
    • Audio playback engine
    • Profile loading/validation
    • Keyboard capture
    • CGO bridge functions

Running Tests

# Zig tests
cd zig && zig build test

# Go tests
cd go && go test ./...

# Integration tests
make test-integration

# Coverage report
make test-coverage

Writing Tests

Zig Example:

test "audio engine loads valid WAV file" {
    const allocator = std.testing.allocator;
    const engine = try AudioEngine.init(allocator);
    defer engine.deinit();
    
    try engine.loadSound("test.wav");
    try std.testing.expect(engine.isLoaded());
}

Go Example:

func TestProfileSwitching(t *testing.T) {
    model := initialModel()
    model, _ = model.Update(tea.KeyMsg{Type: tea.KeyDown})
    
    assert.Equal(t, 1, model.selectedProfile)
}

Code Style

Zig

  • Formatting: Run zig fmt before committing
  • Naming: snake_case for functions/variables
  • Error Handling: Use error unions, never ignore errors
  • Documentation: Doc comments (///) for public APIs
  • Memory: Explicit allocators, no hidden allocations

Example:

/// Loads a WAV file from disk into memory.
/// Returns error if file is invalid or memory allocation fails.
pub fn loadWav(allocator: Allocator, path: []const u8) !Sound {
    const file = try std.fs.cwd().openFile(path, .{});
    defer file.close();
    // ...
}

Go

  • Formatting: Run gofmt before committing
  • Linting: Ensure golangci-lint run passes (no warnings)
  • Naming: camelCase for unexported, PascalCase for exported
  • Error Handling: Wrap errors with context using fmt.Errorf with %w
  • BubbleTea: Follow Model-Update-View separation

Example:

// LoadProfile loads a sound profile by name.
// Returns an error if the profile directory doesn't exist or is incomplete.
func LoadProfile(name string) (*Profile, error) {
    path := filepath.Join("profiles", name)
    if _, err := os.Stat(path); err != nil {
        return nil, fmt.Errorf("profile not found: %w", err)
    }
    // ...
}

CGO Interface

  • Keep C API surface minimal
  • Use only C-compatible types (int, char*)
  • Document thread safety requirements
  • Target <1ms overhead per call

Submitting Changes

Pull Request Process

  1. Update documentation if adding features

  2. Add tests covering your changes

  3. Run full test suite and ensure it passes

  4. Update AGENTS.md if changing architecture

  5. Write clear commit messages:

    Add profile hot-swap without audio interruption
    
    - Implement lock-free buffer swapping
    - Add integration tests for profile switching
    - Update TUI to show switching status
    
    Closes #42
    
  6. Open PR with:

    • Clear title describing the change
    • Detailed description of what and why
    • Link to related issues
    • Screenshots/demos if UI changes

PR Review Checklist

Before requesting review, verify:

  • All tests pass (make test)
  • Code coverage meets 80% minimum
  • Code is formatted (zig fmt, gofmt)
  • Linter passes (golangci-lint run)
  • Documentation updated
  • Commit messages are descriptive
  • No debug code or commented-out blocks
  • Audio files have proper licensing (if applicable)

Performance Requirements

Changes must maintain or improve:

  • Audio latency: <10ms from key event to sound
  • Profile switching: <200ms
  • Startup time: <500ms
  • Memory usage: <100MB per profile
  • CPU idle: <1%

If your change impacts performance, include benchmark results.

Platform-Specific Contributions

macOS

  • Test with both Intel and Apple Silicon
  • Verify IOKit keyboard capture works
  • Check CoreAudio compatibility

Linux

  • Test with ALSA and PulseAudio
  • Verify evdev keyboard capture
  • Test on multiple distributions (Ubuntu, Fedora, Arch)

Questions?

  • Bug reports: Open an issue with detailed reproduction steps
  • Feature requests: Open an issue describing the use case
  • Discussion: Start a GitHub Discussion
  • Security issues: Email maintainer directly (see README)

Attribution

This project was initially developed with assistance from:

  • OpenCode by OpenCoder AI
  • Claude Sonnet 4.5 by Anthropic

We welcome contributions from all developers! 🎉