👷 ci(lint): update lint config and code style

main
canisminor1990 2023-06-18 00:20:15 +08:00
parent 56cf8f327b
commit e9e51e66b2
34 changed files with 1837 additions and 1797 deletions

View File

@ -1,13 +1,16 @@
# http://editorconfig.org # http://editorconfig.org
root = true root = true
[*] [*]
indent_style = space indent_style = space
indent_size = 2 indent_size = 2
tab_width = 2
end_of_line = lf end_of_line = lf
charset = utf-8 charset = utf-8
trim_trailing_whitespace = true trim_trailing_whitespace = true
insert_final_newline = true insert_final_newline = true
max_line_length = 100
[*.md] [*.md]
trim_trailing_whitespace = false trim_trailing_whitespace = false

View File

@ -1 +1,12 @@
module.exports = require('@lobehub/lint').eslint; module.exports = {
...require('@lobehub/lint').eslint,
overrides: [
{
files: ['*.ts', '*.tsx'],
rules: {
'no-undef': 0,
'unicorn/prefer-add-event-listener': 0,
},
},
],
};

View File

@ -3,5 +3,11 @@ module.exports = {
rules: { rules: {
'selector-class-pattern': null, 'selector-class-pattern': null,
'selector-id-pattern': null, 'selector-id-pattern': null,
'no-descending-specificity': [
true,
{
severity: 'warning',
},
],
}, },
}; };

File diff suppressed because one or more lines are too long

View File

@ -1,9 +1,9 @@
import { FloatButton } from 'antd'; import {FloatButton} from 'antd';
import { type ReactNode, memo, useRef } from 'react'; import {type ReactNode, memo, useRef} from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { shallow } from 'zustand/shallow'; import {shallow} from 'zustand/shallow';
import { useAppStore } from '@/store'; import {useAppStore} from '@/store';
const ContentView = styled.div<{ isPromptResizable: boolean }>` const ContentView = styled.div<{ isPromptResizable: boolean }>`
overflow-x: hidden; overflow-x: hidden;
@ -11,11 +11,11 @@ const ContentView = styled.div<{ isPromptResizable: boolean }>`
flex: 1; flex: 1;
[id$='2img_prompt'] textarea { [id$='2img_prompt'] textarea {
max-height: ${({ isPromptResizable }) => (isPromptResizable ? 'unset' : '84px')}; max-height: ${({isPromptResizable}) => (isPromptResizable ? 'unset' : '84px')};
} }
[id$='2img_neg_prompt'] textarea { [id$='2img_neg_prompt'] textarea {
max-height: ${({ isPromptResizable }) => (isPromptResizable ? 'unset' : '84px')}; max-height: ${({isPromptResizable}) => (isPromptResizable ? 'unset' : '84px')};
} }
`; `;
@ -24,16 +24,16 @@ interface ContentProps {
loading?: boolean; loading?: boolean;
} }
const Content = memo<ContentProps>(({ children }) => { const Content = memo<ContentProps>(({children}) => {
const ref: any = useRef(null); const reference: any = useRef(null);
const [setting] = useAppStore((st) => [st.setting], shallow); const [setting] = useAppStore((st) => [st.setting], shallow);
return ( return (
<ContentView isPromptResizable={setting.promotTextarea === 'resizable'} ref={ref}> <ContentView isPromptResizable={setting.promotTextarea === 'resizable'} ref={reference}>
{children} {children}
<FloatButton.BackTop target={() => ref.current} /> <FloatButton.BackTop target={() => reference.current} />
</ContentView> </ContentView>
); );
}); });
export default Content; export default Content;

View File

@ -1,16 +1,16 @@
import { useHover } from 'ahooks'; import {useHover} from 'ahooks';
import { ChevronDown, ChevronLeft, ChevronRight, ChevronUp } from 'lucide-react'; import {ChevronDown, ChevronLeft, ChevronRight, ChevronUp} from 'lucide-react';
import type { Enable, NumberSize, Size } from 're-resizable'; import type {Enable, NumberSize, Size} from 're-resizable';
import { HandleClassName, Resizable } from 're-resizable'; import {HandleClassName, Resizable} from 're-resizable';
import { memo, useEffect, useMemo, useRef, useState } from 'react'; import {memo, useEffect, useMemo, useRef, useState} from 'react';
import { Center } from 'react-layout-kit'; import {Center} from 'react-layout-kit';
import type { Props as RndProps } from 'react-rnd'; import type {Props as RndProps} from 'react-rnd';
import useControlledState from 'use-merge-value'; import useControlledState from 'use-merge-value';
import type { DivProps } from '@/types'; import type {DivProps} from '@/types';
import { useStyle } from './style'; import {useStyle} from './style';
import { revesePlacement } from './utils'; import {revesePlacement} from './utils';
const DEFAULT_HEIGHT = 180; const DEFAULT_HEIGHT = 180;
const DEFAULT_WIDTH = 280; const DEFAULT_WIDTH = 280;
@ -38,186 +38,191 @@ export interface DraggablePanelProps extends DivProps {
} }
const DraggablePanel = memo<DraggablePanelProps>( const DraggablePanel = memo<DraggablePanelProps>(
({ ({
pin = 'true', pin = 'true',
mode = 'fixed', mode = 'fixed',
children, children,
placement = 'right', placement = 'right',
resize, resize,
style, style,
size, size,
defaultSize: customizeDefaultSize, defaultSize: customizeDefaultSize,
minWidth, minWidth,
minHeight, minHeight,
onSizeChange, onSizeChange,
onSizeDragging, onSizeDragging,
expandable = true, expandable = true,
expand, expand,
defaultExpand = true, defaultExpand = true,
onExpandChange, onExpandChange,
className, className,
showHandlerWhenUnexpand, showHandlerWhenUnexpand,
destroyOnClose, destroyOnClose,
hanlderStyle, hanlderStyle,
}) => { }) => {
const ref = useRef(null); const reference = useRef(null);
const isHovering = useHover(ref); const isHovering = useHover(reference);
const isVertical = placement === 'top' || placement === 'bottom'; const isVertical = placement === 'top' || placement === 'bottom';
const { styles, cx } = useStyle('draggable-panel'); const {styles, cx} = useStyle('draggable-panel');
const [isExpand, setIsExpand] = useControlledState(defaultExpand, { const [isExpand, setIsExpand] = useControlledState(defaultExpand, {
value: expand, onChange: onExpandChange,
onChange: onExpandChange, value: expand,
}); });
useEffect(() => { useEffect(() => {
if (pin) return; if (pin) return;
if (isHovering && !isExpand) { if (isHovering && !isExpand) {
setIsExpand(true); setIsExpand(true);
} else if (!isHovering && isExpand) { } else if (!isHovering && isExpand) {
setIsExpand(false); setIsExpand(false);
} }
}, [pin, isHovering, isExpand]); }, [pin, isHovering, isExpand]);
const [showExpand, setShowExpand] = useState(true); const [showExpand, setShowExpand] = useState(true);
const canResizing = resize !== false && isExpand; const canResizing = resize !== false && isExpand;
const resizeHandleClassNames: HandleClassName = useMemo(() => { const resizeHandleClassNames: HandleClassName = useMemo(() => {
if (!canResizing) return {}; if (!canResizing) return {};
return { return {
[revesePlacement(placement)]: styles[`${revesePlacement(placement)}Handle`], [revesePlacement(placement)]: styles[`${revesePlacement(placement)}Handle`],
}; };
}, [canResizing, placement]); }, [canResizing, placement]);
const resizing = { const resizing = {
top: false, bottom: false,
bottom: false, bottomLeft: false,
right: false, bottomRight: false,
left: false, left: false,
topRight: false, right: false,
bottomRight: false, top: false,
bottomLeft: false, topLeft: false,
topLeft: false, topRight: false,
[revesePlacement(placement)]: true, [revesePlacement(placement)]: true,
...(resize as Enable), ...(resize as Enable),
};
const defaultSize: Size = useMemo(() => {
if (isVertical)
return {
width: '100%',
height: DEFAULT_HEIGHT,
...customizeDefaultSize,
}; };
return { const defaultSize: Size = useMemo(() => {
width: DEFAULT_WIDTH, if (isVertical) {
height: '100%', return {
...customizeDefaultSize, height: DEFAULT_HEIGHT,
}; width: '100%',
}, [isVertical]); ...customizeDefaultSize,
};
}
const sizeProps = isExpand return {
? { height: '100%',
minWidth: typeof minWidth === 'number' ? Math.max(minWidth, 0) : 280, width: DEFAULT_WIDTH,
minHeight: typeof minHeight === 'number' ? Math.max(minHeight, 0) : undefined, ...customizeDefaultSize,
defaultSize, };
size: size as Size, }, [isVertical]);
}
: isVertical
? {
minHeight: 0,
size: { height: 0 },
}
: {
minWidth: 0,
size: { width: 0 },
};
const { Arrow, className: arrowPlacement } = useMemo(() => { const sizeProps = isExpand ?
switch (placement) { {
case 'top': defaultSize,
return { className: 'Bottom', Arrow: ChevronDown }; minHeight: typeof minHeight === 'number' ? Math.max(minHeight, 0) : undefined,
case 'bottom': minWidth: typeof minWidth === 'number' ? Math.max(minWidth, 0) : 280,
return { className: 'Top', Arrow: ChevronUp }; size: size as Size,
case 'right': } :
return { className: 'Left', Arrow: ChevronLeft }; isVertical ?
case 'left': {
return { className: 'Right', Arrow: ChevronRight }; minHeight: 0,
} size: {height: 0},
}, [styles, placement]); } :
{
minWidth: 0,
size: {width: 0},
};
const handler = ( const {Arrow, className: arrowPlacement} = useMemo(() => {
// @ts-ignore switch (placement) {
<Center case 'top': {
// @ts-ignore return {Arrow: ChevronDown, className: 'Bottom'};
className={cx(styles[`toggle${arrowPlacement}`])} }
style={{ opacity: isExpand ? (!pin ? 0 : undefined) : showHandlerWhenUnexpand ? 1 : 0 }} case 'bottom': {
> return {Arrow: ChevronUp, className: 'Top'};
<Center }
onClick={() => { case 'right': {
setIsExpand(!isExpand); return {Arrow: ChevronLeft, className: 'Left'};
}} }
style={hanlderStyle} case 'left': {
> return {Arrow: ChevronRight, className: 'Right'};
<div }
className={styles.handlerIcon} }
style={{ transform: `rotate(${isExpand ? 180 : 0}deg)` }} }, [styles, placement]);
>
<Arrow size={16} strokeWidth={1.5} />
</div>
</Center>
</Center>
);
const inner = ( const handler = (
// @ts-ignore // @ts-ignore
<Resizable <Center
{...sizeProps} // @ts-ignore
className={styles.panel} className={cx(styles[`toggle${arrowPlacement}`])}
enable={canResizing ? (resizing as Enable) : undefined} style={{opacity: isExpand ? (pin ? undefined : 0) : showHandlerWhenUnexpand ? 1 : 0}}
handleClasses={resizeHandleClassNames} >
onResize={(_, direction, ref, delta) => { <Center
onSizeDragging?.(delta, { onClick={() => {
width: ref.style.width, setIsExpand(!isExpand);
height: ref.style.height, }}
}); style={hanlderStyle}
}} >
onResizeStart={() => { <div
setShowExpand(false); className={styles.handlerIcon}
}} style={{transform: `rotate(${isExpand ? 180 : 0}deg)`}}
onResizeStop={(e, direction, ref, delta) => { >
setShowExpand(true); <Arrow size={16} strokeWidth={1.5} />
onSizeChange?.(delta, { </div>
width: ref.style.width, </Center>
height: ref.style.height, </Center>
}); );
}}
style={style}
>
{children}
</Resizable>
);
return ( const inner = (
<div // @ts-ignore
className={cx( <Resizable
styles.container, {...sizeProps}
// @ts-ignore className={styles.panel}
styles[mode === 'fixed' ? 'fixed' : `${placement}Float`], enable={canResizing ? (resizing as Enable) : undefined}
className, handleClasses={resizeHandleClassNames}
)} onResize={(_, direction, reference_, delta) => {
ref={ref} onSizeDragging?.(delta, {
style={{ [`border${arrowPlacement}Width`]: 1 }} height: reference_.style.height,
> width: reference_.style.width,
{expandable && showExpand && handler} });
{destroyOnClose ? isExpand && inner : inner} }}
</div> onResizeStart={() => {
); setShowExpand(false);
}, }}
onResizeStop={(e, direction, reference_, delta) => {
setShowExpand(true);
onSizeChange?.(delta, {
height: reference_.style.height,
width: reference_.style.width,
});
}}
style={style}
>
{children}
</Resizable>
);
return (
<div
className={cx(
styles.container,
// @ts-ignore
styles[mode === 'fixed' ? 'fixed' : `${placement}Float`],
className,
)}
ref={reference}
style={{[`border${arrowPlacement}Width`]: 1}}
>
{expandable && showExpand && handler}
{destroyOnClose ? isExpand && inner : inner}
</div>
);
},
); );
export default DraggablePanel; export default DraggablePanel;

View File

@ -1,11 +1,11 @@
import { createStyles, css, cx } from 'antd-style'; import {createStyles, css, cx} from 'antd-style';
const offset = 17; const offset = 17;
const toggleLength = 40; const toggleLength = 40;
const toggleShort = 16; const toggleShort = 16;
export const useStyle = createStyles(({ token }, prefix: string) => { export const useStyle = createStyles(({token}, prefix: string) => {
const commonHandle = css` const commonHandle = css`
position: relative; position: relative;
&::before { &::before {
@ -23,7 +23,7 @@ export const useStyle = createStyles(({ token }, prefix: string) => {
} }
`; `;
const commonToggle = css` const commonToggle = css`
position: absolute; position: absolute;
z-index: 1001; z-index: 1001;
opacity: 0; opacity: 0;
@ -64,15 +64,36 @@ export const useStyle = createStyles(({ token }, prefix: string) => {
} }
`; `;
const float = css` const float = css`
position: absolute; position: absolute;
z-index: 2000; z-index: 2000;
`; `;
return { return {
container: cx( bottomFloat: cx(
prefix, float,
css` css`
right: 0;
bottom: 0;
left: 0;
width: 100%;
`,
),
bottomHandle: cx(
`${prefix}-bottom-handle`,
css`
${commonHandle};
&::before {
bottom: 50%;
width: 100%;
height: 2px;
}
`,
),
container: cx(
prefix,
css`
flex-shrink: 0; flex-shrink: 0;
border: 0 solid ${token.colorBorderSecondary}; border: 0 solid ${token.colorBorderSecondary};
@ -82,111 +103,70 @@ export const useStyle = createStyles(({ token }, prefix: string) => {
} }
} }
`, `,
), ),
fixed: css` fixed: css`
position: relative; position: relative;
`, `,
leftFloat: cx( handlerIcon: css`
float, display: flex;
css` align-items: center;
justify-content: center;
transition: all 0.2s ${token.motionEaseOut};
`,
leftFloat: cx(
float,
css`
top: 0; top: 0;
bottom: 0; bottom: 0;
left: 0; left: 0;
height: 100%; height: 100%;
`, `,
), ),
rightFloat: cx( leftHandle: cx(
float, css`
css` ${commonHandle};
top: 0;
right: 0;
bottom: 0;
height: 100%;
`,
),
topFloat: cx(
float,
css`
top: 0;
right: 0;
left: 0;
width: 100%;
`,
),
bottomFloat: cx(
float,
css`
right: 0;
bottom: 0;
left: 0;
width: 100%;
`,
),
toggleLeft: cx(
`${prefix}-toggle`,
`${prefix}-toggle-left`,
commonToggle,
css`
left: -${offset}px;
width: ${toggleShort}px;
height: 100%;
> div { &::before {
top: 50%;
width: ${toggleShort}px;
height: ${toggleLength}px;
margin-top: -20px;
border-radius: 4px 0 0 4px;
}
`,
),
toggleRight: cx(
`${prefix}-toggle`,
`${prefix}-toggle-right`,
commonToggle,
css`
right: -${offset}px;
width: ${toggleShort}px;
height: 100%;
> div {
top: 50%;
width: ${toggleShort}px;
height: ${toggleLength}px;
margin-top: -20px;
border-radius: 0 4px 4px 0;
}
`,
),
toggleTop: cx(
`${prefix}-toggle`,
`${prefix}-toggle-top`,
commonToggle,
css`
top: -${offset}px;
width: 100%;
height: ${toggleShort}px;
> div {
left: 50%; left: 50%;
width: 2px;
width: ${toggleLength}px; height: 100%;
height: ${toggleShort}px;
margin-left: -20px;
border-radius: 4px 4px 0 0;
} }
`, `,
), `${prefix}-left-handle`,
toggleBottom: cx( ),
`${prefix}-toggle`, panel: cx(
`${prefix}-toggle-bottom`, `${prefix}-fixed`,
commonToggle, css`
css` overflow: hidden;
background: ${token.colorBgContainer};
transition: all 0.2s ${token.motionEaseOut};
`,
),
rightFloat: cx(
float,
css`
top: 0;
right: 0;
bottom: 0;
height: 100%;
`,
),
rightHandle: cx(
css`
${commonHandle};
&::before {
right: 50%;
width: 2px;
height: 100%;
}
`,
`${prefix}-right-handle`,
),
toggleBottom: cx(
`${prefix}-toggle`,
`${prefix}-toggle-bottom`,
commonToggle,
css`
bottom: -${offset}px; bottom: -${offset}px;
width: 100%; width: 100%;
height: ${toggleShort}px; height: ${toggleShort}px;
@ -201,47 +181,79 @@ export const useStyle = createStyles(({ token }, prefix: string) => {
border-radius: 0 0 4px 4px; border-radius: 0 0 4px 4px;
} }
`, `,
), ),
panel: cx( toggleLeft: cx(
`${prefix}-fixed`, `${prefix}-toggle`,
css` `${prefix}-toggle-left`,
overflow: hidden; commonToggle,
background: ${token.colorBgContainer}; css`
transition: all 0.2s ${token.motionEaseOut}; left: -${offset}px;
`, width: ${toggleShort}px;
), height: 100%;
handlerIcon: css`
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ${token.motionEaseOut};
`,
leftHandle: cx(
css`
${commonHandle};
&::before { > div {
top: 50%;
width: ${toggleShort}px;
height: ${toggleLength}px;
margin-top: -20px;
border-radius: 4px 0 0 4px;
}
`,
),
toggleRight: cx(
`${prefix}-toggle`,
`${prefix}-toggle-right`,
commonToggle,
css`
right: -${offset}px;
width: ${toggleShort}px;
height: 100%;
> div {
top: 50%;
width: ${toggleShort}px;
height: ${toggleLength}px;
margin-top: -20px;
border-radius: 0 4px 4px 0;
}
`,
),
toggleTop: cx(
`${prefix}-toggle`,
`${prefix}-toggle-top`,
commonToggle,
css`
top: -${offset}px;
width: 100%;
height: ${toggleShort}px;
> div {
left: 50%; left: 50%;
width: 2px;
height: 100%; width: ${toggleLength}px;
height: ${toggleShort}px;
margin-left: -20px;
border-radius: 4px 4px 0 0;
} }
`, `,
`${prefix}-left-handle`, ),
), topFloat: cx(
rightHandle: cx( float,
css` css`
${commonHandle}; top: 0;
&::before { right: 0;
right: 50%; left: 0;
width: 2px; width: 100%;
height: 100%;
}
`, `,
`${prefix}-right-handle`, ),
), topHandle: cx(
topHandle: cx( `${prefix}-top-handle`,
`${prefix}-top-handle`, css`
css`
${commonHandle}; ${commonHandle};
&::before { &::before {
@ -250,18 +262,6 @@ export const useStyle = createStyles(({ token }, prefix: string) => {
height: 2px; height: 2px;
} }
`, `,
), ),
bottomHandle: cx( };
`${prefix}-bottom-handle`,
css`
${commonHandle};
&::before {
bottom: 50%;
width: 100%;
height: 2px;
}
`,
),
};
}); });

View File

@ -1,14 +1,18 @@
import { placementType } from './index'; import {placementType} from './index';
export const revesePlacement = (placement: placementType) => { export const revesePlacement = (placement: placementType) => {
switch (placement) { switch (placement) {
case 'bottom': case 'bottom': {
return 'top'; return 'top';
case 'top': }
return 'bottom'; case 'top': {
case 'right': return 'bottom';
return 'left'; }
case 'left': case 'right': {
return 'right'; return 'left';
} }
case 'left': {
return 'right';
}
}
}; };

View File

@ -1,28 +1,28 @@
import { memo, useEffect } from 'react'; import {memo, useEffect} from 'react';
interface GiscusProps { interface GiscusProps {
themeMode: 'light' | 'dark'; themeMode: 'light' | 'dark';
} }
const Giscus = memo<GiscusProps>(({ themeMode }) => { const Giscus = memo<GiscusProps>(({themeMode}) => {
useEffect(() => { useEffect(() => {
// giscus // giscus
const giscus: HTMLScriptElement = document.createElement('script'); const giscus: HTMLScriptElement = document.createElement('script');
giscus.src = 'https://giscus.app/client.js'; giscus.src = 'https://giscus.app/client.js';
giscus.setAttribute('data-repo', 'canisminor1990/sd-webui-kitchen-theme'); giscus.dataset.repo = 'canisminor1990/sd-webui-kitchen-theme';
giscus.setAttribute('data-repo-id', 'R_kgDOJCPcNg'); giscus.dataset.repoId = 'R_kgDOJCPcNg';
giscus.setAttribute('data-mapping', 'number'); giscus.dataset.mapping = 'number';
giscus.setAttribute('data-term', '53'); giscus.dataset.term = '53';
giscus.setAttribute('data-reactions-enabled', '1'); giscus.dataset.reactionsEnabled = '1';
giscus.setAttribute('data-emit-metadata', '0'); giscus.dataset.emitMetadata = '0';
giscus.setAttribute('data-input-position', 'bottom'); giscus.dataset.inputPosition = 'bottom';
giscus.setAttribute('data-theme', themeMode); giscus.dataset.theme = themeMode;
giscus.setAttribute('data-lang', 'en'); giscus.dataset.lang = 'en';
giscus.crossOrigin = 'anonymous'; giscus.crossOrigin = 'anonymous';
giscus.async = true; giscus.async = true;
document.getElementsByTagName('head')[0].appendChild(giscus); document.querySelectorAll('head')[0].append(giscus);
}, []); }, []);
return <div className="giscus" id="giscus" />; return <div className="giscus" id="giscus" />;
}); });
export default Giscus; export default Giscus;

View File

@ -1,6 +1,6 @@
import type { MenuProps } from 'antd'; import type {MenuProps} from 'antd';
import { Menu } from 'antd'; import {Menu} from 'antd';
import { memo, useEffect, useState } from 'react'; import {memo, useEffect, useState} from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
/****************************************************** /******************************************************
@ -38,40 +38,40 @@ const NavBar = styled(Menu)`
************************* Dom ************************* ************************* Dom *************************
******************************************************/ ******************************************************/
const Nav = memo(() => { const onClick: MenuProps['onClick'] = (e: any) => {
const [items, setItems] = useState<MenuProps['items']>([]);
useEffect(() => {
onUiLoaded(() => {
const buttons = gradioApp().querySelectorAll('#tabs > .tab-nav:first-child button');
const list: MenuProps['items'] | any = [];
buttons.forEach((button: HTMLButtonElement | any, index) => {
button.id = `kitchen-nav-${index}`;
list.push({
label: button.textContent,
key: String(index),
});
});
setItems(list);
});
}, []);
const onClick: MenuProps['onClick'] = (e: any) => {
const buttons: HTMLButtonElement[] | any = gradioApp().querySelectorAll( const buttons: HTMLButtonElement[] | any = gradioApp().querySelectorAll(
'#tabs > .tab-nav:first-child button', '#tabs > .tab-nav:first-child button',
); );
buttons[Number(e.key)]?.click(); buttons[Number(e.key)]?.click();
}; };
return ( const Nav = memo(() => {
<NavBar const [items, setItems] = useState<MenuProps['items']>([]);
defaultActiveFirst
defaultSelectedKeys={['0']} useEffect(() => {
items={items} onUiLoaded(() => {
mode="horizontal" const buttons = gradioApp().querySelectorAll('#tabs > .tab-nav:first-child button');
onClick={onClick} const list: MenuProps['items'] | any = [];
/> buttons.forEach((button: HTMLButtonElement | any, index) => {
); button.id = `kitchen-nav-${index}`;
list.push({
key: String(index),
label: button.textContent,
});
});
setItems(list);
});
}, []);
return (
<NavBar
defaultActiveFirst
defaultSelectedKeys={['0']}
items={items}
mode="horizontal"
onClick={onClick}
/>
);
}); });
export default Nav; export default Nav;

View File

@ -1,10 +1,10 @@
import { SettingOutlined } from '@ant-design/icons'; import {SettingOutlined} from '@ant-design/icons';
import { Button, Divider, Form, InputNumber, Popover, Segmented, Space, Switch } from 'antd'; import {Button, Divider, Form, InputNumber, Popover, Segmented, Space, Switch} from 'antd';
import { memo, useCallback } from 'react'; import {memo, useCallback} from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { shallow } from 'zustand/shallow'; import {shallow} from 'zustand/shallow';
import { WebuiSetting, defaultSetting, useAppStore } from '@/store'; import {WebuiSetting, defaultSetting, useAppStore} from '@/store';
/****************************************************** /******************************************************
*********************** Style ************************* *********************** Style *************************
@ -39,85 +39,85 @@ const SubTitle = styled.div`
******************************************************/ ******************************************************/
const Setting = memo(() => { const Setting = memo(() => {
const [setting, onSetSetting] = useAppStore((st) => [st.setting, st.onSetSetting], shallow); const [setting, onSetSetting] = useAppStore((st) => [st.setting, st.onSetSetting], shallow);
const onReset = useCallback(() => { const onReset = useCallback(() => {
onSetSetting(defaultSetting); onSetSetting(defaultSetting);
gradioApp().getElementById('settings_restart_gradio')?.click(); gradioApp().querySelector('#settings_restart_gradio')?.click();
}, []); }, []);
const onFinish = useCallback((value: WebuiSetting) => { const onFinish = useCallback((value: WebuiSetting) => {
onSetSetting(value); onSetSetting(value);
gradioApp().getElementById('settings_restart_gradio')?.click(); gradioApp().querySelector('#settings_restart_gradio')?.click();
}, []); }, []);
return ( return (
<Popover <Popover
content={ content={
<Form <Form
initialValues={setting} initialValues={setting}
layout="horizontal" layout="horizontal"
onFinish={onFinish} onFinish={onFinish}
size="small" size="small"
style={{ maxWidth: 260 }} style={{maxWidth: 260}}
> >
<Divider style={{ margin: '4px 0 8px' }} /> <Divider style={{margin: '4px 0 8px'}} />
<SubTitle>Promot Textarea</SubTitle> <SubTitle>Promot Textarea</SubTitle>
<FormItem label="Display mode" name="promotTextarea"> <FormItem label="Display mode" name="promotTextarea">
<Segmented options={['scroll', 'resizable']} /> <Segmented options={['scroll', 'resizable']} />
</FormItem> </FormItem>
<Divider style={{ margin: '4px 0 8px' }} /> <Divider style={{margin: '4px 0 8px'}} />
<SubTitle>Sidebar</SubTitle> <SubTitle>Sidebar</SubTitle>
<FormItem label="Default expand" name="sidebarExpand" valuePropName="checked"> <FormItem label="Default expand" name="sidebarExpand" valuePropName="checked">
<Switch /> <Switch />
</FormItem> </FormItem>
<FormItem label="Display mode" name="sidebarFixedMode"> <FormItem label="Display mode" name="sidebarFixedMode">
<Segmented options={['fixed', 'float']} /> <Segmented options={['fixed', 'float']} />
</FormItem> </FormItem>
<FormItem label="Default width" name="sidebarWidth"> <FormItem label="Default width" name="sidebarWidth">
<InputNumber /> <InputNumber />
</FormItem> </FormItem>
<Divider style={{ margin: '4px 0 8px' }} /> <Divider style={{margin: '4px 0 8px'}} />
<SubTitle>ExtraNetwork Sidebar</SubTitle> <SubTitle>ExtraNetwork Sidebar</SubTitle>
<FormItem label="Enable" name="enableExtraNetworkSidebar" valuePropName="checked"> <FormItem label="Enable" name="enableExtraNetworkSidebar" valuePropName="checked">
<Switch /> <Switch />
</FormItem> </FormItem>
<FormItem label="Display mode" name="extraNetworkFixedMode" valuePropName="checked"> <FormItem label="Display mode" name="extraNetworkFixedMode" valuePropName="checked">
<Segmented options={['fixed', 'float']} /> <Segmented options={['fixed', 'float']} />
</FormItem> </FormItem>
<FormItem label="Default expand" name="extraNetworkSidebarExpand" valuePropName="checked"> <FormItem label="Default expand" name="extraNetworkSidebarExpand" valuePropName="checked">
<Switch /> <Switch />
</FormItem> </FormItem>
<FormItem label="Default width" name="extraNetworkSidebarWidth"> <FormItem label="Default width" name="extraNetworkSidebarWidth">
<InputNumber /> <InputNumber />
</FormItem> </FormItem>
<FormItem label="Default card size" name="extraNetworkCardSize"> <FormItem label="Default card size" name="extraNetworkCardSize">
<InputNumber /> <InputNumber />
</FormItem> </FormItem>
<Divider style={{ margin: '4px 0 8px' }} /> <Divider style={{margin: '4px 0 8px'}} />
<SubTitle>Other</SubTitle> <SubTitle>Other</SubTitle>
<FormItem label="Use svg icons" name="svgIcon" valuePropName="checked"> <FormItem label="Use svg icons" name="svgIcon" valuePropName="checked">
<Switch /> <Switch />
</FormItem> </FormItem>
<Divider style={{ margin: '4px 0 16px' }} /> <Divider style={{margin: '4px 0 16px'}} />
<FormItem> <FormItem>
<Space> <Space>
<Button htmlType="button" onClick={onReset} style={{ borderRadius: 4 }}> <Button htmlType="button" onClick={onReset} style={{borderRadius: 4}}>
Reset Reset
</Button> </Button>
<Button htmlType="submit" style={{ borderRadius: 4 }} type="primary"> <Button htmlType="submit" style={{borderRadius: 4}} type="primary">
Apply and restart UI Apply and restart UI
</Button> </Button>
</Space> </Space>
</FormItem> </FormItem>
</Form> </Form>
} }
title={<Title> Setting</Title>} title={<Title> Setting</Title>}
trigger="click" trigger="click"
> >
<Button icon={<SettingOutlined />} title="Setting" /> <Button icon={<SettingOutlined />} title="Setting" />
</Popover> </Popover>
); );
}); });
export default Setting; export default Setting;

View File

@ -1,19 +1,19 @@
import { BoldOutlined, GithubOutlined } from '@ant-design/icons'; import {BoldOutlined, GithubOutlined} from '@ant-design/icons';
import { Button, Modal, Space } from 'antd'; import {Button, Modal, Space} from 'antd';
import { useResponsive } from 'antd-style'; import {useResponsive} from 'antd-style';
import qs from 'query-string'; import qs from 'query-string';
import { type ReactNode, memo, useCallback, useEffect, useState } from 'react'; import {type ReactNode, memo, useCallback, useEffect, useState} from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { shallow } from 'zustand/shallow'; import {shallow} from 'zustand/shallow';
import { DraggablePanel } from '@/components'; import {DraggablePanel} from '@/components';
import { useAppStore } from '@/store'; import {useAppStore} from '@/store';
import Giscus from './Giscus'; import Giscus from './Giscus';
import Logo from './Logo'; import Logo from './Logo';
import Nav from './Nav'; import Nav from './Nav';
import Setting from './Setting'; import Setting from './Setting';
import { civitaiLogo, themeIcon } from './style'; import {civitaiLogo, themeIcon} from './style';
/****************************************************** /******************************************************
*********************** Style ************************* *********************** Style *************************
@ -38,86 +38,86 @@ interface HeaderProps {
children: ReactNode; children: ReactNode;
} }
const Header = memo<HeaderProps>(({ children }) => { const Header = memo<HeaderProps>(({children}) => {
const [themeMode] = useAppStore((st) => [st.themeMode], shallow); const [themeMode] = useAppStore((st) => [st.themeMode], shallow);
const { mobile } = useResponsive(); const {mobile} = useResponsive();
const [expand, setExpand] = useState<boolean>(true); const [expand, setExpand] = useState<boolean>(true);
const [isModalOpen, setIsModalOpen] = useState(false); const [isModalOpen, setIsModalOpen] = useState(false);
useEffect(() => { useEffect(() => {
if (mobile) setExpand(false); if (mobile) setExpand(false);
}, []); }, []);
const handleSetTheme = useCallback(() => { const handleSetTheme = useCallback(() => {
const theme = themeMode === 'light' ? 'dark' : 'light'; const theme = themeMode === 'light' ? 'dark' : 'light';
const gradioURL = qs.parseUrl(window.location.href); const gradioURL = qs.parseUrl(window.location.href);
gradioURL.query.__theme = theme; gradioURL.query.__theme = theme;
window.location.replace(qs.stringifyUrl(gradioURL)); window.location.replace(qs.stringifyUrl(gradioURL));
}, [themeMode]); }, [themeMode]);
const showModal = () => setIsModalOpen(true); const showModal = () => setIsModalOpen(true);
const handleCancel = () => setIsModalOpen(false); const handleCancel = () => setIsModalOpen(false);
return ( return (
<> <>
<DraggablePanel <DraggablePanel
defaultSize={{ height: 'auto' }} defaultSize={{height: 'auto'}}
expand={expand} expand={expand}
minHeight={64} minHeight={64}
onExpandChange={setExpand} onExpandChange={setExpand}
placement="top" placement="top"
>
<HeaderView id="header" style={{ flexDirection: mobile ? 'column' : 'row' }}>
<a
href="https://github.com/canisminor1990/sd-webui-kitchen-theme"
rel="noreferrer"
target="_blank"
>
<Logo themeMode={themeMode} />
</a>
<Nav />
{children}
<Space.Compact>
<a href="https://civitai.com/" rel="noreferrer" target="_blank">
<Button icon={civitaiLogo} title="Civitai" />
</a>
<a
href="https://www.birme.net/?target_width=512&target_height=512"
rel="noreferrer"
target="_blank"
> >
<Button icon={<BoldOutlined />} title="Birme" /> <HeaderView id="header" style={{flexDirection: mobile ? 'column' : 'row'}}>
</a> <a
<Button icon={<GithubOutlined />} onClick={showModal} title="Feedback" /> href="https://github.com/canisminor1990/sd-webui-kitchen-theme"
<Setting /> rel="noreferrer"
<Button icon={themeIcon[themeMode]} onClick={handleSetTheme} title="Switch Theme" /> target="_blank"
</Space.Compact> >
</HeaderView> <Logo themeMode={themeMode} />
</DraggablePanel> </a>
<Modal
footer={null} <Nav />
onCancel={handleCancel} {children}
open={isModalOpen}
title={ <Space.Compact>
<a <a href="https://civitai.com/" rel="noreferrer" target="_blank">
href="https://github.com/canisminor1990/sd-webui-kitchen-theme" <Button icon={civitaiLogo} title="Civitai" />
rel="noreferrer" </a>
target="_blank" <a
> href="https://www.birme.net/?target_width=512&target_height=512"
<Space> rel="noreferrer"
<GithubOutlined /> target="_blank"
{'canisminor1990/sd-webui-kitchen-theme'} >
</Space> <Button icon={<BoldOutlined />} title="Birme" />
</a> </a>
} <Button icon={<GithubOutlined />} onClick={showModal} title="Feedback" />
> <Setting />
<Giscus themeMode={themeMode} /> <Button icon={themeIcon[themeMode]} onClick={handleSetTheme} title="Switch Theme" />
</Modal> </Space.Compact>
</> </HeaderView>
); </DraggablePanel>
<Modal
footer={false}
onCancel={handleCancel}
open={isModalOpen}
title={
<a
href="https://github.com/canisminor1990/sd-webui-kitchen-theme"
rel="noreferrer"
target="_blank"
>
<Space>
<GithubOutlined />
{'canisminor1990/sd-webui-kitchen-theme'}
</Space>
</a>
}
>
<Giscus themeMode={themeMode} />
</Modal>
</>
);
}); });
export default Header; export default Header;

View File

@ -1,18 +1,18 @@
export const themeIcon = { export const themeIcon = {
light: ( dark: (
<span className="anticon anticon-github" role="img"> <span className="anticon anticon-github" role="img">
<svg fill="currentColor" height="1em" viewBox="0 0 16 16" width="1em"> <svg fill="currentColor" height="1em" viewBox="0 0 16 16" width="1em">
<path d="M8 13a1 1 0 0 1 1 1v1a1 1 0 1 1-2 0v-1a1 1 0 0 1 1-1ZM8 3a1 1 0 0 1-1-1V1a1 1 0 1 1 2 0v1a1 1 0 0 1-1 1Zm7 4a1 1 0 1 1 0 2h-1a1 1 0 1 1 0-2h1ZM3 8a1 1 0 0 1-1 1H1a1 1 0 1 1 0-2h1a1 1 0 0 1 1 1Zm9.95 3.536.707.707a1 1 0 0 1-1.414 1.414l-.707-.707a1 1 0 0 1 1.414-1.414Zm-9.9-7.072-.707-.707a1 1 0 0 1 1.414-1.414l.707.707A1 1 0 0 1 3.05 4.464Zm9.9 0a1 1 0 0 1-1.414-1.414l.707-.707a1 1 0 0 1 1.414 1.414l-.707.707Zm-9.9 7.072a1 1 0 0 1 1.414 1.414l-.707.707a1 1 0 0 1-1.414-1.414l.707-.707ZM8 4a4 4 0 1 0 0 8 4 4 0 0 0 0-8Zm0 6.5a2.5 2.5 0 1 1 0-5 2.5 2.5 0 0 1 0 5Z"></path> <path d="M8.218 1.455c3.527.109 6.327 3.018 6.327 6.545 0 3.6-2.945 6.545-6.545 6.545a6.562 6.562 0 0 1-6.036-4h.218c3.6 0 6.545-2.945 6.545-6.545 0-.91-.182-1.745-.509-2.545m0-1.455c-.473 0-.909.218-1.2.618-.29.4-.327.946-.145 1.382.254.655.4 1.31.4 2 0 2.8-2.291 5.09-5.091 5.09h-.218c-.473 0-.91.22-1.2.62-.291.4-.328.945-.146 1.38C1.891 14.074 4.764 16 8 16c4.4 0 8-3.6 8-8a7.972 7.972 0 0 0-7.745-8h-.037Z"></path>
</svg> </svg>
</span> </span>
), ),
dark: ( light: (
<span className="anticon anticon-github" role="img"> <span className="anticon anticon-github" role="img">
<svg fill="currentColor" height="1em" viewBox="0 0 16 16" width="1em"> <svg fill="currentColor" height="1em" viewBox="0 0 16 16" width="1em">
<path d="M8.218 1.455c3.527.109 6.327 3.018 6.327 6.545 0 3.6-2.945 6.545-6.545 6.545a6.562 6.562 0 0 1-6.036-4h.218c3.6 0 6.545-2.945 6.545-6.545 0-.91-.182-1.745-.509-2.545m0-1.455c-.473 0-.909.218-1.2.618-.29.4-.327.946-.145 1.382.254.655.4 1.31.4 2 0 2.8-2.291 5.09-5.091 5.09h-.218c-.473 0-.91.22-1.2.62-.291.4-.328.945-.146 1.38C1.891 14.074 4.764 16 8 16c4.4 0 8-3.6 8-8a7.972 7.972 0 0 0-7.745-8h-.037Z"></path> <path d="M8 13a1 1 0 0 1 1 1v1a1 1 0 1 1-2 0v-1a1 1 0 0 1 1-1ZM8 3a1 1 0 0 1-1-1V1a1 1 0 1 1 2 0v1a1 1 0 0 1-1 1Zm7 4a1 1 0 1 1 0 2h-1a1 1 0 1 1 0-2h1ZM3 8a1 1 0 0 1-1 1H1a1 1 0 1 1 0-2h1a1 1 0 0 1 1 1Zm9.95 3.536.707.707a1 1 0 0 1-1.414 1.414l-.707-.707a1 1 0 0 1 1.414-1.414Zm-9.9-7.072-.707-.707a1 1 0 0 1 1.414-1.414l.707.707A1 1 0 0 1 3.05 4.464Zm9.9 0a1 1 0 0 1-1.414-1.414l.707-.707a1 1 0 0 1 1.414 1.414l-.707.707Zm-9.9 7.072a1 1 0 0 1 1.414 1.414l-.707.707a1 1 0 0 1-1.414-1.414l.707-.707ZM8 4a4 4 0 1 0 0 8 4 4 0 0 0 0-8Zm0 6.5a2.5 2.5 0 1 1 0-5 2.5 2.5 0 0 1 0 5Z"></path>
</svg> </svg>
</span> </span>
), ),
}; };
export const darkLogo = export const darkLogo =
@ -21,9 +21,9 @@ export const lightLogo =
'https://gw.alipayobjects.com/zos/bmw-prod/e146116d-c65a-4306-a3d2-bb8d05e1c49b.svg'; 'https://gw.alipayobjects.com/zos/bmw-prod/e146116d-c65a-4306-a3d2-bb8d05e1c49b.svg';
export const civitaiLogo = ( export const civitaiLogo = (
<span className="anticon civitai" role="img"> <span className="anticon civitai" role="img">
<svg fill="currentColor" height="1em" viewBox="0 0 16 16" width="1em"> <svg fill="currentColor" height="1em" viewBox="0 0 16 16" width="1em">
<path d="M2 4.5L8 1l6 3.5v7L8 15l-6-3.5v-7zm6-1.194L3.976 5.653v4.694L8 12.694l4.024-2.347V5.653L8 3.306zm0 1.589l2.662 1.552v.824H9.25L8 6.54l-1.25.73v1.458L8 9.46l1.25-.73h1.412v.824L8 11.105 5.338 9.553V6.447L8 4.895z" /> <path d="M2 4.5L8 1l6 3.5v7L8 15l-6-3.5v-7zm6-1.194L3.976 5.653v4.694L8 12.694l4.024-2.347V5.653L8 3.306zm0 1.589l2.662 1.552v.824H9.25L8 6.54l-1.25.73v1.458L8 9.46l1.25-.73h1.412v.824L8 11.105 5.338 9.553V6.447L8 4.895z" />
</svg> </svg>
</span> </span>
); );

View File

@ -1,8 +1,8 @@
import { memo, useCallback, useState } from 'react'; import {memo, useCallback, useState} from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import TagList, { PromptType, TagItem } from './TagList'; import TagList, {PromptType, TagItem} from './TagList';
import { formatPrompt } from './utils'; import {formatPrompt} from './utils';
/****************************************************** /******************************************************
*********************** Style ************************* *********************** Style *************************
@ -14,12 +14,12 @@ const View = styled.div`
gap: 8px; gap: 8px;
`; `;
const BtnGroup = styled.div` const ButtonGroup = styled.div`
display: flex; display: flex;
gap: 8px; gap: 8px;
`; `;
const Btn = styled.button` const Button = styled.button`
cursor: pointer; cursor: pointer;
position: relative; position: relative;
@ -48,48 +48,54 @@ interface PromptProps {
type: PromptType; type: PromptType;
} }
const Prompt = memo<PromptProps>(({ type }) => { const Prompt = memo<PromptProps>(({type}) => {
const [tags, setTags] = useState<TagItem[]>([]); const [tags, setTags] = useState<TagItem[]>([]);
const id = const id =
type === 'positive' ? "[id$='2img_prompt'] textarea" : "[id$='2img_neg_prompt'] textarea"; type === 'positive' ? "[id$='2img_prompt'] textarea" : "[id$='2img_neg_prompt'] textarea";
const getValue = useCallback(() => { const getValue = useCallback(() => {
try { try {
const textarea: HTMLTextAreaElement | any = get_uiCurrentTabContent().querySelector(id); const textarea: HTMLTextAreaElement | any = get_uiCurrentTabContent().querySelector(id);
if (textarea) setTags(formatPrompt(textarea.value)); if (textarea) setTags(formatPrompt(textarea.value));
} catch {} } catch (error) {
}, []); console.log(error);
}
}, []);
const setValue = useCallback(() => { const setValue = useCallback(() => {
try { try {
const textarea: HTMLTextAreaElement | any = get_uiCurrentTabContent().querySelector(id); const textarea: HTMLTextAreaElement | any = get_uiCurrentTabContent().querySelector(id);
if (textarea) textarea.value = tags.map((t) => t.text).join(', '); if (textarea) textarea.value = tags.map((t) => t.text).join(', ');
updateInput(textarea); updateInput(textarea);
} catch {} } catch (error) {
}, [tags]); console.log(error);
}
}, [tags]);
const setCurrentValue = useCallback((currentTags: TagItem[]) => { const setCurrentValue = useCallback((currentTags: TagItem[]) => {
try { try {
const textarea: HTMLTextAreaElement | any = get_uiCurrentTabContent().querySelector(id); const textarea: HTMLTextAreaElement | any = get_uiCurrentTabContent().querySelector(id);
if (textarea) textarea.value = currentTags.map((t) => t.text).join(', '); if (textarea) textarea.value = currentTags.map((t) => t.text).join(', ');
updateInput(textarea); updateInput(textarea);
} catch {} } catch (error) {
}, []); console.log(error);
}
}, []);
return ( return (
<View> <View>
<TagList setTags={setTags} setValue={setCurrentValue} tags={tags} type={type} /> <TagList setTags={setTags} setValue={setCurrentValue} tags={tags} type={type} />
<BtnGroup> <ButtonGroup>
<Btn onClick={getValue} title="Load Prompt"> <Button onClick={getValue} title="Load Prompt">
🔄 🔄
</Btn> </Button>
<Btn onClick={setValue} title="Set Prompt"> <Button onClick={setValue} title="Set Prompt">
</Btn> </Button>
</BtnGroup> </ButtonGroup>
</View> </View>
); );
}); });
export default Prompt; export default Prompt;

View File

@ -1,8 +1,8 @@
import { type FC, memo, useCallback, useMemo } from 'react'; import {type FC, memo, useCallback, useMemo} from 'react';
import { WithContext, ReactTagsProps as WithContextProps } from 'react-tag-input'; import {WithContext, ReactTagsProps as WithContextProps} from 'react-tag-input';
import styled from 'styled-components'; import styled from 'styled-components';
import { genTagType, suggestions } from './utils'; import {genTagType, suggestions} from './utils';
export interface TagItem { export interface TagItem {
className?: string; className?: string;
@ -73,14 +73,14 @@ const View = styled.div<{ type: PromptType }>`
font-size: var(--text-sm); font-size: var(--text-sm);
font-weight: var(--input-text-weight); font-weight: var(--input-text-weight);
line-height: var(--line-sm); line-height: var(--line-sm);
color: ${({ type }) => (type === 'positive' ? 'var(--green-9)' : 'var(--magenta-9)')}; color: ${({type}) => (type === 'positive' ? 'var(--green-9)' : 'var(--magenta-9)')};
background: var(--button-secondary-background-fill); background: var(--button-secondary-background-fill);
border: var(--button-border-width) solid var(--button-secondary-border-color); border: var(--button-border-width) solid var(--button-secondary-border-color);
border-radius: var(--input-radius); border-radius: var(--input-radius);
&:hover { &:hover {
color: ${({ type }) => (type === 'positive' ? 'var(--green-10)' : 'var(--magenta-10)')}; color: ${({type}) => (type === 'positive' ? 'var(--green-10)' : 'var(--magenta-10)')};
} }
} }
@ -155,8 +155,8 @@ const View = styled.div<{ type: PromptType }>`
`; `;
const KeyCodes = { const KeyCodes = {
comma: 188, comma: 188,
enter: 13, enter: 13,
}; };
const delimiters = [KeyCodes.comma, KeyCodes.enter]; const delimiters = [KeyCodes.comma, KeyCodes.enter];
@ -168,65 +168,65 @@ interface TagListProps {
type: PromptType; type: PromptType;
} }
const TagList = memo<TagListProps>(({ tags, setTags, type, setValue }) => { const TagList = memo<TagListProps>(({tags, setTags, type, setValue}) => {
const handleDelete = useCallback( const handleDelete = useCallback(
(i: number) => { (index_: number) => {
const newTags = tags.filter((tag, index) => index !== i); const newTags = tags.filter((tag, index) => index !== index_);
setTags(newTags); setTags(newTags);
setValue(newTags); setValue(newTags);
}, },
[tags], [tags],
); );
const handleAddition = useCallback( const handleAddition = useCallback(
(tag: TagItem) => { (tag: TagItem) => {
const newTags = [...tags, genTagType(tag)]; const newTags = [...tags, genTagType(tag)];
setTags(newTags); setTags(newTags);
setValue(newTags); setValue(newTags);
}, },
[tags], [tags],
); );
const handleDrag = useCallback( const handleDrag = useCallback(
(tag: TagItem, currPos: number, newPos: number) => { (tag: TagItem, currentPos: number, newPos: number) => {
const newTags = tags.slice(); const newTags = [...tags];
newTags.splice(currPos, 1); newTags.splice(currentPos, 1);
newTags.splice(newPos, 0, genTagType(tag)); newTags.splice(newPos, 0, genTagType(tag));
setTags(newTags); setTags(newTags);
setValue(newTags); setValue(newTags);
}, },
[tags], [tags],
); );
const handleTagUpdate = useCallback( const handleTagUpdate = useCallback(
(i: number, tag: TagItem) => { (index: number, tag: TagItem) => {
const newTags = [...tags]; const newTags = [...tags];
newTags[i] = genTagType(tag); newTags[index] = genTagType(tag);
setTags(newTags); setTags(newTags);
setValue(newTags); setValue(newTags);
}, },
[tags], [tags],
); );
const suggestionData = useMemo(() => suggestions[type], [type]); const suggestionData = useMemo(() => suggestions[type], [type]);
return ( return (
<View type={type}> <View type={type}>
<ReactTags <ReactTags
autocomplete autocomplete
delimiters={delimiters} delimiters={delimiters}
editable editable
handleAddition={handleAddition} handleAddition={handleAddition}
handleDelete={handleDelete} handleDelete={handleDelete}
handleDrag={handleDrag} handleDrag={handleDrag}
inline inline
inputFieldPosition="bottom" inputFieldPosition="bottom"
onTagUpdate={handleTagUpdate} onTagUpdate={handleTagUpdate}
suggestions={suggestionData} suggestions={suggestionData}
tags={tags} tags={tags}
/> />
</View> </View>
); );
}); });
export default TagList; export default TagList;

View File

@ -1,48 +1,48 @@
import negativeData from '@/data/negative.json'; import negativeData from '@/data/negative.json';
import positiveData from '@/data/positive.json'; import positiveData from '@/data/positive.json';
import { Converter } from '@/script/formatPrompt'; import {Converter} from '@/script/formatPrompt';
import { TagItem } from './TagList'; import {TagItem} from './TagList';
export const genTagType = (tag: TagItem): TagItem => { export const genTagType = (tag: TagItem): TagItem => {
const newTag = tag; const newTag = tag;
if (newTag.text.includes('<lora')) { if (newTag.text.includes('<lora')) {
newTag.className = 'ReactTags__lora'; newTag.className = 'ReactTags__lora';
} else if (newTag.text.includes('<hypernet')) { } else if (newTag.text.includes('<hypernet')) {
newTag.className = 'ReactTags__hypernet'; newTag.className = 'ReactTags__hypernet';
} else if (newTag.text.includes('<embedding')) { } else if (newTag.text.includes('<embedding')) {
newTag.className = 'ReactTags__embedding'; newTag.className = 'ReactTags__embedding';
} else { } else {
newTag.className = undefined; newTag.className = undefined;
} }
return newTag; return newTag;
}; };
export const formatPrompt = (value: string) => { export const formatPrompt = (value: string) => {
const text = Converter.convertStr(value); const text = Converter.convertStr(value);
const textArray = Converter.convertStr2Array(text).map((item) => { const textArray = Converter.convertStr2Array(text).map((item) => {
if (item.includes('<')) return item; if (item.includes('<')) return item;
const newItem = item const newItem = item
.replace(/\s+/g, ' ') .replaceAll(/\s+/g, ' ')
.replace(/|\.\|。/g, ',') .replaceAll(/|\.\|。/g, ',')
.replace(/“||”|"|\/'/g, '') .replaceAll(/“||”|"|\/'/g, '')
.replace(/, /g, ',') .replaceAll(', ', ',')
.replace(/,,/g, ',') .replaceAll(',,', ',')
.replace(/,/g, ', '); .replaceAll(',', ', ');
return Converter.convertStr2Array(newItem).join(', '); return Converter.convertStr2Array(newItem).join(', ');
}); });
return textArray.map((tag) => genTagType({ id: tag, text: tag })); return textArray.map((tag) => genTagType({id: tag, text: tag}));
}; };
const genSuggestions = (array: string[]) => const genSuggestions = (array: string[]) =>
array.map((text) => { array.map((text) => {
return { return {
id: text, id: text,
text, text,
}; };
}); });
export const suggestions = { export const suggestions = {
positive: genSuggestions(positiveData), negative: genSuggestions(negativeData),
negative: genSuggestions(negativeData), positive: genSuggestions(positiveData),
}; };

View File

@ -1,29 +1,29 @@
import { useEffect, useState } from 'react'; import {useEffect, useState} from 'react';
function checkIsDarkMode() { function checkIsDarkMode() {
try { try {
return window.matchMedia('(prefers-color-scheme: dark)').matches; return window.matchMedia('(prefers-color-scheme: dark)').matches;
} catch (err) { } catch {
return false; return false;
} }
} }
export function useIsDarkMode() { export function useIsDarkMode() {
const [isDarkMode, setIsDarkMode] = useState(checkIsDarkMode()); const [isDarkMode, setIsDarkMode] = useState(checkIsDarkMode());
useEffect(() => { useEffect(() => {
const mqList = window.matchMedia('(prefers-color-scheme: dark)'); const mqList = window.matchMedia('(prefers-color-scheme: dark)');
const listener = (event: any) => { const listener = (event: any) => {
setIsDarkMode(event.matches); setIsDarkMode(event.matches);
}; };
mqList.addEventListener('change', listener); mqList.addEventListener('change', listener);
return () => { return () => {
mqList.removeEventListener('change', listener); mqList.removeEventListener('change', listener);
}; };
}, []); }, []);
return isDarkMode; return isDarkMode;
} }

View File

@ -1,14 +1,14 @@
import { Spin } from 'antd'; import {Spin} from 'antd';
import { useResponsive } from 'antd-style'; import {useResponsive} from 'antd-style';
import { memo, useEffect, useRef, useState } from 'react'; import {memo, useEffect, useRef, useState} from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { shallow } from 'zustand/shallow'; import {shallow} from 'zustand/shallow';
import { Content, ExtraNetworkSidebar, Header, Sidebar } from '@/components'; import {Content, ExtraNetworkSidebar, Header, Sidebar} from '@/components';
import civitaiHelperFix from '@/script/civitaiHelperFix'; import civitaiHelperFix from '@/script/civitaiHelperFix';
import dragablePanel from '@/script/draggablePanel'; import dragablePanel from '@/script/draggablePanel';
import replaceIcon from '@/script/replaceIcon'; import replaceIcon from '@/script/replaceIcon';
import { useAppStore } from '@/store'; import {useAppStore} from '@/store';
const View = styled.div` const View = styled.div`
position: relative; position: relative;
@ -41,124 +41,126 @@ const LoadingBox = styled.div`
`; `;
const App = memo(() => { const App = memo(() => {
const [currentTab, setCurrentTab, setting] = useAppStore( const [currentTab, setCurrentTab, setting] = useAppStore(
(st) => [st.currentTab, st.setCurrentTab, st.setting], (st) => [st.currentTab, st.setCurrentTab, st.setting],
shallow, shallow,
); );
const { mobile } = useResponsive(); const {mobile} = useResponsive();
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [extraLoading, setExtraLoading] = useState(true); const [extraLoading, setExtraLoading] = useState(true);
const sidebarRef: any = useRef<HTMLElement>(); const sidebarReference: any = useRef<HTMLElement>();
const mainRef: any = useRef<HTMLElement>(); const mainReference: any = useRef<HTMLElement>();
const txt2imgExtraNetworkSidebarRef: any = useRef<HTMLElement>(); const txt2imgExtraNetworkSidebarReference: any = useRef<HTMLElement>();
const img2imgExtraNetworkSidebarRef: any = useRef<HTMLElement>(); const img2imgExtraNetworkSidebarReference: any = useRef<HTMLElement>();
useEffect(() => { useEffect(() => {
onUiLoaded(() => { onUiLoaded(() => {
// Content // Content
const main = gradioApp().querySelector('.app'); const main = gradioApp().querySelector('.app');
if (main) mainRef.current?.appendChild(main); if (main) mainReference.current?.append(main);
if (!mobile) dragablePanel(); if (!mobile) dragablePanel();
// Sidebar // Sidebar
const sidebar = gradioApp().querySelector('#quicksettings'); const sidebar = gradioApp().querySelector('#quicksettings');
if (sidebar) sidebarRef.current?.appendChild(sidebar); if (sidebar) sidebarReference.current?.append(sidebar);
// ExtraNetworkSidebar // ExtraNetworkSidebar
if (setting.enableExtraNetworkSidebar) { if (setting.enableExtraNetworkSidebar) {
const txt2imgExtraNetworks = gradioApp().querySelector('div#txt2img_extra_networks'); const txt2imgExtraNetworks = gradioApp().querySelector('div#txt2img_extra_networks');
const img2imgExtraNetworks = gradioApp().querySelector('div#img2img_extra_networks'); const img2imgExtraNetworks = gradioApp().querySelector('div#img2img_extra_networks');
if (txt2imgExtraNetworks && img2imgExtraNetworks) { if (txt2imgExtraNetworks && img2imgExtraNetworks) {
txt2imgExtraNetworkSidebarRef.current?.appendChild(txt2imgExtraNetworks); txt2imgExtraNetworkSidebarReference.current?.append(txt2imgExtraNetworks);
img2imgExtraNetworkSidebarRef.current?.appendChild(img2imgExtraNetworks); img2imgExtraNetworkSidebarReference.current?.append(img2imgExtraNetworks);
}
}
// Other
if (setting.svgIcon) replaceIcon();
setLoading(false);
});
onUiUpdate(() => {
setCurrentTab();
});
}, []);
useEffect(() => {
if (!loading && setting.enableExtraNetworkSidebar) {
if (document.querySelector('#txt2img_lora_cards')) {
civitaiHelperFix();
setExtraLoading(false);
return;
}
setTimeout(() => {
const t2indexButton: any = document.querySelector('#txt2img_extra_refresh');
const index2indexButton: any = document.querySelector('#img2img_extra_refresh');
t2indexButton.click();
index2indexButton.click();
setExtraLoading(false);
try {
const civitaiButton = document.querySelectorAll(
'button[title="Refresh Civitai Helper\'s additional buttons"]',
);
if (civitaiButton) {
civitaiButton.forEach((button: any) => (button.onclick = civitaiHelperFix));
}
civitaiHelperFix();
} catch (error) {
console.log(error);
}
}, 2000);
} }
} }, [loading]);
// Other return (
if (setting.svgIcon) replaceIcon(); <MainView>
<Header>
setLoading(false); {loading && (
}); <LoadingBox>
onUiUpdate(() => { <Spin size="small" />
setCurrentTab(); </LoadingBox>
}); )}
}, []); </Header>
<View>
useEffect(() => { <Sidebar>
if (!loading && setting.enableExtraNetworkSidebar) { {loading && (
if (document.querySelector('#txt2img_lora_cards')) { <LoadingBox>
civitaiHelperFix(); <Spin size="small" />
setExtraLoading(false); </LoadingBox>
return; )}
} <div id="sidebar" ref={sidebarReference} style={loading ? {display: 'none'} : {}} />
setTimeout(() => { </Sidebar>
const t2iBtn: any = document.querySelector('#txt2img_extra_refresh'); <Content loading={loading}>
const i2iBtn: any = document.querySelector('#img2img_extra_refresh'); {loading && (
t2iBtn.click(); <LoadingBox>
i2iBtn.click(); <Spin size="large" tip="Loading" />
setExtraLoading(false); </LoadingBox>
try { )}
const civitaiBtn = document.querySelectorAll( <div id="content" ref={mainReference} style={loading ? {display: 'none'} : {}} />
'button[title="Refresh Civitai Helper\'s additional buttons"]', </Content>
); {setting?.enableExtraNetworkSidebar && (
if (civitaiBtn) { <ExtraNetworkSidebar>
civitaiBtn.forEach((btn: any) => (btn.onclick = civitaiHelperFix)); {extraLoading && (
} <LoadingBox>
civitaiHelperFix(); <Spin size="small" />
} catch {} </LoadingBox>
}, 2000); )}
} <div style={extraLoading ? {display: 'none'} : {}}>
}, [loading]); <div
id="txt2img-extra-netwrok-sidebar"
return ( ref={txt2imgExtraNetworkSidebarReference}
<MainView> style={currentTab === 'tab_img2img' ? {display: 'none'} : {}}
<Header> />
{loading && ( <div
<LoadingBox> id="img2img-extra-netwrok-sidebar"
<Spin size="small" /> ref={img2imgExtraNetworkSidebarReference}
</LoadingBox> style={currentTab === 'tab_img2img' ? {} : {display: 'none'}}
)} />
</Header> </div>
<View> </ExtraNetworkSidebar>
<Sidebar> )}
{loading && ( </View>
<LoadingBox> </MainView>
<Spin size="small" /> );
</LoadingBox>
)}
<div id="sidebar" ref={sidebarRef} style={loading ? { display: 'none' } : {}} />
</Sidebar>
<Content loading={loading}>
{loading && (
<LoadingBox>
<Spin size="large" tip="Loading" />
</LoadingBox>
)}
<div id="content" ref={mainRef} style={loading ? { display: 'none' } : {}} />
</Content>
{setting?.enableExtraNetworkSidebar && (
<ExtraNetworkSidebar>
{extraLoading && (
<LoadingBox>
<Spin size="small" />
</LoadingBox>
)}
<div style={extraLoading ? { display: 'none' } : {}}>
<div
id="txt2img-extra-netwrok-sidebar"
ref={txt2imgExtraNetworkSidebarRef}
style={currentTab !== 'tab_img2img' ? {} : { display: 'none' }}
/>
<div
id="img2img-extra-netwrok-sidebar"
ref={img2imgExtraNetworkSidebarRef}
style={currentTab === 'tab_img2img' ? {} : { display: 'none' }}
/>
</div>
</ExtraNetworkSidebar>
)}
</View>
</MainView>
);
}); });
export default App; export default App;

View File

@ -1,81 +1,81 @@
import { ThemeProvider, setupStyled } from 'antd-style'; import {ThemeProvider, setupStyled} from 'antd-style';
import qs from 'query-string'; import qs from 'query-string';
import { memo, useEffect, useState } from 'react'; import {memo, useEffect, useState} from 'react';
import { createRoot } from 'react-dom/client'; import {createRoot} from 'react-dom/client';
// @ts-ignore // @ts-ignore
import ReactFontLoader from 'react-font-loader'; import ReactFontLoader from 'react-font-loader';
import { ThemeContext } from 'styled-components'; import {ThemeContext} from 'styled-components';
import { shallow } from 'zustand/shallow'; import {shallow} from 'zustand/shallow';
import { useIsDarkMode } from '@/components/theme/useIsDarkMode'; import {useIsDarkMode} from '@/components/theme/useIsDarkMode';
import formatPrompt from '@/script/formatPrompt'; import formatPrompt from '@/script/formatPrompt';
import promptBracketChecker from '@/script/promptBracketChecker'; import promptBracketChecker from '@/script/promptBracketChecker';
import setupHead from '@/script/setupHead'; import setupHead from '@/script/setupHead';
import { useAppStore } from '@/store'; import {useAppStore} from '@/store';
import '@/theme/style.less'; import '@/theme/style.less';
import App from './App'; import App from './App';
import GlobalStyle from './GlobalStyle'; import GlobalStyle from './GlobalStyle';
import { baseToken } from './style'; import {baseToken} from './style';
const Root = memo(() => { const Root = memo(() => {
setupStyled({ ThemeContext }); setupStyled({ThemeContext});
const [onSetThemeMode, onInit] = useAppStore((st) => [st.onSetThemeMode, st.onInit], shallow); const [onSetThemeMode, onInit] = useAppStore((st) => [st.onSetThemeMode, st.onInit], shallow);
const isDarkMode = useIsDarkMode(); const isDarkMode = useIsDarkMode();
const [appearance, setAppearance] = useState<'light' | 'dark'>('light'); const [appearance, setAppearance] = useState<'light' | 'dark'>('light');
const [first, setFirst] = useState(true); const [first, setFirst] = useState(true);
useEffect(() => { useEffect(() => {
onInit(); onInit();
}, []); }, []);
useEffect(() => { useEffect(() => {
const queryTheme: any = String(qs.parseUrl(window.location.href).query.__theme || ''); const queryTheme: any = String(qs.parseUrl(window.location.href).query.__theme || '');
if (queryTheme) { if (queryTheme) {
setAppearance(queryTheme as any); setAppearance(queryTheme as any);
document.body.classList.add(queryTheme); document.body.classList.add(queryTheme);
onSetThemeMode(queryTheme); onSetThemeMode(queryTheme);
return; return;
} }
setAppearance(isDarkMode ? 'dark' : 'light'); setAppearance(isDarkMode ? 'dark' : 'light');
document.body.classList.add(isDarkMode ? 'dark' : 'light'); document.body.classList.add(isDarkMode ? 'dark' : 'light');
onSetThemeMode(isDarkMode ? 'dark' : 'light'); onSetThemeMode(isDarkMode ? 'dark' : 'light');
}, [isDarkMode]); }, [isDarkMode]);
useEffect(() => { useEffect(() => {
if (first) { if (first) {
setFirst(false); setFirst(false);
return; return;
} }
window.location.reload(); window.location.reload();
}, [isDarkMode]); }, [isDarkMode]);
return ( return (
<ThemeProvider appearance={appearance} theme={{ token: baseToken }}> <ThemeProvider appearance={appearance} theme={{token: baseToken}}>
<GlobalStyle /> <GlobalStyle />
<ReactFontLoader url="https://raw.githubusercontent.com/IKKI2000/harmonyos-fonts/main/css/harmonyos_sans.css" /> <ReactFontLoader url="https://raw.githubusercontent.com/IKKI2000/harmonyos-fonts/main/css/harmonyos_sans.css" />
<ReactFontLoader url="https://raw.githubusercontent.com/IKKI2000/harmonyos-fonts/main/css/harmonyos_sans_sc.css" /> <ReactFontLoader url="https://raw.githubusercontent.com/IKKI2000/harmonyos-fonts/main/css/harmonyos_sans_sc.css" />
<App /> <App />
</ThemeProvider> </ThemeProvider>
); );
}); });
document.addEventListener( document.addEventListener(
'DOMContentLoaded', 'DOMContentLoaded',
() => { () => {
setupHead(); setupHead();
const root = document.createElement('div'); const root = document.createElement('div');
root.setAttribute('id', 'root'); root.setAttribute('id', 'root');
try { try {
gradioApp()?.append(root); gradioApp()?.append(root);
} catch { } catch {
document.querySelector('gradio-app')?.append(root); document.querySelector('gradio-app')?.append(root);
} }
const client = createRoot(root); const client = createRoot(root);
client.render(<Root />); client.render(<Root />);
}, },
{ once: true }, {once: true},
); );
onUiLoaded(() => { onUiLoaded(() => {
formatPrompt(); formatPrompt();
promptBracketChecker(); promptBracketChecker();
}); });
export default () => null; export default () => false;

View File

@ -1,17 +1,17 @@
const TAB_PREFIX_LIST = ['txt2img', 'img2img']; const TAB_PREFIX_LIST = ['txt2img', 'img2img'];
const MODEL_TYPE_LIST: ['textual_inversion', 'hypernetworks', 'checkpoints', 'lora', 'lycoris'] = [ const MODEL_TYPE_LIST: ['textual_inversion', 'hypernetworks', 'checkpoints', 'lora', 'lycoris'] = [
'textual_inversion', 'textual_inversion',
'hypernetworks', 'hypernetworks',
'checkpoints', 'checkpoints',
'lora', 'lora',
'lycoris', 'lycoris',
]; ];
const MODEL_TYPE = { const MODEL_TYPE = {
textual_inversion: 'ti', checkpoints: 'ckp',
hypernetworks: 'hyper', hypernetworks: 'hyper',
checkpoints: 'ckp', lora: 'lora',
lora: 'lora', lycoris: 'lycoris',
lycoris: 'lycoris', textual_inversion: 'ti',
}; };
const CARDID_SUFFIX = 'cards'; const CARDID_SUFFIX = 'cards';
@ -23,189 +23,189 @@ const BTN_THUMB_DISPLAY = 'inline';
const BTN_THUMB_POS = 'static'; const BTN_THUMB_POS = 'static';
const BTN_THUMB_BACKGROUND_IMAGE = 'none'; const BTN_THUMB_BACKGROUND_IMAGE = 'none';
const BTN_THUMB_BACKGROUND = 'rgba(0, 0, 0, 0.8)'; const BTN_THUMB_BACKGROUND = 'rgba(0, 0, 0, 0.8)';
const CH_BTN_TXTS = ['🌐', '💡', '🏷️']; const CH_BTN_TXTS = new Set(['🌐', '💡', '🏷️']);
const styleBtn = (node: HTMLElement, isThumbMode: boolean) => { const styleButton = (node: HTMLElement, isThumbMode: boolean) => {
if (!isThumbMode) { if (isThumbMode) {
node.style.fontSize = BTN_FONT_SIZE; node.style.display = BTN_THUMB_DISPLAY;
node.style.margin = BTN_MARGIN; node.style.fontSize = BTN_THUMB_FONT_SIZE;
} else { node.style.position = BTN_THUMB_POS;
node.style.display = BTN_THUMB_DISPLAY; node.style.backgroundImage = BTN_THUMB_BACKGROUND_IMAGE;
node.style.fontSize = BTN_THUMB_FONT_SIZE; } else {
node.style.position = BTN_THUMB_POS; node.style.fontSize = BTN_FONT_SIZE;
node.style.backgroundImage = BTN_THUMB_BACKGROUND_IMAGE; node.style.margin = BTN_MARGIN;
} }
}; };
const updateCardForCivitai = () => { const updateCardForCivitai = () => {
if (!gradioApp().querySelector('#tab_civitai_helper')) return; if (!gradioApp().querySelector('#tab_civitai_helper')) return;
const replacePreviewText = getTranslation('replace preview') || 'replace preview'; const replacePreviewText = getTranslation('replace preview') || 'replace preview';
// Get component // Get component
const chAlwaysDisplayCkb = gradioApp().querySelector( const chAlwaysDisplayCkb = gradioApp().querySelector(
'#ch_always_display_ckb input', '#ch_always_display_ckb input',
) as HTMLInputElement; ) as HTMLInputElement;
const chShowBtnOnThumbCkb = gradioApp().querySelector( const chShowButtonOnThumbCkb = gradioApp().querySelector(
'#ch_show_btn_on_thumb_ckb input', '#ch_show_btn_on_thumb_ckb input',
) as HTMLInputElement; ) as HTMLInputElement;
const chAlwaysDisplay = chAlwaysDisplayCkb?.checked || false; const chAlwaysDisplay = chAlwaysDisplayCkb?.checked || false;
const chShowBtnOnThumb = chShowBtnOnThumbCkb?.checked || false; const chShowButtonOnThumb = chShowButtonOnThumbCkb?.checked || false;
// Change all "replace preview" into an icon // Change all "replace preview" into an icon
let extraNetworkId = ''; let extraNetworkId = '';
let extraNetworkNode: any = null; let extraNetworkNode: any;
let metadataButton: any = null; let metadataButton: any;
let additionalNode: any = null; let additionalNode: any;
let replacePreviewBtn: any = null; let replacePreviewButton: any;
let ulNode: any = null; let ulNode: any;
let searchTermNode: any = null; let searchTermNode: any;
let searchTerm = ''; let searchTerm = '';
let modelType = ''; let modelType = '';
let cards = null; let cards;
let needToAddButtons = false; let needToAddButtons = false;
let isThumbMode = false; let isThumbMode = false;
// Get current tab // Get current tab
TAB_PREFIX_LIST.forEach((activeTabType) => { for (const activeTabType of TAB_PREFIX_LIST) {
MODEL_TYPE_LIST.forEach((jsModelType) => { for (const jsModelType of MODEL_TYPE_LIST) {
modelType = MODEL_TYPE[jsModelType]; modelType = MODEL_TYPE[jsModelType];
// Get model_type for python side // Get model_type for python side
extraNetworkId = `${activeTabType}_${jsModelType}_${CARDID_SUFFIX}`; extraNetworkId = `${activeTabType}_${jsModelType}_${CARDID_SUFFIX}`;
extraNetworkNode = gradioApp().getElementById(extraNetworkId); extraNetworkNode = gradioApp().querySelector(`#${extraNetworkId}`);
// Check if extra network node exists // Check if extra network node exists
if (extraNetworkNode === null) return; if (extraNetworkNode === undefined) continue;
// Check if extr network is under thumbnail mode // Check if extr network is under thumbnail mode
isThumbMode = false; isThumbMode = false;
if (extraNetworkNode?.className === 'extra-network-thumbs') isThumbMode = true; if (extraNetworkNode?.className === 'extra-network-thumbs') isThumbMode = true;
// Get all card nodes // Get all card nodes
cards = extraNetworkNode.querySelectorAll('.card'); cards = extraNetworkNode.querySelectorAll('.card');
for (const card of cards) { for (const card of cards) {
if (card.querySelectorAll('.actions .additional a').length > 2) return; if (card.querySelectorAll('.actions .additional a').length > 2) continue;
// Metadata_buttoncard // Metadata_buttoncard
metadataButton = card.querySelector('.metadata-button'); metadataButton = card.querySelector('.metadata-button');
// Additional node // Additional node
additionalNode = card.querySelector('.actions .additional'); additionalNode = card.querySelector('.actions .additional');
// Get ul node, which is the parent of all buttons // Get ul node, which is the parent of all buttons
ulNode = card.querySelector('.actions .additional ul'); ulNode = card.querySelector('.actions .additional ul');
// Replace preview text button // Replace preview text button
replacePreviewBtn = card.querySelector('.actions .additional a'); replacePreviewButton = card.querySelector('.actions .additional a');
// Check thumb mode // Check thumb mode
if (isThumbMode && additionalNode) { if (isThumbMode && additionalNode) {
additionalNode.style.display = null; additionalNode.style.display = undefined;
if (chShowBtnOnThumb) { if (chShowButtonOnThumb) {
ulNode.style.background = BTN_THUMB_BACKGROUND; ulNode.style.background = BTN_THUMB_BACKGROUND;
} else { } else {
// Reset // Reset
ulNode.style.background = null; ulNode.style.background = undefined;
// Remove existed buttons // Remove existed buttons
if (ulNode) { if (ulNode) {
// Find all .a child nodes // Find all .a child nodes
const atags = ulNode.querySelectorAll('a'); const atags = ulNode.querySelectorAll('a');
for (const atag of atags) { for (const atag of atags) {
// Reset display // Reset display
atag.style.display = null; atag.style.display = undefined;
// Remove extension's button // Remove extension's button
if (CH_BTN_TXTS.indexOf(atag.innerHTML) >= 0) { if (CH_BTN_TXTS.has(atag.innerHTML)) {
// Need to remove // Need to remove
ulNode.removeChild(atag); atag.remove();
} else {
// Do not remove, just reset
atag.innerHTML = replacePreviewText;
atag.style.display = undefined;
atag.style.fontSize = undefined;
atag.style.position = undefined;
atag.style.backgroundImage = undefined;
}
}
// Also remove br tag in ul
const brtag = ulNode.querySelector('br');
if (brtag) brtag.remove();
}
// Just reset and remove nodes, do nothing else
continue;
}
} else { } else {
// Do not remove, just reset // Full preview mode
atag.innerHTML = replacePreviewText; additionalNode.style.display = chAlwaysDisplay ? 'block' : undefined;
atag.style.display = null;
atag.style.fontSize = null; // Remove br tag
atag.style.position = null; const brtag = ulNode.querySelector('br');
atag.style.backgroundImage = null; if (brtag) brtag.remove();
} }
}
// Also remove br tag in ul // Change replace preview text button into icon
const brtag = ulNode.querySelector('br'); if (replacePreviewButton?.innerHTML !== '🖼️') {
if (brtag) ulNode.removeChild(brtag); needToAddButtons = true;
replacePreviewButton.innerHTML = '🖼️';
styleButton(replacePreviewButton, isThumbMode);
}
if (!needToAddButtons) continue;
// Search_term node
// Search_term = subfolder path + model name + ext
searchTermNode = card.querySelector('.actions .additional .search_term');
if (!searchTermNode) return;
// Get search_term
searchTerm = searchTermNode.innerHTML;
if (!searchTerm) continue;
// Then we need to add 3 buttons to each ul node:
const openUrlNode = document.createElement('a');
openUrlNode.href = '#';
openUrlNode.innerHTML = '🌐';
styleButton(openUrlNode, isThumbMode);
openUrlNode.title = "Open this model's civitai url";
openUrlNode.setAttribute(
'onclick',
`open_model_url(event, '${modelType}', '${searchTerm}')`,
);
const addTriggerWordsNode = document.createElement('a');
addTriggerWordsNode.href = '#';
addTriggerWordsNode.innerHTML = '💡';
styleButton(addTriggerWordsNode, isThumbMode);
addTriggerWordsNode.title = 'Add trigger words to prompt';
addTriggerWordsNode.setAttribute(
'onclick',
`add_trigger_words(event, '${modelType}', '${searchTerm}')`,
);
const usePreviewPromptNode = document.createElement('a');
usePreviewPromptNode.href = '#';
usePreviewPromptNode.innerHTML = '🏷️';
styleButton(usePreviewPromptNode, isThumbMode);
usePreviewPromptNode.title = 'Use prompt from preview image';
usePreviewPromptNode.setAttribute(
'onclick',
`use_preview_prompt(event, '${modelType}', '${searchTerm}')`,
);
// Add to card
ulNode.append(openUrlNode);
// Add br if metadata_button exists
if (isThumbMode && metadataButton) ulNode.append(document.createElement('br'));
ulNode.append(addTriggerWordsNode);
ulNode.append(usePreviewPromptNode);
} }
// Just reset and remove nodes, do nothing else
return;
}
} else {
// Full preview mode
additionalNode.style.display = chAlwaysDisplay ? 'block' : null;
// Remove br tag
const brtag = ulNode.querySelector('br');
if (brtag) ulNode.removeChild(brtag);
} }
}
// Change replace preview text button into icon
if (replacePreviewBtn?.innerHTML !== '🖼️') {
needToAddButtons = true;
replacePreviewBtn.innerHTML = '🖼️';
styleBtn(replacePreviewBtn, isThumbMode);
}
if (!needToAddButtons) return;
// Search_term node
// Search_term = subfolder path + model name + ext
searchTermNode = card.querySelector('.actions .additional .search_term');
if (!searchTermNode) return;
// Get search_term
searchTerm = searchTermNode.innerHTML;
if (!searchTerm) return;
// Then we need to add 3 buttons to each ul node:
const openUrlNode = document.createElement('a');
openUrlNode.href = '#';
openUrlNode.innerHTML = '🌐';
styleBtn(openUrlNode, isThumbMode);
openUrlNode.title = "Open this model's civitai url";
openUrlNode.setAttribute(
'onclick',
`open_model_url(event, '${modelType}', '${searchTerm}')`,
);
const addTriggerWordsNode = document.createElement('a');
addTriggerWordsNode.href = '#';
addTriggerWordsNode.innerHTML = '💡';
styleBtn(addTriggerWordsNode, isThumbMode);
addTriggerWordsNode.title = 'Add trigger words to prompt';
addTriggerWordsNode.setAttribute(
'onclick',
`add_trigger_words(event, '${modelType}', '${searchTerm}')`,
);
const usePreviewPromptNode = document.createElement('a');
usePreviewPromptNode.href = '#';
usePreviewPromptNode.innerHTML = '🏷️';
styleBtn(usePreviewPromptNode, isThumbMode);
usePreviewPromptNode.title = 'Use prompt from preview image';
usePreviewPromptNode.setAttribute(
'onclick',
`use_preview_prompt(event, '${modelType}', '${searchTerm}')`,
);
// Add to card
ulNode.appendChild(openUrlNode);
// Add br if metadata_button exists
if (isThumbMode && metadataButton) ulNode.appendChild(document.createElement('br'));
ulNode.appendChild(addTriggerWordsNode);
ulNode.appendChild(usePreviewPromptNode);
}
});
});
}; };
export default () => { export default () => {
const fixInterval = setInterval(() => { const fixInterval = setInterval(() => {
const checkDom = document.querySelector('#txt2img_lora_cards'); const checkDom = document.querySelector('#txt2img_lora_cards');
if (checkDom) { if (checkDom) {
updateCardForCivitai(); updateCardForCivitai();
clearInterval(fixInterval); clearInterval(fixInterval);
} }
}, 1000); }, 1000);
}; };

View File

@ -1,67 +1,67 @@
const MIN_WIDTH = 240; const MIN_WIDTH = 240;
const addDraggable = (tabId: string) => { const addDraggable = (tabId: string) => {
const settings = document.getElementById(`${tabId}_settings`); const settings: any = document.querySelector(`#${tabId}_settings`);
const checkDraggableLine = document.querySelector(`#tab_${tabId} .draggable-line`); const checkDraggableLine = document.querySelector(`#tab_${tabId} .draggable-line`);
if (!settings || checkDraggableLine) return; if (!settings || checkDraggableLine) return;
settings.style.minWidth = `min(${MIN_WIDTH}px, 100%)`; settings.style.minWidth = `min(${MIN_WIDTH}px, 100%)`;
const lineWrapper = document.createElement('div'); const lineWrapper = document.createElement('div');
lineWrapper.classList.add('draggable-line'); lineWrapper.classList.add('draggable-line');
settings.insertAdjacentElement('afterend', lineWrapper); settings.after(lineWrapper);
const container: HTMLElement | any = settings.parentElement; const container: HTMLElement | any = settings.parentElement;
container.classList.add('draggable-container'); container.classList.add('draggable-container');
const results = document.getElementById(`${tabId}_results`); const results: any = document.querySelector(`#${tabId}_results`);
if (!results) return; if (!results) return;
results.style.minWidth = `${MIN_WIDTH}px`; results.style.minWidth = `${MIN_WIDTH}px`;
let linePosition = 50;
settings.style.flexBasis = `${linePosition}%`;
results.style.flexBasis = `${100 - linePosition}%`;
let isDragging = false;
lineWrapper.addEventListener('mousedown', (e) => {
isDragging = true;
e.preventDefault();
});
document.addEventListener('mousemove', (event) => {
if (!isDragging) return;
const tab: HTMLElement | any = document.querySelector(`#tab_${tabId}`);
if (!tab) return;
let offsetX = tab.offsetLeft;
let parent = tab.offsetParent;
while (parent) {
offsetX += parent.offsetLeft;
parent = parent.offsetParent;
}
const containerWidth = container.offsetWidth;
const mouseX = event.clientX;
const linePosition = ((mouseX - offsetX) / containerWidth) * 100;
if (linePosition <= (MIN_WIDTH / containerWidth) * 100) return;
if (linePosition >= (1 - MIN_WIDTH / containerWidth) * 100) return;
let linePosition = 50;
settings.style.flexBasis = `${linePosition}%`; settings.style.flexBasis = `${linePosition}%`;
results.style.flexBasis = `${100 - linePosition}%`; results.style.flexBasis = `${100 - linePosition}%`;
});
document.addEventListener('mouseup', () => { let isDragging = false;
isDragging = false;
}); lineWrapper.addEventListener('mousedown', (e) => {
isDragging = true;
e.preventDefault();
});
document.addEventListener('mousemove', (event) => {
if (!isDragging) return;
const tab: HTMLElement | any = document.querySelector(`#tab_${tabId}`);
if (!tab) return;
let offsetX = tab.offsetLeft;
let parent = tab.offsetParent;
while (parent) {
offsetX += parent.offsetLeft;
parent = parent.offsetParent;
}
const containerWidth = container.offsetWidth;
const mouseX = event.clientX;
const linePosition = ((mouseX - offsetX) / containerWidth) * 100;
if (linePosition <= (MIN_WIDTH / containerWidth) * 100) return;
if (linePosition >= (1 - MIN_WIDTH / containerWidth) * 100) return;
settings.style.flexBasis = `${linePosition}%`;
results.style.flexBasis = `${100 - linePosition}%`;
});
document.addEventListener('mouseup', () => {
isDragging = false;
});
}; };
export default () => { export default () => {
addDraggable('txt2img'); addDraggable('txt2img');
addDraggable('img2img'); addDraggable('img2img');
}; };

View File

@ -1,280 +1,279 @@
/** /**
* *
*/ */
export class Converter { export const Converter = {
/** /**
* *
* @param value * @param type -
* @returns
*/ */
static round(value: number): number { addPromptButton(type: string): void {
return Math.round(value * 10000) / 10000; const generateButton: HTMLElement | null = gradioApp().querySelector(`#${type}_generate`);
} const actionsColumn: HTMLElement | null = gradioApp().querySelector(`#${type}_style_create`);
const nai2local: HTMLElement | null = gradioApp().querySelector(`#${type}_formatconvert`);
/** if (!generateButton || !actionsColumn || nai2local) return;
* const convertButton: HTMLElement = Converter.createButton(`${type}_formatconvert`, '🪄', () =>
* @param srt Converter.onClickConvert(type));
* @returns actionsColumn.parentNode?.append(convertButton);
*/ },
static convertStr(srt: string): string {
return srt.replace(//g, ':').replace(//g, '(').replace(//g, ')');
}
/**
*
* @param str
* @returns
*/
static convertStr2Array(str: string): string[] {
// 匹配各种括号中的内容,包括括号本身
const bracketRegex = /([()<>[\]])/g;
/** /**
*
* @param str
* @returns
*/
const splitByBracket = (str: string): string[] => {
const arr: string[] = [];
let start = 0;
let depth = 0;
let match;
while ((match = bracketRegex.exec(str)) !== null) {
if (depth === 0 && match.index > start) {
arr.push(str.substring(start, match.index));
start = match.index;
}
if (match[0] === '(' || match[0] === '<' || match[0] === '[') {
depth++;
} else if (match[0] === ')' || match[0] === '>' || match[0] === ']') {
depth--;
}
if (depth === 0) {
arr.push(str.substring(start, match.index + 1));
start = match.index + 1;
}
}
if (start < str.length) {
arr.push(str.substring(start));
}
return arr;
};
/**
*
* @param str
* @returns
*/
const splitByComma = (str: string): string[] => {
const arr: string[] = [];
let start = 0;
let inBracket = false;
for (let i = 0; i < str.length; i++) {
if (str[i] === ',' && !inBracket) {
arr.push(str.substring(start, i).trim());
start = i + 1;
} else if (str[i].match(bracketRegex)) {
inBracket = !inBracket;
}
}
arr.push(str.substring(start).trim());
return arr;
};
/**
*
* @param str
* @returns
*/
const cleanStr = (str: string): string[] => {
let arr = splitByBracket(str);
arr = arr.flatMap((s) => splitByComma(s));
return arr.filter((s) => s !== '');
};
return cleanStr(str)
.filter((item) => {
const pattern = /^[,\s ]+$/;
return !pattern.test(item);
})
.filter(Boolean)
.sort((a, b) => {
return a.includes('<') && !b.includes('<')
? 1
: b.includes('<') && !a.includes('<')
? -1
: 0;
});
}
/**
*
* @param array
* @returns
*/
static convertArray2Str(array: string[]): string {
const newArray = array.map((item) => {
if (item.includes('<')) return item;
const newItem = item
.replace(/\s+/g, ' ')
.replace(/|\.\|。/g, ',')
.replace(/“||”|"|\/'/g, '')
.replace(/, /g, ',')
.replace(/,,/g, ',')
.replace(/,/g, ', ');
return Converter.convertStr2Array(newItem).join(', ');
});
return newArray.join(', ');
}
/**
* *
* @param input * @param input
* @returns * @returns
*/ */
static convert(input: string): string { convert(input: string): string {
const re_attention = /\{|\[|\}|\]|[^{}[\]]+/gmu; const re_attention = /\{|\[|\}|\]|[^{}[\]]+/gmu;
let text = Converter.convertStr(input); let text = Converter.convertStr(input);
const textArray = Converter.convertStr2Array(text); const textArray = Converter.convertStr2Array(text);
text = Converter.convertArray2Str(textArray); text = Converter.convertArray2Str(textArray);
let res: [string, number][] = []; let res: [string, number][] = [];
const curly_bracket_multiplier = 1.05; const curly_bracket_multiplier = 1.05;
const square_bracket_multiplier = 1 / 1.05; const square_bracket_multiplier = 1 / 1.05;
const brackets: Record<string, { multiplier: number; stack: number[] }> = { const brackets: Record<string, { multiplier: number; stack: number[] }> = {
'{': { stack: [], multiplier: curly_bracket_multiplier }, '[': {multiplier: square_bracket_multiplier, stack: []},
'[': { stack: [], multiplier: square_bracket_multiplier }, '{': {multiplier: curly_bracket_multiplier, stack: []},
}; };
/** /**
* *
* @param start_position * @param start_position
* @param multiplier * @param multiplier
*/ */
function multiply_range(start_position: number, multiplier: number) { function multiply_range(start_position: number, multiplier: number) {
for (let pos = start_position; pos < res.length; pos++) { for (let pos = start_position; pos < res.length; pos++) {
res[pos][1] = Converter.round(res[pos][1] * multiplier); res[pos][1] = Converter.round(res[pos][1] * multiplier);
} }
}
for (const match of text.matchAll(re_attention)) {
let word = match[0];
if (word in brackets) {
brackets[word].stack.push(res.length);
} else if (word === '}' || word === ']') {
const bracket = brackets[word === '}' ? '{' : '['];
if (bracket.stack.length > 0) {
multiply_range(bracket.stack.pop()!, bracket.multiplier);
} }
} else {
res.push([word, 1.0]);
}
}
Object.keys(brackets).forEach((bracketType) => { for (const match of text.matchAll(re_attention)) {
brackets[bracketType].stack.forEach((pos) => { let word = match[0];
multiply_range(pos, brackets[bracketType].multiplier);
});
});
if (res.length === 0) { if (word in brackets) {
res = [['', 1.0]]; brackets[word].stack.push(res.length);
} } else if (word === '}' || word === ']') {
const bracket = brackets[word === '}' ? '{' : '['];
if (bracket.stack.length > 0) {
multiply_range(bracket.stack.pop()!, bracket.multiplier);
}
} else {
res.push([word, 1]);
}
}
let i = 0; for (const bracketType of Object.keys(brackets)) {
while (i + 1 < res.length) { for (const pos of brackets[bracketType].stack) {
if (res[i][1] === res[i + 1][1]) { multiply_range(pos, brackets[bracketType].multiplier);
res[i][0] += res[i + 1][0]; }
res.splice(i + 1, 1); }
} else {
i += 1;
}
}
let result = ''; if (res.length === 0) {
for (const [word, value] of res) { res = [['', 1]];
result += value === 1.0 ? word : `(${word}:${value.toString()})`; }
}
return result;
}
/** let index = 0;
* input while (index + 1 < res.length) {
* @param target if (res[index][1] === res[index + 1][1]) {
res[index][0] += res[index + 1][0];
res.splice(index + 1, 1);
} else {
index += 1;
}
}
let result = '';
for (const [word, value] of res) {
result += value === 1 ? word : `(${word}:${value.toString()})`;
}
return result;
},
/**
*
* @param array
* @returns
*/ */
static dispatchInputEvent(target: EventTarget) { convertArray2Str(array: string[]): string {
let inputEvent = new Event('input'); const newArray = array.map((item) => {
Object.defineProperty(inputEvent, 'target', { value: target }); if (item.includes('<')) return item;
target.dispatchEvent(inputEvent); const newItem = item
} .replaceAll(/\s+/g, ' ')
.replaceAll(/|\.\|。/g, ',')
.replaceAll(/“||”|"|\/'/g, '')
.replaceAll(', ', ',')
.replaceAll(',,', ',')
.replaceAll(',', ', ');
return Converter.convertStr2Array(newItem).join(', ');
});
return newArray.join(', ');
},
/** /**
* *
* @param type * @param srt
* @returns
*/ */
static onClickConvert(type: string) { convertStr(srt: string): string {
const default_prompt = ''; return srt.replaceAll('', ':').replaceAll('', '(').replaceAll('', ')');
const default_negative = ''; },
const prompt = gradioApp().querySelector( /**
`#${type}_prompt > label > textarea`, *
) as HTMLTextAreaElement; * @param str
const result = Converter.convert(prompt.value); * @returns
prompt.value = */
result.match(/^masterpiece, best quality,/) === null ? default_prompt + result : result; convertStr2Array(string_: string): string[] {
Converter.dispatchInputEvent(prompt); // 匹配各种括号中的内容,包括括号本身
const negprompt = gradioApp().querySelector( const bracketRegex = /([()<>[\]])/g;
`#${type}_neg_prompt > label > textarea`,
) as HTMLTextAreaElement;
const negResult = Converter.convert(negprompt.value);
negprompt.value =
negResult.match(/^lowres,/) === null
? negResult.length === 0
? default_negative
: default_negative + negResult
: negResult;
Converter.dispatchInputEvent(negprompt);
}
/** /**
*
* @param str
* @returns
*/
const splitByBracket = (string__: string): string[] => {
const array: string[] = [];
let start = 0;
let depth = 0;
let match;
while ((match = bracketRegex.exec(string__)) !== null) {
if (depth === 0 && match.index > start) {
array.push(string__.slice(start, match.index));
start = match.index;
}
if (match[0] === '(' || match[0] === '<' || match[0] === '[') {
depth++;
} else if (match[0] === ')' || match[0] === '>' || match[0] === ']') {
depth--;
}
if (depth === 0) {
array.push(string__.slice(start, match.index + 1));
start = match.index + 1;
}
}
if (start < string__.length) {
array.push(string__.slice(Math.max(0, start)));
}
return array;
};
/**
*
* @param str
* @returns
*/
const splitByComma = (string__: string): string[] => {
const array: string[] = [];
let start = 0;
let inBracket = false;
for (let index = 0; index < string__.length; index++) {
if (string__[index] === ',' && !inBracket) {
array.push(string__.slice(start, index).trim());
start = index + 1;
} else if (bracketRegex.test(string__[index])) {
inBracket = !inBracket;
}
}
array.push(string__.slice(Math.max(0, start)).trim());
return array;
};
/**
*
* @param str
* @returns
*/
const cleanString = (string__: string): string[] => {
let array = splitByBracket(string__);
array = array.flatMap((s) => splitByComma(s));
return array.filter((s) => s !== '');
};
return cleanString(string_)
.filter((item) => {
const pattern = /^[\s,]+$/;
return !pattern.test(item);
})
.filter(Boolean)
.sort((a, b) => {
return a.includes('<') && !b.includes('<') ?
1 :
b.includes('<') && !a.includes('<') ?
-1 :
0;
});
},
/**
* *
* @param id id * @param id id
* @param innerHTML * @param innerHTML
* @param onClick * @param onClick
* @returns * @returns
*/ */
static createButton(id: string, innerHTML: string, onClick: () => void): HTMLButtonElement { createButton(id: string, innerHTML: string, onClick: () => void): HTMLButtonElement {
const button = document.createElement('button'); const button = document.createElement('button');
button.id = id; button.id = id;
button.type = 'button'; button.type = 'button';
button.innerHTML = innerHTML; button.innerHTML = innerHTML;
button.title = 'Format prompt~🪄'; button.title = 'Format prompt~🪄';
button.className = 'lg secondary gradio-button tool svelte-1ipelgc'; button.className = 'lg secondary gradio-button tool svelte-1ipelgc';
button.addEventListener('click', onClick); button.addEventListener('click', onClick);
return button; return button;
} },
/** /**
* * input
* @param type - * @param target
*/ */
static addPromptButton(type: string): void { dispatchInputEvent(target: EventTarget) {
const generateBtn: HTMLElement | null = gradioApp().querySelector(`#${type}_generate`); let inputEvent = new Event('input');
const actionsColumn: HTMLElement | null = gradioApp().querySelector(`#${type}_style_create`); Object.defineProperty(inputEvent, 'target', {value: target});
const nai2local: HTMLElement | null = gradioApp().querySelector(`#${type}_formatconvert`); target.dispatchEvent(inputEvent);
if (!generateBtn || !actionsColumn || nai2local) return; },
const convertBtn: HTMLElement = Converter.createButton(`${type}_formatconvert`, '🪄', () =>
Converter.onClickConvert(type), /**
); *
actionsColumn.parentNode?.append(convertBtn); * @param type
} */
} onClickConvert(type: string) {
const default_prompt = '';
const default_negative = '';
const prompt = gradioApp().querySelector(
`#${type}_prompt > label > textarea`,
) as HTMLTextAreaElement;
const result = Converter.convert(prompt.value);
prompt.value =
result.match(/^masterpiece, best quality,/) === null ? default_prompt + result : result;
Converter.dispatchInputEvent(prompt);
const negprompt = gradioApp().querySelector(
`#${type}_neg_prompt > label > textarea`,
) as HTMLTextAreaElement;
const negResult = Converter.convert(negprompt.value);
negprompt.value =
negResult.match(/^lowres,/) === null ?
negResult.length === 0 ?
default_negative :
default_negative + negResult :
negResult;
Converter.dispatchInputEvent(negprompt);
},
/**
*
* @param value
* @returns
*/
round(value: number): number {
return Math.round(value * 10_000) / 10_000;
},
};
export default () => { export default () => {
Converter.addPromptButton('txt2img'); Converter.addPromptButton('txt2img');
Converter.addPromptButton('img2img'); Converter.addPromptButton('img2img');
}; };

View File

@ -4,52 +4,52 @@ interface ErrorString {
} }
class BracketChecker { class BracketChecker {
private textArea: HTMLTextAreaElement; private textArea: HTMLTextAreaElement;
private counterElt: HTMLElement; private counterElt: HTMLElement;
private errorStrings: ErrorString[]; private errorStrings: ErrorString[];
constructor(textArea: HTMLTextAreaElement, counterElt: HTMLElement) { constructor(textArea: HTMLTextAreaElement, counterElt: HTMLElement) {
this.textArea = textArea; this.textArea = textArea;
this.counterElt = counterElt; this.counterElt = counterElt;
this.errorStrings = [ this.errorStrings = [
{ {
regex: '\\(', error: '(...) - Different number of opening and closing parentheses detected.\n',
error: '(...) - Different number of opening and closing parentheses detected.\n', regex: '\\(',
}, },
{ {
regex: '\\[', error: '[...] - Different number of opening and closing square brackets detected.\n',
error: '[...] - Different number of opening and closing square brackets detected.\n', regex: '\\[',
}, },
{ {
regex: '\\{', error: '{...} - Different number of opening and closing curly brackets detected.\n',
error: '{...} - Different number of opening and closing curly brackets detected.\n', regex: '\\{',
}, },
]; ];
} }
/** /**
* *
*/ */
public check = (): void => { public check = (): void => {
let title = ''; let title = '';
this.errorStrings.forEach(({ regex, error }) => { for (const {regex, error} of this.errorStrings) {
const openMatches = (this.textArea.value.match(new RegExp(regex, 'g')) || []).length; const openMatches = (this.textArea.value.match(new RegExp(regex, 'g')) || []).length;
const closeMatches = ( const closeMatches = (
this.textArea.value.match( this.textArea.value.match(
new RegExp(regex.replace(/\(/g, ')').replace(/\[/g, ']').replace(/\{/g, '}'), 'g'), new RegExp(regex.replaceAll('(', ')').replaceAll('[', ']').replaceAll('{', '}'), 'g'),
) || [] ) || []
).length; ).length;
if (openMatches !== closeMatches) { if (openMatches === closeMatches) {
if (!this.counterElt.title.includes(error)) { title = this.counterElt.title.replace(error, '');
title += error; } else {
if (!this.counterElt.title.includes(error)) {
title += error;
}
}
} }
} else { this.counterElt.title = title;
title = this.counterElt.title.replace(error, ''); this.counterElt.classList.toggle('error', !!title);
} };
});
this.counterElt.title = title;
this.counterElt.classList.toggle('error', !!title);
};
} }
/** /**
@ -58,18 +58,18 @@ class BracketChecker {
* @param id_counter ID * @param id_counter ID
*/ */
const setupBracketChecking = (idPrompt: string, idCounter: string): void => { const setupBracketChecking = (idPrompt: string, idCounter: string): void => {
const textarea = gradioApp().querySelector( const textarea = gradioApp().querySelector(
`#${idPrompt} > label > textarea`, `#${idPrompt} > label > textarea`,
) as HTMLTextAreaElement; ) as HTMLTextAreaElement;
const counter = gradioApp().getElementById(idCounter) as HTMLElement; const counter = gradioApp().querySelector(`#${idCounter}`) as HTMLElement;
const bracketChecker = new BracketChecker(textarea, counter); const bracketChecker = new BracketChecker(textarea, counter);
textarea.addEventListener('input', bracketChecker.check); textarea.addEventListener('input', bracketChecker.check);
}; };
export default () => { export default () => {
const elements = ['txt2img', 'txt2img_neg', 'img2img', 'img2img_neg']; const elements = ['txt2img', 'txt2img_neg', 'img2img', 'img2img_neg'];
elements.forEach((prompt) => { for (const prompt of elements) {
setupBracketChecking(`${prompt}_prompt`, `${prompt}_token_counter`); setupBracketChecking(`${prompt}_prompt`, `${prompt}_token_counter`);
setupBracketChecking(`${prompt}_prompt`, `${prompt}_negative_token_counter`); setupBracketChecking(`${prompt}_prompt`, `${prompt}_negative_token_counter`);
}); }
}; };

View File

@ -1,73 +1,73 @@
const replaceIcon = (button: HTMLButtonElement, emoji: string[], svg: string) => { const replaceIcon = (button: HTMLButtonElement, emoji: string[], svg: string) => {
if (!button?.textContent) return; if (!button?.textContent) return;
emoji.forEach((e) => { for (const e of emoji) {
if (button?.textContent?.includes(e)) { if (button?.textContent?.includes(e)) {
button.innerHTML = `<span role="img" class="anticon anticon-replace" aria-label={button.textContent}><svg viewBox="64 64 896 896" focusable="false" width="1em" height="1em" fill="currentColor" aria-hidden="true">${svg}</svg></span>`; button.innerHTML = `<span role="img" class="anticon anticon-replace" aria-label={button.textContent}><svg viewBox="64 64 896 896" focusable="false" width="1em" height="1em" fill="currentColor" aria-hidden="true">${svg}</svg></span>`;
}
} }
});
}; };
export default () => { export default () => {
document.querySelectorAll('button').forEach((button) => { for (const button of document.querySelectorAll('button')) {
replaceIcon( replaceIcon(
button, button,
['📂'], ['📂'],
'<path d="M880 298.4H521L403.7 186.2a8.15 8.15 0 00-5.5-2.2H144c-17.7 0-32 14.3-32 32v592c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V330.4c0-17.7-14.3-32-32-32zM840 768H184V256h188.5l119.6 114.4H840V768z"></path>', '<path d="M880 298.4H521L403.7 186.2a8.15 8.15 0 00-5.5-2.2H144c-17.7 0-32 14.3-32 32v592c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V330.4c0-17.7-14.3-32-32-32zM840 768H184V256h188.5l119.6 114.4H840V768z"></path>',
); );
replaceIcon( replaceIcon(
button, button,
['🔄', '🔁'], ['🔄', '🔁'],
'<path d="M909.1 209.3l-56.4 44.1C775.8 155.1 656.2 92 521.9 92 290 92 102.3 279.5 102 511.5 101.7 743.7 289.8 932 521.9 932c181.3 0 335.8-115 394.6-276.1 1.5-4.2-.7-8.9-4.9-10.3l-56.7-19.5a8 8 0 00-10.1 4.8c-1.8 5-3.8 10-5.9 14.9-17.3 41-42.1 77.8-73.7 109.4A344.77 344.77 0 01655.9 829c-42.3 17.9-87.4 27-133.8 27-46.5 0-91.5-9.1-133.8-27A341.5 341.5 0 01279 755.2a342.16 342.16 0 01-73.7-109.4c-17.9-42.4-27-87.4-27-133.9s9.1-91.5 27-133.9c17.3-41 42.1-77.8 73.7-109.4 31.6-31.6 68.4-56.4 109.3-73.8 42.3-17.9 87.4-27 133.8-27 46.5 0 91.5 9.1 133.8 27a341.5 341.5 0 01109.3 73.8c9.9 9.9 19.2 20.4 27.8 31.4l-60.2 47a8 8 0 003 14.1l175.6 43c5 1.2 9.9-2.6 9.9-7.7l.8-180.9c-.1-6.6-7.8-10.3-13-6.2z"></path>', '<path d="M909.1 209.3l-56.4 44.1C775.8 155.1 656.2 92 521.9 92 290 92 102.3 279.5 102 511.5 101.7 743.7 289.8 932 521.9 932c181.3 0 335.8-115 394.6-276.1 1.5-4.2-.7-8.9-4.9-10.3l-56.7-19.5a8 8 0 00-10.1 4.8c-1.8 5-3.8 10-5.9 14.9-17.3 41-42.1 77.8-73.7 109.4A344.77 344.77 0 01655.9 829c-42.3 17.9-87.4 27-133.8 27-46.5 0-91.5-9.1-133.8-27A341.5 341.5 0 01279 755.2a342.16 342.16 0 01-73.7-109.4c-17.9-42.4-27-87.4-27-133.9s9.1-91.5 27-133.9c17.3-41 42.1-77.8 73.7-109.4 31.6-31.6 68.4-56.4 109.3-73.8 42.3-17.9 87.4-27 133.8-27 46.5 0 91.5 9.1 133.8 27a341.5 341.5 0 01109.3 73.8c9.9 9.9 19.2 20.4 27.8 31.4l-60.2 47a8 8 0 003 14.1l175.6 43c5 1.2 9.9-2.6 9.9-7.7l.8-180.9c-.1-6.6-7.8-10.3-13-6.2z"></path>',
); );
replaceIcon( replaceIcon(
button, button,
['↙️'], ['↙️'],
'<path d="M603.3 327.5l-246 178a7.95 7.95 0 000 12.9l246 178c5.3 3.8 12.7 0 12.7-6.5V643c0-10.2-4.9-19.9-13.2-25.9L457.4 512l145.4-105.2c8.3-6 13.2-15.6 13.2-25.9V334c0-6.5-7.4-10.3-12.7-6.5z"></path><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"></path>', '<path d="M603.3 327.5l-246 178a7.95 7.95 0 000 12.9l246 178c5.3 3.8 12.7 0 12.7-6.5V643c0-10.2-4.9-19.9-13.2-25.9L457.4 512l145.4-105.2c8.3-6 13.2-15.6 13.2-25.9V334c0-6.5-7.4-10.3-12.7-6.5z"></path><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"></path>',
); );
replaceIcon( replaceIcon(
button, button,
['🗑️'], ['🗑️'],
'<path d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"></path>', '<path d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"></path>',
); );
replaceIcon( replaceIcon(
button, button,
['📋'], ['📋'],
'<path d="M832 112H724V72c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v40H500V72c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v40H320c-17.7 0-32 14.3-32 32v120h-96c-17.7 0-32 14.3-32 32v632c0 17.7 14.3 32 32 32h512c17.7 0 32-14.3 32-32v-96h96c17.7 0 32-14.3 32-32V144c0-17.7-14.3-32-32-32zM664 888H232V336h218v174c0 22.1 17.9 40 40 40h174v338zm0-402H514V336h.2L664 485.8v.2zm128 274h-56V456L544 264H360v-80h68v32c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-32h152v32c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-32h68v576z"></path>', '<path d="M832 112H724V72c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v40H500V72c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v40H320c-17.7 0-32 14.3-32 32v120h-96c-17.7 0-32 14.3-32 32v632c0 17.7 14.3 32 32 32h512c17.7 0 32-14.3 32-32v-96h96c17.7 0 32-14.3 32-32V144c0-17.7-14.3-32-32-32zM664 888H232V336h218v174c0 22.1 17.9 40 40 40h174v338zm0-402H514V336h.2L664 485.8v.2zm128 274h-56V456L544 264H360v-80h68v32c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-32h152v32c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-32h68v576z"></path>',
); );
replaceIcon( replaceIcon(
button, button,
['💾'], ['💾'],
'<path d="M893.3 293.3L730.7 130.7c-7.5-7.5-16.7-13-26.7-16V112H144c-17.7 0-32 14.3-32 32v736c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V338.5c0-17-6.7-33.2-18.7-45.2zM384 184h256v104H384V184zm456 656H184V184h136v136c0 17.7 14.3 32 32 32h320c17.7 0 32-14.3 32-32V205.8l136 136V840zM512 442c-79.5 0-144 64.5-144 144s64.5 144 144 144 144-64.5 144-144-64.5-144-144-144zm0 224c-44.2 0-80-35.8-80-80s35.8-80 80-80 80 35.8 80 80-35.8 80-80 80z"></path>', '<path d="M893.3 293.3L730.7 130.7c-7.5-7.5-16.7-13-26.7-16V112H144c-17.7 0-32 14.3-32 32v736c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V338.5c0-17-6.7-33.2-18.7-45.2zM384 184h256v104H384V184zm456 656H184V184h136v136c0 17.7 14.3 32 32 32h320c17.7 0 32-14.3 32-32V205.8l136 136V840zM512 442c-79.5 0-144 64.5-144 144s64.5 144 144 144 144-64.5 144-144-64.5-144-144-144zm0 224c-44.2 0-80-35.8-80-80s35.8-80 80-80 80 35.8 80 80-35.8 80-80 80z"></path>',
); );
replaceIcon( replaceIcon(
button, button,
['🎲️'], ['🎲️'],
'<path d="M136 552h63.6c4.4 0 8-3.6 8-8V288.7h528.6v72.6c0 1.9.6 3.7 1.8 5.2a8.3 8.3 0 0011.7 1.4L893 255.4c4.3-5 3.6-10.3 0-13.2L749.7 129.8a8.22 8.22 0 00-5.2-1.8c-4.6 0-8.4 3.8-8.4 8.4V209H199.7c-39.5 0-71.7 32.2-71.7 71.8V544c0 4.4 3.6 8 8 8zm752-80h-63.6c-4.4 0-8 3.6-8 8v255.3H287.8v-72.6c0-1.9-.6-3.7-1.8-5.2a8.3 8.3 0 00-11.7-1.4L131 768.6c-4.3 5-3.6 10.3 0 13.2l143.3 112.4c1.5 1.2 3.3 1.8 5.2 1.8 4.6 0 8.4-3.8 8.4-8.4V815h536.6c39.5 0 71.7-32.2 71.7-71.8V480c-.2-4.4-3.8-8-8.2-8z"></path>', '<path d="M136 552h63.6c4.4 0 8-3.6 8-8V288.7h528.6v72.6c0 1.9.6 3.7 1.8 5.2a8.3 8.3 0 0011.7 1.4L893 255.4c4.3-5 3.6-10.3 0-13.2L749.7 129.8a8.22 8.22 0 00-5.2-1.8c-4.6 0-8.4 3.8-8.4 8.4V209H199.7c-39.5 0-71.7 32.2-71.7 71.8V544c0 4.4 3.6 8 8 8zm752-80h-63.6c-4.4 0-8 3.6-8 8v255.3H287.8v-72.6c0-1.9-.6-3.7-1.8-5.2a8.3 8.3 0 00-11.7-1.4L131 768.6c-4.3 5-3.6 10.3 0 13.2l143.3 112.4c1.5 1.2 3.3 1.8 5.2 1.8 4.6 0 8.4-3.8 8.4-8.4V815h536.6c39.5 0 71.7-32.2 71.7-71.8V480c-.2-4.4-3.8-8-8.2-8z"></path>',
); );
replaceIcon( replaceIcon(
button, button,
['♻️'], ['♻️'],
'<path d="M793 242H366v-74c0-6.7-7.7-10.4-12.9-6.3l-142 112a8 8 0 000 12.6l142 112c5.2 4.1 12.9.4 12.9-6.3v-74h415v470H175c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h618c35.3 0 64-28.7 64-64V306c0-35.3-28.7-64-64-64z"></path>', '<path d="M793 242H366v-74c0-6.7-7.7-10.4-12.9-6.3l-142 112a8 8 0 000 12.6l142 112c5.2 4.1 12.9.4 12.9-6.3v-74h415v470H175c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h618c35.3 0 64-28.7 64-64V306c0-35.3-28.7-64-64-64z"></path>',
); );
replaceIcon( replaceIcon(
button, button,
['🪄'], ['🪄'],
'<defs><style></style></defs><path d="M899.1 869.6l-53-305.6H864c14.4 0 26-11.6 26-26V346c0-14.4-11.6-26-26-26H618V138c0-14.4-11.6-26-26-26H432c-14.4 0-26 11.6-26 26v182H160c-14.4 0-26 11.6-26 26v192c0 14.4 11.6 26 26 26h17.9l-53 305.6a25.95 25.95 0 0025.6 30.4h723c1.5 0 3-.1 4.4-.4a25.88 25.88 0 0021.2-30zM204 390h272V182h72v208h272v104H204V390zm468 440V674c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v156H416V674c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v156H202.8l45.1-260H776l45.1 260H672z"></path>', '<defs><style></style></defs><path d="M899.1 869.6l-53-305.6H864c14.4 0 26-11.6 26-26V346c0-14.4-11.6-26-26-26H618V138c0-14.4-11.6-26-26-26H432c-14.4 0-26 11.6-26 26v182H160c-14.4 0-26 11.6-26 26v192c0 14.4 11.6 26 26 26h17.9l-53 305.6a25.95 25.95 0 0025.6 30.4h723c1.5 0 3-.1 4.4-.4a25.88 25.88 0 0021.2-30zM204 390h272V182h72v208h272v104H204V390zm468 440V674c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v156H416V674c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v156H202.8l45.1-260H776l45.1 260H672z"></path>',
); );
replaceIcon( replaceIcon(
button, button,
['⚙️'], ['⚙️'],
'<path d="M400 317.7h73.9V656c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V317.7H624c6.7 0 10.4-7.7 6.3-12.9L518.3 163a8 8 0 00-12.6 0l-112 141.7c-4.1 5.3-.4 13 6.3 13zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"></path>', '<path d="M400 317.7h73.9V656c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V317.7H624c6.7 0 10.4-7.7 6.3-12.9L518.3 163a8 8 0 00-12.6 0l-112 141.7c-4.1 5.3-.4 13 6.3 13zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"></path>',
); );
replaceIcon( replaceIcon(
button, button,
['➡️'], ['➡️'],
'<path d="M666.7 505.5l-246-178A8 8 0 00408 334v46.9c0 10.2 4.9 19.9 13.2 25.9L566.6 512 421.2 617.2c-8.3 6-13.2 15.6-13.2 25.9V690c0 6.5 7.4 10.3 12.7 6.5l246-178c4.4-3.2 4.4-9.8 0-13z"></path><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"></path>', '<path d="M666.7 505.5l-246-178A8 8 0 00408 334v46.9c0 10.2 4.9 19.9 13.2 25.9L566.6 512 421.2 617.2c-8.3 6-13.2 15.6-13.2 25.9V690c0 6.5 7.4 10.3 12.7 6.5l246-178c4.4-3.2 4.4-9.8 0-13z"></path><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"></path>',
); );
replaceIcon( replaceIcon(
button, button,
['⇅'], ['⇅'],
'<path d="M847.9 592H152c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h605.2L612.9 851c-4.1 5.2-.4 13 6.3 13h72.5c4.9 0 9.5-2.2 12.6-6.1l168.8-214.1c16.5-21 1.6-51.8-25.2-51.8zM872 356H266.8l144.3-183c4.1-5.2.4-13-6.3-13h-72.5c-4.9 0-9.5 2.2-12.6 6.1L150.9 380.2c-16.5 21-1.6 51.8 25.1 51.8h696c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8z"></path>', '<path d="M847.9 592H152c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h605.2L612.9 851c-4.1 5.2-.4 13 6.3 13h72.5c4.9 0 9.5-2.2 12.6-6.1l168.8-214.1c16.5-21 1.6-51.8-25.2-51.8zM872 356H266.8l144.3-183c4.1-5.2.4-13-6.3-13h-72.5c-4.9 0-9.5 2.2-12.6 6.1L150.9 380.2c-16.5 21-1.6 51.8 25.1 51.8h696c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8z"></path>',
); );
}); }
}; };

View File

@ -1,9 +1,9 @@
export default () => { export default () => {
// favicon // favicon
const favicon: HTMLLinkElement = document.createElement('link'); const favicon: HTMLLinkElement = document.createElement('link');
favicon.rel = 'icon'; favicon.rel = 'icon';
favicon.type = 'image/svg+xml'; favicon.type = 'image/svg+xml';
favicon.href = favicon.href =
'https://gw.alipayobjects.com/zos/bmw-prod/51a51720-8a30-4430-b6c9-be5712364f04.svg'; 'https://gw.alipayobjects.com/zos/bmw-prod/51a51720-8a30-4430-b6c9-be5712364f04.svg';
document.getElementsByTagName('head')[0].appendChild(favicon); document.querySelectorAll('head')[0].append(favicon);
}; };

View File

@ -1,5 +1,5 @@
import { create } from 'zustand'; import {create} from 'zustand';
import { devtools } from 'zustand/middleware'; import {devtools} from 'zustand/middleware';
const SETTING_KEY = 'SD-KITCHEN-SETTING'; const SETTING_KEY = 'SD-KITCHEN-SETTING';
@ -17,16 +17,16 @@ export interface WebuiSetting {
} }
export const defaultSetting: WebuiSetting = { export const defaultSetting: WebuiSetting = {
promotTextarea: 'scroll', enableExtraNetworkSidebar: true,
sidebarExpand: true, extraNetworkCardSize: 86,
sidebarFixedMode: 'fixed', extraNetworkFixedMode: 'fixed',
sidebarWidth: 280, extraNetworkSidebarExpand: true,
enableExtraNetworkSidebar: true, extraNetworkSidebarWidth: 340,
extraNetworkSidebarExpand: true, promotTextarea: 'scroll',
extraNetworkFixedMode: 'fixed', sidebarExpand: true,
extraNetworkSidebarWidth: 340, sidebarFixedMode: 'fixed',
extraNetworkCardSize: 86, sidebarWidth: 280,
svgIcon: false, svgIcon: false,
}; };
export interface AppState { export interface AppState {
currentTab: string; currentTab: string;
@ -39,33 +39,33 @@ export interface AppState {
themeMode: 'light' | 'dark'; themeMode: 'light' | 'dark';
} }
export const useAppStore = create<AppState>()( export const useAppStore = create<AppState>()(
devtools((set, get) => ({ devtools((set, get) => ({
themeMode: 'light', currentTab: 'tab_txt2img',
setting: defaultSetting, onInit: () => {
currentTab: 'tab_txt2img', get().onLoadSetting();
setCurrentTab: () => { },
const currentTab = get_uiCurrentTabContent().id; onLoadSetting: () => {
if (currentTab !== get().currentTab) set({ currentTab }, false, 'setCurrentTab'); let setting: any = localStorage.getItem(SETTING_KEY);
}, if (setting) {
onSetThemeMode: (themeMode) => { setting = JSON.parse(setting);
set(() => ({ themeMode }), false, 'onSetThemeMode'); } else {
}, setting = defaultSetting;
onLoadSetting: () => { localStorage.setItem(SETTING_KEY, JSON.stringify(defaultSetting));
let setting: any = localStorage.getItem(SETTING_KEY); }
if (setting) { set(() => ({setting: {...defaultSetting, ...setting}}), false, 'onLoadSetting');
setting = JSON.parse(setting); },
} else { onSetSetting: (setting) => {
setting = defaultSetting; localStorage.setItem(SETTING_KEY, JSON.stringify(setting));
localStorage.setItem(SETTING_KEY, JSON.stringify(defaultSetting)); set(() => ({setting}), false, 'onSetSetting');
} },
set(() => ({ setting: { ...defaultSetting, ...setting } }), false, 'onLoadSetting'); onSetThemeMode: (themeMode) => {
}, set(() => ({themeMode}), false, 'onSetThemeMode');
onSetSetting: (setting) => { },
localStorage.setItem(SETTING_KEY, JSON.stringify(setting)); setCurrentTab: () => {
set(() => ({ setting }), false, 'onSetSetting'); const currentTab = get_uiCurrentTabContent().id;
}, if (currentTab !== get().currentTab) set({currentTab}, false, 'setCurrentTab');
onInit: () => { },
get().onLoadSetting(); setting: defaultSetting,
}, themeMode: 'light',
})), })),
); );

View File

@ -21,6 +21,12 @@
padding: 6px; padding: 6px;
} }
.head {
> label {
overflow: hidden;
}
}
.wrap.svelte-1p9xokt.svelte-1p9xokt.svelte-1p9xokt { .wrap.svelte-1p9xokt.svelte-1p9xokt.svelte-1p9xokt {
gap: 8px !important; gap: 8px !important;
@ -35,9 +41,9 @@
background: transparent; background: transparent;
} }
[id$='_results'],
#tab_image_browser, #tab_image_browser,
#tab_dreambooth_interface, #tab_dreambooth_interface {
[id$='_results'] {
.block.gradio-accordion.svelte-mppz8v.padded { .block.gradio-accordion.svelte-mppz8v.padded {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -76,9 +82,3 @@
div.svelte-1oo81b7 > *:not(.absolute) { div.svelte-1oo81b7 > *:not(.absolute) {
border-radius: var(--border-radius) !important; border-radius: var(--border-radius) !important;
} }
.head {
> label {
overflow: hidden;
}
}

View File

@ -1,21 +1,14 @@
#txt2img_extra_networks, .extra-network-thumbs {
#img2img_extra_networks { .name {
.tabitem.gradio-tabitem.svelte-19hvt5v { overflow: hidden;
padding: 0; display: block;
font-size: 12px !important;
text-overflow: ellipsis;
white-space: nowrap;
background: transparent; background: transparent;
} }
.search {
box-sizing: border-box;
width: var(--fill-available);
max-width: var(--fill-available);
max-height: 36px !important;
padding: 8px;
}
button {
height: 32px !important;
}
} }
.extra-networks { .extra-networks {
@ -140,15 +133,22 @@
} }
} }
.extra-network-thumbs { #txt2img_extra_networks,
.name { #img2img_extra_networks {
overflow: hidden; .tabitem.gradio-tabitem.svelte-19hvt5v {
display: block; padding: 0;
font-size: 12px !important;
text-overflow: ellipsis;
white-space: nowrap;
background: transparent; background: transparent;
} }
.search {
box-sizing: border-box;
width: var(--fill-available);
max-width: var(--fill-available);
max-height: 36px !important;
padding: 8px;
}
button {
height: 32px !important;
}
} }

View File

@ -1,5 +1,12 @@
#tab_txt2img, #tab_txt2img,
#tab_img2img { #tab_img2img {
[id$='_settings'] {
div.svelte-15lo0d8 > *,
div.svelte-15lo0d8 > .form > * {
flex: 1;
}
}
#txt2img_settings, #txt2img_settings,
#img2img_settings { #img2img_settings {
.block.gradio-checkbox { .block.gradio-checkbox {
@ -34,6 +41,10 @@
border-radius: var(--border-radius) !important; border-radius: var(--border-radius) !important;
} }
.image-buttons button {
min-width: min(160px, 100%) !important;
}
[id$='2img_tools'] > div { [id$='2img_tools'] > div {
display: flex; display: flex;
justify-content: center; justify-content: center;
@ -43,10 +54,6 @@
} }
} }
.image-buttons button {
min-width: min(160px, 100%) !important;
}
#img2img_label_copy_to_img2img { #img2img_label_copy_to_img2img {
display: none; display: none;
} }
@ -58,13 +65,6 @@
gap: 8px !important; gap: 8px !important;
} }
[id$='_settings'] {
div.svelte-15lo0d8 > *,
div.svelte-15lo0d8 > .form > * {
flex: 1;
}
}
#script_list { #script_list {
margin-top: 0 !important; margin-top: 0 !important;
margin-bottom: var(--size-ms) !important; margin-bottom: var(--size-ms) !important;

6
src/types/global.d.ts vendored Normal file
View File

@ -0,0 +1,6 @@
import {Theme as AntdStyleTheme} from 'antd-style';
declare module 'styled-components' {
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface DefaultTheme extends AntdStyleTheme {}
}

View File

@ -1,6 +1,11 @@
import React from 'react'; import {type HTMLAttributes} from 'react';
export type DivProps = React.DetailedHTMLProps< export type DivProps = HTMLAttributes<HTMLDivElement>;
React.HTMLAttributes<HTMLDivElement>,
HTMLDivElement export type SvgProps = HTMLAttributes<SVGSVGElement>;
>;
export type ImgProps = HTMLAttributes<HTMLImageElement>;
export type InputProps = HTMLAttributes<HTMLInputElement>;
export type TextAreaProps = HTMLAttributes<HTMLTextAreaElement>;

12
src/types/webui.d.ts vendored
View File

@ -5,7 +5,7 @@ declare const all_gallery_buttons: () => Element[];
declare const selected_gallery_button: () => Element | null; declare const selected_gallery_button: () => Element | null;
declare const selected_gallery_index: () => number; declare const selected_gallery_index: () => number;
declare const extract_image_from_gallery: (gallery: Element[]) => Element[]; declare const extract_image_from_gallery: (gallery: Element[]) => Element[];
declare const args_to_array: (args: IArguments) => any[]; declare const arguments_to_array: (arguments_: IArguments) => any[];
declare const switch_to_txt2img: () => any[]; declare const switch_to_txt2img: () => any[];
declare const switch_to_img2img_tab: (no: number) => void; declare const switch_to_img2img_tab: (no: number) => void;
declare const switch_to_img2img: () => any[]; declare const switch_to_img2img: () => any[];
@ -14,15 +14,15 @@ declare const switch_to_inpaint_sketch: () => any[];
declare const switch_to_inpaint: () => any[]; declare const switch_to_inpaint: () => any[];
declare const switch_to_extras: () => any[]; declare const switch_to_extras: () => any[];
declare const get_tab_index: (tabId: string) => number; declare const get_tab_index: (tabId: string) => number;
declare const create_tab_index_args: (tabId: string, args: any[]) => any[]; declare const create_tab_index_arguments: (tabId: string, arguments_: any[]) => any[];
declare const get_img2img_tab_index: () => any[]; declare const get_img2img_tab_index: () => any[];
declare const create_submit_args: (args: any[]) => any[]; declare const create_submit_arguments: (arguments_: any[]) => any[];
declare const showSubmitButtons: (tabname: string, show: boolean) => void; declare const showSubmitButtons: (tabname: string, show: boolean) => void;
declare const submit: () => any[]; declare const submit: () => any[];
declare const submit_img2img: () => any[]; declare const submit_img2img: () => any[];
declare const modelmerger: () => any[]; declare const modelmerger: () => any[];
declare const ask_for_style_name: ( declare const ask_for_style_name: (
arg0: any, argument0: any,
prompt_text: string, prompt_text: string,
negative_prompt_text: string, negative_prompt_text: string,
) => [string, string, string]; ) => [string, string, string];
@ -30,8 +30,8 @@ declare const confirm_clear_prompt: (prompt: string, negative_prompt: string) =>
declare const recalculatePromptTokens: (name: string) => void; declare const recalculatePromptTokens: (name: string) => void;
declare const recalculate_prompts_txt2img: () => any[]; declare const recalculate_prompts_txt2img: () => any[];
declare const recalculate_prompts_img2img: () => any[]; declare const recalculate_prompts_img2img: () => any[];
declare const update_txt2img_tokens: (...args: any[]) => any; declare const update_txt2img_tokens: (...arguments_: any[]) => any;
declare const update_img2img_tokens: (...args: any[]) => any; declare const update_img2img_tokens: (...arguments_: any[]) => any;
declare const restart_reload: () => any[]; declare const restart_reload: () => any[];
declare const updateInput: (target: Element) => void; declare const updateInput: (target: Element) => void;
declare const selectCheckpoint: (name: string) => void; declare const selectCheckpoint: (name: string) => void;

File diff suppressed because one or more lines are too long

View File

@ -1,26 +1,19 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ESNext", "baseUrl": ".",
"useDefineForClassFields": true, "declaration": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"], "downlevelIteration": true,
"allowJs": true, "esModuleInterop": true,
"skipLibCheck": true, "jsx": "react-jsx",
"esModuleInterop": false, "lib": ["dom", "dom.iterable", "esnext"],
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "preserve",
"incremental": true,
"paths": { "paths": {
"@/*": ["./src/*"] "@/*": ["./src/*"]
} },
"resolveJsonModule": true,
"skipLibCheck": true,
"strict": true
}, },
"exclude": ["javascript"], "exclude": ["javascript"],
"extends": "./src/.umi/tsconfig.json", "extends": "./src/.umi/tsconfig.json",
"include": ["src"] "include": ["src", "typings.d.ts", "*.ts"]
} }