mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-04-23 03:00:31 -04:00
feat(ui): support loading from file for string input generators
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { Flex, FormControl, FormLabel, Input, Textarea } from '@invoke-ai/ui-library';
|
||||
import { LoadTextFromFileIconButton } from 'features/nodes/components/flow/nodes/Invocation/fields/inputs/LoadTextFromFileIconButton';
|
||||
import type { FloatGeneratorParseString } from 'features/nodes/types/field';
|
||||
import type { ChangeEvent } from 'react';
|
||||
import { memo, useCallback } from 'react';
|
||||
@@ -25,13 +26,20 @@ export const FloatGeneratorParseStringSettings = memo(({ state, onChange }: Floa
|
||||
[onChange, state]
|
||||
);
|
||||
|
||||
const onLoadFile = useCallback(
|
||||
(value: string) => {
|
||||
onChange({ ...state, input: value });
|
||||
},
|
||||
[onChange, state]
|
||||
);
|
||||
|
||||
return (
|
||||
<Flex gap={2} flexDir="column">
|
||||
<FormControl orientation="vertical">
|
||||
<FormLabel>{t('nodes.splitOn')}</FormLabel>
|
||||
<Input value={state.splitOn} onChange={onChangeSplitOn} />
|
||||
</FormControl>
|
||||
<FormControl orientation="vertical">
|
||||
<FormControl orientation="vertical" position="relative">
|
||||
<FormLabel>{t('common.input')}</FormLabel>
|
||||
<Textarea
|
||||
className="nowheel nodrag nopan"
|
||||
@@ -42,6 +50,7 @@ export const FloatGeneratorParseStringSettings = memo(({ state, onChange }: Floa
|
||||
rows={5}
|
||||
fontSize='sm'
|
||||
/>
|
||||
<LoadTextFromFileIconButton position="absolute" top={10} right={2} onLoadFile={onLoadFile} />
|
||||
</FormControl>
|
||||
</Flex>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Flex, FormControl, FormLabel, Input, Textarea } from '@invoke-ai/ui-library';
|
||||
import { LoadTextFromFileIconButton } from 'features/nodes/components/flow/nodes/Invocation/fields/inputs/LoadTextFromFileIconButton';
|
||||
import type { IntegerGeneratorParseString } from 'features/nodes/types/field';
|
||||
import type { ChangeEvent } from 'react';
|
||||
import { memo, useCallback } from 'react';
|
||||
@@ -26,13 +27,20 @@ export const IntegerGeneratorParseStringSettings = memo(
|
||||
[onChange, state]
|
||||
);
|
||||
|
||||
const onLoadFile = useCallback(
|
||||
(value: string) => {
|
||||
onChange({ ...state, input: value });
|
||||
},
|
||||
[onChange, state]
|
||||
);
|
||||
|
||||
return (
|
||||
<Flex gap={2} flexDir="column">
|
||||
<FormControl orientation="vertical">
|
||||
<FormLabel>{t('nodes.splitOn')}</FormLabel>
|
||||
<Input value={state.splitOn} onChange={onChangeSplitOn} />
|
||||
</FormControl>
|
||||
<FormControl orientation="vertical">
|
||||
<FormControl orientation="vertical" position="relative">
|
||||
<FormLabel>{t('common.input')}</FormLabel>
|
||||
<Textarea
|
||||
className="nowheel nodrag nopan"
|
||||
@@ -43,6 +51,7 @@ export const IntegerGeneratorParseStringSettings = memo(
|
||||
rows={5}
|
||||
fontSize="sm"
|
||||
/>
|
||||
<LoadTextFromFileIconButton position="absolute" top={10} right={2} onLoadFile={onLoadFile} />
|
||||
</FormControl>
|
||||
</Flex>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import { IconButton, type IconButtonProps } from '@invoke-ai/ui-library';
|
||||
import { toast } from 'features/toast/toast';
|
||||
import { isString } from 'lodash-es';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useDropzone } from 'react-dropzone';
|
||||
import { PiUploadFill } from 'react-icons/pi';
|
||||
|
||||
type Props = Omit<IconButtonProps, 'aria-label'> & {
|
||||
onLoadFile: (value: string) => void;
|
||||
maxSize?: number;
|
||||
};
|
||||
|
||||
const DEFAULT_MAX_SIZE = 1024 * 128; // 128KB, we don't want to load huge files into node values...
|
||||
|
||||
export const LoadTextFromFileIconButton = memo(({ onLoadFile, maxSize = DEFAULT_MAX_SIZE, ...rest }: Props) => {
|
||||
const onDropAccepted = useCallback(
|
||||
(files: File[]) => {
|
||||
const file = files[0];
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
const result = reader.result;
|
||||
if (!isString(result)) {
|
||||
return;
|
||||
}
|
||||
onLoadFile(result);
|
||||
};
|
||||
reader.onerror = () => {
|
||||
toast({
|
||||
title: 'Failed to load file',
|
||||
status: 'error',
|
||||
});
|
||||
};
|
||||
reader.readAsText(file);
|
||||
},
|
||||
[onLoadFile]
|
||||
);
|
||||
|
||||
const { getInputProps, getRootProps } = useDropzone({
|
||||
accept: { 'text/csv': ['.csv'], 'text/plain': ['.txt'] },
|
||||
maxSize,
|
||||
onDropAccepted,
|
||||
noDrag: true,
|
||||
multiple: false,
|
||||
});
|
||||
return (
|
||||
<>
|
||||
<IconButton
|
||||
tooltip="Load from file"
|
||||
aria-label="Load from file"
|
||||
icon={<PiUploadFill />}
|
||||
variant="link"
|
||||
boxSize={8}
|
||||
minW={8}
|
||||
{...rest}
|
||||
{...getRootProps()}
|
||||
/>
|
||||
<input {...getInputProps()} />
|
||||
</>
|
||||
);
|
||||
});
|
||||
LoadTextFromFileIconButton.displayName = 'LoadTextFromFileIconButton';
|
||||
@@ -1,4 +1,5 @@
|
||||
import { CompositeNumberInput, Flex, FormControl, FormLabel, Textarea } from '@invoke-ai/ui-library';
|
||||
import { LoadTextFromFileIconButton } from 'features/nodes/components/flow/nodes/Invocation/fields/inputs/LoadTextFromFileIconButton';
|
||||
import type { StringGeneratorDynamicPromptsCombinatorial } from 'features/nodes/types/field';
|
||||
import type { ChangeEvent } from 'react';
|
||||
import { memo, useCallback, useEffect, useMemo } from 'react';
|
||||
@@ -46,13 +47,20 @@ export const StringGeneratorDynamicPromptsCombinatorialSettings = memo(
|
||||
}
|
||||
}, [data, isLoading, loadingValues, onChange, state]);
|
||||
|
||||
const onLoadFile = useCallback(
|
||||
(value: string) => {
|
||||
onChange({ ...state, input: value });
|
||||
},
|
||||
[onChange, state]
|
||||
);
|
||||
|
||||
return (
|
||||
<Flex gap={2} flexDir="column">
|
||||
<FormControl orientation="vertical">
|
||||
<FormLabel>{t('dynamicPrompts.maxPrompts')}</FormLabel>
|
||||
<CompositeNumberInput value={state.maxPrompts} onChange={onChangeMaxPrompts} min={1} max={1000} w="full"/>
|
||||
<CompositeNumberInput value={state.maxPrompts} onChange={onChangeMaxPrompts} min={1} max={1000} w="full" />
|
||||
</FormControl>
|
||||
<FormControl orientation="vertical">
|
||||
<FormControl orientation="vertical" position="relative">
|
||||
<FormLabel>{t('common.input')}</FormLabel>
|
||||
<Textarea
|
||||
className="nowheel nodrag nopan"
|
||||
@@ -63,6 +71,7 @@ export const StringGeneratorDynamicPromptsCombinatorialSettings = memo(
|
||||
rows={5}
|
||||
fontSize="sm"
|
||||
/>
|
||||
<LoadTextFromFileIconButton position="absolute" top={10} right={2} onLoadFile={onLoadFile} />
|
||||
</FormControl>
|
||||
</Flex>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Checkbox, CompositeNumberInput, Flex, FormControl, FormLabel, Textarea } from '@invoke-ai/ui-library';
|
||||
import { LoadTextFromFileIconButton } from 'features/nodes/components/flow/nodes/Invocation/fields/inputs/LoadTextFromFileIconButton';
|
||||
import type { StringGeneratorDynamicPromptsRandom } from 'features/nodes/types/field';
|
||||
import { isNil, random } from 'lodash-es';
|
||||
import type { ChangeEvent } from 'react';
|
||||
@@ -38,6 +39,13 @@ export const StringGeneratorDynamicPromptsRandomSettings = memo(
|
||||
[onChange, state, loadingValues]
|
||||
);
|
||||
|
||||
const onLoadFile = useCallback(
|
||||
(value: string) => {
|
||||
onChange({ ...state, input: value });
|
||||
},
|
||||
[onChange, state]
|
||||
);
|
||||
|
||||
const arg = useMemo(() => {
|
||||
const { input, count, seed } = state;
|
||||
return { prompt: input, max_prompts: count, combinatorial: false, seed: seed ?? random() };
|
||||
@@ -79,7 +87,7 @@ export const StringGeneratorDynamicPromptsRandomSettings = memo(
|
||||
<CompositeNumberInput value={state.count} onChange={onChangeCount} min={1} max={1000} />
|
||||
</FormControl>
|
||||
</Flex>
|
||||
<FormControl orientation="vertical">
|
||||
<FormControl orientation="vertical" position="relative">
|
||||
<FormLabel>{t('common.input')}</FormLabel>
|
||||
<Textarea
|
||||
className="nowheel nodrag nopan"
|
||||
@@ -90,6 +98,7 @@ export const StringGeneratorDynamicPromptsRandomSettings = memo(
|
||||
rows={5}
|
||||
fontSize="sm"
|
||||
/>
|
||||
<LoadTextFromFileIconButton position="absolute" top={10} right={2} onLoadFile={onLoadFile} />
|
||||
</FormControl>
|
||||
</Flex>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Flex, FormControl, FormLabel, Input, Textarea } from '@invoke-ai/ui-library';
|
||||
import { LoadTextFromFileIconButton } from 'features/nodes/components/flow/nodes/Invocation/fields/inputs/LoadTextFromFileIconButton';
|
||||
import type { StringGeneratorParseString } from 'features/nodes/types/field';
|
||||
import type { ChangeEvent } from 'react';
|
||||
import { memo, useCallback } from 'react';
|
||||
@@ -26,13 +27,20 @@ export const StringGeneratorParseStringSettings = memo(
|
||||
[onChange, state]
|
||||
);
|
||||
|
||||
const onLoadFile = useCallback(
|
||||
(value: string) => {
|
||||
onChange({ ...state, input: value });
|
||||
},
|
||||
[onChange, state]
|
||||
);
|
||||
|
||||
return (
|
||||
<Flex gap={2} flexDir="column">
|
||||
<FormControl orientation="vertical">
|
||||
<FormLabel>{t('nodes.splitOn')}</FormLabel>
|
||||
<Input value={state.splitOn} onChange={onChangeSplitOn} />
|
||||
</FormControl>
|
||||
<FormControl orientation="vertical">
|
||||
<FormControl position="relative" orientation="vertical">
|
||||
<FormLabel>{t('common.input')}</FormLabel>
|
||||
<Textarea
|
||||
className="nowheel nodrag nopan"
|
||||
@@ -43,6 +51,7 @@ export const StringGeneratorParseStringSettings = memo(
|
||||
rows={5}
|
||||
fontSize="sm"
|
||||
/>
|
||||
<LoadTextFromFileIconButton position="absolute" top={10} right={2} onLoadFile={onLoadFile} />
|
||||
</FormControl>
|
||||
</Flex>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user