diff --git a/invokeai/frontend/web/src/common/components/IAICustomSelect.tsx b/invokeai/frontend/web/src/common/components/IAICustomSelect.tsx index dd1b7ab5e2..d9610346ec 100644 --- a/invokeai/frontend/web/src/common/components/IAICustomSelect.tsx +++ b/invokeai/frontend/web/src/common/components/IAICustomSelect.tsx @@ -1,28 +1,25 @@ -import { CheckIcon, ChevronUpIcon } from '@chakra-ui/icons'; +import { CheckIcon } from '@chakra-ui/icons'; import { + Box, Flex, + FlexProps, FormControl, FormControlProps, FormLabel, Grid, GridItem, - Input, List, ListItem, Select, - Spacer, Text, + Tooltip, + TooltipProps, } from '@chakra-ui/react'; -import { useEnsureOnScreen } from 'common/hooks/useEnsureOnScreen'; +import { autoUpdate, offset, shift, useFloating } from '@floating-ui/react-dom'; import { useSelect } from 'downshift'; import { OverlayScrollbarsComponent } from 'overlayscrollbars-react'; -import { memo, useRef } from 'react'; -import { useIntersection } from 'react-use'; - -const BUTTON_BG = 'base.900'; -const BORDER_HOVER = 'base.700'; -const BORDER_FOCUS = 'accent.600'; +import { memo } from 'react'; type IAICustomSelectProps = { label?: string; @@ -31,6 +28,9 @@ type IAICustomSelectProps = { setSelectedItem: (v: string | null | undefined) => void; withCheckIcon?: boolean; formControlProps?: FormControlProps; + buttonProps?: FlexProps; + tooltip?: string; + tooltipProps?: Omit; }; const IAICustomSelect = (props: IAICustomSelectProps) => { @@ -41,6 +41,9 @@ const IAICustomSelect = (props: IAICustomSelectProps) => { selectedItem, withCheckIcon, formControlProps, + tooltip, + buttonProps, + tooltipProps, } = props; const { @@ -57,105 +60,111 @@ const IAICustomSelect = (props: IAICustomSelectProps) => { setSelectedItem(newSelectedItem), }); - const toggleButtonRef = useRef(null); - const menuRef = useRef(null); + const { refs, floatingStyles } = useFloating({ + whileElementsMounted: autoUpdate, + middleware: [offset(4), shift({ crossAxis: true, padding: 8 })], + }); return ( - + {label && ( { - toggleButtonRef.current && toggleButtonRef.current.focus(); + refs.floating.current && refs.floating.current.focus(); }} > {label} )} - - - - {isOpen && - items.map((item, index) => ( - - {withCheckIcon ? ( - - - {selectedItem === item && } - - - - {item} - - - - ) : ( - - {item} - - )} - - ))} - - + + + + + {isOpen && ( + + + {items.map((item, index) => ( + + {withCheckIcon ? ( + + + {selectedItem === item && } + + + + {item} + + + + ) : ( + + {item} + + )} + + ))} + + + )} + ); }; diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamSampler.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamSampler.tsx index bd31a73829..b3dd4a0f27 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamSampler.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamSampler.tsx @@ -1,5 +1,6 @@ import { RootState } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import IAICustomSelect from 'common/components/IAICustomSelect'; import IAISelect from 'common/components/IAISelect'; import { setSampler } from 'features/parameters/store/generationSlice'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; @@ -23,21 +24,26 @@ const ParamSampler = () => { const { t } = useTranslation(); const handleChange = useCallback( - (e: ChangeEvent) => dispatch(setSampler(e.target.value)), + (v: string | null | undefined) => { + if (!v) { + return; + } + dispatch(setSampler(v)); + }, [dispatch] ); return ( - ); }; diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamSchedulerAndModel.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamSchedulerAndModel.tsx new file mode 100644 index 0000000000..489d4fad55 --- /dev/null +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamSchedulerAndModel.tsx @@ -0,0 +1,19 @@ +import { Box, Flex } from '@chakra-ui/react'; +import { memo } from 'react'; +import ParamSampler from './ParamSampler'; +import ModelSelect from 'features/system/components/ModelSelect'; + +const ParamSchedulerAndModel = () => { + return ( + + + + + + + + + ); +}; + +export default memo(ParamSchedulerAndModel); diff --git a/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts b/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts index 5ad0e4973c..604aabbc9b 100644 --- a/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts +++ b/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts @@ -2,8 +2,9 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; import * as InvokeAI from 'app/types/invokeai'; import promptToString from 'common/util/promptToString'; -import { clamp } from 'lodash-es'; +import { clamp, sample } from 'lodash-es'; import { setAllParametersReducer } from './setAllParametersReducer'; +import { receivedModels } from 'services/thunks/model'; export interface GenerationState { cfgScale: number; @@ -236,6 +237,16 @@ export const generationSlice = createSlice({ state.model = action.payload; }, }, + extraReducers: (builder) => { + builder.addCase(receivedModels.fulfilled, (state, action) => { + if (!state.model) { + const randomModel = sample(action.payload); + if (randomModel) { + state.model = randomModel.name; + } + } + }); + }, }); export const { diff --git a/invokeai/frontend/web/src/features/system/components/ModelSelect.tsx b/invokeai/frontend/web/src/features/system/components/ModelSelect.tsx index e38fda2676..520e30b60a 100644 --- a/invokeai/frontend/web/src/features/system/components/ModelSelect.tsx +++ b/invokeai/frontend/web/src/features/system/components/ModelSelect.tsx @@ -1,21 +1,20 @@ import { createSelector } from '@reduxjs/toolkit'; -import { ChangeEvent, memo } from 'react'; +import { memo, useCallback } from 'react'; import { isEqual } from 'lodash-es'; import { useTranslation } from 'react-i18next'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import IAISelect from 'common/components/IAISelect'; import { selectModelsById, selectModelsIds } from '../store/modelSlice'; import { RootState } from 'app/store/store'; import { modelSelected } from 'features/parameters/store/generationSlice'; import { generationSelector } from 'features/parameters/store/generationSelectors'; +import IAICustomSelect from 'common/components/IAICustomSelect'; const selector = createSelector( [(state: RootState) => state, generationSelector], (state, generation) => { - // const selectedModel = selectedModelSelector(state); const selectedModel = selectModelsById(state, generation.model); - const allModelNames = selectModelsIds(state); + const allModelNames = selectModelsIds(state).map((id) => String(id)); return { allModelNames, selectedModel, @@ -32,19 +31,25 @@ const ModelSelect = () => { const dispatch = useAppDispatch(); const { t } = useTranslation(); const { allModelNames, selectedModel } = useAppSelector(selector); - const handleChangeModel = (e: ChangeEvent) => { - dispatch(modelSelected(e.target.value)); - }; + const handleChangeModel = useCallback( + (v: string | null | undefined) => { + if (!v) { + return; + } + dispatch(modelSelected(v)); + }, + [dispatch] + ); return ( - ); }; diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ImageToImage/ImageToImageTabCoreParameters.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/ImageToImage/ImageToImageTabCoreParameters.tsx index aba85646af..c4161154bb 100644 --- a/invokeai/frontend/web/src/features/ui/components/tabs/ImageToImage/ImageToImageTabCoreParameters.tsx +++ b/invokeai/frontend/web/src/features/ui/components/tabs/ImageToImage/ImageToImageTabCoreParameters.tsx @@ -1,5 +1,5 @@ import { memo } from 'react'; -import { Box, Flex } from '@chakra-ui/react'; +import { Flex } from '@chakra-ui/react'; import { createSelector } from '@reduxjs/toolkit'; import { uiSelector } from 'features/ui/store/uiSelectors'; import { useAppSelector } from 'app/store/storeHooks'; @@ -9,11 +9,10 @@ import ParamSteps from 'features/parameters/components/Parameters/Core/ParamStep import ParamCFGScale from 'features/parameters/components/Parameters/Core/ParamCFGScale'; import ParamWidth from 'features/parameters/components/Parameters/Core/ParamWidth'; import ParamHeight from 'features/parameters/components/Parameters/Core/ParamHeight'; -import ParamSampler from 'features/parameters/components/Parameters/Core/ParamSampler'; -import ModelSelect from 'features/system/components/ModelSelect'; import ImageToImageStrength from 'features/parameters/components/Parameters/ImageToImage/ImageToImageStrength'; import ImageToImageFit from 'features/parameters/components/Parameters/ImageToImage/ImageToImageFit'; import { generationSelector } from 'features/parameters/store/generationSelectors'; +import ParamSchedulerAndModel from 'features/parameters/components/Parameters/Core/ParamSchedulerAndModel'; const selector = createSelector( [uiSelector, generationSelector], @@ -48,14 +47,7 @@ const ImageToImageTabCoreParameters = () => { - - - - - - - - + ) : ( @@ -64,14 +56,7 @@ const ImageToImageTabCoreParameters = () => { - - - - - - - - + diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/TextToImage/TextToImageTabCoreParameters.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/TextToImage/TextToImageTabCoreParameters.tsx index d7edef148c..59512775bc 100644 --- a/invokeai/frontend/web/src/features/ui/components/tabs/TextToImage/TextToImageTabCoreParameters.tsx +++ b/invokeai/frontend/web/src/features/ui/components/tabs/TextToImage/TextToImageTabCoreParameters.tsx @@ -3,14 +3,13 @@ import ParamSteps from 'features/parameters/components/Parameters/Core/ParamStep import ParamCFGScale from 'features/parameters/components/Parameters/Core/ParamCFGScale'; import ParamWidth from 'features/parameters/components/Parameters/Core/ParamWidth'; import ParamHeight from 'features/parameters/components/Parameters/Core/ParamHeight'; -import ParamSampler from 'features/parameters/components/Parameters/Core/ParamSampler'; -import ModelSelect from 'features/system/components/ModelSelect'; -import { Box, Flex } from '@chakra-ui/react'; +import { Flex } from '@chakra-ui/react'; import { useAppSelector } from 'app/store/storeHooks'; import { createSelector } from '@reduxjs/toolkit'; import { uiSelector } from 'features/ui/store/uiSelectors'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import { memo } from 'react'; +import ParamSchedulerAndModel from 'features/parameters/components/Parameters/Core/ParamSchedulerAndModel'; const selector = createSelector( uiSelector, @@ -42,14 +41,7 @@ const TextToImageTabCoreParameters = () => { - - - - - - - - + ) : ( @@ -58,14 +50,7 @@ const TextToImageTabCoreParameters = () => { - - - - - - - - + diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasCoreParameters.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasCoreParameters.tsx index 74949a399d..f2529e5529 100644 --- a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasCoreParameters.tsx +++ b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasCoreParameters.tsx @@ -1,10 +1,9 @@ import { memo } from 'react'; -import { Box, Flex } from '@chakra-ui/react'; +import { Flex } from '@chakra-ui/react'; import { createSelector } from '@reduxjs/toolkit'; import { uiSelector } from 'features/ui/store/uiSelectors'; import { useAppSelector } from 'app/store/storeHooks'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; -import ModelSelect from 'features/system/components/ModelSelect'; import ParamIterations from 'features/parameters/components/Parameters/Core/ParamIterations'; import ParamSteps from 'features/parameters/components/Parameters/Core/ParamSteps'; import ParamCFGScale from 'features/parameters/components/Parameters/Core/ParamCFGScale'; @@ -12,7 +11,7 @@ import ParamWidth from 'features/parameters/components/Parameters/Core/ParamWidt import ParamHeight from 'features/parameters/components/Parameters/Core/ParamHeight'; import ImageToImageStrength from 'features/parameters/components/Parameters/ImageToImage/ImageToImageStrength'; import ImageToImageFit from 'features/parameters/components/Parameters/ImageToImage/ImageToImageFit'; -import ParamSampler from 'features/parameters/components/Parameters/Core/ParamSampler'; +import ParamSchedulerAndModel from 'features/parameters/components/Parameters/Core/ParamSchedulerAndModel'; const selector = createSelector( uiSelector, @@ -46,14 +45,7 @@ const UnifiedCanvasCoreParameters = () => { - - - - - - - - + ) : ( @@ -62,14 +54,7 @@ const UnifiedCanvasCoreParameters = () => { - - - - - - - - +