diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..db57203
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,47 @@
+name: CI
+
+on:
+ push:
+ branches: [master, develop]
+ pull_request:
+
+permissions:
+ contents: read
+
+jobs:
+ test:
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-latest, macos-latest, windows-latest]
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
+ runs-on: ${{ matrix.os }}
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-python@v5
+ with:
+ python-version: ${{ matrix.python-version }}
+ cache: pip
+
+ - name: Install FFmpeg (Ubuntu)
+ if: runner.os == 'Linux'
+ run: sudo apt-get update && sudo apt-get install --yes ffmpeg
+
+ - name: Install FFmpeg (macOS)
+ if: runner.os == 'macOS'
+ run: brew install ffmpeg
+
+ - name: Install FFmpeg (Windows)
+ if: runner.os == 'Windows'
+ run: choco install ffmpeg --yes --no-progress
+
+ - name: Install package and test dependencies
+ run: python -m pip install --upgrade pip && python -m pip install -r requirements.txt
+
+ - name: Run tests
+ run: python -m pytest -q
+
+ - name: Build wheel
+ run: python -m pip wheel . --no-deps --wheel-dir dist
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b16c18f..8cc0e7a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,27 @@ All notable changes to the Sharp Frames project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.4.0] - 2026-07-16
+
+### Added
+- Scrollable frame timeline with keyboard navigation and full-result visualization
+- Resolution-normalized focus scoring for more consistent comparisons across source sizes
+- Cross-platform CI coverage for Python 3.10 through 3.13 on Linux, macOS, and Windows
+- Regression coverage for selection quality, output collisions, cancellation, and runtime cleanup
+
+### Changed
+- Selection methods now balance sharpness and temporal distribution more consistently
+- Frame extraction, analysis, preview, and saving pipelines provide stricter validation and failure reporting
+- Image-directory exports preserve content while correctly handling format conversion and case-insensitive filename collisions
+- Configuration and selection screens provide clearer controls, status, and keyboard behavior
+
+### Fixed
+- FFmpeg and FFprobe process cleanup, cancellation, timeout, and pipe handling
+- Partial or failed image reads being treated as valid zero-quality frames
+- Selection previews disagreeing with the frames produced during processing
+- Output metadata reporting frames that were not successfully saved
+- Platform-specific dependency guidance and Windows-safe process startup
+
## [0.3.1] - 2025-01-29
### Fixed
@@ -136,4 +157,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- outlier-removal: Remove outliers based on comparison with neighbors
- Interactive mode with guided prompts
- Proper Python package structure for pip and pipx installation
-- Command-line interface with `sharp-frames` command
\ No newline at end of file
+- Command-line interface with `sharp-frames` command
diff --git a/README.md b/README.md
index 25f88d6..fa06358 100644
--- a/README.md
+++ b/README.md
@@ -14,11 +14,13 @@ Or with pipx for isolated installation:
pipx install sharp-frames
```
-**IMPORTANT: Video Processing Requirement**: Install FFmpeg separately for video input support.
+**IMPORTANT: Video Processing Requirement**: Install an FFmpeg distribution that includes both `ffmpeg` and `ffprobe`. Both executables must be on `PATH`; image-directory processing does not require them.
- **Windows**: Download from [FFmpeg website](https://ffmpeg.org/download.html) and add to PATH
- **macOS**: `brew install ffmpeg`
- **Linux**: `sudo apt install ffmpeg`
+HDR-to-SDR extraction additionally requires an FFmpeg build with the `zscale` filter (`libzimg`). You can verify support with `ffmpeg -filters | grep zscale`. Non-HDR video processing does not require `zscale`.
+
## Quick Start
### Modern Interface (Default)
@@ -45,9 +47,9 @@ sharp-frames