mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): Add fusejs to NodeSearch
This commit is contained in:
parent
ff891b1ff2
commit
3f334d9e5e
@ -54,6 +54,7 @@
|
|||||||
"dateformat": "^5.0.3",
|
"dateformat": "^5.0.3",
|
||||||
"formik": "^2.2.9",
|
"formik": "^2.2.9",
|
||||||
"framer-motion": "^9.0.4",
|
"framer-motion": "^9.0.4",
|
||||||
|
"fuse.js": "^6.6.2",
|
||||||
"i18next": "^22.4.10",
|
"i18next": "^22.4.10",
|
||||||
"i18next-browser-languagedetector": "^7.0.1",
|
"i18next-browser-languagedetector": "^7.0.1",
|
||||||
"i18next-http-backend": "^2.1.1",
|
"i18next-http-backend": "^2.1.1",
|
||||||
|
@ -5,6 +5,7 @@ import IAIInput from 'common/components/IAIInput';
|
|||||||
import { Panel } from 'reactflow';
|
import { Panel } from 'reactflow';
|
||||||
import { map } from 'lodash';
|
import { map } from 'lodash';
|
||||||
import {
|
import {
|
||||||
|
ChangeEvent,
|
||||||
FocusEvent,
|
FocusEvent,
|
||||||
KeyboardEvent,
|
KeyboardEvent,
|
||||||
memo,
|
memo,
|
||||||
@ -19,6 +20,8 @@ import { useBuildInvocation } from 'features/nodes/hooks/useBuildInvocation';
|
|||||||
import { makeToast } from 'features/system/hooks/useToastWatcher';
|
import { makeToast } from 'features/system/hooks/useToastWatcher';
|
||||||
import { addToast } from 'features/system/store/systemSlice';
|
import { addToast } from 'features/system/store/systemSlice';
|
||||||
import { nodeAdded } from '../../store/nodesSlice';
|
import { nodeAdded } from '../../store/nodesSlice';
|
||||||
|
import Fuse from 'fuse.js';
|
||||||
|
import { InvocationTemplate } from 'features/nodes/types/types';
|
||||||
|
|
||||||
interface NodeListItemProps {
|
interface NodeListItemProps {
|
||||||
title: string;
|
title: string;
|
||||||
@ -55,6 +58,9 @@ const NodeSearch = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const nodes = map(invocationTemplates);
|
const nodes = map(invocationTemplates);
|
||||||
|
const [filteredNodes, setFilteredNodes] = useState<
|
||||||
|
Fuse.FuseResult<InvocationTemplate>[]
|
||||||
|
>([]);
|
||||||
|
|
||||||
const buildInvocation = useBuildInvocation();
|
const buildInvocation = useBuildInvocation();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
@ -64,6 +70,21 @@ const NodeSearch = () => {
|
|||||||
const [focusedIndex, setFocusedIndex] = useState<number>(-1);
|
const [focusedIndex, setFocusedIndex] = useState<number>(-1);
|
||||||
const nodeSearchRef = useRef<HTMLDivElement>(null);
|
const nodeSearchRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
const fuseOptions = {
|
||||||
|
findAllMatches: true,
|
||||||
|
threshold: 0,
|
||||||
|
ignoreLocation: true,
|
||||||
|
keys: ['title', 'type', 'tags'],
|
||||||
|
};
|
||||||
|
|
||||||
|
const fuse = new Fuse(nodes, fuseOptions);
|
||||||
|
|
||||||
|
const findNode = (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setSearchText(e.target.value);
|
||||||
|
setFilteredNodes(fuse.search(e.target.value));
|
||||||
|
setShowNodeList(true);
|
||||||
|
};
|
||||||
|
|
||||||
const addNode = useCallback(
|
const addNode = useCallback(
|
||||||
(nodeType: AnyInvocationType) => {
|
(nodeType: AnyInvocationType) => {
|
||||||
const invocation = buildInvocation(nodeType);
|
const invocation = buildInvocation(nodeType);
|
||||||
@ -85,8 +106,24 @@ const NodeSearch = () => {
|
|||||||
const renderNodeList = () => {
|
const renderNodeList = () => {
|
||||||
const nodeListToRender: ReactNode[] = [];
|
const nodeListToRender: ReactNode[] = [];
|
||||||
|
|
||||||
nodes.forEach(({ title, description, type }, index) => {
|
if (searchText.length > 0) {
|
||||||
if (title.toLowerCase().includes(searchText)) {
|
filteredNodes.forEach(({ item }, index) => {
|
||||||
|
const { title, description, type } = item;
|
||||||
|
if (title.toLowerCase().includes(searchText)) {
|
||||||
|
nodeListToRender.push(
|
||||||
|
<NodeListItem
|
||||||
|
key={index}
|
||||||
|
title={title}
|
||||||
|
description={description}
|
||||||
|
type={type}
|
||||||
|
isSelected={focusedIndex === index}
|
||||||
|
addNode={addNode}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
nodes.forEach(({ title, description, type }, index) => {
|
||||||
nodeListToRender.push(
|
nodeListToRender.push(
|
||||||
<NodeListItem
|
<NodeListItem
|
||||||
key={index}
|
key={index}
|
||||||
@ -97,26 +134,11 @@ const NodeSearch = () => {
|
|||||||
addNode={addNode}
|
addNode={addNode}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else {
|
});
|
||||||
<NodeListItem
|
}
|
||||||
key={index}
|
|
||||||
title={title}
|
|
||||||
description={description}
|
|
||||||
type={type}
|
|
||||||
isSelected={focusedIndex === index}
|
|
||||||
addNode={addNode}
|
|
||||||
/>;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex flexDirection="column" background="base.900" borderRadius={6}>
|
||||||
flexDirection="column"
|
|
||||||
background="base.900"
|
|
||||||
borderRadius={6}
|
|
||||||
maxHeight={400}
|
|
||||||
overflowY="scroll"
|
|
||||||
>
|
|
||||||
{nodeListToRender}
|
{nodeListToRender}
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
@ -128,12 +150,21 @@ const NodeSearch = () => {
|
|||||||
|
|
||||||
if (key === 'ArrowDown') {
|
if (key === 'ArrowDown') {
|
||||||
setShowNodeList(true);
|
setShowNodeList(true);
|
||||||
nextIndex = (focusedIndex + 1) % nodes.length;
|
if (searchText.length > 0) {
|
||||||
|
nextIndex = (focusedIndex + 1) % filteredNodes.length;
|
||||||
|
} else {
|
||||||
|
nextIndex = (focusedIndex + 1) % nodes.length;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key === 'ArrowUp') {
|
if (key === 'ArrowUp') {
|
||||||
setShowNodeList(true);
|
setShowNodeList(true);
|
||||||
nextIndex = (focusedIndex + nodes.length - 1) % nodes.length;
|
if (searchText.length > 0) {
|
||||||
|
nextIndex =
|
||||||
|
(focusedIndex + filteredNodes.length - 1) % filteredNodes.length;
|
||||||
|
} else {
|
||||||
|
nextIndex = (focusedIndex + filteredNodes.length - 1) % nodes.length;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// # TODO Handle Blur
|
// # TODO Handle Blur
|
||||||
@ -141,7 +172,14 @@ const NodeSearch = () => {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
if (key === 'Enter') {
|
if (key === 'Enter') {
|
||||||
const selectedNodeType = nodes[focusedIndex].type;
|
let selectedNodeType: AnyInvocationType;
|
||||||
|
|
||||||
|
if (searchText.length > 0) {
|
||||||
|
selectedNodeType = filteredNodes[focusedIndex].item.type;
|
||||||
|
} else {
|
||||||
|
selectedNodeType = nodes[focusedIndex].type;
|
||||||
|
}
|
||||||
|
|
||||||
addNode(selectedNodeType);
|
addNode(selectedNodeType);
|
||||||
setShowNodeList(false);
|
setShowNodeList(false);
|
||||||
}
|
}
|
||||||
@ -163,13 +201,7 @@ const NodeSearch = () => {
|
|||||||
onBlur={searchInputBlurHandler}
|
onBlur={searchInputBlurHandler}
|
||||||
ref={nodeSearchRef}
|
ref={nodeSearchRef}
|
||||||
>
|
>
|
||||||
<IAIInput
|
<IAIInput value={searchText} onChange={findNode} />
|
||||||
value={searchText}
|
|
||||||
onChange={(e) => {
|
|
||||||
setSearchText(e.target.value);
|
|
||||||
setShowNodeList(true);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{showNodeList && renderNodeList()}
|
{showNodeList && renderNodeList()}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Panel>
|
</Panel>
|
||||||
|
@ -3454,6 +3454,11 @@ functions-have-names@^1.2.2:
|
|||||||
resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834"
|
resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834"
|
||||||
integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==
|
integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==
|
||||||
|
|
||||||
|
fuse.js@^6.6.2:
|
||||||
|
version "6.6.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-6.6.2.tgz#fe463fed4b98c0226ac3da2856a415576dc9a111"
|
||||||
|
integrity sha512-cJaJkxCCxC8qIIcPBF9yGxY0W/tVZS3uEISDxhYIdtk8OL93pe+6Zj7LjCqVV4dzbqcriOZ+kQ/NE4RXZHsIGA==
|
||||||
|
|
||||||
get-amd-module-type@^3.0.0:
|
get-amd-module-type@^3.0.0:
|
||||||
version "3.0.2"
|
version "3.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/get-amd-module-type/-/get-amd-module-type-3.0.2.tgz#46550cee2b8e1fa4c3f2c8a5753c36990aa49ab0"
|
resolved "https://registry.yarnpkg.com/get-amd-module-type/-/get-amd-module-type-3.0.2.tgz#46550cee2b8e1fa4c3f2c8a5753c36990aa49ab0"
|
||||||
|
Loading…
Reference in New Issue
Block a user