simplify adjustments type to optional not null

This commit is contained in:
dunkeroni
2025-09-06 20:28:29 -04:00
committed by psychedelicious
parent 32c2d3f740
commit 5991067fd9
6 changed files with 90 additions and 116 deletions

View File

@@ -20,6 +20,7 @@ import {
rasterLayerAdjustmentsSimpleUpdated,
} from 'features/controlLayers/store/canvasSlice';
import { selectEntity } from 'features/controlLayers/store/selectors';
import { makeDefaultRasterLayerAdjustments } from 'features/controlLayers/store/util';
import React, { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiCaretDownBold } from 'react-icons/pi';
@@ -66,10 +67,15 @@ export const RasterLayerAdjustmentsPanel = memo(() => {
const onToggleEnabled = useCallback(
(v: boolean) => {
// Only toggle the enabled state; preserve current mode/collapsed so users can A/B compare
dispatch(rasterLayerAdjustmentsSet({ entityIdentifier, adjustments: { enabled: v } }));
const current = layer?.adjustments ?? makeDefaultRasterLayerAdjustments(mode);
dispatch(
rasterLayerAdjustmentsSet({
entityIdentifier,
adjustments: { ...current, enabled: v },
})
);
},
[dispatch, entityIdentifier]
[dispatch, entityIdentifier, layer?.adjustments, mode]
);
const onReset = useCallback(() => {
@@ -98,26 +104,25 @@ export const RasterLayerAdjustmentsPanel = memo(() => {
}, [dispatch, entityIdentifier]);
const onToggleCollapsed = useCallback(() => {
const current = layer?.adjustments ?? makeDefaultRasterLayerAdjustments(mode);
dispatch(
rasterLayerAdjustmentsSet({
entityIdentifier,
adjustments: { collapsed: !collapsed },
adjustments: { ...current, collapsed: !collapsed },
})
);
}, [dispatch, entityIdentifier, collapsed]);
}, [dispatch, entityIdentifier, collapsed, layer?.adjustments, mode]);
const onSetMode = useCallback(
(nextMode: 'simple' | 'curves') => {
if (!layer?.adjustments) {
return;
}
if (nextMode === mode) {
return;
}
const current = layer?.adjustments ?? makeDefaultRasterLayerAdjustments(nextMode);
dispatch(
rasterLayerAdjustmentsSet({
entityIdentifier,
adjustments: { mode: nextMode },
adjustments: { ...current, mode: nextMode },
})
);
},
@@ -215,12 +220,35 @@ export const RasterLayerAdjustmentsPanel = memo(() => {
{!collapsed && mode === 'simple' && (
<>
<AdjustmentSliderRow label={t('controlLayers.adjustments.brightness')} value={simple.brightness} onChange={onBrightness} />
<AdjustmentSliderRow label={t('controlLayers.adjustments.contrast')} value={simple.contrast} onChange={onContrast} />
<AdjustmentSliderRow label={t('controlLayers.adjustments.saturation')} value={simple.saturation} onChange={onSaturation} />
<AdjustmentSliderRow label={t('controlLayers.adjustments.temperature')} value={simple.temperature} onChange={onTemperature} />
<AdjustmentSliderRow
label={t('controlLayers.adjustments.brightness')}
value={simple.brightness}
onChange={onBrightness}
/>
<AdjustmentSliderRow
label={t('controlLayers.adjustments.contrast')}
value={simple.contrast}
onChange={onContrast}
/>
<AdjustmentSliderRow
label={t('controlLayers.adjustments.saturation')}
value={simple.saturation}
onChange={onSaturation}
/>
<AdjustmentSliderRow
label={t('controlLayers.adjustments.temperature')}
value={simple.temperature}
onChange={onTemperature}
/>
<AdjustmentSliderRow label={t('controlLayers.adjustments.tint')} value={simple.tint} onChange={onTint} />
<AdjustmentSliderRow label={t('controlLayers.adjustments.sharpness')} value={simple.sharpness} onChange={onSharpness} min={0} max={1} step={0.01} />
<AdjustmentSliderRow
label={t('controlLayers.adjustments.sharpness')}
value={simple.sharpness}
onChange={onSharpness}
min={0}
max={1}
step={0.01}
/>
</>
)}

View File

@@ -1,10 +1,10 @@
import { Flex, Text, Box } from '@invoke-ai/ui-library';
import { Box, Flex, Text } from '@invoke-ai/ui-library';
import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useEntityAdapterContext } from 'features/controlLayers/contexts/EntityAdapterContext';
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
import { rasterLayerAdjustmentsCurvesUpdated } from 'features/controlLayers/store/canvasSlice';
import { selectEntity, selectCanvasSlice } from 'features/controlLayers/store/selectors';
import { createSelector } from '@reduxjs/toolkit';
import { selectCanvasSlice, selectEntity } from 'features/controlLayers/store/selectors';
import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
@@ -307,8 +307,7 @@ export const RasterLayerCurvesEditor = memo(() => {
const adapter = useEntityAdapterContext<'raster_layer'>('raster_layer');
const { t } = useTranslation();
const selectLayer = useMemo(
() =>
createSelector(selectCanvasSlice, (canvas) => selectEntity(canvas, entityIdentifier)),
() => createSelector(selectCanvasSlice, (canvas) => selectEntity(canvas, entityIdentifier)),
[entityIdentifier]
);
const layer = useAppSelector(selectLayer);

View File

@@ -3,6 +3,7 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
import { rasterLayerAdjustmentsReset, rasterLayerAdjustmentsSet } from 'features/controlLayers/store/canvasSlice';
import type { CanvasRasterLayerState } from 'features/controlLayers/store/types';
import { makeDefaultRasterLayerAdjustments } from 'features/controlLayers/store/util';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiSlidersHorizontalBold } from 'react-icons/pi';
@@ -22,7 +23,7 @@ export const RasterLayerMenuItemsAdjustments = memo(() => {
dispatch(
rasterLayerAdjustmentsSet({
entityIdentifier,
adjustments: { enabled: true, collapsed: false, mode: 'simple' },
adjustments: makeDefaultRasterLayerAdjustments('simple'),
})
);
}

View File

@@ -96,6 +96,8 @@ import {
initialFLUXRedux,
initialIPAdapter,
initialT2IAdapter,
makeDefaultRasterLayerAdjustments,
type RasterLayerAdjustments,
} from './util';
const slice = createSlice({
@@ -109,10 +111,7 @@ const slice = createSlice({
action: PayloadAction<
EntityIdentifierPayload<
{
adjustments:
| NonNullable<CanvasRasterLayerState['adjustments']>
| { enabled?: boolean; collapsed?: boolean; mode?: 'simple' | 'curves' }
| null;
adjustments: RasterLayerAdjustments | null;
},
'raster_layer'
>
@@ -124,43 +123,13 @@ const slice = createSlice({
return;
}
if (adjustments === null) {
layer.adjustments = null;
delete layer.adjustments;
return;
}
if (layer.adjustments === null) {
layer.adjustments = {
version: 1,
enabled: true,
collapsed: false,
mode: 'simple',
simple: { brightness: 0, contrast: 0, saturation: 0, temperature: 0, tint: 0, sharpness: 0 },
curves: {
master: [
[0, 0],
[255, 255],
],
r: [
[0, 0],
[255, 255],
],
g: [
[0, 0],
[255, 255],
],
b: [
[0, 0],
[255, 255],
],
},
};
}
if (typeof adjustments === 'object' && adjustments !== null && 'version' in adjustments) {
layer.adjustments = merge(layer.adjustments, adjustments as NonNullable<CanvasRasterLayerState['adjustments']>);
} else {
// Shallow toggles only
const partial = adjustments as { enabled?: boolean; collapsed?: boolean; mode?: 'simple' | 'curves' };
layer.adjustments = merge(layer.adjustments, partial);
if (!layer.adjustments) {
layer.adjustments = makeDefaultRasterLayerAdjustments(adjustments.mode ?? 'simple');
}
layer.adjustments = merge(layer.adjustments, adjustments);
},
rasterLayerAdjustmentsReset: (state, action: PayloadAction<EntityIdentifierPayload<void, 'raster_layer'>>) => {
const { entityIdentifier } = action.payload;
@@ -168,14 +137,14 @@ const slice = createSlice({
if (!layer) {
return;
}
layer.adjustments = null;
delete layer.adjustments;
},
rasterLayerAdjustmentsSimpleUpdated: (
state,
action: PayloadAction<
EntityIdentifierPayload<
{
simple: Partial<NonNullable<NonNullable<CanvasRasterLayerState['adjustments']>['simple']>>;
simple: Partial<RasterLayerAdjustments['simple']>;
},
'raster_layer'
>
@@ -187,32 +156,7 @@ const slice = createSlice({
return;
}
if (!layer.adjustments) {
// initialize baseline
layer.adjustments = {
version: 1,
enabled: true,
collapsed: false,
mode: 'simple',
simple: { brightness: 0, contrast: 0, saturation: 0, temperature: 0, tint: 0, sharpness: 0 },
curves: {
master: [
[0, 0],
[255, 255],
],
r: [
[0, 0],
[255, 255],
],
g: [
[0, 0],
[255, 255],
],
b: [
[0, 0],
[255, 255],
],
},
};
layer.adjustments = makeDefaultRasterLayerAdjustments('simple');
}
layer.adjustments.simple = merge(layer.adjustments.simple, simple);
},
@@ -234,32 +178,7 @@ const slice = createSlice({
return;
}
if (!layer.adjustments) {
// initialize baseline
layer.adjustments = {
version: 1,
enabled: true,
collapsed: false,
mode: 'curves',
simple: { brightness: 0, contrast: 0, saturation: 0, temperature: 0, tint: 0, sharpness: 0 },
curves: {
master: [
[0, 0],
[255, 255],
],
r: [
[0, 0],
[255, 255],
],
g: [
[0, 0],
[255, 255],
],
b: [
[0, 0],
[255, 255],
],
},
};
layer.adjustments = makeDefaultRasterLayerAdjustments('curves');
}
layer.adjustments.curves[channel] = points;
},

View File

@@ -383,7 +383,7 @@ const zCanvasRasterLayerState = zCanvasEntityBase.extend({
position: zCoordinate,
opacity: zOpacity,
objects: z.array(zCanvasObjectState),
// Optional per-layer color adjustments (simple + curves). When null/undefined, no adjustments are applied.
// Optional per-layer color adjustments (simple + curves). When undefined, no adjustments are applied.
adjustments: z
.object({
version: z.literal(1),
@@ -407,8 +407,7 @@ const zCanvasRasterLayerState = zCanvasEntityBase.extend({
b: z.array(z.tuple([z.number().int().min(0).max(255), z.number().int().min(0).max(255)])).min(2),
}),
})
.optional()
.nullable(),
.optional(),
});
export type CanvasRasterLayerState = z.infer<typeof zCanvasRasterLayerState>;

View File

@@ -118,6 +118,34 @@ export const initialControlLoRA: ControlLoRAConfig = {
weight: 0.75,
};
export type RasterLayerAdjustments = NonNullable<CanvasRasterLayerState['adjustments']>;
export const makeDefaultRasterLayerAdjustments = (mode: 'simple' | 'curves' = 'simple'): RasterLayerAdjustments => ({
version: 1,
enabled: true,
collapsed: false,
mode,
simple: { brightness: 0, contrast: 0, saturation: 0, temperature: 0, tint: 0, sharpness: 0 },
curves: {
master: [
[0, 0],
[255, 255],
],
r: [
[0, 0],
[255, 255],
],
g: [
[0, 0],
[255, 255],
],
b: [
[0, 0],
[255, 255],
],
},
});
export const getReferenceImageState = (id: string, overrides?: PartialDeep<RefImageState>): RefImageState => {
const entityState: RefImageState = {
id,
@@ -187,7 +215,7 @@ export const getRasterLayerState = (
objects: [],
opacity: 1,
position: { x: 0, y: 0 },
adjustments: null,
adjustments: undefined,
};
merge(entityState, overrides);
return entityState;