47 Commits
Author SHA1 Message Date
Brandon ThomasandClaude Opus 5.5 c699e3cda6 Release 0.9.0
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 10:16:05 -04:00
skylar taylor-barrick d6fbac1720 Pin qualification fixes integrated with current print progress 2026-10-13 05:04:13 +02:00
skylar taylor-barrick 70059ac1b4 Pin driver option retention qualification fixes 2026-10-13 04:56:32 +02:00
skylar taylor-barrick 4de93daa91 Pin pre-submission annotation visibility guard 2026-10-13 04:25:32 +02:00
skylartaylor d9bc5a8cd5 Use shared native print transport and keep page composition in PhotoCraft 2026-10-13 02:56:11 +02:00
skylartaylor 3c4e1536a0 Merge current PhotoCraft and preserve asynchronous native printing 2026-10-13 01:48:54 +02:00
skylar taylor-barrick 7b0317ff41 Merge PhotoCraft 0.8.2 and stabilize background-job tests 2026-10-12 20:21:10 +02:00
skylar taylor-barrick a301e31864 Merge current main fixes and validate craft-print rollout 2026-10-12 19:39:05 +02:00
Claude d43db3e153 Release 0.8.2: web build back under the wasm size gate
0.8.1's Web job failed: photocraft-web's wasm was 25,587,059 bytes, over
packaging/web/package.sh's 24 MiB gate. #4253 merged with #4147 brings
it to 24,161,781.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 12:57:00 -04:00
Claude 7f9c4394c1 Merge #4253: wasm back under the 24 MiB gate
Combined with #4147's opt-level "z" list. Conflict in Cargo.toml's
wasm-release profile: kept #4147's photocraft-psd = "z" and dropped
#4253's "s" entry for it (one table per package). Measured with trunk
--release: main 184c226a is 25,235,857 bytes (over the 25,165,824 gate);
with this merge, 24,161,781.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 12:56:54 -04:00
Brandon ThomasandClaude Opus 5.5 184c226a76 Fix main: wasm size cap, Windows Export As test, filter preview window (scope and a crash), CLI duplicate font warnings (#4147)
* test(ui-egui): Export As artboards test accepts the Windows path separator

The test added in #3909 compared the second file's path with a literal
'/', but the exporter joins the sibling name with the platform separator,
so Windows CI saw '/out\boards-2-Capsule.jpg' and failed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(ui): only Filter menu dialogs get the in-dialog preview window

#3877 drew the preview window in every schema dialog with a Preview
checkbox, including Image > Rotation > Arbitrary. Photoshop's Rotate
Canvas and adjustment dialogs have no preview window, and the taller
dialog covered the canvas, which broke
arbitrary_rotation_preview::size_changing_rotation_preview_is_visible_and_preserves_the_panned_center
on main. The window and its reserved height now apply to filter.*
commands only.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* build(web): opt-level z for large cold crates in the wasm release profile

The browser wasm sat at the 24 MiB gate. The document model, file
parsers (psd, format, affinity), serde_json, usvg and naga are off the
per-frame and per-pixel paths; opt-level "z" for them takes the
release wasm from 25,186,233 to 24,829,840 bytes (-356 KB) with no
behaviour change.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(cli): run prints each missing-font warning once

Since #3630 the headless open and save also report missing fonts, so
`run <file> --out` and `run --cmd ... --out` printed the same warning
two or three times and missing_typeface_is_reported_by_run_info_and_convert
failed on main. Warnings from the open and the save now go through the
same already-reported set as the per-command ones.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(ui): the filter preview window never uploads a texture past the GPU limit

With low-resolution previews off, the preview window from #3877 uploaded
the whole document as one texture. egui panics on a texture bigger than
max_texture_side, so opening a filter dialog on a large image crashed
(full_res_preview::filter_dialog_previews_at_full_resolution_when_low_res_is_off).
The texture is now reduced to fit; the view maps through UV fractions,
so it still lines up.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-12 11:48:28 -04:00
Brandon ThomasandClaude Opus 5.5 2db6b7d8f9 Release 0.8.1: rebuild with the fixed shared release workflow
The previous release build failed in craft-common-actions, not in this
repo: riscv64 packaging got --formats deb,rpm,tar (fixed in
craft-common-actions#3), Linux jobs lacked the ALSA headers
(craft-common-actions#5), and Windows signing needed the AZURE_* secrets
listed in the caller.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 11:11:02 -04:00
skylar taylor-barrick 5fe4d09478 Merge current main while preserving craft-print transport contracts 2026-10-12 16:17:51 +02:00
JGFandClaude Opus 5.5 5a0e2e2658 Web: wasm back under the 24 MiB gate with ~1 MiB headroom (25.38 -> 24.17 MB)
The "web build and wasm size gate" job failed on main and every PR: the
release `_bg.wasm` measured 25,379,677 bytes at 8d49ecf5, over the
25,165,824-byte limit in packaging/web/package.sh.

- egui's default fonts (Ubuntu Light, Hack, Noto Emoji, emoji-icon-font)
  were embedded uncompressed (1.4 MB) by egui's `default_fonts` feature.
  The app now builds egui without it; ui-egui's build.rs deflates the
  same files from the `epaint_default_fonts` crate (a build-dependency,
  so no font files are committed) and `egui_fonts.rs` rebuilds egui's
  exact default stack from them on first use. Test builds keep the
  feature as the oracle: the rebuilt definitions equal egui's byte for
  byte.
- Translation catalogs: every catalog repeated the same English sources.
  build.rs now stores the `context<TAB>source` keys once and each catalog
  as references plus translations, losslessly (`text()` still returns
  each .tsv byte for byte; malformed lines are kept as they are).
- wasm-release profile: opt-level "z" for PhotoCraft's own crates.
  Dependencies stay at "s" (at "z" the wasm grew 0.4 MB, measured), the
  file-decoding crates (codecs, psd, raw, heif, vector) stay at "s" and
  the pixel crates at 3, so decoding and filters keep their speed.

Measured with `trunk build --release` (trunk 0.21.14, wasm-opt v123 -Oz):
  upstream main 8d49ecf5      25,379,677 bytes
  this change                 24,167,186 bytes (-1,212,491)
  gate                        25,165,824 bytes (998,638 headroom)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 09:59:25 -04:00
Brandon ThomasandClaude Opus 5.5 3d0c819d46 Release prep: 0.8.0
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 05:44:37 -04:00
skylar taylor-barrick 8c557fcb7d Place identical browser engine profile beside upstream override 2026-10-12 05:47:22 +02:00
skylar taylor-barrick d380309338 CI: restore tests, catalogs, and web size gate (#3923)
* ci: restore test and Clippy gates

* ci: keep browser wasm below size gate

* CI: restore plural catalog and MCP resource test gates

Adapted the catalog, parser, and shared-cache test fixes from PhotoCraft PR #3821 by @bommerts. The source-key test, bundled-catalog test, and MCP resource test pass locally.

* CI: expect saved native document name in CLI test

The native save path intentionally writes the output filename into the reopened bundle. Keep the pre-save Run assertion and assert r.pcraft after reopening.

* CI: compare orientation round trips without saved filenames

Native saves encode the output filename in document name. Check each expected name, then compare the remaining document fields and pixels.
2026-10-11 21:37:09 -06:00
skylar taylor-barrick 9db0b30910 Keep browser command orchestration within the existing size budget 2026-10-12 03:53:52 +02:00
skylar taylor-barrick ccee4894bd Use craft-print for PhotoCraft system printing 2026-10-12 02:28:40 +02:00
MoiztermindandClaude Opus 5.5 628a54b564 Print on Windows: printer list, driver Print Settings, GDI printing
- photocraft-winprint: an isolated unsafe crate (like photocraft-tablet)
  for Windows printing: printer list, the driver's Print Settings dialog,
  page geometry, and GDI printing with ICM off.
- file.print prints on Windows (explicit "send": true only); new
  file.print.printers and file.print.page commands; printerSettings param.
- Print dialog on Windows: Printer menu, Print Settings…, paper from the
  driver.
- panic_hunt fuzzes print commands as dry runs.

Closes #2697.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 23:18:58 +01:00
skylar taylor-barrick d0a18cfd83 UI.9: keep browser bundle under size budget on current main 2026-10-10 20:56:34 -06:00
Brandon ThomasandClaude Opus 5.5 7926161595 Release prep 0.7.0: Linux riscv64 tarball + version bump (#2965)
* Release prep 0.7.0: add Linux riscv64 tar.gz to the release; bump to 0.7.0

- release.yml: new linux-riscv64 job, ported from GridCraft: cross-compiled on ubuntu-24.04
  against ports.ubuntu.com riscv64 libs, tar.gz only, photocraft-cli --version smoke-tested
  under QEMU. The draft-release job now needs it, so SHA256SUMS.txt covers it.
- packaging/linux/package.sh: CROSS_ARCH / CROSS_TARGET / CROSS_COMPILE / EMULATOR support.
- linux_libs: search /usr/lib/riscv64-linux-gnu on riscv64 (was the x86_64 triplet).
- README and docs/releasing.md list the riscv64 tarball.
- Version 0.6.0 -> 0.7.0 (cargo xtask version set), Cargo.lock refreshed; contributors refreshed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* canvas: crisp elliptical marquee ants again in an unrotated view

#2732 (a27d7175) built the elliptical marquee's live ants in document space and snapped every
vertex to the half-pixel grid on its own. Near the edge midpoints the nearly flat arcs became
1 px stairs, whose antialiased steps are grey, so the ants no longer contrast on white or black:
drag_preview_canvas's marquee test fails on main (ubuntu and windows). In an axis-aligned view
(rotation a multiple of 90 degrees) draw the ellipse inscribed in the snapped screen box again,
as before #2732; rotated views keep the mapped ellipse.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-10 21:36:30 -04:00
skylar taylor-barrick 77f50330c6 UI.9: refresh shared panel integration against current main 2026-10-10 19:30:56 -06:00
skylar taylor-barrick 254450f38e UI.8: pin final shared dock action labels 2026-10-10 16:20:57 -06:00
skylar taylor-barrick 99a3970399 UI.9: preserve PhotoCraft controls in shared UI migration 2026-10-10 12:51:56 -06:00
3092d3b6f9 Tests: line-tables-only dev debuginfo, nextest GPU group, faster loop docs (#2204) (#2414)
- [profile.dev] debug = "line-tables-only", matching CI's CARGO_PROFILE_DEV_DEBUG; halves link
  time and PDB size for test binaries.
- .config/nextest.toml: optional cargo-nextest runs with the GPU tests capped at 4 concurrent
  processes (unlimited ones time out in wgpu).
- AGENTS.md / docs/development.md: iteration commands (--lib --tests, --test <file>, nextest,
  doctests separately). CI is unchanged.

Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
2026-10-10 14:03:30 -04:00
Elvis33LE b75267899d DNG: lossy compression 34892 (baseline JPEG tiles) (#2021)
* DNG: lossy compression 34892 (baseline JPEG tiles)

Adobe's lossy DNG (compression 34892) decodes: each tile is a complete
baseline JPEG (8-bit luma at tile size), decoded by zune-jpeg into the
tile geometry (a mismatch is an error), widened 0xFF -> 0xFFFF like
Photoshop's read of 8-bit CFA data. Predictors and the rest of the
pipeline are unchanged.

testgen grows LossyJpegTiles (image-rs encodes the Q60 fixtures behind
the testgen feature), so CI runs a decode oracle: a smooth ramp stays
within a few quantisation steps of the 8-bit source, and a tile cut
mid-stream fails cleanly.

* Lossy DNG: check the tile JPEG header before decoding; zune deps go workspace

decode_headers() runs and the (width, height) == (s.w, rows) check fires
before decode(), so a hostile SOF cannot allocate the decoder's buffers
(up to 16384x16364) before the size mismatch is caught. The mismatch has
a regression test that swaps a 32x32 tile JPEG for a 16x16 one. zune-core
and zune-jpeg move to [workspace.dependencies] next to image and flate2.

* chore(ui): format prefs_ui (the workspace fmt gate is red on main until #2417 lands)

* Lossy DNG: 3-sample LinearRaw (YCbCr) decodes to RGB; tiles size-check

Real lossy DNGs are 3-sample YCbCr LinearRaw, not single-channel CFA:
the tile now decodes through zune-jpeg's YCbCr-to-RGB output when
SamplesPerPixel is 3 (gray stays for CFA), the decoded length is checked
against the tile's sample count, and other sample counts say unsupported.
The generator writes RGB tiles for 3-sample specs. A LinearizationTable
maps the decoded samples in the develop stage as before - nothing to add
in the sensor. Test: a 3-sample ramp with 64-step channel separation
(surviving Q60 chroma quantisation) decodes within the quality band.
2026-10-10 13:39:57 -04:00
Brandon ThomasandClaude Opus 5.5 d3fa5e4c6f Bump version to 0.6.0 (from 0.5.0) for release; refresh contributors
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 21:02:23 -04:00
callmeBalloch e3932fa7a3 Merge remote-tracking branch 'upstream/main' into claude/kind-darwin-btjse2 2026-10-08 16:08:36 +00:00
callmeBalloch 1bb37eda05 AF.1: import bounded Affinity previews with fidelity warnings
Read the indexed version-12 PNG preview with bounds, chunk ordering and CRC checks. Show native-content and resolution warnings, preserve the source through normal and automation opening, and reject auxiliary routes that cannot display the warning. Add format documentation, malformed-input and save-lifecycle tests, native-oracle hooks and parser/import fuzz targets. Editable native layers and Affinity export remain unsupported.
2026-10-08 17:42:23 +02:00
Brandon ThomasandClaude Opus 5.5 e5e3e39752 Bump version to 0.5.0 (from 0.4.1) for release
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 10:01:32 -04:00
Brandon ThomasandClaude Opus 5.5 c9a7d26f6a Web build: ICU complex-script dictionaries native-only; bump version to 0.4.1
The v0.4.0 release failed its web job: the wasm was 26,365,303 bytes, over the
24 MiB gate in packaging/web/package.sh and Cloudflare's 25 MiB per-file cap.
The largest single item was ICU4X's CJK segmentation dictionary (3.8 MB),
pulled in by parley's complex-scripts feature. Enable that feature on native
targets only; the web build falls back to the generic Unicode rules for
Japanese word boundaries and Thai/Lao/Khmer/Burmese line breaking.

wasm: 26,368,656 -> 22,578,266 bytes (21.5 MiB), measured with trunk build --release.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 08:48:03 -04:00
Brandon ThomasandClaude Opus 5.5 173fc90ecb Bump version to 0.4.0 (from 0.3.0) for release
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 08:12:20 -04:00
BFlatandClaude Opus 5.5 60224d3fb7 Release 0.3.0 (#577)
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-07 17:10:08 +07:00
Carter Smith f373fdb52f Raise rust-version to 1.95, the minimum egui 0.36 needs (#471) 2026-10-07 11:32:23 +07:00
Alex GorevskiandCopilot 1cb5af9218 Build desktop app by default (#425)
Include apps/photocraft in workspace default-members so a plain cargo build also builds the native UI.

Co-authored-by: Copilot <[email protected]>
Copilot-Session: f77709e9-6475-4953-ab57-b78347b4499d
2026-10-07 10:34:59 +07:00
Brandon Thomas 47f4abfed4 Stylus pressure, tilt, rotation and eraser on macOS and X11 (#255) (#255) 2026-10-05 17:47:59 -04:00
Brandon Thomas 7f3d9ea68a Packaging: Linux runtime deps + start-up library check (#201); wasm 26.3 -> 18.8 MiB (#198) (#237) (#237) 2026-10-05 10:52:20 -04:00
Brandon ThomasandClaude Opus 5.5 ad86321738 Release 0.2.0; CI builds with --locked (#113)
Bump the workspace version to 0.2.0 (cargo xtask version set 0.2.0; Cargo.lock changes only
the 26 workspace crates). CI's test and clippy steps now pass --locked, as the release packaging
scripts do, so a stale Cargo.lock fails CI instead of the release build.

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-05 00:21:13 -04:00
Brandon ThomasandClaude Opus 5.5 7a5c1930b3 Sandboxed WebAssembly plug-ins: photocraft-plugins (wasmi), plugin.* commands, Filter › Plug-ins (#67)
* photocraft-plugins: sandboxed WebAssembly filter plug-ins (wasmi host, ABI v1)

A new L4 crate runs plug-ins in wasmi (pure Rust, MIT/Apache) with no host
imports, a fuel budget metered in slices with a wall-clock deadline, a memory
cap, a recursion cap and wasmi's strict module limits. Every band gets a fresh
instance; traps, budgets, bad manifests and bad pointers are all Errors.
Pixels go over as interleaved normalised f32 in tile-aligned bands, in
parallel on native, blended by the selection. Includes the example Invert
plug-in (no_std Rust, examples/plugins/invert-rs) built into a test fixture,
hostile-module tests, and docs/plugins.md.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Engine: plugin.* commands (install, list, run, remove, reload)

plugin.run filters the active pixel layer, a targeted channel or a smart
object (recorded as a smart filter) as one undo step; Preferences >
Plug-ins gains Use Additional Plug-ins Folder, loaded when prefs apply.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* UI: Filter > Plug-ins lists installed plug-ins with generated dialogs

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Plug-ins: bands never outgrow the memory cap; regenerate parity

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Bench: example plug-in Invert vs built-in Invert on 24 MP

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-04 16:08:28 -04:00
Brandon ThomasandClaude Opus 5.5 5bd3dac660 Camera raw: clean-room pure-Rust decoder (DNG, CR2, TIFF/EP raws) with develop pipeline (#65)
* raw: clean-room camera raw decoder and developer (photocraft-raw)

A new standalone crate (no workspace deps, wasm-safe, no unsafe) written
from TIFF 6.0, TIFF/EP, the DNG 1.7 spec, ITU-T T.81 Annex H and published
demosaicing papers:

- Lossless JPEG (LJ92) decoder: predictors 1-7, point transform, restart
  intervals, combined Huffman+extra-bits fast table.
- DNG: uncompressed (8-16 bit, packed) and LJ92, strips and tiles, CFA and
  LinearRaw, linearization, black level (repeat, deltas), white level,
  active area, default crop, ColorMatrix/CameraCalibration/ForwardMatrix
  interpolation by CCT, AnalogBalance, AsShotNeutral/WhiteXY,
  BaselineExposure, orientation.
- CR2 (LJ92 slices, SensorInfo borders, ColorData as-shot WB, measured
  black) and TIFF/EP raws (NEF/ARW/PEF uncompressed or LJ92, incl. Sony
  lossless ARW tiles, Nikon/Sony documented black and WB tags).
- Develop: black/white scaling, WB with highlight clipping, bilinear /
  Malvar-He-Cutler / AHD demosaic (rayon bands, sequential on wasm),
  camera -> XYZ D50 -> ProPhoto, exposure, gamma 1.8, 16-bit, orientation.
- Embedded JPEG preview finder; clear Unsupported errors for CR3, RAF,
  ORF, RW2, vendor-compressed NEF/ARW/PEF, lossy/JXL/float DNG, X-Trans.
- Limits checked before every allocation; testgen feature with an LJ92
  encoder and DNG/CR2/TIFF-EP writers; round-trip, colour-reference,
  truncation and corruption tests.

Registered as a standalone crate in xtask layers.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* io: open camera raws through photocraft-raw (16-bit ProPhoto), preview fallback

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* raw: DNG GainMap opcode, fuzz targets, stress tools; open raws from the apps

- Apply DNG OpcodeList2 GainMap (lens shading, e.g. phone DNGs); report other opcodes.
- cargo-fuzz targets (develop, ljpeg) and a mutation stress example.
- Raw extensions in the desktop/web open dialogs and the batch folder list.
- file.open returns the import notes (how a raw was developed, preview fallback).
- codecs: the camera raw refusal points at photocraft-raw.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* raw: clippy 1.99 (is_multiple_of, clamp)

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-04 15:43:47 -04:00
Gh0stly 8b1cbb741b security: authenticate control channels and bound transport resources (#61)
* security: authenticate and bound control transports

* fix: keep connection limiter compatible with current stable
2026-10-04 13:58:59 -04:00
Brandon ThomasandClaude Opus 5.5 50a27e6c74 Never crash: steering rules, clippy.toml, forbid unsafe (#33)
AGENTS.md gets a prominent "Never crash" section at the top of the golden
rules (Rule 9 now points to it), docs/contributing.md a short paragraph,
a root clippy.toml lets tests use unwrap/expect/panic/indexing, and the
workspace moves unsafe_code from "deny" to "forbid" (no unsafe exists in
the workspace).

Standard: craftrules/standards/never-crash.md

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-04 11:14:55 -04:00
Brandon ThomasandClaude Opus 5.5 7750529bbb Release 0.1.1
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-03 09:55:27 -04:00
Brandon Thomas 536a7346fb more algorithms, progress 2026-10-01 08:25:06 -04:00
Brandon Thomas ae92ac20bf continued wip 2026-09-30 09:37:18 -04:00
Brandon Thomas 16941f7198 one-shot 2026-09-30 05:02:08 -04:00