diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..d75b851
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,97 @@
+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
+
+ minimum-textual:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-python@v5
+ with:
+ python-version: "3.10"
+ cache: pip
+
+ - name: Install FFmpeg
+ run: sudo apt-get update && sudo apt-get install --yes ffmpeg
+
+ - name: Install minimum supported Textual
+ run: >-
+ python -m pip install --upgrade pip &&
+ python -m pip install -e . -r tests/requirements.txt "textual==5.0.0" &&
+ python -m pip check
+
+ - name: Run tests at the minimum Textual version
+ run: python -m pytest -q
+
+ package:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-python@v5
+ with:
+ python-version: "3.13"
+ cache: pip
+
+ - name: Install build tooling and FFmpeg
+ run: |
+ sudo apt-get update && sudo apt-get install --yes ffmpeg
+ python -m pip install --upgrade pip build
+
+ - name: Build distribution artifacts
+ run: python -m build
+
+ - name: Smoke test installed wheel outside the checkout
+ run: |
+ python -m venv "$RUNNER_TEMP/wheel-smoke"
+ "$RUNNER_TEMP/wheel-smoke/bin/python" -m pip install dist/*.whl
+ cd "$RUNNER_TEMP"
+ "$RUNNER_TEMP/wheel-smoke/bin/python" -c "import sharp_frames; from sharp_frames.ui.components.raster_preview import RasterImagePreview; print(sharp_frames.__version__)"
+ "$RUNNER_TEMP/wheel-smoke/bin/sharp-frames" --help
diff --git a/.gitignore b/.gitignore
index aa61f75..b104b7a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,9 +30,10 @@ pip-delete-this-directory.txt
htmlcov/
.tox/
-# Editor files
+# Editor and local agent files
.vscode/
.idea/
+.codex/
*.swp
*.swo
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b16c18f..9d372dd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,35 @@ 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-18
+
+### Added
+- Scrollable full-frame selection timeline with click-to-inspect and keyboard navigation
+- Inline terminal frame preview via Sixel/Kitty raster graphics with an external-viewer fallback
+- Automatic color space detection and conversion to sRGB for HDR and wide-gamut sources (BT.2020, HLG, PQ)
+- Resolution-normalized focus scoring for more consistent comparisons across source sizes
+- Trend-aware outlier detection with improved defaults
+- Responsive selection layout that keeps the preview and chart visible on short terminals
+- Shared alpha-preserving image decode/resize/encode helpers
+- Cross-platform CI coverage for Python 3.10 through 3.13 on Linux, macOS, and Windows, plus the minimum supported Textual version
+- 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; the processing screen is vertically centered
+- Textual requirement raised to 5.0 or newer; new dependencies `textual-image` and `Pillow`
+
+### 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
+- Start Over after saving returning to the middle of setup instead of the first step
+- Cancel during setup leaving the application running on an empty screen
+
## [0.3.1] - 2025-01-29
### Fixed
@@ -136,4 +165,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..8c277e2 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