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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,9 +1,11 @@
import { Flex, Text } from '@chakra-ui/react'; import { Flex, Text } from '@chakra-ui/react';
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
import { ReactNode, memo } from 'react'; import { ReactNode, memo } from 'react';
type SubParameterWrapperProps = { type SubParameterWrapperProps = {
children: ReactNode | ReactNode[]; children: ReactNode | ReactNode[];
label?: string; label?: string;
headerInfoPopover?: string;
}; };
const SubParametersWrapper = (props: SubParameterWrapperProps) => ( 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 <Text
fontSize="sm" fontSize="sm"
fontWeight="bold" fontWeight="bold"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -80,6 +80,13 @@ const invokeAIOutline = defineStyle((props) => {
return { return {
border: '1px solid', border: '1px solid',
borderColor: c === 'gray' ? borderColor : 'currentColor', 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)': '.chakra-button__group[data-attached][data-orientation=horizontal] > &:not(:last-of-type)':
{ {
marginEnd: '-1px', 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) => ({ const invokeAI = definePartsStyle((props) => ({
content: invokeAIContent(props), content: invokeAIContent(props),
})); }));
const informational = definePartsStyle((props) => ({
content: informationalContent(props),
body: { padding: 0 },
}));
export const popoverTheme = defineMultiStyleConfig({ export const popoverTheme = defineMultiStyleConfig({
variants: { variants: {
invokeAI, invokeAI,
informational,
}, },
defaultProps: { defaultProps: {
variant: 'invokeAI', variant: 'invokeAI',