mirror of
https://github.com/storytold/photocraft.git
synced 2026-10-30 11:12:24 +01:00
Conflicts: docs/gaps.md: header line is ours plus the PR's missing change item (later Last updated date); change-log rows kept both, newest first docs/file-format-parity.md: same
This commit is contained in:
@@ -2187,6 +2187,20 @@ fn duplicate_to(id: &str, p: &Value) -> Option<Descriptor> {
|
||||
Some(d)
|
||||
}
|
||||
|
||||
/// Only a comp name in the active document is portable across action playback.
|
||||
fn apply_comp_from(d: &Descriptor) -> Mapped {
|
||||
match get_ref(d, "null") {
|
||||
Some([ReferenceItem::Name { class, name }]) if trimmed(&class.class_id) == "compsClass" => {
|
||||
step("layerComp.apply", json!({"comp": name.to_string_lossy()}))
|
||||
}
|
||||
_ => Mapped::Unsupported { harmless: false },
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_comp_to(_: &str, p: &Value) -> Option<Descriptor> {
|
||||
Some(desc("null").with("null", name_ref("compsClass", jstr(p, "comp")?)))
|
||||
}
|
||||
|
||||
fn play_from(d: &Descriptor) -> Mapped {
|
||||
match get_ref(d, "null").map(target) {
|
||||
Some(Target::Name(n)) => step("actions.play", json!({"action": n})),
|
||||
@@ -3473,6 +3487,7 @@ const EVENTS: &[Event] = &[
|
||||
ev!("save", "save", "Save", "file.save", &[]),
|
||||
ev!("Rvrt", "revert", "Revert", "file.revert", &[]),
|
||||
Event { code: "Ply ", name: "play", label: "Play", ids: &["actions.play"], from: play_from, to: play_to },
|
||||
Event { code: "", name: "applyComp", label: "Apply Layer Comp", ids: &["layerComp.apply"], from: apply_comp_from, to: apply_comp_to },
|
||||
Event { code: "Stop", name: "stop", label: "Stop", ids: &[STOP_MESSAGE], from: stop_from, to: stop_to },
|
||||
];
|
||||
|
||||
|
||||
@@ -66,6 +66,105 @@ fn sample() -> AtnFile {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn layer_comp_apply_imports_plays_and_undoes() {
|
||||
let name = "合成 𠮷";
|
||||
let data = set_of("Comps", vec![("Apply", vec![item("applyComp", "Apply Layer Comp", Some(desc("null").with("null", name_ref("compsClass", name))))])]);
|
||||
for depth in [8, 16, 32] {
|
||||
let mut s = Session::new();
|
||||
let loaded = s.execute("actions.load", json!({"data": data})).unwrap();
|
||||
assert_eq!(loaded["unsupported"], json!([]), "{loaded}");
|
||||
assert_eq!(s.actions.list[0].steps, [("layerComp.apply".into(), json!({"comp": name}))]);
|
||||
|
||||
s.execute("file.new", json!({"width": 8, "height": 8, "depth": depth})).unwrap();
|
||||
s.execute("layerComp.new", json!({"name": "Other"})).unwrap();
|
||||
let before = s.active().unwrap().doc.clone();
|
||||
let played = s.execute("actions.play", json!({"action": "Apply"})).unwrap();
|
||||
assert_eq!(played["ran"], 0, "{played}");
|
||||
assert!(played["failed"]["error"].as_str().unwrap().contains(name), "{played}");
|
||||
assert_eq!(*s.active().unwrap().doc, *before, "a missing comp must not apply another comp");
|
||||
|
||||
let comp = s.execute("layerComp.new", json!({"name": name})).unwrap()["comp"].clone();
|
||||
let layer = s.active().unwrap().active_layer.unwrap();
|
||||
s.execute("layer.setProps", json!({"visible": false})).unwrap();
|
||||
s.execute("layerComp.new", json!({"name": "Hidden"})).unwrap();
|
||||
let played = s.execute("actions.play", json!({"action": "Apply"})).unwrap();
|
||||
assert_eq!(played["ran"], 1, "{played}");
|
||||
assert!(played.get("failed").is_none(), "{played}");
|
||||
assert!(s.active().unwrap().doc.layer(layer).unwrap().visible);
|
||||
assert_eq!(json!(s.active().unwrap().doc.last_applied_comp), comp);
|
||||
assert!(s.undo());
|
||||
assert!(!s.active().unwrap().doc.layer(layer).unwrap().visible);
|
||||
assert!(s.redo());
|
||||
assert!(s.active().unwrap().doc.layer(layer).unwrap().visible);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn layer_comp_apply_round_trips_by_name() {
|
||||
for name in ["Comp 1", "合成 𠮷"] {
|
||||
let descriptor = desc("null").with("null", name_ref("compsClass", name));
|
||||
let data = set_of("Comps", vec![("Apply", vec![item("applyComp", "Apply Layer Comp", Some(descriptor.clone()))])]);
|
||||
let mut s = Session::new();
|
||||
let loaded = s.execute("actions.load", json!({"data": data})).unwrap();
|
||||
assert_eq!(loaded["unsupported"], json!([]), "{loaded}");
|
||||
let saved = s.execute("actions.save", json!({"set": "Comps"})).unwrap();
|
||||
assert_eq!(saved["skipped"], json!([]));
|
||||
let bytes = photocraft_paint::tile::b64_decode(saved["data"].as_str().unwrap()).unwrap();
|
||||
let file = atn::parse(&bytes).unwrap();
|
||||
assert_eq!(file.actions[0].items[0].event, AtnEvent::Name("applyComp".into()));
|
||||
assert_eq!(file.actions[0].items[0].descriptor, Some(descriptor));
|
||||
let mut reloaded = Session::new();
|
||||
reloaded.execute("actions.load", json!({"data": saved["data"]})).unwrap();
|
||||
assert_eq!(reloaded.actions.list, s.actions.list);
|
||||
}
|
||||
// Local comp ids and the default comp cannot be resolved to a portable name at export.
|
||||
for params in [json!({}), json!({"comp": 1}), json!({"comp": null}), json!({"comp": true}), json!({"comp": []})] {
|
||||
assert!(export_item("layerComp.apply", ¶ms, "Comps").is_none(), "{params}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn layer_comp_apply_unknown_references_are_kept_and_stop_playback() {
|
||||
let references = [
|
||||
None,
|
||||
Some(D::Integer(1)),
|
||||
Some(D::Reference(vec![])),
|
||||
Some(name_ref("Lyr ", "Comp 1")),
|
||||
Some(target_ref("compsClass")),
|
||||
Some(class_ref("compsClass")),
|
||||
Some(D::Reference(vec![ReferenceItem::Index { class: class("compsClass"), index: 1 }])),
|
||||
Some(D::Reference(vec![ReferenceItem::Identifier { class: class("compsClass"), id: 1 }])),
|
||||
Some(D::Reference(vec![
|
||||
ReferenceItem::Name { class: class("compsClass"), name: UnicodeString::new_nul("Comp 1") },
|
||||
ReferenceItem::Name { class: class("Dcmn"), name: UnicodeString::new_nul("Other") },
|
||||
])),
|
||||
];
|
||||
let items: Vec<_> =
|
||||
references.into_iter().map(|reference| item("applyComp", "Apply Layer Comp", reference.map(|r| desc("null").with("null", r)))).collect();
|
||||
let data = set_of("Comps", vec![("Unknown", items.clone())]);
|
||||
let mut s = Session::new();
|
||||
let loaded = s.execute("actions.load", json!({"data": data})).unwrap();
|
||||
assert_eq!(loaded["unsupported"].as_array().unwrap().len(), items.len());
|
||||
assert!(s.actions.list[0].steps.iter().all(|(id, _)| id == PHOTOSHOP_STEP));
|
||||
assert!(s.actions.list[0].disabled.is_empty());
|
||||
s.execute("file.new", json!({"width": 8, "height": 8})).unwrap();
|
||||
s.execute("layerComp.new", json!({"name": "Comp 1"})).unwrap();
|
||||
let before = s.active().unwrap().doc.clone();
|
||||
let played = s.execute("actions.play", json!({"action": "Unknown"})).unwrap();
|
||||
assert_eq!(played["ran"], 0, "{played}");
|
||||
assert!(played["failed"]["error"].as_str().unwrap().contains("Apply Layer Comp"), "{played}");
|
||||
assert_eq!(*s.active().unwrap().doc, *before);
|
||||
let saved = s.execute("actions.save", json!({"set": "Comps"})).unwrap();
|
||||
let bytes = photocraft_paint::tile::b64_decode(saved["data"].as_str().unwrap()).unwrap();
|
||||
let file = atn::parse(&bytes).unwrap();
|
||||
assert_eq!(file.actions[0].items.len(), items.len());
|
||||
for (saved, original) in file.actions[0].items.iter().zip(&items) {
|
||||
assert_eq!(saved.event, original.event);
|
||||
assert_eq!(saved.descriptor, original.descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_maps_events_keeps_flags_and_plays() {
|
||||
let mut s = Session::new();
|
||||
@@ -427,6 +526,7 @@ fn every_mapped_command_exports_and_imports_back() {
|
||||
("image.mode.bitmap", json!({"method": "threshold"})),
|
||||
("image.imageSize", json!({"resolution": 1200, "resample": "preserveDetails", "keepPrintSize": true})),
|
||||
("layer.new.layerFromBackground", json!({"ifPresent": true, "select": false})),
|
||||
("layerComp.apply", json!({"comp": "Comp 1"})),
|
||||
];
|
||||
for (id, params) in &cases {
|
||||
let item = export_item(id, params, "S").unwrap_or_else(|| panic!("{id} does not export"));
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# File-format parity
|
||||
|
||||
> **Last reviewed:** 2026-10-12 · **Last updated:** 2026-10-15 · **Change:** minor (Photoshop mixed-order TIFF type geometry, #4758; DDS textures save: uncompressed or BC1–BC3, optional mip chain, DDS Options in Save As (#1327); PSD smart filters: 45 filters (31 more: Blur, Surface Blur, Smart Sharpen, Diffuse, Wind, Offset, Mezzotint, Clouds, Difference Clouds, Fibers, Lens Flare and others) plus Hue/Saturation and Perspective Warp mapped both ways, #6296, #6298, #6300, #6302, #6304, #6314, #6316, #6267, #6268, FILE-215-2; large-file fixtures: a 20 MP layered PSD, a 150-layer layout, a 2 GB PSD and a 4.4 GB PSB generated at test time and round-tripped, FILE-216-2 done, #5107; earlier: PNG `sTER` stereo pairing kept on save, #6063; PDF write one flattened page via Save As / Save a Copy / CLI, #3401; CMYK Invert, Posterize and Threshold on the inks; smart filter masks on PSD open and save; Photoshop-oracle floor 188/258; lossy DNG, compression 34892, decodes with its levels and LinearizationTable applied to the stored 8-bit samples, FILE-215-11; CMYK Hue/Saturation and Color Balance keep the black ink; CMYK Posterize on the stored values, psd-tools oracle floor 242/309; CMYK adjustment layers composite on the document's inks; Photoshop-oracle floor 165/258; CMYK Brightness/Contrast on the inks, CMYK Threshold's luma; modern Brightness/Contrast's brightness curve; Photoshop-oracle floor 151/258; Lab documents composite on their stored L, a, b; Photoshop-oracle floor 152/258; Levels on whole levels at every step with Photoshop's shadow toe; Photoshop-oracle floor 146/258, psd-tools 238/309; 32-bit adjustments unclipped; Photoshop-oracle floor 149/258; mask feathers blur with their exact variance; Threshold splits on Photoshop's luminosity; a pass-through clipping base with an adjustment renders isolated; psd-tools oracle floor 240/309; 16-bit Posterize as Photoshop bins it; psd-tools oracle floor 241/309; Gradient Map luminosity; Photoshop-oracle floor 147/258; PSD: `Mt16`/`Mt32` merged transparency read; palette PNGs open in Indexed Color mode, #4804; earlier: CMYK Invert, Posterize and Threshold on the inks; 16-bit Lab Invert; Photoshop-oracle floor 145/258; `.atn` import maps 112 events; DDS textures open, #1327; DPX read and write, #1049; #2465 fixed: CS2 placed layers with only a `PlLd` block open their contents; Photoshop Actions `.atn` read and write; `.shc` contour files read and write; earlier the same day: indexed BMP palette mode; Save keeps a JPEG's quality and a WebP's encoding) · **Target:** Adobe Photoshop 2026
|
||||
> **Last reviewed:** 2026-10-12 · **Last updated:** 2026-10-17 · **Change:** minor (Apply Layer Comp action mapping, #7101; earlier: earlier: PDF write one flattened page via Save As / Save a Copy / CLI, #3401; Photoshop mixed-order TIFF type geometry, #4758; DDS textures save: uncompressed or BC1–BC3, optional mip chain, DDS Options in Save As (#1327); PSD smart filters: 45 filters (31 more: Blur, Surface Blur, Smart Sharpen, Diffuse, Wind, Offset, Mezzotint, Clouds, Difference Clouds, Fibers, Lens Flare and others) plus Hue/Saturation and Perspective Warp mapped both ways, #6296, #6298, #6300, #6302, #6304, #6314, #6316, #6267, #6268, FILE-215-2; large-file fixtures: a 20 MP layered PSD, a 150-layer layout, a 2 GB PSD and a 4.4 GB PSB generated at test time and round-tripped, FILE-216-2 done, #5107; earlier: PNG `sTER` stereo pairing kept on save, #6063; PDF write one flattened page via Save As / Save a Copy / CLI, #3401; CMYK Invert, Posterize and Threshold on the inks; smart filter masks on PSD open and save; Photoshop-oracle floor 188/258; lossy DNG, compression 34892, decodes with its levels and LinearizationTable applied to the stored 8-bit samples, FILE-215-11; CMYK Hue/Saturation and Color Balance keep the black ink; CMYK Posterize on the stored values, psd-tools oracle floor 242/309; CMYK adjustment layers composite on the document's inks; Photoshop-oracle floor 165/258; CMYK Brightness/Contrast on the inks, CMYK Threshold's luma; modern Brightness/Contrast's brightness curve; Photoshop-oracle floor 151/258; Lab documents composite on their stored L, a, b; Photoshop-oracle floor 152/258; Levels on whole levels at every step with Photoshop's shadow toe; Photoshop-oracle floor 146/258, psd-tools 238/309; 32-bit adjustments unclipped; Photoshop-oracle floor 149/258; mask feathers blur with their exact variance; Threshold splits on Photoshop's luminosity; a pass-through clipping base with an adjustment renders isolated; psd-tools oracle floor 240/309; 16-bit Posterize as Photoshop bins it; psd-tools oracle floor 241/309; Gradient Map luminosity; Photoshop-oracle floor 147/258; PSD: `Mt16`/`Mt32` merged transparency read; palette PNGs open in Indexed Color mode, #4804; earlier: CMYK Invert, Posterize and Threshold on the inks; 16-bit Lab Invert; Photoshop-oracle floor 145/258; `.atn` import maps 112 events; DDS textures open, #1327; DPX read and write, #1049; #2465 fixed: CS2 placed layers with only a `PlLd` block open their contents; Photoshop Actions `.atn` read and write; `.shc` contour files read and write; earlier the same day: indexed BMP palette mode; Save keeps a JPEG's quality and a WebP's encoding) · **Target:** Adobe Photoshop 2026
|
||||
|
||||
Every format Photoshop 2026 (27.11.0) reads or writes, with PhotoCraft's support, fidelity and
|
||||
tests. Photoshop's list comes from its `Info.plist` `CFBundleDocumentTypes`, the PiPL read/write
|
||||
@@ -131,7 +131,7 @@ remaining gaps in #1606 stay tracked with per-sample corpus ceilings.
|
||||
| LUTs `.cube` / `.3dl` / `.look` | R/W (Export Color Lookup) | R/W / R / R | `cms/src/lutfile.rs` |
|
||||
| Keyboard shortcuts `.kys` | R/W | R/W | `engine/src/kys.rs` (`write`: Edit › Keyboard Shortcuts › Export…, rows carry our id; Photoshop reading our files is unverified) |
|
||||
| ICC profiles | R | R | Own CMS |
|
||||
| Actions `.atn` | R/W | R/W | `psd/src/atn.rs` (version 16 sets, exact round trip), `engine/src/atn_cmds.rs` (112 Photoshop events ↔ commands; other steps kept verbatim). Panel menu, CLI `batch --actions set.atn`, `actions.load`/`actions.save`. Photoshop 5-era version 12 files unsupported (AUTO-219-2) |
|
||||
| Actions `.atn` | R/W | R/W | `psd/src/atn.rs` (version 16 sets, exact round trip), `engine/src/atn_cmds.rs` (Photoshop events ↔ commands, including Apply Layer Comp by name, #7101; other steps kept verbatim). Panel menu, CLI `batch --actions set.atn`, `actions.load`/`actions.save`. Photoshop 5-era version 12 files unsupported (AUTO-219-2) |
|
||||
| Styles `.asl` | R/W | no | #1693 |
|
||||
| Contours `.shc` | R/W | R/W | `psd/src/shc.rs`; Contour Editor Load…/Save… (FILE-215-15). No user contour library; not yet checked against a Photoshop-written file |
|
||||
| Custom shapes `.csh`, tool presets `.tpl` | R/W | no | |
|
||||
@@ -142,6 +142,7 @@ remaining gaps in #1606 stay tracked with per-sample corpus ceilings.
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-17 | minor | `.atn`: Apply Layer Comp by name maps to `layerComp.apply` and exports as `applyComp` (#7101) |
|
||||
| 2026-10-15 | minor | PSD smart filters: Clouds, Difference Clouds, Fibers and Lens Flare open as editable filters with their seeds, colours and lens, and save like Photoshop (checked against a Photoshop 27 file); render filters on a smart object work within the object, so a flare sits where Photoshop puts it |
|
||||
| 2026-10-15 | minor | DDS textures save (#1327): uncompressed or BC1/BC2/BC3 (DXT1/3/5) from a clean-room block encoder, optional box-filtered mip chain, legacy header; DDS Options in Save As and Save a Copy, `ddsCompression`/`ddsMipmaps` for scripts, the CLI and MCP |
|
||||
| 2026-10-16 | minor | PSD smart filters: Perspective Warp (`perspectiveWarpTransform`: `vertices`, `warpedVertices`, `quads` of vertex indices, document pixels) opens as `layer.smartObjects.perspectiveWarp`, one plane per quad, and re-renders warped; save writes the original item with only the geometry rewritten, or a new one in Photoshop's layout; inconsistent descriptors stay verbatim (FILE-215-2, #6268) |
|
||||
|
||||
+6
-1
@@ -1,6 +1,6 @@
|
||||
# Where PhotoCraft falls short of Photoshop
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-18 · **Change:** minor (G9: Preferences › File Handling › Save in Background takes effect, #6207, 24 of 169 preferences do nothing; G7: Content-Aware Move previews every selection the command takes, coarse then exact, TOOL-213-4 done, #6680; G4: Pattern Stamp and Background Eraser draw live, TOOL-213-7; G9: Animated Zoom pref read: holding the Zoom tool zooms continuously, #6210, prefs that do nothing 25 of 166; G2: a cancelled 24 MP Gaussian Blur job stops in at most 44 ms, P25 under its 200 ms budget in local runs, baseline not re-run, #6205; G1: generated large-file fixtures, a 4.38 GB PSB opens on Windows through io, FILE-216-2 done, #5107; G9: fill and adjustment layers copy and paste whole, #2344; G1: smart filter masks show on PSD open, Photoshop-oracle floor 188/258; G10: DPX read and write, #1049, Cineon read; G1: CMYK Hue/Saturation and Color Balance keep the black ink; G1: CMYK Posterize on the stored values, psd-tools oracle floor 242/309; G1: CMYK adjustment layers composite on the document's inks, Photoshop-oracle floor 165/258; G1: CMYK Brightness/Contrast on the inks, CMYK Threshold's luma; G1: modern Brightness/Contrast's brightness curve matches Photoshop, Photoshop-oracle floor 151/258; G1: Lab documents composite on their stored L, a, b, Photoshop-oracle floor 152/258; G1: Levels on whole levels at every step, Photoshop-oracle floor 146/258, psd-tools 238/309; G1: 32-bit Brightness/Contrast legacy, Channel Mixer, Photo Filter and Black & White tint unclipped as in Photoshop, Photoshop-oracle floor 149/258; G1: mask feathers blur with their exact variance, psd-tools oracle floor 240/309; G1: Threshold splits on Photoshop's luminosity, psd-tools oracle floor 241/309; G1: pass-through clipping base with an adjustment renders isolated, psd-tools oracle floor 240/309; G1: 16-bit Posterize as Photoshop bins it, psd-tools oracle floor 241/309; G1: Gradient Map driven by the W3C luminosity, Photoshop-oracle floor 147/258; G7: Field Blur's on-canvas pins, #3373; G4: Hue Jitter amount track, #3825 partial; G4: minimum field interaction, #3825 partial; G1: CMYK Invert, Posterize and Threshold on the inks, 16-bit Lab Invert; Photoshop-oracle floor 145/258; G11: `.atn` import maps 112 events: Select and Mask, Filter Gallery Spatter, Ripple, Crystallize, Pattern Fill layers, mask targeting, Step Backward, nudges in points, Bitmap mode; G5: Free Transform H/V skew fields; G9: toolbar tool shortcut editing; refreshed against main: G2, G5, G7, G9, G10, G13, G14, G17 items that landed; G10: DPX read and write, #1049; G4: checkbox-only Brush Settings rows; G11: Load/Save Actions `.atn` landed, AUTO-219-2 partial; G5: hold-a-key spring-loaded tools landed, #2672; G1: #2465 fixed; stale missing-tool rows corrected: Color Replacement, Red Eye and Add/Delete Anchor Point are live tools, so only Art History Brush is missing; no estimate changed) · **Target:** Adobe Photoshop 2026
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-19 · **Change:** minor (Apply Layer Comp action mapping, #7101; G9: Preferences › File Handling › Save in Background takes effect, #6207, 24 of 169 preferences do nothing; G7: Content-Aware Move previews every selection the command takes, coarse then exact, TOOL-213-4 done, #6680; G4: Pattern Stamp and Background Eraser draw live, TOOL-213-7; G9: Animated Zoom pref read: holding the Zoom tool zooms continuously, #6210, prefs that do nothing 25 of 166; G2: a cancelled 24 MP Gaussian Blur job stops in at most 44 ms, P25 under its 200 ms budget in local runs, baseline not re-run, #6205; G1: generated large-file fixtures, a 4.38 GB PSB opens on Windows through io, FILE-216-2 done, #5107; G9: fill and adjustment layers copy and paste whole, #2344; G1: smart filter masks show on PSD open, Photoshop-oracle floor 188/258; G10: DPX read and write, #1049, Cineon read; G1: CMYK Hue/Saturation and Color Balance keep the black ink; G1: CMYK Posterize on the stored values, psd-tools oracle floor 242/309; G1: CMYK adjustment layers composite on the document's inks, Photoshop-oracle floor 165/258; G1: CMYK Brightness/Contrast on the inks, CMYK Threshold's luma; G1: modern Brightness/Contrast's brightness curve matches Photoshop, Photoshop-oracle floor 151/258; G1: Lab documents composite on their stored L, a, b, Photoshop-oracle floor 152/258; G1: Levels on whole levels at every step, Photoshop-oracle floor 146/258, psd-tools 238/309; G1: 32-bit Brightness/Contrast legacy, Channel Mixer, Photo Filter and Black & White tint unclipped as in Photoshop, Photoshop-oracle floor 149/258; G1: mask feathers blur with their exact variance, psd-tools oracle floor 240/309; G1: Threshold splits on Photoshop's luminosity, psd-tools oracle floor 241/309; G1: pass-through clipping base with an adjustment renders isolated, psd-tools oracle floor 240/309; G1: 16-bit Posterize as Photoshop bins it, psd-tools oracle floor 241/309; G1: Gradient Map driven by the W3C luminosity, Photoshop-oracle floor 147/258; G7: Field Blur's on-canvas pins, #3373; G4: Hue Jitter amount track, #3825 partial; G4: minimum field interaction, #3825 partial; G1: CMYK Invert, Posterize and Threshold on the inks, 16-bit Lab Invert; Photoshop-oracle floor 145/258; G11: `.atn` import maps 112 events: Select and Mask, Filter Gallery Spatter, Ripple, Crystallize, Pattern Fill layers, mask targeting, Step Backward, nudges in points, Bitmap mode; G5: Free Transform H/V skew fields; G9: toolbar tool shortcut editing; refreshed against main: G2, G5, G7, G9, G10, G13, G14, G17 items that landed; G10: DPX read and write, #1049; G4: checkbox-only Brush Settings rows; G11: Load/Save Actions `.atn` landed, AUTO-219-2 partial; G5: hold-a-key spring-loaded tools landed, #2672; G1: #2465 fixed; stale missing-tool rows corrected: Color Replacement, Red Eye and Add/Delete Anchor Point are live tools, so only Art History Brush is missing; no estimate changed) · **Target:** Adobe Photoshop 2026
|
||||
|
||||
Every known shortfall against Photoshop 2026 (27.11.0), one entry each, ranked by user impact
|
||||
on the way to beta. This is the work list agents pick from: take the highest-ranked entry that
|
||||
@@ -98,6 +98,10 @@ what an agent can't finish alone.
|
||||
- **Estimate:** 120–200 h.
|
||||
|
||||
### G11. Actions and scripting
|
||||
- **Done 2026-10-17 (#7101):** `.atn` Apply Layer Comp steps with a named `compsClass`
|
||||
reference import as `layerComp.apply` and export to the same Photoshop event. Names resolve
|
||||
in the document being played; missing comps stop playback. Other reference forms stay kept
|
||||
Photoshop steps and are reported as unsupported. AUTO-219-2 remains partial.
|
||||
- **2026-10-13 evidence:** Batch supports [output naming templates](batch-naming.md) using
|
||||
`{name}`, `{serial}` and a captured UTC `{date}`, with a starting serial number. Colliding
|
||||
names remain errors; custom output names cannot replace inputs. AUTO-219-5 stays partial.
|
||||
@@ -292,6 +296,7 @@ what an agent can't finish alone.
|
||||
|---|---|---|
|
||||
| 2026-10-15 | minor | G1 / #5972 A1: render only provably constant-RGB `GdFl` noise fills; variable noise synthesis remains open |
|
||||
| 2026-10-18 | minor | G9: with no selection, Copy takes fill and adjustment layers whole and Paste recreates them (#2344); UI-217-23 stays partial (a single pixel layer copies its pixels only); estimate unchanged |
|
||||
| 2026-10-17 | minor | G11: `.atn` Apply Layer Comp imports, plays and exports by comp name (#7101); other reference forms remain unsupported |
|
||||
| 2026-10-15 | minor | G2: a cancelled 24 MP Gaussian Blur job stops within 44 ms max (P25, budget 200 ms; was about 400 ms; local runs, baseline not re-run), the uncancelled job on a locked-transparency layer about 40% faster; #6205 |
|
||||
| 2026-10-15 | minor | G9: Animated Zoom preference takes effect (hold the Zoom tool to zoom continuously, #6210); prefs that do nothing 25 of 166; no estimate changed |
|
||||
| 2026-10-16 | minor | G7: Content-Aware Move's live preview covers every selection the command takes (over 256 × 256: a coarse result solved scaled down, then the exact one); TOOL-213-4 done (#6680) |
|
||||
|
||||
Reference in New Issue
Block a user