* 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 <noreply@anthropic.com>
* io: open camera raws through photocraft-raw (16-bit ProPhoto), preview fallback
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* 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 <noreply@anthropic.com>
* raw: clippy 1.99 (is_multiple_of, clamp)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* File open/save: set the path everywhere, Open Recent, show errors and import/export warnings
File › Open, drag-and-drop onto the window and launch paths now share
PhotocraftApp::open_file: the document is named after the file (not the full
path), keeps its path so File › Save writes back, and goes to Open Recent.
Failures are shown as errors (status bar + a non-blocking notice).
Import/export warnings from photocraft-io are no longer thrown away: they show
in the status bar and in a dismissable notice (native and web), and the
control protocol returns them from app.open/app.save/file.open/file.save/
file.saveAs/file.saveACopy. .pcraft is in the Open and Save filters, and
File › Save writes .pcraft documents in place.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* macOS: open files from Finder, Open With and the Dock (Apple events)
Finder double-click, Open With, drops on the Dock icon and open -a deliver
files as kAEOpenDocuments Apple events, not argv, and winit 0.30 ignores them.
Register an NSAppleEventManager handler through the small fmv-macos-events
crate (MIT OR Apache-2.0, 168 lines, audited, pinned =0.1.0; it keeps the
Objective-C unsafe out of our forbid(unsafe_code) workspace) before the event
loop starts, so both the launching event and later ones reach the UI as
OsEvent::Open; the window is focused. The quit Apple event goes through the
normal close path.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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 <noreply@anthropic.com>
Adopt the Crafting Apps licensing standard (craftrules standards/licensing.md):
- LICENSE-MIT and LICENSE-APACHE (the repo had neither), NOTICE with the
third-party list, docs/brand/LICENSE-brand.txt for the ArtCraft logos.
- ATTRIBUTION.md: every bundled asset with path, title, author, source and
license (Inter, JetBrains Mono, Lucide icons, SCOWL, the CMYK ICC profile,
the app icon, the original slice-knife glyph, brand files, screenshots).
- README: the standard "License and credits" section; badge alt text.
- Fuzz crates (outside the workspace) declare license = "MIT OR Apache-2.0".
- AGENTS.md and docs/contributing.md point new assets at ATTRIBUTION.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Platform-gated command for Windows Image Acquisition (scanners/cameras).
Reports device availability; real WIA COM device I/O is Windows + hardware
only (Photoshop gates this item to Windows too), so off-Windows it returns
available:false with zero devices. 1 test. Parity 624 -> 625 (100.0%);
FLOOR 620. Every Photoshop menu item now has a live command.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
layer.videoLayers.{newVideoLayerFromFile,replaceFootage,interpretFootage,
reloadFrame} load frames from a single image or an image-sequence folder;
file.import.videoFramesToLayers adds each frame as its own layer;
file.export.renderVideo composites the whole document at every timeline
frame and writes an image sequence (clean-room, no proprietary codec).
6 tests. Only Windows-only WIA Support remains. Parity 618 -> 624 (99.8%);
FLOOR 619.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Layer.video (VideoData: frame surfaces + fps + source); the layer's
displayed content is the frame at the timeline playhead, re-synced after
any edit/navigation. Commands: New Blank Video Layer, Insert Blank /
Duplicate / Delete / Restore Frame, Restore All Frames, Show Altered
Video, Rasterize (Layer > Video Layers > Rasterize and Rasterize > Video).
Timeline duration grows to fit. 4 tests; engine green, clippy clean, wasm
OK. Parity 609 -> 618 (98.9%); FLOOR 613. (File-based footage + Render
Video + Video Frames to Layers + .pcraft frame persistence are follow-ups.)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
timeline_ui::handles() wired into menus::is_live so the Timeline panel
counts toward parity. 608 -> 609 (97.4%).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Clean-room trap: per-ink grayscale dilation (separable max filter,
par_rows/wasm-safe) applied only under already-inked pixels, so internal
colour edges get a trap band while object-vs-paper edges stay clean.
Engine image.trap (CMYK-only, active pixel layer, one history step).
algo 3 tests + engine 2 tests. Parity 607 -> 608; FLOOR 603.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Merge a presets/preferences file (another install, or exported library)
into the current preset library, appending gradient/style/shape/pattern/
tool-preset groups the library lacks (by name); idempotent. Headless,
2 tests. Parity 606 -> 607; FLOOR 602.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Groups the selected layers and clips them to a frame rect (their bounds)
via a rectangular layer mask — the visual result of Photoshop's Frame for
this command, built from existing group + mask primitives (no doc-model
change). Headless/scriptable; 2 tests. Parity 605 -> 606; FLOOR 601.
(A distinct Frame layer type + the interactive Frame tool K are a later arc.)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Clean-room implementation of Photoshop's data-driven graphics, headless
and scriptable for CLI/MCP batch generation:
- doc: Variables model (visibility/text/pixel-replacement bindings + named
data sets), threaded through .pcraft (manifest + convert).
- engine variables_cmds.rs: image.variables.define, image.variables.dataSets,
image.applyDataSet (visibility/text/pixel replace w/ fit/fill/conform/as-is
+ 9 alignments, one history step), file.import.variableDataSets (CSV),
file.export.dataSetsAsFiles (apply each set and export), variables.list.
- Pixel replacement loads+scales+aligns an image into the layer bounds via
the existing resample helpers.
- 7 tests (doc round-trip + lookups; engine define/apply, CSV import, export,
pixel replace, bad params). Parity 600 -> 605; FLOOR 600.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Weighted parity by area, the remaining 25 menu items grouped (video/
timeline, data-driven, niche), and agent-hour/wall-clock estimates to
90/95/98/99%. ~25-40 wall-clock hours (parallel) to a 95% ships-for-most
state; video and AI are the long tail.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
crates/automation/src/rpc.rs: a headless session behind the control
protocol's envelope (engine.execute, doc.*, session.list, batch), on
stdio or loopback TCP. MCP gains command_batch (headless and bridge).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Lowercase reverse-DNS id ai.storyteller.photocraft across icons, Linux
desktop/metainfo/mime/flatpak, Windows WiX, macOS Info.plist and workflows.
- PhotoCraft display name in the title bar, start screen, web loader,
Windows exe metadata, README and docs.
- .gitignore target/, roadmap refresh.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>