fix(studio): the repair belongs to the frame, so a look loaded over it leaves it standing

This commit is contained in:
2026-10-12 22:26:51 +07:00
parent fcd0957148
commit bf5eeac14f
+26 -2
View File
@@ -220,6 +220,23 @@ type DevPanel = 'wb' | 'tone' | 'presence' | 'effects';
type StripKey = GroupKey | ToolStrip | DevPanel;
const WM_SUBGROUPS: StripKey[] = ['wmColor', 'wmSize', 'wmFont', 'gpsColor', 'gpsName', 'gpsTime', 'gpsFont'];
const isWmPanel = (g: StripKey | null) => g === 'wm' || (g !== null && WM_SUBGROUPS.includes(g));
// A look is colour and tone. HEAL's dust spots, FX's mosaic and the gradient
// masks are drawn by hand on the frame standing on the stage — they belong to
// that frame, not to whichever look is loaded next. A preset, a recent or a
// .rcp file carries none of them, so laying one down must not take the
// session's brushing with it: the marks already on the stage win, and a
// recipe's own marks only land when the stage has none (a frame reopened from
// the catalogue arrives with its own). UNDO is untouched by this — a snapshot
// holds the whole recipe, marks included.
function layLook(next: Recipe['adjustments'], prev: Recipe['adjustments']): Recipe['adjustments'] {
return {
...next,
heal: prev.heal?.length ? prev.heal : next.heal,
mosaic: prev.mosaic?.length ? prev.mosaic : next.mosaic,
masks: prev.masks?.length ? prev.masks : next.masks,
};
}
// A slider row opened from a strip returns to it with "<" (COLOR TEMP -> TEMP).
// Every knob LIGHT owns names the panel it was opened from, so its ruler's back
// reopens that panel's strip rather than dropping the strip off the screen.
@@ -1435,7 +1452,8 @@ export function Workspace() {
const applyRecipe = useCallback(
(next: Recipe) => {
remember();
setRecipe(next);
// The look arrives, the frame's own marks stay (see layLook).
setRecipe((prev) => ({ ...next, adjustments: layLook(next.adjustments, prev.adjustments) }));
setSimId(next.isCustom ? null : next.id);
// A recipe filed by SAVE RECENT carries the frame it was saved with;
// a bundled preset carries none, and then the frame on the stage stays.
@@ -1882,7 +1900,13 @@ export function Workspace() {
// the recipe can be judged before saving. No undo entry — this is a draft, and
// SAVE/EXPORT is what commits it.
const previewCreatedRecipe = useCallback((draft: Pick<Recipe, 'baseFilter' | 'adjustments'>) => {
setRecipe((prev) => ({ ...prev, ...draft, isCustom: true }));
setRecipe((prev) => ({
...prev,
...draft,
// The draft is a look being judged; the frame's marks stay put (see layLook).
adjustments: layLook(draft.adjustments, prev.adjustments),
isCustom: true,
}));
}, []);
// The phone's encrypted .recipe envelope (`shared/utils/recipeShare` is the