diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ImportQueue.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ImportQueue.tsx
index 3d80d11c29..804a728cbd 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ImportQueue.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ImportQueue.tsx
@@ -7,7 +7,7 @@ import { useCallback, useMemo } from 'react';
import { RiSparklingFill } from 'react-icons/ri';
import { useGetModelImportsQuery, usePruneModelImportsMutation } from 'services/api/endpoints/models';
-import { ImportQueueModel } from '../ImportQueueModel';
+import { ImportQueueModel } from './ImportQueueModel';
export const ImportQueue = () => {
const dispatch = useAppDispatch();
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ScanModels/ScanModels.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ScanModels/ScanModels.tsx
new file mode 100644
index 0000000000..02eb3b50ba
--- /dev/null
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ScanModels/ScanModels.tsx
@@ -0,0 +1,5 @@
+import { ScanModelsForm } from './ScanModelsForm';
+
+export const ScanModels = () => {
+ return ;
+};
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ScanModels/ScanModelsForm.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ScanModels/ScanModelsForm.tsx
new file mode 100644
index 0000000000..48b8bb0dda
--- /dev/null
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ScanModels/ScanModelsForm.tsx
@@ -0,0 +1,54 @@
+import { Flex, FormControl, FormLabel, Input, Button, FormErrorMessage, Divider } from '@invoke-ai/ui-library';
+import { ChangeEventHandler, useCallback, useState } from 'react';
+import { useLazyScanModelsQuery } from '../../../../../services/api/endpoints/models';
+import { useTranslation } from 'react-i18next';
+import { ScanModelsResults } from './ScanModelsResults';
+
+export const ScanModelsForm = () => {
+ const [scanPath, setScanPath] = useState('');
+ const [errorMessage, setErrorMessage] = useState('');
+ const [results, setResults] = useState();
+ const { t } = useTranslation();
+
+ const [_scanModels, { isLoading }] = useLazyScanModelsQuery();
+
+ const handleSubmitScan = useCallback(async () => {
+ _scanModels({ scan_path: scanPath })
+ .unwrap()
+ .then((result) => {
+ setResults(result);
+ })
+ .catch((error) => {
+ if (error) {
+ setErrorMessage(error.data.detail);
+ }
+ });
+ }, [scanPath]);
+
+ const handleSetScanPath: ChangeEventHandler = useCallback((e) => {
+ setScanPath(e.target.value);
+ setErrorMessage('');
+ }, []);
+
+ return (
+ <>
+
+
+
+
+ {t('common.folder')}
+
+
+
+
+
+ {!!errorMessage.length && {errorMessage}}
+
+
+
+ {results && }
+ >
+ );
+};
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ScanModels/ScanModelsResults.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ScanModels/ScanModelsResults.tsx
new file mode 100644
index 0000000000..9bccf8a0d7
--- /dev/null
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ScanModels/ScanModelsResults.tsx
@@ -0,0 +1,77 @@
+import {
+ Text,
+ Flex,
+ Heading,
+ IconButton,
+ Input,
+ InputGroup,
+ InputRightElement,
+ Divider,
+ Box,
+} from '@invoke-ai/ui-library';
+import { t } from 'i18next';
+import { ChangeEventHandler, useCallback, useMemo, useState } from 'react';
+import { PiXBold } from 'react-icons/pi';
+
+export const ScanModelsResults = ({ results }: { results: string[] }) => {
+ const [searchTerm, setSearchTerm] = useState('');
+ const [filteredResults, setFilteredResults] = useState(results);
+
+ const handleSearch: ChangeEventHandler = useCallback(
+ (e) => {
+ setSearchTerm(e.target.value);
+ setFilteredResults(
+ results.filter((result) => {
+ const modelName = result.split('\\').slice(-1)[0];
+ return modelName?.includes(e.target.value);
+ })
+ );
+ },
+ [results]
+ );
+
+ const clearSearch = useCallback(() => {
+ setSearchTerm('');
+ }, []);
+
+ return (
+ <>
+
+
+
+
+ Scan Results
+
+
+
+
+ {!!searchTerm?.length && (
+
+ }
+ onClick={clearSearch}
+ />
+
+ )}
+
+
+
+ {filteredResults.map((result) => (
+
+ {result.split('\\').slice(-1)[0]}
+ {result}
+
+ ))}
+
+ >
+ );
+};
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ImportModels.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ImportModels.tsx
index a0dd39de2e..88423758e6 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ImportModels.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ImportModels.tsx
@@ -2,6 +2,7 @@ import { Box, Divider, Heading, Tab, TabList, TabPanel, TabPanels, Tabs } from '
import { ImportQueue } from './AddModelPanel/ImportQueue';
import { SimpleImport } from './AddModelPanel/SimpleImport';
+import { ScanModels } from './AddModelPanel/ScanModels/ScanModels';
export const ImportModels = () => {
return (
@@ -9,7 +10,7 @@ export const ImportModels = () => {
Add Model
-
+
Simple
@@ -21,7 +22,9 @@ export const ImportModels = () => {
Advanced Import Placeholder
- Scan Models Placeholder
+
+
+
diff --git a/invokeai/frontend/web/src/services/api/endpoints/models.ts b/invokeai/frontend/web/src/services/api/endpoints/models.ts
index a1cdb43617..61f0975920 100644
--- a/invokeai/frontend/web/src/services/api/endpoints/models.ts
+++ b/invokeai/frontend/web/src/services/api/endpoints/models.ts
@@ -349,6 +349,7 @@ export const {
useMergeMainModelsMutation,
useSyncModelsMutation,
useScanModelsQuery,
+ useLazyScanModelsQuery,
useGetCheckpointConfigsQuery,
useGetModelImportsQuery,
useGetModelMetadataQuery,