Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,15 @@ jobs:
- platform: macos-latest
args: '--bundles dmg --target aarch64-apple-darwin'
arch: 'silicon'
rust_targets: 'aarch64-apple-darwin'
- platform: macos-latest
args: '--bundles dmg --target x86_64-apple-darwin'
arch: 'intel'
rust_targets: 'x86_64-apple-darwin'
- platform: windows-latest
args: '--bundles msi'
arch: 'x64'
rust_targets: ''

runs-on: ${{ matrix.platform }}
steps:
Expand All @@ -40,7 +46,7 @@ jobs:
- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
with:
targets: aarch64-apple-darwin,x86_64-apple-darwin
targets: ${{ matrix.rust_targets }}

- name: Install frontend dependencies
run: pnpm install
Expand All @@ -59,9 +65,12 @@ jobs:
|----------|--------------|----------|
| macOS | Apple Silicon (M1/M2/M3) | `bettershot_*_aarch64.dmg` |
| macOS | Intel | `bettershot_*_x64.dmg` |
| Windows | x64 | `bettershot_*_x64-setup.msi` |

## Installation

### macOS

1. Download the `.dmg` file for your Mac
- **Apple Silicon** (M1, M2, M3): Download the `aarch64` version
- **Intel Mac**: Download the `x64` version
Expand All @@ -80,6 +89,14 @@ jobs:

> **Note**: This is an ad-hoc signed indie app. macOS shows a warning for apps not notarized through Apple's $99/year developer program. The app is completely safe and open source.

### Windows

1. Download the `.msi` installer
2. Run the installer and follow the prompts
3. Launch Better Shot from the Start Menu or Desktop shortcut

> **Note**: Windows may show a SmartScreen warning for unsigned apps. Click "More info" → "Run anyway" to proceed. The app is completely safe and open source.

## What's New

- Initial release of Better Shot
Expand Down
24 changes: 18 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@

# Better Shot

> An open-source alternative to CleanShot X for macOS. Capture, edit, and enhance your screenshots with professional quality.
> An open-source alternative to CleanShot X for macOS and Windows. Capture, edit, and enhance your screenshots with professional quality.

Better Shot is a fast, lightweight screenshot tool built with Tauri and React. It provides a powerful yet simple interface for capturing screenshots, editing them with beautiful backgrounds and effects, and sharing them instantly.

## Features

### Capture Modes

- **Region Capture** - Select any area of your screen with pixel-perfect precision (`⌘⇧2`)
- **Fullscreen Capture** - Capture your entire screen instantly (`⌘⇧3`)
- **Window Capture** - Capture a specific window with one click (`⌘⇧4`)
- **Region Capture** - Select any area of your screen with pixel-perfect precision (`Cmd/Ctrl+Shift+2`)
- **Fullscreen Capture** - Capture your entire screen instantly (`Cmd/Ctrl+Shift+3`)
- **Window Capture** - Capture a specific window with one click (`Cmd/Ctrl+Shift+4`)

### Image Editing

Expand Down Expand Up @@ -41,7 +41,7 @@ Better Shot is a fast, lightweight screenshot tool built with Tauri and React. I
- **Clipboard Integration** - Automatically copy screenshots to clipboard
- **Custom Save Directory** - Choose where your screenshots are saved (defaults to Desktop)
- **Settings Persistence** - All preferences are saved and restored automatically
- **System Tray Integration** - Access from the menu bar
- **System Tray Integration** - Access from the menu bar (macOS) or system tray (Windows)
- **Native Performance** - Built with Rust and Tauri for minimal resource usage

### Preferences
Expand All @@ -53,6 +53,7 @@ Better Shot is a fast, lightweight screenshot tool built with Tauri and React. I
### Why Better Shot?

- **100% Free & Open Source** - No subscriptions, no paywalls
- **Cross-Platform** - Available for macOS and Windows
- **Lightweight** - Minimal resource usage compared to Electron apps
- **Beautiful UI** - Modern, dark-themed interface
- **Privacy First** - All processing happens locally, no cloud uploads
Expand All @@ -63,9 +64,12 @@ Better Shot is a fast, lightweight screenshot tool built with Tauri and React. I
### Download Pre-built Release

1. Go to [Releases](https://github.com/KartikLabhshetwar/better-shot/releases)
2. Download the appropriate DMG file:
2. Download the appropriate installer for your platform:

#### macOS
- **Apple Silicon** (M1, M2, M3): `bettershot_*_aarch64.dmg`
- **Intel Mac**: `bettershot_*_x64.dmg`

3. Open the DMG and drag Better Shot to Applications
4. **First Launch** (choose one method):

Expand All @@ -81,6 +85,13 @@ Better Shot is a fast, lightweight screenshot tool built with Tauri and React. I

> **Note**: Better Shot is ad-hoc signed (free indie app). macOS Gatekeeper shows a warning for apps not notarized through Apple's $99/year developer program. The app is safe - you can [view the source code](https://github.com/KartikLabhshetwar/better-shot) and build it yourself.

#### Windows
- Download `bettershot_*_x64-setup.msi`
- Run the installer and follow the prompts
- Launch Better Shot from the Start Menu or Desktop shortcut

> **Note**: Windows may show a SmartScreen warning for unsigned apps. Click "More info" → "Run anyway" to proceed. The app is completely safe and open source.

### From Source

```bash
Expand All @@ -100,6 +111,7 @@ The installer will be located in `src-tauri/target/release/bundle/`
### Requirements

- **macOS**: 10.15 or later
- **Windows**: Windows 10 or later
- **Node.js**: 18 or higher
- **pnpm**: Latest version
- **Rust**: Latest stable version (for building from source)
Expand Down
68 changes: 66 additions & 2 deletions src-tauri/src/clipboard.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,32 @@
//! Clipboard operations module

use crate::utils::AppResult;
use std::process::Command;

/// Copy an image file to the system clipboard
/// Uses platform-specific methods for each OS
pub fn copy_image_to_clipboard(image_path: &str) -> AppResult<()> {
#[cfg(target_os = "macos")]
{
copy_image_to_clipboard_macos(image_path)
}

#[cfg(target_os = "windows")]
{
copy_image_to_clipboard_windows(image_path)
}

#[cfg(not(any(target_os = "macos", target_os = "windows")))]
{
Err(format!("Clipboard copy not supported on this platform"))
}
}

/// Copy an image file to the system clipboard using macOS native APIs
/// This approach works with clipboard managers like Raycast
pub fn copy_image_to_clipboard(image_path: &str) -> AppResult<()> {
#[cfg(target_os = "macos")]
fn copy_image_to_clipboard_macos(image_path: &str) -> AppResult<()> {
use std::process::Command;

// Use osascript to copy the image file to clipboard
// This method properly integrates with macOS clipboard and clipboard managers
let script = format!(
Expand All @@ -26,3 +47,46 @@ pub fn copy_image_to_clipboard(image_path: &str) -> AppResult<()> {

Ok(())
}

/// Copy an image file to the system clipboard using Windows APIs via PowerShell
#[cfg(target_os = "windows")]
fn copy_image_to_clipboard_windows(image_path: &str) -> AppResult<()> {
use std::path::Path;
use std::process::Command;

// Validate that the file exists and is a regular file
let path = Path::new(image_path);
if !path.exists() {
return Err(format!("File not found: {}", image_path));
}
if !path.is_file() {
return Err(format!("Path is not a file: {}", image_path));
}

// Get the canonical path to ensure it's a valid, absolute path
let canonical_path = path
.canonicalize()
.map_err(|e| format!("Failed to resolve path: {}", e))?;
let path_str = canonical_path.to_string_lossy();

// Use PowerShell to copy the image to clipboard
// This uses .NET's System.Windows.Forms.Clipboard class
// Escape single quotes for PowerShell string literal
let escaped_path = path_str.replace("'", "''");
let script = format!(
r#"Add-Type -AssemblyName System.Windows.Forms; $image = [System.Drawing.Image]::FromFile('{}'); [System.Windows.Forms.Clipboard]::SetImage($image); $image.Dispose()"#,
escaped_path
);

let output = Command::new("powershell")
.args(["-NoProfile", "-NonInteractive", "-Command", &script])
.output()
.map_err(|e| format!("Failed to execute PowerShell: {}", e))?;

if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
return Err(format!("Failed to copy image to clipboard: {}", stderr));
}

Ok(())
}
Loading