mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-04-23 03:00:31 -04:00
feat(ui): just make the damn thing myself
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { GlobalImageHotkeys } from 'app/components/GlobalImageHotkeys';
|
||||
import { ModelCmdk } from 'common/components/ModelCmdk/ModelCmdk';
|
||||
import { ModelCombobox } from 'common/components/ModelCombobox/ModelCombobox';
|
||||
import ChangeBoardModal from 'features/changeBoardModal/components/ChangeBoardModal';
|
||||
import { CanvasPasteModal } from 'features/controlLayers/components/CanvasPasteModal';
|
||||
import {
|
||||
@@ -59,7 +59,7 @@ export const GlobalModalIsolator = memo(() => {
|
||||
<CanvasPasteModal />
|
||||
</CanvasManagerProviderGate>
|
||||
<LoadWorkflowFromGraphModal />
|
||||
<ModelCmdk />
|
||||
<ModelCombobox />
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,270 +0,0 @@
|
||||
import type { SystemStyleObject } from '@invoke-ai/ui-library';
|
||||
import { Box, chakra, Flex, Input, Modal, ModalBody, ModalContent, ModalOverlay, Text } from '@invoke-ai/ui-library';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { CommandEmpty, CommandItem, CommandList, CommandRoot } from 'cmdk';
|
||||
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
|
||||
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
|
||||
import { atom } from 'nanostores';
|
||||
import type { ChangeEvent, RefObject } from 'react';
|
||||
import { memo, useCallback, useMemo, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { AnyModelConfig } from 'services/api/types';
|
||||
import { useDebounce } from 'use-debounce';
|
||||
|
||||
export type ModelCmdkOptions = {
|
||||
modelConfigs: AnyModelConfig[];
|
||||
onSelect: (modelConfig: AnyModelConfig) => void;
|
||||
onClose?: () => void;
|
||||
};
|
||||
|
||||
type ModelCmdkState =
|
||||
| (ModelCmdkOptions & {
|
||||
isOpen: true;
|
||||
})
|
||||
| {
|
||||
isOpen: false;
|
||||
};
|
||||
|
||||
const $modelCmdkState = atom<ModelCmdkState>({ isOpen: false });
|
||||
|
||||
const openModelCmdk = (options: ModelCmdkOptions) => {
|
||||
$modelCmdkState.set({
|
||||
isOpen: true,
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
const closeModelCmdk = () => {
|
||||
$modelCmdkState.set({ isOpen: false });
|
||||
};
|
||||
|
||||
const getRegex = (searchTerm: string) =>
|
||||
new RegExp(
|
||||
searchTerm
|
||||
.trim()
|
||||
.replace(/[-[\]{}()*+!<=:?./\\^$|#,]/g, '')
|
||||
.split(' ')
|
||||
.join('.*'),
|
||||
'gi'
|
||||
);
|
||||
|
||||
const isMatch = (model: AnyModelConfig, searchTerm: string) => {
|
||||
const regex = getRegex(searchTerm);
|
||||
|
||||
if (
|
||||
model.name.includes(searchTerm) ||
|
||||
regex.test(model.name) ||
|
||||
model.base.includes(searchTerm) ||
|
||||
regex.test(model.base) ||
|
||||
model.type.includes(searchTerm) ||
|
||||
regex.test(model.type) ||
|
||||
(model.description ?? '').includes(searchTerm) ||
|
||||
regex.test(model.description ?? '') ||
|
||||
model.format.includes(searchTerm) ||
|
||||
regex.test(model.format)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
export const useModelCmdk = (options: ModelCmdkOptions) => {
|
||||
const onOpen = useCallback(() => {
|
||||
openModelCmdk(options);
|
||||
}, [options]);
|
||||
const onClose = useCallback(() => {
|
||||
closeModelCmdk();
|
||||
}, []);
|
||||
return {
|
||||
onOpen,
|
||||
onClose,
|
||||
};
|
||||
};
|
||||
|
||||
const cmdkRootSx: SystemStyleObject = {
|
||||
p: 2,
|
||||
h: 'full',
|
||||
'[cmdk-root]': {
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
},
|
||||
'[cmdk-list]': {
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
},
|
||||
};
|
||||
|
||||
export const ModelCmdk = memo(() => {
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const state = useStore($modelCmdkState);
|
||||
|
||||
const onSelect = useCallback(
|
||||
(model: AnyModelConfig) => {
|
||||
if (!state.isOpen) {
|
||||
// If the command menu is closed, we shouldn't do anything
|
||||
return;
|
||||
}
|
||||
state.onSelect(model);
|
||||
closeModelCmdk();
|
||||
},
|
||||
[state]
|
||||
);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={state.isOpen}
|
||||
onClose={closeModelCmdk}
|
||||
useInert={false}
|
||||
initialFocusRef={inputRef}
|
||||
size="xl"
|
||||
isCentered
|
||||
>
|
||||
<ModalOverlay />
|
||||
<ModalContent h="512" maxH="70%">
|
||||
<ModalBody sx={cmdkRootSx}>
|
||||
{state.isOpen && (
|
||||
<ModelCommandRoot inputRef={inputRef} modelConfigs={state.modelConfigs} onSelect={onSelect} />
|
||||
)}
|
||||
</ModalBody>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
});
|
||||
|
||||
ModelCmdk.displayName = 'ModelCmdk';
|
||||
|
||||
const ModelCommandRoot = memo(
|
||||
(props: {
|
||||
inputRef: RefObject<HTMLInputElement>;
|
||||
modelConfigs: AnyModelConfig[];
|
||||
onSelect: (model: AnyModelConfig) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [value, setValue] = useState('');
|
||||
const { inputRef, modelConfigs, onSelect } = props;
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
// Filtering the list is expensive - debounce the search term to avoid stutters
|
||||
const [debouncedSearchTerm] = useDebounce(searchTerm, 300);
|
||||
|
||||
const onChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||
setSearchTerm(e.target.value);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<CommandRoot loop shouldFilter={false} value={value} onValueChange={setValue}>
|
||||
<Flex flexDir="column" h="full" gap={2}>
|
||||
<Input ref={inputRef} value={searchTerm} onChange={onChange} placeholder={t('nodes.nodeSearch')} />
|
||||
<Box w="full" h="full">
|
||||
<ScrollableContent>
|
||||
<CommandEmpty>
|
||||
<IAINoContentFallback
|
||||
position="absolute"
|
||||
top={0}
|
||||
right={0}
|
||||
bottom={0}
|
||||
left={0}
|
||||
icon={null}
|
||||
label="No matching items"
|
||||
/>
|
||||
</CommandEmpty>
|
||||
<CommandList>
|
||||
<ModelList
|
||||
searchTerm={debouncedSearchTerm}
|
||||
onSelect={onSelect}
|
||||
modelConfigs={modelConfigs}
|
||||
setValue={setValue}
|
||||
/>
|
||||
</CommandList>
|
||||
</ScrollableContent>
|
||||
</Box>
|
||||
</Flex>
|
||||
</CommandRoot>
|
||||
);
|
||||
}
|
||||
);
|
||||
ModelCommandRoot.displayName = 'ModelCommandRoot';
|
||||
|
||||
const ModelList = memo(
|
||||
(props: {
|
||||
searchTerm: string;
|
||||
modelConfigs: AnyModelConfig[];
|
||||
onSelect: (model: AnyModelConfig) => void;
|
||||
setValue: (value: string) => void;
|
||||
}) => {
|
||||
const { searchTerm, modelConfigs, onSelect: _onSelect, setValue } = props;
|
||||
|
||||
const onSelect = useCallback(
|
||||
(key: string) => {
|
||||
const model = modelConfigs.find((model) => model.key === key);
|
||||
if (!model) {
|
||||
// Model not found? We should never get here.
|
||||
return;
|
||||
}
|
||||
_onSelect(model);
|
||||
},
|
||||
[_onSelect, modelConfigs]
|
||||
);
|
||||
const results = useMemo(() => {
|
||||
if (!searchTerm) {
|
||||
setValue(modelConfigs[0]?.key ?? '');
|
||||
return modelConfigs;
|
||||
}
|
||||
const results = modelConfigs.filter((model) => isMatch(model, searchTerm));
|
||||
setValue(results[0]?.key ?? '');
|
||||
return results;
|
||||
}, [modelConfigs, searchTerm, setValue]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{results.map((model) => (
|
||||
<ModelItem key={model.key} model={model} onSelect={onSelect} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
);
|
||||
ModelList.displayName = 'ModelList';
|
||||
|
||||
const cmdkItemSx: SystemStyleObject = {
|
||||
display: 'flex',
|
||||
flexDir: 'column',
|
||||
py: 1,
|
||||
px: 2,
|
||||
borderRadius: 'base',
|
||||
'&[data-selected="true"]': {
|
||||
bg: 'base.700',
|
||||
},
|
||||
'.model-header': {
|
||||
display: 'flex',
|
||||
gap: 2,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
'model-name': {
|
||||
fontSize: 'sm',
|
||||
},
|
||||
'model-base': {
|
||||
fontSize: 'sm',
|
||||
color: 'base.500',
|
||||
},
|
||||
'model-desc': {
|
||||
color: 'base.200',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const ChakraCommandItem = chakra(CommandItem);
|
||||
|
||||
const ModelItem = memo((props: { model: AnyModelConfig; onSelect: (key: string) => void }) => {
|
||||
const { model, onSelect } = props;
|
||||
return (
|
||||
<ChakraCommandItem value={model.key} onSelect={onSelect} role="button" sx={cmdkItemSx}>
|
||||
<Box className="model-header">
|
||||
<Text className="model-name">{model.name}</Text>
|
||||
<Text className="model-base">{model.base}</Text>
|
||||
</Box>
|
||||
{model.description && <Text className="model-desc">{model.description}</Text>}
|
||||
</ChakraCommandItem>
|
||||
);
|
||||
});
|
||||
ModelItem.displayName = 'ModelItem';
|
||||
@@ -0,0 +1,349 @@
|
||||
import type { SystemStyleObject } from '@invoke-ai/ui-library';
|
||||
import { Box, Flex, Input, Modal, ModalBody, ModalContent, ModalOverlay, Text } from '@invoke-ai/ui-library';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
|
||||
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
|
||||
import { atom } from 'nanostores';
|
||||
import type { ChangeEvent, RefObject } from 'react';
|
||||
import { memo, useCallback, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { AnyModelConfig } from 'services/api/types';
|
||||
|
||||
export type ModelComboboxOptions = {
|
||||
modelConfigs: AnyModelConfig[];
|
||||
onSelect: (modelConfig: AnyModelConfig) => void;
|
||||
onClose?: () => void;
|
||||
};
|
||||
|
||||
const $modelComboboxState = atom<ModelComboboxOptions | null>(null);
|
||||
|
||||
const openModelCombobox = (options: ModelComboboxOptions) => {
|
||||
$modelComboboxState.set(options);
|
||||
};
|
||||
|
||||
const closeModelCombobox = () => {
|
||||
$modelComboboxState.set(null);
|
||||
};
|
||||
|
||||
const getRegex = (searchTerm: string) =>
|
||||
new RegExp(
|
||||
searchTerm
|
||||
.trim()
|
||||
.replace(/[-[\]{}()*+!<=:?./\\^$|#,]/g, '')
|
||||
.split(' ')
|
||||
.join('.*'),
|
||||
'gi'
|
||||
);
|
||||
|
||||
const isMatch = (model: AnyModelConfig, searchTerm: string) => {
|
||||
const regex = getRegex(searchTerm);
|
||||
|
||||
if (
|
||||
model.name.includes(searchTerm) ||
|
||||
regex.test(model.name) ||
|
||||
model.base.includes(searchTerm) ||
|
||||
regex.test(model.base) ||
|
||||
model.type.includes(searchTerm) ||
|
||||
regex.test(model.type) ||
|
||||
(model.description ?? '').includes(searchTerm) ||
|
||||
regex.test(model.description ?? '') ||
|
||||
model.format.includes(searchTerm) ||
|
||||
regex.test(model.format)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
export const useModelCombobox = (options: ModelComboboxOptions) => {
|
||||
const onOpen = useCallback(() => {
|
||||
openModelCombobox(options);
|
||||
}, [options]);
|
||||
const onClose = useCallback(() => {
|
||||
closeModelCombobox();
|
||||
}, []);
|
||||
return {
|
||||
onOpen,
|
||||
onClose,
|
||||
};
|
||||
};
|
||||
|
||||
export const ModelCombobox = memo(() => {
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const state = useStore($modelComboboxState);
|
||||
|
||||
const onSelect = useCallback(
|
||||
(model: AnyModelConfig) => {
|
||||
if (!state) {
|
||||
// If the command menu is closed, we shouldn't do anything
|
||||
return;
|
||||
}
|
||||
state.onSelect(model);
|
||||
closeModelCombobox();
|
||||
},
|
||||
[state]
|
||||
);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={!!state}
|
||||
onClose={closeModelCombobox}
|
||||
useInert={false}
|
||||
initialFocusRef={inputRef}
|
||||
size="xl"
|
||||
isCentered
|
||||
>
|
||||
<ModalOverlay />
|
||||
<ModalContent h="512" maxH="70%">
|
||||
<ModalBody p={0}>
|
||||
{state && <ModelComboboxContent inputRef={inputRef} modelConfigs={state.modelConfigs} onSelect={onSelect} />}
|
||||
</ModalBody>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
});
|
||||
|
||||
ModelCombobox.displayName = 'ModelCombobox';
|
||||
|
||||
const ModelComboboxContent = memo(
|
||||
(props: {
|
||||
inputRef: RefObject<HTMLInputElement>;
|
||||
modelConfigs: AnyModelConfig[];
|
||||
onSelect: (model: AnyModelConfig) => void;
|
||||
}) => {
|
||||
const { inputRef, modelConfigs, onSelect: _onSelect } = props;
|
||||
const { t } = useTranslation();
|
||||
const [$value] = useState(() => atom(modelConfigs[0]?.key ?? ''));
|
||||
const value = useStore($value);
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
// const [value, setValue] = useState(modelConfigs[0]?.key ?? '');
|
||||
const [items, setItems] = useState<AnyModelConfig[]>(modelConfigs);
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
|
||||
const onChangeSearchTerm = useCallback(
|
||||
(e: ChangeEvent<HTMLInputElement>) => {
|
||||
setSearchTerm(e.target.value);
|
||||
if (!e.target.value) {
|
||||
setItems(modelConfigs);
|
||||
$value.set(modelConfigs[0]?.key ?? '');
|
||||
} else {
|
||||
const filtered = modelConfigs.filter((model) => isMatch(model, e.target.value));
|
||||
setItems(filtered);
|
||||
$value.set(filtered[0]?.key ?? '');
|
||||
}
|
||||
},
|
||||
[$value, modelConfigs]
|
||||
);
|
||||
|
||||
const onSelect = useCallback(
|
||||
(key: string) => {
|
||||
const model = modelConfigs.find((model) => model.key === key);
|
||||
if (!model) {
|
||||
// Model not found? We should never get here.
|
||||
return;
|
||||
}
|
||||
_onSelect(model);
|
||||
},
|
||||
[_onSelect, modelConfigs]
|
||||
);
|
||||
|
||||
const setValueAndScrollIntoView = useCallback(
|
||||
(key: string) => {
|
||||
$value.set(key);
|
||||
const rootEl = rootRef.current;
|
||||
if (!rootEl) {
|
||||
return;
|
||||
}
|
||||
const itemEl = rootEl.querySelector(`#${CSS.escape(key)}`);
|
||||
if (!itemEl) {
|
||||
return;
|
||||
}
|
||||
itemEl.scrollIntoView({ block: 'nearest' });
|
||||
},
|
||||
[$value]
|
||||
);
|
||||
|
||||
const prev = useCallback(
|
||||
(e: React.KeyboardEvent) => {
|
||||
e.preventDefault();
|
||||
const value = $value.get();
|
||||
if (items.length === 0) {
|
||||
return;
|
||||
}
|
||||
if (e.metaKey) {
|
||||
const item = items.at(0);
|
||||
if (item) {
|
||||
setValueAndScrollIntoView(item.key);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const currentIndex = items.findIndex((model) => model.key === value);
|
||||
if (currentIndex < 0) {
|
||||
return;
|
||||
}
|
||||
let newIndex = currentIndex - 1;
|
||||
if (newIndex < 0) {
|
||||
newIndex = items.length - 1;
|
||||
}
|
||||
const item = items.at(newIndex);
|
||||
if (item) {
|
||||
setValueAndScrollIntoView(item.key);
|
||||
}
|
||||
},
|
||||
[$value, items, setValueAndScrollIntoView]
|
||||
);
|
||||
|
||||
const next = useCallback(
|
||||
(e: React.KeyboardEvent) => {
|
||||
e.preventDefault();
|
||||
const value = $value.get();
|
||||
if (items.length === 0) {
|
||||
return;
|
||||
}
|
||||
if (e.metaKey) {
|
||||
const item = items.at(-1);
|
||||
if (item) {
|
||||
setValueAndScrollIntoView(item.key);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const currentIndex = items.findIndex((model) => model.key === value);
|
||||
if (currentIndex < 0) {
|
||||
return;
|
||||
}
|
||||
let newIndex = currentIndex + 1;
|
||||
if (newIndex >= items.length) {
|
||||
newIndex = 0;
|
||||
}
|
||||
const item = items.at(newIndex);
|
||||
if (item) {
|
||||
setValueAndScrollIntoView(item.key);
|
||||
}
|
||||
},
|
||||
[$value, items, setValueAndScrollIntoView]
|
||||
);
|
||||
|
||||
const onKeyDown = useCallback(
|
||||
(e: React.KeyboardEvent) => {
|
||||
if (e.key === 'ArrowUp') {
|
||||
prev(e);
|
||||
} else if (e.key === 'ArrowDown') {
|
||||
next(e);
|
||||
} else if (e.key === 'Enter') {
|
||||
const value = $value.get();
|
||||
const model = items.find((model) => model.key === value);
|
||||
if (!model) {
|
||||
// Model not found? We should never get here.
|
||||
return;
|
||||
}
|
||||
_onSelect(model);
|
||||
closeModelCombobox();
|
||||
} else if (e.key === 'Escape') {
|
||||
closeModelCombobox();
|
||||
} else if (e.key === '/') {
|
||||
e.preventDefault();
|
||||
inputRef.current?.focus();
|
||||
inputRef.current?.select();
|
||||
}
|
||||
},
|
||||
[$value, _onSelect, inputRef, items, next, prev]
|
||||
);
|
||||
|
||||
return (
|
||||
<Flex tabIndex={-1} ref={rootRef} flexDir="column" p={2} h="full" gap={2} onKeyDown={onKeyDown}>
|
||||
<Input ref={inputRef} value={searchTerm} onChange={onChangeSearchTerm} placeholder={t('nodes.nodeSearch')} />
|
||||
<Box tabIndex={-1} role="listbox" w="full" h="full">
|
||||
<ScrollableContent>
|
||||
{items.length === 0 && (
|
||||
<IAINoContentFallback
|
||||
position="absolute"
|
||||
top={0}
|
||||
right={0}
|
||||
bottom={0}
|
||||
left={0}
|
||||
icon={null}
|
||||
label="No matching items"
|
||||
/>
|
||||
)}
|
||||
{items.length > 0 &&
|
||||
items.map((model) => (
|
||||
<ModelComboboxItem
|
||||
key={model.key}
|
||||
model={model}
|
||||
setActive={$value.set}
|
||||
onSelect={onSelect}
|
||||
isSelected={model.key === value}
|
||||
isDisabled={false}
|
||||
/>
|
||||
))}
|
||||
</ScrollableContent>
|
||||
</Box>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
);
|
||||
ModelComboboxContent.displayName = 'ModelComboboxContent';
|
||||
|
||||
const itemSx: SystemStyleObject = {
|
||||
display: 'flex',
|
||||
flexDir: 'column',
|
||||
py: 1,
|
||||
px: 2,
|
||||
borderRadius: 'base',
|
||||
'&[data-selected="true"]': {
|
||||
bg: 'base.700',
|
||||
},
|
||||
};
|
||||
|
||||
const ModelComboboxItem = memo(
|
||||
(props: {
|
||||
model: AnyModelConfig;
|
||||
setActive: (key: string) => void;
|
||||
onSelect: (key: string) => void;
|
||||
isSelected: boolean;
|
||||
isDisabled: boolean;
|
||||
}) => {
|
||||
const { model, setActive, onSelect, isDisabled, isSelected } = props;
|
||||
const onPointerMove = useCallback(() => {
|
||||
setActive(model.key);
|
||||
}, [model.key, setActive]);
|
||||
const onClick = useCallback(() => {
|
||||
onSelect(model.key);
|
||||
}, [model.key, onSelect]);
|
||||
return (
|
||||
<Box
|
||||
role="option"
|
||||
sx={itemSx}
|
||||
id={model.key}
|
||||
aria-disabled={isDisabled}
|
||||
aria-selected={isSelected}
|
||||
data-disabled={isDisabled}
|
||||
data-selected={isSelected}
|
||||
onPointerMove={isDisabled ? undefined : onPointerMove}
|
||||
onClick={isDisabled ? undefined : onClick}
|
||||
>
|
||||
<ModelComboboxItemContent model={model} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
);
|
||||
ModelComboboxItem.displayName = 'ModelComboboxItem';
|
||||
|
||||
const ModelComboboxItemContent = memo(({ model }: { model: AnyModelConfig }) => {
|
||||
return (
|
||||
<>
|
||||
<Flex tabIndex={-1} gap={2} alignItems="center" justifyContent="space-between">
|
||||
<Text fontSize="sm" fontWeight="semibold">
|
||||
{model.name}
|
||||
</Text>
|
||||
<Text fontSize="sm" color="base.500">
|
||||
{model.base}
|
||||
</Text>
|
||||
</Flex>
|
||||
{model.description && <Text color="base.200">{model.description}</Text>}
|
||||
</>
|
||||
);
|
||||
});
|
||||
ModelComboboxItemContent.displayName = 'ModelComboboxItemContent';
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Box, Button, Flex } from '@invoke-ai/ui-library';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { useModelCmdk } from 'common/components/ModelCmdk/ModelCmdk';
|
||||
import { useModelCombobox } from 'common/components/ModelCombobox/ModelCombobox';
|
||||
import { overlayScrollbarsParams } from 'common/components/OverlayScrollbars/constants';
|
||||
import { selectIsCogView4, selectIsSDXL } from 'features/controlLayers/store/paramsSlice';
|
||||
import { Prompts } from 'features/parameters/components/Prompts/Prompts';
|
||||
@@ -13,23 +13,28 @@ import { RefinerSettingsAccordion } from 'features/settingsAccordions/components
|
||||
import { StylePresetMenu } from 'features/stylePresets/components/StylePresetMenu';
|
||||
import { StylePresetMenuTrigger } from 'features/stylePresets/components/StylePresetMenuTrigger';
|
||||
import { $isStylePresetsMenuOpen } from 'features/stylePresets/store/stylePresetSlice';
|
||||
import { noop } from 'lodash-es';
|
||||
import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
|
||||
import type { CSSProperties } from 'react';
|
||||
import { memo } from 'react';
|
||||
import { useControlNetModels } from 'services/api/hooks/modelsByType';
|
||||
import type { AnyModelConfig } from 'services/api/types';
|
||||
|
||||
const overlayScrollbarsStyles: CSSProperties = {
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
};
|
||||
|
||||
const onSelect = (modelConfig: AnyModelConfig) => {
|
||||
// Handle model selection
|
||||
console.log('Selected model:', modelConfig);
|
||||
};
|
||||
|
||||
const ParametersPanelTextToImage = () => {
|
||||
const isSDXL = useAppSelector(selectIsSDXL);
|
||||
const isCogview4 = useAppSelector(selectIsCogView4);
|
||||
const isStylePresetsMenuOpen = useStore($isStylePresetsMenuOpen);
|
||||
const [modelConfigs] = useControlNetModels();
|
||||
const modelCmdk = useModelCmdk({ onSelect: noop, modelConfigs });
|
||||
const modelCmdk = useModelCombobox({ onSelect, modelConfigs });
|
||||
|
||||
return (
|
||||
<Flex w="full" h="full" flexDir="column" gap={2}>
|
||||
|
||||
Reference in New Issue
Block a user