♻️ refactor: refactor project
parent
e116458d10
commit
b9046f13a2
|
|
@ -1,16 +1,13 @@
|
||||||
# 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
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
VITE_CONTEXT=PROD
|
||||||
|
|
@ -28,3 +28,4 @@ logs
|
||||||
# add other ignore file below
|
# add other ignore file below
|
||||||
javascript
|
javascript
|
||||||
style.css
|
style.css
|
||||||
|
vite-env.d.ts
|
||||||
25
.eslintrc.js
25
.eslintrc.js
|
|
@ -1,12 +1,17 @@
|
||||||
|
const config = require('@lobehub/lint').eslint;
|
||||||
|
|
||||||
|
config.rules['indent'] = ['off', 2];
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
...require('@lobehub/lint').eslint,
|
...config,
|
||||||
overrides: [
|
overrides: [
|
||||||
{
|
{
|
||||||
files: ['*.ts', '*.tsx'],
|
files: ['*.ts', '*.tsx'],
|
||||||
rules: {
|
rules: {
|
||||||
'no-undef': 0,
|
'no-undef': 0,
|
||||||
'unicorn/prefer-add-event-listener': 0,
|
'object-curly-spacing': 0,
|
||||||
},
|
'unicorn/prefer-add-event-listener': 0,
|
||||||
},
|
},
|
||||||
],
|
},
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
|
||||||
62
.umirc.ts
62
.umirc.ts
|
|
@ -1,62 +0,0 @@
|
||||||
// @ts-ignore
|
|
||||||
import browserslist from 'browserslist';
|
|
||||||
// @ts-ignore
|
|
||||||
import CssMinimizerPlugin from 'css-minimizer-webpack-plugin';
|
|
||||||
// @ts-ignore
|
|
||||||
import lightningcss from 'lightningcss';
|
|
||||||
import { defineConfig } from 'umi';
|
|
||||||
import WebpackShellPlugin from 'webpack-shell-plugin-next';
|
|
||||||
|
|
||||||
const mac = [
|
|
||||||
'rm ./javascript/index.js',
|
|
||||||
'rm ./style.css',
|
|
||||||
'cp ./dist/index.js ./javascript/index.js',
|
|
||||||
'cp ./dist/index.css ./style.css',
|
|
||||||
];
|
|
||||||
|
|
||||||
const win = [
|
|
||||||
'del javascript\\index.js',
|
|
||||||
'del style.css',
|
|
||||||
'copy dist\\index.js javascript\\index.js',
|
|
||||||
'copy dist\\index.css style.css',
|
|
||||||
];
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
routes: [{ path: '/', component: 'index' }],
|
|
||||||
npmClient: 'pnpm',
|
|
||||||
mpa: {},
|
|
||||||
codeSplitting: false,
|
|
||||||
define: {
|
|
||||||
'process.env': process.env,
|
|
||||||
},
|
|
||||||
extraBabelPlugins: [
|
|
||||||
[
|
|
||||||
'babel-plugin-styled-components',
|
|
||||||
{
|
|
||||||
minify: true,
|
|
||||||
transpileTemplateLiterals: true,
|
|
||||||
displayName: process.env.NODE_ENV === 'development',
|
|
||||||
pure: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
],
|
|
||||||
chainWebpack(memo) {
|
|
||||||
memo.plugin('minimizer').use(CssMinimizerPlugin, [
|
|
||||||
{
|
|
||||||
minify: CssMinimizerPlugin.lightningCssMinify,
|
|
||||||
minimizerOptions: {
|
|
||||||
targets: lightningcss.browserslistToTargets(browserslist('>= 0.25%')),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
memo.plugin('shell').use(WebpackShellPlugin, [
|
|
||||||
{
|
|
||||||
onBuildExit: {
|
|
||||||
scripts: process.platform === 'win32' ? win : mac,
|
|
||||||
blocking: false,
|
|
||||||
parallel: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
1190
javascript/index.js
1190
javascript/index.js
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
35
package.json
35
package.json
|
|
@ -2,6 +2,13 @@
|
||||||
"name": "sd-webui-kitchen-theme",
|
"name": "sd-webui-kitchen-theme",
|
||||||
"version": "1.8.3",
|
"version": "1.8.3",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
"keywords": [
|
||||||
|
"stable-diffusion-webui"
|
||||||
|
],
|
||||||
|
"homepage": "https://github.com/canisminor1990/sd-webui-kitchen-theme",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/canisminor1990/sd-webui-kitchen-theme/issues/new"
|
||||||
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/canisminor1990/sd-webui-kitchen-theme.git"
|
"url": "https://github.com/canisminor1990/sd-webui-kitchen-theme.git"
|
||||||
|
|
@ -10,8 +17,8 @@
|
||||||
"author": "canisminor1990 <i@canisminor.cc>",
|
"author": "canisminor1990 <i@canisminor.cc>",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "umi build",
|
"build": "tsc && vite build",
|
||||||
"dev": "umi build",
|
"dev": "vite",
|
||||||
"lint": "eslint \"src/**/*.{js,jsx,ts,tsx}\" --fix",
|
"lint": "eslint \"src/**/*.{js,jsx,ts,tsx}\" --fix",
|
||||||
"lint:md": "remark . --quiet --output",
|
"lint:md": "remark . --quiet --output",
|
||||||
"lint:style": "stylelint \"src/**/*.{css,less,js,jsx,ts,tsx}\" --fix",
|
"lint:style": "stylelint \"src/**/*.{css,less,js,jsx,ts,tsx}\" --fix",
|
||||||
|
|
@ -19,8 +26,6 @@
|
||||||
"prettier": "prettier -c --write \"**/**\"",
|
"prettier": "prettier -c --write \"**/**\"",
|
||||||
"release": "semantic-release",
|
"release": "semantic-release",
|
||||||
"sd-debug": "cd ../../ && ./webui.sh",
|
"sd-debug": "cd ../../ && ./webui.sh",
|
||||||
"setup": "umi setup",
|
|
||||||
"start": "umi build",
|
|
||||||
"test": "npm run lint",
|
"test": "npm run lint",
|
||||||
"type-check": "tsc -p tsconfig-check.json"
|
"type-check": "tsc -p tsconfig-check.json"
|
||||||
},
|
},
|
||||||
|
|
@ -49,18 +54,17 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@ant-design/icons": "^5",
|
"@ant-design/icons": "^5",
|
||||||
|
"@babel/plugin-syntax-import-assertions": "^7",
|
||||||
"@commitlint/cli": "^17",
|
"@commitlint/cli": "^17",
|
||||||
"@lobehub/lint": "latest",
|
"@lobehub/lint": "latest",
|
||||||
"@lobehub/ui": "latest",
|
"@lobehub/ui": "latest",
|
||||||
"@types/lodash-es": "^4",
|
"@types/lodash-es": "^4",
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
"@types/react": "^18",
|
"@types/react": "^18",
|
||||||
"@types/react-dnd": "^3",
|
|
||||||
"@types/react-dom": "^18",
|
"@types/react-dom": "^18",
|
||||||
"@types/react-rnd": "^8",
|
|
||||||
"@types/react-tag-input": "^6",
|
"@types/react-tag-input": "^6",
|
||||||
"@types/styled-components": "^5",
|
"@types/styled-components": "^5",
|
||||||
"@umijs/lint": "^4",
|
"@vitejs/plugin-react": "^4",
|
||||||
"ahooks": "^3",
|
"ahooks": "^3",
|
||||||
"antd": "^5",
|
"antd": "^5",
|
||||||
"antd-style": "latest",
|
"antd-style": "latest",
|
||||||
|
|
@ -70,6 +74,7 @@
|
||||||
"concurrently": "^8",
|
"concurrently": "^8",
|
||||||
"css-minimizer-webpack-plugin": "^5",
|
"css-minimizer-webpack-plugin": "^5",
|
||||||
"eslint": "^8",
|
"eslint": "^8",
|
||||||
|
"fast-deep-equal": "^3",
|
||||||
"husky": "^8",
|
"husky": "^8",
|
||||||
"lightningcss": "^1",
|
"lightningcss": "^1",
|
||||||
"lint-staged": "^13",
|
"lint-staged": "^13",
|
||||||
|
|
@ -79,10 +84,7 @@
|
||||||
"polished": "^4",
|
"polished": "^4",
|
||||||
"prettier": "^2",
|
"prettier": "^2",
|
||||||
"query-string": "^8",
|
"query-string": "^8",
|
||||||
"re-resizable": "^6",
|
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
"react-dnd": "^16",
|
|
||||||
"react-dnd-html5-backend": "^16",
|
|
||||||
"react-dom": "^18",
|
"react-dom": "^18",
|
||||||
"react-layout-kit": "^1",
|
"react-layout-kit": "^1",
|
||||||
"react-rnd": "^10",
|
"react-rnd": "^10",
|
||||||
|
|
@ -93,9 +95,18 @@
|
||||||
"styled-components": "latest",
|
"styled-components": "latest",
|
||||||
"stylelint": "^15",
|
"stylelint": "^15",
|
||||||
"typescript": "^5",
|
"typescript": "^5",
|
||||||
"umi": "^4",
|
"vite": "^4.3.9",
|
||||||
"use-merge-value": "^1",
|
|
||||||
"webpack-shell-plugin-next": "^2",
|
"webpack-shell-plugin-next": "^2",
|
||||||
"zustand": "^4"
|
"zustand": "^4"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"antd": ">=5",
|
||||||
|
"antd-style": ">=3",
|
||||||
|
"react": ">=18",
|
||||||
|
"react-dom": ">=18"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public",
|
||||||
|
"registry": "https://registry.npmjs.org"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { memo, useEffect, useState } from 'react';
|
||||||
|
import { shallow } from 'zustand/shallow';
|
||||||
|
|
||||||
|
import Layout from '@/layouts';
|
||||||
|
import Index from '@/pages';
|
||||||
|
import formatPrompt from '@/script/formatPrompt';
|
||||||
|
import promptBracketChecker from '@/script/promptBracketChecker';
|
||||||
|
import setupHead from '@/script/setupHead';
|
||||||
|
import { useAppStore } from '@/store';
|
||||||
|
|
||||||
|
const App = memo(() => {
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const setCurrentTab = useAppStore((st) => st.setCurrentTab, shallow);
|
||||||
|
useEffect(() => {
|
||||||
|
setupHead();
|
||||||
|
onUiLoaded(() => {
|
||||||
|
formatPrompt();
|
||||||
|
promptBracketChecker();
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
onUiUpdate(() => {
|
||||||
|
setCurrentTab();
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return <Layout>{!loading && <Index />}</Layout>;
|
||||||
|
});
|
||||||
|
|
||||||
|
export default App;
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
import RefreshRuntime from '/@react-refresh';
|
||||||
|
|
||||||
|
const RefreshSig = (type) => type;
|
||||||
|
|
||||||
|
RefreshRuntime.injectIntoGlobalHook(window);
|
||||||
|
window.$RefreshReg$ = () => {};
|
||||||
|
window.$RefreshSig$ = () => RefreshSig;
|
||||||
|
window.__vite_plugin_react_preamble_installed__ = true;
|
||||||
|
|
||||||
|
console.log('🚧 Injecting React Refresh');
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
import {FloatButton} from 'antd';
|
|
||||||
import {type ReactNode, memo, useRef} from 'react';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
import {shallow} from 'zustand/shallow';
|
|
||||||
|
|
||||||
import {useAppStore} from '@/store';
|
|
||||||
|
|
||||||
const ContentView = styled.div<{ isPromptResizable: boolean }>`
|
|
||||||
overflow-x: hidden;
|
|
||||||
overflow-y: auto;
|
|
||||||
flex: 1;
|
|
||||||
|
|
||||||
[id$='2img_prompt'] textarea {
|
|
||||||
max-height: ${({isPromptResizable}) => (isPromptResizable ? 'unset' : '84px')};
|
|
||||||
}
|
|
||||||
|
|
||||||
[id$='2img_neg_prompt'] textarea {
|
|
||||||
max-height: ${({isPromptResizable}) => (isPromptResizable ? 'unset' : '84px')};
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
interface ContentProps {
|
|
||||||
children: ReactNode;
|
|
||||||
loading?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Content = memo<ContentProps>(({children}) => {
|
|
||||||
const reference: any = useRef(null);
|
|
||||||
const [setting] = useAppStore((st) => [st.setting], shallow);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ContentView isPromptResizable={setting.promotTextarea === 'resizable'} ref={reference}>
|
|
||||||
{children}
|
|
||||||
<FloatButton.BackTop target={() => reference.current} />
|
|
||||||
</ContentView>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
export default Content;
|
|
||||||
|
|
@ -1,228 +0,0 @@
|
||||||
import {useHover} from 'ahooks';
|
|
||||||
import {ChevronDown, ChevronLeft, ChevronRight, ChevronUp} from 'lucide-react';
|
|
||||||
import type {Enable, NumberSize, Size} from 're-resizable';
|
|
||||||
import {HandleClassName, Resizable} from 're-resizable';
|
|
||||||
import {memo, useEffect, useMemo, useRef, useState} from 'react';
|
|
||||||
import {Center} from 'react-layout-kit';
|
|
||||||
import type {Props as RndProps} from 'react-rnd';
|
|
||||||
import useControlledState from 'use-merge-value';
|
|
||||||
|
|
||||||
import type {DivProps} from '@/types';
|
|
||||||
|
|
||||||
import {useStyle} from './style';
|
|
||||||
import {revesePlacement} from './utils';
|
|
||||||
|
|
||||||
const DEFAULT_HEIGHT = 180;
|
|
||||||
const DEFAULT_WIDTH = 280;
|
|
||||||
|
|
||||||
export type placementType = 'right' | 'left' | 'top' | 'bottom';
|
|
||||||
|
|
||||||
export interface DraggablePanelProps extends DivProps {
|
|
||||||
defaultExpand?: boolean;
|
|
||||||
defaultSize?: Partial<Size>;
|
|
||||||
destroyOnClose?: boolean;
|
|
||||||
expand?: boolean;
|
|
||||||
expandable?: boolean;
|
|
||||||
hanlderStyle?: React.CSSProperties;
|
|
||||||
minHeight?: number;
|
|
||||||
minWidth?: number;
|
|
||||||
mode?: 'fixed' | 'float';
|
|
||||||
onExpandChange?: (expand: boolean) => void;
|
|
||||||
onSizeChange?: (delta: NumberSize, size?: Size) => void;
|
|
||||||
onSizeDragging?: (delta: NumberSize, size?: Size) => void;
|
|
||||||
pin?: boolean;
|
|
||||||
placement: placementType;
|
|
||||||
resize?: RndProps['enableResizing'];
|
|
||||||
showHandlerWhenUnexpand?: boolean;
|
|
||||||
size?: Partial<Size>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const DraggablePanel = memo<DraggablePanelProps>(
|
|
||||||
({
|
|
||||||
pin = 'true',
|
|
||||||
mode = 'fixed',
|
|
||||||
children,
|
|
||||||
placement = 'right',
|
|
||||||
resize,
|
|
||||||
style,
|
|
||||||
size,
|
|
||||||
defaultSize: customizeDefaultSize,
|
|
||||||
minWidth,
|
|
||||||
minHeight,
|
|
||||||
onSizeChange,
|
|
||||||
onSizeDragging,
|
|
||||||
expandable = true,
|
|
||||||
expand,
|
|
||||||
defaultExpand = true,
|
|
||||||
onExpandChange,
|
|
||||||
className,
|
|
||||||
showHandlerWhenUnexpand,
|
|
||||||
destroyOnClose,
|
|
||||||
hanlderStyle,
|
|
||||||
}) => {
|
|
||||||
const reference = useRef(null);
|
|
||||||
const isHovering = useHover(reference);
|
|
||||||
const isVertical = placement === 'top' || placement === 'bottom';
|
|
||||||
|
|
||||||
const {styles, cx} = useStyle('draggable-panel');
|
|
||||||
|
|
||||||
const [isExpand, setIsExpand] = useControlledState(defaultExpand, {
|
|
||||||
onChange: onExpandChange,
|
|
||||||
value: expand,
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (pin) return;
|
|
||||||
if (isHovering && !isExpand) {
|
|
||||||
setIsExpand(true);
|
|
||||||
} else if (!isHovering && isExpand) {
|
|
||||||
setIsExpand(false);
|
|
||||||
}
|
|
||||||
}, [pin, isHovering, isExpand]);
|
|
||||||
|
|
||||||
const [showExpand, setShowExpand] = useState(true);
|
|
||||||
|
|
||||||
const canResizing = resize !== false && isExpand;
|
|
||||||
|
|
||||||
const resizeHandleClassNames: HandleClassName = useMemo(() => {
|
|
||||||
if (!canResizing) return {};
|
|
||||||
|
|
||||||
return {
|
|
||||||
[revesePlacement(placement)]: styles[`${revesePlacement(placement)}Handle`],
|
|
||||||
};
|
|
||||||
}, [canResizing, placement]);
|
|
||||||
|
|
||||||
const resizing = {
|
|
||||||
bottom: false,
|
|
||||||
bottomLeft: false,
|
|
||||||
bottomRight: false,
|
|
||||||
left: false,
|
|
||||||
right: false,
|
|
||||||
top: false,
|
|
||||||
topLeft: false,
|
|
||||||
topRight: false,
|
|
||||||
[revesePlacement(placement)]: true,
|
|
||||||
...(resize as Enable),
|
|
||||||
};
|
|
||||||
|
|
||||||
const defaultSize: Size = useMemo(() => {
|
|
||||||
if (isVertical) {
|
|
||||||
return {
|
|
||||||
height: DEFAULT_HEIGHT,
|
|
||||||
width: '100%',
|
|
||||||
...customizeDefaultSize,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
height: '100%',
|
|
||||||
width: DEFAULT_WIDTH,
|
|
||||||
...customizeDefaultSize,
|
|
||||||
};
|
|
||||||
}, [isVertical]);
|
|
||||||
|
|
||||||
const sizeProps = isExpand ?
|
|
||||||
{
|
|
||||||
defaultSize,
|
|
||||||
minHeight: typeof minHeight === 'number' ? Math.max(minHeight, 0) : undefined,
|
|
||||||
minWidth: typeof minWidth === 'number' ? Math.max(minWidth, 0) : 280,
|
|
||||||
size: size as Size,
|
|
||||||
} :
|
|
||||||
isVertical ?
|
|
||||||
{
|
|
||||||
minHeight: 0,
|
|
||||||
size: {height: 0},
|
|
||||||
} :
|
|
||||||
{
|
|
||||||
minWidth: 0,
|
|
||||||
size: {width: 0},
|
|
||||||
};
|
|
||||||
|
|
||||||
const {Arrow, className: arrowPlacement} = useMemo(() => {
|
|
||||||
switch (placement) {
|
|
||||||
case 'top': {
|
|
||||||
return {Arrow: ChevronDown, className: 'Bottom'};
|
|
||||||
}
|
|
||||||
case 'bottom': {
|
|
||||||
return {Arrow: ChevronUp, className: 'Top'};
|
|
||||||
}
|
|
||||||
case 'right': {
|
|
||||||
return {Arrow: ChevronLeft, className: 'Left'};
|
|
||||||
}
|
|
||||||
case 'left': {
|
|
||||||
return {Arrow: ChevronRight, className: 'Right'};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [styles, placement]);
|
|
||||||
|
|
||||||
const handler = (
|
|
||||||
// @ts-ignore
|
|
||||||
<Center
|
|
||||||
// @ts-ignore
|
|
||||||
className={cx(styles[`toggle${arrowPlacement}`])}
|
|
||||||
style={{opacity: isExpand ? (pin ? undefined : 0) : showHandlerWhenUnexpand ? 1 : 0}}
|
|
||||||
>
|
|
||||||
<Center
|
|
||||||
onClick={() => {
|
|
||||||
setIsExpand(!isExpand);
|
|
||||||
}}
|
|
||||||
style={hanlderStyle}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={styles.handlerIcon}
|
|
||||||
style={{transform: `rotate(${isExpand ? 180 : 0}deg)`}}
|
|
||||||
>
|
|
||||||
<Arrow size={16} strokeWidth={1.5} />
|
|
||||||
</div>
|
|
||||||
</Center>
|
|
||||||
</Center>
|
|
||||||
);
|
|
||||||
|
|
||||||
const inner = (
|
|
||||||
// @ts-ignore
|
|
||||||
<Resizable
|
|
||||||
{...sizeProps}
|
|
||||||
className={styles.panel}
|
|
||||||
enable={canResizing ? (resizing as Enable) : undefined}
|
|
||||||
handleClasses={resizeHandleClassNames}
|
|
||||||
onResize={(_, direction, reference_, delta) => {
|
|
||||||
onSizeDragging?.(delta, {
|
|
||||||
height: reference_.style.height,
|
|
||||||
width: reference_.style.width,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
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;
|
|
||||||
|
|
@ -1,267 +0,0 @@
|
||||||
import {createStyles, css, cx} from 'antd-style';
|
|
||||||
|
|
||||||
const offset = 17;
|
|
||||||
const toggleLength = 40;
|
|
||||||
const toggleShort = 16;
|
|
||||||
|
|
||||||
export const useStyle = createStyles(({token}, prefix: string) => {
|
|
||||||
const commonHandle = css`
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
z-index: 50;
|
|
||||||
transition: all 0.2s ${token.motionEaseOut};
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:active {
|
|
||||||
&::before {
|
|
||||||
background: ${token.colorPrimary};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const commonToggle = css`
|
|
||||||
position: absolute;
|
|
||||||
z-index: 1001;
|
|
||||||
opacity: 0;
|
|
||||||
transition: all 0.2s ${token.motionEaseOut};
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
opacity: 1 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
opacity: 1 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
> div {
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
color: ${token.colorTextTertiary};
|
|
||||||
|
|
||||||
background: ${token.colorFillTertiary};
|
|
||||||
border-color: ${token.colorBorderSecondary};
|
|
||||||
border-style: solid;
|
|
||||||
border-width: 1px;
|
|
||||||
border-radius: 4px;
|
|
||||||
|
|
||||||
transition: all 0.2s ${token.motionEaseOut};
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: ${token.colorTextSecondary};
|
|
||||||
background: ${token.colorFillSecondary};
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
color: ${token.colorText};
|
|
||||||
background: ${token.colorFill};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const float = css`
|
|
||||||
position: absolute;
|
|
||||||
z-index: 2000;
|
|
||||||
`;
|
|
||||||
|
|
||||||
return {
|
|
||||||
bottomFloat: cx(
|
|
||||||
float,
|
|
||||||
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;
|
|
||||||
border: 0 solid ${token.colorBorderSecondary};
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
.${prefix}-toggle {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
),
|
|
||||||
fixed: css`
|
|
||||||
position: relative;
|
|
||||||
`,
|
|
||||||
handlerIcon: css`
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
transition: all 0.2s ${token.motionEaseOut};
|
|
||||||
`,
|
|
||||||
leftFloat: cx(
|
|
||||||
float,
|
|
||||||
css`
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
height: 100%;
|
|
||||||
`,
|
|
||||||
),
|
|
||||||
leftHandle: cx(
|
|
||||||
css`
|
|
||||||
${commonHandle};
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
left: 50%;
|
|
||||||
width: 2px;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
`${prefix}-left-handle`,
|
|
||||||
),
|
|
||||||
panel: cx(
|
|
||||||
`${prefix}-fixed`,
|
|
||||||
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;
|
|
||||||
width: 100%;
|
|
||||||
height: ${toggleShort}px;
|
|
||||||
|
|
||||||
> div {
|
|
||||||
left: 50%;
|
|
||||||
|
|
||||||
width: ${toggleLength}px;
|
|
||||||
height: 16px;
|
|
||||||
margin-left: -20px;
|
|
||||||
|
|
||||||
border-radius: 0 0 4px 4px;
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
),
|
|
||||||
toggleLeft: cx(
|
|
||||||
`${prefix}-toggle`,
|
|
||||||
`${prefix}-toggle-left`,
|
|
||||||
commonToggle,
|
|
||||||
css`
|
|
||||||
left: -${offset}px;
|
|
||||||
width: ${toggleShort}px;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
> 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%;
|
|
||||||
|
|
||||||
width: ${toggleLength}px;
|
|
||||||
height: ${toggleShort}px;
|
|
||||||
margin-left: -20px;
|
|
||||||
|
|
||||||
border-radius: 4px 4px 0 0;
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
),
|
|
||||||
topFloat: cx(
|
|
||||||
float,
|
|
||||||
css`
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
`,
|
|
||||||
),
|
|
||||||
topHandle: cx(
|
|
||||||
`${prefix}-top-handle`,
|
|
||||||
css`
|
|
||||||
${commonHandle};
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
top: 50%;
|
|
||||||
width: 100%;
|
|
||||||
height: 2px;
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
import {placementType} from './index';
|
|
||||||
|
|
||||||
export const revesePlacement = (placement: placementType) => {
|
|
||||||
switch (placement) {
|
|
||||||
case 'bottom': {
|
|
||||||
return 'top';
|
|
||||||
}
|
|
||||||
case 'top': {
|
|
||||||
return 'bottom';
|
|
||||||
}
|
|
||||||
case 'right': {
|
|
||||||
return 'left';
|
|
||||||
}
|
|
||||||
case 'left': {
|
|
||||||
return 'right';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -1,114 +0,0 @@
|
||||||
import {ZoomInOutlined} from '@ant-design/icons';
|
|
||||||
import {DraggablePanel} from '@lobehub/ui';
|
|
||||||
import {Slider} from 'antd';
|
|
||||||
import {useResponsive} from 'antd-style';
|
|
||||||
import {type CSSProperties, type ReactNode, memo, useEffect, useState} from 'react';
|
|
||||||
import styled, {createGlobalStyle} from 'styled-components';
|
|
||||||
import {shallow} from 'zustand/shallow';
|
|
||||||
|
|
||||||
import {useAppStore} from '@/store';
|
|
||||||
|
|
||||||
const GlobalStyle = createGlobalStyle`
|
|
||||||
button#txt2img_extra_networks,
|
|
||||||
button#img2img_extra_networks {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const View = styled.div`
|
|
||||||
overflow: hidden;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
height: var(--fill-available);
|
|
||||||
`;
|
|
||||||
|
|
||||||
const SidebarView = styled.div<{ size: number }>`
|
|
||||||
overflow-x: hidden;
|
|
||||||
overflow-y: auto;
|
|
||||||
flex: 1;
|
|
||||||
padding: 16px;
|
|
||||||
|
|
||||||
#txt2img_extra_networks,
|
|
||||||
#img2img_extra_networks {
|
|
||||||
display: block !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.extra-network-cards .actions .name {
|
|
||||||
background: unset !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.extra-network-cards,
|
|
||||||
.extra-network-thumbs {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fill, minmax(${({size}) => size}px, 1fr));
|
|
||||||
|
|
||||||
> .card {
|
|
||||||
width: var(--fill-available) !important;
|
|
||||||
height: ${({size}) => size * 1.5}px !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Footer = styled.div`
|
|
||||||
display: flex;
|
|
||||||
flex: 0;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: flex-start;
|
|
||||||
|
|
||||||
padding: 8px 16px;
|
|
||||||
|
|
||||||
border-top: 1px solid var(--color-border);
|
|
||||||
`;
|
|
||||||
|
|
||||||
const ZoomSlider = styled(Slider)`
|
|
||||||
flex: 1;
|
|
||||||
margin-left: 16px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
interface SidebarProps {
|
|
||||||
children: ReactNode;
|
|
||||||
loading?: boolean;
|
|
||||||
style?: CSSProperties;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Sidebar = memo<SidebarProps>(({children, style}) => {
|
|
||||||
const {mobile} = useResponsive();
|
|
||||||
const [setting] = useAppStore((st) => [st.setting], shallow);
|
|
||||||
const [mode] = useState<'fixed' | 'float'>(setting.extraNetworkFixedMode);
|
|
||||||
const [expand, setExpand] = useState<boolean>(setting.extraNetworkSidebarExpand);
|
|
||||||
const [size, setSize] = useState<number>(setting?.extraNetworkCardSize || 86);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (mobile) setExpand(false);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<GlobalStyle />
|
|
||||||
<DraggablePanel
|
|
||||||
defaultSize={{width: setting.extraNetworkSidebarWidth}}
|
|
||||||
expand={expand}
|
|
||||||
minWidth={setting.extraNetworkSidebarWidth}
|
|
||||||
mode={mode}
|
|
||||||
onExpandChange={setExpand}
|
|
||||||
pin={mode === 'fixed'}
|
|
||||||
placement="right"
|
|
||||||
style={{
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
...style,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<View>
|
|
||||||
<SidebarView size={size}>{children}</SidebarView>
|
|
||||||
<Footer>
|
|
||||||
<ZoomInOutlined />
|
|
||||||
<ZoomSlider defaultValue={size} max={256} min={64} onChange={setSize} step={8} />
|
|
||||||
</Footer>
|
|
||||||
</View>
|
|
||||||
</DraggablePanel>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
export default Sidebar;
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
import {memo, useEffect} from 'react';
|
|
||||||
|
|
||||||
interface GiscusProps {
|
|
||||||
themeMode: 'light' | 'dark';
|
|
||||||
}
|
|
||||||
const Giscus = memo<GiscusProps>(({themeMode}) => {
|
|
||||||
useEffect(() => {
|
|
||||||
// giscus
|
|
||||||
const giscus: HTMLScriptElement = document.createElement('script');
|
|
||||||
giscus.src = 'https://giscus.app/client.js';
|
|
||||||
giscus.dataset.repo = 'canisminor1990/sd-webui-kitchen-theme';
|
|
||||||
giscus.dataset.repoId = 'R_kgDOJCPcNg';
|
|
||||||
giscus.dataset.mapping = 'number';
|
|
||||||
giscus.dataset.term = '53';
|
|
||||||
giscus.dataset.reactionsEnabled = '1';
|
|
||||||
giscus.dataset.emitMetadata = '0';
|
|
||||||
giscus.dataset.inputPosition = 'bottom';
|
|
||||||
giscus.dataset.theme = themeMode;
|
|
||||||
giscus.dataset.lang = 'en';
|
|
||||||
giscus.crossOrigin = 'anonymous';
|
|
||||||
giscus.async = true;
|
|
||||||
document.querySelectorAll('head')[0].append(giscus);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return <div className="giscus" id="giscus" />;
|
|
||||||
});
|
|
||||||
|
|
||||||
export default Giscus;
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
import {TabsNav, type TabsNavProps} from '@lobehub/ui';
|
|
||||||
import {memo, useEffect, useState} from 'react';
|
|
||||||
|
|
||||||
const getNavButtons: HTMLButtonElement[] | any = () =>
|
|
||||||
gradioApp().querySelectorAll('#tabs > .tab-nav:first-child button') || [];
|
|
||||||
|
|
||||||
const onChange: TabsNavProps['onChange'] = (activeKey) => {
|
|
||||||
const buttons = getNavButtons();
|
|
||||||
buttons[Number(activeKey)]?.click();
|
|
||||||
};
|
|
||||||
|
|
||||||
const Nav = memo(() => {
|
|
||||||
const [items, setItems] = useState<TabsNavProps['items']>([]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
onUiLoaded(() => {
|
|
||||||
const buttons = getNavButtons();
|
|
||||||
const list: TabsNavProps['items'] | any = [];
|
|
||||||
buttons.forEach((button: HTMLButtonElement | any, index: number) => {
|
|
||||||
button.id = `kitchen-nav-${index}`;
|
|
||||||
list.push({
|
|
||||||
key: String(index),
|
|
||||||
label: button.textContent,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
setItems(list.filter(Boolean));
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return <TabsNav items={items} onChange={onChange} />;
|
|
||||||
});
|
|
||||||
|
|
||||||
export default Nav;
|
|
||||||
|
|
@ -1,123 +0,0 @@
|
||||||
import {SettingOutlined} from '@ant-design/icons';
|
|
||||||
import {Button, Divider, Form, InputNumber, Popover, Segmented, Space, Switch} from 'antd';
|
|
||||||
import {memo, useCallback} from 'react';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
import {shallow} from 'zustand/shallow';
|
|
||||||
|
|
||||||
import {WebuiSetting, defaultSetting, useAppStore} from '@/store';
|
|
||||||
|
|
||||||
/******************************************************
|
|
||||||
*********************** Style *************************
|
|
||||||
******************************************************/
|
|
||||||
|
|
||||||
const FormItem = styled(Form.Item)`
|
|
||||||
margin-bottom: 8px;
|
|
||||||
|
|
||||||
.ant-row {
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
> div {
|
|
||||||
flex: unset !important;
|
|
||||||
flex-grow: unset !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Title = styled.div`
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 600;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const SubTitle = styled.div`
|
|
||||||
margin-bottom: 4px;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
`;
|
|
||||||
|
|
||||||
/******************************************************
|
|
||||||
************************* Dom *************************
|
|
||||||
******************************************************/
|
|
||||||
|
|
||||||
const Setting = memo(() => {
|
|
||||||
const [setting, onSetSetting] = useAppStore((st) => [st.setting, st.onSetSetting], shallow);
|
|
||||||
|
|
||||||
const onReset = useCallback(() => {
|
|
||||||
onSetSetting(defaultSetting);
|
|
||||||
gradioApp().querySelector('#settings_restart_gradio')?.click();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const onFinish = useCallback((value: WebuiSetting) => {
|
|
||||||
onSetSetting(value);
|
|
||||||
gradioApp().querySelector('#settings_restart_gradio')?.click();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Popover
|
|
||||||
content={
|
|
||||||
<Form
|
|
||||||
initialValues={setting}
|
|
||||||
layout="horizontal"
|
|
||||||
onFinish={onFinish}
|
|
||||||
size="small"
|
|
||||||
style={{maxWidth: 260}}
|
|
||||||
>
|
|
||||||
<Divider style={{margin: '4px 0 8px'}} />
|
|
||||||
<SubTitle>Promot Textarea</SubTitle>
|
|
||||||
<FormItem label="Display mode" name="promotTextarea">
|
|
||||||
<Segmented options={['scroll', 'resizable']} />
|
|
||||||
</FormItem>
|
|
||||||
<Divider style={{margin: '4px 0 8px'}} />
|
|
||||||
<SubTitle>Sidebar</SubTitle>
|
|
||||||
<FormItem label="Default expand" name="sidebarExpand" valuePropName="checked">
|
|
||||||
<Switch />
|
|
||||||
</FormItem>
|
|
||||||
<FormItem label="Display mode" name="sidebarFixedMode">
|
|
||||||
<Segmented options={['fixed', 'float']} />
|
|
||||||
</FormItem>
|
|
||||||
<FormItem label="Default width" name="sidebarWidth">
|
|
||||||
<InputNumber />
|
|
||||||
</FormItem>
|
|
||||||
<Divider style={{margin: '4px 0 8px'}} />
|
|
||||||
<SubTitle>ExtraNetwork Sidebar</SubTitle>
|
|
||||||
<FormItem label="Enable" name="enableExtraNetworkSidebar" valuePropName="checked">
|
|
||||||
<Switch />
|
|
||||||
</FormItem>
|
|
||||||
<FormItem label="Display mode" name="extraNetworkFixedMode" valuePropName="checked">
|
|
||||||
<Segmented options={['fixed', 'float']} />
|
|
||||||
</FormItem>
|
|
||||||
<FormItem label="Default expand" name="extraNetworkSidebarExpand" valuePropName="checked">
|
|
||||||
<Switch />
|
|
||||||
</FormItem>
|
|
||||||
<FormItem label="Default width" name="extraNetworkSidebarWidth">
|
|
||||||
<InputNumber />
|
|
||||||
</FormItem>
|
|
||||||
<FormItem label="Default card size" name="extraNetworkCardSize">
|
|
||||||
<InputNumber />
|
|
||||||
</FormItem>
|
|
||||||
<Divider style={{margin: '4px 0 8px'}} />
|
|
||||||
<SubTitle>Other</SubTitle>
|
|
||||||
<FormItem label="Use svg icons" name="svgIcon" valuePropName="checked">
|
|
||||||
<Switch />
|
|
||||||
</FormItem>
|
|
||||||
<Divider style={{margin: '4px 0 16px'}} />
|
|
||||||
<FormItem>
|
|
||||||
<Space>
|
|
||||||
<Button htmlType="button" onClick={onReset} style={{borderRadius: 4}}>
|
|
||||||
Reset
|
|
||||||
</Button>
|
|
||||||
<Button htmlType="submit" style={{borderRadius: 4}} type="primary">
|
|
||||||
Apply and restart UI
|
|
||||||
</Button>
|
|
||||||
</Space>
|
|
||||||
</FormItem>
|
|
||||||
</Form>
|
|
||||||
}
|
|
||||||
title={<Title>⚙ Setting</Title>}
|
|
||||||
trigger="click"
|
|
||||||
>
|
|
||||||
<Button icon={<SettingOutlined />} title="Setting" />
|
|
||||||
</Popover>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
export default Setting;
|
|
||||||
|
|
@ -1,94 +0,0 @@
|
||||||
import {BoldOutlined, GithubOutlined} from '@ant-design/icons';
|
|
||||||
import {Header as H} from '@lobehub/ui';
|
|
||||||
import {Button, Modal, Space} from 'antd';
|
|
||||||
import qs from 'query-string';
|
|
||||||
import {type ReactNode, memo, useCallback, useState} from 'react';
|
|
||||||
import {shallow} from 'zustand/shallow';
|
|
||||||
|
|
||||||
import {useAppStore} from '@/store';
|
|
||||||
|
|
||||||
import Giscus from './Giscus';
|
|
||||||
import Logo from './Logo';
|
|
||||||
import Nav from './Nav';
|
|
||||||
import Setting from './Setting';
|
|
||||||
import {civitaiLogo, themeIcon} from './style';
|
|
||||||
|
|
||||||
interface HeaderProps {
|
|
||||||
children: ReactNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Header = memo<HeaderProps>(({children}) => {
|
|
||||||
const [themeMode] = useAppStore((st) => [st.themeMode], shallow);
|
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
||||||
|
|
||||||
const handleSetTheme = useCallback(() => {
|
|
||||||
const theme = themeMode === 'light' ? 'dark' : 'light';
|
|
||||||
const gradioURL = qs.parseUrl(window.location.href);
|
|
||||||
gradioURL.query.__theme = theme;
|
|
||||||
window.location.replace(qs.stringifyUrl(gradioURL));
|
|
||||||
}, [themeMode]);
|
|
||||||
|
|
||||||
const showModal = () => setIsModalOpen(true);
|
|
||||||
|
|
||||||
const handleCancel = () => setIsModalOpen(false);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<H
|
|
||||||
actions={
|
|
||||||
<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" />
|
|
||||||
</a>
|
|
||||||
<Button icon={<GithubOutlined />} onClick={showModal} title="Feedback" />
|
|
||||||
<Setting />
|
|
||||||
<Button icon={themeIcon[themeMode]} onClick={handleSetTheme} title="Switch Theme" />
|
|
||||||
</Space.Compact>
|
|
||||||
}
|
|
||||||
logo={
|
|
||||||
<a
|
|
||||||
href="https://github.com/canisminor1990/sd-webui-kitchen-theme"
|
|
||||||
rel="noreferrer"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
<Logo themeMode={themeMode} />
|
|
||||||
</a>
|
|
||||||
}
|
|
||||||
nav={
|
|
||||||
<>
|
|
||||||
<Nav />
|
|
||||||
{children}
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<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;
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
export const themeIcon = {
|
|
||||||
dark: (
|
|
||||||
<span className="anticon anticon-github" role="img">
|
|
||||||
<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>
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
),
|
|
||||||
light: (
|
|
||||||
<span className="anticon anticon-github" role="img">
|
|
||||||
<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>
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
export const darkLogo =
|
|
||||||
'https://gw.alipayobjects.com/zos/bmw-prod/9ecb2822-1592-4cb0-a087-ce0097fef2ca.svg';
|
|
||||||
export const lightLogo =
|
|
||||||
'https://gw.alipayobjects.com/zos/bmw-prod/e146116d-c65a-4306-a3d2-bb8d05e1c49b.svg';
|
|
||||||
|
|
||||||
export const civitaiLogo = (
|
|
||||||
<span className="anticon civitai" role="img">
|
|
||||||
<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" />
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
|
|
@ -1,101 +0,0 @@
|
||||||
import {memo, useCallback, useState} from 'react';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
|
|
||||||
import TagList, {PromptType, TagItem} from './TagList';
|
|
||||||
import {formatPrompt} from './utils';
|
|
||||||
|
|
||||||
/******************************************************
|
|
||||||
*********************** Style *************************
|
|
||||||
******************************************************/
|
|
||||||
|
|
||||||
const View = styled.div`
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 8px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const ButtonGroup = styled.div`
|
|
||||||
display: flex;
|
|
||||||
gap: 8px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Button = styled.button`
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex: 1;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
padding: var(--input-padding);
|
|
||||||
|
|
||||||
font-size: var(--input-text-size);
|
|
||||||
font-weight: var(--input-text-weight);
|
|
||||||
line-height: var(--line-sm);
|
|
||||||
|
|
||||||
background: var(--button-secondary-background-fill);
|
|
||||||
border: var(--button-border-width) solid var(--button-secondary-border-color);
|
|
||||||
border-radius: var(--input-radius);
|
|
||||||
`;
|
|
||||||
|
|
||||||
/******************************************************
|
|
||||||
************************* Dom *************************
|
|
||||||
******************************************************/
|
|
||||||
|
|
||||||
interface PromptProps {
|
|
||||||
type: PromptType;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Prompt = memo<PromptProps>(({type}) => {
|
|
||||||
const [tags, setTags] = useState<TagItem[]>([]);
|
|
||||||
|
|
||||||
const id =
|
|
||||||
type === 'positive' ? "[id$='2img_prompt'] textarea" : "[id$='2img_neg_prompt'] textarea";
|
|
||||||
|
|
||||||
const getValue = useCallback(() => {
|
|
||||||
try {
|
|
||||||
const textarea: HTMLTextAreaElement | any = get_uiCurrentTabContent().querySelector(id);
|
|
||||||
if (textarea) setTags(formatPrompt(textarea.value));
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const setValue = useCallback(() => {
|
|
||||||
try {
|
|
||||||
const textarea: HTMLTextAreaElement | any = get_uiCurrentTabContent().querySelector(id);
|
|
||||||
if (textarea) textarea.value = tags.map((t) => t.text).join(', ');
|
|
||||||
updateInput(textarea);
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}, [tags]);
|
|
||||||
|
|
||||||
const setCurrentValue = useCallback((currentTags: TagItem[]) => {
|
|
||||||
try {
|
|
||||||
const textarea: HTMLTextAreaElement | any = get_uiCurrentTabContent().querySelector(id);
|
|
||||||
if (textarea) textarea.value = currentTags.map((t) => t.text).join(', ');
|
|
||||||
updateInput(textarea);
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View>
|
|
||||||
<TagList setTags={setTags} setValue={setCurrentValue} tags={tags} type={type} />
|
|
||||||
<ButtonGroup>
|
|
||||||
<Button onClick={getValue} title="Load Prompt">
|
|
||||||
🔄
|
|
||||||
</Button>
|
|
||||||
<Button onClick={setValue} title="Set Prompt">
|
|
||||||
➡️
|
|
||||||
</Button>
|
|
||||||
</ButtonGroup>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
export default Prompt;
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
import {DraggablePanel} from '@lobehub/ui';
|
|
||||||
import {useResponsive} from 'antd-style';
|
|
||||||
import {type CSSProperties, type ReactNode, memo, useEffect, useState} from 'react';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
import {shallow} from 'zustand/shallow';
|
|
||||||
|
|
||||||
import {useAppStore} from '@/store';
|
|
||||||
|
|
||||||
import PromptGroup from './PromptGroup';
|
|
||||||
|
|
||||||
/******************************************************
|
|
||||||
*********************** Style *************************
|
|
||||||
******************************************************/
|
|
||||||
|
|
||||||
const SidebarView = styled.div`
|
|
||||||
overflow-x: hidden;
|
|
||||||
overflow-y: auto;
|
|
||||||
height: var(--fill-available);
|
|
||||||
padding: 16px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
/******************************************************
|
|
||||||
************************* Dom *************************
|
|
||||||
******************************************************/
|
|
||||||
|
|
||||||
interface SidebarProps {
|
|
||||||
children: ReactNode;
|
|
||||||
loading?: boolean;
|
|
||||||
style?: CSSProperties;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Sidebar = memo<SidebarProps>(({children, loading, style}) => {
|
|
||||||
const [setting] = useAppStore((st) => [st.setting], shallow);
|
|
||||||
const [mode] = useState<'fixed' | 'float'>(setting.sidebarFixedMode);
|
|
||||||
const {mobile} = useResponsive();
|
|
||||||
const [expand, setExpand] = useState<boolean>(setting.sidebarExpand);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (mobile) setExpand(false);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DraggablePanel
|
|
||||||
defaultSize={{width: setting.sidebarWidth}}
|
|
||||||
expand={expand}
|
|
||||||
minWidth={setting.sidebarWidth}
|
|
||||||
mode={mode}
|
|
||||||
onExpandChange={setExpand}
|
|
||||||
pin={mode === 'fixed'}
|
|
||||||
placement="left"
|
|
||||||
style={{
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
...style,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<SidebarView>
|
|
||||||
{!loading && <PromptGroup />}
|
|
||||||
{children}
|
|
||||||
</SidebarView>
|
|
||||||
</DraggablePanel>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
export default Sidebar;
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
import negativeData from '@/data/negative.json';
|
|
||||||
import positiveData from '@/data/positive.json';
|
|
||||||
import {Converter} from '@/script/formatPrompt';
|
|
||||||
|
|
||||||
import {TagItem} from './TagList';
|
|
||||||
|
|
||||||
export const genTagType = (tag: TagItem): TagItem => {
|
|
||||||
const newTag = tag;
|
|
||||||
if (newTag.text.includes('<lora')) {
|
|
||||||
newTag.className = 'ReactTags__lora';
|
|
||||||
} else if (newTag.text.includes('<hypernet')) {
|
|
||||||
newTag.className = 'ReactTags__hypernet';
|
|
||||||
} else if (newTag.text.includes('<embedding')) {
|
|
||||||
newTag.className = 'ReactTags__embedding';
|
|
||||||
} else {
|
|
||||||
newTag.className = undefined;
|
|
||||||
}
|
|
||||||
return newTag;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const formatPrompt = (value: string) => {
|
|
||||||
const text = Converter.convertStr(value);
|
|
||||||
const textArray = Converter.convertStr2Array(text).map((item) => {
|
|
||||||
if (item.includes('<')) return item;
|
|
||||||
const newItem = item
|
|
||||||
.replaceAll(/\s+/g, ' ')
|
|
||||||
.replaceAll(/,|\.\|。/g, ',')
|
|
||||||
.replaceAll(/“|‘|”|"|\/'/g, '')
|
|
||||||
.replaceAll(', ', ',')
|
|
||||||
.replaceAll(',,', ',')
|
|
||||||
.replaceAll(',', ', ');
|
|
||||||
return Converter.convertStr2Array(newItem).join(', ');
|
|
||||||
});
|
|
||||||
return textArray.map((tag) => genTagType({id: tag, text: tag}));
|
|
||||||
};
|
|
||||||
|
|
||||||
const genSuggestions = (array: string[]) =>
|
|
||||||
array.map((text) => {
|
|
||||||
return {
|
|
||||||
id: text,
|
|
||||||
text,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
export const suggestions = {
|
|
||||||
negative: genSuggestions(negativeData),
|
|
||||||
positive: genSuggestions(positiveData),
|
|
||||||
};
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
export { default as Content } from './Content';
|
|
||||||
export { default as DraggablePanel } from './DraggablePanel';
|
|
||||||
export { default as ExtraNetworkSidebar } from './ExtraNetworkSidebar';
|
|
||||||
export { default as Header } from './Header';
|
|
||||||
export { default as Sidebar } from './Sidebar';
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
import {useEffect, useState} from 'react';
|
|
||||||
|
|
||||||
function checkIsDarkMode() {
|
|
||||||
try {
|
|
||||||
return window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
||||||
} catch {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useIsDarkMode() {
|
|
||||||
const [isDarkMode, setIsDarkMode] = useState(checkIsDarkMode());
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const mqList = window.matchMedia('(prefers-color-scheme: dark)');
|
|
||||||
|
|
||||||
const listener = (event: any) => {
|
|
||||||
setIsDarkMode(event.matches);
|
|
||||||
};
|
|
||||||
|
|
||||||
mqList.addEventListener('change', listener);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
mqList.removeEventListener('change', listener);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return isDarkMode;
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
function checkIsDarkMode() {
|
||||||
|
try {
|
||||||
|
return window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useIsDarkMode() {
|
||||||
|
const [isDarkMode, setIsDarkMode] = useState(checkIsDarkMode());
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const mqList = window.matchMedia('(prefers-color-scheme: dark)');
|
||||||
|
|
||||||
|
const listener = (event: any) => {
|
||||||
|
setIsDarkMode(event.matches);
|
||||||
|
};
|
||||||
|
|
||||||
|
mqList.addEventListener('change', listener);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
mqList.removeEventListener('change', listener);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return isDarkMode;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,298 @@
|
||||||
|
import { createGlobalStyle, css } from 'antd-style';
|
||||||
|
import { readableColor } from 'polished';
|
||||||
|
|
||||||
|
const GlobalStyle = createGlobalStyle`
|
||||||
|
${({ theme }) => {
|
||||||
|
const checkBoxIcon = `data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='${encodeURIComponent(
|
||||||
|
theme.colorBgLayout,
|
||||||
|
)}' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e`;
|
||||||
|
const radioIcon = `data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='${encodeURIComponent(
|
||||||
|
theme.colorBgLayout,
|
||||||
|
)}' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e`;
|
||||||
|
return css`
|
||||||
|
:root,
|
||||||
|
.dark {
|
||||||
|
--primary-50: ${theme.geekblue1};
|
||||||
|
--primary-100: ${theme.geekblue2};
|
||||||
|
--primary-200: ${theme.geekblue3};
|
||||||
|
--primary-300: ${theme.geekblue4};
|
||||||
|
--primary-400: ${theme.geekblue5};
|
||||||
|
--primary-500: ${theme.geekblue6};
|
||||||
|
--primary-600: ${theme.geekblue7};
|
||||||
|
--primary-700: ${theme.geekblue8};
|
||||||
|
--primary-800: ${theme.geekblue9};
|
||||||
|
--primary-900: ${theme.geekblue10};
|
||||||
|
--primary-950: ${theme.geekblue11};
|
||||||
|
--secondary-50: ${theme.geekblue1};
|
||||||
|
--secondary-100: ${theme.geekblue2};
|
||||||
|
--secondary-200: ${theme.geekblue3};
|
||||||
|
--secondary-300: ${theme.geekblue4};
|
||||||
|
--secondary-400: ${theme.geekblue5};
|
||||||
|
--secondary-500: ${theme.geekblue6};
|
||||||
|
--secondary-600: ${theme.geekblue7};
|
||||||
|
--secondary-700: ${theme.geekblue8};
|
||||||
|
--secondary-800: ${theme.geekblue9};
|
||||||
|
--secondary-900: ${theme.geekblue10};
|
||||||
|
--secondary-950: ${theme.geekblue11};
|
||||||
|
--neutral-50: ${theme.colorText};
|
||||||
|
--neutral-100: ${theme.colorText};
|
||||||
|
--neutral-200: ${theme.colorTextSecondary};
|
||||||
|
--neutral-300: ${theme.colorTextTertiary};
|
||||||
|
--neutral-400: ${theme.colorTextQuaternary};
|
||||||
|
--neutral-500: ${theme.colorFill};
|
||||||
|
--neutral-600: ${theme.colorFillSecondary};
|
||||||
|
--neutral-700: ${theme.colorFillTertiary};
|
||||||
|
--neutral-800: ${theme.colorFillQuaternary};
|
||||||
|
--neutral-900: ${theme.colorBgElevated};
|
||||||
|
--neutral-950: ${theme.colorBgContainer};
|
||||||
|
--spacing-xxs: ${theme.paddingXXS / 4}px;
|
||||||
|
--spacing-xs: ${theme.paddingXS / 4}px;
|
||||||
|
--spacing-sm: ${theme.paddingSM / 4}px;
|
||||||
|
--spacing-md: ${theme.paddingMD / 4}px;
|
||||||
|
--spacing-lg: ${theme.paddingLG / 4}px;
|
||||||
|
--spacing-xl: ${theme.paddingXL / 4}px;
|
||||||
|
--spacing-xxl: ${theme.paddingXL / 4}px;
|
||||||
|
--radius-xxs: ${theme.borderRadiusXS}px;
|
||||||
|
--radius-xs: ${theme.borderRadiusXS}px;
|
||||||
|
--radius-sm: ${theme.borderRadiusSM}px;
|
||||||
|
--radius-md: ${theme.borderRadius}px;
|
||||||
|
--radius-lg: ${theme.borderRadius}px;
|
||||||
|
--radius-xl: ${theme.borderRadiusLG}px;
|
||||||
|
--radius-xxl: ${theme.borderRadiusLG}px;
|
||||||
|
--text-xxs: ${theme.fontSizeSM}px;
|
||||||
|
--text-xs: ${theme.fontSizeSM}px;
|
||||||
|
--text-sm: ${theme.fontSize}px;
|
||||||
|
--text-md: ${theme.fontSize}px;
|
||||||
|
--text-lg: ${theme.fontSizeLG}px;
|
||||||
|
--text-xl: ${theme.fontSizeXL}px;
|
||||||
|
--text-xxl: ${theme.fontSizeXL}px;
|
||||||
|
--font: ${theme.fontFamily};
|
||||||
|
--font-mono: ${theme.fontFamilyCode};
|
||||||
|
--body-background-fill: ${theme.colorBgLayout};
|
||||||
|
--body-text-color: ${theme.colorText};
|
||||||
|
--body-text-size: ${theme.fontSize}px;
|
||||||
|
--body-text-weight: 400;
|
||||||
|
--embed-radius: var(--radius-lg);
|
||||||
|
--color-accent: ${theme.colorPrimary};
|
||||||
|
--color-accent-soft: ${theme.colorPrimaryHover};
|
||||||
|
--background-fill-primary: ${theme.colorBgLayout};
|
||||||
|
--background-fill-secondary: var(--neutral-50);
|
||||||
|
--border-color-accent: ${theme.colorBorder};
|
||||||
|
--border-color-primary: ${theme.colorBorderSecondary};
|
||||||
|
--link-text-color: ${theme.colorInfoText};
|
||||||
|
--link-text-color-active: ${theme.colorInfoTextActive};
|
||||||
|
--link-text-color-hover: ${theme.colorInfoTextHover};
|
||||||
|
--link-text-color-visited: ${theme.colorInfoText};
|
||||||
|
--body-text-color-subdued: ${theme.colorTextDescription};
|
||||||
|
--shadow-drop: ${theme.boxShadowSecondary};
|
||||||
|
--shadow-drop-lg: ${theme.boxShadow};
|
||||||
|
--shadow-inset: ${theme.boxShadowSecondary} inset;
|
||||||
|
--shadow-spread: 3px;
|
||||||
|
--block-background-fill: ${theme.colorBgContainer};
|
||||||
|
--block-border-color: ${theme.colorBorderSecondary};
|
||||||
|
--block-border-width: 1px;
|
||||||
|
--block-info-text-color: ${theme.colorTextSecondary};
|
||||||
|
--block-info-text-size: var(--text-sm);
|
||||||
|
--block-info-text-weight: 400;
|
||||||
|
--block-label-background-fill: ${theme.colorFillSecondary};
|
||||||
|
--block-label-border-color: ${theme.colorBorderSecondary};
|
||||||
|
--block-label-border-width: 1px;
|
||||||
|
--block-label-shadow: ${theme.boxShadowTertiary};
|
||||||
|
--block-label-text-color: ${theme.colorText}
|
||||||
|
--block-label-margin: 0;
|
||||||
|
--block-label-padding: var(--spacing-sm) var(--spacing-lg);
|
||||||
|
--block-label-radius: ${theme.borderRadius}px;
|
||||||
|
--block-label-right-radius: ${theme.borderRadius}px;
|
||||||
|
--block-label-text-size: var(--text-sm);
|
||||||
|
--block-label-text-weight: 400;
|
||||||
|
--block-padding: var(--spacing-xl) calc(var(--spacing-xl) + 2px);
|
||||||
|
--block-radius: var(--radius-lg);
|
||||||
|
--block-shadow: ${theme.boxShadowSecondary};
|
||||||
|
--block-title-background-fill: none;
|
||||||
|
--block-title-border-color: none;
|
||||||
|
--block-title-border-width: 0;
|
||||||
|
--block-title-text-color: ${theme.colorTextDescription};
|
||||||
|
--block-title-padding: 0;
|
||||||
|
--block-title-radius: none;
|
||||||
|
--block-title-text-size: var(--text-md);
|
||||||
|
--block-title-text-weight: 400;
|
||||||
|
--container-radius: var(--radius-lg);
|
||||||
|
--form-gap-width: 1px;
|
||||||
|
--layout-gap: var(--spacing-xxl);
|
||||||
|
--panel-background-fill: ${theme.colorBgContainer};
|
||||||
|
--panel-border-color: ${theme.colorBorderSecondary};
|
||||||
|
--panel-border-width: 0;
|
||||||
|
--section-header-text-size: var(--text-md);
|
||||||
|
--section-header-text-weight: 400;
|
||||||
|
--chatbot-code-background-color: ${theme.colorBgContainer};
|
||||||
|
--checkbox-background-color: ${theme.colorFillTertiary};
|
||||||
|
--checkbox-background-color-focus: ${theme.colorFillSecondary};
|
||||||
|
--checkbox-background-color-hover: ${theme.colorFillSecondary};
|
||||||
|
--checkbox-background-color-selected: ${theme.colorText};
|
||||||
|
--checkbox-border-color: ${theme.colorBorderSecondary};
|
||||||
|
--checkbox-border-color-focus: ${theme.colorBorder};
|
||||||
|
--checkbox-border-color-hover: ${theme.colorBorder};
|
||||||
|
--checkbox-border-color-selected: ${theme.colorText};
|
||||||
|
--checkbox-border-radius: ${theme.borderRadiusXS}px;
|
||||||
|
--checkbox-border-width: 1px;
|
||||||
|
--checkbox-label-background-fill: linear-gradient(
|
||||||
|
to top,
|
||||||
|
${theme.colorFillTertiary},
|
||||||
|
${theme.colorBgLayout}
|
||||||
|
);
|
||||||
|
--checkbox-label-background-fill-hover: linear-gradient(
|
||||||
|
to top,
|
||||||
|
${theme.colorFillSecondary},
|
||||||
|
${theme.colorBgLayout}
|
||||||
|
);
|
||||||
|
--checkbox-label-background-fill-selected: ${theme.colorFillSecondary};
|
||||||
|
--checkbox-label-border-color: ${theme.colorBorderSecondary};
|
||||||
|
--checkbox-label-border-color-hover: ${theme.colorBorder};
|
||||||
|
--checkbox-label-border-width: 1px;
|
||||||
|
--checkbox-label-gap: var(--spacing-lg);
|
||||||
|
--checkbox-label-padding: var(--spacing-md) calc(2 * var(--spacing-md));
|
||||||
|
--checkbox-label-shadow: none;
|
||||||
|
--checkbox-label-text-size: var(--text-md);
|
||||||
|
--checkbox-label-text-weight: 400;
|
||||||
|
--checkbox-check: url(${checkBoxIcon});
|
||||||
|
--radio-circle: url(${radioIcon});
|
||||||
|
--checkbox-shadow: none;
|
||||||
|
--checkbox-label-text-color: ${theme.colorTextDescription};
|
||||||
|
--checkbox-label-text-color-selected: ${theme.colorText};
|
||||||
|
--error-background-fill: linear-gradient(
|
||||||
|
to right,
|
||||||
|
${theme.colorErrorBg},
|
||||||
|
${theme.colorFillSecondary}
|
||||||
|
);
|
||||||
|
--error-border-color: ${theme.colorErrorBorder};
|
||||||
|
--error-border-width: 1px;
|
||||||
|
--error-text-color: ${theme.colorErrorText};
|
||||||
|
--input-background-fill: ${theme.colorFillTertiary};
|
||||||
|
--input-background-fill-focus: ${theme.colorFillSecondary};
|
||||||
|
--input-background-fill-hover: ${theme.colorFillSecondary};
|
||||||
|
--input-border-color: ${theme.colorBorderSecondary};
|
||||||
|
--input-border-color-focus: ${theme.colorBorder};
|
||||||
|
--input-border-color-hover: ${theme.colorBorder};
|
||||||
|
--input-border-width: 1px;
|
||||||
|
--input-padding: var(--spacing-xl);
|
||||||
|
--input-placeholder-color: ${theme.colorTextQuaternary};
|
||||||
|
--input-radius: var(--radius-lg);
|
||||||
|
--input-shadow: none;
|
||||||
|
--input-shadow-focus: none;
|
||||||
|
--input-text-size: var(--text-md);
|
||||||
|
--input-text-weight: 400;
|
||||||
|
--loader-color: ${theme.colorFillSecondary};
|
||||||
|
--prose-text-size: var(--text-md);
|
||||||
|
--prose-text-weight: 400;
|
||||||
|
--prose-header-text-weight: 600;
|
||||||
|
--slider-color: ${theme.colorPrimary};
|
||||||
|
--stat-background-fill: linear-gradient(to right,
|
||||||
|
${theme.colorInfo},
|
||||||
|
${theme.colorInfoHover});
|
||||||
|
--table-border-color: ${theme.colorBorderSecondary};
|
||||||
|
--table-even-background-fill: transparent;
|
||||||
|
--table-odd-background-fill: ${theme.colorFillTertiary};
|
||||||
|
--table-radius: var(--radius-lg);
|
||||||
|
--table-row-focus: ${theme.colorFillSecondary};
|
||||||
|
--button-border-width: 1px;
|
||||||
|
--button-cancel-background-fill: ${theme.colorError};
|
||||||
|
--button-cancel-background-fill-hover: ${theme.colorErrorHover};
|
||||||
|
--button-cancel-border-color: ${theme.colorErrorBorder};
|
||||||
|
--button-cancel-border-color-hover: ${theme.colorErrorBorderHover};
|
||||||
|
--button-cancel-text-color: ${readableColor(theme.colorError)};
|
||||||
|
--button-cancel-text-color-hover: ${readableColor(theme.colorError)};
|
||||||
|
--button-large-padding: var(--spacing-lg) calc(2 * var(--spacing-lg));
|
||||||
|
--button-large-radius: var(--radius-lg);
|
||||||
|
--button-large-text-size: var(--text-lg);
|
||||||
|
--button-large-text-weight: 600;
|
||||||
|
--button-primary-background-fill: ${theme.colorPrimary};
|
||||||
|
--button-primary-background-fill-hover: ${theme.colorPrimaryHover};
|
||||||
|
--button-primary-border-color: ${theme.colorPrimaryBorder};
|
||||||
|
--button-primary-border-color-hover: ${theme.colorPrimaryBorderHover};
|
||||||
|
--button-primary-text-color: ${readableColor(theme.colorPrimary)};
|
||||||
|
--button-primary-text-color-hover: ${readableColor(theme.colorPrimary)};
|
||||||
|
--button-secondary-background-fill: ${theme.colorFillSecondary};
|
||||||
|
--button-secondary-background-fill-hover: ${theme.colorFill};;
|
||||||
|
--button-secondary-border-color: ${theme.colorBorderSecondary};
|
||||||
|
--button-secondary-border-color-hover: ${theme.colorBorder};
|
||||||
|
--button-secondary-text-color: ${theme.colorTextSecondary};
|
||||||
|
--button-secondary-text-color-hover: ${theme.colorText};
|
||||||
|
--button-shadow: none;
|
||||||
|
--button-shadow-active: none;
|
||||||
|
--button-shadow-hover: none;
|
||||||
|
--button-small-padding: var(--spacing-sm) calc(2 * var(--spacing-sm));
|
||||||
|
--button-small-radius: var(--radius-lg);
|
||||||
|
--button-small-text-size: var(--text-md);
|
||||||
|
--button-small-text-weight: 400;
|
||||||
|
--button-transition: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-tooltip-inner {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
min-height: unset;
|
||||||
|
padding: 4px 8px;
|
||||||
|
|
||||||
|
color: ${theme.colorBgLayout};
|
||||||
|
|
||||||
|
background-color: ${theme.colorText} !important;
|
||||||
|
border-radius: ${theme.borderRadiusSM}px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-tooltip-arrow {
|
||||||
|
&::before,
|
||||||
|
&::after {
|
||||||
|
background: ${theme.colorText} !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
cursor: pointer;
|
||||||
|
background: ${theme.colorFillSecondary};
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: ${theme.colorFill};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="range"]{
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
height: 3px;
|
||||||
|
margin-top: 8px;
|
||||||
|
|
||||||
|
appearance: none;
|
||||||
|
background: ${theme.colorBorder};
|
||||||
|
border-radius: ${theme.borderRadiusXS}px;
|
||||||
|
outline: none;
|
||||||
|
|
||||||
|
&::-webkit-slider-thumb {
|
||||||
|
width: 12px;
|
||||||
|
height:16px;
|
||||||
|
|
||||||
|
appearance: none;
|
||||||
|
background: ${theme.colorBgElevated};
|
||||||
|
border: 1px solid ${theme.colorBorder};
|
||||||
|
border-radius: ${theme.borderRadiusSM}px;
|
||||||
|
|
||||||
|
&:hover,&:active {
|
||||||
|
background: ${theme.colorText};
|
||||||
|
border-color: ${theme.colorText};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: ${theme.colorTextDescription};
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background: ${theme.colorPrimary};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default GlobalStyle;
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
import { DivProps, ThemeProvider } from '@lobehub/ui';
|
||||||
|
import qs from 'query-string';
|
||||||
|
import { memo, useEffect, useState } from 'react';
|
||||||
|
import { shallow } from 'zustand/shallow';
|
||||||
|
|
||||||
|
import { useIsDarkMode } from '@/hooks/useIsDarkMode';
|
||||||
|
import { useAppStore } from '@/store';
|
||||||
|
|
||||||
|
import GlobalStyle from './GlobalStyle';
|
||||||
|
import { useStyles } from './style';
|
||||||
|
|
||||||
|
const Layout = memo<DivProps>(({ children }) => {
|
||||||
|
const { onSetThemeMode, onInit } = useAppStore(
|
||||||
|
(st) => ({ onInit: st.onInit, onSetThemeMode: st.onSetThemeMode }),
|
||||||
|
shallow,
|
||||||
|
);
|
||||||
|
const isDarkMode = useIsDarkMode();
|
||||||
|
const [appearance, setAppearance] = useState<'light' | 'dark'>('light');
|
||||||
|
const [first, setFirst] = useState(true);
|
||||||
|
|
||||||
|
const { styles } = useStyles();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
onInit();
|
||||||
|
}, []);
|
||||||
|
useEffect(() => {
|
||||||
|
const queryTheme: any = String(qs.parseUrl(window.location.href).query.__theme || '');
|
||||||
|
if (queryTheme) {
|
||||||
|
setAppearance(queryTheme as any);
|
||||||
|
document.body.classList.add(queryTheme);
|
||||||
|
onSetThemeMode(queryTheme);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setAppearance(isDarkMode ? 'dark' : 'light');
|
||||||
|
document.body.classList.add(isDarkMode ? 'dark' : 'light');
|
||||||
|
onSetThemeMode(isDarkMode ? 'dark' : 'light');
|
||||||
|
}, [isDarkMode]);
|
||||||
|
useEffect(() => {
|
||||||
|
if (first) {
|
||||||
|
setFirst(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window.location.reload();
|
||||||
|
}, [isDarkMode]);
|
||||||
|
return (
|
||||||
|
<ThemeProvider themeMode={appearance}>
|
||||||
|
<GlobalStyle />
|
||||||
|
<div className={styles}>{children}</div>
|
||||||
|
</ThemeProvider>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Layout;
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { createStyles } from 'antd-style';
|
||||||
|
|
||||||
|
export const useStyles = createStyles(({ css, token }) => {
|
||||||
|
console.log(token.colorText);
|
||||||
|
return css``;
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { createRoot } from 'react-dom/client';
|
||||||
|
|
||||||
|
import App from './App';
|
||||||
|
|
||||||
|
if (window.global === undefined) window.global = window;
|
||||||
|
|
||||||
|
if (window.location.href.includes('dev') && import.meta.env.VITE_CONTEXT !== 'DEV') {
|
||||||
|
console.log('🚧 Theme Loader in Dev Mode');
|
||||||
|
} else {
|
||||||
|
document.addEventListener(
|
||||||
|
'DOMContentLoaded',
|
||||||
|
() => {
|
||||||
|
const root = document.createElement('div');
|
||||||
|
root.setAttribute('id', 'root');
|
||||||
|
try {
|
||||||
|
gradioApp()?.append(root);
|
||||||
|
} catch {
|
||||||
|
document.querySelector('gradio-app')?.append(root);
|
||||||
|
}
|
||||||
|
const client = createRoot(root);
|
||||||
|
client.render(<App />);
|
||||||
|
},
|
||||||
|
{ once: true },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
import { useResponsive } from 'antd-style';
|
||||||
|
import isEqual from 'fast-deep-equal';
|
||||||
|
import { memo, useEffect, useRef } from 'react';
|
||||||
|
|
||||||
|
import dragablePanel from '@/script/draggablePanel';
|
||||||
|
import { useAppStore } from '@/store';
|
||||||
|
import { DivProps } from '@/types';
|
||||||
|
|
||||||
|
import { useStyles } from './style';
|
||||||
|
|
||||||
|
const Content = memo<DivProps>(({ className, ...props }) => {
|
||||||
|
const mainReference: any = useRef<HTMLElement>();
|
||||||
|
const setting = useAppStore((st) => st.setting, isEqual);
|
||||||
|
const { cx, styles } = useStyles({ isPromptResizable: setting.promotTextarea === 'resizable' });
|
||||||
|
const { mobile } = useResponsive();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Content
|
||||||
|
const main = gradioApp().querySelector('.app');
|
||||||
|
if (main) mainReference.current?.append(main);
|
||||||
|
if (!mobile) dragablePanel();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return <div className={cx(styles.container, className)} ref={mainReference} {...props} />;
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Content;
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { createStyles } from 'antd-style';
|
||||||
|
|
||||||
|
export const useStyles = createStyles(
|
||||||
|
({ css }, { isPromptResizable }: { isPromptResizable: boolean }) => ({
|
||||||
|
container: css`
|
||||||
|
position: relative;
|
||||||
|
flex: 1;
|
||||||
|
max-width: 100%;
|
||||||
|
|
||||||
|
[id$='2img_prompt'] textarea {
|
||||||
|
max-height: ${isPromptResizable ? 'unset' : '84px'};
|
||||||
|
}
|
||||||
|
|
||||||
|
[id$='2img_neg_prompt'] textarea {
|
||||||
|
max-height: ${isPromptResizable ? 'unset' : '84px'};
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
import { Icon } from '@lobehub/ui';
|
||||||
|
import { Skeleton, Slider } from 'antd';
|
||||||
|
import { ZoomIn } from 'lucide-react';
|
||||||
|
import { memo, useEffect, useRef, useState } from 'react';
|
||||||
|
import { shallow } from 'zustand/shallow';
|
||||||
|
|
||||||
|
import { useStyles } from '@/pages/ExtraNetworkSidebar/style';
|
||||||
|
import civitaiHelperFix from '@/script/civitaiHelperFix';
|
||||||
|
import { useAppStore } from '@/store';
|
||||||
|
|
||||||
|
const Inner = memo(() => {
|
||||||
|
const txt2imgExtraNetworkSidebarReference: any = useRef<HTMLElement>();
|
||||||
|
const img2imgExtraNetworkSidebarReference: any = useRef<HTMLElement>();
|
||||||
|
const [extraLoading, setExtraLoading] = useState(true);
|
||||||
|
const [setting, currentTab] = useAppStore((st) => [st.setting, st.currentTab], shallow);
|
||||||
|
const [size, setSize] = useState<number>(setting?.extraNetworkCardSize || 86);
|
||||||
|
const { styles } = useStyles({ size });
|
||||||
|
useEffect(() => {
|
||||||
|
if (setting.enableExtraNetworkSidebar) {
|
||||||
|
const txt2imgExtraNetworks = gradioApp().querySelector('div#txt2img_extra_networks');
|
||||||
|
const img2imgExtraNetworks = gradioApp().querySelector('div#img2img_extra_networks');
|
||||||
|
if (txt2imgExtraNetworks && img2imgExtraNetworks) {
|
||||||
|
txt2imgExtraNetworkSidebarReference.current?.append(txt2imgExtraNetworks);
|
||||||
|
img2imgExtraNetworkSidebarReference.current?.append(img2imgExtraNetworks);
|
||||||
|
}
|
||||||
|
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"]',
|
||||||
|
) as NodeListOf<HTMLButtonElement>;
|
||||||
|
if (civitaiButton) {
|
||||||
|
for (const button of civitaiButton) {
|
||||||
|
button.onclick = civitaiHelperFix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
civitaiHelperFix();
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className={styles.list}>
|
||||||
|
{extraLoading && <Skeleton active />}
|
||||||
|
<div style={extraLoading ? { display: 'none' } : {}}>
|
||||||
|
<div
|
||||||
|
id="txt2img-extra-netwrok-sidebar"
|
||||||
|
ref={txt2imgExtraNetworkSidebarReference}
|
||||||
|
style={currentTab === 'tab_img2img' ? { display: 'none' } : {}}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
id="img2img-extra-netwrok-sidebar"
|
||||||
|
ref={img2imgExtraNetworkSidebarReference}
|
||||||
|
style={currentTab === 'tab_img2img' ? {} : { display: 'none' }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.footer}>
|
||||||
|
<Icon icon={ZoomIn} />
|
||||||
|
<Slider
|
||||||
|
defaultValue={size}
|
||||||
|
max={256}
|
||||||
|
min={64}
|
||||||
|
onChange={setSize}
|
||||||
|
step={8}
|
||||||
|
style={{ flex: 1 }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Inner;
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
import { DraggablePanel, LayoutSidebarInner } from '@lobehub/ui';
|
||||||
|
import { useResponsive } from 'antd-style';
|
||||||
|
import isEqual from 'fast-deep-equal';
|
||||||
|
import { memo, useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
import { useAppStore } from '@/store';
|
||||||
|
import { DivProps } from '@/types/index';
|
||||||
|
|
||||||
|
import Inner from './Inner';
|
||||||
|
import { useStyles } from './style';
|
||||||
|
|
||||||
|
export interface ExtraNetworkSidebarProps extends DivProps {
|
||||||
|
headerHeight: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ExtraNetworkSidebar = memo<ExtraNetworkSidebarProps>(({ headerHeight }) => {
|
||||||
|
const setting = useAppStore((st) => st.setting, isEqual);
|
||||||
|
const [mode] = useState<'fixed' | 'float'>(setting.extraNetworkFixedMode);
|
||||||
|
const [expand, setExpand] = useState<boolean>(setting.extraNetworkSidebarExpand);
|
||||||
|
const { mobile } = useResponsive();
|
||||||
|
const { styles } = useStyles({ headerHeight });
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (mobile) setExpand(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DraggablePanel
|
||||||
|
defaultSize={{ width: setting.extraNetworkSidebarWidth }}
|
||||||
|
expand={expand}
|
||||||
|
minWidth={setting.extraNetworkSidebarWidth}
|
||||||
|
mode={mode}
|
||||||
|
onExpandChange={setExpand}
|
||||||
|
pin={mode === 'fixed'}
|
||||||
|
placement="right"
|
||||||
|
>
|
||||||
|
<LayoutSidebarInner>
|
||||||
|
<div className={styles.container}>
|
||||||
|
<Inner />
|
||||||
|
</div>
|
||||||
|
</LayoutSidebarInner>
|
||||||
|
</DraggablePanel>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default ExtraNetworkSidebar;
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
import { createStyles } from 'antd-style';
|
||||||
|
import { rgba } from 'polished';
|
||||||
|
|
||||||
|
export const useStyles = createStyles(
|
||||||
|
(
|
||||||
|
{ css, cx, stylish, token },
|
||||||
|
{ headerHeight = 64, size = 86 }: { headerHeight?: number; size?: number },
|
||||||
|
) => ({
|
||||||
|
container: cx(
|
||||||
|
stylish.blur,
|
||||||
|
css`
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
height: calc(100vh - ${headerHeight}px);
|
||||||
|
|
||||||
|
background: ${rgba(token.colorBgLayout, 0.5)};
|
||||||
|
|
||||||
|
button#txt2img_extra_networks,
|
||||||
|
button#img2img_extra_networks {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
),
|
||||||
|
footer: css`
|
||||||
|
display: flex;
|
||||||
|
flex: 0;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
|
||||||
|
padding: 8px 16px;
|
||||||
|
|
||||||
|
border-top: 1px solid ${token.colorBorderSecondary};
|
||||||
|
`,
|
||||||
|
list: css`
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
flex: 1;
|
||||||
|
padding: 16px;
|
||||||
|
|
||||||
|
#txt2img_extra_networks,
|
||||||
|
#img2img_extra_networks {
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.extra-network-cards .actions .name {
|
||||||
|
background: unset !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.extra-network-cards,
|
||||||
|
.extra-network-thumbs {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(${size}px, 1fr));
|
||||||
|
|
||||||
|
> .card {
|
||||||
|
width: var(--fill-available) !important;
|
||||||
|
height: ${size * 1.5}px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
import { Icon } from '@lobehub/ui';
|
||||||
|
import { FooterProps as FProps } from '@lobehub/ui/es/Footer';
|
||||||
|
import { Bug, FileClock, GitFork, Github } from 'lucide-react';
|
||||||
|
|
||||||
|
import { homepage } from '@/../package.json';
|
||||||
|
|
||||||
|
export const columns: FProps['columns'] = [
|
||||||
|
{
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
description: 'AIGC Components',
|
||||||
|
openExternal: true,
|
||||||
|
title: '🤯 Lobe UI',
|
||||||
|
url: 'https://github.com/lobehub/lobe-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Chatbot Client',
|
||||||
|
openExternal: true,
|
||||||
|
title: '🤯 Lobe Chat',
|
||||||
|
url: 'https://github.com/lobehub/lobe-chat',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Node Flow Editor',
|
||||||
|
openExternal: true,
|
||||||
|
title: '🤯 Lobe Flow',
|
||||||
|
url: 'https://github.com/lobehub/lobe-flow',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
title: 'Resources',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
icon: <Icon icon={Bug} size="small" />,
|
||||||
|
openExternal: true,
|
||||||
|
title: 'Report Bug',
|
||||||
|
url: `${homepage}/issues/new/choose`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <Icon icon={GitFork} size="small" />,
|
||||||
|
openExternal: true,
|
||||||
|
title: 'Request Feature',
|
||||||
|
url: `${homepage}/issues/new/choose`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
title: 'Community',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
icon: <Icon icon={Github} size="small" />,
|
||||||
|
openExternal: true,
|
||||||
|
title: 'GitHub',
|
||||||
|
url: homepage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <Icon icon={FileClock} size="small" />,
|
||||||
|
openExternal: true,
|
||||||
|
title: 'Changelog',
|
||||||
|
url: `${homepage}/blob/main/CHANGELOG.md`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
title: 'Help',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
description: 'AI Commit CLI',
|
||||||
|
openExternal: true,
|
||||||
|
title: '💌 Lobe Commit',
|
||||||
|
url: 'https://github.com/lobehub/lobe-commit',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Lint Config',
|
||||||
|
openExternal: true,
|
||||||
|
title: '📐 Lobe Lint',
|
||||||
|
url: 'https://github.com/lobehub/lobe-lint',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
title: 'More Products',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { Footer as F } from '@lobehub/ui';
|
||||||
|
import { memo, useEffect, useRef } from 'react';
|
||||||
|
|
||||||
|
import { DivProps } from '@/types/index';
|
||||||
|
|
||||||
|
import { columns } from './data';
|
||||||
|
import { useStyles } from './style';
|
||||||
|
|
||||||
|
const Footer = memo<DivProps>(({ className, ...props }) => {
|
||||||
|
const { cx, styles } = useStyles();
|
||||||
|
const footerReference: any = useRef<HTMLElement>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const footer = gradioApp().querySelector('#footer');
|
||||||
|
if (footer) footerReference.current?.append(footer);
|
||||||
|
}, []);
|
||||||
|
return (
|
||||||
|
<div className={cx(styles.footer, className)} {...props}>
|
||||||
|
<F bottom={<div ref={footerReference} />} columns={columns} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Footer;
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { createStyles } from 'antd-style';
|
||||||
|
|
||||||
|
export const useStyles = createStyles(({ css }) => ({
|
||||||
|
footer: css`
|
||||||
|
footer {
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
}));
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
import { GithubOutlined } from '@ant-design/icons';
|
||||||
|
import { ActionIcon } from '@lobehub/ui';
|
||||||
|
import { Modal, Popover, Space } from 'antd';
|
||||||
|
import { Bold, Github, LucideIcon, Moon, Settings2, Sun } from 'lucide-react';
|
||||||
|
import qs from 'query-string';
|
||||||
|
import { memo, useCallback, useState } from 'react';
|
||||||
|
import { shallow } from 'zustand/shallow';
|
||||||
|
|
||||||
|
import Giscus from '@/slots/Giscus';
|
||||||
|
import Setting from '@/slots/Setting';
|
||||||
|
import { useAppStore } from '@/store';
|
||||||
|
|
||||||
|
const CivitaiLogo: LucideIcon = ({ size }) => (
|
||||||
|
<svg fill="currentColor" height={size} viewBox="0 0 16 16" width={size}>
|
||||||
|
<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>
|
||||||
|
);
|
||||||
|
|
||||||
|
interface ActionsProps {
|
||||||
|
themeMode: 'dark' | 'light';
|
||||||
|
}
|
||||||
|
|
||||||
|
const Actions = memo<ActionsProps>(() => {
|
||||||
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
|
const themeMode = useAppStore((st) => st.themeMode, shallow);
|
||||||
|
|
||||||
|
const handleSetTheme = useCallback(() => {
|
||||||
|
const theme = themeMode === 'light' ? 'dark' : 'light';
|
||||||
|
const gradioURL = qs.parseUrl(window.location.href);
|
||||||
|
gradioURL.query.__theme = theme;
|
||||||
|
window.location.replace(qs.stringifyUrl(gradioURL));
|
||||||
|
}, [themeMode]);
|
||||||
|
|
||||||
|
const showModal = () => setIsModalOpen(true);
|
||||||
|
|
||||||
|
const handleCancel = () => setIsModalOpen(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Space.Compact>
|
||||||
|
<a href="https://civitai.com/" rel="noreferrer" target="_blank">
|
||||||
|
<ActionIcon icon={CivitaiLogo} title="Civitai" />
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="https://www.birme.net/?target_width=512&target_height=512"
|
||||||
|
rel="noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<ActionIcon icon={Bold} title="Birme" />
|
||||||
|
</a>
|
||||||
|
<ActionIcon icon={Github} onClick={showModal} title="Feedback" />
|
||||||
|
<Popover
|
||||||
|
content={<Setting />}
|
||||||
|
title={<h3 style={{ lineHeight: 2, margin: 0 }}>⚙ Setting</h3>}
|
||||||
|
trigger="click"
|
||||||
|
>
|
||||||
|
<ActionIcon icon={Settings2} title="Setting" />
|
||||||
|
</Popover>
|
||||||
|
<ActionIcon
|
||||||
|
icon={themeMode === 'light' ? Sun : Moon}
|
||||||
|
onClick={handleSetTheme}
|
||||||
|
title="Switch Theme"
|
||||||
|
/>
|
||||||
|
</Space.Compact>
|
||||||
|
<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 Actions;
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
import { TabsNav, type TabsNavProps } from '@lobehub/ui';
|
||||||
|
import { memo, useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
const hideOriganlNav = () => {
|
||||||
|
(gradioApp().querySelector('#tabs > .tab-nav:first-child') as HTMLDivElement).style.display =
|
||||||
|
'none';
|
||||||
|
};
|
||||||
|
|
||||||
|
const getNavButtons = (): HTMLButtonElement[] =>
|
||||||
|
Array.prototype.slice.call(
|
||||||
|
gradioApp().querySelectorAll(
|
||||||
|
'#tabs > .tab-nav:first-child button',
|
||||||
|
) as NodeListOf<HTMLButtonElement>,
|
||||||
|
);
|
||||||
|
|
||||||
|
const onChange: TabsNavProps['onChange'] = (activeKey) => {
|
||||||
|
const buttons = getNavButtons();
|
||||||
|
buttons[Number(activeKey)]?.click();
|
||||||
|
};
|
||||||
|
|
||||||
|
const Nav = memo(() => {
|
||||||
|
const [items, setItems] = useState<TabsNavProps['items']>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
hideOriganlNav();
|
||||||
|
const buttons = getNavButtons();
|
||||||
|
const list: TabsNavProps['items'] = buttons.map(
|
||||||
|
(button: HTMLButtonElement | any, index: number) => {
|
||||||
|
button.id = `kitchen-nav-${index}`;
|
||||||
|
return {
|
||||||
|
key: String(index),
|
||||||
|
label: button.textContent,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
setItems(list.filter(Boolean));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return <TabsNav items={items} onChange={onChange} />;
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Nav;
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
import { Header as H } from '@lobehub/ui';
|
||||||
|
import { memo } from 'react';
|
||||||
|
import { shallow } from 'zustand/shallow';
|
||||||
|
|
||||||
|
import { useAppStore } from '@/store';
|
||||||
|
import { DivProps } from '@/types/index';
|
||||||
|
|
||||||
|
import Actions from './Actions';
|
||||||
|
import Logo from './Logo';
|
||||||
|
import Nav from './Nav';
|
||||||
|
|
||||||
|
const Header = memo<DivProps>(({ children }) => {
|
||||||
|
const themeMode = useAppStore((st) => st.themeMode, shallow);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<H
|
||||||
|
actions={<Actions themeMode={themeMode} />}
|
||||||
|
logo={
|
||||||
|
<a
|
||||||
|
href="https://github.com/canisminor1990/sd-webui-kitchen-theme"
|
||||||
|
rel="noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<Logo themeMode={themeMode} />
|
||||||
|
</a>
|
||||||
|
}
|
||||||
|
nav={
|
||||||
|
<>
|
||||||
|
<Nav />
|
||||||
|
{children}
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Header;
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
export const darkLogo =
|
||||||
|
'https://gw.alipayobjects.com/zos/bmw-prod/9ecb2822-1592-4cb0-a087-ce0097fef2ca.svg';
|
||||||
|
export const lightLogo =
|
||||||
|
'https://gw.alipayobjects.com/zos/bmw-prod/e146116d-c65a-4306-a3d2-bb8d05e1c49b.svg';
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { DivProps } from '@lobehub/ui';
|
||||||
|
import { memo, useEffect, useRef } from 'react';
|
||||||
|
|
||||||
|
import PromptGroup from '@/slots/PromptEditor';
|
||||||
|
|
||||||
|
const Inner = memo<DivProps>(() => {
|
||||||
|
const sidebarReference: any = useRef<HTMLElement>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const sidebar = gradioApp().querySelector('#quicksettings');
|
||||||
|
if (sidebar) sidebarReference.current?.append(sidebar);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<PromptGroup />
|
||||||
|
<div ref={sidebarReference} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Inner;
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
import { DivProps, DraggablePanel, LayoutSidebarInner } from '@lobehub/ui';
|
||||||
|
import { useResponsive } from 'antd-style';
|
||||||
|
import isEqual from 'fast-deep-equal';
|
||||||
|
import { memo, useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
import { useAppStore } from '@/store';
|
||||||
|
|
||||||
|
import Inner from './Inner';
|
||||||
|
import { useStyles } from './style';
|
||||||
|
|
||||||
|
export interface QuickSettingSidebarProps extends DivProps {
|
||||||
|
headerHeight: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QuickSettingSidebar = memo<QuickSettingSidebarProps>(({ headerHeight }) => {
|
||||||
|
const setting = useAppStore((st) => st.setting, isEqual);
|
||||||
|
const [mode] = useState<'fixed' | 'float'>(setting.sidebarFixedMode);
|
||||||
|
const [expand, setExpand] = useState<boolean>(setting.sidebarExpand);
|
||||||
|
const { mobile } = useResponsive();
|
||||||
|
const { styles } = useStyles({ headerHeight });
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (mobile) setExpand(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DraggablePanel
|
||||||
|
defaultSize={{ width: setting.sidebarWidth }}
|
||||||
|
expand={expand}
|
||||||
|
minWidth={setting.sidebarWidth}
|
||||||
|
mode={mode}
|
||||||
|
onExpandChange={setExpand}
|
||||||
|
pin={mode === 'fixed'}
|
||||||
|
placement="left"
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<LayoutSidebarInner>
|
||||||
|
<div className={styles.container}>
|
||||||
|
<Inner />
|
||||||
|
</div>
|
||||||
|
</LayoutSidebarInner>
|
||||||
|
</DraggablePanel>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default QuickSettingSidebar;
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { createStyles } from 'antd-style';
|
||||||
|
import { rgba } from 'polished';
|
||||||
|
|
||||||
|
export const useStyles = createStyles(
|
||||||
|
({ css, cx, stylish, token }, { headerHeight = 64 }: { headerHeight?: number }) => ({
|
||||||
|
container: cx(
|
||||||
|
stylish.blur,
|
||||||
|
css`
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
height: calc(100vh - ${headerHeight}px);
|
||||||
|
padding: 16px;
|
||||||
|
|
||||||
|
background: ${rgba(token.colorBgLayout, 0.5)};
|
||||||
|
`,
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
import { LayoutHeader, LayoutMain, LayoutSidebar } from '@lobehub/ui';
|
||||||
|
import isEqual from 'fast-deep-equal';
|
||||||
|
import { memo, useEffect } from 'react';
|
||||||
|
|
||||||
|
import replaceIcon from '@/script/replaceIcon';
|
||||||
|
import { useAppStore } from '@/store';
|
||||||
|
|
||||||
|
import Content from './Content';
|
||||||
|
import ExtraNetworkSidebar from './ExtraNetworkSidebar';
|
||||||
|
import Footer from './Footer';
|
||||||
|
import Header from './Header';
|
||||||
|
import QuickSettingSidebar from './QuickSettingSidebar';
|
||||||
|
import { useStyles } from './style';
|
||||||
|
|
||||||
|
const HEADER_HEIGHT = 64;
|
||||||
|
|
||||||
|
const Index = memo(() => {
|
||||||
|
const { styles } = useStyles(HEADER_HEIGHT);
|
||||||
|
const setting = useAppStore((st) => st.setting, isEqual);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (setting.svgIcon) replaceIcon();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<LayoutHeader headerHeight={HEADER_HEIGHT}>
|
||||||
|
<Header />
|
||||||
|
</LayoutHeader>
|
||||||
|
<LayoutMain>
|
||||||
|
<LayoutSidebar className={styles.sidebar} headerHeight={HEADER_HEIGHT}>
|
||||||
|
<QuickSettingSidebar headerHeight={HEADER_HEIGHT} />
|
||||||
|
</LayoutSidebar>
|
||||||
|
<Content />
|
||||||
|
{setting?.enableExtraNetworkSidebar && (
|
||||||
|
<LayoutSidebar className={styles.sidebar} headerHeight={HEADER_HEIGHT}>
|
||||||
|
<ExtraNetworkSidebar headerHeight={HEADER_HEIGHT} />
|
||||||
|
</LayoutSidebar>
|
||||||
|
)}
|
||||||
|
</LayoutMain>
|
||||||
|
<Footer />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Index;
|
||||||
|
|
@ -1,166 +0,0 @@
|
||||||
import {Spin} from 'antd';
|
|
||||||
import {useResponsive} from 'antd-style';
|
|
||||||
import {memo, useEffect, useRef, useState} from 'react';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
import {shallow} from 'zustand/shallow';
|
|
||||||
|
|
||||||
import {Content, ExtraNetworkSidebar, Header, Sidebar} from '@/components';
|
|
||||||
import civitaiHelperFix from '@/script/civitaiHelperFix';
|
|
||||||
import dragablePanel from '@/script/draggablePanel';
|
|
||||||
import replaceIcon from '@/script/replaceIcon';
|
|
||||||
import {useAppStore} from '@/store';
|
|
||||||
|
|
||||||
const View = styled.div`
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
overflow: hidden;
|
|
||||||
display: flex;
|
|
||||||
flex: 1;
|
|
||||||
flex-direction: row !important;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const MainView = styled.div`
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
overflow: hidden;
|
|
||||||
display: flex;
|
|
||||||
flex: 1;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
width: 100vw;
|
|
||||||
height: 100vh;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const LoadingBox = styled.div`
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const App = memo(() => {
|
|
||||||
const [currentTab, setCurrentTab, setting] = useAppStore(
|
|
||||||
(st) => [st.currentTab, st.setCurrentTab, st.setting],
|
|
||||||
shallow,
|
|
||||||
);
|
|
||||||
const {mobile} = useResponsive();
|
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
const [extraLoading, setExtraLoading] = useState(true);
|
|
||||||
const sidebarReference: any = useRef<HTMLElement>();
|
|
||||||
const mainReference: any = useRef<HTMLElement>();
|
|
||||||
const txt2imgExtraNetworkSidebarReference: any = useRef<HTMLElement>();
|
|
||||||
const img2imgExtraNetworkSidebarReference: any = useRef<HTMLElement>();
|
|
||||||
useEffect(() => {
|
|
||||||
onUiLoaded(() => {
|
|
||||||
// Content
|
|
||||||
const main = gradioApp().querySelector('.app');
|
|
||||||
if (main) mainReference.current?.append(main);
|
|
||||||
if (!mobile) dragablePanel();
|
|
||||||
|
|
||||||
// Sidebar
|
|
||||||
const sidebar = gradioApp().querySelector('#quicksettings');
|
|
||||||
if (sidebar) sidebarReference.current?.append(sidebar);
|
|
||||||
|
|
||||||
// ExtraNetworkSidebar
|
|
||||||
if (setting.enableExtraNetworkSidebar) {
|
|
||||||
const txt2imgExtraNetworks = gradioApp().querySelector('div#txt2img_extra_networks');
|
|
||||||
const img2imgExtraNetworks = gradioApp().querySelector('div#img2img_extra_networks');
|
|
||||||
if (txt2imgExtraNetworks && img2imgExtraNetworks) {
|
|
||||||
txt2imgExtraNetworkSidebarReference.current?.append(txt2imgExtraNetworks);
|
|
||||||
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]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<MainView>
|
|
||||||
<Header>
|
|
||||||
{loading && (
|
|
||||||
<LoadingBox>
|
|
||||||
<Spin size="small" />
|
|
||||||
</LoadingBox>
|
|
||||||
)}
|
|
||||||
</Header>
|
|
||||||
<View>
|
|
||||||
<Sidebar>
|
|
||||||
{loading && (
|
|
||||||
<LoadingBox>
|
|
||||||
<Spin size="small" />
|
|
||||||
</LoadingBox>
|
|
||||||
)}
|
|
||||||
<div id="sidebar" ref={sidebarReference} style={loading ? {display: 'none'} : {}} />
|
|
||||||
</Sidebar>
|
|
||||||
<Content loading={loading}>
|
|
||||||
{loading && (
|
|
||||||
<LoadingBox>
|
|
||||||
<Spin size="large" tip="Loading" />
|
|
||||||
</LoadingBox>
|
|
||||||
)}
|
|
||||||
<div id="content" ref={mainReference} 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={txt2imgExtraNetworkSidebarReference}
|
|
||||||
style={currentTab === 'tab_img2img' ? {display: 'none'} : {}}
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
id="img2img-extra-netwrok-sidebar"
|
|
||||||
ref={img2imgExtraNetworkSidebarReference}
|
|
||||||
style={currentTab === 'tab_img2img' ? {} : {display: 'none'}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</ExtraNetworkSidebar>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
</MainView>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
export default App;
|
|
||||||
|
|
@ -1,77 +0,0 @@
|
||||||
import { createGlobalStyle } from 'antd-style';
|
|
||||||
|
|
||||||
const GlobalStyle = createGlobalStyle`
|
|
||||||
|
|
||||||
html,body {
|
|
||||||
--font-settings: "cv01", "tnum", "kern";
|
|
||||||
--font-variations: "opsz" auto, tabular-nums;
|
|
||||||
|
|
||||||
overflow-x: hidden;
|
|
||||||
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
|
|
||||||
font-family: ${({ theme }) => theme.fontFamily};
|
|
||||||
font-size: ${({ theme }) => theme.fontSize}px;
|
|
||||||
font-feature-settings: var(--font-settings);
|
|
||||||
font-variation-settings: var(--font-variations);
|
|
||||||
line-height: 1;
|
|
||||||
color: ${({ theme }) => theme.colorTextBase};
|
|
||||||
text-size-adjust: none;
|
|
||||||
text-rendering: optimizelegibility;
|
|
||||||
vertical-align: baseline;
|
|
||||||
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
-webkit-tap-highlight-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
* {
|
|
||||||
box-sizing: border-box;
|
|
||||||
vertical-align: baseline;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar {
|
|
||||||
width: 0;
|
|
||||||
height: 4px;
|
|
||||||
background-color: transparent;
|
|
||||||
|
|
||||||
&-thumb {
|
|
||||||
background-color: transparent;
|
|
||||||
border-radius: 4px;
|
|
||||||
transition: background-color 500ms ${({ theme }) => theme.motionEaseOut};
|
|
||||||
}
|
|
||||||
|
|
||||||
&-corner {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#root {
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
text-align: justify;
|
|
||||||
word-wrap: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
font-family: ${({ theme }) => theme.fontFamilyCode} !important;
|
|
||||||
|
|
||||||
* {
|
|
||||||
font-family: inherit !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*:hover, *:focus {
|
|
||||||
::-webkit-scrollbar {
|
|
||||||
&-thumb {
|
|
||||||
background-color: ${({ theme }) => theme.colorFill};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default GlobalStyle;
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
import {ThemeProvider} from '@lobehub/ui';
|
|
||||||
import qs from 'query-string';
|
|
||||||
import {memo, useEffect, useState} from 'react';
|
|
||||||
import {createRoot} from 'react-dom/client';
|
|
||||||
import {shallow} from 'zustand/shallow';
|
|
||||||
|
|
||||||
import {useIsDarkMode} from '@/components/theme/useIsDarkMode';
|
|
||||||
import formatPrompt from '@/script/formatPrompt';
|
|
||||||
import promptBracketChecker from '@/script/promptBracketChecker';
|
|
||||||
import setupHead from '@/script/setupHead';
|
|
||||||
import {useAppStore} from '@/store';
|
|
||||||
import '@/theme/style.less';
|
|
||||||
|
|
||||||
import App from './App';
|
|
||||||
|
|
||||||
const Root = memo(() => {
|
|
||||||
const [onSetThemeMode, onInit] = useAppStore((st) => [st.onSetThemeMode, st.onInit], shallow);
|
|
||||||
const isDarkMode = useIsDarkMode();
|
|
||||||
const [appearance, setAppearance] = useState<'light' | 'dark'>('light');
|
|
||||||
const [first, setFirst] = useState(true);
|
|
||||||
useEffect(() => {
|
|
||||||
onInit();
|
|
||||||
}, []);
|
|
||||||
useEffect(() => {
|
|
||||||
const queryTheme: any = String(qs.parseUrl(window.location.href).query.__theme || '');
|
|
||||||
if (queryTheme) {
|
|
||||||
setAppearance(queryTheme as any);
|
|
||||||
document.body.classList.add(queryTheme);
|
|
||||||
onSetThemeMode(queryTheme);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setAppearance(isDarkMode ? 'dark' : 'light');
|
|
||||||
document.body.classList.add(isDarkMode ? 'dark' : 'light');
|
|
||||||
onSetThemeMode(isDarkMode ? 'dark' : 'light');
|
|
||||||
}, [isDarkMode]);
|
|
||||||
useEffect(() => {
|
|
||||||
if (first) {
|
|
||||||
setFirst(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
window.location.reload();
|
|
||||||
}, [isDarkMode]);
|
|
||||||
return (
|
|
||||||
<ThemeProvider themeMode={appearance}>
|
|
||||||
<App />
|
|
||||||
</ThemeProvider>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener(
|
|
||||||
'DOMContentLoaded',
|
|
||||||
() => {
|
|
||||||
setupHead();
|
|
||||||
const root = document.createElement('div');
|
|
||||||
root.setAttribute('id', 'root');
|
|
||||||
try {
|
|
||||||
gradioApp()?.append(root);
|
|
||||||
} catch {
|
|
||||||
document.querySelector('gradio-app')?.append(root);
|
|
||||||
}
|
|
||||||
const client = createRoot(root);
|
|
||||||
client.render(<Root />);
|
|
||||||
},
|
|
||||||
{once: true},
|
|
||||||
);
|
|
||||||
|
|
||||||
onUiLoaded(() => {
|
|
||||||
formatPrompt();
|
|
||||||
promptBracketChecker();
|
|
||||||
});
|
|
||||||
|
|
||||||
export default () => false;
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
import { AliasToken } from 'antd/es/theme/interface';
|
|
||||||
|
|
||||||
const FONT_EMOJI = `"Segoe UI Emoji","Segoe UI Symbol","Apple Color Emoji","Twemoji Mozilla","Noto Color Emoji","Android Emoji"`;
|
|
||||||
const FONT_EN = `"HarmonyOS Sans","Segoe UI","SF Pro Display",-apple-system,BlinkMacSystemFont,Roboto,Oxygen,Ubuntu,Cantarell,"Open Sans","Helvetica Neue",sans-serif`;
|
|
||||||
const FONT_CN = `"HarmonyOS Sans SC","PingFang SC","Hiragino Sans GB","Microsoft Yahei UI","Microsoft Yahei","Source Han Sans CN",sans-serif`;
|
|
||||||
const FONT_CODE = `ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace`;
|
|
||||||
|
|
||||||
export const baseToken: Partial<AliasToken> = {
|
|
||||||
fontFamily: [FONT_EN, FONT_CN, FONT_EMOJI].join(','),
|
|
||||||
fontFamilyCode: [FONT_CODE, FONT_CN, FONT_EMOJI].join(','),
|
|
||||||
};
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { createStyles } from 'antd-style';
|
||||||
|
|
||||||
|
export const useStyles = createStyles(({ css }, headerHeight: number) => ({
|
||||||
|
sidebar: css`
|
||||||
|
height: calc(100vh - ${headerHeight}px);
|
||||||
|
`,
|
||||||
|
}));
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
import { memo, useEffect } from 'react';
|
||||||
|
|
||||||
|
import { useStyles } from './style';
|
||||||
|
|
||||||
|
interface GiscusProps {
|
||||||
|
themeMode: 'light' | 'dark';
|
||||||
|
}
|
||||||
|
const Giscus = memo<GiscusProps>(({ themeMode }) => {
|
||||||
|
const { styles, cx } = useStyles();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// giscus
|
||||||
|
const giscus: HTMLScriptElement = document.createElement('script');
|
||||||
|
giscus.src = 'https://giscus.app/client.js';
|
||||||
|
giscus.dataset.repo = 'canisminor1990/sd-webui-kitchen-theme';
|
||||||
|
giscus.dataset.repoId = 'R_kgDOJCPcNg';
|
||||||
|
giscus.dataset.mapping = 'number';
|
||||||
|
giscus.dataset.term = '53';
|
||||||
|
giscus.dataset.reactionsEnabled = '1';
|
||||||
|
giscus.dataset.emitMetadata = '0';
|
||||||
|
giscus.dataset.inputPosition = 'bottom';
|
||||||
|
giscus.dataset.theme = themeMode;
|
||||||
|
giscus.dataset.lang = 'en';
|
||||||
|
giscus.crossOrigin = 'anonymous';
|
||||||
|
giscus.async = true;
|
||||||
|
document.querySelectorAll('head')[0].append(giscus);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return <div className={cx('giscus', styles.container)} id="giscus" />;
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Giscus;
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { createStyles } from 'antd-style';
|
||||||
|
|
||||||
|
export const useStyles = createStyles(({ css }) => ({
|
||||||
|
container: css`
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
height: 60vh;
|
||||||
|
`,
|
||||||
|
}));
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
import { memo, useCallback, useState } from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import TagList, { PromptType, TagItem } from './TagList';
|
||||||
|
import { formatPrompt } from './utils';
|
||||||
|
|
||||||
|
/******************************************************
|
||||||
|
*********************** Style *************************
|
||||||
|
******************************************************/
|
||||||
|
|
||||||
|
const View = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ButtonGroup = styled.div`
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Button = styled.button`
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
padding: var(--input-padding);
|
||||||
|
|
||||||
|
font-size: var(--input-text-size);
|
||||||
|
font-weight: var(--input-text-weight);
|
||||||
|
line-height: var(--line-sm);
|
||||||
|
|
||||||
|
background: var(--button-secondary-background-fill);
|
||||||
|
border: var(--button-border-width) solid var(--button-secondary-border-color);
|
||||||
|
border-radius: var(--input-radius);
|
||||||
|
`;
|
||||||
|
|
||||||
|
/******************************************************
|
||||||
|
************************* Dom *************************
|
||||||
|
******************************************************/
|
||||||
|
|
||||||
|
interface PromptProps {
|
||||||
|
type: PromptType;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Prompt = memo<PromptProps>(({ type }) => {
|
||||||
|
const [tags, setTags] = useState<TagItem[]>([]);
|
||||||
|
|
||||||
|
const id =
|
||||||
|
type === 'positive' ? "[id$='2img_prompt'] textarea" : "[id$='2img_neg_prompt'] textarea";
|
||||||
|
|
||||||
|
const getValue = useCallback(() => {
|
||||||
|
try {
|
||||||
|
const textarea: HTMLTextAreaElement | any = get_uiCurrentTabContent().querySelector(id);
|
||||||
|
if (textarea) setTags(formatPrompt(textarea.value));
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const setValue = useCallback(() => {
|
||||||
|
try {
|
||||||
|
const textarea: HTMLTextAreaElement | any = get_uiCurrentTabContent().querySelector(id);
|
||||||
|
if (textarea) textarea.value = tags.map((t) => t.text).join(', ');
|
||||||
|
updateInput(textarea);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}, [tags]);
|
||||||
|
|
||||||
|
const setCurrentValue = useCallback((currentTags: TagItem[]) => {
|
||||||
|
try {
|
||||||
|
const textarea: HTMLTextAreaElement | any = get_uiCurrentTabContent().querySelector(id);
|
||||||
|
if (textarea) textarea.value = currentTags.map((t) => t.text).join(', ');
|
||||||
|
updateInput(textarea);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
<TagList setTags={setTags} setValue={setCurrentValue} tags={tags} type={type} />
|
||||||
|
<ButtonGroup>
|
||||||
|
<Button onClick={getValue} title="Load Prompt">
|
||||||
|
🔄
|
||||||
|
</Button>
|
||||||
|
<Button onClick={setValue} title="Set Prompt">
|
||||||
|
➡️
|
||||||
|
</Button>
|
||||||
|
</ButtonGroup>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Prompt;
|
||||||
|
|
@ -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(
|
||||||
(index_: number) => {
|
(index_: number) => {
|
||||||
const newTags = tags.filter((tag, index) => index !== index_);
|
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, currentPos: number, newPos: number) => {
|
(tag: TagItem, currentPos: number, newPos: number) => {
|
||||||
const newTags = [...tags];
|
const newTags = [...tags];
|
||||||
newTags.splice(currentPos, 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(
|
||||||
(index: number, tag: TagItem) => {
|
(index: number, tag: TagItem) => {
|
||||||
const newTags = [...tags];
|
const newTags = [...tags];
|
||||||
newTags[index] = 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;
|
||||||
|
|
@ -35,7 +35,7 @@ const Desc = styled.div`
|
||||||
************************* Dom *************************
|
************************* Dom *************************
|
||||||
******************************************************/
|
******************************************************/
|
||||||
|
|
||||||
const PromptGroup = memo(() => {
|
const PromptEditor = memo(() => {
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<Desc>Positive</Desc>
|
<Desc>Positive</Desc>
|
||||||
|
|
@ -46,4 +46,4 @@ const PromptGroup = memo(() => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
export default PromptGroup;
|
export default PromptEditor;
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
import negativeData from '@/data/negative.json';
|
||||||
|
import positiveData from '@/data/positive.json';
|
||||||
|
import { Converter } from '@/script/formatPrompt';
|
||||||
|
|
||||||
|
import { TagItem } from './TagList';
|
||||||
|
|
||||||
|
export const genTagType = (tag: TagItem): TagItem => {
|
||||||
|
const newTag = tag;
|
||||||
|
if (newTag.text.includes('<lora')) {
|
||||||
|
newTag.className = 'ReactTags__lora';
|
||||||
|
} else if (newTag.text.includes('<hypernet')) {
|
||||||
|
newTag.className = 'ReactTags__hypernet';
|
||||||
|
} else if (newTag.text.includes('<embedding')) {
|
||||||
|
newTag.className = 'ReactTags__embedding';
|
||||||
|
} else {
|
||||||
|
newTag.className = undefined;
|
||||||
|
}
|
||||||
|
return newTag;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const formatPrompt = (value: string) => {
|
||||||
|
const text = Converter.convertStr(value);
|
||||||
|
const textArray = Converter.convertStr2Array(text).map((item) => {
|
||||||
|
if (item.includes('<')) return item;
|
||||||
|
const newItem = item
|
||||||
|
.replaceAll(/\s+/g, ' ')
|
||||||
|
.replaceAll(/,|\.\|。/g, ',')
|
||||||
|
.replaceAll(/“|‘|”|"|\/'/g, '')
|
||||||
|
.replaceAll(', ', ',')
|
||||||
|
.replaceAll(',,', ',')
|
||||||
|
.replaceAll(',', ', ');
|
||||||
|
return Converter.convertStr2Array(newItem).join(', ');
|
||||||
|
});
|
||||||
|
return textArray.map((tag) => genTagType({ id: tag, text: tag }));
|
||||||
|
};
|
||||||
|
|
||||||
|
const genSuggestions = (array: string[]) =>
|
||||||
|
array.map((text) => {
|
||||||
|
return {
|
||||||
|
id: text,
|
||||||
|
text,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
export const suggestions = {
|
||||||
|
negative: genSuggestions(negativeData),
|
||||||
|
positive: genSuggestions(positiveData),
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,103 @@
|
||||||
|
import { Button, Divider, Form, InputNumber, Segmented, Space, Switch } from 'antd';
|
||||||
|
import isEqual from 'fast-deep-equal';
|
||||||
|
import { memo, useCallback } from 'react';
|
||||||
|
import { shallow } from 'zustand/shallow';
|
||||||
|
|
||||||
|
import { WebuiSetting, defaultSetting, useAppStore } from '@/store';
|
||||||
|
|
||||||
|
import { useStyles } from './style';
|
||||||
|
|
||||||
|
const { Item } = Form;
|
||||||
|
|
||||||
|
const Setting = memo(() => {
|
||||||
|
const setting = useAppStore((st) => st.setting, isEqual);
|
||||||
|
const onSetSetting = useAppStore((st) => st.onSetSetting, shallow);
|
||||||
|
const { styles } = useStyles();
|
||||||
|
|
||||||
|
const onReset = useCallback(() => {
|
||||||
|
onSetSetting(defaultSetting);
|
||||||
|
(gradioApp().querySelector('#settings_restart_gradio') as HTMLButtonElement)?.click();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const onFinish = useCallback((value: WebuiSetting) => {
|
||||||
|
onSetSetting(value);
|
||||||
|
(gradioApp().querySelector('#settings_restart_gradio') as HTMLButtonElement)?.click();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form
|
||||||
|
initialValues={setting}
|
||||||
|
layout="horizontal"
|
||||||
|
onFinish={onFinish}
|
||||||
|
size="small"
|
||||||
|
style={{ maxWidth: 320 }}
|
||||||
|
>
|
||||||
|
<Divider style={{ margin: '4px 0 8px' }} />
|
||||||
|
<title className={styles.title}>Promot Textarea</title>
|
||||||
|
<Item className={styles.item} label="Display mode" name="promotTextarea">
|
||||||
|
<Segmented options={['scroll', 'resizable']} />
|
||||||
|
</Item>
|
||||||
|
<Divider style={{ margin: '4px 0 8px' }} />
|
||||||
|
<title className={styles.title}>Sidebar</title>
|
||||||
|
<Item
|
||||||
|
className={styles.item}
|
||||||
|
label="Default expand"
|
||||||
|
name="sidebarExpand"
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Item>
|
||||||
|
<Item className={styles.item} label="Display mode" name="sidebarFixedMode">
|
||||||
|
<Segmented options={['fixed', 'float']} />
|
||||||
|
</Item>
|
||||||
|
<Item className={styles.item} label="Default width" name="sidebarWidth">
|
||||||
|
<InputNumber />
|
||||||
|
</Item>
|
||||||
|
<Divider style={{ margin: '4px 0 8px' }} />
|
||||||
|
<title className={styles.title}>ExtraNetwork Sidebar</title>
|
||||||
|
<Item
|
||||||
|
className={styles.item}
|
||||||
|
label="Enable"
|
||||||
|
name="enableExtraNetworkSidebar"
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Item>
|
||||||
|
<Item className={styles.item} label="Display mode" name="extraNetworkFixedMode">
|
||||||
|
<Segmented options={['fixed', 'float']} />
|
||||||
|
</Item>
|
||||||
|
<Item
|
||||||
|
className={styles.item}
|
||||||
|
label="Default expand"
|
||||||
|
name="extraNetworkSidebarExpand"
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Item>
|
||||||
|
<Item className={styles.item} label="Default width" name="extraNetworkSidebarWidth">
|
||||||
|
<InputNumber />
|
||||||
|
</Item>
|
||||||
|
<Item className={styles.item} label="Default card size" name="extraNetworkCardSize">
|
||||||
|
<InputNumber />
|
||||||
|
</Item>
|
||||||
|
<Divider style={{ margin: '4px 0 8px' }} />
|
||||||
|
<title className={styles.title}>Other</title>
|
||||||
|
<Item className={styles.item} label="Use svg icons" name="svgIcon" valuePropName="checked">
|
||||||
|
<Switch />
|
||||||
|
</Item>
|
||||||
|
<Divider style={{ margin: '4px 0 16px' }} />
|
||||||
|
<Item className={styles.item}>
|
||||||
|
<Space>
|
||||||
|
<Button htmlType="button" onClick={onReset} style={{ borderRadius: 4 }}>
|
||||||
|
Reset
|
||||||
|
</Button>
|
||||||
|
<Button htmlType="submit" style={{ borderRadius: 4 }} type="primary">
|
||||||
|
Apply and restart UI
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
</Item>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Setting;
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { createStyles } from 'antd-style';
|
||||||
|
|
||||||
|
export const useStyles = createStyles(({ css }) => ({
|
||||||
|
item: css`
|
||||||
|
.ant-row {
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
flex: unset !important;
|
||||||
|
flex-grow: unset !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
title: css`
|
||||||
|
margin-bottom: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
`,
|
||||||
|
}));
|
||||||
|
|
@ -1,6 +1,14 @@
|
||||||
import {Theme as AntdStyleTheme} from 'antd-style';
|
import type { LobeCustomStylish, LobeCustomToken } from '@lobehub/ui';
|
||||||
|
import 'antd-style';
|
||||||
|
import { AntdToken } from 'antd-style/lib/types/theme';
|
||||||
|
|
||||||
|
declare module 'antd-style' {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||||
|
export interface CustomToken extends LobeCustomToken {}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||||
|
export interface CustomStylish extends LobeCustomStylish {}
|
||||||
|
}
|
||||||
|
|
||||||
declare module 'styled-components' {
|
declare module 'styled-components' {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
export interface DefaultTheme extends AntdToken, LobeCustomToken {}
|
||||||
export interface DefaultTheme extends AntdStyleTheme {}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
/// <reference types="vite/client" />
|
||||||
|
|
@ -1,19 +1,27 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
|
"target": "esnext",
|
||||||
|
"module": "esnext",
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"downlevelIteration": true,
|
"downlevelIteration": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
"lib": ["dom", "dom.iterable", "esnext"],
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"importHelpers": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./src/*"]
|
"@/*": ["./src/*"]
|
||||||
},
|
},
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"skipLibCheck": true,
|
|
||||||
"strict": true
|
"strict": true
|
||||||
},
|
},
|
||||||
"exclude": ["javascript"],
|
"exclude": ["javascript"],
|
||||||
"extends": "./src/.umi/tsconfig.json",
|
|
||||||
"include": ["src", "typings.d.ts", "*.ts"]
|
"include": ["src", "typings", "*.ts", "*.d.ts", "*.tsx", "vite.config.ts"]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1,137 @@
|
||||||
import 'umi/typings';
|
// This file is generated by Umi automatically
|
||||||
|
// DO NOT CHANGE IT MANUALLY!
|
||||||
|
type CSSModuleClasses = { readonly [key: string]: string };
|
||||||
|
declare module '*.css' {
|
||||||
|
const classes: CSSModuleClasses;
|
||||||
|
export default classes;
|
||||||
|
}
|
||||||
|
declare module '*.scss' {
|
||||||
|
const classes: CSSModuleClasses;
|
||||||
|
export default classes;
|
||||||
|
}
|
||||||
|
declare module '*.sass' {
|
||||||
|
const classes: CSSModuleClasses;
|
||||||
|
export default classes;
|
||||||
|
}
|
||||||
|
declare module '*.less' {
|
||||||
|
const classes: CSSModuleClasses;
|
||||||
|
export default classes;
|
||||||
|
}
|
||||||
|
declare module '*.styl' {
|
||||||
|
const classes: CSSModuleClasses;
|
||||||
|
export default classes;
|
||||||
|
}
|
||||||
|
declare module '*.stylus' {
|
||||||
|
const classes: CSSModuleClasses;
|
||||||
|
export default classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// images
|
||||||
|
declare module '*.jpg' {
|
||||||
|
const source: string;
|
||||||
|
export default source;
|
||||||
|
}
|
||||||
|
declare module '*.jpeg' {
|
||||||
|
const source: string;
|
||||||
|
export default source;
|
||||||
|
}
|
||||||
|
declare module '*.png' {
|
||||||
|
const source: string;
|
||||||
|
export default source;
|
||||||
|
}
|
||||||
|
declare module '*.gif' {
|
||||||
|
const source: string;
|
||||||
|
export default source;
|
||||||
|
}
|
||||||
|
declare module '*.svg' {
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
export const ReactComponent: React.FunctionComponent<
|
||||||
|
React.SVGProps<SVGSVGElement> & { title?: string }
|
||||||
|
>;
|
||||||
|
|
||||||
|
const source: string;
|
||||||
|
export default source;
|
||||||
|
}
|
||||||
|
declare module '*.ico' {
|
||||||
|
const source: string;
|
||||||
|
export default source;
|
||||||
|
}
|
||||||
|
declare module '*.webp' {
|
||||||
|
const source: string;
|
||||||
|
export default source;
|
||||||
|
}
|
||||||
|
declare module '*.avif' {
|
||||||
|
const source: string;
|
||||||
|
export default source;
|
||||||
|
}
|
||||||
|
|
||||||
|
// media
|
||||||
|
declare module '*.mp4' {
|
||||||
|
const source: string;
|
||||||
|
export default source;
|
||||||
|
}
|
||||||
|
declare module '*.webm' {
|
||||||
|
const source: string;
|
||||||
|
export default source;
|
||||||
|
}
|
||||||
|
declare module '*.ogg' {
|
||||||
|
const source: string;
|
||||||
|
export default source;
|
||||||
|
}
|
||||||
|
declare module '*.mp3' {
|
||||||
|
const source: string;
|
||||||
|
export default source;
|
||||||
|
}
|
||||||
|
declare module '*.wav' {
|
||||||
|
const source: string;
|
||||||
|
export default source;
|
||||||
|
}
|
||||||
|
declare module '*.flac' {
|
||||||
|
const source: string;
|
||||||
|
export default source;
|
||||||
|
}
|
||||||
|
declare module '*.aac' {
|
||||||
|
const source: string;
|
||||||
|
export default source;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fonts
|
||||||
|
declare module '*.woff' {
|
||||||
|
const source: string;
|
||||||
|
export default source;
|
||||||
|
}
|
||||||
|
declare module '*.woff2' {
|
||||||
|
const source: string;
|
||||||
|
export default source;
|
||||||
|
}
|
||||||
|
declare module '*.eot' {
|
||||||
|
const source: string;
|
||||||
|
export default source;
|
||||||
|
}
|
||||||
|
declare module '*.ttf' {
|
||||||
|
const source: string;
|
||||||
|
export default source;
|
||||||
|
}
|
||||||
|
declare module '*.otf' {
|
||||||
|
const source: string;
|
||||||
|
export default source;
|
||||||
|
}
|
||||||
|
|
||||||
|
// other
|
||||||
|
declare module '*.wasm' {
|
||||||
|
const initWasm: (options: WebAssembly.Imports) => Promise<WebAssembly.Exports>;
|
||||||
|
export default initWasm;
|
||||||
|
}
|
||||||
|
declare module '*.webmanifest' {
|
||||||
|
const source: string;
|
||||||
|
export default source;
|
||||||
|
}
|
||||||
|
declare module '*.pdf' {
|
||||||
|
const source: string;
|
||||||
|
export default source;
|
||||||
|
}
|
||||||
|
declare module '*.txt' {
|
||||||
|
const source: string;
|
||||||
|
export default source;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
import react from '@vitejs/plugin-react';
|
||||||
|
import { resolve } from 'node:path';
|
||||||
|
import { defineConfig } from 'vite';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
base: 'dev',
|
||||||
|
build: {
|
||||||
|
outDir: './',
|
||||||
|
rollupOptions: {
|
||||||
|
input: resolve(__dirname, 'src/main.tsx'),
|
||||||
|
output: {
|
||||||
|
assetFileNames: `javascript/[name].[ext]`,
|
||||||
|
chunkFileNames: `javascript/[name].js`,
|
||||||
|
entryFileNames: `javascript/[name].js`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
react({
|
||||||
|
babel: {
|
||||||
|
plugins: ['@babel/plugin-syntax-import-assertions'],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
configureServer: (server) => {
|
||||||
|
server.middlewares.use((_request, res, next) => {
|
||||||
|
res.setHeader('Cross-Origin-Embedder-Policy', 'unsafe-none');
|
||||||
|
res.setHeader('Cross-Origin-Opener-Policy', 'unsafe-non');
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
name: 'configure-response-headers',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
configureServer: (server) => {
|
||||||
|
server.middlewares.use(async(_request, res, next): Promise<void> => {
|
||||||
|
if (
|
||||||
|
_request.originalUrl === '/dev' ||
|
||||||
|
_request.originalUrl === '/dev?__theme=dark' ||
|
||||||
|
_request.originalUrl === '/dev?__theme=light'
|
||||||
|
) {
|
||||||
|
const response = await fetch('http://127.0.0.1:7860/');
|
||||||
|
|
||||||
|
let updatedResponse = await response.text();
|
||||||
|
|
||||||
|
const toAdd = `
|
||||||
|
<script type="module" src="/dev/src/_react_refresh.js"></script>
|
||||||
|
<script type="module" src="/dev/src/main.tsx"></script>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// replace </body> with </body><script type="module" src="/main.js"></script>
|
||||||
|
updatedResponse = updatedResponse.replace('</body>', `</body>${toAdd}`);
|
||||||
|
|
||||||
|
// Set the modified response
|
||||||
|
res.statusCode = 200;
|
||||||
|
res.setHeader('Content-Type', 'text/html');
|
||||||
|
res.setHeader('charset', 'utf8');
|
||||||
|
res.end(updatedResponse);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continue to the next middleware
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
name: 'route-default-to-index',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': resolve(__dirname, 'src'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
host: '127.0.0.1',
|
||||||
|
port: 5173,
|
||||||
|
proxy: {
|
||||||
|
'/queue/join': {
|
||||||
|
target: 'ws://127.0.0.1:7860',
|
||||||
|
ws: true,
|
||||||
|
},
|
||||||
|
'^(?!.*dev).*$': 'http://127.0.0.1:7860',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue