Skip to content
Merged
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
63 changes: 63 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: CI

on:
pull_request:
branches: [master]
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

jobs:
check:
name: Checks
runs-on: windows-2025
timeout-minutes: 45

steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 11.14.0

- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 24
cache: pnpm

- name: Install Rust
uses: dtolnay/rust-toolchain@stable

- name: Cache Rust
uses: Swatinem/rust-cache@v2
with:
workspaces: apps/desktop/src-tauri

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Check version consistency
run: pnpm version:check

- name: Typecheck
run: pnpm typecheck

- name: Lint
run: pnpm lint

- name: Test
run: pnpm test

- name: Verify native application
run: |
cargo fmt --manifest-path apps/desktop/src-tauri/Cargo.toml --check
cargo clippy --manifest-path apps/desktop/src-tauri/Cargo.toml --all-targets -- -D warnings
cargo test --manifest-path apps/desktop/src-tauri/Cargo.toml
121 changes: 121 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
name: Release

on:
push:
branches: [master]
workflow_dispatch:

permissions:
contents: write

concurrency:
group: release

jobs:
detect:
name: Detect version change
runs-on: ubuntu-latest
outputs:
release: ${{ steps.check.outputs.release }}
version: ${{ steps.check.outputs.version }}
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Check for a matching tag
id: check
run: |
VERSION=$(node -p "require('./apps/desktop/src-tauri/tauri.conf.json').version")
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
if git rev-parse -q --verify "refs/tags/v$VERSION" >/dev/null; then
echo "release=false" >> "$GITHUB_OUTPUT"
else
echo "release=true" >> "$GITHUB_OUTPUT"
fi

build:
name: Build and publish
needs: detect
if: needs.detect.outputs.release == 'true'
runs-on: windows-2025
timeout-minutes: 60
env:
VERSION: ${{ needs.detect.outputs.version }}

steps:
- name: Check out repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 11.14.0

- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 24
cache: pnpm

- name: Install Rust
uses: dtolnay/rust-toolchain@stable

- name: Cache Rust
uses: Swatinem/rust-cache@v2
with:
workspaces: apps/desktop/src-tauri

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Check version consistency
run: pnpm version:check

- name: Typecheck
run: pnpm typecheck

- name: Lint
run: pnpm lint

- name: Test
run: pnpm test

- name: Verify native application
run: |
cargo fmt --manifest-path apps/desktop/src-tauri/Cargo.toml --check
cargo clippy --manifest-path apps/desktop/src-tauri/Cargo.toml --all-targets -- -D warnings
cargo test --manifest-path apps/desktop/src-tauri/Cargo.toml

- name: Build Windows installer
env:
CAPKIT_COMMIT: ${{ github.sha }}
run: pnpm bundle:windows

- name: Publish GitHub release
shell: powershell
env:
GH_TOKEN: ${{ github.token }}
run: |
$installer = "apps/desktop/src-tauri/target/release/bundle/nsis/CapKit_${{ env.VERSION }}_x64-setup.exe"
if (!(Test-Path -LiteralPath $installer)) {
throw "Missing installer at $installer"
}
$hash = (Get-FileHash -Algorithm SHA256 -Path $installer).Hash.ToLowerInvariant()
$checksum = "$installer.sha256"
Set-Content -LiteralPath $checksum -Value $hash -NoNewline
gh api "repos/${{ github.repository }}/releases/generate-notes" -f tag_name="v${{ env.VERSION }}" -f target_commitish="${{ github.sha }}" --jq .body > notes.md
Add-Content -Path notes.md -Value ""
Add-Content -Path notes.md -Value "Full changelog: CHANGELOG-PUBLIC.md"
gh release create "v${{ env.VERSION }}" $installer $checksum --target "${{ github.sha }}" --title "CapKit ${{ env.VERSION }}" --notes-file notes.md

- name: Upload installer artifact
uses: actions/upload-artifact@v4
with:
name: CapKit-${{ env.VERSION }}-windows-x64
path: apps/desktop/src-tauri/target/release/bundle/nsis/CapKit_${{ env.VERSION }}_x64-setup.exe
if-no-files-found: error
compression-level: 0
4 changes: 3 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ Also verify affected components, multi-monitor/DPI-sensitive geometry, loading,
- Landing page: Next.js 16.2 App Router, React 19, TypeScript 5, Tailwind CSS 4, Framer Motion 12
- Tooling: ESLint 9, Vitest 4, Testing Library, Cargo
- Targets: Windows 11 supported; macOS, Ubuntu, and Fedora provisional
- CI: no every-PR workflow is currently present; setup records the requested CI policy
- CI: `ci.yml` on every PR to master; `release.yml` publishes a GitHub Release when the version changes on master

## Commands

Expand All @@ -89,6 +89,8 @@ Also verify affected components, multi-monitor/DPI-sensitive geometry, loading,
| Landing lint | `pnpm.cmd --filter @snaphub/landing-page lint` |
| Store package | `pnpm.cmd bundle:store` |
| Store package test | `pnpm.cmd test:store` |
| Version bump | `pnpm.cmd version:bump minor` |
| Version check | `pnpm.cmd version:check` |

## Project docs

Expand Down
20 changes: 15 additions & 5 deletions CHANGELOG-PUBLIC.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,30 @@

User-facing changes only, grouped by date under Added / Improved / Fixed.

## 2026-09-26
## 0.2.0 — 2026-09-26

### Fixed
### Added

- Text in Screen Draw now appears where you click.
- Recording opens again from Record and its shortcut.
- Select tool in Screen Draw: move, resize and delete what you've drawn. It's now the default.
- Copy & Save to selected-region completion, with a configurable `A` shortcut by default. It copies the finished image first and saves the same rendered pixels to your configured folder.
- Save the whole screen with your Screen Draw annotations by pressing S.

### Improved

- Capture opens faster and closes instantly.
- Settings are organized into General, Screenshots, Screen Draw and Shortcuts tabs.
- Record now holds your recordings and editing; Dashboard is renamed Screenshots.
- Capture opens faster and closes instantly.
- Drag pinned images anywhere with the handle in their corner.

### Fixed

- Closing capture no longer leaves a black screen.
- Text in Screen Draw now appears where you click.
- Recording opens again from Record and its shortcut.
- The capture screen no longer shifts slightly when it opens, and saved screenshots match the selected area exactly.
- Capture no longer flashes dark when it opens.
- The Screen Draw laser follows the cursor smoothly and fades out after you release it.

### Removed

- Click-through for pinned images.
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@snaphub/desktop",
"version": "0.1.1",
"version": "0.2.0",
"private": true,
"type": "module",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion apps/desktop/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "capkit-desktop"
version = "0.1.1"
version = "0.2.0"
description = "CapKit lightweight desktop toolkit to capture, record, and showcase"
authors = ["CapKit"]
edition = "2024"
Expand Down
21 changes: 20 additions & 1 deletion apps/desktop/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ use tauri::{
};
use tauri_plugin_global_shortcut::{GlobalShortcutExt, Shortcut, ShortcutState};
#[cfg(target_os = "windows")]
use windows::Win32::UI::WindowsAndMessaging::{AW_BLEND, AnimateWindow, SetForegroundWindow};
use windows::Win32::UI::WindowsAndMessaging::{
AW_BLEND, AnimateWindow, SW_HIDE, SetForegroundWindow, ShowWindow,
};

// AnimateWindow's cross-fade masks the one or two frames before WebView2's swap chain
// has composited fresh content, which otherwise reads as a black flash on reveal.
Expand All @@ -46,6 +48,13 @@ fn reveal_window_smoothly(window: &tauri::WebviewWindow) -> Result<(), SnaphubEr
if let Ok(hwnd) = window.hwnd() {
let hwnd = windows::Win32::Foundation::HWND(hwnd.0);
if unsafe { AnimateWindow(hwnd, CAPTURE_REVEAL_MS, AW_BLEND) }.is_ok() {
// AnimateWindow shows the window through Win32 without telling Tauri, so
// tao's diff-based `set_visible` still believes it is hidden and a later
// `window.hide()` becomes a no-op. Calling `window.show()` on the already
// visible window only syncs tao's `VISIBLE` flag so future hides work.
window
.show()
.map_err(|error| SnaphubError::Window(error.to_string()))?;
return Ok(());
}
}
Expand Down Expand Up @@ -918,6 +927,16 @@ fn hide_capture_window(app: &AppHandle) -> Result<(), SnaphubError> {
window
.hide()
.map_err(|error| SnaphubError::Window(error.to_string()))?;
// Second safeguard for any other direct-Win32 show path (see
// `reveal_window_smoothly`): hide through Win32 as well so the overlay
// cannot stay visible when tao's `VISIBLE` flag is out of sync.
#[cfg(target_os = "windows")]
{
if let Ok(hwnd) = window.hwnd() {
let hwnd = windows::Win32::Foundation::HWND(hwnd.0);
let _ = unsafe { ShowWindow(hwnd, SW_HIDE) };
}
}
}
Ok(())
}
Expand Down
7 changes: 5 additions & 2 deletions apps/desktop/src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "CapKit",
"version": "0.1.1",
"version": "0.2.0",
"identifier": "com.snaphub.desktop",
"build": {
"beforeDevCommand": "pnpm dev",
Expand Down Expand Up @@ -45,7 +45,10 @@
"csp": "default-src 'self' customprotocol: asset:; img-src 'self' asset: http://asset.localhost blob: data:; style-src 'self' 'unsafe-inline'; connect-src ipc: http://ipc.localhost",
"assetProtocol": {
"enable": true,
"scope": ["$TEMP/CapKit/**", "$PICTURE/CapKit/**"]
"scope": [
"$TEMP/CapKit/**",
"$PICTURE/CapKit/**"
]
}
}
},
Expand Down
Loading
Loading