Contributing to TinyClips

August 12, 2026 · View on GitHub

Thank you for your interest in contributing to TinyClips! This guide covers everything you need to get started.

Table of Contents


Requirements

  • macOS 15.0 (Sequoia) or later
  • Xcode 16.0 or later
  • An Apple Developer account (needed for screen recording entitlements when running locally)

Project Setup

1. Fork and clone the repository

git clone https://github.com/<your-username>/tiny-clips.git
cd tiny-clips

2. Open the project in Xcode

open mac/TinyClips.xcodeproj

3. Add the Sparkle dependency (direct distribution target only)

Sparkle is not committed to the repository and must be added via Xcode:

  1. Go to File → Add Package Dependencies…
  2. Enter URL: https://github.com/sparkle-project/Sparkle
  3. Select Up to Next Major Version from 2.8.1
  4. Add the Sparkle framework to the TinyClips target only (not TinyClipsMAS)

See docs/sparkle-setup.md for full details.

4. Select a scheme and run

SchemeDescription
TinyClipsDirect distribution — non-sandboxed, Sparkle-enabled
TinyClipsMASMac App Store — sandboxed, no Sparkle

Press ⌘R to build and run the selected scheme.

Building without code signing (CI / headless)

# Direct distribution
xcodebuild build -project mac/TinyClips.xcodeproj -scheme TinyClips -configuration Debug \
  CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO

# Mac App Store variant
xcodebuild build -project mac/TinyClips.xcodeproj -scheme TinyClipsMAS -configuration Debug \
  CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO

Running macOS unit tests

The TinyClipsTests target covers deterministic capture math, settings, hotkey, filename, and analytics behavior without requiring capture permissions or hardware:

xcodebuild test -project mac/TinyClips.xcodeproj -scheme TinyClips -configuration Debug \
  CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO

Permissions

TinyClips requires Screen Recording permission at runtime. On first launch, macOS will prompt you to grant access in System Settings → Privacy & Security → Screen Recording.


Project Structure

mac/TinyClips/
├── TinyClipsApp.swift          # App entry point, MenuBarExtra, CaptureManager
├── Models/
│   ├── CaptureSettings.swift   # @AppStorage settings model and shared types
│   └── ClipItem.swift          # Model for saved clips
├── Capture/
│   ├── RegionSelector.swift    # Fullscreen overlay for region selection
│   ├── ScreenshotCapture.swift # SCScreenshotManager → PNG
│   ├── VideoRecorder.swift     # SCStream → AVAssetWriter → MP4
│   └── GifWriter.swift         # SCStream → CGImageDestination → GIF
├── Services/
│   ├── SaveService.swift       # File saving, clipboard, Finder, notifications
│   ├── PermissionManager.swift # Screen recording permission handling
│   ├── SparkleController.swift # Sparkle auto-update integration
│   ├── StoreService.swift      # StoreKit subscription management
│   └── ProManager.swift        # Pro feature gating (MAS only)
└── Views/
    ├── SettingsView.swift       # Settings window
    ├── ClipsManagerWindow.swift # Clips Manager window
    ├── VideoTrimmerWindow.swift # Post-recording trim editor
    ├── CapturePickerPanel.swift # Floating mode/countdown picker
    ├── ScreenPickerWindow.swift # Multi-display screen picker
    ├── RegionIndicatorPanel.swift # Red region outline overlay
    ├── StartRecordingPanel.swift  # Floating record panel
    ├── StopRecordingPanel.swift   # Floating stop panel
    └── ...
docs/                           # Additional setup guides

Two build targets share one codebase. Use #if APPSTORE compilation conditions for any Mac App Store–specific behavior and #if canImport(Sparkle) to guard Sparkle imports.


Development Tips

Architecture overview

  • Menu bar app using SwiftUI MenuBarExtra with no Dock icon (LSUIElement = true).
  • Mixed SwiftUI + AppKit: SwiftUI for the menu, settings, and Clips Manager; AppKit NSWindow/NSPanel subclasses for capture-time overlays.
  • CaptureManager (in TinyClipsApp.swift) is the central coordinator that owns recorders and capture window lifecycles.
  • Singleton services: CaptureSettings.shared, SaveService.shared, PermissionManager.shared, SparkleController.shared.

Key conventions

  • Use ObservableObject / @Published / @StateObjectnot the @Observable macro.
  • Use @AppStorage for all user preferences (see CaptureSettings.swift).
  • Mark all UI-facing classes @MainActor. Use @unchecked Sendable + DispatchQueue for off-main-thread capture classes.
  • Use // MARK: - comments for section organization within files.
  • Single CaptureError enum conforming to LocalizedError; surface errors via SaveService.shared.showError().

Accessibility

Accessibility is treated as a release gate. When adding or changing UI:

  • Add explicit .accessibilityLabel, .accessibilityHint, and .accessibilityValue on icon-only buttons, custom controls, toggles, and stateful elements.
  • Ensure custom controls have keyboard alternatives (default/cancel shortcuts, Esc cancel paths).
  • Validate both TinyClips and TinyClipsMAS schemes with VoiceOver and keyboard-only navigation.

Capture flows

  1. Screenshot: permission → picker (region/screen/window + countdown) → optional screen picker for multi-display → optional region indicator → capture → optional editor → save.
  2. Video/GIF: permission → picker → optional region indicator → start panel → optional countdown → record → stop panel → optional trimmer → save.

Contribution Workflow

  1. Check existing issues — search open issues before opening a new one or starting work.
  2. Open or claim an issue — comment on the issue you intend to work on so others know it's in progress.
  3. Create a feature branch off main:
    git checkout -b feature/short-description
    
  4. Make your changes following the code style guidelines below.
  5. Run unit tests and build both schemes to confirm nothing is broken:
    xcodebuild test -project mac/TinyClips.xcodeproj -scheme TinyClips -configuration Debug \
      CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO
    
    xcodebuild build -project mac/TinyClips.xcodeproj -scheme TinyClips -configuration Debug \
      CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO
    
    xcodebuild build -project mac/TinyClips.xcodeproj -scheme TinyClipsMAS -configuration Debug \
      CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO
    
  6. Push your branch and open a pull request against main.

Pull Request Guidelines

  • One concern per PR — keep pull requests focused on a single feature, fix, or improvement.
  • Descriptive title — use a short, imperative sentence (e.g., "Add countdown timer to GIF picker").
  • Fill in the PR description — explain what changed and why. Link the related issue with Closes #123.
  • Both schemes must build — the CI workflow builds both TinyClips and TinyClipsMAS; ensure your changes compile under both.
  • No Sparkle in TinyClipsMAS — guard any Sparkle references with #if canImport(Sparkle) or #if !APPSTORE.
  • Update the CHANGELOG — add an entry under the appropriate section (Added, Improved, or Fixed) in CHANGELOG.md.
  • Accessibility — if your change affects any UI, verify it works with VoiceOver and keyboard-only navigation.
  • Small, incremental commits — prefer clear, atomic commits with descriptive messages.

Code Style

  • Swift 5, targeting macOS 15.0+.
  • Follow the existing file and naming conventions — see the // MARK: - section organization in existing files as a reference.
  • Keep SwiftUI views defined as private struct inside their window/panel file.
  • Use popover(item:) over popover(isPresented:) for data-dependent popovers.
  • Prefer closures/callbacks for inter-component communication — no NotificationCenter posting.
  • Use withCheckedContinuation / withCheckedThrowingContinuation to bridge callback APIs to async/await.
  • Output file names follow the pattern TinyClips yyyy-MM-dd 'at' HH.mm.ss.{ext}.

Reporting Bugs

Please use the bug report template when opening a new issue. Include:

  • macOS version
  • TinyClips version
  • Steps to reproduce
  • Expected vs. actual behavior
  • Any relevant logs or screenshots

Thank you for helping make TinyClips better!