Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
2bb957a
build(cargo): workspace deps, release profile, rand 0.9, total_cmp
lovelaced Jul 20, 2026
122b7e3
refactor(error): typed NormalizeError across the lib, exit-code table
lovelaced Jul 20, 2026
fff0c72
feat(options): PipelineOptions as the single config-assembly path
lovelaced Jul 20, 2026
c2b086d
feat(cli): all-Option flags, ValueEnum, arg groups; main uses options…
lovelaced Jul 20, 2026
7c5e91d
feat(grid): fractional Grid type; margins become partial blocks
lovelaced Jul 20, 2026
a6f91e7
feat(grid-detect): fractional-pitch detection via gradient profiles
lovelaced Jul 20, 2026
90db51c
feat(color): hyAB metric, shared is_between_oklab, histogram iterators
lovelaced Jul 20, 2026
e4a8c6e
feat(quantize): seeded weighted k-means over unique colors, medoid pa…
lovelaced Jul 20, 2026
124a297
feat(aa): alpha-aware neighbors, multi-pass fixpoint, dither guard
lovelaced Jul 20, 2026
7eb3831
feat(snap): OKLAB-agglomerated weighted-medoid votes, binarized alpha…
lovelaced Jul 20, 2026
dda86e8
feat(background): OKLAB border clustering, pre-snap mask flow, de-fri…
lovelaced Jul 20, 2026
9dd622d
feat(dither): detect alternating pairs, pin them through quantization
lovelaced Jul 20, 2026
f58a880
feat(output): integer re-upscale default for reduced modes, --logical…
lovelaced Jul 20, 2026
6866be7
feat(spritesheet): per-tile grid rebase via tile origins
lovelaced Jul 20, 2026
d91b5cd
feat(report): Reporter, --json, exit codes, analyze subcommand
lovelaced Jul 20, 2026
f6f06a9
feat(io): png-default naming, atomic saves, stdin/stdout, --output-fo…
lovelaced Jul 20, 2026
f47173a
feat(batch): event-driven API, collision planning, --suffix/--preserv…
lovelaced Jul 20, 2026
7e8adb0
fix(lospec): request timeout, slug validation, --refresh, cache doc
lovelaced Jul 20, 2026
5dee8b3
refactor(spritesheet): split into fixed/autosplit modules, shared flo…
lovelaced Jul 20, 2026
b3b8537
refactor(pixfix): options-based config, raw IPC previews, robustness
lovelaced Jul 20, 2026
ed8f612
test: end-to-end CLI integration suite
lovelaced Jul 20, 2026
5577567
ci: lint gates, pixfix check on PRs, single release path, untrack bundle
lovelaced Jul 20, 2026
79a31f7
docs(readme): fractional pitch, agent contract, new flags, fix stale …
lovelaced Jul 20, 2026
016cf96
style: cargo fmt across the workspace
lovelaced Jul 20, 2026
7811409
fix(ci): tauri hooks run from the workspace root, keep root-relative …
lovelaced Jul 20, 2026
e830b17
feat(chroma): green-screen keying in CLI, config, and desktop app
lovelaced Jul 20, 2026
e179bcf
docs(site): single-page docs with llms.txt, deployed to Pages
lovelaced Jul 20, 2026
62601aa
ci+docs: codecov coverage job, README badges and chroma/docs sections
lovelaced Jul 20, 2026
cc56def
docs(site): unbreak the hero heart
lovelaced Jul 20, 2026
5f026d4
refactor: the program is pixfix — rename crate, binary, and every ref…
lovelaced Jul 20, 2026
d098e22
feat(grid): --coarsen multiplies the final pitch — render quantum vs …
lovelaced Jul 20, 2026
8af0bf6
feat: --min-confidence flag; desktop parity for coarsen, floor, multi…
lovelaced Jul 20, 2026
62ad9e6
feat(grid-detect): cross-axis harmonic reconciliation
lovelaced Jul 20, 2026
f820cb9
feat(analyze): probe artistic resolution, suggest --coarsen
lovelaced Jul 20, 2026
18e4abe
feat(process): --debug-overlay contested-block heatmap
lovelaced Jul 20, 2026
faae7a0
build: crates.io metadata and tag-triggered publish
lovelaced Jul 20, 2026
aa6e368
test: real-image regression corpus (two Midjourney field-test images)
lovelaced Jul 20, 2026
190449b
refactor(features): cli/parallel feature split; core compiles to wasm32
lovelaced Jul 20, 2026
c13e0ba
feat(docs): in-browser wasm demo — drop a sprite, watch it snap
lovelaced Jul 20, 2026
6076ac6
style: fmt pass over the corpus tests
lovelaced Jul 20, 2026
dabf201
feat(process): animated GIFs — one grid, one palette across frames
lovelaced Jul 20, 2026
8bfbb6f
fix(docs): reveal all sections when loading with a deep link
lovelaced Jul 20, 2026
6334798
feat(ux): curate visible settings in CLI help and desktop app
lovelaced Jul 20, 2026
a284f0a
fix(desktop): broken image previews from CSP-blocked IPC transport
lovelaced Jul 21, 2026
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
123 changes: 88 additions & 35 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,72 @@ on:
tags: ['v*']
pull_request:
workflow_dispatch:
inputs:
tag:
description: 'Tag to create and release (e.g. v0.1.0)'
required: true
type: string

env:
CARGO_TERM_COLOR: always
CARGO_INCREMENTAL: 0

jobs:
# ── Tests ─────────────────────────────────────────────────────────────
# ── Tests + lints ─────────────────────────────────────────────────────
# Runs on every push and PR. Gates the release.
test:
name: Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- uses: Swatinem/rust-cache@v2
- run: cargo fmt --all --check
- run: cargo clippy -p pixfix --all-targets --features tui -- -D warnings
- run: cargo test --locked --features tui

# ── Coverage ──────────────────────────────────────────────────────────
# cargo-llvm-cov over the CLI crate (pixfix needs a GUI stack and has no
# tests). Upload failures don't fail CI — coverage is a signal, not a gate.
coverage:
name: Coverage
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: llvm-tools-preview
- uses: taiki-e/install-action@cargo-llvm-cov
- uses: Swatinem/rust-cache@v2
- run: cargo test --locked
with:
key: coverage
- run: cargo llvm-cov -p pixfix --features tui --locked --lcov --output-path lcov.info
- uses: codecov/codecov-action@v5
with:
files: lcov.info
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: false

# ── pixfix compile check ──────────────────────────────────────────────
# The desktop app is a workspace member the root test job never builds;
# without this, PRs cannot catch desktop-app breakage.
check-pixfix:
name: Check pixfix
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- uses: Swatinem/rust-cache@v2
with:
key: desktop-check
- name: Install Linux dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
libgtk-3-dev \
libwebkit2gtk-4.1-dev \
libappindicator3-dev \
librsvg2-dev
- run: cargo clippy -p pixfix-desktop --all-targets --locked -- -D warnings

# ── CLI binaries ──────────────────────────────────────────────────────
# Pure Rust, no system deps. Runs on push to master, tags, and manual dispatch.
Expand Down Expand Up @@ -58,23 +103,27 @@ jobs:
if: runner.os != 'Windows'
run: |
cd target/${{ matrix.target }}/release
tar czf "$GITHUB_WORKSPACE/normalize-pixelart-${{ matrix.name }}.tar.gz" normalize-pixelart
tar czf "$GITHUB_WORKSPACE/pixfix-${{ matrix.name }}.tar.gz" pixfix

- name: Package (Windows)
if: runner.os == 'Windows'
shell: bash
run: |
cd target/${{ matrix.target }}/release
7z a "$GITHUB_WORKSPACE/normalize-pixelart-${{ matrix.name }}.zip" normalize-pixelart.exe
7z a "$GITHUB_WORKSPACE/pixfix-${{ matrix.name }}.zip" pixfix.exe

- uses: actions/upload-artifact@v4
with:
name: cli-${{ matrix.name }}
path: normalize-pixelart-${{ matrix.name }}.*
path: pixfix-${{ matrix.name }}.*
if-no-files-found: error

# ── Tauri desktop app ─────────────────────────────────────────────────
# Needs system libs on Linux, bun for frontend, Tauri CLI for bundling.
# The frontend bundle is built by tauri.conf.json's beforeBuildCommand;
# Tauri executes hooks from the cargo workspace root, hence the
# repo-root-relative paths in the config. No separate frontend step or
# config override needed.
build-tauri:
name: Tauri / ${{ matrix.name }}
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
Expand Down Expand Up @@ -108,17 +157,13 @@ jobs:
librsvg2-dev \
patchelf

- name: Build frontend
run: bun build pixfix/ui/src/app.ts --outfile pixfix/ui/app.js --minify

- name: Build Tauri app
shell: bash
working-directory: pixfix
working-directory: desktop
run: |
bunx @tauri-apps/cli@^2 build \
--target ${{ matrix.target }} \
--bundles ${{ matrix.bundles }} \
--config '{"build":{"beforeBuildCommand":""}}'
--bundles ${{ matrix.bundles }}

- name: Collect bundles
shell: bash
Expand All @@ -136,31 +181,40 @@ jobs:
path: dist/*
if-no-files-found: error

# ── crates.io ─────────────────────────────────────────────────────────
# Publishes the CLI crate on version tags. Skips quietly when the
# CARGO_REGISTRY_TOKEN secret is not configured.
publish-crate:
name: Publish to crates.io
if: startsWith(github.ref, 'refs/tags/v')
needs: [test, check-pixfix]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- name: Publish
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: |
if [ -z "$CARGO_REGISTRY_TOKEN" ]; then
echo "CARGO_REGISTRY_TOKEN not set; skipping crates.io publish"
exit 0
fi
cargo publish -p pixfix --locked

# ── GitHub Release ────────────────────────────────────────────────────
# Runs on version tags or manual dispatch with a tag input.
# Tag pushes release automatically; manual re-releases from an existing
# CI run live in release.yml (the only other release path).
release:
name: Release
if: startsWith(github.ref, 'refs/tags/v') || (github.event_name == 'workflow_dispatch' && inputs.tag != '')
needs: [test, build-cli, build-tauri]
if: startsWith(github.ref, 'refs/tags/v')
needs: [test, check-pixfix, build-cli, build-tauri]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4

- name: Create and push tag
if: github.event_name == 'workflow_dispatch'
env:
GH_TOKEN: ${{ github.token }}
run: |
TAG="${{ inputs.tag }}"
if git ls-remote --tags origin | grep -q "refs/tags/$TAG$"; then
echo "Tag $TAG already exists on remote, skipping creation"
else
git tag "$TAG"
git push origin "$TAG"
fi

- uses: actions/download-artifact@v4
with:
path: artifacts
Expand All @@ -173,7 +227,6 @@ jobs:
env:
GH_TOKEN: ${{ github.token }}
run: |
TAG="${{ inputs.tag || github.ref_name }}"
gh release create "$TAG" artifacts/* \
gh release create "${{ github.ref_name }}" artifacts/* \
--generate-notes \
--title "$TAG"
--title "${{ github.ref_name }}"
35 changes: 35 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Docs

on:
push:
branches: [master]
paths: ['docs/**', '.github/workflows/docs.yml']
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: pages
cancel-in-progress: true

jobs:
deploy:
name: Deploy to GitHub Pages
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/checkout@v4
- uses: actions/configure-pages@v5
# docs/ is deployed as-is: the wasm demo bundle (docs/demo/pkg) is
# prebuilt and committed. Regenerate with:
# wasm-pack build wasm --target web --release --out-dir ../docs/demo/pkg
- uses: actions/upload-pages-artifact@v3
with:
path: docs
- id: deployment
uses: actions/deploy-pages@v4
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ test_images/
!screenshots/**

# Allow Tauri app icons
!pixfix/icons/**
!desktop/icons/**

# Editor
*.swp
Expand All @@ -25,3 +25,9 @@ test_images/

# macOS
.DS_Store

# Built frontend bundle (bun build output; CI and tauri hooks rebuild it)
desktop/ui/app.js

# Real-image regression corpus is tracked despite the global image ignore
!tests/corpus/*.png
Loading
Loading