Highlights since 0.2.0
182 commits from 183 merged pull requests, including work from 31 community contributors (thank you!), and 146 issues closed.
- Your work is safer:
- Recovered unsaved documents survive a second crash.
- Deeply nested
.pcraftfiles reopen. photocraft-cli batchrefuses to overwrite its input folder.- Preferences no longer get lost when two windows or tabs save, or when a save fails.
- Languages: the interface is available in 日本語, 简体中文, 繁體中文, Español, Русский and Čeština (Edit › Preferences › Interface › Language), with CJK fonts and IME input in the Type tool.
- Files:
- JPEG and other no-alpha exports composite transparency over white.
- Formats without colour profiles get sRGB.
- Truncated JPEGs and multi-frame files open with a warning instead of silently.
- TGA is offered in Save As and Export As.
- Editing:
- Free Transform a Copy and Transform Again on a Copy.
- Keyboard layer navigation, number keys for opacity, Alt-click colour sampling.
- New from Clipboard.
- Undo and redo restore the targeted layer.
- Shortcuts pressed in quick succession all run, in order.
- Automation (CLI, MCP, control channel):
- Stricter, clearer parameter and flag errors, and
<subcommand> --help. - Background jobs that agents can wait for or poll.
- Recorded actions run in
batch.
- Stricter, clearer parameter and flag errors, and
Scorecard
This is the first release with docs/scorecard.md, so these numbers are its baseline. 0.2.0 had no scorecard to compare against. Area counts are done / partial / missing.
| Area | Done | Partial | Missing | Measured |
|---|---|---|---|---|
| Performance | 3 | 22 | 12 | 46 scenarios: 3 within budget, 22 over budget or failing, 13 tracked only, 4 not run, 8 not yet measurable (baseline 2026-10-05) |
| File compatibility | 1 | 8 | 18 | io corpus oracle ≥ 146/170, round trip ≥ 169/170; psd-tools oracle ≥ 229/309, round trip ≥ 307/309; Photoshop-authored oracles ≥ 130/256, round trip 258/258 |
| Tools | 3 | 14 | 13 | |
| Workspace and UI | 0 | 4 | 19 | settings that do nothing: 60 of 133 (target 0) |
| Type | 0 | 3 | 6 | |
| Automation | 1 | 3 | 7 | 626 of 626 menu items live |
| Reliability | 3 | 1 | 3 | never-crash lints on 26 of 26 crates; panic_hunt fuzzes every command
|
| Distribution | 4 | 3 | 4 | |
| Open bugs | 0 | 3 | 29 |
What's Changed
- perf(compose): avoid unnecessary backdrop snapshots by @MikeeI in #109
- perf(compose): prepare gradient stops outside pixel loops by @MikeeI in #103
- Fix stale effect maps after default-only mask changes by @MikeeI in #105
- Preserve HDR samples when importing white-matted PSD composites by @MikeeI in #106
- Mix pass-through group coverage in premultiplied space by @MikeeI in #108
- perf(compose): avoid redundant effect-halo rendering by @MikeeI in #110
- Preserve merged PSD alpha at the exported sample precision by @MikeeI in #111
- perf(compose): reuse compiled vector masks within each render by @MikeeI in #112
- perf(compose): reuse bounded pattern preparation across render tiles by @MikeeI in #104
- fix(ui): pasting an image copied in another app never ran by @tiredIsa in #102
- fix(ui): make Move transform controls interactive by @Ni-zav in #99
- Menu overlap and overflow #92 by @Ni-zav in #98
- PR #96 — Liquify undo by @Ni-zav in #97
- fix(ui): shape tools show the shape while dragging by @sommerper in #87
- security: bound automation previews and reply resources by @Gh0stlyKn1ght in #93
- Japanese support: IME input in the Type tool and CJK glyphs in the UI by @kntrttm in #101
- Community batch follow-ups: on-demand OS clipboard paste; fmt + egui 0.36 test port by @echelon in #134
- perf(ui): borrow the active layer from its document snapshot by @MikeeI in #121
- perf(ui): reuse exact cached content bounds by @MikeeI in #116
- perf(ui): skip offscreen layer thumbnail generation by @MikeeI in #117
- perf(ui): retain channel thumbnails across view-only revisions by @MikeeI in #120
- fix(ui): prune thumbnails for removed layers and documents by @MikeeI in #119
- fix(ui): prune closed-document display and preview resources by @MikeeI in #118
- Disambiguate colliding document identities at session admission by @MikeeI in #114
- Warn when the PSD channel limit drops a Quick Mask by @MikeeI in #115
- Preserve compositing properties in single-layer flat export by @MikeeI in #122
- Preserve selected real masks during PSD import by @MikeeI in #130
- Scale artboard bounds in proxy documents by @MikeeI in #131
- Crop tool: move and resize the crop frame, Space repositions while drawing by @echelon in #132
- Fix flaky canvas_geom undo test (process-wide text gamma raced by another test) by @echelon in #135
- Dock: fixed-height panel groups, splitters, collapse, reorder; panels never vanish by @echelon in #139
- Brush Settings panel: Photoshop structure and depth, edits through tools.setBrush by @echelon in #136
- Selecting current bit depth must preserve redo by @MikeeI in #133
- Detect PhotoCraft archives independent of ZIP entry order by @MikeeI in #137
- Type tool: caret/selection land on the glyph under the pointer; font size is live, one undo step, ~60x faster (#123, #124) by @echelon in #142
- Layers groups expand/collapse (#126); every displayed shortcut fires (#127) by @echelon in #141
- Layers rows: names never run under the indicators; fx triangle; Collapse All Groups; designer PSD for QA by @echelon in #167
- fix(geom): saturate Rect::translate and inflate on i32 overflow by @kevin9327 in #159
- fix(ui): make tall menus scrollable on short displays by @Ni-zav in #160
- PSD type layers: bounds regression tests; #148 is the layer's vector mask, not text bounds by @echelon in #165
- Flatpak single-file bundles as release artifacts by @echelon in #163
- Roadmap: honest parity assessment (2026-10-05) by @echelon in #182
- AGENTS.md: no Tauri, Electron or webview shells by @echelon in #184
- Menus scroll and navigate by keyboard on small displays; Color Range dialog on the no-GPU path (#145, #138) by @echelon in #168
- Docs: where we stand and where we're going (roadmap, README, AGENTS.md) by @echelon in #186
- Brushes: Tilt Scale, Brush Projection, Spacing checkbox, section locks, Mixer Wetness/Mix; Brushes grid + drag and drop; options bar through tools.setBrush by @echelon in #174
- fix(ui): refresh Liquify preview after undo by @Ni-zav in #175
- fix(ui): sample eyedropper colours while dragging by @Ni-zav in #176
- fix(ui): keep Curves endpoint handles inside the graph area by @Ni-zav in #177
- fix(ui): avoid duplicate sliders for wide filter ranges by @Ni-zav in #179
- Dock polish: Layers gets the height, Character/Paragraph group, eliding tab strips, reveal the active layer (#147, #150, #151, #152) by @echelon in #181
- Move/Free Transform modifier keys (#90); sharp full-resolution transform preview (#91) by @echelon in #183
- CJK UI font fallback: Simplified/Traditional Chinese and Korean, lazily loaded in locale order (#149) by @echelon in #185
- Layers mask thumbnails + chains; Channels/Paths temporary rows and footers (#153, #154) by @echelon in #191
- GPU compositor: paged residents and effect maps, so 20k+ documents refresh on the GPU (#49) by @echelon in #187
- Properties: uniform sections, per-kind Quick Actions, full-width controls (#155) by @echelon in #192
- PSD corpus: psd-tools set (pinned, verified), an oracle for every file, and the fixes it found by @echelon in #195
- Tool feedback: selection-mode cursor badges, scrubby zoom, contrasting outlines, Shift lines by @echelon in #188
- PSD fidelity: 16/32-bit colour modes, Lab, Levels, Exposure, Dissolve oracle (corpus 133 → 140, psd-tools 212 → 222) by @echelon in #202
- Fix tests on FreeBSD with RADV by @mekanix in #194
- fix(clipboard): use the wayland clipboard for images by @nuIIpointerexception in #222
- FreeBSD: build and test the workspace in a FreeBSD VM by @echelon in #223
- Fix automatic UI scaling on 4K monitors by @john8675309 in #225
- FreeBSD: serialise canvas GPU tests, CI template, RAM detection, CJK font paths by @echelon in #227
- Title bar: never draw the window title over the menus or controls by @echelon in #241
- Packaging: Linux runtime deps + start-up library check (#201); wasm 26.3 -> 18.8 MiB (#198) by @echelon in #237
- Fix live brush preview drawing a polygon (#189) by @echelon in #240
- PSD effects fidelity: spread metric, emboss, gradient geometry, text gamma (113 -> 118 oracle passes) by @echelon in #224
- Scorecard: cargo xtask perf + scorecard, budgets, checklists, nightly perf (#221) by @echelon in #239
- Layout documents: fix GPU device loss, live Move tool, local damage, no per-frame work (#125, #128) by @echelon in #226
- PSD export: write tagged blocks other readers can parse (#200) by @echelon in #236
- CR2: measure the colour filter row phase instead of assuming RGGB (#193) by @echelon in #244
- Atomic saves: one crash-safe atomic_write for every document write by @echelon in #230
- Preferences: wire tooltips, Home screen, recent files; hide what nothing reads by @echelon in #245
- Eraser on locked layers: no lock-out from the simple lock button; Stroke Path erases to the background colour (#76) by @echelon in #229
- Retouch tools paint the mask/channel target; Sample All Layers for Blur/Sharpen/Smudge; premultiplied Smudge (#207) by @echelon in #234
- Free Transform: selection moves only its part of a linked mask; masked preview; target masks and channels (#205) by @echelon in #242
- Vertical type (tategaki): lay out, render, edit and round-trip (#199) by @echelon in #246
- PSD stroke effects: outlines of filled shapes, vector stroke above interior effects, joined subpaths by @echelon in #233
- Live gradients: Gradient tool makes editable Gradient Fill layers (on-canvas handles, stops, midpoints) + Classic mode (#180) by @echelon in #197
- Marquee: Shift squares, Alt draws from the centre, live W × H readout (#208) by @echelon in #247
- GPU: crash-safe startup and device-loss fallback (#243, #4) by @echelon in #252
- Text: deterministic font family order; hide private system families (fixes a flaky test) by @echelon in #264
- Photoshop oracles via storytold/photocraft-corpus: pinned corpora, corpus feature, test-corpus, CI job by @echelon in #190
- Masks: Alt-click to view mask, Cmd-click vector mask to select, vector-mask target brackets (#196) by @echelon in #256
- Stylus pressure, tilt, rotation and eraser on macOS and X11 by @echelon in #255
- Fix Windows MSI advertised shortcut icons by @CQMHV in #238
- Windows taskbar icon: .exe icon id for the MSI shortcut + icon regression tests (#248) by @echelon in #269
- Colours: handle non-hex Unicode values without an internal error by @storybored379 in #261
- Align the CJK fallback font's baseline with Latin text by @kntrttm in #260
- UI: platform shortcut notation in every label and tooltip (#251) by @tiredIsa in #262
- New Document: a typed Width/Height reaches Create (#254) by @echelon in #270
- Menus are as tall as their items; scroll only when the window is too short (#235) by @echelon in #272
- Portable mode: keep settings beside the exe in the Windows portable zip (#228) by @echelon in #267
- Brush Settings for every brush: options-bar toggle and real preset picker (#258) by @echelon in #268
- Ctrl+Alt-drag resizes the brush on the canvas (#231) by @echelon in #271
- Shift rubber band: preview the straight line before the click (#257) by @echelon in #273
- Selection drag previews: regression tests for #253 (visible mid-drag, 1x and 2x) by @echelon in #274
- Shortcuts: rebindable fill keys, D/X and held temporary tools (#249) by @echelon in #275
- PSD manual and optical kerning: import, export, render, edit (#206) by @echelon in #259
- Localise the UI: i18n foundation and a Japanese catalog (menus, dialogs, panels) by @kntrttm in #169
- Pencil tool, Edit › Fill… dialog and "Rasterize?" prompt (#213) by @echelon in #279
- Fast Select › Modify › Smooth and Feather (part of #211) by @echelon in #280
- Compositor: effect maps never block inside parallel tiles (fixes #276 deadlock) by @echelon in #281
- FreeBSD release tarball and README platform lines by @echelon in #284
- fix: forward document index in bridge inspection by @kuishou68 in #287
- Marquee: stop the selection at the canvas edge by @Multipad-cyber in #263
- Background layer: double-click, masks and cut behave like Photoshop by @Multipad-cyber in #266
- i18n: add Simplified Chinese localization by @CQMHV in #282
- Enable dictionary segmentation for Japanese type by @midasdf in #250
- Smart Objects and Smart Filters survive PSD save and open by @echelon in #288
- Apply EXIF orientation on open, write Orientation = 1 on export (#285) by @echelon in #318
- PSD export: 32-bit files open in Photoshop (HDR toning Color Mode Data, #291) by @echelon in #327
- Windows on ARM64: release builds, PE header check, install test on ARM64 hardware by @echelon in #333
- Fix preference usage scanning on CRLF checkouts by @CQMHV in #331
- Windows: Alt-click clone hint and Ctrl+N channel errors (#251 follow-up) by @bommerts in #336
- Move tool: Auto-Select on by default; a click picks and drags the layer under the pointer (#296) by @echelon in #330
- Cmd+Enter (Ctrl+Enter) loads the path as a selection (#306) by @echelon in #329
- Layers panel: rename commits on click elsewhere, Enter or Tab; one at a time (#314) by @echelon in #326
- Keyboard Shortcuts: Ctrl+F records as Ctrl+F, not Ctrl+ControlLeft (#292) by @echelon in #323
- macOS CLI: verify the shipped CLI is Developer ID signed and notarized in the release workflow (#308) by @echelon in #320
- fix(ui): feed every pointer move per frame so fast strokes stay smooth by @leonaci in #317
- Fix logic Show the clone crosshair only while Option is held by @juansebsol in #301
- i18n: add Traditional Chinese (zh-hant) UI catalog by @WildfootW in #302
- Add a Spanish catalog (menus, dialogs, panels) by @Mawfyy in #299
- Localise the UI into Russian: a complete catalog and the missed tl! call sites by @tiredIsa in #298
- Preferences: add an Apply button by @CQMHV in #325
- i18n: add Czech (cs) UI translation by @petrleocompel in #328
- Menus: no menu or submenu covers the menu bar (#319) by @echelon in #337
- Menus stay above the taskbar; oversized windows open maximized (#315) by @echelon in #343
- Background jobs: engine job API, progress and cancel (part of #210) by @echelon in #289
- Background jobs in the app: progress, Cancel, open-as-job (part of #210) by @echelon in #373
- Harden EXIF orientation: limits after rotation, fallible allocation, strict IFD parsing by @echelon in #345
- Fonts: use craft-fonts as an optional build input; move the Japanese fonts out by @echelon in #353
- Stamp Visible (Cmd+Alt+Shift+E) and Stamp Down; Alt+Merge keeps the originals (#217) by @echelon in #356
- Right-click on the canvas lists the layers under the pointer (#307) by @echelon in #360
- Canvas scrollbars, and Preferences > Tools > Overscroll works (#300) by @echelon in #367
- Alt+right-drag resizes the brush and changes its hardness (#297) by @echelon in #370
- Alt+scroll zooms the canvas in 5% steps around the pointer (#293) by @echelon in #371
- ui.set: reject unknown fields; the theme error names all five themes (#412, #413) by @bflatastic in #437
- serve:
methodslists jobs.list and jobs.cancel (#414) by @bflatastic in #438 - MCP command_batch and serve batch honour each step's
wait(#415) by @bflatastic in #439 - Headless saves without
pathnever flatten over the opened file (#416) by @bflatastic in #447 - Alt-click with the Brush, Pencil, Gradient or Paint Bucket samples a colour (#417) by @bflatastic in #450
- Layers panel: double-click beside a layer's name opens Layer Style (#350) by @bflatastic in #452
- Batch runs report inputs that share an output name instead of overwriting (#420, #422) by @bflatastic in #448
- New from Clipboard, and Paste with nothing open makes a document (#368) by @bflatastic in #458
- CLI: unknown flags are usage errors,
<subcommand> --helpprints usage, usage lists --quality (#421, #423) by @bflatastic in #460 - Liquify remembers its brush settings between uses and launches (#418) by @bflatastic in #461
- The main window opens centred on the screen (#419) by @bflatastic in #462
- Keyboard layer navigation: Alt+[ / Alt+] / Alt+, / Alt+. and Shift to add (part of #352) by @bflatastic in #470
- Transform Again on a Copy (Cmd+Alt+Shift+T): step and repeat (part of #352) by @bflatastic in #474
- Number keys set opacity, Shift+[ / Shift+] step hardness, Shift+Cmd+A opens Camera Raw (part of #352) by @bflatastic in #473
- Export: TGA in Save As and Export As (#363) by @Pedroj-64 in #377
- Docs and MCP schema: use the real Gaussian Blur id (#378) by @floflo777 in #390
- Image Size shows the one-pixel minimum it applies (#441) by @HEOJUNFO in #468
- prefs.reset resets a single colorSettings key (#445) by @HEOJUNFO in #467
- Content-Aware Scale: enlarging a 1 px wide or tall layer no longer panics by @Ar-maan05 in #387
- Build desktop app by default (#392) by @agorevski in #425
- fix(engine): use wasm-safe timing for Liquify by @naumanAhmed3 in #393
- Profile mismatch: compare profiles by colour, not bytes (sRGB vs sRGB prompt) by @ljkx in #394
- Type: dragging a paragraph box handle resizes the box (#351) by @Pedroj-64 in #376
- docs: correct VectorCraft sibling name by @agorevski in #429
- Free Transform a Copy (Cmd+Alt+T); Esc takes the copy back (part of #352) by @bflatastic in #482
- Raise rust-version to 1.95, the minimum egui 0.36 needs by @carterwsmith in #471
- docs: add star history chart to README by @coygeek in #469
- Move drag reveals a larger-than-canvas layer's off-canvas pixels by @sommerper in #526
- Spanish: add missing tool, filter and path translations by @neversaynv3r in #488
- Dialogs keep their top-left when their content resizes by @sommerper in #487
- PSD export selects PSB above size limit (#212) by @agorevski in #431
- 32-bit documents: Exposure works on the stored linear values by @floflo777 in #400
- Ctrl+Insert copies and Shift+Insert pastes on Linux; Shift+Insert pastes images on Windows by @sommerper in #531
- Docs: roadmap, scorecard and README after the 2026-10-07 fixes by @bflatastic in #538
- CLI batch: recorded-action steps, --format .ext, strict --quality, refuse --out = --in (#489, #490, #491, #492) by @bflatastic in #545
- Gradients honour explicit transparency stops; smart filter commands accept an explicit layer target (#465, #466) by @bflatastic in #544
- Recovered documents keep their recovery data until saved or closed (#444) by @bflatastic in #549
- History: tool and preset names, Frame from Layers as one step, undo/redo restore the targeted layers (#513, #528, #446, #496, #495) by @bflatastic in #551
- Shortcuts run in the order pressed; Shift+Delete fills on Windows; colour pickers stay put while dragging (#440, #530, #534) by @bflatastic in #552
- Control channel: menu/dialog requests wait for their job; right-click menus at the point; MCP ui_pointer button (#510, #511, #512, #514) by @bflatastic in #548
- Automation: non-object params rejected; headless sees finished jobs; serve stdio survives a bad line; MCP batch keeps results (#503, #504, #505, #506, #529) by @bflatastic in #546
- Formats: deep .pcraft groups reopen; zero-size PSD rejected; JPEG mattes over white; no-ICC formats get sRGB (#517, #519, #521, #522) by @bflatastic in #555
- Preferences merge with other tabs/windows and retry failed saves; web startup shows a Reload hint (#475, #476, #477) by @bflatastic in #561
- FreeBSD CI: run the GPU integration tests one at a time by @bflatastic in #576
- Import warns when it keeps only the first frame or page, or a JPEG ends early (#518, #523) by @bflatastic in #563
- Linux: AppImage update information and a .zsync beside it (#349) by @ianswope in #399
- Color Picker and Color panel fields accept typed values by @carterwsmith in #485
- Release 0.3.0 by @bflatastic in #577
New Contributors
- @MikeeI made their first contribution in #109
- @tiredIsa made their first contribution in #102
- @Ni-zav made their first contribution in #99
- @kntrttm made their first contribution in #101
- @kevin9327 made their first contribution in #159
- @mekanix made their first contribution in #194
- @nuIIpointerexception made their first contribution in #222
- @john8675309 made their first contribution in #225
- @CQMHV made their first contribution in #238
- @kuishou68 made their first contribution in #287
- @midasdf made their first contribution in #250
- @bommerts made their first contribution in #336
- @leonaci made their first contribution in #317
- @juansebsol made their first contribution in #301
- @WildfootW made their first contribution in #302
- @Mawfyy made their first contribution in #299
- @petrleocompel made their first contribution in #328
- @bflatastic made their first contribution in #437
- @Pedroj-64 made their first contribution in #377
- @floflo777 made their first contribution in #390
- @HEOJUNFO made their first contribution in #468
- @Ar-maan05 made their first contribution in #387
- @naumanAhmed3 made their first contribution in #393
- @ljkx made their first contribution in #394
- @carterwsmith made their first contribution in #471
- @coygeek made their first contribution in #469
- @neversaynv3r made their first contribution in #488
- @ianswope made their first contribution in #399
Full Changelog: v0.2.0...v0.3.0