feat(ui): support loading from file for string input generators

This commit is contained in:
psychedelicious
2025-01-19 10:43:55 +11:00
parent 180a67d11b
commit 4fdc6eec9d
6 changed files with 115 additions and 6 deletions

View File

@@ -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>
);

View File

@@ -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>
);

View File

@@ -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';

View File

@@ -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>
);

View File

@@ -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>
);

View File

@@ -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>
);