mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat: add flag for allowPrivateStylePresets that shows a type field when creating a style preset
This commit is contained in:
parent
e5f7c2a9b7
commit
e0d3927265
@ -1,6 +1,7 @@
|
||||
[
|
||||
{
|
||||
"name": "Photography (General)",
|
||||
"type": "default",
|
||||
"preset_data": {
|
||||
"positive_prompt": "[prompt]. photography. f/2.8 macro photo, bokeh, photorealism",
|
||||
"negative_prompt": "painting, digital art. sketch, blurry"
|
||||
@ -8,6 +9,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Photography (Studio Lighting)",
|
||||
"type": "default",
|
||||
"preset_data": {
|
||||
"positive_prompt": "[prompt], photography. f/8 photo. centered subject, studio lighting.",
|
||||
"negative_prompt": "painting, digital art. sketch, blurry"
|
||||
@ -15,6 +17,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Photography (Landscape)",
|
||||
"type": "default",
|
||||
"preset_data": {
|
||||
"positive_prompt": "[prompt], landscape photograph, f/12, lifelike, highly detailed.",
|
||||
"negative_prompt": "painting, digital art. sketch, blurry"
|
||||
@ -22,6 +25,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Photography (Portrait)",
|
||||
"type": "default",
|
||||
"preset_data": {
|
||||
"positive_prompt": "[prompt]. photography. portraiture. catch light in eyes. one flash. rembrandt lighting. Soft box. dark shadows. High contrast. 80mm lens. F2.8.",
|
||||
"negative_prompt": "painting, digital art. sketch, blurry"
|
||||
@ -29,6 +33,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Photography (Black and White)",
|
||||
"type": "default",
|
||||
"preset_data": {
|
||||
"positive_prompt": "[prompt] photography. natural light. 80mm lens. F1.4. strong contrast, hard light. dark contrast. blurred background. black and white",
|
||||
"negative_prompt": "painting, digital art. sketch, colour+"
|
||||
@ -36,6 +41,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Architectural Visualization",
|
||||
"type": "default",
|
||||
"preset_data": {
|
||||
"positive_prompt": "[prompt]. architectural photography, f/12, luxury, aesthetically pleasing form and function.",
|
||||
"negative_prompt": "painting, digital art. sketch, blurry"
|
||||
@ -43,6 +49,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Concept Art (Fantasy)",
|
||||
"type": "default",
|
||||
"preset_data": {
|
||||
"positive_prompt": "concept artwork of a [prompt]. (digital painterly art style)++, mythological, (textured 2d dry media brushpack)++, glazed brushstrokes, otherworldly. painting+, illustration+, ",
|
||||
"negative_prompt": "photo. distorted, blurry, out of focus. sketch. (cgi, 3d.)++"
|
||||
@ -50,6 +57,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Concept Art (Sci-Fi)",
|
||||
"type": "default",
|
||||
"preset_data": {
|
||||
"positive_prompt": "(concept art)++, [prompt], (sleek futurism)++, (textured 2d dry media)++, metallic highlights, digital painting style",
|
||||
"negative_prompt": "photo. distorted, blurry, out of focus. sketch. (cgi, 3d.)++"
|
||||
@ -57,6 +65,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Concept Art (Character)",
|
||||
"type": "default",
|
||||
"preset_data": {
|
||||
"positive_prompt": "(character concept art)++, stylized painterly digital painting of [prompt], (painterly, impasto. Dry brush.)++",
|
||||
"negative_prompt": "photo. distorted, blurry, out of focus. sketch. (cgi, 3d.)++"
|
||||
@ -64,6 +73,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Concept Art (Painterly)",
|
||||
"type": "default",
|
||||
"preset_data": {
|
||||
"positive_prompt": "[prompt] Oil painting. high contrast. impasto. sfumato. chiaroscuro. Palette knife.",
|
||||
"negative_prompt": "photo. smooth. border. frame"
|
||||
@ -71,6 +81,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Environment Art",
|
||||
"type": "default",
|
||||
"preset_data": {
|
||||
"positive_prompt": "[prompt] environment artwork, hyper-realistic digital painting style with cinematic composition, atmospheric, depth and detail, voluminous. textured dry brush 2d media",
|
||||
"negative_prompt": "photo, distorted, blurry, out of focus. sketch. "
|
||||
@ -78,6 +89,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Interior Design (Visualization)",
|
||||
"type": "default",
|
||||
"preset_data": {
|
||||
"positive_prompt": "[prompt] interior design photo, gentle shadows, light mid-tones, dimension, mix of smooth and textured surfaces, focus on negative space and clean lines, focus",
|
||||
"negative_prompt": "photo, distorted. sketch."
|
||||
@ -85,6 +97,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Product Rendering",
|
||||
"type": "default",
|
||||
"preset_data": {
|
||||
"positive_prompt": "[prompt] high quality product photography, 3d rendering with key lighting, shallow depth of field, simple plain background, studio lighting.",
|
||||
"negative_prompt": "blurry, sketch, messy, dirty. unfinished."
|
||||
@ -92,6 +105,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Sketch",
|
||||
"type": "default",
|
||||
"preset_data": {
|
||||
"positive_prompt": "[prompt] black and white pencil drawing, off-center composition, cross-hatching for shadows, bold strokes, textured paper. sketch+++",
|
||||
"negative_prompt": "blurry, photo, painting, color. messy, dirty. unfinished. frame, borders."
|
||||
@ -99,6 +113,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Line Art",
|
||||
"type": "default",
|
||||
"preset_data": {
|
||||
"positive_prompt": "[prompt] Line art. bold outline. simplistic. white background. 2d",
|
||||
"negative_prompt": "photo. digital art. greyscale. solid black. painting "
|
||||
@ -106,6 +121,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Anime",
|
||||
"type": "default",
|
||||
"preset_data": {
|
||||
"positive_prompt": "[prompt] anime++, bold outline, cel-shaded coloring, shounen, seinen",
|
||||
"negative_prompt": "(photo)+++. greyscale. solid black. painting "
|
||||
@ -113,6 +129,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Illustration",
|
||||
"type": "default",
|
||||
"preset_data": {
|
||||
"positive_prompt": "[prompt] illustration, bold linework, illustrative details, vector art style, flat coloring,",
|
||||
"negative_prompt": "(photo)+++. greyscale. painting, black and white. "
|
||||
@ -120,6 +137,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Vehicles",
|
||||
"type": "default",
|
||||
"preset_data": {
|
||||
"positive_prompt": "A weird futuristic normal auto, [prompt] elegant design , nice color , nice wheels",
|
||||
"negative_prompt": "sketch. digital art. greyscale. painting "
|
||||
|
@ -32,7 +32,7 @@ class StylePresetChanges(BaseModel, extra="forbid"):
|
||||
class StylePresetWithoutId(BaseModel):
|
||||
name: str = Field(description="The name of the style preset.")
|
||||
preset_data: PresetData = Field(description="The preset data")
|
||||
type: PresetType = Field(description="The type of style preset", default=PresetType.User)
|
||||
type: PresetType = Field(description="The type of style preset")
|
||||
|
||||
|
||||
class StylePresetRecordDTO(StylePresetWithoutId):
|
||||
|
@ -5,7 +5,6 @@ from invokeai.app.services.invoker import Invoker
|
||||
from invokeai.app.services.shared.sqlite.sqlite_database import SqliteDatabase
|
||||
from invokeai.app.services.style_preset_records.style_preset_records_base import StylePresetRecordsStorageBase
|
||||
from invokeai.app.services.style_preset_records.style_preset_records_common import (
|
||||
PresetType,
|
||||
StylePresetChanges,
|
||||
StylePresetNotFoundError,
|
||||
StylePresetRecordDTO,
|
||||
@ -136,7 +135,7 @@ class SqliteStylePresetRecordsStorage(StylePresetRecordsStorageBase):
|
||||
SELECT
|
||||
*
|
||||
FROM style_presets
|
||||
ORDER BY name ASC
|
||||
ORDER BY LOWER(name) ASC
|
||||
"""
|
||||
|
||||
self._cursor.execute(main_query)
|
||||
@ -173,5 +172,4 @@ class SqliteStylePresetRecordsStorage(StylePresetRecordsStorageBase):
|
||||
presets = json.load(file)
|
||||
for preset in presets:
|
||||
style_preset = StylePresetWithoutId.model_validate(preset)
|
||||
style_preset.type = PresetType.Default
|
||||
self.create(style_preset)
|
||||
|
@ -1711,9 +1711,13 @@
|
||||
"promptTemplatesDesc3": "If you omit the placeholder, the template will be appended to the end of your prompt.",
|
||||
"positivePrompt": "Positive Prompt",
|
||||
"preview": "Preview",
|
||||
"private": "Private",
|
||||
"searchByName": "Search by name",
|
||||
"shared": "Shared",
|
||||
"sharedTemplates": "Shared Templates",
|
||||
"templateDeleted": "Prompt template deleted",
|
||||
"toggleViewMode": "Toggle View Mode",
|
||||
"type": "Type",
|
||||
"unableToDeleteTemplate": "Unable to delete prompt template",
|
||||
"updatePromptTemplate": "Update Prompt Template",
|
||||
"uploadImage": "Upload Image",
|
||||
|
@ -13,7 +13,7 @@ import ChangeBoardModal from 'features/changeBoardModal/components/ChangeBoardMo
|
||||
import DeleteImageModal from 'features/deleteImageModal/components/DeleteImageModal';
|
||||
import { DynamicPromptsModal } from 'features/dynamicPrompts/components/DynamicPromptsPreviewModal';
|
||||
import { useStarterModelsToast } from 'features/modelManagerV2/hooks/useStarterModelsToast';
|
||||
import { StylePresetModal } from 'features/stylePresets/components/StylePresetModal';
|
||||
import { StylePresetModal } from 'features/stylePresets/components/StylePresetForm/StylePresetModal';
|
||||
import { configChanged } from 'features/system/store/configSlice';
|
||||
import { languageSelector } from 'features/system/store/systemSelectors';
|
||||
import InvokeTabs from 'features/ui/components/InvokeTabs';
|
||||
|
@ -71,6 +71,7 @@ export type AppConfig = {
|
||||
*/
|
||||
maxUpscaleDimension?: number;
|
||||
allowPrivateBoards: boolean;
|
||||
allowPrivateStylePresets: boolean;
|
||||
disabledTabs: InvokeTabName[];
|
||||
disabledFeatures: AppFeature[];
|
||||
disabledSDFeatures: SDFeature[];
|
||||
|
@ -76,6 +76,7 @@ export const useImageActions = (image_name?: string) => {
|
||||
positivePrompt,
|
||||
negativePrompt,
|
||||
imageUrl: imageDTO.image_url,
|
||||
type: 'user',
|
||||
},
|
||||
updatingStylePresetId: null,
|
||||
isModalOpen: true,
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { Box, Button, Flex, FormControl, FormLabel, Input, Text } from '@invoke-ai/ui-library';
|
||||
import { Box, Button, Flex, FormControl, FormLabel, Input, Spacer, Text } from '@invoke-ai/ui-library';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { PRESET_PLACEHOLDER } from 'features/stylePresets/hooks/usePresetModifiedPrompts';
|
||||
import { $stylePresetModalState } from 'features/stylePresets/store/stylePresetModal';
|
||||
import { toast } from 'features/toast/toast';
|
||||
@ -12,12 +13,14 @@ import { useCreateStylePresetMutation, useUpdateStylePresetMutation } from 'serv
|
||||
|
||||
import { StylePresetImageField } from './StylePresetImageField';
|
||||
import { StylePresetPromptField } from './StylePresetPromptField';
|
||||
import { StylePresetTypeField } from './StylePresetTypeField';
|
||||
|
||||
export type StylePresetFormData = {
|
||||
name: string;
|
||||
positivePrompt: string;
|
||||
negativePrompt: string;
|
||||
image: File | null;
|
||||
type: PresetType;
|
||||
};
|
||||
|
||||
export const StylePresetForm = ({
|
||||
@ -30,6 +33,7 @@ export const StylePresetForm = ({
|
||||
const [createStylePreset] = useCreateStylePresetMutation();
|
||||
const [updateStylePreset] = useUpdateStylePresetMutation();
|
||||
const { t } = useTranslation();
|
||||
const allowPrivateStylePresets = useAppSelector((s) => s.config.allowPrivateStylePresets);
|
||||
|
||||
const { handleSubmit, control, register, formState } = useForm<StylePresetFormData>({
|
||||
defaultValues: formData || {
|
||||
@ -37,6 +41,7 @@ export const StylePresetForm = ({
|
||||
positivePrompt: '',
|
||||
negativePrompt: '',
|
||||
image: null,
|
||||
type: 'user',
|
||||
},
|
||||
mode: 'onChange',
|
||||
});
|
||||
@ -48,7 +53,7 @@ export const StylePresetForm = ({
|
||||
name: data.name,
|
||||
positive_prompt: data.positivePrompt,
|
||||
negative_prompt: data.negativePrompt,
|
||||
type: 'user' as PresetType,
|
||||
type: data.type,
|
||||
},
|
||||
image: data.image,
|
||||
};
|
||||
@ -102,7 +107,8 @@ export const StylePresetForm = ({
|
||||
<Text variant="subtext">{t('stylePresets.promptTemplatesDesc3')}</Text>
|
||||
</Box>
|
||||
|
||||
<Flex justifyContent="flex-end">
|
||||
<Flex justifyContent="space-between" alignItems="flex-end" gap={10}>
|
||||
{allowPrivateStylePresets ? <StylePresetTypeField control={control} name="type" /> : <Spacer />}
|
||||
<Button onClick={handleSubmit(handleClickSave)} isDisabled={!formState.isValid}>
|
||||
{t('common.save')}
|
||||
</Button>
|
@ -0,0 +1,46 @@
|
||||
import type { ComboboxOnChange } from '@invoke-ai/ui-library';
|
||||
import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { $stylePresetModalState } from 'features/stylePresets/store/stylePresetModal';
|
||||
import { t } from 'i18next';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import type { UseControllerProps } from 'react-hook-form';
|
||||
import { useController } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import type { StylePresetFormData } from './StylePresetForm';
|
||||
|
||||
const OPTIONS = [
|
||||
{ label: t('stylePresets.private'), value: 'user' },
|
||||
{ label: t('stylePresets.shared'), value: 'project' },
|
||||
];
|
||||
|
||||
export const StylePresetTypeField = (props: UseControllerProps<StylePresetFormData, 'type'>) => {
|
||||
const { field } = useController(props);
|
||||
const stylePresetModalState = useStore($stylePresetModalState);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const onChange = useCallback<ComboboxOnChange>(
|
||||
(v) => {
|
||||
if (v) {
|
||||
field.onChange(v.value);
|
||||
}
|
||||
},
|
||||
[field]
|
||||
);
|
||||
|
||||
const value = useMemo(() => {
|
||||
return OPTIONS.find((opt) => opt.value === field.value);
|
||||
}, [field.value]);
|
||||
|
||||
return (
|
||||
<FormControl
|
||||
orientation="vertical"
|
||||
maxW={48}
|
||||
isDisabled={stylePresetModalState.prefilledFormData?.type === 'project'}
|
||||
>
|
||||
<FormLabel>{t('stylePresets.type')}</FormLabel>
|
||||
<Combobox value={value} options={OPTIONS} onChange={onChange} />
|
||||
</FormControl>
|
||||
);
|
||||
};
|
@ -32,6 +32,7 @@ export const StylePresetListItem = ({ preset }: { preset: StylePresetRecordWithI
|
||||
positivePrompt: positive_prompt || '',
|
||||
negativePrompt: negative_prompt || '',
|
||||
imageUrl: preset.image,
|
||||
type: preset.type,
|
||||
},
|
||||
updatingStylePresetId: preset.id,
|
||||
isModalOpen: true,
|
||||
@ -65,6 +66,7 @@ export const StylePresetListItem = ({ preset }: { preset: StylePresetRecordWithI
|
||||
positivePrompt: positive_prompt || '',
|
||||
negativePrompt: negative_prompt || '',
|
||||
imageUrl: preset.image,
|
||||
type: 'user',
|
||||
},
|
||||
updatingStylePresetId: null,
|
||||
isModalOpen: true,
|
||||
|
@ -13,21 +13,31 @@ import StylePresetSearch from './StylePresetSearch';
|
||||
|
||||
export const StylePresetMenu = () => {
|
||||
const searchTerm = useAppSelector((s) => s.stylePreset.searchTerm);
|
||||
const allowPrivateStylePresets = useAppSelector((s) => s.config.allowPrivateStylePresets);
|
||||
const { data } = useListStylePresetsQuery(undefined, {
|
||||
selectFromResult: ({ data }) => {
|
||||
const filteredData =
|
||||
data?.filter((preset) => preset.name.toLowerCase().includes(searchTerm.toLowerCase())) || EMPTY_ARRAY;
|
||||
|
||||
const groupedData = filteredData.reduce(
|
||||
(acc: { defaultPresets: StylePresetRecordWithImage[]; presets: StylePresetRecordWithImage[] }, preset) => {
|
||||
(
|
||||
acc: {
|
||||
defaultPresets: StylePresetRecordWithImage[];
|
||||
sharedPresets: StylePresetRecordWithImage[];
|
||||
presets: StylePresetRecordWithImage[];
|
||||
},
|
||||
preset
|
||||
) => {
|
||||
if (preset.type === 'default') {
|
||||
acc.defaultPresets.push(preset);
|
||||
} else if (preset.type === 'project') {
|
||||
acc.sharedPresets.push(preset);
|
||||
} else {
|
||||
acc.presets.push(preset);
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{ defaultPresets: [], presets: [] }
|
||||
{ defaultPresets: [], sharedPresets: [], presets: [] }
|
||||
);
|
||||
|
||||
return {
|
||||
@ -70,6 +80,10 @@ export const StylePresetMenu = () => {
|
||||
|
||||
<StylePresetList title={t('stylePresets.myTemplates')} data={data.presets} />
|
||||
|
||||
{allowPrivateStylePresets && (
|
||||
<StylePresetList title={t('stylePresets.sharedTemplates')} data={data.sharedPresets} />
|
||||
)}
|
||||
|
||||
<StylePresetList title={t('stylePresets.defaultTemplates')} data={data.defaultPresets} />
|
||||
</Flex>
|
||||
);
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { atom } from 'nanostores';
|
||||
import type { PresetType } from 'services/api/endpoints/stylePresets';
|
||||
|
||||
const initialState: StylePresetModalState = {
|
||||
isModalOpen: false,
|
||||
@ -22,4 +23,5 @@ export type PrefilledFormData = {
|
||||
positivePrompt: string;
|
||||
negativePrompt: string;
|
||||
imageUrl: string | null;
|
||||
type: PresetType;
|
||||
};
|
||||
|
@ -19,6 +19,7 @@ const initialConfigState: AppConfig = {
|
||||
shouldUpdateImagesOnConnect: false,
|
||||
shouldFetchMetadataFromApi: false,
|
||||
allowPrivateBoards: false,
|
||||
allowPrivateStylePresets: false,
|
||||
disabledTabs: [],
|
||||
disabledFeatures: ['lightbox', 'faceRestore', 'batches'],
|
||||
disabledSDFeatures: ['variation', 'symmetry', 'hires', 'perlinNoise', 'noiseThreshold'],
|
||||
|
@ -13730,11 +13730,8 @@ export type components = {
|
||||
name: string;
|
||||
/** @description The preset data */
|
||||
preset_data: components["schemas"]["PresetData"];
|
||||
/**
|
||||
* @description The type of style preset
|
||||
* @default user
|
||||
*/
|
||||
type?: components["schemas"]["PresetType"];
|
||||
/** @description The type of style preset */
|
||||
type: components["schemas"]["PresetType"];
|
||||
/**
|
||||
* Id
|
||||
* @description The style preset ID.
|
||||
|
Loading…
Reference in New Issue
Block a user