Commit Graph
60 Commits
Author SHA1 Message Date
yoriswind 838dca6f5d Document action history ownership and continuation contracts 2026-11-03 22:06:22 -08:00
yoriswind 15cfdc8398 Preserve paused action authorization and history boundaries 2026-11-03 17:54:45 -08:00
yoriswind c0bd466d5f fix(actions): retain explicit history deletions and user purge boundaries 2026-11-03 00:16:38 -08:00
yoriswind 70aea3465d merge: integrate exact main scratch storage checkpoint
Merge 655295b2a2. Resolve generated scorecard conflict with exact upstream text only; actual source-bound regeneration and combined validation remain pending.
2026-11-02 23:30:10 -08:00
yoriswind 4d188a9106 fix(actions): preserve cleared source context and Undo Clear 2026-11-02 23:07:41 -08:00
yoriswind 15977fee7f fix(actions): retain explicit history controls during grouping 2026-11-02 21:31:33 -08:00
yoriswind d29cbec07e fix: consolidate action history after command transactions
Keep command-local history, purge and rollback intact, then consolidate affected documents at action completion or dialog suspension. Preserve semantic no-op identity and redo, visibility semantics and row identifiers. Bind dialog continuation to its exact document/history, inherit the setting in scratch sessions and carry droplet action names. Add the review counterexamples and focused continuation, Batch/droplet and history-option controls. Source is formatted; compilation and runtime qualification remain pending.
2026-11-02 19:02:08 -08:00
FredApps 44f13e5498 Scope scratch read failures to the document that holds the tile
One unreadable tile used to block editing, saving and exporting every open document
(spill::check_integrity was process-global). Add Tile/Surface/Document::scratch_health
(free while no tile has failed) and Document::for_each_surface (ops' history walk now
delegates to it). edit_with checks the document before and after, jobs the document they
captured, import the result, save/export the document written; check_since is now
generation-only so a failure or recovery during an operation refuses it once and a retry
works. Caches check their own sources and purge once per new failure instead of every
frame. Fault-injection tests use a duplicated handle (the Unix file has no name).
2026-11-02 22:23:42 +01:00
FredApps b0f3c7516b Merge current main into codex/scratch-storage
Resolve conflicts with main's prefs UI split, session/execute.rs move, GPU memory
budget (gpuMemoryMb, 0 = auto, kept from main) and effective_memory_usage_mb history
budget. Port the Memory Usage % and Scratch Disks preferences into prefs_ui, the edit
integrity guard into session/execute.rs, and adapt main's new pixel readers to the
guarded TileBytes API. Cargo.lock regenerated; scorecard regenerated.
2026-11-02 22:16:22 +01:00
aiongg 7d66766afb Merge pull request #10157 from FredApps/integration/reviewed-performance-2026-10-29
Integrate 16 reviewed performance and UI fixes
2026-10-29 21:07:40 -04:00
FredApps 29bf828ec2 Merge upstream main into reviewed performance integration
Preserve both UI parity status entries; retain all reviewed performance changes.
2026-10-29 23:35:19 +01:00
Khawar Zamman Wani 272e5279ba Wrap the history budget doc comments to the line width 2026-10-29 10:08:06 +05:30
Khawar Zamman Wani b779bd8f02 Count embedded smart object contents toward the history budget
History counted a document's pixel tiles but not the file bytes a
smart object embeds (SmartSource::Embedded). A document of large
embedded sources was far over its memory budget without trimming
ever noticing, and every Replace Contents kept the old bytes alive in
the undo states unaccounted.

The four counters (add, remove, bytes_with and the uncounted walk)
now visit one shared list of blocks: the tiles as before plus each
embedded source's Arc, keyed by address. States, snapshots and
duplicated layers that share an Arc count it once, and trimming the
last state that holds an old source releases it.
2026-10-29 10:03:25 +05:30
Federico Olivares 5a6270921c Merge main into the integration batch
Main now carries #9031 (Halftone) and #8923; this branch keeps only what it adds on top: the guard for a setting beside a Gallery stack's effect list, its test, and the PSD open warning for patterns that paint nothing.
2026-10-28 11:17:59 -03:00
dotCooCoo b436516881 History: a newest History Brush source counts as the state the byte budget keeps
When the source was the newest past state, trimming kept it and one older state as well, beyond the budget.
2026-10-28 05:41:16 -07:00
dotCooCoo 4c26572d7f Merge main into fix/history-trim-keeps-the-cleared-brush-source
main moved the crates/ops tests from lib.rs into tests.rs; the new
test goes to tests.rs.
2026-10-27 15:50:09 -07:00
Federico Olivares e173a9be17 Merge main into the integration batch
Main reorganised the source tree (command modules under engine/src/cmd, tools and dialogs under ui-egui/src, large files split into modules, inline test modules moved to their own files). This branch's changes to the moved and split files are ported to their new locations.
2026-10-27 10:44:16 -03:00
David MannandClaude Opus 5.5 69d6037f57 Merge main into fix/history-options
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 02:53:17 +01:00
aiongg d061762566 refactor(ops): move lib.rs tests to tests.rs 2026-10-26 22:30:12 +00:00
dotCooCoo 7525331f6a History: trimming to the memory budget after Clear History keeps the History Brush source
Clear History moves the History Brush source into the cleared states'
record and leaves History::brush_source empty. While Clear History can
be undone, trim_with trims the cleared states, but it looked the
pinned source up in the emptied field. The source state was then
dropped like any other old state, and after Edit > Undo Clear History
the History Brush had no source.

trim_with now takes the source from Cleared::brush_source while Clear
History can be undone, and from History::brush_source otherwise, as
History::held picks the cleared or the live states.
2026-10-22 14:13:28 -07:00
David MannandClaude Opus 5.5 6fcb4d346a Merge origin/main into fix/history-options
Keeps main's Clear History, state ids and History Brush source (#3534, #3536 and
the integration fix-up) together with History Options: states carry both their
id and their visibility; non-linear record keeps the pinned brush source; trim_with
keeps the source-first logic. The rollback log now records the index each state
was dropped at (the brush source is never dropped, so it is not always the front)
and the replaced document, so a rollback puts every state back where it was.
Clear History ends the rollback window. Catalogs: one line per string.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 18:17:35 +02:00
David MannandClaude Opus 5.5 b52f023191 Address CodeRabbit review: rollback brings back states the limit dropped for the step
In non-linear history a record moves the later states onto the undo side and trims to the
History States limit (and the byte budget) at once. A compound command whose later part failed
then could not take its first step back when the trim dropped the state it replaced: the
fallback Undo landed on the last later state and cleared the rest. Even when that state survived,
the oldest states were lost. The latest record now holds the states it evicted until the next
change to the list, and History::rollback puts them back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 15:27:23 +02:00
David MannandClaude Opus 5.5 09562dc898 Address review: undo restores visibility a recorded step changed; non-linear Delete at the oldest state
With Make Layer Visibility Changes Undoable off, stepping through history kept every layer's
current visibility, so undoing a recorded step that changed visibility (Apply Layer Comp, a
snapshot, a compound) left the new visibility in place. Each history state now remembers the
layers whose visibility its own step changed (History::current_visibility, set by the session
when it records); Undo, Redo, History rows and non-linear Delete carry only the other layers'
visibility, so only the unrecorded eye changes survive. Deleting, purging or branching keeps the
lists consistent by merging them into the following step.

history.deleteState is now enabled in non-linear history while the oldest state is current and
later states exist, so a later state can still be deleted there.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 11:05:50 +02:00
aiongg 194dfcf0ec Integration: Clear History resets the History Brush source and Undo brings it back, for #3534 and #3536 2026-10-21 05:04:04 +00:00
aiongg 34c5e7d49e Merge PR #3536: History: set the History Brush source from the panel (history.setSource) (#2966)
# Conflicts:
#	crates/engine/src/commands.rs
#	crates/ops/src/lib.rs
2026-10-21 05:04:02 +00:00
David MannandClaude Opus 5.5 ad273614f8 History Options: keep unrecorded visibility, roll back exactly in non-linear history
- With Make Layer Visibility Changes Undoable off, Undo, Redo, a History row and
  non-linear Delete keep every layer's visibility as the user left it.
- History::rollback takes the last record back exactly (also the later states it
  moved in non-linear history); Transform Again on a copy, New Frame from Layers,
  the Free Transform take-back and an abandoned Alt-drag copy use it via
  Session::rollback. The Alt-drag fold keeps the moved later states.
- Non-linear Delete reaches a later (dimmed) state; the History panel lets those
  rows be dragged onto the trash.
- Reword the snapshot-option comments: they come with #3634.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 14:02:57 +02:00
David MannandClaude Opus 5.5 8435a4586f History Brush source: snapshot sources, size check, purge and budget fixes
- Edit > Purge > Histories resets a state source to the default instead of
  leaving every document without one (History::clear).
- The pinned source state counts against the history byte budget first; the
  newest states fill what remains, so trimming stays within max_bytes.
- The History Brush and Fill > History refuse a source of another canvas size
  (after Crop, Canvas Size or Image Size) instead of painting shifted pixels.
- Snapshots can be the source: history.setSource {snapshot}, a source column on
  snapshot rows, HistorySource::State|Snapshot in the engine; deleting the
  snapshot leaves no source, Purge > Histories keeps it.
- paint.historyBrush and Fill > History take History panel rows for `state`
  (redo rows included), as history.setSource does, and accept `snapshot`.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 13:58:10 +02:00
David MannandClaude Opus 5.5 e23840f516 History panel menu: review fixes (floating steps, cleared-state memory, clean undo, New Snapshot)
- Step Backward in the History panel puts a floating selection back like Edit > Undo; Step
  Forward and Edit > Redo put it back and step instead of dropping it first (which recorded a
  Move and wiped the redo states); with nothing to step to the piece keeps floating.
- Memory figures and the byte budget count the states Clear History keeps for its undo.
- Undoing Clear History keeps a saved document clean.
- New Snapshot... in the panel menu runs history.newSnapshot.
- Tests: Clear on one document leaves the others alone, and each fix above.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 13:57:54 +02:00
David MannandClaude Opus 5.5 5f795b0b37 Merge main into fix/history-options
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 13:11:16 +02:00
David Mann ce5af000f2 Merge remote-tracking branch 'origin/main' into fix/history-panel-menu 2026-10-15 13:03:07 +02:00
David Mann 2bc49845a7 Merge remote-tracking branch 'origin/main' into fix/history-brush-source 2026-10-15 13:03:04 +02:00
47cf5af9fd perf: keep effect-cache pins and History snapshots inside the memory budgets (#5336)
* Raw: measure the Nikon D90 image area (4288x2848 at (10, 10))

Aligning the decoded D90 sensor plane with the camera's own JPEG puts the
image at (10, 10) in the 4352x2868 sensor plane, giving the camera's
4288x2848 frame (10-pixel margins; the right border reads the camera's flat
255 from column 4310). Add the measured ImageArea so a D90 NEF opens on the
camera's frame instead of the uncropped plane, and make a measured profile
leave a file that carries its own colour calibration (a DNG) untouched.

* Codecs: open Cineon (.cin) film frames read-only

Cineon is the 10-bit logarithmic film-scan format DPX grew from. Read it from
the same Kodak header family: the generic header (big-endian, orientation at
192, channel count at 193, the channel-1 bits/dimensions), one or three
channels, three 10-bit samples packed into each 32-bit word on a word
boundary. The 10-bit code values open as U16 (round(c x 65535 / 1023)); the
file's log black/white points are a display choice and are not applied. A
depth other than 10 bits or a channel count other than 1/3 is Unsupported;
truncation errors rather than panicking. Read-only (no Cineon writer), so
.cin joins the open filters and Cineon read closes part of #1049's G10 gap.

* PSD: write tiff blocks without copies, inflate merged ZIP once

- ImageSourceData::to_bytes writes the global mask and global blocks
  straight from the model with 4-byte alignment instead of cloning each
  block (data included) to set its padding. Same bytes.
- The tiff transcoder's nested regions and blocks write into the parent's
  buffer (taken and handed back, error or not) instead of building their
  own output and copying it up per nesting level. Bodies that fail or are
  not transcoded are truncated before being copied as stored or dropped.
  Same bytes.
- PsdFile::from_bytes_and_merged decodes the merged image with the parse
  when asked: a ZIP stream is inflated once (the decode's stream check is
  the parse's); the parse-only check runs only when the decode fails, to
  keep from_bytes' errors. PSD import uses it with merged_needed.
- PsdBuilder::composite documents that Gray/CMYK composites stay straight.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Content-Aware Fill: measure a thin hole by its thickness, not its bounds (#1366)

Removing long, thin objects (coiled guitar strings, wires) left their
outline as darker and lighter blotches and copied pieces of other strings
into the fill. The pyramid shrank until the hole's bounding box was four
patches across: for a string across the image that is a level where the
hole has swallowed its surroundings, so the matches found there copy
whatever looks like the blur, and the finer levels (refine-only for wide
bounds) keep them.

A hole at most a quarter as thick as its bounds are long now counts as
thin: the pyramid stops while it is still about two patches thick, and
that level starts from a membrane across it (over a few pixels, the
surroundings themselves) instead of the onion peel, whose exhaustive
search is what made a shallow pyramid unaffordable. Compact holes are
unchanged bit for bit.

Refs #1366

* perf(compose,gpu): effect caches reserve tile addresses instead of pinning layers

Effect map cache entries kept a clone of their layer (a deep clone for groups) so the tile addresses the cache key hashes couldn't be reused. That kept an edited or deleted layer's pixels alive, uncounted, until the entry was evicted. Keep a Weak to each hashed tile instead: it reserves the address without keeping the pixels. The GPU compositor's styled-group pins and shape-source tile snapshots do the same. Part of #4530.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(history): count snapshot-only tiles against the history budget

History panel snapshots outlived every budget: a Merged Layers snapshot is a full flattened copy that neither the history budget, the GPU budget's document bytes nor Purge accounted for. Trim now counts the tiles only snapshots hold as fixed cost, the GPU budget includes them, and Purge > Histories no longer reports tiles a kept snapshot still holds as freed. Part of #4530.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Content-Aware Fill: every part of a thin selection keeps its surroundings (#1366)

Two more causes of ghosts when removing strings and the small knobs
selected with them:

* Auto sampling summarises the selection's surroundings by a few
  clusters. Around a long selection those come from the strings'
  surroundings, so a small part elsewhere (a tuning knob in a differently
  coloured corner) lost its own surroundings and was filled from far away
  in another colour. Around a thin selection the Auto area now always
  reaches as far as the selection is thick.
* The membrane that starts a thin hole now starts a patch radius out, past
  the anti-aliased fringe or halo that a selection drawn close to the
  object leaves outside it.

Refs #1366

* Content-Aware Fill: peel a thin hole where that stays affordable (#1366)

Starting every thin hole from a membrane smoothed Spot Healing on film
grain (spot_healing_keeps_film_grain_through_overlapping_strokes: grain
9.0 against 12.1 around). The onion peel copies the grain; it was only
given up because its exhaustive search is too slow on the big level a
long thin hole stops at. It now starts the thin hole whenever hole cells
times sources stay within 2^28 comparisons, and the membrane only beyond.

Refs #1366

* Select Subject: keep subject parts coloured like the backdrop, leave enclosed gaps open

Users on #2788 reported Select Subject dropping clothes that are close in
colour to a plain wall and bridging the gap between an arm and the torso.
Both come from the same place: the saliency pass labels low-contrast parts
of the subject as background, the background colour model then learns
them and explains them away, and the final blind hole fill closes every
enclosed gap up to 5 % of the mask.

Two changes in crates/algo:

- A second GrabCut pass whose background model only knows the backdrop. A
  colour model fitted to the image frame (minus a margin around the
  salient blob, in case the subject runs out of the picture) marks every
  pixel it explains as probable background; the blob and whatever the
  backdrop cannot account for start as probable foreground. Only
  components that touch the first pass's result survive.
- Hole filling that consults the colour models: specks (<= 0.2 % of the
  mask) always fill, larger holes up to 5 % fill only when their pixels
  fit the foreground model better than the background's.

Tests: two regressions (a cream-shouldered figure on a near-white wall
keeps its shoulders; a ring keeps its backdrop-coloured hole open), plus
ignored timing and picture-dump helpers. The 6 MP first run goes from
about 100 ms to about 200 ms on one core; the P29 repeat path is untouched.

* Keyboard shortcut sets: Set menu, New Set, Save, Delete, .kys Export and Summarize (K2)

Edit › Keyboard Shortcuts gets Photoshop's set management. A set is a saved copy of the
bindings (Preferences.shortcutSets, the one in use in shortcutSet; "" is PhotoCraft Defaults),
and the Set menu shows "(modified)" when the bindings differ from it. New Set…, Save and
Delete wait for OK like the rest of the dialog. Export… writes the dialog's bindings as a .kys
file (commands then tools, Photoshop's macOS key spelling), each row carrying our id as pc-id
so PhotoCraft imports its own sets back exactly, whatever the label; Summarize… writes
Photoshop's HTML list of every bound command by menu.

edit.keyboardShortcuts takes useSet (before the call's edits), saveSet, deleteSet, sets and
activeSet (after them), exportKys and summarize; every reply reports the sets, the set in use
and whether it is modified. Bad names, keys or counts are errors that change nothing.

* .kys import: tool keys and sets saved on Windows (K3)

Photoshop's <tool> rows now import: each matches our tool by name (our tool names are
Photoshop's) or by pc-id in sets PhotoCraft exported, and a letter other than the tool's own
becomes its override, while its own letter keeps the tool cycling with the others (#5036).

A set saved on Windows spells the primary modifier Ctrl where a Mac set writes Cmd and keeps
Control for the Control key. A set with no Cmd or Opt anywhere but some Ctrl is read as a
Windows set, its Ctrl as our Cmd: Ctrl+L is the Cmd+L default, not a Control-key override that
would also mean Control on a Mac. The import reports `windows`.

* cargo fmt the extension lists

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Vikram Bhaskaran <vikram.bhaskaran@outlook.com>
Co-authored-by: vangel76 <vangel1976@googlemail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Gert Pellin <gert@pellin.be>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Diego Chagas <drochachagas@gmail.com>
Co-authored-by: Tanish Manku <tmanku.dev@gmail.com>
2026-10-15 00:44:55 -04:00
David MannandClaude Opus 5.5 b2e98a7d20 Merge main into fix/history-panel-menu
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 00:42:55 +02:00
David MannandClaude Opus 5.5 b0f661be3d Merge main into fix/history-options
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 00:40:14 +02:00
David MannandClaude Opus 5.5 0d32619ed8 Merge main into fix/history-brush-source
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 00:39:45 +02:00
MarekSpodBiedry dbfe8ba412 fix(history): count fill caches and smart filter masks in undo memory (#4972) (#5075) 2026-10-14 15:03:00 -07:00
David MannandClaude Opus 5.5 afdc75faf9 Merge main into fix/history-panel-menu
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 14:54:24 +02:00
vistaero 0456bc3f22 Add guarded scratch storage with owned memory supervision 2026-10-14 00:45:24 +02:00
David MannandClaude Opus 5.5 8e01570d3b Merge origin/main into fix/history-options
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 20:19:30 +02:00
David MannandClaude Opus 5.5 3120afdbb2 Merge origin/main into fix/history-brush-source
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 19:52:41 +02:00
jonathanwxh-cellandClaude Opus 5.5 ecf54d1baf Count document patterns in history budgets (#5105)
A pattern fill or layer style copies its pattern into the document, but
the tile visitor behind History's byte budget skipped Document::patterns.
The pattern's pixels were left out of the budget, of trimming and of
Edit > Purge > Histories, including after an undo left them only to the
redo states.

Related to #4972.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 12:46:53 -04:00
David Mann 23cf5ec69a Merge main into fix/history-brush-source
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

# Conflicts:
#	crates/ui-egui/src/i18n/cs.tsv
#	crates/ui-egui/src/i18n/de.tsv
#	crates/ui-egui/src/i18n/el.tsv
#	crates/ui-egui/src/i18n/es.tsv
#	crates/ui-egui/src/i18n/fr.tsv
#	crates/ui-egui/src/i18n/id.tsv
#	crates/ui-egui/src/i18n/it.tsv
#	crates/ui-egui/src/i18n/ja.tsv
#	crates/ui-egui/src/i18n/ko.tsv
#	crates/ui-egui/src/i18n/nl.tsv
#	crates/ui-egui/src/i18n/pl.tsv
#	crates/ui-egui/src/i18n/pt-br.tsv
#	crates/ui-egui/src/i18n/ru.tsv
#	crates/ui-egui/src/i18n/uk.tsv
#	crates/ui-egui/src/i18n/vi.tsv
#	crates/ui-egui/src/i18n/zh-hans.tsv
#	crates/ui-egui/src/i18n/zh-hant.tsv
2026-10-13 00:07:53 +02:00
David Mann 2ed5fd38c2 Merge main into fix/history-panel-menu
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

# Conflicts:
#	crates/ui-egui/src/i18n/vi.tsv
2026-10-13 00:01:56 +02:00
David MannandClaude Opus 5.5 78a1069e34 Merge main into fix/history-options
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 19:09:00 +02:00
f4fa53140c ops: count selection masks in history pixel budget (#3614)
* ops: count selection masks in history pixel budget

* test: expect unclipped HDR levels in float documents

* web: optimize engine code for the wasm size budget

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 10:43:07 -04:00
8072636e0f Count video footage in history memory budgets (#3894)
Co-authored-by: jonathnwxh <258340642+jonathanwxh-cell@users.noreply.github.com>
Co-authored-by: Codex <noreply@openai.com>
2026-10-12 09:46:59 -04:00
David MannandClaude Opus 5.5 fce9f00414 History: History Options with Allow Non-Linear History and undoable visibility changes (#2966)
History panel menu > History Options... in Photoshop has five options; this lands the two that
don't need snapshots, as preferences (`historyOptions.*`, `prefs.set`, reported by `ui.inspect`
under session.historyOptions) plus the dialog and a `history.options` command.

- Allow Non-Linear History (default off): an edit after stepping back keeps the later states and
  is appended at the end (ops::History::non_linear); History panel Delete then removes only that
  state (History::remove / drop_current), the later ones stay.
- Make Layer Visibility Changes Undoable (default on, as in Photoshop and PhotoCraft today): when
  off, the eye, Alt-click (Show Only), Hide/Show Layers change the document without a history
  state (Session::edit_visibility), leaving redo states alone.
- history.options: with params sets them (validated, unknown keys and non-bools are errors);
  without params the GUI opens the History Options dialog (checkboxes, OK/Cancel).
- New strings translated in all 16 catalogs.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 18:03:42 +02:00
David MannandClaude Opus 5.5 2b738e0c7c History: panel menu, Step Forward/Backward and Clear History (#2966)
The History panel's menu (☰) now has Photoshop's items: Step Forward, Step
Backward, New Snapshot… (greyed, snapshots aren't built yet), Delete, Clear
History, New Document, History Options… (greyed, built in parallel) and Close,
above the dock's own group items. Each runs an engine command.

- history.stepForward / history.stepBackward: one state along the list
  (Err when there is none).
- history.clear: removes every state but the current one without changing
  the image. It is undoable: History keeps the cleared states until the next
  step, and Edit > Undo puts them back. Fade is dropped and can no longer
  underflow on an empty history.
- history.deleteState is enabled only when there is an earlier state.
- Translations for the new labels in all complete-menu languages.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 17:56:26 +02:00
David MannandClaude Opus 5.5 b5e3e80a3b History: set the History Brush source from the panel (history.setSource) (#2966)
The History panel gets Photoshop's source column at the left of every state:
the History Brush icon marks the source, and a click there makes that state
the source through the new `history.setSource {state}` command (not a step,
and the row is not stepped to). `paint.historyBrush`, the interactive History
Brush and Fill > History use the source when no `state` is passed; by
default it is the oldest state held, as before.

The source is kept by state, not index: photocraft-ops gives every history
state a stable id, so the source stays on its state while older states fall
off. As in Photoshop (checked by script), the source state itself does not
fall off the history-state limit or the memory budget, and once it is
deleted or discarded by a new step after stepping back there is no source:
the History Brush then refuses to paint without an explicit `state` until a
new source is set. Rows count the greyed redo states too, so the common
"filter, step back, paint the filtered state in" workflow works.

`document.inspect` / `ui_inspect` report `historySource` {state, label}
(null when its state was deleted). Snapshots are not implemented yet:
`snapshot` returns an "unsupported" error.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 17:37:42 +02:00
Elvis33LEandClaude Opus 5.5 b7e0d3810d History: count retained tiles incrementally below the byte budget (#1157)
Every edit's History::trim rebuilt a set of every tile in every retained state, even well
under the budget. Retained tile references are now counted as states are recorded, evicted,
undone, redone, purged and cleared, and below the budget trim only looks at the current
document's tiles. Record plus trim on a 24 MP, 150-layer document with 50 states drops from
62 ms to under 2 ms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 00:48:31 -04:00