(wip) add informational popover base component and sample (#4522)

## What type of PR is this? (check all applicable)

- [ ] Refactor
- [x] Feature
- [ ] Bug Fix
- [ ] Optimization
- [ ] Documentation Update
- [ ] Community Node Submission


## Have you discussed this change with the InvokeAI team?
- [x] Yes
- [ ] No, because:

      
## Have you updated all relevant documentation?
- [ ] Yes
- [ ] No


## Description
Adds a new common component `IAIInformationPopover` that composes JSX to
be rendered within a popover as a tooltip. We were not able to use the
`Tooltip` component provided by chakra because you cannot interact with
elements within those (at least not that I could get working).

This just a sample over positive prompt. We need content from
@hipsterusername and @Millu before we can roll this out.

## Related Tickets & Documents

<!--
For pull requests that relate or close an issue, please include them
below. 

For example having the text: "closes #1234" would connect the current
pull
request to issue 1234.  And when we merge the pull request, Github will
automatically close the issue.
-->

- Related Issue #
- Closes #

## QA Instructions, Screenshots, Recordings

<!-- 
Please provide steps on how to test changes, any hardware or 
software specifications as well as any other pertinent information. 
-->

## Added/updated tests?

- [ ] Yes
- [ ] No : _please replace this line with details on why tests
      have not been included_

## [optional] Are there any post deployment tasks we need to perform?
This commit is contained in:
Kent Keirsey 2023-09-20 13:37:12 -04:00 committed by GitHub
commit 8b8d589033
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 809 additions and 400 deletions

View File

@ -1154,6 +1154,136 @@
"variations": "Try a variation with a value between 0.1 and 1.0 to change the result for a given seed. Interesting variations of the seed are between 0.1 and 0.3."
}
},
"popovers": {
"clipSkip": {
"heading": "CLIP Skip",
"paragraph": "Choose how many layers of the CLIP model to skip. Certain models are better suited to be used with CLIP Skip."
},
"compositingBlur": {
"heading": "Blur",
"paragraph": "The blur radius of the mask."
},
"compositingBlurMethod": {
"heading": "Blur Method",
"paragraph": "The method of blur applied to the masked area."
},
"compositingCoherencePass": {
"heading": "Coherence Pass",
"paragraph": "Composite the Inpainted/Outpainted images."
},
"compositingCoherenceMode": {
"heading": "Mode",
"paragraph": "The mode of the Coherence Pass."
},
"compositingCoherenceSteps": {
"heading": "Steps",
"paragraph": "Number of steps in the Coherence Pass. Similar to Denoising Steps."
},
"compositingStrength": {
"heading": "Strength",
"paragraph": "Amount of noise added for the Coherence Pass. Similar to Denoising Strength."
},
"compositingMaskAdjustments": {
"heading": "Mask Adjustments",
"paragraph": "Adjust the mask."
},
"controlNetBeginEnd": {
"heading": "Begin / End Step Percentage",
"paragraph": "Which parts of the denoising process will have the ControlNet applied. ControlNets applied at the start of the process guide composition, and ControlNets applied at the end guide details."
},
"controlNetControlMode": {
"heading": "Control Mode",
"paragraph": "Lends more weight to either the prompt or ControlNet."
},
"controlNetResizeMode": {
"heading": "Resize Mode",
"paragraph": "How the ControlNet image will be fit to the image generation Ratio"
},
"controlNetToggle": {
"heading": "Enable ControlNet",
"paragraph": "ControlNets provide guidance to the generation process, helping create images with controlled composition, structure, or style, depending on the model selected."
},
"controlNetWeight": {
"heading": "Weight",
"paragraph": "How strongly the ControlNet will impact the generated image."
},
"dynamicPromptsToggle": {
"heading": "Enable Dynamic Prompts",
"paragraph": "Dynamic prompts allow multiple options within a prompt. Dynamic prompts can be used by: {option1|option2|option3}. Combinations of prompts will be randomly generated until the “Images” number has been reached."
},
"dynamicPromptsCombinatorial": {
"heading": "Combinatorial Generation",
"paragraph": "Generate an image for every possible combination of Dynamic Prompt until the Max Prompts is reached."
},
"infillMethod": {
"heading": "Infill Method",
"paragraph": "Method to infill the selected area."
},
"lora": {
"heading": "LoRA",
"paragraph": "Weight of the LoRA. Higher weight will lead to larger impacts on the final image."
},
"noiseEnable": {
"heading": "Enable Noise Settings",
"paragraph": "Advanced control over noise generation."
},
"noiseUseCPU": {
"heading": "Use CPU Noise",
"paragraph": "Uses the CPU to generate random noise."
},
"paramCFGScale": {
"heading": "CFG Scale",
"paragraph": "Controls how much your prompt influences the generation process."
},
"paramDenoisingStrength": {
"heading": "Denoising Strength",
"paragraph": "How much noise is added to the input image. 0 will result in an identical image, while 1 will result in a completely new image."
},
"paramImages": {
"heading": "Images",
"paragraph": "Number of images that will be generated."
},
"paramModel": {
"heading": "Model",
"paragraph": "Model used for the denoising steps. Different models are trained to specialize in producing different aesthetic results and content."
},
"paramNegativeConditioning": {
"heading": "Negative Prompts",
"paragraph": "This is where you enter your negative prompts."
},
"paramPositiveConditioning": {
"heading": "Positive Prompts",
"paragraph": "This is where you enter your positive prompts."
},
"paramRatio": {
"heading": "Ratio",
"paragraph": "The ratio of the dimensions of the image generated. An image size (in number of pixels) equivalent to 512x512 is recommended for SD1.5 models and a size equivalent to 1024x1024 is recommended for SDXL models."
},
"paramScheduler": {
"heading": "Scheduler",
"paragraph": "Scheduler defines how to iteratively add noise to an image or how to update a sample based on a model's output."
},
"paramSeed": {
"heading": "Seed",
"paragraph": "Controls the starting noise used for generation. Disable “Random Seed” to produce identical results with the same generation settings."
},
"paramSteps": {
"heading": "Steps",
"paragraph": "Number of steps that will be performed in each generation. Higher step counts will typically create better images but will require more generation time."
},
"paramVAE": {
"heading": "VAE",
"paragraph": "Model used for translating AI output into the final image."
},
"paramVAEPrecision": {
"heading": "VAE Precision",
"paragraph": "The precision used during VAE encoding and decoding. Fp16/Half precision is more efficient, at the expense of minor image variations."
},
"scaleBeforeProcessing": {
"heading": "Scale Before Processing",
"paragraph": "Scales the selected area to the size best suited for the model before the image generation process."
}
},
"ui": {
"hideProgressImages": "Hide Progress Images",
"lockRatio": "Lock Ratio",

View File

@ -0,0 +1,112 @@
import {
Button,
Popover,
PopoverTrigger,
PopoverContent,
PopoverArrow,
PopoverCloseButton,
PopoverHeader,
PopoverBody,
PopoverProps,
Flex,
Text,
Image,
} from '@chakra-ui/react';
import { useAppSelector } from '../../app/store/storeHooks';
import { useTranslation } from 'react-i18next';
interface Props extends PopoverProps {
details: string;
children: JSX.Element;
image?: string;
buttonLabel?: string;
buttonHref?: string;
placement?: PopoverProps['placement'];
}
function IAIInformationalPopover({
details,
image,
buttonLabel,
buttonHref,
children,
placement,
}: Props): JSX.Element {
const shouldDisableInformationalPopovers = useAppSelector(
(state) => state.system.shouldDisableInformationalPopovers
);
const { t } = useTranslation();
const heading = t(`popovers.${details}.heading`);
const paragraph = t(`popovers.${details}.paragraph`);
if (shouldDisableInformationalPopovers) {
return children;
} else {
return (
<Popover
placement={placement || 'top'}
closeOnBlur={false}
trigger="hover"
variant="informational"
>
<PopoverTrigger>
<div>{children}</div>
</PopoverTrigger>
<PopoverContent>
<PopoverArrow />
<PopoverCloseButton />
<PopoverBody>
<Flex
sx={{
gap: 3,
flexDirection: 'column',
width: '100%',
alignItems: 'center',
}}
>
{image && (
<Image
sx={{
objectFit: 'contain',
maxW: '60%',
maxH: '60%',
backgroundColor: 'white',
}}
src={image}
alt="Optional Image"
/>
)}
<Flex
sx={{
gap: 3,
flexDirection: 'column',
width: '100%',
p: 3,
pt: heading ? 0 : 3,
}}
>
{heading && <PopoverHeader>{heading}</PopoverHeader>}
<Text sx={{ px: 3 }}>{paragraph}</Text>
{buttonLabel && (
<Flex sx={{ px: 3 }} justifyContent="flex-end">
<Button
onClick={() => window.open(buttonHref)}
size="sm"
variant="invokeAIOutline"
>
{buttonLabel}
</Button>
</Flex>
)}
</Flex>
</Flex>
</PopoverBody>
</PopoverContent>
</Popover>
);
}
}
export default IAIInformationalPopover;

View File

@ -10,6 +10,7 @@ import {
Tooltip,
} from '@chakra-ui/react';
import { useAppDispatch } from 'app/store/storeHooks';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
import {
ControlNetConfig,
controlNetBeginStepPctChanged,
@ -49,6 +50,7 @@ const ParamControlNetBeginEnd = (props: Props) => {
);
return (
<IAIInformationalPopover details="controlNetBeginEnd">
<FormControl isDisabled={!isEnabled}>
<FormLabel>{t('controlnet.beginEndStepPercent')}</FormLabel>
<HStack w="100%" gap={2} alignItems="center">
@ -101,6 +103,7 @@ const ParamControlNetBeginEnd = (props: Props) => {
</RangeSlider>
</HStack>
</FormControl>
</IAIInformationalPopover>
);
};

View File

@ -1,4 +1,5 @@
import { useAppDispatch } from 'app/store/storeHooks';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
import IAIMantineSelect from 'common/components/IAIMantineSelect';
import {
ControlModes,
@ -34,6 +35,7 @@ export default function ParamControlNetControlMode(
);
return (
<IAIInformationalPopover details="controlNetControlMode">
<IAIMantineSelect
disabled={!isEnabled}
label={t('controlnet.controlMode')}
@ -41,5 +43,6 @@ export default function ParamControlNetControlMode(
value={String(controlMode)}
onChange={handleControlModeChange}
/>
</IAIInformationalPopover>
);
}

View File

@ -2,6 +2,7 @@ import { createSelector } from '@reduxjs/toolkit';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
import IAISwitch from 'common/components/IAISwitch';
import { isControlNetEnabledToggled } from 'features/controlNet/store/controlNetSlice';
import { memo, useCallback } from 'react';
@ -25,6 +26,7 @@ const ParamControlNetFeatureToggle = () => {
}, [dispatch]);
return (
<IAIInformationalPopover details="controlNetToggle">
<IAISwitch
label="Enable ControlNet"
isChecked={isEnabled}
@ -33,6 +35,7 @@ const ParamControlNetFeatureToggle = () => {
width: '100%',
}}
/>
</IAIInformationalPopover>
);
};

View File

@ -1,4 +1,5 @@
import { useAppDispatch } from 'app/store/storeHooks';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
import IAIMantineSelect from 'common/components/IAIMantineSelect';
import {
ControlNetConfig,
@ -33,6 +34,7 @@ export default function ParamControlNetResizeMode(
);
return (
<IAIInformationalPopover details="controlNetResizeMode">
<IAIMantineSelect
disabled={!isEnabled}
label={t('controlnet.resizeMode')}
@ -40,5 +42,6 @@ export default function ParamControlNetResizeMode(
value={String(resizeMode)}
onChange={handleResizeModeChange}
/>
</IAIInformationalPopover>
);
}

View File

@ -1,4 +1,5 @@
import { useAppDispatch } from 'app/store/storeHooks';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
import IAISlider from 'common/components/IAISlider';
import {
ControlNetConfig,
@ -23,6 +24,7 @@ const ParamControlNetWeight = (props: ParamControlNetWeightProps) => {
);
return (
<IAIInformationalPopover details="controlNetWeight">
<IAISlider
isDisabled={!isEnabled}
label={t('controlnet.weight')}
@ -34,6 +36,7 @@ const ParamControlNetWeight = (props: ParamControlNetWeightProps) => {
withSliderMarks
sliderMarks={[0, 1, 2]}
/>
</IAIInformationalPopover>
);
};

View File

@ -6,6 +6,7 @@ import IAISwitch from 'common/components/IAISwitch';
import { memo, useCallback } from 'react';
import { combinatorialToggled } from '../store/dynamicPromptsSlice';
import { useTranslation } from 'react-i18next';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
const selector = createSelector(
stateSelector,
@ -27,11 +28,13 @@ const ParamDynamicPromptsCombinatorial = () => {
}, [dispatch]);
return (
<IAIInformationalPopover details="dynamicPromptsCombinatorial">
<IAISwitch
label={t('dynamicPrompts.combinatorial')}
isChecked={combinatorial}
onChange={handleChange}
/>
</IAIInformationalPopover>
);
};

View File

@ -10,6 +10,7 @@ import {
loraWeightChanged,
loraWeightReset,
} from '../store/loraSlice';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
type Props = {
lora: LoRA;
@ -35,6 +36,7 @@ const ParamLora = (props: Props) => {
}, [dispatch, lora.id]);
return (
<IAIInformationalPopover details="lora">
<Flex sx={{ gap: 2.5, alignItems: 'flex-end' }}>
<IAISlider
label={lora.model_name}
@ -59,6 +61,7 @@ const ParamLora = (props: Props) => {
colorScheme="error"
/>
</Flex>
</IAIInformationalPopover>
);
};

View File

@ -1,5 +1,6 @@
import { RootState } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
import IAISlider from 'common/components/IAISlider';
import { setClipSkip } from 'features/parameters/store/generationSlice';
import { clipSkipMap } from 'features/parameters/types/constants';
@ -42,6 +43,7 @@ export default function ParamClipSkip() {
}, [model]);
return (
<IAIInformationalPopover details="clipSkip">
<IAISlider
label={t('parameters.clipSkip')}
aria-label={t('parameters.clipSkip')}
@ -56,5 +58,6 @@ export default function ParamClipSkip() {
withReset
handleReset={handleClipSkipReset}
/>
</IAIInformationalPopover>
);
}

View File

@ -18,6 +18,7 @@ import ParamAspectRatio, {
} from '../../Core/ParamAspectRatio';
import ParamBoundingBoxHeight from './ParamBoundingBoxHeight';
import ParamBoundingBoxWidth from './ParamBoundingBoxWidth';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
const sizeOptsSelector = createSelector(
[generationSelector, canvasSelector],
@ -93,6 +94,7 @@ export default function ParamBoundingBoxSize() {
}}
>
<Flex alignItems="center" gap={2}>
<IAIInformationalPopover details="paramRatio">
<Text
sx={{
fontSize: 'sm',
@ -105,6 +107,7 @@ export default function ParamBoundingBoxSize() {
>
{t('parameters.aspectRatio')}
</Text>
</IAIInformationalPopover>
<Spacer />
<ParamAspectRatio />
<IAIIconButton

View File

@ -1,5 +1,6 @@
import type { RootState } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
import { IAISelectDataType } from 'common/components/IAIMantineSearchableSelect';
import IAIMantineSelect from 'common/components/IAIMantineSelect';
import { setCanvasCoherenceMode } from 'features/parameters/store/generationSlice';
@ -30,12 +31,14 @@ const ParamCanvasCoherenceMode = () => {
};
return (
<IAIInformationalPopover details="compositingCoherenceMode">
<IAIMantineSelect
label={t('parameters.coherenceMode')}
data={coherenceModeSelectData}
value={canvasCoherenceMode}
onChange={handleCoherenceModeChange}
/>
</IAIInformationalPopover>
);
};

View File

@ -1,5 +1,6 @@
import type { RootState } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
import IAISlider from 'common/components/IAISlider';
import { setCanvasCoherenceSteps } from 'features/parameters/store/generationSlice';
import { memo } from 'react';
@ -13,6 +14,7 @@ const ParamCanvasCoherenceSteps = () => {
const { t } = useTranslation();
return (
<IAIInformationalPopover details="compositingCoherenceSteps">
<IAISlider
label={t('parameters.coherenceSteps')}
min={1}
@ -30,6 +32,7 @@ const ParamCanvasCoherenceSteps = () => {
dispatch(setCanvasCoherenceSteps(20));
}}
/>
</IAIInformationalPopover>
);
};

View File

@ -1,5 +1,6 @@
import type { RootState } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
import IAISlider from 'common/components/IAISlider';
import { setCanvasCoherenceStrength } from 'features/parameters/store/generationSlice';
import { memo } from 'react';
@ -13,6 +14,7 @@ const ParamCanvasCoherenceStrength = () => {
const { t } = useTranslation();
return (
<IAIInformationalPopover details="compositingStrength">
<IAISlider
label={t('parameters.coherenceStrength')}
min={0}
@ -30,6 +32,7 @@ const ParamCanvasCoherenceStrength = () => {
dispatch(setCanvasCoherenceStrength(0.3));
}}
/>
</IAIInformationalPopover>
);
};

View File

@ -1,5 +1,6 @@
import type { RootState } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
import IAISlider from 'common/components/IAISlider';
import { setMaskBlur } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next';
@ -12,6 +13,7 @@ export default function ParamMaskBlur() {
const { t } = useTranslation();
return (
<IAIInformationalPopover details="compositingBlur">
<IAISlider
label={t('parameters.maskBlur')}
min={0}
@ -28,5 +30,6 @@ export default function ParamMaskBlur() {
dispatch(setMaskBlur(16));
}}
/>
</IAIInformationalPopover>
);
}

View File

@ -2,6 +2,7 @@ import { SelectItem } from '@mantine/core';
import { RootState } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
import IAIMantineSelect from 'common/components/IAIMantineSelect';
import { setMaskBlurMethod } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next';
@ -28,11 +29,13 @@ export default function ParamMaskBlurMethod() {
};
return (
<IAIInformationalPopover details="compositingBlurMethod">
<IAIMantineSelect
value={maskBlurMethod}
onChange={handleMaskBlurMethodChange}
label={t('parameters.maskBlurMethod')}
data={maskBlurMethods}
/>
</IAIInformationalPopover>
);
}

View File

@ -15,13 +15,19 @@ const ParamCompositingSettingsCollapse = () => {
return (
<IAICollapse label={t('parameters.compositingSettingsHeader')}>
<Flex sx={{ flexDirection: 'column', gap: 2 }}>
<SubParametersWrapper label={t('parameters.coherencePassHeader')}>
<SubParametersWrapper
label={t('parameters.coherencePassHeader')}
headerInfoPopover="compositingCoherencePass"
>
<ParamCanvasCoherenceMode />
<ParamCanvasCoherenceSteps />
<ParamCanvasCoherenceStrength />
</SubParametersWrapper>
<Divider />
<SubParametersWrapper label={t('parameters.maskAdjustmentsHeader')}>
<SubParametersWrapper
label={t('parameters.maskAdjustmentsHeader')}
headerInfoPopover="compositingMaskAdjustments"
>
<ParamMaskBlur />
<ParamMaskBlurMethod />
</SubParametersWrapper>

View File

@ -2,6 +2,7 @@ import { createSelector } from '@reduxjs/toolkit';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
import IAIMantineSelect from 'common/components/IAIMantineSelect';
import { setInfillMethod } from 'features/parameters/store/generationSlice';
@ -39,6 +40,7 @@ const ParamInfillMethod = () => {
);
return (
<IAIInformationalPopover details="infillMethod">
<IAIMantineSelect
disabled={infill_methods?.length === 0}
placeholder={isLoading ? 'Loading...' : undefined}
@ -47,6 +49,7 @@ const ParamInfillMethod = () => {
data={infill_methods ?? []}
onChange={handleChange}
/>
</IAIInformationalPopover>
);
};

View File

@ -1,6 +1,7 @@
import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
import IAIMantineSearchableSelect from 'common/components/IAIMantineSearchableSelect';
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
import { setBoundingBoxScaleMethod } from 'features/canvas/store/canvasSlice';
@ -35,12 +36,14 @@ const ParamScaleBeforeProcessing = () => {
};
return (
<IAIInformationalPopover details="scaleBeforeProcessing">
<IAIMantineSearchableSelect
label={t('parameters.scaleBeforeProcessing')}
data={BOUNDING_BOX_SCALES_DICT}
value={boundingBoxScale}
onChange={handleChangeBoundingBoxScaleMethod}
/>
</IAIInformationalPopover>
);
};

View File

@ -2,6 +2,7 @@ import { createSelector } from '@reduxjs/toolkit';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
import IAINumberInput from 'common/components/IAINumberInput';
import IAISlider from 'common/components/IAISlider';
import { setCfgScale } from 'features/parameters/store/generationSlice';
@ -53,6 +54,7 @@ const ParamCFGScale = () => {
);
return shouldUseSliders ? (
<IAIInformationalPopover details="paramCFGScale">
<IAISlider
label={t('parameters.cfgScale')}
step={shift ? 0.1 : 0.5}
@ -67,7 +69,9 @@ const ParamCFGScale = () => {
withSliderMarks
isInteger={false}
/>
</IAIInformationalPopover>
) : (
<IAIInformationalPopover details="paramCFGScale">
<IAINumberInput
label={t('parameters.cfgScale')}
step={0.5}
@ -78,6 +82,7 @@ const ParamCFGScale = () => {
isInteger={false}
numberInputFieldProps={{ textAlign: 'center' }}
/>
</IAIInformationalPopover>
);
};

View File

@ -2,6 +2,7 @@ import { createSelector } from '@reduxjs/toolkit';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
import IAINumberInput from 'common/components/IAINumberInput';
import IAISlider from 'common/components/IAISlider';
import { setIterations } from 'features/parameters/store/generationSlice';
@ -60,6 +61,7 @@ const ParamIterations = ({ asSlider }: Props) => {
}, [dispatch, initial]);
return asSlider || shouldUseSliders ? (
<IAIInformationalPopover details="paramImages">
<IAISlider
label={t('parameters.iterations')}
step={step}
@ -73,7 +75,9 @@ const ParamIterations = ({ asSlider }: Props) => {
withSliderMarks
sliderNumberInputProps={{ max: inputMax }}
/>
</IAIInformationalPopover>
) : (
<IAIInformationalPopover details="paramImages">
<IAINumberInput
label={t('parameters.iterations')}
step={step}
@ -83,6 +87,7 @@ const ParamIterations = ({ asSlider }: Props) => {
value={iterations}
numberInputFieldProps={{ textAlign: 'center' }}
/>
</IAIInformationalPopover>
);
};

View File

@ -9,6 +9,7 @@ import { ChangeEvent, KeyboardEvent, memo, useCallback, useRef } from 'react';
import { flushSync } from 'react-dom';
import { useTranslation } from 'react-i18next';
import { useFeatureStatus } from '../../../../system/hooks/useFeatureStatus';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
const ParamNegativeConditioning = () => {
const negativePrompt = useAppSelector(
@ -81,6 +82,7 @@ const ParamNegativeConditioning = () => {
onClose={onClose}
onSelect={handleSelectEmbedding}
>
<IAIInformationalPopover details="paramNegativeConditioning">
<IAITextarea
id="negativePrompt"
name="negativePrompt"
@ -93,6 +95,7 @@ const ParamNegativeConditioning = () => {
minH={16}
{...(isEmbeddingEnabled && { onKeyDown: handleKeyDown })}
/>
</IAIInformationalPopover>
</ParamEmbeddingPopover>
{!isOpen && isEmbeddingEnabled && (
<Box

View File

@ -12,6 +12,7 @@ import { flushSync } from 'react-dom';
import { useHotkeys } from 'react-hotkeys-hook';
import { useTranslation } from 'react-i18next';
import { useFeatureStatus } from '../../../../system/hooks/useFeatureStatus';
import IAIInformationalPopover from '../../../../../common/components/IAIInformationalPopover';
const promptInputSelector = createSelector(
[stateSelector],
@ -109,6 +110,7 @@ const ParamPositiveConditioning = () => {
onClose={onClose}
onSelect={handleSelectEmbedding}
>
<IAIInformationalPopover details="paramPositiveConditioning">
<IAITextarea
id="prompt"
name="prompt"
@ -120,6 +122,7 @@ const ParamPositiveConditioning = () => {
resize="vertical"
minH={32}
/>
</IAIInformationalPopover>
</ParamEmbeddingPopover>
</FormControl>
{!isOpen && isEmbeddingEnabled && (

View File

@ -1,6 +1,7 @@
import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
import IAIMantineSearchableSelect from 'common/components/IAIMantineSearchableSelect';
import { generationSelector } from 'features/parameters/store/generationSelectors';
import { setScheduler } from 'features/parameters/store/generationSlice';
@ -51,12 +52,14 @@ const ParamScheduler = () => {
);
return (
<IAIInformationalPopover details="paramScheduler">
<IAIMantineSearchableSelect
label={t('parameters.scheduler')}
value={scheduler}
data={data}
onChange={handleChange}
/>
</IAIInformationalPopover>
);
};

View File

@ -16,6 +16,7 @@ import { activeTabNameSelector } from '../../../../ui/store/uiSelectors';
import ParamAspectRatio, { mappedAspectRatios } from './ParamAspectRatio';
import ParamHeight from './ParamHeight';
import ParamWidth from './ParamWidth';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
const sizeOptsSelector = createSelector(
@ -83,6 +84,7 @@ export default function ParamSize() {
}}
>
<Flex alignItems="center" gap={2}>
<IAIInformationalPopover details="paramRatio">
<Text
sx={{
fontSize: 'sm',
@ -95,6 +97,7 @@ export default function ParamSize() {
>
{t('parameters.aspectRatio')}
</Text>
</IAIInformationalPopover>
<Spacer />
<ParamAspectRatio />
<IAIIconButton

View File

@ -2,6 +2,7 @@ import { createSelector } from '@reduxjs/toolkit';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
import IAINumberInput from 'common/components/IAINumberInput';
import IAISlider from 'common/components/IAISlider';
@ -56,6 +57,7 @@ const ParamSteps = () => {
}, [dispatch]);
return shouldUseSliders ? (
<IAIInformationalPopover details="paramSteps">
<IAISlider
label={t('parameters.steps')}
min={min}
@ -69,7 +71,9 @@ const ParamSteps = () => {
withSliderMarks
sliderNumberInputProps={{ max: inputMax }}
/>
</IAIInformationalPopover>
) : (
<IAIInformationalPopover details="paramSteps">
<IAINumberInput
label={t('parameters.steps')}
min={min}
@ -80,6 +84,7 @@ const ParamSteps = () => {
numberInputFieldProps={{ textAlign: 'center' }}
onBlur={handleBlur}
/>
</IAIInformationalPopover>
);
};

View File

@ -7,6 +7,7 @@ import { setImg2imgStrength } from 'features/parameters/store/generationSlice';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import SubParametersWrapper from '../SubParametersWrapper';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
const selector = createSelector(
[stateSelector],
@ -46,6 +47,7 @@ const ImageToImageStrength = () => {
return (
<SubParametersWrapper>
<IAIInformationalPopover details="paramDenoisingStrength">
<IAISlider
label={`${t('parameters.denoisingStrength')}`}
step={step}
@ -60,6 +62,7 @@ const ImageToImageStrength = () => {
withReset
sliderNumberInputProps={{ max: inputMax }}
/>
</IAIInformationalPopover>
</SubParametersWrapper>
);
};

View File

@ -21,6 +21,7 @@ import {
useGetOnnxModelsQuery,
} from 'services/api/endpoints/models';
import { useFeatureStatus } from '../../../../system/hooks/useFeatureStatus';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
const selector = createSelector(
stateSelector,
@ -118,12 +119,15 @@ const ParamMainModelSelect = () => {
data={[]}
/>
) : (
<IAIInformationalPopover details="paramModel" placement="bottom">
<Flex w="100%" alignItems="center" gap={3}>
<IAIMantineSearchableSelect
tooltip={selectedModel?.description}
label={t('modelManager.model')}
value={selectedModel?.id}
placeholder={data.length > 0 ? 'Select a model' : 'No models available'}
placeholder={
data.length > 0 ? 'Select a model' : 'No models available'
}
data={data}
error={data.length === 0}
disabled={data.length === 0}
@ -136,6 +140,7 @@ const ParamMainModelSelect = () => {
</Box>
)}
</Flex>
</IAIInformationalPopover>
);
};

View File

@ -1,4 +1,5 @@
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
import IAISwitch from 'common/components/IAISwitch';
import { shouldUseCpuNoiseChanged } from 'features/parameters/store/generationSlice';
import { ChangeEvent, useCallback } from 'react';
@ -19,10 +20,12 @@ export const ParamCpuNoiseToggle = () => {
);
return (
<IAIInformationalPopover details="noiseUseCPU">
<IAISwitch
label={t('parameters.useCpuNoise')}
isChecked={shouldUseCpuNoise}
onChange={handleChange}
/>
</IAIInformationalPopover>
);
};

View File

@ -3,14 +3,17 @@ import { memo } from 'react';
import ParamSeed from './ParamSeed';
import ParamSeedShuffle from './ParamSeedShuffle';
import ParamSeedRandomize from './ParamSeedRandomize';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
const ParamSeedFull = () => {
return (
<IAIInformationalPopover details="paramSeed">
<Flex sx={{ gap: 3, alignItems: 'flex-end' }}>
<ParamSeed />
<ParamSeedShuffle />
<ParamSeedRandomize />
</Flex>
</IAIInformationalPopover>
);
};

View File

@ -1,9 +1,11 @@
import { Flex, Text } from '@chakra-ui/react';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
import { ReactNode, memo } from 'react';
type SubParameterWrapperProps = {
children: ReactNode | ReactNode[];
label?: string;
headerInfoPopover?: string;
};
const SubParametersWrapper = (props: SubParameterWrapperProps) => (
@ -21,7 +23,18 @@ const SubParametersWrapper = (props: SubParameterWrapperProps) => (
},
}}
>
{props.label && (
{props.headerInfoPopover && props.label && (
<IAIInformationalPopover details={props.headerInfoPopover}>
<Text
fontSize="sm"
fontWeight="bold"
sx={{ color: 'base.600', _dark: { color: 'base.300' } }}
>
{props.label}
</Text>
</IAIInformationalPopover>
)}
{!props.headerInfoPopover && props.label && (
<Text
fontSize="sm"
fontWeight="bold"

View File

@ -15,6 +15,7 @@ import IAIMantineSelectItemWithTooltip from 'common/components/IAIMantineSelectI
import { vaeSelected } from 'features/parameters/store/generationSlice';
import { MODEL_TYPE_MAP } from 'features/parameters/types/constants';
import { modelIdToVAEModelParam } from 'features/parameters/util/modelIdToVAEModelParam';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
const selector = createSelector(
stateSelector,
@ -93,6 +94,7 @@ const ParamVAEModelSelect = () => {
);
return (
<IAIInformationalPopover details="paramVAE">
<IAIMantineSearchableSelect
itemComponent={IAIMantineSelectItemWithTooltip}
tooltip={selectedVaeModel?.description}
@ -104,6 +106,7 @@ const ParamVAEModelSelect = () => {
disabled={data.length === 0}
clearable
/>
</IAIInformationalPopover>
);
};

View File

@ -2,6 +2,7 @@ import { createSelector } from '@reduxjs/toolkit';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
import IAIMantineSelect from 'common/components/IAIMantineSelect';
import { vaePrecisionChanged } from 'features/parameters/store/generationSlice';
import { PrecisionParam } from 'features/parameters/types/parameterSchemas';
@ -34,12 +35,14 @@ const ParamVAEModelSelect = () => {
);
return (
<IAIInformationalPopover details="paramVAEPrecision">
<IAIMantineSelect
label="VAE Precision"
value={vaePrecision}
data={DATA}
onChange={handleChange}
/>
</IAIInformationalPopover>
);
};

View File

@ -7,6 +7,7 @@ import SubParametersWrapper from 'features/parameters/components/Parameters/SubP
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { setSDXLImg2ImgDenoisingStrength } from '../store/sdxlSlice';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
const selector = createSelector(
[stateSelector],
@ -36,6 +37,7 @@ const ParamSDXLImg2ImgDenoisingStrength = () => {
return (
<SubParametersWrapper>
<IAIInformationalPopover details="paramDenoisingStrength">
<IAISlider
label={t('sdxl.denoisingStrength')}
step={0.01}
@ -49,6 +51,7 @@ const ParamSDXLImg2ImgDenoisingStrength = () => {
withSliderMarks
withReset
/>
</IAIInformationalPopover>
</SubParametersWrapper>
);
};

View File

@ -23,6 +23,7 @@ import {
consoleLogLevelChanged,
setEnableImageDebugging,
setShouldConfirmOnDelete,
setShouldDisableInformationalPopovers,
shouldAntialiasProgressImageChanged,
shouldLogToConsoleChanged,
shouldUseNSFWCheckerChanged,
@ -66,6 +67,7 @@ const selector = createSelector(
shouldAntialiasProgressImage,
shouldUseNSFWChecker,
shouldUseWatermarker,
shouldDisableInformationalPopovers,
} = system;
const {
@ -85,6 +87,7 @@ const selector = createSelector(
shouldUseNSFWChecker,
shouldUseWatermarker,
shouldAutoChangeDimensions,
shouldDisableInformationalPopovers,
};
},
{
@ -158,6 +161,7 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => {
shouldUseNSFWChecker,
shouldUseWatermarker,
shouldAutoChangeDimensions,
shouldDisableInformationalPopovers,
} = useAppSelector(selector);
const handleClickResetWebUI = useCallback(() => {
@ -307,6 +311,15 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => {
onChange={handleLanguageChanged}
/>
)}
<SettingSwitch
label="Disable informational popovers"
isChecked={shouldDisableInformationalPopovers}
onChange={(e: ChangeEvent<HTMLInputElement>) =>
dispatch(
setShouldDisableInformationalPopovers(e.target.checked)
)
}
/>
</StyledFlex>
{shouldShowDeveloperSettings && (

View File

@ -35,6 +35,7 @@ export const initialSystemState: SystemState = {
language: 'en',
shouldUseNSFWChecker: false,
shouldUseWatermarker: false,
shouldDisableInformationalPopovers: false,
status: 'DISCONNECTED',
};
@ -75,6 +76,12 @@ export const systemSlice = createSlice({
shouldUseWatermarkerChanged(state, action: PayloadAction<boolean>) {
state.shouldUseWatermarker = action.payload;
},
setShouldDisableInformationalPopovers(
state,
action: PayloadAction<boolean>
) {
state.shouldDisableInformationalPopovers = action.payload;
},
},
extraReducers(builder) {
/**
@ -234,6 +241,7 @@ export const {
languageChanged,
shouldUseNSFWCheckerChanged,
shouldUseWatermarkerChanged,
setShouldDisableInformationalPopovers,
} = systemSlice.actions;
export default systemSlice.reducer;

View File

@ -33,6 +33,7 @@ export interface SystemState {
shouldUseNSFWChecker: boolean;
shouldUseWatermarker: boolean;
status: SystemStatus;
shouldDisableInformationalPopovers: boolean;
}
export const LANGUAGES = {

View File

@ -80,6 +80,13 @@ const invokeAIOutline = defineStyle((props) => {
return {
border: '1px solid',
borderColor: c === 'gray' ? borderColor : 'currentColor',
_hover: {
bg: mode(`${c}.500`, `${c}.500`)(props),
color: mode('white', `base.50`)(props),
svg: {
fill: mode('white', `base.50`)(props),
},
},
'.chakra-button__group[data-attached][data-orientation=horizontal] > &:not(:last-of-type)':
{
marginEnd: '-1px',

View File

@ -29,13 +29,34 @@ const invokeAIContent = defineStyle((props) => {
};
});
const informationalContent = defineStyle((props) => {
return {
[$arrowBg.variable]: mode('colors.base.100', 'colors.base.600')(props),
[$popperBg.variable]: mode('colors.base.100', 'colors.base.600')(props),
[$arrowShadowColor.variable]: mode(
'colors.base.400',
'colors.base.400'
)(props),
p: 0,
bg: mode('base.100', 'base.600')(props),
border: 'none',
shadow: 'dark-lg',
};
});
const invokeAI = definePartsStyle((props) => ({
content: invokeAIContent(props),
}));
const informational = definePartsStyle((props) => ({
content: informationalContent(props),
body: { padding: 0 },
}));
export const popoverTheme = defineMultiStyleConfig({
variants: {
invokeAI,
informational,
},
defaultProps: {
variant: 'invokeAI',