🔧 chore: merge

pull/240/head
canisminor1990 2023-06-28 17:31:19 +08:00
parent 71970fec59
commit 47cb06cf8b
9 changed files with 663 additions and 893 deletions

View File

@ -4,5 +4,4 @@
git add .
npx --no-install lint-staged
npm run test
npm run build
git add .

File diff suppressed because one or more lines are too long

View File

@ -1,55 +1,16 @@
import { memo, useCallback, useState } from 'react';
import styled from 'styled-components';
import TagList, { PromptType, TagItem } from './TagList';
import { useStyles } from './style';
import { formatPrompt } from './utils';
/******************************************************
*********************** Style *************************
******************************************************/
const View = styled.div`
display: flex;
flex-direction: column;
gap: 8px;
`;
const ButtonGroup = styled.div`
display: flex;
gap: 8px;
`;
const Button = styled.button`
cursor: pointer;
position: relative;
display: flex;
flex: 1;
align-items: center;
justify-content: center;
padding: var(--input-padding);
font-size: var(--input-text-size);
font-weight: var(--input-text-weight);
line-height: var(--line-sm);
background: var(--button-secondary-background-fill);
border: var(--button-border-width) solid var(--button-secondary-border-color);
border-radius: var(--input-radius);
`;
/******************************************************
************************* Dom *************************
******************************************************/
interface PromptProps {
type: PromptType;
}
const Prompt = memo<PromptProps>(({ type }) => {
const [tags, setTags] = useState<TagItem[]>([]);
const { styles } = useStyles();
const id =
type === 'positive' ? "[id$='2img_prompt'] textarea" : "[id$='2img_neg_prompt'] textarea";
@ -84,17 +45,27 @@ const Prompt = memo<PromptProps>(({ type }) => {
}, []);
return (
<View>
<div className={styles.promptView}>
<TagList setTags={setTags} setValue={setCurrentValue} tags={tags} type={type} />
<ButtonGroup>
<Button onClick={getValue} title="Load Prompt">
<div className={styles.buttonGroup}>
<button
className="lg secondary gradio-button tool svelte-1ipelgc"
onClick={getValue}
title="Load Prompt"
type="button"
>
🔄
</Button>
<Button onClick={setValue} title="Set Prompt">
</button>
<button
className="lg secondary gradio-button tool svelte-1ipelgc"
onClick={setValue}
title="Set Prompt"
type="button"
>
</Button>
</ButtonGroup>
</View>
</button>
</div>
</div>
);
});

View File

@ -1,232 +0,0 @@
import { type FC, memo, useCallback, useMemo } from 'react';
import { WithContext, ReactTagsProps as WithContextProps } from 'react-tag-input';
import styled from 'styled-components';
import { genTagType, suggestions } from './utils';
export interface TagItem {
className?: string;
id: string;
text: string;
}
export type PromptType = 'positive' | 'negative';
interface ReactTagsProps extends WithContextProps {
editable?: boolean;
onTagUpdate?: (editIndex: number, tag: TagItem) => void;
}
// @ts-ignore
const ReactTags: FC<ReactTagsProps> = WithContext;
const View = styled.div<{ type: PromptType }>`
/* Styles for the input */
.ReactTags__editTagInput,
.ReactTags__tagInput {
display: inline-block;
width: 100%;
margin: 0;
input,
input:focus {
position: relative;
display: block;
width: 100%;
margin: 0;
padding: var(--input-padding);
font-size: var(--input-text-size);
font-weight: var(--input-text-weight);
line-height: var(--line-sm);
color: var(--body-text-color);
background: var(--input-background-fill);
border: var(--input-border-width) solid var(--input-border-color);
border-radius: var(--input-radius);
outline: none;
}
}
/* Styles for selected tags */
.ReactTags__tags.react-tags-wrapper {
display: flex;
flex-direction: column;
gap: 4px;
}
.ReactTags__selected {
display: flex;
flex-wrap: wrap;
gap: 4px;
span.ReactTags__tag {
position: relative;
display: flex;
align-items: center;
padding: var(--input-padding);
font-size: var(--text-sm);
font-weight: var(--input-text-weight);
line-height: var(--line-sm);
color: ${({ type }) => (type === 'positive' ? 'var(--green-9)' : 'var(--magenta-9)')};
background: var(--button-secondary-background-fill);
border: var(--button-border-width) solid var(--button-secondary-border-color);
border-radius: var(--input-radius);
&:hover {
color: ${({ type }) => (type === 'positive' ? 'var(--green-10)' : 'var(--magenta-10)')};
}
}
a.ReactTags__remove {
cursor: pointer;
margin-left: 5px;
color: #aaa;
}
}
/* Styles for suggestions */
.ReactTags__suggestions {
position: absolute;
z-index: 1000;
}
ul {
overflow-x: hidden;
overflow-y: auto;
width: 248px;
max-height: 480px;
padding: 0;
list-style-type: none;
background: var(--color-bg-container);
border-radius: var(--border-radius);
box-shadow: var(--box-shadow);
li {
margin: 0;
padding: 4px 8px;
font-size: 12px;
&.ReactTags__activeSuggestion {
cursor: pointer;
color: #fff;
background: var(--color-primary);
}
mark {
font-weight: 600;
color: #fff;
background: var(--color-primary-hover);
border-radius: 2px;
}
}
}
.ReactTags__remove {
cursor: pointer;
color: var(--color-text);
background: none;
border: none;
}
.ReactTags__lora {
background: var(--cyan-2) !important;
border-color: var(--cyan-3) !important;
}
.ReactTags__hypernet {
background: var(--purple-2) !important;
border-color: var(--purple-3) !important;
}
.ReactTags__embedding {
background: var(--orange-2) !important;
border-color: var(--orange-3) !important;
}
`;
const KeyCodes = {
comma: 188,
enter: 13,
};
const delimiters = [KeyCodes.comma, KeyCodes.enter];
interface TagListProps {
setTags: (tags: TagItem[]) => void;
setValue: (tags: TagItem[]) => void;
tags: TagItem[];
type: PromptType;
}
const TagList = memo<TagListProps>(({ tags, setTags, type, setValue }) => {
const handleDelete = useCallback(
(index_: number) => {
const newTags = tags.filter((tag, index) => index !== index_);
setTags(newTags);
setValue(newTags);
},
[tags],
);
const handleAddition = useCallback(
(tag: TagItem) => {
const newTags = [...tags, genTagType(tag)];
setTags(newTags);
setValue(newTags);
},
[tags],
);
const handleDrag = useCallback(
(tag: TagItem, currentPos: number, newPos: number) => {
const newTags = [...tags];
newTags.splice(currentPos, 1);
newTags.splice(newPos, 0, genTagType(tag));
setTags(newTags);
setValue(newTags);
},
[tags],
);
const handleTagUpdate = useCallback(
(index: number, tag: TagItem) => {
const newTags = [...tags];
newTags[index] = genTagType(tag);
setTags(newTags);
setValue(newTags);
},
[tags],
);
const suggestionData = useMemo(() => suggestions[type], [type]);
return (
<View type={type}>
<ReactTags
autocomplete
delimiters={delimiters}
editable
handleAddition={handleAddition}
handleDelete={handleDelete}
handleDrag={handleDrag}
inline
inputFieldPosition="bottom"
onTagUpdate={handleTagUpdate}
suggestions={suggestionData}
tags={tags}
/>
</View>
);
});
export default TagList;

View File

@ -0,0 +1,99 @@
import { type FC, memo, useCallback, useMemo } from 'react';
import { WithContext, ReactTagsProps as WithContextProps } from 'react-tag-input';
import { genTagType, suggestions } from '../utils';
import { useStyles } from './style';
export interface TagItem {
className?: string;
id: string;
text: string;
}
export type PromptType = 'positive' | 'negative';
interface ReactTagsProps extends WithContextProps {
editable?: boolean;
onTagUpdate?: (editIndex: number, tag: TagItem) => void;
}
// @ts-ignore
const ReactTags: FC<ReactTagsProps> = WithContext;
const KeyCodes = {
comma: 188,
enter: 13,
};
const delimiters = [KeyCodes.comma, KeyCodes.enter];
interface TagListProps {
setTags: (tags: TagItem[]) => void;
setValue: (tags: TagItem[]) => void;
tags: TagItem[];
type: PromptType;
}
const TagList = memo<TagListProps>(({ tags, setTags, type, setValue }) => {
const { styles } = useStyles(type);
const handleDelete = useCallback(
(index_: number) => {
const newTags = tags.filter((tag, index) => index !== index_);
setTags(newTags);
setValue(newTags);
},
[tags],
);
const handleAddition = useCallback(
(tag: TagItem) => {
const newTags = [...tags, genTagType(tag)];
setTags(newTags);
setValue(newTags);
},
[tags],
);
const handleDrag = useCallback(
(tag: TagItem, currentPos: number, newPos: number) => {
const newTags = [...tags];
newTags.splice(currentPos, 1);
newTags.splice(newPos, 0, genTagType(tag));
setTags(newTags);
setValue(newTags);
},
[tags],
);
const handleTagUpdate = useCallback(
(index: number, tag: TagItem) => {
const newTags = [...tags];
newTags[index] = genTagType(tag);
setTags(newTags);
setValue(newTags);
},
[tags],
);
const suggestionData = useMemo(() => suggestions[type], [type]);
return (
<div className={styles}>
<ReactTags
autocomplete
delimiters={delimiters}
editable
handleAddition={handleAddition}
handleDelete={handleDelete}
handleDrag={handleDrag}
inline
inputFieldPosition="bottom"
onTagUpdate={handleTagUpdate}
suggestions={suggestionData}
tags={tags}
/>
</div>
);
});
export default TagList;

View File

@ -0,0 +1,137 @@
import { createStyles } from 'antd-style';
export const useStyles = createStyles(
({ css }, type: 'positive' | 'negative') => css`
/* Styles for the input */
.ReactTags__editTagInput,
.ReactTags__tagInput {
display: inline-block;
width: 100%;
margin: 0;
input,
input:focus {
position: relative;
display: block;
width: 100%;
margin: 0;
padding: var(--input-padding);
font-size: var(--input-text-size);
font-weight: var(--input-text-weight);
line-height: var(--line-sm);
color: var(--body-text-color);
background: var(--input-background-fill);
border: var(--input-border-width) solid var(--input-border-color);
border-radius: var(--input-radius);
outline: none;
}
}
/* Styles for selected tags */
.ReactTags__tags.react-tags-wrapper {
display: flex;
flex-direction: column;
gap: 4px;
}
.ReactTags__selected {
display: flex;
flex-wrap: wrap;
gap: 4px;
span.ReactTags__tag {
position: relative;
display: flex;
align-items: center;
padding: var(--input-padding);
font-size: var(--text-sm);
font-weight: var(--input-text-weight);
line-height: var(--line-sm);
color: ${type === 'positive' ? 'var(--green-9)' : 'var(--magenta-9)'};
background: var(--button-secondary-background-fill);
border: var(--button-border-width) solid var(--button-secondary-border-color);
border-radius: var(--input-radius);
&:hover {
color: ${type === 'positive' ? 'var(--green-10)' : 'var(--magenta-10)'};
}
}
a.ReactTags__remove {
cursor: pointer;
margin-left: 5px;
color: #aaa;
}
}
/* Styles for suggestions */
.ReactTags__suggestions {
position: absolute;
z-index: 1000;
}
ul {
overflow-x: hidden;
overflow-y: auto;
width: 248px;
max-height: 480px;
padding: 0;
list-style-type: none;
background: var(--color-bg-container);
border-radius: var(--border-radius);
box-shadow: var(--box-shadow);
li {
margin: 0;
padding: 4px 8px;
font-size: 12px;
&.ReactTags__activeSuggestion {
cursor: pointer;
color: #fff;
background: var(--color-primary);
}
mark {
font-weight: 600;
color: #fff;
background: var(--color-primary-hover);
border-radius: 2px;
}
}
}
.ReactTags__remove {
cursor: pointer;
color: var(--color-text);
background: none;
border: none;
}
.ReactTags__lora {
background: var(--cyan-2) !important;
border-color: var(--cyan-3) !important;
}
.ReactTags__hypernet {
background: var(--purple-2) !important;
border-color: var(--purple-3) !important;
}
.ReactTags__embedding {
background: var(--orange-2) !important;
border-color: var(--orange-3) !important;
}
`,
);

View File

@ -1,48 +1,18 @@
import { memo } from 'react';
import styled from 'styled-components';
import { useStyles } from '@/slots/PromptEditor/style';
import Prompt from './Prompt';
/******************************************************
*********************** Style *************************
******************************************************/
const View = styled.div`
display: flex;
flex-direction: column;
gap: 1em;
margin-bottom: 1em;
`;
const Desc = styled.div`
position: relative;
z-index: var(--layer-4);
margin-bottom: -10px;
padding: var(--block-title-padding);
font-size: var(--block-title-text-size);
font-weight: var(--block-title-text-weight);
line-height: var(--line-sm);
color: var(--block-title-text-color);
background: var(--block-title-background-fill);
border: solid var(--block-title-border-width) var(--block-title-border-color);
border-radius: var(--block-title-radius);
`;
/******************************************************
************************* Dom *************************
******************************************************/
const PromptEditor = memo(() => {
const { styles } = useStyles();
return (
<View>
<Desc>Positive</Desc>
<div className={styles.view}>
<span style={{ marginBottom: -10 }}>Positive</span>
<Prompt type="positive" />
<Desc>Negative</Desc>
<span style={{ marginBottom: -10 }}>Negative</span>
<Prompt type="negative" />
</View>
</div>
);
});

View File

@ -0,0 +1,39 @@
import { createStyles } from 'antd-style';
export const useStyles = createStyles(({ css }) => ({
button: css`
cursor: pointer;
position: relative;
display: flex;
flex: 1;
align-items: center;
justify-content: center;
padding: var(--input-padding);
font-size: var(--input-text-size);
font-weight: var(--input-text-weight);
line-height: var(--line-sm);
background: var(--button-secondary-background-fill);
border: var(--button-border-width) solid var(--button-secondary-border-color);
border-radius: var(--input-radius);
`,
buttonGroup: css`
display: flex;
gap: 8px;
`,
promptView: css`
display: flex;
flex-direction: column;
gap: 8px;
`,
view: css`
display: flex;
flex-direction: column;
gap: 1em;
margin-bottom: 1em;
`,
}));

View File

@ -66,13 +66,13 @@ const SettingForm = memo(() => {
const onReset = useCallback(() => {
onSetSetting(defaultSetting);
(gradioApp().querySelector('#settings_restart_gradio') as HTMLButtonElement)?.click();
location.reload();
}, []);
const onFinish = useCallback(
(value: WebuiSetting) => {
onSetSetting({ ...value, neutralColor, primaryColor });
(gradioApp().querySelector('#settings_restart_gradio') as HTMLButtonElement)?.click();
location.reload();
},
[primaryColor, neutralColor],
);