Skip to main content

Welcome

Contributions and suggestions are welcome! Scratch is designed to be a minimal, focused note-taking app with exceptional user experience.
What makes Scratch special is its minimal feature set and focus on user experience. We’re not trying to build Obsidian or Notion, so not every feature will be a fit.

Before Contributing

Small Fixes and Improvements

Go ahead and open a PR for:
  • Bug fixes
  • Performance improvements
  • UI/UX refinements
  • Documentation updates
  • Code quality improvements
We’ll try to merge these in regularly.

Bigger Changes

If you’re not sure whether a feature fits Scratch’s philosophy:
1

Open an issue first

Describe the feature and explain how it aligns with Scratch’s minimal, focused approach.
2

Wait for feedback

The maintainers will discuss whether the feature is a good fit.
3

Implement if approved

Once approved, proceed with implementation following the code quality standards below.

Development Philosophy

Scratch prioritizes:
  1. Simplicity - Minimal feature set, focused on core note-taking
  2. Performance - 5-10x smaller and faster than alternatives
  3. User Experience - Keyboard-first, distraction-free, polished
  4. Code Quality - Clean, maintainable, low technical debt

Code Quality Standards

General Principles

  • Clean codebase - No commented-out code or TODOs in production
  • Type safety - TypeScript throughout the frontend
  • Proper patterns - React contexts, hooks, memoization where appropriate
  • Performance first - Debouncing, async operations, memoization

Frontend (React/TypeScript)

// Use dual-context pattern for performance
import { createContext, useContext } from 'react';

// Data context - frequently changing values
const NotesDataContext = createContext(null);

// Actions context - stable references
const NotesActionsContext = createContext(null);

// Separate hooks for data and actions
export const useNotesData = () => useContext(NotesDataContext);
export const useNotesActions = () => useContext(NotesActionsContext);

Backend (Rust)

// Use proper error handling
#[tauri::command]
async fn save_note(
    note_id: String,
    content: String,
    state: State<'_, AppState>,
) -> Result<(), String> {
    // Implementation
    Ok(())
}

Pull Request Process

1

Fork and clone

Fork the repository and clone your fork locally:
git clone https://github.com/YOUR_USERNAME/scratch.git
cd scratch
2

Create a feature branch

git checkout -b feature/your-feature-name
3

Make your changes

Follow the code quality standards and test your changes thoroughly.
4

Address CodeRabbit comments

The repository uses CodeRabbit for automated code review. Address any comments before requesting review.
5

Submit pull request

Push your branch and open a PR with a clear description of the changes.
6

Review process

The maintainers generally won’t go back and forth with review comments. After addressing CodeRabbit feedback, any additional changes will be made directly.
Make sure to test your changes on your target platform(s) before submitting.

Testing Guidelines

Manual Testing

Since Scratch focuses on user experience, thorough manual testing is essential:
  1. Core workflows:
    • Creating, editing, and deleting notes
    • Search functionality
    • Wikilinks and markdown features
    • Keyboard shortcuts
  2. Edge cases:
    • Large notes (>10,000 lines)
    • Many notes (>1,000 files)
    • Special characters in titles
    • External file modifications
  3. Platform-specific:
    • Test on macOS, Windows, or Linux as applicable
    • Verify native integrations work correctly

Performance Testing

  • Check auto-save doesn’t cause lag
  • Verify search is responsive with many notes
  • Ensure file watcher doesn’t impact performance

Development Setup

See the Building from Source guide for detailed setup instructions.

Quick Start

# Clone repository
git clone https://github.com/erictli/scratch.git
cd scratch

# Install dependencies
npm install

# Run in development mode
npm run tauri dev

Code Style

TypeScript/React

  • Use functional components with hooks
  • Prefer const over let
  • Use arrow functions for callbacks
  • Extract complex logic into custom hooks
  • Keep components small and focused

Rust

  • Follow standard Rust formatting (cargo fmt)
  • Use clippy for linting (cargo clippy)
  • Prefer anyhow for internal error handling
  • Convert errors to strings at Tauri command boundaries

Naming Conventions

  • Components: PascalCase (EditorToolbar.tsx)
  • Hooks: camelCase with use prefix (useNotes.ts)
  • Utilities: camelCase (formatDate.ts)
  • Constants: UPPER_SNAKE_CASE (MAX_RESULTS)
  • Tauri commands: snake_case (save_note)

Project Structure

scratch/
├── src/                    # React frontend
│   ├── components/         # UI components
│   ├── context/           # React contexts
│   ├── services/          # Tauri command wrappers
│   └── types/             # TypeScript types
├── src-tauri/             # Rust backend
│   ├── src/
│   │   ├── lib.rs         # Tauri commands
│   │   └── git.rs         # Git integration
│   └── capabilities/      # Tauri permissions
└── docs/                  # Documentation
For detailed architecture information, see Architecture.

Getting Help

If you need help with your contribution:
  • Open an issue for discussion
  • Check existing issues for similar questions
  • Review the Architecture documentation
  • Look at recent PRs for examples

License

By contributing to Scratch, you agree that your contributions will be licensed under the MIT License.

Recognition

All contributors are appreciated! Significant contributions will be recognized in release notes.

View on GitHub

Visit the repository to get started

Build docs developers (and LLMs) love