mirror of
https://github.com/lobehub/sd-webui-lobe-theme.git
synced 2026-01-09 06:23:44 +08:00
✨ feat: Fit SD WebUI 1.9
This commit is contained in:
parent
77102c0e1e
commit
3e01da0cea
@ -10,7 +10,7 @@ module.exports = {
|
||||
entryLocale: 'en_US',
|
||||
output: 'locales',
|
||||
outputLocales: outputLocales,
|
||||
modelName: 'gpt-3.5-turbo-1106',
|
||||
modelName: 'gpt-3.5-turbo-0125',
|
||||
experimental: {
|
||||
jsonMode: true,
|
||||
},
|
||||
|
||||
77
javascript/giscus-BNK3dBIH.js
Normal file
77
javascript/giscus-BNK3dBIH.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -25,6 +25,7 @@
|
||||
"title": "Themenfeedback"
|
||||
},
|
||||
"themeSetting": {
|
||||
"desc": "Einstellungen für Thema und Layout",
|
||||
"title": "Themen-Einstellungen"
|
||||
}
|
||||
},
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
{
|
||||
"brand": {
|
||||
"custom": "Custom",
|
||||
"kitchen": "Kitchen",
|
||||
"lobe": "LobeHub",
|
||||
"custom": "Custom"
|
||||
"lobe": "LobeHub"
|
||||
},
|
||||
"cancel": "Cancel",
|
||||
"confirm": "Confirm",
|
||||
@ -10,34 +10,35 @@
|
||||
"initializing": "StableDiffusion / LobeTheme is initializing, please wait..."
|
||||
},
|
||||
"footer": {
|
||||
"resources": "Resources",
|
||||
"community": "Community",
|
||||
"help": "Help",
|
||||
"moreProducts": "More Products"
|
||||
"moreProducts": "More Products",
|
||||
"resources": "Resources"
|
||||
},
|
||||
"header": {
|
||||
"feedback": "Feedback",
|
||||
"switchTheme": "Switch Light/Dark Theme",
|
||||
"setting": "Setting"
|
||||
"setting": "Setting",
|
||||
"switchTheme": "Switch Light/Dark Theme"
|
||||
},
|
||||
"modal": {
|
||||
"themeFeedback": {
|
||||
"title": "Theme Feedback"
|
||||
},
|
||||
"themeSetting": {
|
||||
"desc": "Preferences and Layout Settings",
|
||||
"title": "Theme Settings"
|
||||
}
|
||||
},
|
||||
"prompt": {
|
||||
"area": {
|
||||
"object": "Object Selection",
|
||||
"attribute": "Attribute Selection",
|
||||
"object": "Object Selection",
|
||||
"tag": "Tag Selection"
|
||||
},
|
||||
"load": "Load Prompt",
|
||||
"set": "Set Prompt",
|
||||
"negative": "Negative",
|
||||
"positive": "Positive"
|
||||
"positive": "Positive",
|
||||
"set": "Set Prompt"
|
||||
},
|
||||
"setting": {
|
||||
"button": {
|
||||
@ -45,152 +46,152 @@
|
||||
"submit": "Apply and Restart Interface"
|
||||
},
|
||||
"confirmPageUnload": {
|
||||
"title": "Confirmation on page leaving",
|
||||
"desc": "Helps prevent loss of unsaved data"
|
||||
"desc": "Helps prevent loss of unsaved data",
|
||||
"title": "Confirmation on page leaving"
|
||||
},
|
||||
"customFont": {
|
||||
"title": "Custom Font",
|
||||
"desc": "When enabled, it will automatically load a webfont to enhance the display of text in Chinese, English, and code"
|
||||
"desc": "When enabled, it will automatically load a webfont to enhance the display of text in Chinese, English, and code",
|
||||
"title": "Custom Font"
|
||||
},
|
||||
"customLogo": {
|
||||
"title": "Custom Logo",
|
||||
"desc": "Support URL / Base64 / Emoji symbols"
|
||||
"desc": "Support URL / Base64 / Emoji symbols",
|
||||
"title": "Custom Logo"
|
||||
},
|
||||
"customTitle": {
|
||||
"title": "Custom Title",
|
||||
"desc": "Custom Logo Title"
|
||||
"desc": "Custom Logo Title",
|
||||
"title": "Custom Title"
|
||||
},
|
||||
"extraNetworkSidebar": {
|
||||
"defaultCardSize": {
|
||||
"title": "Model Cover Size",
|
||||
"desc": "Default value of model cover size when starting"
|
||||
"desc": "Default value of model cover size when starting",
|
||||
"title": "Model Cover Size"
|
||||
},
|
||||
"defaultExpand": {
|
||||
"title": "Default Expand",
|
||||
"desc": "Whether to expand the sidebar by default when starting"
|
||||
"desc": "Whether to expand the sidebar by default when starting",
|
||||
"title": "Default Expand"
|
||||
},
|
||||
"defaultWidth": {
|
||||
"title": "Default Width",
|
||||
"desc": "Default width of the sidebar when starting"
|
||||
"desc": "Default width of the sidebar when starting",
|
||||
"title": "Default Width"
|
||||
},
|
||||
"displayMode": {
|
||||
"title": "Display Mode",
|
||||
"desc": "Fixed as grid mode for constant display, auto-expand when the mouse moves to the side in floating mode"
|
||||
"desc": "Fixed as grid mode for constant display, auto-expand when the mouse moves to the side in floating mode",
|
||||
"title": "Display Mode"
|
||||
},
|
||||
"enable": {
|
||||
"title": "Enable",
|
||||
"desc": "Enable the extra network sidebar on the right side"
|
||||
"desc": "Enable the extra network sidebar on the right side",
|
||||
"title": "Enable"
|
||||
}
|
||||
},
|
||||
"tab": {
|
||||
"appearance": "Appearance",
|
||||
"sidebar": "Sidebar",
|
||||
"layout": "Layout",
|
||||
"experimental": "Experimental"
|
||||
},
|
||||
"group": {
|
||||
"experimental": "Experimental Features",
|
||||
"extraNetworkSidebar": "Extra Network Sidebar",
|
||||
"layout": "Layout Settings",
|
||||
"promptTextarea": "Prompt Textbox",
|
||||
"quickSettingSidebar": "Quick Setting Sidebar",
|
||||
"theme": "Theme Settings",
|
||||
"experimental": "Experimental Features"
|
||||
},
|
||||
"imageInfo": {
|
||||
"title": "Image Info Alternative",
|
||||
"desc": "Display better image information in the generated image"
|
||||
"theme": "Theme Settings"
|
||||
},
|
||||
"hideFooter": {
|
||||
"title": "Hide Footer",
|
||||
"desc": "Hide the theme footer and only display the default footer of stable diffusion webui"
|
||||
"desc": "Hide the theme footer and only display the default footer of stable diffusion webui",
|
||||
"title": "Hide Footer"
|
||||
},
|
||||
"imageInfo": {
|
||||
"desc": "Display better image information in the generated image",
|
||||
"title": "Image Info Alternative"
|
||||
},
|
||||
"language": {
|
||||
"title": "Language",
|
||||
"desc": "Lobe Theme language"
|
||||
"desc": "Lobe Theme language",
|
||||
"title": "Language"
|
||||
},
|
||||
"logoType": {
|
||||
"title": "Logo Type",
|
||||
"desc": "Logo Type",
|
||||
"preview": "Preview"
|
||||
"preview": "Preview",
|
||||
"title": "Logo Type"
|
||||
},
|
||||
"neutralColor": {
|
||||
"title": "Neutral Color",
|
||||
"desc": "Customize different shades of gray with different color tendencies, the second one is the original Kitchen neutral color"
|
||||
"desc": "Customize different shades of gray with different color tendencies, the second one is the original Kitchen neutral color",
|
||||
"title": "Neutral Color"
|
||||
},
|
||||
"primaryColor": {
|
||||
"title": "Primary Color",
|
||||
"desc": "Custom primary color, the second one is the original Kitchen theme color"
|
||||
"desc": "Custom primary color, the second one is the original Kitchen theme color",
|
||||
"title": "Primary Color"
|
||||
},
|
||||
"promptDisplayMode": {
|
||||
"title": "Prompt Display Mode",
|
||||
"desc": "Fixed height or auto height with draggable resize support",
|
||||
"resizable": "Resizable",
|
||||
"scroll": "Scroll"
|
||||
"scroll": "Scroll",
|
||||
"title": "Prompt Display Mode"
|
||||
},
|
||||
"promptEditor": {
|
||||
"title": "Prompt Editor",
|
||||
"desc": "Provide a simple prompt editor at the top of the quick setting sidebar"
|
||||
"desc": "Provide a simple prompt editor at the top of the quick setting sidebar",
|
||||
"title": "Prompt Editor"
|
||||
},
|
||||
"promptHighlight": {
|
||||
"title": "Prompt Syntax Highlighting",
|
||||
"desc": "Automatically colorize prompt display according to the Stable Diffusion syntax rules"
|
||||
"desc": "Automatically colorize prompt display according to the Stable Diffusion syntax rules",
|
||||
"title": "Prompt Syntax Highlighting"
|
||||
},
|
||||
"quickSettingSidebar": {
|
||||
"defaultExpand": {
|
||||
"title": "Default Expand",
|
||||
"desc": "Whether to expand the sidebar by default when starting"
|
||||
"desc": "Whether to expand the sidebar by default when starting",
|
||||
"title": "Default Expand"
|
||||
},
|
||||
"defaultWidth": {
|
||||
"title": "Default Width",
|
||||
"desc": "Default width of the sidebar when starting"
|
||||
"desc": "Default width of the sidebar when starting",
|
||||
"title": "Default Width"
|
||||
},
|
||||
"displayMode": {
|
||||
"title": "Display Mode",
|
||||
"desc": "Fixed as grid mode for constant display, auto-expand when the mouse moves to the side in floating mode"
|
||||
"desc": "Fixed as grid mode for constant display, auto-expand when the mouse moves to the side in floating mode",
|
||||
"title": "Display Mode"
|
||||
},
|
||||
"enable": {
|
||||
"title": "Enable",
|
||||
"desc": "Enable the quick setting sidebar on the left side"
|
||||
"desc": "Enable the quick setting sidebar on the left side",
|
||||
"title": "Enable"
|
||||
}
|
||||
},
|
||||
"reduceAnimation": {
|
||||
"title": "Reduce Animation",
|
||||
"desc": "Reduce the blur effect and background flow color, which can improve smoothness and save CPU usage"
|
||||
"desc": "Reduce the blur effect and background flow color, which can improve smoothness and save CPU usage",
|
||||
"title": "Reduce Animation"
|
||||
},
|
||||
"splitPreviewer": {
|
||||
"title": "Split Previewer",
|
||||
"desc": "Put the prompt input box on the left and the generate button on the right, ensuring that the generated image is always displayed at the top when scrolling (experimental)"
|
||||
"desc": "Put the prompt input box on the left and the generate button on the right, ensuring that the generated image is always displayed at the top when scrolling (experimental)",
|
||||
"title": "Split Previewer"
|
||||
},
|
||||
"svgIcons": {
|
||||
"title": "SVG Icons",
|
||||
"desc": "Replace all Emoji icons in stable diffusion webui with SVG icons globally"
|
||||
"desc": "Replace all Emoji icons in stable diffusion webui with SVG icons globally",
|
||||
"title": "SVG Icons"
|
||||
},
|
||||
"tab": {
|
||||
"appearance": "Appearance",
|
||||
"experimental": "Experimental",
|
||||
"layout": "Layout",
|
||||
"sidebar": "Sidebar"
|
||||
}
|
||||
},
|
||||
"share": "Share",
|
||||
"shareModal": {
|
||||
"download": "Download Screenshot",
|
||||
"imageType": "Image Format",
|
||||
"screenshot": "Screenshot",
|
||||
"info": "Image Info",
|
||||
"screenshot": "Screenshot",
|
||||
"settings": "Export Settings",
|
||||
"withBackground": "Include Background Image",
|
||||
"withFooter": "Include Footer",
|
||||
"warn": "Please Generate Image First",
|
||||
"showNegative": "Show Negative Promot",
|
||||
"showConfig": "Show Generate Config",
|
||||
"showAllImages": "Show All Images",
|
||||
"title": "Image Name",
|
||||
"showConfig": "Show Generate Config",
|
||||
"showNegative": "Show Negative Promot",
|
||||
"tabs": {
|
||||
"info": "Info",
|
||||
"settings": "Settings"
|
||||
}
|
||||
},
|
||||
"title": "Image Name",
|
||||
"warn": "Please Generate Image First",
|
||||
"withBackground": "Include Background Image",
|
||||
"withFooter": "Include Footer"
|
||||
},
|
||||
"sidebar": {
|
||||
"extraNetwork": "Extra Network",
|
||||
"quickSetting": "Quick Setting",
|
||||
"mode": {
|
||||
"fixed": "Fixed",
|
||||
"float": "Float"
|
||||
}
|
||||
},
|
||||
"quickSetting": "Quick Setting"
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
"title": "Comentarios sobre el tema"
|
||||
},
|
||||
"themeSetting": {
|
||||
"desc": "Preferencias y configuración de diseño",
|
||||
"title": "Configuración del tema"
|
||||
}
|
||||
},
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
"title": "Retour sur le thème"
|
||||
},
|
||||
"themeSetting": {
|
||||
"desc": "Paramètres de thème et de mise en page",
|
||||
"title": "Paramètres du thème"
|
||||
}
|
||||
},
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
"title": "テーマフィードバック"
|
||||
},
|
||||
"themeSetting": {
|
||||
"desc": "テーマとレイアウト設定",
|
||||
"title": "テーマ設定"
|
||||
}
|
||||
},
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
"title": "테마 피드백"
|
||||
},
|
||||
"themeSetting": {
|
||||
"desc": "테마 및 레이아웃 설정",
|
||||
"title": "테마 설정"
|
||||
}
|
||||
},
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
"title": "Feedback do Tema"
|
||||
},
|
||||
"themeSetting": {
|
||||
"desc": "Preferências e configurações de layout",
|
||||
"title": "Configurações do Tema"
|
||||
}
|
||||
},
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
"title": "Обратная связь по теме"
|
||||
},
|
||||
"themeSetting": {
|
||||
"desc": "Настройки темы и макета",
|
||||
"title": "Настройки темы"
|
||||
}
|
||||
},
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
"title": "Tema Geri Bildirimi"
|
||||
},
|
||||
"themeSetting": {
|
||||
"desc": "Tema ve Düzen Ayarları",
|
||||
"title": "Tema Ayarları"
|
||||
}
|
||||
},
|
||||
|
||||
@ -25,7 +25,8 @@
|
||||
"title": "主题反馈"
|
||||
},
|
||||
"themeSetting": {
|
||||
"title": "主题设置"
|
||||
"title": "主题设置",
|
||||
"desc": "偏好与布局设置"
|
||||
}
|
||||
},
|
||||
"prompt": {
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
"title": "主題反饋"
|
||||
},
|
||||
"themeSetting": {
|
||||
"desc": "偏好與版面設定",
|
||||
"title": "主題設置"
|
||||
}
|
||||
},
|
||||
|
||||
117
package.json
117
package.json
@ -2,7 +2,7 @@
|
||||
"name": "sd-webui-lobe-theme",
|
||||
"version": "3.4.10",
|
||||
"private": true,
|
||||
"description": "The modern theme for stable diffusion webui, exquisite interface design, highly customizable UI, and efficiency boosting features.",
|
||||
"description": "LobeThem: The Modern Theme for Stable Diffusion WebUI, Exquisite interface design, Highly customizable UI, and Efficiency boosting features.",
|
||||
"keywords": [
|
||||
"lobehub",
|
||||
"stable-diffusion-webui",
|
||||
@ -67,67 +67,68 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@bluelovers/auto1111-pnginfo": "^2.0.1",
|
||||
"@lobehub/ui": "latest",
|
||||
"@rollup/rollup-win32-x64-msvc": "^4.13.0",
|
||||
"ahooks": "^3",
|
||||
"antd": "^5",
|
||||
"antd-style": "latest",
|
||||
"consola": "^3",
|
||||
"dayjs": "^1",
|
||||
"i18next": "^23",
|
||||
"i18next-http-backend": "^2",
|
||||
"lodash-es": "^4",
|
||||
"@bluelovers/auto1111-pnginfo": "^2.0.2",
|
||||
"@lobehub/ui": "^1.138.24",
|
||||
"@rollup/rollup-win32-x64-msvc": "^4.17.2",
|
||||
"ahooks": "^3.7.11",
|
||||
"antd": "5.17.0",
|
||||
"antd-style": "^3.6.2",
|
||||
"consola": "^3.2.3",
|
||||
"dayjs": "^1.11.11",
|
||||
"i18next": "^23.11.4",
|
||||
"i18next-http-backend": "^2.5.1",
|
||||
"lodash-es": "^4.17.21",
|
||||
"lucide-react": "latest",
|
||||
"lucide-static": "latest",
|
||||
"modern-screenshot": "^4",
|
||||
"polished": "^4",
|
||||
"react": "^18",
|
||||
"react-dnd": "^16",
|
||||
"react-dnd-html5-backend": "^16",
|
||||
"react-dom": "^18",
|
||||
"react-helmet": "^6",
|
||||
"react-i18next": "^13",
|
||||
"react-layout-kit": "^1",
|
||||
"react-rnd": "^10",
|
||||
"react-tag-input": "^6",
|
||||
"semver": "^7",
|
||||
"shikiji": "^0.9",
|
||||
"swr": "^2",
|
||||
"zustand": "^4.4.1",
|
||||
"zustand-utils": "^1.3.1"
|
||||
"modern-screenshot": "^4.4.39",
|
||||
"polished": "^4.3.1",
|
||||
"react": "^18.3.1",
|
||||
"react-dnd": "^16.0.1",
|
||||
"react-dnd-html5-backend": "^16.0.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-helmet": "^6.1.0",
|
||||
"react-i18next": "^13.5.0",
|
||||
"react-layout-kit": "^1.9.0",
|
||||
"react-rnd": "^10.4.10",
|
||||
"react-tag-input": "^6.9.0",
|
||||
"semver": "^7.6.2",
|
||||
"shikiji": "^0.9.19",
|
||||
"swr": "^2.2.5",
|
||||
"url-join": "^5.0.0",
|
||||
"zustand": "^4.5.2",
|
||||
"zustand-utils": "^1.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^18",
|
||||
"@lobehub/lint": "latest",
|
||||
"@testing-library/jest-dom": "^6",
|
||||
"@testing-library/react": "^14",
|
||||
"@types/lodash-es": "^4",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"@types/react-helmet": "^6",
|
||||
"@types/react-tag-input": "^6",
|
||||
"@types/semver": "^7",
|
||||
"@vitejs/plugin-react-swc": "^3",
|
||||
"@vitest/coverage-v8": "^1",
|
||||
"commitlint": "^18",
|
||||
"dotenv": "^16",
|
||||
"eslint": "^8",
|
||||
"fast-deep-equal": "^3",
|
||||
"husky": "^8",
|
||||
"jsdom": "^23.0.0",
|
||||
"lint-staged": "^15",
|
||||
"prettier": "^3",
|
||||
"query-string": "^8",
|
||||
"remark": "^14",
|
||||
"remark-cli": "^11",
|
||||
"semantic-release": "^21",
|
||||
"stylelint": "^15",
|
||||
"terser": "^5",
|
||||
"typescript": "^5",
|
||||
"vite": "^5",
|
||||
"vitest": "latest"
|
||||
"@commitlint/cli": "^18.6.1",
|
||||
"@lobehub/lint": "^1.23.4",
|
||||
"@testing-library/jest-dom": "^6.4.5",
|
||||
"@testing-library/react": "^14.3.1",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/node": "^20.12.12",
|
||||
"@types/react": "^18.3.2",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@types/react-helmet": "^6.1.11",
|
||||
"@types/react-tag-input": "^6.6.6",
|
||||
"@types/semver": "^7.5.8",
|
||||
"@vitejs/plugin-react-swc": "^3.6.0",
|
||||
"@vitest/coverage-v8": "^1.6.0",
|
||||
"commitlint": "^18.6.1",
|
||||
"dotenv": "^16.4.5",
|
||||
"eslint": "^8.57.0",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"husky": "^8.0.3",
|
||||
"jsdom": "^23.2.0",
|
||||
"lint-staged": "^15.2.2",
|
||||
"prettier": "^3.2.5",
|
||||
"query-string": "^8.2.0",
|
||||
"remark": "^14.0.3",
|
||||
"remark-cli": "^11.0.0",
|
||||
"semantic-release": "^21.1.2",
|
||||
"stylelint": "^15.11.0",
|
||||
"terser": "^5.31.0",
|
||||
"typescript": "^5.4.5",
|
||||
"vite": "^5.2.11",
|
||||
"vitest": "~1.2.2"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
|
||||
@ -2,6 +2,7 @@ import { LayoutHeader, LayoutMain, LayoutSidebar } from '@lobehub/ui';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { memo, useEffect } from 'react';
|
||||
|
||||
import StructuredData from '@/components/StructuredData';
|
||||
import PromptFormator from '@/features/PromptFormator';
|
||||
import '@/locales/config';
|
||||
import ImageInfo from '@/modules/ImageInfo/page';
|
||||
@ -35,6 +36,7 @@ const Index = memo(() => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<StructuredData />
|
||||
<GlobalStyle />
|
||||
<LayoutHeader headerHeight={HEADER_HEIGHT}>
|
||||
<Header />
|
||||
|
||||
@ -67,6 +67,7 @@ export const Layout = memo<PropsWithChildren>(({ children }) => {
|
||||
content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, viewport-fit=cover, user-scalable=no"
|
||||
name="viewport"
|
||||
/>
|
||||
<title>{TITLE}</title>
|
||||
<meta content={TITLE} name="apple-mobile-web-app-title" />
|
||||
<meta content={TITLE} name="application-name" />
|
||||
<meta content={DESC} name="description" />
|
||||
@ -76,7 +77,29 @@ export const Layout = memo<PropsWithChildren>(({ children }) => {
|
||||
<meta content="yes" name="apple-mobile-web-app-capable" />
|
||||
<meta content={TITLE} name="apple-mobile-web-app-title" />
|
||||
<meta content="black-translucent" name="apple-mobile-web-app-status-bar-style" />
|
||||
<meta content={TITLE} name="apple-mobile-web-app-title" />
|
||||
<meta content="yes" name="apple-mobile-web-app-capable" />
|
||||
<meta content="index,follow" name="robots" />
|
||||
<link href={manifest(genAssets)} rel="manifest" />
|
||||
<meta content={TITLE} property="og:title" />
|
||||
<meta content={DESC} property="og:description" />
|
||||
<meta content="https://github.com/lobehub/sd-webui-lobe-theme" property="og:url" />
|
||||
<meta content={TITLE} property="og:site_name" />
|
||||
<meta content="en-US" property="og:locale" />
|
||||
<meta
|
||||
content="https://repository-images.githubusercontent.com/606329910/7fd79db5-fd91-450c-9e95-8ccce8ffdc0b"
|
||||
property="og:image"
|
||||
/>
|
||||
<meta content="website" property="og:type" />
|
||||
<meta content="summary_large_image" name="twitter:card" />
|
||||
<meta content="@lobehub" name="twitter:site" />
|
||||
<meta content={TITLE} name="twitter:title" />
|
||||
<meta content={DESC} name="twitter:description" />
|
||||
<meta
|
||||
content="https://repository-images.githubusercontent.com/606329910/7fd79db5-fd91-450c-9e95-8ccce8ffdc0b"
|
||||
name="twitter:image"
|
||||
/>
|
||||
<link href="https://github.com/lobehub/sd-webui-lobe-theme" rel="canonical" />
|
||||
</Helmet>
|
||||
<GlobalLayout>
|
||||
{storeLoading === false && loading === false ? children : <Loading />}
|
||||
|
||||
26
src/components/StructuredData/index.tsx
Normal file
26
src/components/StructuredData/index.tsx
Normal file
@ -0,0 +1,26 @@
|
||||
import { FC } from 'react';
|
||||
|
||||
import pkg from '@/../package.json';
|
||||
import { ldModule } from '@/components/StructuredData/ld';
|
||||
|
||||
const TITLE = 'Stable Diffusion · LobeHub';
|
||||
const DESC = pkg.description;
|
||||
|
||||
const StructuredData: FC = () => {
|
||||
const ld = ldModule.generate({
|
||||
description: DESC,
|
||||
image:
|
||||
'https://repository-images.githubusercontent.com/606329910/7fd79db5-fd91-450c-9e95-8ccce8ffdc0b',
|
||||
title: TITLE,
|
||||
url: '/',
|
||||
});
|
||||
|
||||
return (
|
||||
<script
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(ld) }}
|
||||
id="structured-data"
|
||||
type="application/ld+json"
|
||||
/>
|
||||
);
|
||||
};
|
||||
export default StructuredData;
|
||||
216
src/components/StructuredData/ld.ts
Normal file
216
src/components/StructuredData/ld.ts
Normal file
@ -0,0 +1,216 @@
|
||||
import urlJoin from 'url-join';
|
||||
|
||||
import pkg from '@/../package.json';
|
||||
import { EMAIL_BUSINESS, EMAIL_SUPPORT, OFFICIAL_SITE, SITE_URL, X } from '@/const/url';
|
||||
|
||||
const LAST_MODIFIED = new Date().toISOString();
|
||||
export const AUTHOR_LIST = {
|
||||
arvinxx: {
|
||||
avatar: 'https://avatars.githubusercontent.com/u/28616219?v=4',
|
||||
desc: 'Founder, Design Engineer',
|
||||
name: 'Arvin Xu',
|
||||
url: 'https://github.com/arvinxx',
|
||||
},
|
||||
canisminor: {
|
||||
avatar: 'https://avatars.githubusercontent.com/u/17870709?v=4',
|
||||
desc: 'Founder, Design Engineer',
|
||||
name: 'CanisMinor',
|
||||
url: 'https://github.com/arvinxx',
|
||||
},
|
||||
lobehub: {
|
||||
avatar: 'https://avatars.githubusercontent.com/u/131470832?v=4',
|
||||
desc: 'Official Account',
|
||||
name: 'LobeHub',
|
||||
url: 'https://github.com/lobehub',
|
||||
},
|
||||
};
|
||||
|
||||
class Ld {
|
||||
generate({
|
||||
image = '/og/cover.png',
|
||||
url,
|
||||
title,
|
||||
description,
|
||||
date,
|
||||
webpage = {
|
||||
enable: true,
|
||||
},
|
||||
}: {
|
||||
date?: string;
|
||||
description: string;
|
||||
image?: string;
|
||||
title: string;
|
||||
url: string;
|
||||
webpage?: {
|
||||
enable?: boolean;
|
||||
search?: boolean;
|
||||
};
|
||||
}) {
|
||||
return {
|
||||
'@context': 'https://schema.org',
|
||||
'@graph': [
|
||||
this.genWebSite(),
|
||||
webpage?.enable &&
|
||||
this.genWebPage({
|
||||
...webpage,
|
||||
date,
|
||||
description,
|
||||
image,
|
||||
title,
|
||||
url,
|
||||
}),
|
||||
image && this.genImageObject({ image, url }),
|
||||
this.genOrganization(),
|
||||
].filter(Boolean),
|
||||
};
|
||||
}
|
||||
|
||||
genOrganization() {
|
||||
return {
|
||||
'@id': this.getId(SITE_URL, '#organization'),
|
||||
'@type': 'Organization',
|
||||
'alternateName': 'LobeTheme',
|
||||
'contactPoint': {
|
||||
'@type': 'ContactPoint',
|
||||
'contactType': 'customer support',
|
||||
'email': EMAIL_SUPPORT,
|
||||
},
|
||||
'description':
|
||||
'We are a group of e/acc design-engineers, hoping to provide modern design components and tools for AIGC, and creating a technology-driven forum, fostering knowledge interaction and the exchange of ideas that may culminate in mutual inspiration and collaborative innovation.',
|
||||
'email': EMAIL_BUSINESS,
|
||||
'founders': [this.getAuthors(['arvinxx']), this.getAuthors(['canisminor'])],
|
||||
'image': urlJoin(OFFICIAL_SITE, '/icon-512x512.png'),
|
||||
'logo': {
|
||||
'@type': 'ImageObject',
|
||||
'height': 512,
|
||||
'url': urlJoin(OFFICIAL_SITE, '/icon-512x512.png'),
|
||||
'width': 512,
|
||||
},
|
||||
'name': 'LobeHub',
|
||||
'sameAs': [
|
||||
X,
|
||||
'https://github.com/lobehub',
|
||||
'https://medium.com/@lobehub',
|
||||
'https://www.youtube.com/@lobehub',
|
||||
],
|
||||
'url': OFFICIAL_SITE,
|
||||
};
|
||||
}
|
||||
|
||||
getAuthors(ids: string[] = []) {
|
||||
const defaultAuthor = {
|
||||
'@id': this.getId(SITE_URL, '#organization'),
|
||||
'@type': 'Organization',
|
||||
};
|
||||
if (!ids || ids.length === 0) return defaultAuthor;
|
||||
if (ids.length === 1 && ids[0] === 'lobehub') return defaultAuthor;
|
||||
const personId = ids.find((id) => id !== 'lobehub');
|
||||
if (!personId) return defaultAuthor;
|
||||
const person = (AUTHOR_LIST as any)?.[personId];
|
||||
if (!person) return defaultAuthor;
|
||||
return {
|
||||
'@type': 'Person',
|
||||
'name': person.name,
|
||||
'url': person.url,
|
||||
};
|
||||
}
|
||||
|
||||
genWebPage({
|
||||
date,
|
||||
image,
|
||||
search,
|
||||
description,
|
||||
title,
|
||||
url,
|
||||
}: {
|
||||
breadcrumbs?: { title: string; url: string }[];
|
||||
date?: string;
|
||||
description: string;
|
||||
image?: string;
|
||||
search?: boolean;
|
||||
title: string;
|
||||
url: string;
|
||||
}) {
|
||||
const fixedUrl = this.fixUrl(url);
|
||||
const dateCreated = date ? new Date(date).toISOString() : LAST_MODIFIED;
|
||||
const dateModified = date ? new Date(date).toISOString() : LAST_MODIFIED;
|
||||
|
||||
const baseInfo: any = {
|
||||
'@id': fixedUrl,
|
||||
'@type': 'WebPage',
|
||||
'about': {
|
||||
'@id': this.getId(SITE_URL, '#organization'),
|
||||
},
|
||||
'breadcrumbs': {
|
||||
'@id': this.getId(fixedUrl, '#breadcrumb'),
|
||||
},
|
||||
'dateModified': dateModified,
|
||||
'datePublished': dateCreated,
|
||||
'description': description,
|
||||
'image': {
|
||||
'@id': this.getId(fixedUrl, '#primaryimage'),
|
||||
},
|
||||
'inLanguage': 'en-US',
|
||||
'isPartOf': {
|
||||
'@id': this.getId(SITE_URL, '#website'),
|
||||
},
|
||||
'name': this.fixTitle(title),
|
||||
'primaryImageOfPage': {
|
||||
'@id': this.getId(fixedUrl, '#primaryimage'),
|
||||
},
|
||||
'thumbnailUrl': image,
|
||||
};
|
||||
|
||||
if (search) {
|
||||
baseInfo.potentialAction = {
|
||||
'@type': 'SearchAction',
|
||||
'query-input': 'required name=search_term_string',
|
||||
'target': `${fixedUrl}?q={search_term_string}`,
|
||||
};
|
||||
}
|
||||
|
||||
return baseInfo;
|
||||
}
|
||||
|
||||
genImageObject({ image, url }: { image: string; url: string }) {
|
||||
const fixedUrl = this.fixUrl(url);
|
||||
|
||||
return {
|
||||
'@id': this.getId(fixedUrl, '#primaryimage'),
|
||||
'@type': 'ImageObject',
|
||||
'contentUrl': image,
|
||||
'inLanguage': 'en-US',
|
||||
'url': image,
|
||||
};
|
||||
}
|
||||
|
||||
genWebSite() {
|
||||
const baseInfo: any = {
|
||||
'@id': this.getId(SITE_URL, '#website'),
|
||||
'@type': 'WebSite',
|
||||
'description': pkg.description,
|
||||
'inLanguage': 'en-US',
|
||||
'name': 'LobeTheme',
|
||||
'publisher': {
|
||||
'@id': this.getId(SITE_URL, '#organization'),
|
||||
},
|
||||
'url': SITE_URL,
|
||||
};
|
||||
|
||||
return baseInfo;
|
||||
}
|
||||
|
||||
private getId(url: string, id: string) {
|
||||
return [url, id].join('/');
|
||||
}
|
||||
|
||||
private fixTitle(title: string) {
|
||||
return title.includes('LobeTheme') ? title : `${title} · LobeTheme`;
|
||||
}
|
||||
|
||||
private fixUrl(url: string) {
|
||||
return urlJoin(SITE_URL, url);
|
||||
}
|
||||
}
|
||||
|
||||
export const ldModule = new Ld();
|
||||
@ -2,10 +2,14 @@ import pkg from '@/../package.json';
|
||||
|
||||
export const DISCORD_URL = 'https://discord.gg/AYFPHvv2jT';
|
||||
export const SPONSOR_URL = 'https://opencollective.com/lobehub';
|
||||
export const SPONSOR_IMG = 'https://readme-wizard.lobehub.com/api/sponsor';
|
||||
export const GISCUS_REPO_ID = 'R_kgDOJCPcNg';
|
||||
export const GITHUB_REPO_URL = pkg.homepage;
|
||||
export const REPO_NAME = GITHUB_REPO_URL.replace(
|
||||
'https://github.com/',
|
||||
'',
|
||||
) as `${string}/${string}`;
|
||||
export const OFFICIAL_SITE = 'https://lobehub.com/';
|
||||
export const SITE_URL = location.origin;
|
||||
export const EMAIL_SUPPORT = 'support@lobehub.com';
|
||||
export const EMAIL_BUSINESS = 'hello@lobehub.com';
|
||||
export const X = 'https://x.com/lobehub';
|
||||
|
||||
@ -36,11 +36,14 @@ export const useStyles = createStyles(
|
||||
overflow: unset;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(${size}px, 1fr));
|
||||
flex: none !important;
|
||||
gap: 8px;
|
||||
|
||||
height: unset;
|
||||
min-height: unset;
|
||||
|
||||
border: unset !important;
|
||||
|
||||
.name {
|
||||
background: unset !important;
|
||||
}
|
||||
@ -55,6 +58,19 @@ export const useStyles = createStyles(
|
||||
}
|
||||
}
|
||||
|
||||
.extra-network-dirs {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
margin-bottom: 12px;
|
||||
|
||||
> button.lg.secondary.gradio-button {
|
||||
padding: 4px 8px;
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.extra-networks {
|
||||
.pending {
|
||||
opacity: 1 !important;
|
||||
@ -78,6 +94,37 @@ export const useStyles = createStyles(
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.extra-networks-controls-div {
|
||||
height: unset !important;
|
||||
}
|
||||
|
||||
.extra-network-control {
|
||||
position: relative;
|
||||
flex: none;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
|
||||
.extra-network-control--search {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
small {
|
||||
display: none;
|
||||
}
|
||||
|
||||
> div:has(i) {
|
||||
position: relative;
|
||||
|
||||
display: flex;
|
||||
flex: none;
|
||||
|
||||
height: 32px;
|
||||
|
||||
background: ${token.colorFillTertiary};
|
||||
border-radius: ${token.borderRadius}px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.extra-network-subdirs {
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { Form, Swatches } from '@lobehub/ui';
|
||||
import { Input, Segmented, Select, Switch } from 'antd';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { Palette } from 'lucide-react';
|
||||
import { memo, useCallback, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
@ -153,7 +152,7 @@ const SettingForm = memo(() => {
|
||||
valuePropName: 'checked',
|
||||
},
|
||||
],
|
||||
icon: Palette,
|
||||
|
||||
title: t('setting.group.theme'),
|
||||
}),
|
||||
[
|
||||
@ -173,6 +172,7 @@ const SettingForm = memo(() => {
|
||||
onFinish={onFinish}
|
||||
onValuesChange={(_, v) => setRawSetting(v)}
|
||||
style={{ flex: 1 }}
|
||||
variant={'pure'}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { Form } from '@lobehub/ui';
|
||||
import { Switch } from 'antd';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { Puzzle, TextCursorInput } from 'lucide-react';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
@ -31,7 +30,6 @@ const SettingForm = memo(() => {
|
||||
valuePropName: 'checked',
|
||||
},
|
||||
],
|
||||
icon: Puzzle,
|
||||
title: t('setting.group.experimental'),
|
||||
}),
|
||||
[],
|
||||
@ -55,7 +53,6 @@ const SettingForm = memo(() => {
|
||||
valuePropName: 'checked',
|
||||
},
|
||||
],
|
||||
icon: TextCursorInput,
|
||||
title: t('setting.group.promptTextarea'),
|
||||
}),
|
||||
[],
|
||||
@ -68,6 +65,7 @@ const SettingForm = memo(() => {
|
||||
items={[experimental, promptTextarea]}
|
||||
onFinish={onFinish}
|
||||
style={{ flex: 1 }}
|
||||
variant={'pure'}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { Form } from '@lobehub/ui';
|
||||
import { Segmented, Switch } from 'antd';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { Layout, TextCursorInput } from 'lucide-react';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
@ -38,7 +37,7 @@ const SettingForm = memo(() => {
|
||||
valuePropName: 'checked',
|
||||
},
|
||||
],
|
||||
icon: Layout,
|
||||
|
||||
title: t('setting.group.layout'),
|
||||
}),
|
||||
[],
|
||||
@ -67,7 +66,7 @@ const SettingForm = memo(() => {
|
||||
name: 'promptTextareaType',
|
||||
},
|
||||
],
|
||||
icon: TextCursorInput,
|
||||
|
||||
title: t('setting.group.promptTextarea'),
|
||||
}),
|
||||
[],
|
||||
@ -80,6 +79,7 @@ const SettingForm = memo(() => {
|
||||
items={[layout, promptTextarea]}
|
||||
onFinish={onFinish}
|
||||
style={{ flex: 1 }}
|
||||
variant={'pure'}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { Form } from '@lobehub/ui';
|
||||
import { InputNumber, Segmented, Switch } from 'antd';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { PanelLeftClose, PanelRightClose } from 'lucide-react';
|
||||
import { memo, useCallback, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
@ -67,7 +66,7 @@ const SettingForm = memo(() => {
|
||||
name: 'sidebarWidth',
|
||||
},
|
||||
],
|
||||
icon: PanelLeftClose,
|
||||
|
||||
title: t('setting.group.quickSettingSidebar'),
|
||||
}),
|
||||
[rawSetting.enableSidebar],
|
||||
@ -126,7 +125,7 @@ const SettingForm = memo(() => {
|
||||
name: 'extraNetworkCardSize',
|
||||
},
|
||||
],
|
||||
icon: PanelRightClose,
|
||||
|
||||
title: t('setting.group.extraNetworkSidebar'),
|
||||
}),
|
||||
[rawSetting.enableExtraNetworkSidebar],
|
||||
@ -140,6 +139,7 @@ const SettingForm = memo(() => {
|
||||
onFinish={onFinish}
|
||||
onValuesChange={(_, v) => setRawSetting(v)}
|
||||
style={{ flex: 1 }}
|
||||
variant={'pure'}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
41
src/features/Setting/Sidebar/BrandWatermark.tsx
Normal file
41
src/features/Setting/Sidebar/BrandWatermark.tsx
Normal file
@ -0,0 +1,41 @@
|
||||
import { Logo } from '@lobehub/ui';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { memo } from 'react';
|
||||
import { Flexbox, FlexboxProps } from 'react-layout-kit';
|
||||
|
||||
const useStyles = createStyles(({ token, css }) => ({
|
||||
logoLink: css`
|
||||
height: 20px;
|
||||
color: inherit;
|
||||
|
||||
&:hover {
|
||||
color: ${token.colorLink};
|
||||
}
|
||||
`,
|
||||
}));
|
||||
|
||||
const BrandWatermark = memo<Omit<FlexboxProps, 'children'>>(({ style, ...rest }) => {
|
||||
const { styles, theme } = useStyles();
|
||||
return (
|
||||
<Flexbox
|
||||
align={'center'}
|
||||
flex={'none'}
|
||||
gap={4}
|
||||
horizontal
|
||||
style={{ color: theme.colorTextDescription, fontSize: 12, ...style }}
|
||||
{...rest}
|
||||
>
|
||||
<span>Powered by</span>
|
||||
<a
|
||||
className={styles.logoLink}
|
||||
href={'https://lobehub.com'}
|
||||
rel="noreferrer"
|
||||
target={'_blank'}
|
||||
>
|
||||
<Logo size={20} type={'text'} />
|
||||
</a>
|
||||
</Flexbox>
|
||||
);
|
||||
});
|
||||
|
||||
export default BrandWatermark;
|
||||
@ -1,47 +0,0 @@
|
||||
import { Icon, List } from '@lobehub/ui';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { type LucideIcon } from 'lucide-react';
|
||||
import { CSSProperties, ReactNode, memo } from 'react';
|
||||
|
||||
const { Item } = List;
|
||||
|
||||
const useStyles = createStyles(({ css, token }) => ({
|
||||
container: css`
|
||||
position: relative;
|
||||
padding: 16px;
|
||||
border-radius: ${token.borderRadius}px;
|
||||
|
||||
div {
|
||||
overflow: visible;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
`,
|
||||
}));
|
||||
|
||||
export interface ItemProps {
|
||||
active?: boolean;
|
||||
className?: string;
|
||||
icon: LucideIcon;
|
||||
label: ReactNode;
|
||||
onClick?: () => void;
|
||||
style?: CSSProperties;
|
||||
}
|
||||
|
||||
const SettingItem = memo<ItemProps>(
|
||||
({ label, icon, active = false, style, className, onClick }) => {
|
||||
const { cx, styles } = useStyles();
|
||||
return (
|
||||
<Item
|
||||
active={active}
|
||||
avatar={<Icon icon={icon} size={{ fontSize: 16 }} />}
|
||||
className={cx(styles.container, className)}
|
||||
onClick={onClick}
|
||||
style={style}
|
||||
title={label as string}
|
||||
/>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
export default SettingItem;
|
||||
97
src/features/Setting/Sidebar/Menu.tsx
Normal file
97
src/features/Setting/Sidebar/Menu.tsx
Normal file
@ -0,0 +1,97 @@
|
||||
import { Menu as AntdMenu, MenuProps as AntdMenuProps, ConfigProvider } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { memo } from 'react';
|
||||
|
||||
const useStyles = createStyles(({ css, token, prefixCls }) => ({
|
||||
compact: css`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.125rem;
|
||||
`,
|
||||
menu: css`
|
||||
flex: 1;
|
||||
background: transparent;
|
||||
border: none !important;
|
||||
|
||||
.${prefixCls}-menu-item-divider {
|
||||
margin-block: 0.125rem;
|
||||
border-color: ${token.colorFillTertiary};
|
||||
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.${prefixCls}-menu-item, .${prefixCls}-menu-submenu-title {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
align-items: center;
|
||||
|
||||
height: unset;
|
||||
min-height: 2rem;
|
||||
padding: 0.375rem 0.75rem;
|
||||
|
||||
line-height: 2;
|
||||
|
||||
.anticon + .${prefixCls}-menu-title-content {
|
||||
margin-inline-start: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.${prefixCls}-menu-item-selected {
|
||||
.${prefixCls}-menu-item-icon svg {
|
||||
color: ${token.colorText};
|
||||
}
|
||||
}
|
||||
|
||||
.${prefixCls}-menu-item-icon svg {
|
||||
color: ${token.colorTextSecondary};
|
||||
}
|
||||
|
||||
.${prefixCls}-menu-title-content {
|
||||
flex: 1;
|
||||
}
|
||||
`,
|
||||
}));
|
||||
|
||||
export interface MenuProps extends AntdMenuProps {
|
||||
variant?: 'default' | 'compact';
|
||||
}
|
||||
|
||||
const Menu = memo<MenuProps>(({ className, selectable = false, variant, ...rest }) => {
|
||||
const isCompact = variant === 'compact';
|
||||
const { cx, styles, theme } = useStyles();
|
||||
return (
|
||||
<ConfigProvider
|
||||
theme={{
|
||||
components: {
|
||||
Menu: {
|
||||
controlHeightLG: 36,
|
||||
iconMarginInlineEnd: 8,
|
||||
iconSize: 16,
|
||||
itemBorderRadius: theme.borderRadius,
|
||||
itemColor: selectable ? theme.colorTextSecondary : theme.colorText,
|
||||
itemHoverBg: theme.colorFillTertiary,
|
||||
itemMarginBlock: isCompact ? 0 : 4,
|
||||
itemMarginInline: isCompact ? 0 : 4,
|
||||
itemSelectedBg: theme.colorFillSecondary,
|
||||
paddingXS: -8,
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<AntdMenu
|
||||
className={cx(styles.menu, isCompact && styles.compact, className)}
|
||||
mode="vertical"
|
||||
selectable={selectable}
|
||||
{...rest}
|
||||
/>
|
||||
</ConfigProvider>
|
||||
);
|
||||
});
|
||||
|
||||
export default Menu;
|
||||
@ -15,7 +15,7 @@ const MobileSidebar = memo<SidebarProps>(({ tab, setTab }) => {
|
||||
<Segmented
|
||||
block
|
||||
onChange={setTab as any}
|
||||
options={items.map(({ value, label }) => ({ label, value }))}
|
||||
options={items.map(({ key, label }) => ({ label, value: key }))}
|
||||
value={tab}
|
||||
/>
|
||||
);
|
||||
|
||||
56
src/features/Setting/Sidebar/SidebarLayout.tsx
Normal file
56
src/features/Setting/Sidebar/SidebarLayout.tsx
Normal file
@ -0,0 +1,56 @@
|
||||
import { createStyles } from 'antd-style';
|
||||
import { ReactNode } from 'react';
|
||||
import { Flexbox, FlexboxProps } from 'react-layout-kit';
|
||||
|
||||
import BrandWatermark from './BrandWatermark';
|
||||
|
||||
const useStyles = createStyles(({ token, css }) => ({
|
||||
container: css`
|
||||
padding: 24px 12px 16px;
|
||||
background: ${token.colorBgContainer};
|
||||
border-inline-end: 1px solid ${token.colorBorder};
|
||||
`,
|
||||
desc: css`
|
||||
line-height: 1.4;
|
||||
color: ${token.colorTextDescription};
|
||||
`,
|
||||
header: css`
|
||||
padding: 0 0.75rem;
|
||||
`,
|
||||
logo: css`
|
||||
fill: ${token.colorText};
|
||||
`,
|
||||
title: css`
|
||||
margin: 0;
|
||||
font-size: 26px;
|
||||
font-weight: 600;
|
||||
line-height: 1.3;
|
||||
`,
|
||||
}));
|
||||
|
||||
interface SidebarLayoutProps extends FlexboxProps {
|
||||
desc?: ReactNode;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
const SidebarLayout = ({ children, className, title, desc, ...rest }: SidebarLayoutProps) => {
|
||||
const { cx, styles } = useStyles();
|
||||
return (
|
||||
<Flexbox
|
||||
className={cx(styles.container, className)}
|
||||
flex={'none'}
|
||||
gap={20}
|
||||
width={280}
|
||||
{...rest}
|
||||
>
|
||||
<Flexbox className={styles.header} gap={4}>
|
||||
<h1 className={styles.title}>{title}</h1>
|
||||
{desc && <p className={styles.desc}>{desc}</p>}
|
||||
</Flexbox>
|
||||
{children}
|
||||
<BrandWatermark paddingInline={12} />
|
||||
</Flexbox>
|
||||
);
|
||||
};
|
||||
|
||||
export default SidebarLayout;
|
||||
@ -1,9 +1,11 @@
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Flexbox } from 'react-layout-kit';
|
||||
|
||||
import { useTabItems } from '@/features/Setting/Sidebar/useTabItems';
|
||||
|
||||
import Item from './Item';
|
||||
import VersionTag from '../../../components/VersionTag';
|
||||
import Menu from './Menu';
|
||||
import SidebarLayout from './SidebarLayout';
|
||||
import { useTabItems } from './useTabItems';
|
||||
|
||||
export enum SettingsTabs {
|
||||
Appearance = 'appearance',
|
||||
@ -19,19 +21,24 @@ interface SidebarProps {
|
||||
|
||||
const Sidebar = memo<SidebarProps>(({ tab, setTab }) => {
|
||||
const items = useTabItems();
|
||||
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Flexbox gap={4}>
|
||||
{items.map(({ value, icon, label }) => (
|
||||
<Item
|
||||
active={tab === value}
|
||||
icon={icon}
|
||||
key={value}
|
||||
label={label}
|
||||
onClick={() => setTab(value)}
|
||||
/>
|
||||
))}
|
||||
</Flexbox>
|
||||
<SidebarLayout
|
||||
desc={
|
||||
<Flexbox align={'center'} gap={4} horizontal>
|
||||
{t('modal.themeSetting.desc')}
|
||||
<VersionTag />
|
||||
</Flexbox>
|
||||
}
|
||||
title={t('modal.themeSetting.title')}
|
||||
>
|
||||
<Menu
|
||||
items={items}
|
||||
onClick={({ key }) => setTab(key as SettingsTabs)}
|
||||
selectable
|
||||
selectedKeys={[tab as any]}
|
||||
/>
|
||||
</SidebarLayout>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@ -1,15 +0,0 @@
|
||||
import { Brush, FlaskConical, Layout, PanelRight } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { SettingsTabs } from '@/features/Setting/Sidebar/index';
|
||||
|
||||
export const useTabItems = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return [
|
||||
{ icon: Brush, label: t('setting.tab.appearance'), value: SettingsTabs.Appearance },
|
||||
{ icon: Layout, label: t('setting.tab.layout'), value: SettingsTabs.Layout },
|
||||
{ icon: PanelRight, label: t('setting.tab.sidebar'), value: SettingsTabs.Sidebar },
|
||||
{ icon: FlaskConical, label: t('setting.tab.experimental'), value: SettingsTabs.Experimental },
|
||||
];
|
||||
};
|
||||
28
src/features/Setting/Sidebar/useTabItems.tsx
Normal file
28
src/features/Setting/Sidebar/useTabItems.tsx
Normal file
@ -0,0 +1,28 @@
|
||||
import { Icon } from '@lobehub/ui';
|
||||
import { Brush, FlaskConical, Layout, PanelRight } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { SettingsTabs } from '@/features/Setting/Sidebar/index';
|
||||
|
||||
export const useTabItems = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return [
|
||||
{
|
||||
icon: <Icon icon={Brush} />,
|
||||
key: SettingsTabs.Appearance,
|
||||
label: t('setting.tab.appearance'),
|
||||
},
|
||||
{ icon: <Icon icon={Layout} />, key: SettingsTabs.Layout, label: t('setting.tab.layout') },
|
||||
{
|
||||
icon: <Icon icon={PanelRight} />,
|
||||
key: SettingsTabs.Sidebar,
|
||||
label: t('setting.tab.sidebar'),
|
||||
},
|
||||
{
|
||||
icon: <Icon icon={FlaskConical} />,
|
||||
key: SettingsTabs.Experimental,
|
||||
label: t('setting.tab.experimental'),
|
||||
},
|
||||
];
|
||||
};
|
||||
@ -1,13 +1,8 @@
|
||||
import { ActionIcon, Modal, type ModalProps } from '@lobehub/ui';
|
||||
import { useResponsive } from 'antd-style';
|
||||
import { Book } from 'lucide-react';
|
||||
import { Modal, type ModalProps } from '@lobehub/ui';
|
||||
import { useResponsive, useTheme } from 'antd-style';
|
||||
import { memo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Flexbox } from 'react-layout-kit';
|
||||
|
||||
import VersionTag from '@/components/VersionTag';
|
||||
import { GITHUB_REPO_URL } from '@/const/url';
|
||||
|
||||
import FormAppearance from './Form/Appearance';
|
||||
import FormExperimental from './Form/Experimental';
|
||||
import Footer from './Form/Footer';
|
||||
@ -23,7 +18,7 @@ export interface SettingProps {
|
||||
const Setting = memo<SettingProps>(({ open, onCancel }) => {
|
||||
const [tab, setTab] = useState<SettingsTabs>(SettingsTabs.Appearance);
|
||||
const { mobile } = useResponsive();
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
|
||||
const content = (
|
||||
<>
|
||||
@ -36,37 +31,65 @@ const Setting = memo<SettingProps>(({ open, onCancel }) => {
|
||||
|
||||
return (
|
||||
<Modal
|
||||
footer={<Footer />}
|
||||
allowFullscreen={true}
|
||||
footer={mobile ? <Footer /> : null}
|
||||
onCancel={onCancel}
|
||||
open={open}
|
||||
styles={{
|
||||
body: mobile ? { padding: 0 } : {},
|
||||
body: {
|
||||
display: 'flex',
|
||||
minHeight: 'min(75vh, 750px)',
|
||||
overflow: 'hidden',
|
||||
padding: 0,
|
||||
paddingBlock: 0,
|
||||
},
|
||||
content: {
|
||||
background: mobile ? theme.colorBgContainer : undefined,
|
||||
border: 'none',
|
||||
boxShadow: `0 0 0 1px ${theme.colorBorderSecondary}`,
|
||||
},
|
||||
}}
|
||||
title={
|
||||
<Flexbox align={'center'} gap={4}>
|
||||
<Flexbox align={'center'} gap={4} horizontal>
|
||||
<a href={GITHUB_REPO_URL} rel="noreferrer" target="_blank">
|
||||
<ActionIcon icon={Book} title="Setting Documents" />
|
||||
</a>
|
||||
|
||||
{t('modal.themeSetting.title')}
|
||||
<VersionTag />
|
||||
</Flexbox>
|
||||
</Flexbox>
|
||||
}
|
||||
width={960}
|
||||
title={false}
|
||||
width={1024}
|
||||
>
|
||||
{mobile ? (
|
||||
<Flexbox>
|
||||
<Flexbox
|
||||
height={'100%'}
|
||||
style={{ overflow: 'hidden', position: 'relative' }}
|
||||
width={'100%'}
|
||||
>
|
||||
<div style={{ padding: 16 }}>
|
||||
<MobileSidebar setTab={setTab} tab={tab} />
|
||||
</div>
|
||||
{content}
|
||||
<Flexbox
|
||||
height={'100%'}
|
||||
style={{ overflowX: 'hidden', overflowY: 'auto', position: 'relative' }}
|
||||
width={'100%'}
|
||||
>
|
||||
{content}
|
||||
</Flexbox>
|
||||
</Flexbox>
|
||||
) : (
|
||||
<Flexbox gap={16} horizontal>
|
||||
<Flexbox horizontal width={'100%'}>
|
||||
<Sidebar setTab={setTab} tab={tab} />
|
||||
{content}
|
||||
<Flexbox
|
||||
align={'center'}
|
||||
gap={64}
|
||||
style={{
|
||||
background: theme.isDarkMode ? theme.colorFillQuaternary : theme.colorBgElevated,
|
||||
minHeight: '100%',
|
||||
overflowX: 'hidden',
|
||||
overflowY: 'auto',
|
||||
paddingBlock: 40,
|
||||
paddingInline: 56,
|
||||
}}
|
||||
width={'100%'}
|
||||
>
|
||||
{content}
|
||||
<Flexbox width={'100%'}>
|
||||
<Footer />
|
||||
</Flexbox>
|
||||
</Flexbox>
|
||||
</Flexbox>
|
||||
)}
|
||||
</Modal>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { Converter } from '@/scripts/formatPrompt';
|
||||
import { parseFromRawInfo } from '@bluelovers/auto1111-pnginfo';
|
||||
|
||||
import { Converter } from '@/scripts/formatPrompt';
|
||||
|
||||
const formatPrompt = (prompt: string) => {
|
||||
let newPrompt = prompt.replaceAll('<', '<').replaceAll('>', '>');
|
||||
return Converter.convert(newPrompt);
|
||||
@ -13,13 +14,13 @@ export const formatInfo = (info: string) => {
|
||||
let {
|
||||
prompt: position,
|
||||
negative_prompt: negative,
|
||||
...config,
|
||||
...config
|
||||
} = parseFromRawInfo(info, {
|
||||
isIncludePrompts: true,
|
||||
})
|
||||
});
|
||||
|
||||
position = position.trim().replaceAll('<br>', '\n').replace(/[\s\r\n]+$/g, '');
|
||||
negative = negative.trim().replaceAll('<br>', '\n').replace(/[\s\r\n]+$/g, '');
|
||||
position = position.trim().replaceAll('<br>', '\n').replaceAll(/\s+$/g, '');
|
||||
negative = negative.trim().replaceAll('<br>', '\n').replaceAll(/\s+$/g, '');
|
||||
|
||||
position = position ? formatPrompt(position) : '';
|
||||
negative = negative ? formatPrompt(negative) : '';
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user