feat(ui): add notice for FLUX dev commercial license requirement

This commit is contained in:
Mary Hipp
2024-09-20 13:08:47 -04:00
parent a4c9b0d421
commit f37eee29a9
6 changed files with 97 additions and 49 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

View File

@@ -1541,6 +1541,12 @@
"paragraphs": [ "paragraphs": [
"Structure controls how closely the output image will keep to the layout of the original. Low structure allows major changes, while high structure strictly maintains the original composition and layout." "Structure controls how closely the output image will keep to the layout of the original. Low structure allows major changes, while high structure strictly maintains the original composition and layout."
] ]
},
"fluxDevLicense": {
"heading": "Non-Commercial License",
"paragraphs": [
"FLUX.1 [dev] models are licensed under the FLUX [dev] non-commercial license. To use this model type for commercial purposes in Invoke, visit our website to learn more."
]
} }
}, },
"unifiedCanvas": { "unifiedCanvas": {

View File

@@ -29,6 +29,7 @@ import { OPEN_DELAY, POPOVER_DATA, POPPER_MODIFIERS } from './constants';
type Props = { type Props = {
feature: Feature; feature: Feature;
inPortal?: boolean; inPortal?: boolean;
hideDisable?: boolean;
children: ReactElement; children: ReactElement;
}; };
@@ -37,48 +38,51 @@ const selectShouldEnableInformationalPopovers = createSelector(
(system) => system.shouldEnableInformationalPopovers (system) => system.shouldEnableInformationalPopovers
); );
export const InformationalPopover = memo(({ feature, children, inPortal = true, ...rest }: Props) => { export const InformationalPopover = memo(
const shouldEnableInformationalPopovers = useAppSelector(selectShouldEnableInformationalPopovers); ({ feature, children, inPortal = true, hideDisable = false, ...rest }: Props) => {
const shouldEnableInformationalPopovers = useAppSelector(selectShouldEnableInformationalPopovers);
const data = useMemo(() => POPOVER_DATA[feature], [feature]); const data = useMemo(() => POPOVER_DATA[feature], [feature]);
const popoverProps = useMemo(() => merge(omit(data, ['image', 'href', 'buttonLabel']), rest), [data, rest]); const popoverProps = useMemo(() => merge(omit(data, ['image', 'href', 'buttonLabel']), rest), [data, rest]);
if (!shouldEnableInformationalPopovers) { if (!hideDisable && !shouldEnableInformationalPopovers) {
return children; return children;
}
return (
<Popover
isLazy
closeOnBlur={false}
trigger="hover"
variant="informational"
openDelay={OPEN_DELAY}
modifiers={POPPER_MODIFIERS}
placement="top"
{...popoverProps}
>
<PopoverTrigger>{children}</PopoverTrigger>
{inPortal ? (
<Portal>
<Content data={data} feature={feature} hideDisable={hideDisable} />
</Portal>
) : (
<Content data={data} feature={feature} hideDisable={hideDisable} />
)}
</Popover>
);
} }
);
return (
<Popover
isLazy
closeOnBlur={false}
trigger="hover"
variant="informational"
openDelay={OPEN_DELAY}
modifiers={POPPER_MODIFIERS}
placement="top"
{...popoverProps}
>
<PopoverTrigger>{children}</PopoverTrigger>
{inPortal ? (
<Portal>
<Content data={data} feature={feature} />
</Portal>
) : (
<Content data={data} feature={feature} />
)}
</Popover>
);
});
InformationalPopover.displayName = 'InformationalPopover'; InformationalPopover.displayName = 'InformationalPopover';
type ContentProps = { type ContentProps = {
data?: PopoverData; data?: PopoverData;
feature: Feature; feature: Feature;
hideDisable: boolean;
}; };
const Content = ({ data, feature }: ContentProps) => { const Content = ({ data, feature, hideDisable }: ContentProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const heading = useMemo<string | undefined>(() => t(`popovers.${feature}.heading`), [feature, t]); const heading = useMemo<string | undefined>(() => t(`popovers.${feature}.heading`), [feature, t]);
@@ -120,14 +124,7 @@ const Content = ({ data, feature }: ContentProps) => {
)} )}
{data?.image && ( {data?.image && (
<> <>
<Image <Image objectFit="contain" backgroundColor="white" src={data.image} alt="Optional Image" />
objectFit="contain"
maxW="60%"
maxH="60%"
backgroundColor="white"
src={data.image}
alt="Optional Image"
/>
<Divider /> <Divider />
</> </>
)} )}
@@ -137,9 +134,11 @@ const Content = ({ data, feature }: ContentProps) => {
<Divider /> <Divider />
<Flex alignItems="center" justifyContent="space-between" w="full"> <Flex alignItems="center" justifyContent="space-between" w="full">
<Button onClick={onClickDontShowMeThese} variant="link" size="sm"> {!hideDisable && (
{t('common.dontShowMeThese')} <Button onClick={onClickDontShowMeThese} variant="link" size="sm">
</Button> {t('common.dontShowMeThese')}
</Button>
)}
<Spacer /> <Spacer />
{data?.href && ( {data?.href && (
<Button onClick={onClickLearnMore} leftIcon={<PiArrowSquareOutBold />} variant="link" size="sm"> <Button onClick={onClickLearnMore} leftIcon={<PiArrowSquareOutBold />} variant="link" size="sm">

View File

@@ -1,4 +1,5 @@
import type { PopoverProps } from '@invoke-ai/ui-library'; import type { PopoverProps } from '@invoke-ai/ui-library';
import commercialLicenseBg from 'public/assets/images/commercial-license-bg.png';
export type Feature = export type Feature =
| 'clipSkip' | 'clipSkip'
@@ -58,7 +59,8 @@ export type Feature =
| 'upscaleModel' | 'upscaleModel'
| 'scale' | 'scale'
| 'creativity' | 'creativity'
| 'structure'; | 'structure'
| 'fluxDevLicense';
export type PopoverData = PopoverProps & { export type PopoverData = PopoverProps & {
image?: string; image?: string;
@@ -186,6 +188,10 @@ export const POPOVER_DATA: { [key in Feature]?: PopoverData } = {
seamlessTilingYAxis: { seamlessTilingYAxis: {
href: 'https://support.invoke.ai/support/solutions/articles/151000178161-advanced-settings', href: 'https://support.invoke.ai/support/solutions/articles/151000178161-advanced-settings',
}, },
fluxDevLicense: {
href: 'https://www.invoke.com/get-a-commercial-license-for-flux',
image: commercialLicenseBg,
},
} as const; } as const;
export const OPEN_DELAY = 1000; // in milliseconds export const OPEN_DELAY = 1000; // in milliseconds

View File

@@ -1,23 +1,41 @@
import { Box, Combobox, FormControl, FormLabel, Tooltip } from '@invoke-ai/ui-library'; import { Box, Combobox, Flex, FormControl, FormLabel, Icon, Spacer, Tooltip } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover'; import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { useGroupedModelCombobox } from 'common/hooks/useGroupedModelCombobox'; import { useGroupedModelCombobox } from 'common/hooks/useGroupedModelCombobox';
import { selectModel } from 'features/controlLayers/store/paramsSlice'; import { selectModelKey } from 'features/controlLayers/store/paramsSlice';
import { zModelIdentifierField } from 'features/nodes/types/common'; import { zModelIdentifierField } from 'features/nodes/types/common';
import { modelSelected } from 'features/parameters/store/actions'; import { modelSelected } from 'features/parameters/store/actions';
import { selectActiveTab } from 'features/ui/store/uiSelectors'; import { selectActiveTab } from 'features/ui/store/uiSelectors';
import { memo, useCallback, useMemo } from 'react'; import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { MdMoneyOff } from 'react-icons/md';
import { useMainModels } from 'services/api/hooks/modelsByType'; import { useMainModels } from 'services/api/hooks/modelsByType';
import type { AnyModelConfig, MainModelConfig } from 'services/api/types'; import { type AnyModelConfig, isCheckpointMainModelConfig, type MainModelConfig } from 'services/api/types';
const ParamMainModelSelect = () => { const ParamMainModelSelect = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { t } = useTranslation(); const { t } = useTranslation();
const activeTabName = useAppSelector(selectActiveTab); const activeTabName = useAppSelector(selectActiveTab);
const selectedModel = useAppSelector(selectModel); const selectedModelKey = useAppSelector(selectModelKey);
// const selectedModel = useAppSelector(selectModel);
const [modelConfigs, { isLoading }] = useMainModels(); const [modelConfigs, { isLoading }] = useMainModels();
const selectedModel = useMemo(() => {
if (!modelConfigs) {
return null;
}
if (selectedModelKey === null) {
return null;
}
const modelConfig = modelConfigs.find((model) => model.key === selectedModelKey);
if (!modelConfig) {
return null;
}
return modelConfig;
}, [modelConfigs, selectedModelKey]);
const tooltipLabel = useMemo(() => { const tooltipLabel = useMemo(() => {
if (!modelConfigs.length || !selectedModel) { if (!modelConfigs.length || !selectedModel) {
return; return;
@@ -54,11 +72,26 @@ const ParamMainModelSelect = () => {
getIsDisabled, getIsDisabled,
}); });
const isFluxDevSelected = useMemo(() => {
return selectedModel && isCheckpointMainModelConfig(selectedModel) && selectedModel.config_path === 'flux-dev';
}, [selectedModel]);
return ( return (
<FormControl isDisabled={!modelConfigs.length} isInvalid={!value || !modelConfigs.length}> <FormControl isDisabled={!modelConfigs.length} isInvalid={!value || !modelConfigs.length}>
<InformationalPopover feature="paramModel"> <Flex>
<FormLabel>{t('modelManager.model')}</FormLabel> <InformationalPopover feature="paramModel">
</InformationalPopover> <FormLabel>{t('modelManager.model')}</FormLabel>
</InformationalPopover>
{isFluxDevSelected ? (
<InformationalPopover feature="fluxDevLicense" hideDisable={true}>
<Flex justifyContent="flex-start">
<Icon as={MdMoneyOff} />
</Flex>
</InformationalPopover>
) : (
<Spacer />
)}
</Flex>
<Tooltip label={tooltipLabel}> <Tooltip label={tooltipLabel}>
<Box w="full" minW={0}> <Box w="full" minW={0}>
<Combobox <Combobox

View File

@@ -129,6 +129,10 @@ export const isNonRefinerMainModelConfig = (config: AnyModelConfig): config is M
return config.type === 'main' && config.base !== 'sdxl-refiner'; return config.type === 'main' && config.base !== 'sdxl-refiner';
}; };
export const isCheckpointMainModelConfig = (config: AnyModelConfig): config is CheckpointModelConfig => {
return config.type === 'main' && (config.format === 'checkpoint' || config.format === 'bnb_quantized_nf4b');
};
export const isRefinerMainModelModelConfig = (config: AnyModelConfig): config is MainModelConfig => { export const isRefinerMainModelModelConfig = (config: AnyModelConfig): config is MainModelConfig => {
return config.type === 'main' && config.base === 'sdxl-refiner'; return config.type === 'main' && config.base === 'sdxl-refiner';
}; };