Keep an edit under a name, not just on the clipboard

FR-DEV-6 asks for three things — named presets, copy/paste between
images, and batch-apply to a selection. The last two have been here for
a while; this is the first.

The format is the sidecar's, deliberately. A preset *is* the non-default
half of a version, so the lines are the same lines keyed the same way,
which makes the two files diffable against each other and lets someone
debugging an edit paste a block from one into the other. One file rather
than one per preset: a preset per file makes the name a path, and every
name then has to survive a filesystem — a `/` becomes a directory, a
name differing only in case collides on one platform and not another,
and renaming becomes two operations that can half-fail. As a key in a
document it is none of those.

Unknown *parameters* needed no machinery. `Preset` already holds
whatever keys it is given and resolves them against the descriptors only
at apply time, so one written by a newer build survives by being stored.
Only lines that are not `op.param = float` at all are preserved
verbatim, which is the sidecar's version-skew promise made here too.

Applying is the paste path with a different source, so a preset reaches
a selection through the sidecar read-modify-write that was already
there: no graph, no decode, no GPU, forty files or one.

Two smaller decisions worth the record. A library that fails to parse is
held empty in memory and *not* written back over — settings regenerate
themselves and this is work, so a parse failure must not be the moment
it is destroyed. And every save persists immediately and rolls the
in-memory copy back if the write fails, so the sheet never lists a
preset the file does not have.

The grid's "Presets" button is gated on the selection alone, unlike the
"Paste to 40" beside it. That button needs a clipboard armed this
session; the preset list is whatever was saved last month, and hiding it
behind an unrelated action is what makes a feature only its author knows
about.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-29 20:07:14 +02:00
co-authored by Claude Opus 5
parent 5133e53bc8
commit 5a8327824f
10 changed files with 1502 additions and 47 deletions
+14
View File
@@ -733,6 +733,10 @@ export component TransferPanel inherits VerticalLayout {
callback copy();
callback paste();
/// TRACES: FR-DEV-6
/// Open the named-preset sheet. Beside copy and paste because it is the
/// same thought given a name — this edit, kept.
callback open-presets();
/// TRACES: FR-UI-2
/// How wide this panel has to be before it starts clipping itself — the
@@ -772,6 +776,16 @@ export component TransferPanel inherits VerticalLayout {
}
}
// TRACES: FR-DEV-6
// The saved half. On its own row rather than a third of the one above,
// because copy and paste are a pair — one arms the other — and a button
// that does neither sitting between them would read as part of that pair.
Button {
text: "Presets…";
enabled: root.enabled;
clicked => { root.open-presets(); }
}
if root.armed: Caption {
text: root.summary + (root.framing-withheld ? " · crop not included" : "");
}