diff --git a/invokeai/frontend/web/public/assets/images/commercial-license-bg.png b/invokeai/frontend/web/public/assets/images/commercial-license-bg.png
new file mode 100644
index 0000000000..a5e8c3a002
Binary files /dev/null and b/invokeai/frontend/web/public/assets/images/commercial-license-bg.png differ
diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json
index b10846e2d8..3e858f0076 100644
--- a/invokeai/frontend/web/public/locales/en.json
+++ b/invokeai/frontend/web/public/locales/en.json
@@ -1541,6 +1541,12 @@
"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."
]
+ },
+ "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": {
diff --git a/invokeai/frontend/web/src/common/components/InformationalPopover/InformationalPopover.tsx b/invokeai/frontend/web/src/common/components/InformationalPopover/InformationalPopover.tsx
index 04126647aa..b3bdb2baf0 100644
--- a/invokeai/frontend/web/src/common/components/InformationalPopover/InformationalPopover.tsx
+++ b/invokeai/frontend/web/src/common/components/InformationalPopover/InformationalPopover.tsx
@@ -29,6 +29,7 @@ import { OPEN_DELAY, POPOVER_DATA, POPPER_MODIFIERS } from './constants';
type Props = {
feature: Feature;
inPortal?: boolean;
+ hideDisable?: boolean;
children: ReactElement;
};
@@ -37,48 +38,51 @@ const selectShouldEnableInformationalPopovers = createSelector(
(system) => system.shouldEnableInformationalPopovers
);
-export const InformationalPopover = memo(({ feature, children, inPortal = true, ...rest }: Props) => {
- const shouldEnableInformationalPopovers = useAppSelector(selectShouldEnableInformationalPopovers);
+export const InformationalPopover = memo(
+ ({ 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) {
- return children;
+ if (!hideDisable && !shouldEnableInformationalPopovers) {
+ return children;
+ }
+
+ return (
+
+ {children}
+ {inPortal ? (
+
+
+
+ ) : (
+
+ )}
+
+ );
}
-
- return (
-
- {children}
- {inPortal ? (
-
-
-
- ) : (
-
- )}
-
- );
-});
+);
InformationalPopover.displayName = 'InformationalPopover';
type ContentProps = {
data?: PopoverData;
feature: Feature;
+ hideDisable: boolean;
};
-const Content = ({ data, feature }: ContentProps) => {
+const Content = ({ data, feature, hideDisable }: ContentProps) => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const heading = useMemo(() => t(`popovers.${feature}.heading`), [feature, t]);
@@ -120,14 +124,7 @@ const Content = ({ data, feature }: ContentProps) => {
)}
{data?.image && (
<>
-
+
>
)}
@@ -137,9 +134,11 @@ const Content = ({ data, feature }: ContentProps) => {
-
+ {!hideDisable && (
+
+ )}
{data?.href && (
} variant="link" size="sm">
diff --git a/invokeai/frontend/web/src/common/components/InformationalPopover/constants.ts b/invokeai/frontend/web/src/common/components/InformationalPopover/constants.ts
index fae8900787..450a9f2f06 100644
--- a/invokeai/frontend/web/src/common/components/InformationalPopover/constants.ts
+++ b/invokeai/frontend/web/src/common/components/InformationalPopover/constants.ts
@@ -1,4 +1,5 @@
import type { PopoverProps } from '@invoke-ai/ui-library';
+import commercialLicenseBg from 'public/assets/images/commercial-license-bg.png';
export type Feature =
| 'clipSkip'
@@ -58,7 +59,8 @@ export type Feature =
| 'upscaleModel'
| 'scale'
| 'creativity'
- | 'structure';
+ | 'structure'
+ | 'fluxDevLicense';
export type PopoverData = PopoverProps & {
image?: string;
@@ -186,6 +188,10 @@ export const POPOVER_DATA: { [key in Feature]?: PopoverData } = {
seamlessTilingYAxis: {
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;
export const OPEN_DELAY = 1000; // in milliseconds
diff --git a/invokeai/frontend/web/src/features/parameters/components/MainModel/ParamMainModelSelect.tsx b/invokeai/frontend/web/src/features/parameters/components/MainModel/ParamMainModelSelect.tsx
index 3b69ea6014..b52b7ee02f 100644
--- a/invokeai/frontend/web/src/features/parameters/components/MainModel/ParamMainModelSelect.tsx
+++ b/invokeai/frontend/web/src/features/parameters/components/MainModel/ParamMainModelSelect.tsx
@@ -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 { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
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 { modelSelected } from 'features/parameters/store/actions';
import { selectActiveTab } from 'features/ui/store/uiSelectors';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
+import { MdMoneyOff } from 'react-icons/md';
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 dispatch = useAppDispatch();
const { t } = useTranslation();
const activeTabName = useAppSelector(selectActiveTab);
- const selectedModel = useAppSelector(selectModel);
+ const selectedModelKey = useAppSelector(selectModelKey);
+ // const selectedModel = useAppSelector(selectModel);
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(() => {
if (!modelConfigs.length || !selectedModel) {
return;
@@ -54,11 +72,26 @@ const ParamMainModelSelect = () => {
getIsDisabled,
});
+ const isFluxDevSelected = useMemo(() => {
+ return selectedModel && isCheckpointMainModelConfig(selectedModel) && selectedModel.config_path === 'flux-dev';
+ }, [selectedModel]);
+
return (
-
- {t('modelManager.model')}
-
+
+
+ {t('modelManager.model')}
+
+ {isFluxDevSelected ? (
+
+
+
+
+
+ ) : (
+
+ )}
+
{
+ return config.type === 'main' && (config.format === 'checkpoint' || config.format === 'bnb_quantized_nf4b');
+};
+
export const isRefinerMainModelModelConfig = (config: AnyModelConfig): config is MainModelConfig => {
return config.type === 'main' && config.base === 'sdxl-refiner';
};