💄 style: Add locales and improve devops
parent
361ae84698
commit
a03f1b3bfb
21
.eslintrc.js
21
.eslintrc.js
|
|
@ -1,19 +1,8 @@
|
|||
const config = require('@lobehub/lint').eslint;
|
||||
|
||||
config.rules['indent'] = ['off', 2];
|
||||
config.rules['linebreak-style'] = 0;
|
||||
config.rules['no-undef'] = 0;
|
||||
config.rules['object-curly-spacing'] = 0;
|
||||
config.rules['unicorn/prefer-add-event-listener'] = 0;
|
||||
|
||||
module.exports = {
|
||||
...config,
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.ts', '*.tsx'],
|
||||
rules: {
|
||||
'linebreak-style': 0,
|
||||
'no-undef': 0,
|
||||
'object-curly-spacing': 0,
|
||||
'unicorn/prefer-add-event-listener': 0,
|
||||
'unused-imports/no-unused-imports': 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
module.exports = config;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ module.exports = {
|
|||
entryLocale: 'en_US',
|
||||
output: 'locales',
|
||||
outputLocales: outputLocales,
|
||||
temperature: 0,
|
||||
modelName: 'gpt-3.5-turbo-1106',
|
||||
experimental: {
|
||||
jsonMode: true,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,159 @@
|
|||
{
|
||||
"brand": {
|
||||
"kitchen": "Kitchen",
|
||||
"lobe": "LobeHub",
|
||||
"custom": "Benutzerdefiniert"
|
||||
},
|
||||
"custom": {
|
||||
"initializing": "StableDiffusion / LobeTheme initialisiert, bitte warten..."
|
||||
},
|
||||
"footer": {
|
||||
"resources": "Ressourcen",
|
||||
"community": "Gemeinschaft",
|
||||
"help": "Hilfe",
|
||||
"moreProducts": "Weitere Produkte"
|
||||
},
|
||||
"header": {
|
||||
"feedback": "Feedback",
|
||||
"switchTheme": "Wechseln Sie das Licht-/Dunkel-Thema",
|
||||
"setting": "Einstellung"
|
||||
},
|
||||
"modal": {
|
||||
"themeFeedback": {
|
||||
"title": "Themenfeedback"
|
||||
},
|
||||
"themeSetting": {
|
||||
"title": "Themen-Einstellungen"
|
||||
}
|
||||
},
|
||||
"prompt": {
|
||||
"load": "Lade-Prompt",
|
||||
"set": "Setze-Prompt",
|
||||
"negative": "Negativ",
|
||||
"positive": "Positiv"
|
||||
},
|
||||
"setting": {
|
||||
"button": {
|
||||
"reset": "Zurücksetzen",
|
||||
"submit": "Anwenden und Schnittstelle neu starten"
|
||||
},
|
||||
"confirmPageUnload": {
|
||||
"title": "Bestätigung beim Verlassen der Seite",
|
||||
"desc": "Hilft, den Verlust von ungespeicherten Daten zu verhindern"
|
||||
},
|
||||
"customFont": {
|
||||
"title": "Benutzerdefinierte Schriftart",
|
||||
"desc": "Wenn aktiviert, wird automatisch eine Webfont geladen, um die Anzeige von Text in Chinesisch, Englisch und Code zu verbessern"
|
||||
},
|
||||
"customLogo": {
|
||||
"title": "Benutzerdefiniertes Logo",
|
||||
"desc": "Unterstützt URL / Base64 / Emoji-Symbole"
|
||||
},
|
||||
"customTitle": {
|
||||
"title": "Benutzerdefinierter Titel",
|
||||
"desc": "Benutzerdefinierter Logotitel"
|
||||
},
|
||||
"extraNetworkSidebar": {
|
||||
"defaultCardSize": {
|
||||
"title": "Modell-Cover-Größe",
|
||||
"desc": "Standardwert der Modell-Cover-Größe beim Starten"
|
||||
},
|
||||
"defaultExpand": {
|
||||
"title": "Standardmäßig erweitern",
|
||||
"desc": "Ob die Seitenleiste beim Starten standardmäßig erweitert werden soll"
|
||||
},
|
||||
"defaultWidth": {
|
||||
"title": "Standardbreite",
|
||||
"desc": "Standardbreite der Seitenleiste beim Starten"
|
||||
},
|
||||
"displayMode": {
|
||||
"title": "Anzeigemodus",
|
||||
"desc": "Festgelegt als Rastermodus für konstante Anzeige, automatische Erweiterung, wenn die Maus an den Rand im schwebenden Modus bewegt wird"
|
||||
},
|
||||
"enable": {
|
||||
"title": "Aktivieren",
|
||||
"desc": "Aktivieren Sie die zusätzliche Netzwerkseitenleiste auf der rechten Seite"
|
||||
}
|
||||
},
|
||||
"group": {
|
||||
"extraNetworkSidebar": "Zusätzliche Netzwerkseitenleiste",
|
||||
"layout": "Layout-Einstellungen",
|
||||
"promptTextarea": "Prompt-Textfeld",
|
||||
"quickSettingSidebar": "Schnelle Einstellungsseitenleiste",
|
||||
"theme": "Themen-Einstellungen"
|
||||
},
|
||||
"hideFooter": {
|
||||
"title": "Fußzeile ausblenden",
|
||||
"desc": "Blenden Sie die Themenfußzeile aus und zeigen Sie nur die Standardfußzeile von Stable Diffusion WebUI an"
|
||||
},
|
||||
"language": {
|
||||
"title": "Sprache",
|
||||
"desc": "Lappen-Themensprache"
|
||||
},
|
||||
"logoType": {
|
||||
"title": "Logo-Typ",
|
||||
"desc": "Logo-Typ",
|
||||
"preview": "Vorschau"
|
||||
},
|
||||
"neutralColor": {
|
||||
"title": "Neutrale Farbe",
|
||||
"desc": "Passen Sie verschiedene Grautöne mit unterschiedlichen Farbtendenzen an, der zweite ist die originale neutrale Farbe der Küche"
|
||||
},
|
||||
"primaryColor": {
|
||||
"title": "Primärfarbe",
|
||||
"desc": "Benutzerdefinierte Primärfarbe, die zweite ist die originale Farbe des Küchenthemas"
|
||||
},
|
||||
"promptDisplayMode": {
|
||||
"title": "Prompt-Anzeigemodus",
|
||||
"desc": "Feste Höhe oder automatische Höhe mit ziehbarer Größenänderungsunterstützung",
|
||||
"resizable": "Größenänderbar",
|
||||
"scroll": "Scrollen"
|
||||
},
|
||||
"promptEditor": {
|
||||
"title": "Prompt-Editor",
|
||||
"desc": "Bietet einen einfachen Prompt-Editor oben in der Schnelleinstellungsseitenleiste"
|
||||
},
|
||||
"promptHighlight": {
|
||||
"title": "Prompt-Syntaxhervorhebung",
|
||||
"desc": "Färbt die Prompt-Anzeige automatisch gemäß den Stable Diffusion-Syntaxregeln ein"
|
||||
},
|
||||
"quickSettingSidebar": {
|
||||
"defaultExpand": {
|
||||
"title": "Standardmäßig erweitern",
|
||||
"desc": "Ob die Seitenleiste beim Starten standardmäßig erweitert werden soll"
|
||||
},
|
||||
"defaultWidth": {
|
||||
"title": "Standardbreite",
|
||||
"desc": "Standardbreite der Seitenleiste beim Starten"
|
||||
},
|
||||
"displayMode": {
|
||||
"title": "Anzeigemodus",
|
||||
"desc": "Festgelegt als Rastermodus für konstante Anzeige, automatische Erweiterung, wenn die Maus an den Rand im schwebenden Modus bewegt wird"
|
||||
},
|
||||
"enable": {
|
||||
"title": "Aktivieren",
|
||||
"desc": "Aktivieren Sie die Schnelleinstellungsseitenleiste auf der linken Seite"
|
||||
}
|
||||
},
|
||||
"reduceAnimation": {
|
||||
"title": "Animation reduzieren",
|
||||
"desc": "Reduzieren Sie den Unschärfeeffekt und die Hintergrundflussfarbe, um die Geschmeidigkeit zu verbessern und die CPU-Auslastung zu reduzieren"
|
||||
},
|
||||
"splitPreviewer": {
|
||||
"title": "Split-Previewer",
|
||||
"desc": "Platzieren Sie das Prompt-Eingabefeld links und die Generierungsschaltfläche rechts, um sicherzustellen, dass das generierte Bild beim Scrollen immer oben angezeigt wird (experimentell)"
|
||||
},
|
||||
"svgIcons": {
|
||||
"title": "SVG-Symbole",
|
||||
"desc": "Ersetzen Sie alle Emoji-Symbole in Stable Diffusion WebUI global durch SVG-Symbole"
|
||||
}
|
||||
},
|
||||
"sidebar": {
|
||||
"extraNetwork": "Zusätzliches Netzwerk",
|
||||
"quickSetting": "Schnelleinstellung",
|
||||
"mode": {
|
||||
"fixed": "Fixiert",
|
||||
"float": "Schwebend"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,82 +1,159 @@
|
|||
{
|
||||
"appInitializing": "StableDiffusion / LobeTheme is initializing, please wait...",
|
||||
"community": "Community",
|
||||
"custom": "Custom",
|
||||
"extraNetwork": "Extra Network",
|
||||
"feedback": "Feedback",
|
||||
"fixed": "Fixed",
|
||||
"float": "Float",
|
||||
"help": "Help",
|
||||
"kitchen": "Kitchen",
|
||||
"loadPrompt": "Load Prompt",
|
||||
"lobe": "Lobe",
|
||||
"moreProducts": "More Products",
|
||||
"negative": "Negative",
|
||||
"positive": "Positive",
|
||||
"quickSetting": "Quick Setting",
|
||||
"resizable": "Resizable",
|
||||
"resources": "Resources",
|
||||
"scroll": "Scroll",
|
||||
"setPrompt": "Set Prompt",
|
||||
"setting": "Setting",
|
||||
"settingButtonReset": "Reset",
|
||||
"settingButtonSubmit": "Apply and Restart Interface",
|
||||
"settingConfirmPageUnload": "Confirmation on page leaving",
|
||||
"settingConfirmPageUnloadDesc": "Helps prevent loss of unsaved data",
|
||||
"settingCustomFont": "Load Custom Font",
|
||||
"settingCustomFontDesc": "When enabled, it will automatically load a webfont to enhance the display of text in Chinese, English, and code",
|
||||
"settingCustomLogo": "Custom Logo",
|
||||
"settingCustomLogoDesc": "Support URL / Base64 / Emoji symbols",
|
||||
"settingCustomTitle": "Custom Title",
|
||||
"settingCustomTitleDesc": "Custom Logo Title",
|
||||
"settingExtraNetworkSidebarDefaultCardSize": "Model Cover Size",
|
||||
"settingExtraNetworkSidebarDefaultCardSizeDesc": "Default value of model cover size when starting",
|
||||
"settingExtraNetworkSidebarDefaultExpand": "Default Expand",
|
||||
"settingExtraNetworkSidebarDefaultExpandDesc": "Whether to expand the sidebar by default when starting",
|
||||
"settingExtraNetworkSidebarDefaultWidth": "Default Width",
|
||||
"settingExtraNetworkSidebarDefaultWidthDesc": "Default width of the sidebar when starting",
|
||||
"settingExtraNetworkSidebarDisplayMode": "Display Mode",
|
||||
"settingExtraNetworkSidebarDisplayModeDesc": "Fixed as grid mode for constant display, auto-expand when the mouse moves to the side in floating mode",
|
||||
"settingExtraNetworkSidebarEnable": "Enable",
|
||||
"settingExtraNetworkSidebarEnableDesc": "Enable the extra network sidebar on the right side",
|
||||
"settingGroupExtraNetworkSidebar": "Extra Network Sidebar",
|
||||
"settingGroupLayout": "Layout Settings",
|
||||
"settingGroupPromptTextarea": "Prompt Textbox",
|
||||
"settingGroupQuickSettingSidebar": "Quick Setting Sidebar",
|
||||
"settingGroupTheme": "Theme Settings",
|
||||
"settingHideFooter": "Hide Footer",
|
||||
"settingHideFooterDesc": "Hide the theme footer and only display the default footer of stable diffusion webui",
|
||||
"settingLanguage": "Language",
|
||||
"settingLanguageDesc": "Lobe Theme language",
|
||||
"settingLogoPreview": "Preview",
|
||||
"settingLogoType": "Logo Type",
|
||||
"settingLogoTypeDesc": "Logo Type",
|
||||
"settingNeutralColor": "Neutral Color",
|
||||
"settingNeutralColorDesc": "Customize different shades of gray with different color tendencies, the second one is the original Kitchen neutral color",
|
||||
"settingPrimaryColor": "Primary Color",
|
||||
"settingPrimaryColorDesc": "Custom primary color, the second one is the original Kitchen theme color",
|
||||
"settingPromptDisplayMode": "Display Mode",
|
||||
"settingPromptDisplayModeDesc": "Fixed height or auto height with draggable resize support",
|
||||
"settingPromptEditor": "Prompt Editor",
|
||||
"settingPromptEditorDesc": "Provide a simple prompt editor at the top of the quick setting sidebar",
|
||||
"settingPromptHighlight": "Prompt Syntax Highlighting",
|
||||
"settingPromptHighlightDesc": "Automatically colorize prompt display according to the Stable Diffusion syntax rules",
|
||||
"settingQuickSettingSidebarDefaultExpand": "Default Expand",
|
||||
"settingQuickSettingSidebarDefaultExpandDesc": "Whether to expand the sidebar by default when starting",
|
||||
"settingQuickSettingSidebarDefaultWidth": "Default Width",
|
||||
"settingQuickSettingSidebarDefaultWidthDesc": "Default width of the sidebar when starting",
|
||||
"settingQuickSettingSidebarDisplayMode": "Display Mode",
|
||||
"settingQuickSettingSidebarDisplayModeDesc": "Fixed as grid mode for constant display, auto-expand when the mouse moves to the side in floating mode",
|
||||
"settingQuickSettingSidebarEnable": "Enable",
|
||||
"settingQuickSettingSidebarEnableDesc": "Enable the quick setting sidebar on the left side",
|
||||
"settingReduceAnimation": "Reduce Animation",
|
||||
"settingReduceAnimationDesc": "Reduce the blur effect and background flow color, which can improve smoothness and save CPU usage",
|
||||
"settingSplitPreviewer": "Split Previewer",
|
||||
"settingSplitPreviewerDesc": "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)",
|
||||
"settingSvgIcons": "Use SVG Icons",
|
||||
"settingSvgIconsDesc": "Replace all Emoji icons in stable diffusion webui with SVG icons globally",
|
||||
"switchTheme": "Switch Light/Dark Theme",
|
||||
"sync": "Sync with webui setting",
|
||||
"themeFeedback": "Theme Feedback",
|
||||
"themeSetting": "Theme Settings"
|
||||
"brand": {
|
||||
"kitchen": "Kitchen",
|
||||
"lobe": "LobeHub",
|
||||
"custom": "Custom"
|
||||
},
|
||||
"custom": {
|
||||
"initializing": "StableDiffusion / LobeTheme is initializing, please wait..."
|
||||
},
|
||||
"footer": {
|
||||
"resources": "Resources",
|
||||
"community": "Community",
|
||||
"help": "Help",
|
||||
"moreProducts": "More Products"
|
||||
},
|
||||
"header": {
|
||||
"feedback": "Feedback",
|
||||
"switchTheme": "Switch Light/Dark Theme",
|
||||
"setting": "Setting"
|
||||
},
|
||||
"modal": {
|
||||
"themeFeedback": {
|
||||
"title": "Theme Feedback"
|
||||
},
|
||||
"themeSetting": {
|
||||
"title": "Theme Settings"
|
||||
}
|
||||
},
|
||||
"prompt": {
|
||||
"load": "Load Prompt",
|
||||
"set": "Set Prompt",
|
||||
"negative": "Negative",
|
||||
"positive": "Positive"
|
||||
},
|
||||
"setting": {
|
||||
"button": {
|
||||
"reset": "Reset",
|
||||
"submit": "Apply and Restart Interface"
|
||||
},
|
||||
"confirmPageUnload": {
|
||||
"title": "Confirmation on page leaving",
|
||||
"desc": "Helps prevent loss of unsaved data"
|
||||
},
|
||||
"customFont": {
|
||||
"title": "Custom Font",
|
||||
"desc": "When enabled, it will automatically load a webfont to enhance the display of text in Chinese, English, and code"
|
||||
},
|
||||
"customLogo": {
|
||||
"title": "Custom Logo",
|
||||
"desc": "Support URL / Base64 / Emoji symbols"
|
||||
},
|
||||
"customTitle": {
|
||||
"title": "Custom Title",
|
||||
"desc": "Custom Logo Title"
|
||||
},
|
||||
"extraNetworkSidebar": {
|
||||
"defaultCardSize": {
|
||||
"title": "Model Cover Size",
|
||||
"desc": "Default value of model cover size when starting"
|
||||
},
|
||||
"defaultExpand": {
|
||||
"title": "Default Expand",
|
||||
"desc": "Whether to expand the sidebar by default when starting"
|
||||
},
|
||||
"defaultWidth": {
|
||||
"title": "Default Width",
|
||||
"desc": "Default width of the sidebar when starting"
|
||||
},
|
||||
"displayMode": {
|
||||
"title": "Display Mode",
|
||||
"desc": "Fixed as grid mode for constant display, auto-expand when the mouse moves to the side in floating mode"
|
||||
},
|
||||
"enable": {
|
||||
"title": "Enable",
|
||||
"desc": "Enable the extra network sidebar on the right side"
|
||||
}
|
||||
},
|
||||
"group": {
|
||||
"extraNetworkSidebar": "Extra Network Sidebar",
|
||||
"layout": "Layout Settings",
|
||||
"promptTextarea": "Prompt Textbox",
|
||||
"quickSettingSidebar": "Quick Setting Sidebar",
|
||||
"theme": "Theme Settings"
|
||||
},
|
||||
"hideFooter": {
|
||||
"title": "Hide Footer",
|
||||
"desc": "Hide the theme footer and only display the default footer of stable diffusion webui"
|
||||
},
|
||||
"language": {
|
||||
"title": "Language",
|
||||
"desc": "Lobe Theme language"
|
||||
},
|
||||
"logoType": {
|
||||
"title": "Logo Type",
|
||||
"desc": "Logo Type",
|
||||
"preview": "Preview"
|
||||
},
|
||||
"neutralColor": {
|
||||
"title": "Neutral Color",
|
||||
"desc": "Customize different shades of gray with different color tendencies, the second one is the original Kitchen neutral color"
|
||||
},
|
||||
"primaryColor": {
|
||||
"title": "Primary Color",
|
||||
"desc": "Custom primary color, the second one is the original Kitchen theme color"
|
||||
},
|
||||
"promptDisplayMode": {
|
||||
"title": "Prompt Display Mode",
|
||||
"desc": "Fixed height or auto height with draggable resize support",
|
||||
"resizable": "Resizable",
|
||||
"scroll": "Scroll"
|
||||
},
|
||||
"promptEditor": {
|
||||
"title": "Prompt Editor",
|
||||
"desc": "Provide a simple prompt editor at the top of the quick setting sidebar"
|
||||
},
|
||||
"promptHighlight": {
|
||||
"title": "Prompt Syntax Highlighting",
|
||||
"desc": "Automatically colorize prompt display according to the Stable Diffusion syntax rules"
|
||||
},
|
||||
"quickSettingSidebar": {
|
||||
"defaultExpand": {
|
||||
"title": "Default Expand",
|
||||
"desc": "Whether to expand the sidebar by default when starting"
|
||||
},
|
||||
"defaultWidth": {
|
||||
"title": "Default Width",
|
||||
"desc": "Default width of the sidebar when starting"
|
||||
},
|
||||
"displayMode": {
|
||||
"title": "Display Mode",
|
||||
"desc": "Fixed as grid mode for constant display, auto-expand when the mouse moves to the side in floating mode"
|
||||
},
|
||||
"enable": {
|
||||
"title": "Enable",
|
||||
"desc": "Enable the quick setting sidebar on the left side"
|
||||
}
|
||||
},
|
||||
"reduceAnimation": {
|
||||
"title": "Reduce Animation",
|
||||
"desc": "Reduce the blur effect and background flow color, which can improve smoothness and save CPU usage"
|
||||
},
|
||||
"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)"
|
||||
},
|
||||
"svgIcons": {
|
||||
"title": "SVG Icons",
|
||||
"desc": "Replace all Emoji icons in stable diffusion webui with SVG icons globally"
|
||||
}
|
||||
},
|
||||
"sidebar": {
|
||||
"extraNetwork": "Extra Network",
|
||||
"quickSetting": "Quick Setting",
|
||||
"mode": {
|
||||
"fixed": "Fixed",
|
||||
"float": "Float"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,82 +1,159 @@
|
|||
{
|
||||
"appInitializing": "StableDiffusion/LobeTheme se está inicializando, espere...",
|
||||
"community": "Comunidad",
|
||||
"custom": "Personalizado",
|
||||
"extraNetwork": "Red Adicional",
|
||||
"feedback": "Comentarios",
|
||||
"fixed": "Fijado",
|
||||
"float": "Flotante",
|
||||
"help": "Ayuda",
|
||||
"kitchen": "Kitchen",
|
||||
"loadPrompt": "Cargar Prompt",
|
||||
"lobe": "Lobe",
|
||||
"moreProducts": "Más Productos",
|
||||
"negative": "Negativo",
|
||||
"positive": "Positivo",
|
||||
"quickSetting": "Configuración Rápida",
|
||||
"resizable": "Redimensionable",
|
||||
"resources": "Recursos",
|
||||
"scroll": "Desplazarse",
|
||||
"setPrompt": "Establecer Prompt",
|
||||
"setting": "Configuración",
|
||||
"settingButtonReset": "Reiniciar",
|
||||
"settingButtonSubmit": "Aplicar y reiniciar la interfaz",
|
||||
"settingConfirmPageUnload": "Confirma el cierre de la página",
|
||||
"settingConfirmPageUnloadDesc": "Ayuda a evitar la pérdida de datos no guardados",
|
||||
"settingCustomFont": "Cargar fuente personalizada",
|
||||
"settingCustomFontDesc": "Cuando esté habilitado, cargará automáticamente una fuente web para mejorar la visualización del texto en chino, inglés y código",
|
||||
"settingCustomLogo": "Logo personalizado",
|
||||
"settingCustomLogoDesc": "URL de soporte/Base64/símbolos Emoji",
|
||||
"settingCustomTitle": "Título personalizado",
|
||||
"settingCustomTitleDesc": "Título del logo personalizado",
|
||||
"settingExtraNetworkSidebarDefaultCardSize": "Tamaño de portada del modelo",
|
||||
"settingExtraNetworkSidebarDefaultCardSizeDesc": "Valor predeterminado del tamaño de la cubierta del modelo al iniciar",
|
||||
"settingExtraNetworkSidebarDefaultExpand": "Expansión predeterminada",
|
||||
"settingExtraNetworkSidebarDefaultExpandDesc": "Si se expande la barra lateral de forma predeterminada al iniciar",
|
||||
"settingExtraNetworkSidebarDefaultWidth": "Ancho predeterminado",
|
||||
"settingExtraNetworkSidebarDefaultWidthDesc": "Ancho predeterminado de la barra lateral al iniciar",
|
||||
"settingExtraNetworkSidebarDisplayMode": "Modo de visualización",
|
||||
"settingExtraNetworkSidebarDisplayModeDesc": "Fijo a modo de cuadrícula para visualización constante, se expande automáticamente cuando el mouse se mueve hacia un lado en modo flotante",
|
||||
"settingExtraNetworkSidebarEnable": "Habilitar",
|
||||
"settingExtraNetworkSidebarEnableDesc": "Habilita la barra lateral de red adicional en el lado derecho",
|
||||
"settingGroupExtraNetworkSidebar": "Barra lateral de red adicional",
|
||||
"settingGroupLayout": "Ajustes de diseño",
|
||||
"settingGroupPromptTextarea": "Cuadro de texto del Prompt",
|
||||
"settingGroupQuickSettingSidebar": "Barra lateral de configuración rápida",
|
||||
"settingGroupTheme": "Configuración del tema",
|
||||
"settingHideFooter": "Ocultar pie de página",
|
||||
"settingHideFooterDesc": "Ocultar el pie de página del tema y mostrar solo el pie de página predeterminado de la webui de stable diffusion webui",
|
||||
"settingLanguage": "Idioma",
|
||||
"settingLanguageDesc": "Idioma del tema Lobe",
|
||||
"settingLogoPreview": "Vista previa",
|
||||
"settingLogoType": "Tipo de Logo",
|
||||
"settingLogoTypeDesc": "Tipo de logo",
|
||||
"settingNeutralColor": "Color Neutro",
|
||||
"settingNeutralColorDesc": "Personaliza diferentes tonos de gris con diferentes tendencias de color, el segundo es el color neutro original del tema Kitchen",
|
||||
"settingPrimaryColor": "Color primario",
|
||||
"settingPrimaryColorDesc": "Color primario personalizado, el segundo es el color original del tema Kitchen",
|
||||
"settingPromptDisplayMode": "Modo de Visualización",
|
||||
"settingPromptDisplayModeDesc": "Altura fija o altura automática con soporte para cambio de tamaño arrastrable",
|
||||
"settingPromptEditor": "Editor de Prompts",
|
||||
"settingPromptEditorDesc": "Proporciona un editor de Prompts sencillo en la parte superior de la barra lateral de configuración rápida",
|
||||
"settingPromptHighlight": "Resaltar sintaxis del Prompt",
|
||||
"settingPromptHighlightDesc": "Colorear automáticamente la visualización del prompt de acuerdo con las reglas de sintaxis de Stable Diffusion",
|
||||
"settingQuickSettingSidebarDefaultExpand": "Expansión predeterminada",
|
||||
"settingQuickSettingSidebarDefaultExpandDesc": "Si se expande la barra lateral de forma predeterminada al iniciar",
|
||||
"settingQuickSettingSidebarDefaultWidth": "Ancho predeterminado",
|
||||
"settingQuickSettingSidebarDefaultWidthDesc": "Ancho predeterminado de la barra lateral al iniciar",
|
||||
"settingQuickSettingSidebarDisplayMode": "Modo de Visualización",
|
||||
"settingQuickSettingSidebarDisplayModeDesc": "Fijo como modo de cuadrícula para visualización constante, se expande automáticamente cuando el mouse se mueve hacia un lado en el modo flotante",
|
||||
"settingQuickSettingSidebarEnable": "Habilitar",
|
||||
"settingQuickSettingSidebarEnableDesc": "Habilita la barra lateral de configuración rápida en el lado izquierdo",
|
||||
"settingReduceAnimation": "Reducir animación",
|
||||
"settingReduceAnimationDesc": "Reduce el efecto de desenfoque y el color del flujo de fondo, lo que puede mejorar la suavidad y ahorrar uso de CPU",
|
||||
"settingSplitPreviewer": "Vista Previa Dividida",
|
||||
"settingSplitPreviewerDesc": "Coloque el cuadro de entrada de solicitud a la izquierda y el botón generar a la derecha, asegurándose de que la imagen generada siempre se muestre en la parte superior al desplazarse (experimental)",
|
||||
"settingSvgIcons": "Usar Iconos SVG",
|
||||
"settingSvgIconsDesc": "Reemplazar globalmente todos los íconos Emoji en la interfaz de Stable Diffusion con íconos SVG",
|
||||
"switchTheme": "Cambiar Tema Claro/Oscuro",
|
||||
"sync": "Sincronizar con la configuración Webui",
|
||||
"themeFeedback": "Comentarios del Tema",
|
||||
"themeSetting": "Configuración del Tema"
|
||||
"brand": {
|
||||
"kitchen": "Kitchen",
|
||||
"lobe": "LobeHub",
|
||||
"custom": "Personalizado"
|
||||
},
|
||||
"custom": {
|
||||
"initializing": "StableDiffusion / LobeTheme se está inicializando, por favor espere..."
|
||||
},
|
||||
"footer": {
|
||||
"resources": "Recursos",
|
||||
"community": "Comunidad",
|
||||
"help": "Ayuda",
|
||||
"moreProducts": "Más productos"
|
||||
},
|
||||
"header": {
|
||||
"feedback": "Comentarios",
|
||||
"switchTheme": "Cambiar tema claro/oscuro",
|
||||
"setting": "Configuración"
|
||||
},
|
||||
"modal": {
|
||||
"themeFeedback": {
|
||||
"title": "Comentarios sobre el tema"
|
||||
},
|
||||
"themeSetting": {
|
||||
"title": "Configuración del tema"
|
||||
}
|
||||
},
|
||||
"prompt": {
|
||||
"load": "Cargar aviso",
|
||||
"set": "Establecer aviso",
|
||||
"negative": "Negativo",
|
||||
"positive": "Positivo"
|
||||
},
|
||||
"setting": {
|
||||
"button": {
|
||||
"reset": "Restablecer",
|
||||
"submit": "Aplicar y reiniciar interfaz"
|
||||
},
|
||||
"confirmPageUnload": {
|
||||
"title": "Confirmación al abandonar la página",
|
||||
"desc": "Ayuda a prevenir la pérdida de datos no guardados"
|
||||
},
|
||||
"customFont": {
|
||||
"title": "Fuente personalizada",
|
||||
"desc": "Cuando está habilitada, cargará automáticamente una webfont para mejorar la visualización del texto en chino, inglés y código"
|
||||
},
|
||||
"customLogo": {
|
||||
"title": "Logotipo personalizado",
|
||||
"desc": "Soporta URL / Base64 / Símbolos Emoji"
|
||||
},
|
||||
"customTitle": {
|
||||
"title": "Título personalizado",
|
||||
"desc": "Título del logotipo personalizado"
|
||||
},
|
||||
"extraNetworkSidebar": {
|
||||
"defaultCardSize": {
|
||||
"title": "Tamaño de portada del modelo",
|
||||
"desc": "Valor predeterminado del tamaño de portada del modelo al iniciar"
|
||||
},
|
||||
"defaultExpand": {
|
||||
"title": "Expansión predeterminada",
|
||||
"desc": "Si expandir la barra lateral de forma predeterminada al iniciar"
|
||||
},
|
||||
"defaultWidth": {
|
||||
"title": "Ancho predeterminado",
|
||||
"desc": "Ancho predeterminado de la barra lateral al iniciar"
|
||||
},
|
||||
"displayMode": {
|
||||
"title": "Modo de visualización",
|
||||
"desc": "Fijo como modo de cuadrícula para visualización constante, autoexpandible cuando el mouse se mueve hacia el lado en modo flotante"
|
||||
},
|
||||
"enable": {
|
||||
"title": "Habilitar",
|
||||
"desc": "Habilitar la barra lateral de red adicional en el lado derecho"
|
||||
}
|
||||
},
|
||||
"group": {
|
||||
"extraNetworkSidebar": "Barra lateral de red adicional",
|
||||
"layout": "Configuración de diseño",
|
||||
"promptTextarea": "Cuadro de texto de aviso",
|
||||
"quickSettingSidebar": "Barra lateral de configuración rápida",
|
||||
"theme": "Configuración del tema"
|
||||
},
|
||||
"hideFooter": {
|
||||
"title": "Ocultar pie de página",
|
||||
"desc": "Ocultar el pie de página del tema y solo mostrar el pie de página predeterminado de stable diffusion webui"
|
||||
},
|
||||
"language": {
|
||||
"title": "Idioma",
|
||||
"desc": "Idioma del tema Lóbulo"
|
||||
},
|
||||
"logoType": {
|
||||
"title": "Tipo de logotipo",
|
||||
"desc": "Tipo de logotipo",
|
||||
"preview": "Vista previa"
|
||||
},
|
||||
"neutralColor": {
|
||||
"title": "Color neutral",
|
||||
"desc": "Personalizar diferentes tonos de gris con diferentes tendencias de color, el segundo es el color neutral original de Cocina"
|
||||
},
|
||||
"primaryColor": {
|
||||
"title": "Color primario",
|
||||
"desc": "Color primario personalizado, el segundo es el color del tema original de Cocina"
|
||||
},
|
||||
"promptDisplayMode": {
|
||||
"title": "Modo de visualización de aviso",
|
||||
"desc": "Altura fija o altura automática con soporte de redimensionamiento arrastrable",
|
||||
"resizable": "Redimensionable",
|
||||
"scroll": "Desplazamiento"
|
||||
},
|
||||
"promptEditor": {
|
||||
"title": "Editor de aviso",
|
||||
"desc": "Proporcionar un editor de aviso simple en la parte superior de la barra lateral de configuración rápida"
|
||||
},
|
||||
"promptHighlight": {
|
||||
"title": "Resaltado de sintaxis de aviso",
|
||||
"desc": "Colorear automáticamente la visualización del aviso según las reglas de sintaxis de Stable Diffusion"
|
||||
},
|
||||
"quickSettingSidebar": {
|
||||
"defaultExpand": {
|
||||
"title": "Expansión predeterminada",
|
||||
"desc": "Si expandir la barra lateral de forma predeterminada al iniciar"
|
||||
},
|
||||
"defaultWidth": {
|
||||
"title": "Ancho predeterminado",
|
||||
"desc": "Ancho predeterminado de la barra lateral al iniciar"
|
||||
},
|
||||
"displayMode": {
|
||||
"title": "Modo de visualización",
|
||||
"desc": "Fijo como modo de cuadrícula para visualización constante, autoexpandible cuando el mouse se mueve hacia el lado en modo flotante"
|
||||
},
|
||||
"enable": {
|
||||
"title": "Habilitar",
|
||||
"desc": "Habilitar la barra lateral de configuración rápida en el lado izquierdo"
|
||||
}
|
||||
},
|
||||
"reduceAnimation": {
|
||||
"title": "Reducir animación",
|
||||
"desc": "Reducir el efecto de desenfoque y el color de flujo de fondo, lo que puede mejorar la suavidad y ahorrar uso de CPU"
|
||||
},
|
||||
"splitPreviewer": {
|
||||
"title": "Dividir previsualización",
|
||||
"desc": "Colocar el cuadro de entrada de aviso a la izquierda y el botón de generación a la derecha, asegurando que la imagen generada se muestre siempre en la parte superior al desplazarse (experimental)"
|
||||
},
|
||||
"svgIcons": {
|
||||
"title": "Iconos SVG",
|
||||
"desc": "Reemplazar todos los iconos Emoji en stable diffusion webui con iconos SVG globalmente"
|
||||
}
|
||||
},
|
||||
"sidebar": {
|
||||
"extraNetwork": "Red adicional",
|
||||
"quickSetting": "Configuración rápida",
|
||||
"mode": {
|
||||
"fixed": "Fijo",
|
||||
"float": "Flotante"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,159 @@
|
|||
{
|
||||
"brand": {
|
||||
"kitchen": "Kitchen",
|
||||
"lobe": "LobeHub",
|
||||
"custom": "Personnalisé"
|
||||
},
|
||||
"custom": {
|
||||
"initializing": "StableDiffusion / LobeTheme est en cours d'initialisation, veuillez patienter..."
|
||||
},
|
||||
"footer": {
|
||||
"resources": "Ressources",
|
||||
"community": "Communauté",
|
||||
"help": "Aide",
|
||||
"moreProducts": "Plus de Produits"
|
||||
},
|
||||
"header": {
|
||||
"feedback": "Retour",
|
||||
"switchTheme": "Changer de thème clair/sombre",
|
||||
"setting": "Réglage"
|
||||
},
|
||||
"modal": {
|
||||
"themeFeedback": {
|
||||
"title": "Retour sur le thème"
|
||||
},
|
||||
"themeSetting": {
|
||||
"title": "Paramètres du thème"
|
||||
}
|
||||
},
|
||||
"prompt": {
|
||||
"load": "Charger la demande",
|
||||
"set": "Définir la demande",
|
||||
"negative": "Négatif",
|
||||
"positive": "Positif"
|
||||
},
|
||||
"setting": {
|
||||
"button": {
|
||||
"reset": "Réinitialiser",
|
||||
"submit": "Appliquer et redémarrer l'interface"
|
||||
},
|
||||
"confirmPageUnload": {
|
||||
"title": "Confirmation de quitter la page",
|
||||
"desc": "Aide à éviter la perte de données non enregistrées"
|
||||
},
|
||||
"customFont": {
|
||||
"title": "Police personnalisée",
|
||||
"desc": "Lorsqu'activée, elle chargera automatiquement une web police pour améliorer l'affichage du texte en chinois, anglais et code"
|
||||
},
|
||||
"customLogo": {
|
||||
"title": "Logo personnalisé",
|
||||
"desc": "Support URL / Base64 / Symboles Emoji"
|
||||
},
|
||||
"customTitle": {
|
||||
"title": "Titre personnalisé",
|
||||
"desc": "Titre du logo personnalisé"
|
||||
},
|
||||
"extraNetworkSidebar": {
|
||||
"defaultCardSize": {
|
||||
"title": "Taille de la couverture du modèle",
|
||||
"desc": "Valeur par défaut de la taille de la couverture du modèle au démarrage"
|
||||
},
|
||||
"defaultExpand": {
|
||||
"title": "Développement par défaut",
|
||||
"desc": "S'il faut développer la barre latérale par défaut au démarrage"
|
||||
},
|
||||
"defaultWidth": {
|
||||
"title": "Largeur par défaut",
|
||||
"desc": "Largeur par défaut de la barre latérale au démarrage"
|
||||
},
|
||||
"displayMode": {
|
||||
"title": "Mode d'affichage",
|
||||
"desc": "Fixé en mode grille pour un affichage constant, auto-développement lorsque la souris se déplace sur le côté en mode flottant"
|
||||
},
|
||||
"enable": {
|
||||
"title": "Activer",
|
||||
"desc": "Activer la barre latérale réseau supplémentaire sur le côté droit"
|
||||
}
|
||||
},
|
||||
"group": {
|
||||
"extraNetworkSidebar": "Barre latérale réseau supplémentaire",
|
||||
"layout": "Réglages de mise en page",
|
||||
"promptTextarea": "Zone de texte de la demande",
|
||||
"quickSettingSidebar": "Barre latérale de réglage rapide",
|
||||
"theme": "Paramètres du thème"
|
||||
},
|
||||
"hideFooter": {
|
||||
"title": "Masquer le pied de page",
|
||||
"desc": "Masquer le pied de page du thème et afficher uniquement le pied de page par défaut de stable diffusion webui"
|
||||
},
|
||||
"language": {
|
||||
"title": "Langue",
|
||||
"desc": "Langue du thème Lobe"
|
||||
},
|
||||
"logoType": {
|
||||
"title": "Type de logo",
|
||||
"desc": "Type de logo",
|
||||
"preview": "Aperçu"
|
||||
},
|
||||
"neutralColor": {
|
||||
"title": "Couleur neutre",
|
||||
"desc": "Personnaliser différentes nuances de gris avec différentes tendances de couleur, la deuxième est la couleur neutre d'origine de Kitchen"
|
||||
},
|
||||
"primaryColor": {
|
||||
"title": "Couleur primaire",
|
||||
"desc": "Couleur primaire personnalisée, la deuxième est la couleur de thème d'origine de Kitchen"
|
||||
},
|
||||
"promptDisplayMode": {
|
||||
"title": "Mode d'affichage de la demande",
|
||||
"desc": "Hauteur fixe ou hauteur automatique avec prise en charge redimensionnable",
|
||||
"resizable": "Redimensionnable",
|
||||
"scroll": "Faire défiler"
|
||||
},
|
||||
"promptEditor": {
|
||||
"title": "Éditeur de demande",
|
||||
"desc": "Fournir un éditeur de demande simple en haut de la barre latérale de réglage rapide"
|
||||
},
|
||||
"promptHighlight": {
|
||||
"title": "Mise en évidence de la syntaxe de la demande",
|
||||
"desc": "Coloriser automatiquement l'affichage de la demande selon les règles de syntaxe de Stable Diffusion"
|
||||
},
|
||||
"quickSettingSidebar": {
|
||||
"defaultExpand": {
|
||||
"title": "Développement par défaut",
|
||||
"desc": "S'il faut développer la barre latérale par défaut au démarrage"
|
||||
},
|
||||
"defaultWidth": {
|
||||
"title": "Largeur par défaut",
|
||||
"desc": "Largeur par défaut de la barre latérale au démarrage"
|
||||
},
|
||||
"displayMode": {
|
||||
"title": "Mode d'affichage",
|
||||
"desc": "Fixé en mode grille pour un affichage constant, auto-développement lorsque la souris se déplace sur le côté en mode flottant"
|
||||
},
|
||||
"enable": {
|
||||
"title": "Activer",
|
||||
"desc": "Activer la barre latérale de réglage rapide sur le côté gauche"
|
||||
}
|
||||
},
|
||||
"reduceAnimation": {
|
||||
"title": "Réduire l'animation",
|
||||
"desc": "Réduire l'effet de flou et la couleur de fond en mouvement, ce qui peut améliorer la fluidité et économiser l'utilisation du processeur"
|
||||
},
|
||||
"splitPreviewer": {
|
||||
"title": "Diviser le visualiseur",
|
||||
"desc": "Placer la zone de saisie de la demande à gauche et le bouton de génération à droite, en veillant à ce que l'image générée soit toujours affichée en haut lors du défilement (expérimental)"
|
||||
},
|
||||
"svgIcons": {
|
||||
"title": "Icônes SVG",
|
||||
"desc": "Remplacer tous les icônes Emoji dans stable diffusion webui par des icônes SVG globalement"
|
||||
}
|
||||
},
|
||||
"sidebar": {
|
||||
"extraNetwork": "Réseau supplémentaire",
|
||||
"quickSetting": "Réglage rapide",
|
||||
"mode": {
|
||||
"fixed": "Fixe",
|
||||
"float": "Flottant"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,82 +1,159 @@
|
|||
{
|
||||
"appInitializing": "StableDiffusion / LobeThemeが初期化中です。お待ちください...",
|
||||
"community": "コミュニティ",
|
||||
"custom": "カスタム",
|
||||
"extraNetwork": "追加ネットワーク",
|
||||
"feedback": "フィードバック",
|
||||
"fixed": "固定",
|
||||
"float": "フロート",
|
||||
"help": "ヘルプ",
|
||||
"kitchen": "キッチン",
|
||||
"loadPrompt": "プロンプトをロード",
|
||||
"lobe": "ローブ",
|
||||
"moreProducts": "その他の製品",
|
||||
"negative": "ネガティブなヒント",
|
||||
"positive": "ポジティブなヒント",
|
||||
"quickSetting": "クイック設定",
|
||||
"resizable": "リサイズ可能",
|
||||
"resources": "関連リソース",
|
||||
"scroll": "スクロール",
|
||||
"setPrompt": "プロンプトを設定",
|
||||
"setting": "設定",
|
||||
"settingButtonReset": "リセット",
|
||||
"settingButtonSubmit": "適用して再起動",
|
||||
"settingConfirmPageUnload": "ページのクローズ確認",
|
||||
"settingConfirmPageUnloadDesc": "未保存のデータの損失を防ぐ",
|
||||
"settingCustomFont": "カスタムフォントの読み込み",
|
||||
"settingCustomFontDesc": "有効にすると、Webフォントを自動的に読み込んで、英語、中国語、およびコードの表示効果を最適化します",
|
||||
"settingCustomLogo": "カスタムロゴ",
|
||||
"settingCustomLogoDesc": "URL / Base64 / 絵文字をサポート",
|
||||
"settingCustomTitle": "カスタムタイトル",
|
||||
"settingCustomTitleDesc": "カスタムロゴのタイトル名",
|
||||
"settingExtraNetworkSidebarDefaultCardSize": "モデルカバーサイズ",
|
||||
"settingExtraNetworkSidebarDefaultCardSizeDesc": "起動時のモデルカバーサイズのデフォルト値",
|
||||
"settingExtraNetworkSidebarDefaultExpand": "デフォルトで展開",
|
||||
"settingExtraNetworkSidebarDefaultExpandDesc": "起動時にサイドバーをデフォルトで展開しますか?",
|
||||
"settingExtraNetworkSidebarDefaultWidth": "デフォルト幅",
|
||||
"settingExtraNetworkSidebarDefaultWidthDesc": "起動時のサイドバーのデフォルト幅",
|
||||
"settingExtraNetworkSidebarDisplayMode": "表示モード",
|
||||
"settingExtraNetworkSidebarDisplayModeDesc": "グリッドモードで常に表示するか、ホバー時に自動的に展開するフロートモードで表示するか",
|
||||
"settingExtraNetworkSidebarEnable": "有効にする",
|
||||
"settingExtraNetworkSidebarEnableDesc": "右側の追加ネットワークサイドバーを有効にする",
|
||||
"settingGroupExtraNetworkSidebar": "追加ネットワークサイドバー",
|
||||
"settingGroupLayout": "レイアウト設定",
|
||||
"settingGroupPromptTextarea": "プロンプトテキストエリア",
|
||||
"settingGroupQuickSettingSidebar": "クイック設定サイドバー",
|
||||
"settingGroupTheme": "テーマ設定",
|
||||
"settingHideFooter": "フッターを非表示にする",
|
||||
"settingHideFooterDesc": "テーマのフッターを非表示にし、stable diffusion webui のデフォルトフッターのみ表示します",
|
||||
"settingLanguage": "言語",
|
||||
"settingLanguageDesc": "Lobe Themeの言語",
|
||||
"settingLogoPreview": "プレビュー",
|
||||
"settingLogoType": "ロゴタイプ",
|
||||
"settingLogoTypeDesc": "ロゴタイプ",
|
||||
"settingNeutralColor": "中立色",
|
||||
"settingNeutralColorDesc": "異なる色相のグレースケールのカスタマイズ。2番目は元のKitchenの中立色です",
|
||||
"settingPrimaryColor": "プライマリカラー",
|
||||
"settingPrimaryColorDesc": "カスタムプライマリカラー。2番目は元のKitchenのプライマリカラーです",
|
||||
"settingPromptDisplayMode": "表示モード",
|
||||
"settingPromptDisplayModeDesc": "固定の高さまたはドラッグリサイズをサポートする自動の高さ",
|
||||
"settingPromptEditor": "プロンプトエディタ",
|
||||
"settingPromptEditorDesc": "クイック設定サイドバーの上部に簡単なプロンプトエディタを提供します",
|
||||
"settingPromptHighlight": "Promptのシンタックスハイライト",
|
||||
"settingPromptHighlightDesc": "Stable Diffusionのシンタックスルールに基づいて、promptの表示を自動的にハイライトします",
|
||||
"settingQuickSettingSidebarDefaultExpand": "デフォルトで展開",
|
||||
"settingQuickSettingSidebarDefaultExpandDesc": "起動時にサイドバーをデフォルトで展開しますか?",
|
||||
"settingQuickSettingSidebarDefaultWidth": "デフォルト幅",
|
||||
"settingQuickSettingSidebarDefaultWidthDesc": "起動時のサイドバーのデフォルト幅",
|
||||
"settingQuickSettingSidebarDisplayMode": "表示モード",
|
||||
"settingQuickSettingSidebarDisplayModeDesc": "グリッドモードで常に表示するか、ホバー時に自動的に展開するフロートモードで表示するか",
|
||||
"settingQuickSettingSidebarEnable": "有効にする",
|
||||
"settingQuickSettingSidebarEnableDesc": "左側のクイック設定サイドバーを有効にする",
|
||||
"settingReduceAnimation": "アニメーションを削減",
|
||||
"settingReduceAnimationDesc": "ガラスのエフェクトと背景の流れる色を削減し、スムーズさを向上させ、CPUの使用量を節約できます",
|
||||
"settingSplitPreviewer": "2列モード",
|
||||
"settingSplitPreviewerDesc": "プロンプト入力ボックスを左側に配置し、生成ボタンを右側に配置し、スクロール時に生成された画像が常にトップに表示されるようにします(実験的)",
|
||||
"settingSvgIcons": "SVGアイコンを使用",
|
||||
"settingSvgIconsDesc": "stable diffusion webuiの絵文字アイコンをすべてSVGアイコンに置き換えます",
|
||||
"switchTheme": "明暗テーマを切り替える",
|
||||
"sync": "WebUIの設定と同期する",
|
||||
"themeFeedback": "テーマのフィードバック",
|
||||
"themeSetting": "テーマ設定"
|
||||
"brand": {
|
||||
"kitchen": "Kitchen",
|
||||
"lobe": "LobeHub",
|
||||
"custom": "カスタム"
|
||||
},
|
||||
"custom": {
|
||||
"initializing": "StableDiffusion / LobeTheme が初期化中です。お待ちください..."
|
||||
},
|
||||
"footer": {
|
||||
"resources": "リソース",
|
||||
"community": "コミュニティ",
|
||||
"help": "ヘルプ",
|
||||
"moreProducts": "その他の製品"
|
||||
},
|
||||
"header": {
|
||||
"feedback": "フィードバック",
|
||||
"switchTheme": "ライト/ダークテーマの切り替え",
|
||||
"setting": "設定"
|
||||
},
|
||||
"modal": {
|
||||
"themeFeedback": {
|
||||
"title": "テーマフィードバック"
|
||||
},
|
||||
"themeSetting": {
|
||||
"title": "テーマ設定"
|
||||
}
|
||||
},
|
||||
"prompt": {
|
||||
"load": "プロンプトの読み込み",
|
||||
"set": "プロンプトの設定",
|
||||
"negative": "ネガティブ",
|
||||
"positive": "ポジティブ"
|
||||
},
|
||||
"setting": {
|
||||
"button": {
|
||||
"reset": "リセット",
|
||||
"submit": "適用してインターフェースを再起動"
|
||||
},
|
||||
"confirmPageUnload": {
|
||||
"title": "ページ離脱の確認",
|
||||
"desc": "未保存のデータの損失を防ぐのに役立ちます"
|
||||
},
|
||||
"customFont": {
|
||||
"title": "カスタムフォント",
|
||||
"desc": "有効にすると、中国語、英語、コードのテキストの表示を向上させるために自動的にWebフォントを読み込みます"
|
||||
},
|
||||
"customLogo": {
|
||||
"title": "カスタムロゴ",
|
||||
"desc": "URL / Base64 / 絵文字シンボルをサポート"
|
||||
},
|
||||
"customTitle": {
|
||||
"title": "カスタムタイトル",
|
||||
"desc": "カスタムロゴのタイトル"
|
||||
},
|
||||
"extraNetworkSidebar": {
|
||||
"defaultCardSize": {
|
||||
"title": "モデルカバーサイズ",
|
||||
"desc": "開始時のモデルカバーサイズのデフォルト値"
|
||||
},
|
||||
"defaultExpand": {
|
||||
"title": "デフォルト展開",
|
||||
"desc": "開始時にサイドバーをデフォルトで展開するかどうか"
|
||||
},
|
||||
"defaultWidth": {
|
||||
"title": "デフォルト幅",
|
||||
"desc": "開始時のサイドバーのデフォルト幅"
|
||||
},
|
||||
"displayMode": {
|
||||
"title": "表示モード",
|
||||
"desc": "固定表示のグリッドモードまたは浮動モードでサイドにマウスを移動すると自動的に展開"
|
||||
},
|
||||
"enable": {
|
||||
"title": "有効にする",
|
||||
"desc": "右側に追加のネットワークサイドバーを有効にする"
|
||||
}
|
||||
},
|
||||
"group": {
|
||||
"extraNetworkSidebar": "追加のネットワークサイドバー",
|
||||
"layout": "レイアウト設定",
|
||||
"promptTextarea": "プロンプトテキストボックス",
|
||||
"quickSettingSidebar": "クイック設定サイドバー",
|
||||
"theme": "テーマ設定"
|
||||
},
|
||||
"hideFooter": {
|
||||
"title": "フッターを非表示",
|
||||
"desc": "テーマのフッターを非表示にし、Stable Diffusion WebUI のデフォルトフッターのみを表示"
|
||||
},
|
||||
"language": {
|
||||
"title": "言語",
|
||||
"desc": "Lobeテーマの言語"
|
||||
},
|
||||
"logoType": {
|
||||
"title": "ロゴタイプ",
|
||||
"desc": "ロゴタイプ",
|
||||
"preview": "プレビュー"
|
||||
},
|
||||
"neutralColor": {
|
||||
"title": "ニュートラルカラー",
|
||||
"desc": "異なる色傾向のグレーの異なるシェードをカスタマイズします。2番目は元のキッチンのニュートラルカラーです"
|
||||
},
|
||||
"primaryColor": {
|
||||
"title": "プライマリカラー",
|
||||
"desc": "カスタムプライマリカラー。2番目は元のキッチンのテーマカラーです"
|
||||
},
|
||||
"promptDisplayMode": {
|
||||
"title": "プロンプト表示モード",
|
||||
"desc": "固定高さまたはドラッグ可能なリサイズサポート付きの自動高さ",
|
||||
"resizable": "リサイズ可能",
|
||||
"scroll": "スクロール"
|
||||
},
|
||||
"promptEditor": {
|
||||
"title": "プロンプトエディタ",
|
||||
"desc": "クイック設定サイドバーの上部にシンプルなプロンプトエディタを提供"
|
||||
},
|
||||
"promptHighlight": {
|
||||
"title": "プロンプトシンタックスハイライト",
|
||||
"desc": "Stable Diffusionの構文ルールに従ってプロンプト表示を自動的にカラーリング"
|
||||
},
|
||||
"quickSettingSidebar": {
|
||||
"defaultExpand": {
|
||||
"title": "デフォルト展開",
|
||||
"desc": "開始時にサイドバーをデフォルトで展開するかどうか"
|
||||
},
|
||||
"defaultWidth": {
|
||||
"title": "デフォルト幅",
|
||||
"desc": "開始時のサイドバーのデフォルト幅"
|
||||
},
|
||||
"displayMode": {
|
||||
"title": "表示モード",
|
||||
"desc": "固定表示のグリッドモードまたは浮動モードでサイドにマウスを移動すると自動的に展開"
|
||||
},
|
||||
"enable": {
|
||||
"title": "有効にする",
|
||||
"desc": "左側にクイック設定サイドバーを有効にする"
|
||||
}
|
||||
},
|
||||
"reduceAnimation": {
|
||||
"title": "アニメーションを削減",
|
||||
"desc": "ぼかし効果と背景フローカラーを削減し、滑らかさを向上させCPU使用量を節約"
|
||||
},
|
||||
"splitPreviewer": {
|
||||
"title": "分割プレビューア",
|
||||
"desc": "プロンプト入力ボックスを左側に配置し、生成ボタンを右側に配置し、スクロール時に常に生成された画像が上部に表示されるようにします(実験的)"
|
||||
},
|
||||
"svgIcons": {
|
||||
"title": "SVGアイコン",
|
||||
"desc": "Stable Diffusion WebUI のすべての絵文字アイコンをグローバルにSVGアイコンで置き換えます"
|
||||
}
|
||||
},
|
||||
"sidebar": {
|
||||
"extraNetwork": "追加のネットワーク",
|
||||
"quickSetting": "クイック設定",
|
||||
"mode": {
|
||||
"fixed": "固定",
|
||||
"float": "フロート"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,82 +1,159 @@
|
|||
{
|
||||
"appInitializing": "StableDiffusion / LobeTheme가 초기화 중입니다. 잠시 기다려주세요...",
|
||||
"community": "커뮤니티",
|
||||
"custom": "사용자 정의",
|
||||
"extraNetwork": "추가 네트워크",
|
||||
"feedback": "피드백",
|
||||
"fixed": "고정",
|
||||
"float": "부유",
|
||||
"help": "도움말",
|
||||
"kitchen": "Kitchen",
|
||||
"loadPrompt": "로드 프롬프트",
|
||||
"lobe": "Lobe",
|
||||
"moreProducts": "더 많은 제품",
|
||||
"negative": "부정적인",
|
||||
"positive": "긍정적인",
|
||||
"quickSetting": "빠른 설정",
|
||||
"resizable": "크기 조절 가능",
|
||||
"resources": "관련 자료",
|
||||
"scroll": "스크롤",
|
||||
"setPrompt": "프롬프트 설정",
|
||||
"setting": "설정",
|
||||
"settingButtonReset": "재설정",
|
||||
"settingButtonSubmit": "적용 및 인터페이스 재시작",
|
||||
"settingConfirmPageUnload": "페이지 닫기 확인",
|
||||
"settingConfirmPageUnloadDesc": "저장되지 않은 데이터 손실 방지",
|
||||
"settingCustomFont": "폰트 로드",
|
||||
"settingCustomFontDesc": "이 기능을 사용하면 웹 폰트를 자동으로 로드하여 영문, 한글 및 코드 표시 효과를 최적화합니다",
|
||||
"settingCustomLogo": "사용자 정의 로고",
|
||||
"settingCustomLogoDesc": "URL / Base64 / 이모지 표정을 지원합니다.",
|
||||
"settingCustomTitle": "사용자 정의 제목",
|
||||
"settingCustomTitleDesc": "사용자 정의 로고 제목",
|
||||
"settingExtraNetworkSidebarDefaultCardSize": "모델 커버 크기",
|
||||
"settingExtraNetworkSidebarDefaultCardSizeDesc": "시작시 모델 커버 크기 기본값",
|
||||
"settingExtraNetworkSidebarDefaultExpand": "기본 확장",
|
||||
"settingExtraNetworkSidebarDefaultExpandDesc": "시작시 사이드바 기본 확장 여부",
|
||||
"settingExtraNetworkSidebarDefaultWidth": "기본 너비",
|
||||
"settingExtraNetworkSidebarDefaultWidthDesc": "시작시 사이드바 기본 너비",
|
||||
"settingExtraNetworkSidebarDisplayMode": "표시 모드",
|
||||
"settingExtraNetworkSidebarDisplayModeDesc": "그리드 모드로 고정하여 항상 표시하거나, 부유 모드로 설정하여 사이드바에 마우스를 가져가면 자동으로 확장",
|
||||
"settingExtraNetworkSidebarEnable": "사용",
|
||||
"settingExtraNetworkSidebarEnableDesc": "오른쪽에 추가 네트워크 사이드바 활성화",
|
||||
"settingGroupExtraNetworkSidebar": "추가 네트워크 사이드바",
|
||||
"settingGroupLayout": "레이아웃 설정",
|
||||
"settingGroupPromptTextarea": "프롬프트 텍스트 영역",
|
||||
"settingGroupQuickSettingSidebar": "빠른 설정 사이드바",
|
||||
"settingGroupTheme": "테마 설정",
|
||||
"settingHideFooter": "푸터 숨기기",
|
||||
"settingHideFooterDesc": "테마 푸터를 숨기고 stable diffusion webui의 기본 푸터만 표시",
|
||||
"settingLanguage": "언어",
|
||||
"settingLanguageDesc": "Lobe Theme 테마 언어",
|
||||
"settingLogoPreview": "미리보기",
|
||||
"settingLogoType": "로고 유형",
|
||||
"settingLogoTypeDesc": "로고 유형",
|
||||
"settingNeutralColor": "중립색",
|
||||
"settingNeutralColorDesc": "다른 색상 경향의 그레이 스케일 사용자 정의, 두 번째는 원래 Kitchen의 중립색",
|
||||
"settingPrimaryColor": "기본 색상",
|
||||
"settingPrimaryColorDesc": "사용자 정의 기본 색상, 두 번째는 원래 Kitchen의 기본 색상",
|
||||
"settingPromptDisplayMode": "표시 모드",
|
||||
"settingPromptDisplayModeDesc": "고정 높이 또는 자동 높이 및 드래그 조절 지원",
|
||||
"settingPromptEditor": "프롬프트 편집기",
|
||||
"settingPromptEditorDesc": "빠른 설정 사이드바 상단에 간단한 프롬프트 편집기 제공",
|
||||
"settingPromptHighlight": "Prompt 구문 강조",
|
||||
"settingPromptHighlightDesc": "Stable Diffusion 구문 규칙에 따라 자동으로 prompt를 강조하여 표시합니다",
|
||||
"settingQuickSettingSidebarDefaultExpand": "기본 확장",
|
||||
"settingQuickSettingSidebarDefaultExpandDesc": "시작시 사이드바 기본 확장 여부",
|
||||
"settingQuickSettingSidebarDefaultWidth": "기본 너비",
|
||||
"settingQuickSettingSidebarDefaultWidthDesc": "시작시 사이드바 기본 너비",
|
||||
"settingQuickSettingSidebarDisplayMode": "표시 모드",
|
||||
"settingQuickSettingSidebarDisplayModeDesc": "그리드 모드로 고정하여 항상 표시하거나, 부유 모드로 설정하여 사이드바에 마우스를 가져가면 자동으로 확장",
|
||||
"settingQuickSettingSidebarEnable": "사용",
|
||||
"settingQuickSettingSidebarEnableDesc": "왼쪽에 빠른 설정 사이드바 활성화",
|
||||
"settingReduceAnimation": "애니메이션 줄이기",
|
||||
"settingReduceAnimationDesc": "유리 효과와 배경 흐름 색상을 줄여서 부드러움을 향상시키고 CPU 사용량을 줄일 수 있습니다.",
|
||||
"settingSplitPreviewer": "이중 열 모드",
|
||||
"settingSplitPreviewerDesc": "프롬프트 입력 상자를 왼쪽에 배치하고, 우측에 생성 버튼을 두어 스크롤 시 생성된 이미지가 항상 위에 표시되도록 합니다 (실험적)",
|
||||
"settingSvgIcons": "SVG 아이콘 사용",
|
||||
"settingSvgIconsDesc": "stable diffusion webui의 이모지 아이콘을 전역적으로 SVG 아이콘으로 교체합니다.",
|
||||
"switchTheme": "밝기 테마 전환",
|
||||
"sync": "웹 UI 설정과 동기화",
|
||||
"themeFeedback": "테마 피드백",
|
||||
"themeSetting": "테마 설정"
|
||||
"brand": {
|
||||
"kitchen": "Kitchen",
|
||||
"lobe": "LobeHub",
|
||||
"custom": "사용자 정의"
|
||||
},
|
||||
"custom": {
|
||||
"initializing": "StableDiffusion / LobeTheme이 초기화 중입니다. 잠시 기다려주세요..."
|
||||
},
|
||||
"footer": {
|
||||
"resources": "자료",
|
||||
"community": "커뮤니티",
|
||||
"help": "도움말",
|
||||
"moreProducts": "더 많은 제품"
|
||||
},
|
||||
"header": {
|
||||
"feedback": "피드백",
|
||||
"switchTheme": "밝은/어두운 테마 전환",
|
||||
"setting": "설정"
|
||||
},
|
||||
"modal": {
|
||||
"themeFeedback": {
|
||||
"title": "테마 피드백"
|
||||
},
|
||||
"themeSetting": {
|
||||
"title": "테마 설정"
|
||||
}
|
||||
},
|
||||
"prompt": {
|
||||
"load": "로드 프롬프트",
|
||||
"set": "설정 프롬프트",
|
||||
"negative": "부정적",
|
||||
"positive": "긍정적"
|
||||
},
|
||||
"setting": {
|
||||
"button": {
|
||||
"reset": "재설정",
|
||||
"submit": "적용 및 인터페이스 재시작"
|
||||
},
|
||||
"confirmPageUnload": {
|
||||
"title": "페이지 이탈 확인",
|
||||
"desc": "저장되지 않은 데이터 손실을 방지하는 데 도움이 됩니다."
|
||||
},
|
||||
"customFont": {
|
||||
"title": "사용자 정의 글꼴",
|
||||
"desc": "활성화되면 중국어, 영어 및 코드의 텍스트 표시를 향상시키기 위해 자동으로 웹 글꼴을 로드합니다."
|
||||
},
|
||||
"customLogo": {
|
||||
"title": "사용자 정의 로고",
|
||||
"desc": "URL / Base64 / 이모지 심볼 지원"
|
||||
},
|
||||
"customTitle": {
|
||||
"title": "사용자 정의 제목",
|
||||
"desc": "사용자 정의 로고 제목"
|
||||
},
|
||||
"extraNetworkSidebar": {
|
||||
"defaultCardSize": {
|
||||
"title": "모델 커버 크기",
|
||||
"desc": "시작할 때 모델 커버 크기의 기본값"
|
||||
},
|
||||
"defaultExpand": {
|
||||
"title": "기본 확장",
|
||||
"desc": "시작할 때 사이드바를 기본적으로 확장할지 여부"
|
||||
},
|
||||
"defaultWidth": {
|
||||
"title": "기본 너비",
|
||||
"desc": "시작할 때 사이드바의 기본 너비"
|
||||
},
|
||||
"displayMode": {
|
||||
"title": "표시 모드",
|
||||
"desc": "고정된 그리드 모드로 상시 표시하거나 부유 모드에서 측면으로 마우스를 이동할 때 자동으로 확장"
|
||||
},
|
||||
"enable": {
|
||||
"title": "활성화",
|
||||
"desc": "오른쪽에 추가 네트워크 사이드바 활성화"
|
||||
}
|
||||
},
|
||||
"group": {
|
||||
"extraNetworkSidebar": "추가 네트워크 사이드바",
|
||||
"layout": "레이아웃 설정",
|
||||
"promptTextarea": "프롬프트 텍스트 상자",
|
||||
"quickSettingSidebar": "빠른 설정 사이드바",
|
||||
"theme": "테마 설정"
|
||||
},
|
||||
"hideFooter": {
|
||||
"title": "푸터 숨기기",
|
||||
"desc": "테마 푸터를 숨기고 Stable Diffusion 웹UI의 기본 푸터만 표시"
|
||||
},
|
||||
"language": {
|
||||
"title": "언어",
|
||||
"desc": "로브 테마 언어"
|
||||
},
|
||||
"logoType": {
|
||||
"title": "로고 유형",
|
||||
"desc": "로고 유형",
|
||||
"preview": "미리보기"
|
||||
},
|
||||
"neutralColor": {
|
||||
"title": "중립 색상",
|
||||
"desc": "다른 색상 경향을 가진 다양한 회색 음영을 사용자 정의합니다. 두 번째는 원래 주방의 중립 색상입니다."
|
||||
},
|
||||
"primaryColor": {
|
||||
"title": "기본 색상",
|
||||
"desc": "사용자 정의 기본 색상, 두 번째는 원래 주방 테마 색상입니다."
|
||||
},
|
||||
"promptDisplayMode": {
|
||||
"title": "프롬프트 표시 모드",
|
||||
"desc": "고정 높이 또는 드래그 가능한 크기 조정 지원을 통한 자동 높이",
|
||||
"resizable": "크기 조절 가능",
|
||||
"scroll": "스크롤"
|
||||
},
|
||||
"promptEditor": {
|
||||
"title": "프롬프트 편집기",
|
||||
"desc": "빠른 설정 사이드바 상단에 간단한 프롬프트 편집기 제공"
|
||||
},
|
||||
"promptHighlight": {
|
||||
"title": "프롬프트 구문 강조",
|
||||
"desc": "Stable Diffusion 구문 규칙에 따라 프롬프트 표시를 자동으로 색상화"
|
||||
},
|
||||
"quickSettingSidebar": {
|
||||
"defaultExpand": {
|
||||
"title": "기본 확장",
|
||||
"desc": "시작할 때 사이드바를 기본적으로 확장할지 여부"
|
||||
},
|
||||
"defaultWidth": {
|
||||
"title": "기본 너비",
|
||||
"desc": "시작할 때 사이드바의 기본 너비"
|
||||
},
|
||||
"displayMode": {
|
||||
"title": "표시 모드",
|
||||
"desc": "고정된 그리드 모드로 상시 표시하거나 부유 모드에서 측면으로 마우스를 이동할 때 자동으로 확장"
|
||||
},
|
||||
"enable": {
|
||||
"title": "활성화",
|
||||
"desc": "왼쪽에 빠른 설정 사이드바 활성화"
|
||||
}
|
||||
},
|
||||
"reduceAnimation": {
|
||||
"title": "애니메이션 줄이기",
|
||||
"desc": "부드러움을 향상시키고 CPU 사용량을 줄일 수 있는 흐림 효과와 배경 흐름 색상을 줄입니다."
|
||||
},
|
||||
"splitPreviewer": {
|
||||
"title": "분할 미리보기",
|
||||
"desc": "프롬프트 입력 상자를 왼쪽에 놓고 생성 버튼을 오른쪽에 놓아 스크롤할 때 항상 생성된 이미지가 위에 표시되도록 합니다 (실험적)"
|
||||
},
|
||||
"svgIcons": {
|
||||
"title": "SVG 아이콘",
|
||||
"desc": "Stable Diffusion 웹UI의 모든 이모지 아이콘을 전역적으로 SVG 아이콘으로 대체"
|
||||
}
|
||||
},
|
||||
"sidebar": {
|
||||
"extraNetwork": "추가 네트워크",
|
||||
"quickSetting": "빠른 설정",
|
||||
"mode": {
|
||||
"fixed": "고정",
|
||||
"float": "부유"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,16 +15,28 @@
|
|||
"label": "日本語",
|
||||
"value": "ja_JP"
|
||||
},
|
||||
{
|
||||
"label": "Русский",
|
||||
"value": "ru_RU"
|
||||
},
|
||||
{
|
||||
"label": "한국어",
|
||||
"value": "ko_KR"
|
||||
},
|
||||
{
|
||||
"label": "Français",
|
||||
"value": "fr_FR"
|
||||
},
|
||||
{
|
||||
"label": "Deutsch",
|
||||
"value": "de_DE"
|
||||
},
|
||||
{
|
||||
"label": "Русский",
|
||||
"value": "ru_RU"
|
||||
},
|
||||
{
|
||||
"label": "Español",
|
||||
"value": "es_ES"
|
||||
},
|
||||
{
|
||||
"label": "Português",
|
||||
"value": "pt_BR"
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,159 @@
|
|||
{
|
||||
"brand": {
|
||||
"kitchen": "Kitchen",
|
||||
"lobe": "LobeHub",
|
||||
"custom": "Personalizado"
|
||||
},
|
||||
"custom": {
|
||||
"initializing": "StableDiffusion / LobeTheme está inicializando, por favor aguarde..."
|
||||
},
|
||||
"footer": {
|
||||
"resources": "Recursos",
|
||||
"community": "Comunidade",
|
||||
"help": "Ajuda",
|
||||
"moreProducts": "Mais Produtos"
|
||||
},
|
||||
"header": {
|
||||
"feedback": "Feedback",
|
||||
"switchTheme": "Alternar Tema Claro/Escuro",
|
||||
"setting": "Configuração"
|
||||
},
|
||||
"modal": {
|
||||
"themeFeedback": {
|
||||
"title": "Feedback do Tema"
|
||||
},
|
||||
"themeSetting": {
|
||||
"title": "Configurações do Tema"
|
||||
}
|
||||
},
|
||||
"prompt": {
|
||||
"load": "Carregar Prompt",
|
||||
"set": "Definir Prompt",
|
||||
"negative": "Negativo",
|
||||
"positive": "Positivo"
|
||||
},
|
||||
"setting": {
|
||||
"button": {
|
||||
"reset": "Redefinir",
|
||||
"submit": "Aplicar e Reiniciar Interface"
|
||||
},
|
||||
"confirmPageUnload": {
|
||||
"title": "Confirmação ao sair da página",
|
||||
"desc": "Ajuda a evitar a perda de dados não salvos"
|
||||
},
|
||||
"customFont": {
|
||||
"title": "Fonte Personalizada",
|
||||
"desc": "Quando ativado, carregará automaticamente uma webfont para aprimorar a exibição de texto em chinês, inglês e código"
|
||||
},
|
||||
"customLogo": {
|
||||
"title": "Logotipo Personalizado",
|
||||
"desc": "Suporte URL / Base64 / Símbolos de Emoji"
|
||||
},
|
||||
"customTitle": {
|
||||
"title": "Título Personalizado",
|
||||
"desc": "Título do Logotipo Personalizado"
|
||||
},
|
||||
"extraNetworkSidebar": {
|
||||
"defaultCardSize": {
|
||||
"title": "Tamanho da Capa do Modelo",
|
||||
"desc": "Valor padrão do tamanho da capa do modelo ao iniciar"
|
||||
},
|
||||
"defaultExpand": {
|
||||
"title": "Expansão Padrão",
|
||||
"desc": "Se deve expandir a barra lateral por padrão ao iniciar"
|
||||
},
|
||||
"defaultWidth": {
|
||||
"title": "Largura Padrão",
|
||||
"desc": "Largura padrão da barra lateral ao iniciar"
|
||||
},
|
||||
"displayMode": {
|
||||
"title": "Modo de Exibição",
|
||||
"desc": "Fixo como modo de grade para exibição constante, auto-expandir quando o mouse se move para o lado no modo flutuante"
|
||||
},
|
||||
"enable": {
|
||||
"title": "Habilitar",
|
||||
"desc": "Habilitar a barra lateral de rede extra no lado direito"
|
||||
}
|
||||
},
|
||||
"group": {
|
||||
"extraNetworkSidebar": "Barra Lateral de Rede Extra",
|
||||
"layout": "Configurações de Layout",
|
||||
"promptTextarea": "Caixa de Texto do Prompt",
|
||||
"quickSettingSidebar": "Barra Lateral de Configuração Rápida",
|
||||
"theme": "Configurações do Tema"
|
||||
},
|
||||
"hideFooter": {
|
||||
"title": "Ocultar Rodapé",
|
||||
"desc": "Ocultar o rodapé do tema e exibir apenas o rodapé padrão do stable diffusion webui"
|
||||
},
|
||||
"language": {
|
||||
"title": "Idioma",
|
||||
"desc": "Idioma do Tema Lobe"
|
||||
},
|
||||
"logoType": {
|
||||
"title": "Tipo de Logotipo",
|
||||
"desc": "Tipo de Logotipo",
|
||||
"preview": "Visualização"
|
||||
},
|
||||
"neutralColor": {
|
||||
"title": "Cor Neutra",
|
||||
"desc": "Personalize diferentes tons de cinza com diferentes tendências de cor, o segundo é a cor neutra original da Cozinha"
|
||||
},
|
||||
"primaryColor": {
|
||||
"title": "Cor Primária",
|
||||
"desc": "Cor primária personalizada, o segundo é a cor do tema original da Cozinha"
|
||||
},
|
||||
"promptDisplayMode": {
|
||||
"title": "Modo de Exibição do Prompt",
|
||||
"desc": "Altura fixa ou altura automática com suporte para redimensionamento arrastável",
|
||||
"resizable": "Redimensionável",
|
||||
"scroll": "Rolagem"
|
||||
},
|
||||
"promptEditor": {
|
||||
"title": "Editor de Prompt",
|
||||
"desc": "Fornece um editor de prompt simples no topo da barra lateral de configuração rápida"
|
||||
},
|
||||
"promptHighlight": {
|
||||
"title": "Realce de Sintaxe do Prompt",
|
||||
"desc": "Colorize automaticamente a exibição do prompt de acordo com as regras de sintaxe do Stable Diffusion"
|
||||
},
|
||||
"quickSettingSidebar": {
|
||||
"defaultExpand": {
|
||||
"title": "Expansão Padrão",
|
||||
"desc": "Se deve expandir a barra lateral por padrão ao iniciar"
|
||||
},
|
||||
"defaultWidth": {
|
||||
"title": "Largura Padrão",
|
||||
"desc": "Largura padrão da barra lateral ao iniciar"
|
||||
},
|
||||
"displayMode": {
|
||||
"title": "Modo de Exibição",
|
||||
"desc": "Fixo como modo de grade para exibição constante, auto-expandir quando o mouse se move para o lado no modo flutuante"
|
||||
},
|
||||
"enable": {
|
||||
"title": "Habilitar",
|
||||
"desc": "Habilitar a barra lateral de configuração rápida no lado esquerdo"
|
||||
}
|
||||
},
|
||||
"reduceAnimation": {
|
||||
"title": "Reduzir Animação",
|
||||
"desc": "Reduzir o efeito de desfoque e cor de fundo, o que pode melhorar a suavidade e economizar o uso da CPU"
|
||||
},
|
||||
"splitPreviewer": {
|
||||
"title": "Dividir Visualizador",
|
||||
"desc": "Coloque a caixa de entrada do prompt à esquerda e o botão de geração à direita, garantindo que a imagem gerada seja sempre exibida no topo ao rolar (experimental)"
|
||||
},
|
||||
"svgIcons": {
|
||||
"title": "Ícones SVG",
|
||||
"desc": "Substituir todos os ícones de Emoji no stable diffusion webui por ícones SVG globalmente"
|
||||
}
|
||||
},
|
||||
"sidebar": {
|
||||
"extraNetwork": "Rede Extra",
|
||||
"quickSetting": "Configuração Rápida",
|
||||
"mode": {
|
||||
"fixed": "Fixo",
|
||||
"float": "Flutuante"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,82 +1,159 @@
|
|||
{
|
||||
"appInitializing": "StableDiffusion / LobeTheme инициализируется, пожалуйста, подождите...",
|
||||
"community": "Сообщество",
|
||||
"custom": "Кастомный",
|
||||
"extraNetwork": "Доп. Сети",
|
||||
"feedback": "Отзыв",
|
||||
"fixed": "Фиксированный",
|
||||
"float": "Плавающий",
|
||||
"help": "Помощь",
|
||||
"kitchen": "Kitchen",
|
||||
"loadPrompt": "Подгрузить промт",
|
||||
"lobe": "Lobe",
|
||||
"moreProducts": "Другие проекты",
|
||||
"negative": "Отрицательные",
|
||||
"positive": "Положительные",
|
||||
"quickSetting": "Быстрые настройки",
|
||||
"resizable": "Растягиваемый",
|
||||
"resources": "Ресурсы",
|
||||
"scroll": "Скрол",
|
||||
"setPrompt": "Вставить промт",
|
||||
"setting": "Настройки",
|
||||
"settingButtonReset": "Сбросить",
|
||||
"settingButtonSubmit": "Применить и перезапустить интерфейс",
|
||||
"settingConfirmPageUnload": "Подтверждать закрытие страницы",
|
||||
"settingConfirmPageUnloadDesc": "Помогает предотвратить потерю несохраненных данных",
|
||||
"settingCustomFont": "Загружать спец. шрифт",
|
||||
"settingCustomFontDesc": "Когда он включен, он автоматически загружает веб-шрифт для улучшения отображения текста на китайском, английском и кодовом языках.",
|
||||
"settingCustomLogo": "Пользовательский логотип",
|
||||
"settingCustomLogoDesc": "Поддержка символов URL/Base64/Emoji",
|
||||
"settingCustomTitle": "Пользовательское название",
|
||||
"settingCustomTitleDesc": "Название пользовательского логотипа",
|
||||
"settingExtraNetworkSidebarDefaultCardSize": "Размер превью модели",
|
||||
"settingExtraNetworkSidebarDefaultCardSizeDesc": "Значение по умолчанию размера обложки модели при запуске",
|
||||
"settingExtraNetworkSidebarDefaultExpand": "Состояние разворота по умолчанию",
|
||||
"settingExtraNetworkSidebarDefaultExpandDesc": "Нужно ли по умолчанию разворачивать боковую панель при запуске?",
|
||||
"settingExtraNetworkSidebarDefaultWidth": "Ширина по умолчанию",
|
||||
"settingExtraNetworkSidebarDefaultWidthDesc": "Ширина боковой панели по умолчанию при запуске",
|
||||
"settingExtraNetworkSidebarDisplayMode": "Режим отображения",
|
||||
"settingExtraNetworkSidebarDisplayModeDesc": "Фиксирован постоянно или появляется при наведении на боковую панель",
|
||||
"settingExtraNetworkSidebarEnable": "Включено",
|
||||
"settingExtraNetworkSidebarEnableDesc": "Включить Панель Доп. моделей справа",
|
||||
"settingGroupExtraNetworkSidebar": "Боковая панель с доп сетями",
|
||||
"settingGroupLayout": "Настройки макета",
|
||||
"settingGroupPromptTextarea": "Промт(подсказка)",
|
||||
"settingGroupQuickSettingSidebar": "Боковая панель быстрых настроек",
|
||||
"settingGroupTheme": "Настройка темы",
|
||||
"settingHideFooter": "Скрыть футтер",
|
||||
"settingHideFooterDesc": "Скрыть футтер темы и отображать только футтер дефолтного веб-интерфейса.",
|
||||
"settingLanguage": "Язык",
|
||||
"settingLanguageDesc": "Язык Lobe темы",
|
||||
"settingLogoPreview": "Предпросмотр",
|
||||
"settingLogoType": "Стиль лого",
|
||||
"settingLogoTypeDesc": "Стиль лого",
|
||||
"settingNeutralColor": "Нейтральный цвет",
|
||||
"settingNeutralColorDesc": "Настройте различные оттенки серого с различными цветовыми тенденциями, второй - оригинальный нейтральный цвет темы Kitchen.",
|
||||
"settingPrimaryColor": "Основной цвет",
|
||||
"settingPrimaryColorDesc": "Основной цвет - пользовательский, второй - оригинальный цвет темы Kitchen",
|
||||
"settingPromptDisplayMode": "Режим отображения",
|
||||
"settingPromptDisplayModeDesc": "Фиксированная высота или автоматическая высота с поддержкой перетаскиваемого изменения размера",
|
||||
"settingPromptEditor": "Редактор промта",
|
||||
"settingPromptEditorDesc": "Включить простой редактор подсказок в верхней части боковой панели быстрых настроек",
|
||||
"settingPromptHighlight": "Подсветка синтаксиса промта",
|
||||
"settingPromptHighlightDesc": "Автоматическое изменение цвета отображения подсказок в соответствии с правилами синтаксиса Stable Diffusion",
|
||||
"settingQuickSettingSidebarDefaultExpand": "Состояние разворота по умолчанию",
|
||||
"settingQuickSettingSidebarDefaultExpandDesc": "Нужно ли по умолчанию разворачивать боковую панель при запуске?",
|
||||
"settingQuickSettingSidebarDefaultWidth": "Ширина по умолчанию",
|
||||
"settingQuickSettingSidebarDefaultWidthDesc": "Ширина боковой панели по умолчанию при запуске",
|
||||
"settingQuickSettingSidebarDisplayMode": "Режим отображения",
|
||||
"settingQuickSettingSidebarDisplayModeDesc": "Фиксирован постоянно или появляется при наведении на боковую панель",
|
||||
"settingQuickSettingSidebarEnable": "Включено",
|
||||
"settingQuickSettingSidebarEnableDesc": "Включить боковую панель быстрых настроек в левой части экрана",
|
||||
"settingReduceAnimation": "Уменьшить анимации",
|
||||
"settingReduceAnimationDesc": "Уменьшить эффект размытия и цвет фоновой подсветки, что позволит повысить плавность работы и снизить нагрузку на процессор",
|
||||
"settingSplitPreviewer": "Рзделенный просмоторщик",
|
||||
"settingSplitPreviewerDesc": "Разделяет страницу на настройки генрации и поле ввода промта, а на второй половине остается окно сгенерированной картинки и кнопка генерации ",
|
||||
"settingSvgIcons": "Использование SVG-значков",
|
||||
"settingSvgIconsDesc": "Заменить все иконки Emoji в стабильном diffusion webui на SVG-иконки в глобальном масштабе",
|
||||
"switchTheme": "Переключатель светлой/темной темы",
|
||||
"sync": "Синхронизация с настройками webui",
|
||||
"themeFeedback": "Отзыв о теме",
|
||||
"themeSetting": "Настройки темы"
|
||||
"brand": {
|
||||
"kitchen": "Kitchen",
|
||||
"lobe": "LobeHub",
|
||||
"custom": "Пользовательский"
|
||||
},
|
||||
"custom": {
|
||||
"initializing": "StableDiffusion / LobeTheme инициализируется, пожалуйста, подождите..."
|
||||
},
|
||||
"footer": {
|
||||
"resources": "Ресурсы",
|
||||
"community": "Сообщество",
|
||||
"help": "Помощь",
|
||||
"moreProducts": "Дополнительные продукты"
|
||||
},
|
||||
"header": {
|
||||
"feedback": "Обратная связь",
|
||||
"switchTheme": "Переключить светлую/темную тему",
|
||||
"setting": "Настройка"
|
||||
},
|
||||
"modal": {
|
||||
"themeFeedback": {
|
||||
"title": "Обратная связь по теме"
|
||||
},
|
||||
"themeSetting": {
|
||||
"title": "Настройки темы"
|
||||
}
|
||||
},
|
||||
"prompt": {
|
||||
"load": "Загрузить подсказку",
|
||||
"set": "Установить подсказку",
|
||||
"negative": "Негативный",
|
||||
"positive": "Позитивный"
|
||||
},
|
||||
"setting": {
|
||||
"button": {
|
||||
"reset": "Сброс",
|
||||
"submit": "Применить и перезапустить интерфейс"
|
||||
},
|
||||
"confirmPageUnload": {
|
||||
"title": "Подтверждение покидания страницы",
|
||||
"desc": "Помогает предотвратить потерю несохраненных данных"
|
||||
},
|
||||
"customFont": {
|
||||
"title": "Пользовательский шрифт",
|
||||
"desc": "При включении автоматически загружает веб-шрифт для улучшения отображения текста на китайском, английском и коде"
|
||||
},
|
||||
"customLogo": {
|
||||
"title": "Пользовательский логотип",
|
||||
"desc": "Поддерживает URL / Base64 / символы Emoji"
|
||||
},
|
||||
"customTitle": {
|
||||
"title": "Пользовательское название",
|
||||
"desc": "Пользовательское название логотипа"
|
||||
},
|
||||
"extraNetworkSidebar": {
|
||||
"defaultCardSize": {
|
||||
"title": "Размер обложки модели",
|
||||
"desc": "Значение по умолчанию размера обложки модели при запуске"
|
||||
},
|
||||
"defaultExpand": {
|
||||
"title": "Раскрыть по умолчанию",
|
||||
"desc": "Развернуть боковую панель по умолчанию при запуске"
|
||||
},
|
||||
"defaultWidth": {
|
||||
"title": "Ширина по умолчанию",
|
||||
"desc": "Ширина боковой панели по умолчанию при запуске"
|
||||
},
|
||||
"displayMode": {
|
||||
"title": "Режим отображения",
|
||||
"desc": "Фиксированный режим сетки для постоянного отображения, автоматическое развертывание при перемещении мыши к боковой стороне в плавающем режиме"
|
||||
},
|
||||
"enable": {
|
||||
"title": "Включить",
|
||||
"desc": "Включить дополнительную боковую панель сети справа"
|
||||
}
|
||||
},
|
||||
"group": {
|
||||
"extraNetworkSidebar": "Дополнительная боковая панель сети",
|
||||
"layout": "Настройки макета",
|
||||
"promptTextarea": "Текстовое поле подсказки",
|
||||
"quickSettingSidebar": "Быстрая боковая панель настроек",
|
||||
"theme": "Настройки темы"
|
||||
},
|
||||
"hideFooter": {
|
||||
"title": "Скрыть нижний колонтитул",
|
||||
"desc": "Скрыть нижний колонтитул темы и отобразить только стандартный колонтитул стабильного веб-интерфейса диффузии"
|
||||
},
|
||||
"language": {
|
||||
"title": "Язык",
|
||||
"desc": "Язык темы Lobe"
|
||||
},
|
||||
"logoType": {
|
||||
"title": "Тип логотипа",
|
||||
"desc": "Тип логотипа",
|
||||
"preview": "Предварительный просмотр"
|
||||
},
|
||||
"neutralColor": {
|
||||
"title": "Нейтральный цвет",
|
||||
"desc": "Настроить различные оттенки серого с различными цветовыми тенденциями, второй - это оригинальный нейтральный цвет кухни"
|
||||
},
|
||||
"primaryColor": {
|
||||
"title": "Основной цвет",
|
||||
"desc": "Пользовательский основной цвет, второй - это оригинальный цвет темы кухни"
|
||||
},
|
||||
"promptDisplayMode": {
|
||||
"title": "Режим отображения подсказки",
|
||||
"desc": "Фиксированная высота или автоматическая высота с поддержкой изменения размера",
|
||||
"resizable": "Изменяемый размер",
|
||||
"scroll": "Прокрутка"
|
||||
},
|
||||
"promptEditor": {
|
||||
"title": "Редактор подсказки",
|
||||
"desc": "Предоставляет простой редактор подсказок в верхней части быстрой боковой панели настроек"
|
||||
},
|
||||
"promptHighlight": {
|
||||
"title": "Подсветка синтаксиса подсказки",
|
||||
"desc": "Автоматическое окрашивание отображения подсказок в соответствии с правилами синтаксиса Stable Diffusion"
|
||||
},
|
||||
"quickSettingSidebar": {
|
||||
"defaultExpand": {
|
||||
"title": "Раскрыть по умолчанию",
|
||||
"desc": "Развернуть боковую панель по умолчанию при запуске"
|
||||
},
|
||||
"defaultWidth": {
|
||||
"title": "Ширина по умолчанию",
|
||||
"desc": "Ширина боковой панели по умолчанию при запуске"
|
||||
},
|
||||
"displayMode": {
|
||||
"title": "Режим отображения",
|
||||
"desc": "Фиксированный режим сетки для постоянного отображения, автоматическое развертывание при перемещении мыши к боковой стороне в плавающем режиме"
|
||||
},
|
||||
"enable": {
|
||||
"title": "Включить",
|
||||
"desc": "Включить быструю боковую панель настроек слева"
|
||||
}
|
||||
},
|
||||
"reduceAnimation": {
|
||||
"title": "Уменьшить анимацию",
|
||||
"desc": "Уменьшить эффект размытия и цвет фона, что может улучшить плавность и экономить использование ЦП"
|
||||
},
|
||||
"splitPreviewer": {
|
||||
"title": "Разделить предварительный просмотр",
|
||||
"desc": "Разместить поле ввода подсказки слева и кнопку генерации справа, обеспечивая отображение сгенерированного изображения всегда вверху при прокрутке (экспериментально)"
|
||||
},
|
||||
"svgIcons": {
|
||||
"title": "SVG-иконы",
|
||||
"desc": "Заменить все иконки Emoji в стабильном веб-интерфейсе диффузии на глобальные SVG-иконы"
|
||||
}
|
||||
},
|
||||
"sidebar": {
|
||||
"extraNetwork": "Дополнительная сеть",
|
||||
"quickSetting": "Быстрая настройка",
|
||||
"mode": {
|
||||
"fixed": "Фиксированный",
|
||||
"float": "Плавающий"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,82 +1,159 @@
|
|||
{
|
||||
"appInitializing": "StableDiffusion / LobeTheme 启动中,请耐心等待...",
|
||||
"community": "社区",
|
||||
"custom": "自定义",
|
||||
"extraNetwork": "附加网络",
|
||||
"feedback": "反馈",
|
||||
"fixed": "固定",
|
||||
"float": "悬浮",
|
||||
"help": "帮助",
|
||||
"kitchen": "Kitchen",
|
||||
"loadPrompt": "加载提示",
|
||||
"lobe": "Lobe",
|
||||
"moreProducts": "更多产品",
|
||||
"negative": "反向提示词",
|
||||
"positive": "正面提示词",
|
||||
"quickSetting": "快捷设置",
|
||||
"resizable": "缩放",
|
||||
"resources": "相关资源",
|
||||
"scroll": "滚动",
|
||||
"setPrompt": "发送提示词",
|
||||
"setting": "设置",
|
||||
"settingButtonReset": "重置",
|
||||
"settingButtonSubmit": "应用并重启界面",
|
||||
"settingConfirmPageUnload": "确认离开页面",
|
||||
"settingConfirmPageUnloadDesc": "有助于防止丢失未保存的数据",
|
||||
"settingCustomFont": "加载字体美化",
|
||||
"settingCustomFontDesc": "开启后会自动加载 Webfont 美化字体,优化中英文和代码显示效果",
|
||||
"settingCustomLogo": "自定义 Logo",
|
||||
"settingCustomLogoDesc": "支持 URL / Base64 / Emoji 表情符号",
|
||||
"settingCustomTitle": "自定义标题",
|
||||
"settingCustomTitleDesc": "自定义 Logo 标题名称",
|
||||
"settingExtraNetworkSidebarDefaultCardSize": "模型封面尺寸",
|
||||
"settingExtraNetworkSidebarDefaultCardSizeDesc": "启动时模型封面尺寸默认值",
|
||||
"settingExtraNetworkSidebarDefaultExpand": "默认展开",
|
||||
"settingExtraNetworkSidebarDefaultExpandDesc": "是否在启动时将侧边栏默认展开",
|
||||
"settingExtraNetworkSidebarDefaultWidth": "默认宽度",
|
||||
"settingExtraNetworkSidebarDefaultWidthDesc": "侧边栏在启动时的默认宽度",
|
||||
"settingExtraNetworkSidebarDisplayMode": "显示模式",
|
||||
"settingExtraNetworkSidebarDisplayModeDesc": "固定为栅格模式常驻显示,悬浮模式时当鼠标移到侧边时自动展开",
|
||||
"settingExtraNetworkSidebarEnable": "启用",
|
||||
"settingExtraNetworkSidebarEnableDesc": "启用位于右侧的附加网络侧边栏",
|
||||
"settingGroupExtraNetworkSidebar": "附加网络侧边栏",
|
||||
"settingGroupLayout": "布局设置",
|
||||
"settingGroupPromptTextarea": "提示词文本框",
|
||||
"settingGroupQuickSettingSidebar": "快捷设置侧边栏",
|
||||
"settingGroupTheme": "主题设置",
|
||||
"settingHideFooter": "隐藏页脚",
|
||||
"settingHideFooterDesc": "隐藏主题页脚,只显示 stable diffusion webui 默认页脚",
|
||||
"settingLanguage": "语言",
|
||||
"settingLanguageDesc": "Lobe Theme 主题语言",
|
||||
"settingLogoPreview": "预览",
|
||||
"settingLogoType": "Logo 类型",
|
||||
"settingLogoTypeDesc": "Logo 类型",
|
||||
"settingNeutralColor": "中性色",
|
||||
"settingNeutralColorDesc": "不同色彩倾向的灰阶自定义,第二个为原始 Kitchen 中性色",
|
||||
"settingPrimaryColor": "主题色",
|
||||
"settingPrimaryColorDesc": "自定义主题色,第二个为原始 Kitchen 主题色",
|
||||
"settingPromptDisplayMode": "显示模式",
|
||||
"settingPromptDisplayModeDesc": "固定高度或自动高度并支持拖拽拉伸",
|
||||
"settingPromptEditor": "提示词编辑器",
|
||||
"settingPromptEditorDesc": "提供简易的提示词编辑器位于快捷设置侧边栏顶部",
|
||||
"settingPromptHighlight": "Prompt 语法高亮",
|
||||
"settingPromptHighlightDesc": "按 Stable Diffusion 语法规则,自动染色 prompt 显示",
|
||||
"settingQuickSettingSidebarDefaultExpand": "默认展开",
|
||||
"settingQuickSettingSidebarDefaultExpandDesc": "是否在启动时将侧边栏默认展开",
|
||||
"settingQuickSettingSidebarDefaultWidth": "默认宽度",
|
||||
"settingQuickSettingSidebarDefaultWidthDesc": "侧边栏在启动时的默认宽度",
|
||||
"settingQuickSettingSidebarDisplayMode": "显示模式",
|
||||
"settingQuickSettingSidebarDisplayModeDesc": "固定为栅格模式常驻显示,悬浮模式时当鼠标移到侧边时自动展开",
|
||||
"settingQuickSettingSidebarEnable": "启用",
|
||||
"settingQuickSettingSidebarEnableDesc": "启用位于左侧的快捷设置侧边栏",
|
||||
"settingReduceAnimation": "减少动画效果",
|
||||
"settingReduceAnimationDesc": "减少毛玻璃效果和背景流动色,可以提升流畅度并节省 CPU 使用",
|
||||
"settingSplitPreviewer": "双列模式",
|
||||
"settingSplitPreviewerDesc": "将提示词输入框放在左侧,生成按钮于右侧,确保在滚动时生成的图像始终显示在顶部 (实验性)",
|
||||
"settingSvgIcons": "使用 SVG 图标",
|
||||
"settingSvgIconsDesc": "将 stable diffusion webui 中的 Emoji 图标全局替换为 SVG 图标",
|
||||
"switchTheme": "切换亮暗色主题",
|
||||
"sync": "与 WebUI 设置同步",
|
||||
"themeFeedback": "主题反馈",
|
||||
"themeSetting": "主题设置"
|
||||
"brand": {
|
||||
"kitchen": "Kitchen",
|
||||
"lobe": "LobeHub",
|
||||
"custom": "自定义"
|
||||
},
|
||||
"custom": {
|
||||
"initializing": "StableDiffusion / LobeTheme 正在初始化,请稍候..."
|
||||
},
|
||||
"footer": {
|
||||
"resources": "资源",
|
||||
"community": "社区",
|
||||
"help": "帮助",
|
||||
"moreProducts": "更多产品"
|
||||
},
|
||||
"header": {
|
||||
"feedback": "反馈",
|
||||
"switchTheme": "切换浅色/深色主题",
|
||||
"setting": "设置"
|
||||
},
|
||||
"modal": {
|
||||
"themeFeedback": {
|
||||
"title": "主题反馈"
|
||||
},
|
||||
"themeSetting": {
|
||||
"title": "主题设置"
|
||||
}
|
||||
},
|
||||
"prompt": {
|
||||
"load": "加载提示",
|
||||
"set": "设置提示",
|
||||
"negative": "否定",
|
||||
"positive": "肯定"
|
||||
},
|
||||
"setting": {
|
||||
"button": {
|
||||
"reset": "重置",
|
||||
"submit": "应用并重新启动界面"
|
||||
},
|
||||
"confirmPageUnload": {
|
||||
"title": "确认离开页面",
|
||||
"desc": "帮助防止未保存数据的丢失"
|
||||
},
|
||||
"customFont": {
|
||||
"title": "自定义字体",
|
||||
"desc": "启用后,将自动加载网页字体以增强中文、英文和代码的显示"
|
||||
},
|
||||
"customLogo": {
|
||||
"title": "自定义标志",
|
||||
"desc": "支持 URL / Base64 / 表情符号"
|
||||
},
|
||||
"customTitle": {
|
||||
"title": "自定义标题",
|
||||
"desc": "自定义标志标题"
|
||||
},
|
||||
"extraNetworkSidebar": {
|
||||
"defaultCardSize": {
|
||||
"title": "模型封面大小",
|
||||
"desc": "启动时模型封面大小的默认值"
|
||||
},
|
||||
"defaultExpand": {
|
||||
"title": "默认展开",
|
||||
"desc": "启动时默认是否展开侧边栏"
|
||||
},
|
||||
"defaultWidth": {
|
||||
"title": "默认宽度",
|
||||
"desc": "启动时侧边栏的默认宽度"
|
||||
},
|
||||
"displayMode": {
|
||||
"title": "显示模式",
|
||||
"desc": "固定网格模式以保持恒定显示,在浮动模式下鼠标移至侧边时自动展开"
|
||||
},
|
||||
"enable": {
|
||||
"title": "启用",
|
||||
"desc": "在右侧启用额外的网络侧边栏"
|
||||
}
|
||||
},
|
||||
"group": {
|
||||
"extraNetworkSidebar": "额外网络侧边栏",
|
||||
"layout": "布局设置",
|
||||
"promptTextarea": "提示文本框",
|
||||
"quickSettingSidebar": "快速设置侧边栏",
|
||||
"theme": "主题设置"
|
||||
},
|
||||
"hideFooter": {
|
||||
"title": "隐藏页脚",
|
||||
"desc": "隐藏主题页脚,仅显示 Stable Diffusion webui 的默认页脚"
|
||||
},
|
||||
"language": {
|
||||
"title": "语言",
|
||||
"desc": "叶片主题语言"
|
||||
},
|
||||
"logoType": {
|
||||
"title": "标志类型",
|
||||
"desc": "标志类型",
|
||||
"preview": "预览"
|
||||
},
|
||||
"neutralColor": {
|
||||
"title": "中性颜色",
|
||||
"desc": "定制不同色调的灰色,第二个是原始厨房中性颜色"
|
||||
},
|
||||
"primaryColor": {
|
||||
"title": "主色",
|
||||
"desc": "自定义主色,第二个是原始厨房主题颜色"
|
||||
},
|
||||
"promptDisplayMode": {
|
||||
"title": "提示显示模式",
|
||||
"desc": "固定高度或可拖动调整大小的自动高度",
|
||||
"resizable": "可调整大小",
|
||||
"scroll": "滚动"
|
||||
},
|
||||
"promptEditor": {
|
||||
"title": "提示编辑器",
|
||||
"desc": "在快速设置侧边栏顶部提供简单的提示编辑器"
|
||||
},
|
||||
"promptHighlight": {
|
||||
"title": "提示语法高亮",
|
||||
"desc": "根据 Stable Diffusion 语法规则自动着色提示显示"
|
||||
},
|
||||
"quickSettingSidebar": {
|
||||
"defaultExpand": {
|
||||
"title": "默认展开",
|
||||
"desc": "启动时默认是否展开侧边栏"
|
||||
},
|
||||
"defaultWidth": {
|
||||
"title": "默认宽度",
|
||||
"desc": "启动时侧边栏的默认宽度"
|
||||
},
|
||||
"displayMode": {
|
||||
"title": "显示模式",
|
||||
"desc": "固定网格模式以保持恒定显示,在浮动模式下鼠标移至侧边时自动展开"
|
||||
},
|
||||
"enable": {
|
||||
"title": "启用",
|
||||
"desc": "在左侧启用快速设置侧边栏"
|
||||
}
|
||||
},
|
||||
"reduceAnimation": {
|
||||
"title": "减少动画",
|
||||
"desc": "减少模糊效果和背景流动颜色,可提高流畅度并节省 CPU 使用率"
|
||||
},
|
||||
"splitPreviewer": {
|
||||
"title": "分割预览器",
|
||||
"desc": "将提示输入框放在左侧,生成按钮放在右侧,确保滚动时生成的图像始终显示在顶部(实验性)"
|
||||
},
|
||||
"svgIcons": {
|
||||
"title": "SVG 图标",
|
||||
"desc": "全局替换 Stable Diffusion webui 中的所有表情符号为 SVG 图标"
|
||||
}
|
||||
},
|
||||
"sidebar": {
|
||||
"extraNetwork": "额外网络",
|
||||
"quickSetting": "快速设置",
|
||||
"mode": {
|
||||
"fixed": "固定",
|
||||
"float": "浮动"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,82 +1,159 @@
|
|||
{
|
||||
"appInitializing": "StableDiffusion / LobeTheme 正在初始化,请稍候...",
|
||||
"community": "社區",
|
||||
"custom": "自訂",
|
||||
"extraNetwork": "附加網絡",
|
||||
"feedback": "反饋",
|
||||
"fixed": "固定",
|
||||
"float": "懸浮",
|
||||
"help": "幫助",
|
||||
"kitchen": "Kitchen",
|
||||
"loadPrompt": "加載提示",
|
||||
"lobe": "Lobe",
|
||||
"moreProducts": "更多產品",
|
||||
"negative": "反向提示詞",
|
||||
"positive": "正面提示詞",
|
||||
"quickSetting": "快捷設置",
|
||||
"resizable": "縮放",
|
||||
"resources": "相關資源",
|
||||
"scroll": "滾動",
|
||||
"setPrompt": "發送提示詞",
|
||||
"setting": "設置",
|
||||
"settingButtonReset": "重置",
|
||||
"settingButtonSubmit": "應用並重啟界面",
|
||||
"settingConfirmPageUnload": "確認離開頁面",
|
||||
"settingConfirmPageUnloadDesc": "有助於防止遺失未保存的數據",
|
||||
"settingCustomFont": "載入字型美化",
|
||||
"settingCustomFontDesc": "開啟後會自動載入 Webfont 美化字型,優化中英文和程式碼顯示效果",
|
||||
"settingCustomLogo": "自定義 Logo",
|
||||
"settingCustomLogoDesc": "支持 URL / Base64 / Emoji 表情符號",
|
||||
"settingCustomTitle": "自定義標題",
|
||||
"settingCustomTitleDesc": "自定義 Logo 標題名稱",
|
||||
"settingExtraNetworkSidebarDefaultCardSize": "模型封面尺寸",
|
||||
"settingExtraNetworkSidebarDefaultCardSizeDesc": "啟動時模型封面尺寸默認值",
|
||||
"settingExtraNetworkSidebarDefaultExpand": "默認展開",
|
||||
"settingExtraNetworkSidebarDefaultExpandDesc": "是否在啟動時將側邊欄默認展開",
|
||||
"settingExtraNetworkSidebarDefaultWidth": "默認寬度",
|
||||
"settingExtraNetworkSidebarDefaultWidthDesc": "側邊欄在啟動時的默認寬度",
|
||||
"settingExtraNetworkSidebarDisplayMode": "顯示模式",
|
||||
"settingExtraNetworkSidebarDisplayModeDesc": "固定為格模式常駐顯示,懸浮模式時當鼠標移到側邊時自動展開",
|
||||
"settingExtraNetworkSidebarEnable": "啟用",
|
||||
"settingExtraNetworkSidebarEnableDesc": "啟用位於右側的附加網絡側邊欄",
|
||||
"settingGroupExtraNetworkSidebar": "附加網絡側邊欄",
|
||||
"settingGroupLayout": "佈局設置",
|
||||
"settingGroupPromptTextarea": "提示詞文本框",
|
||||
"settingGroupQuickSettingSidebar": "快捷設置側邊欄",
|
||||
"settingGroupTheme": "主題設置",
|
||||
"settingHideFooter": "隱藏頁腳",
|
||||
"settingHideFooterDesc": "隱藏主題頁腳,只顯示 stable diffusion webui 默認頁腳",
|
||||
"settingLanguage": "語言",
|
||||
"settingLanguageDesc": "Lobe Theme 主題語言",
|
||||
"settingLogoPreview": "預覽",
|
||||
"settingLogoType": "Logo 類型",
|
||||
"settingLogoTypeDesc": "Logo 類型",
|
||||
"settingNeutralColor": "中性色",
|
||||
"settingNeutralColorDesc": "不同色彩傾向的灰階自定義,第二個為原始 Kitchen 中性色",
|
||||
"settingPrimaryColor": "主題色",
|
||||
"settingPrimaryColorDesc": "自定義主題色,第二個為原始 Kitchen 主題色",
|
||||
"settingPromptDisplayMode": "顯示模式",
|
||||
"settingPromptDisplayModeDesc": "固定高度或自動高度並支持拖拽拉伸",
|
||||
"settingPromptEditor": "提示詞編輯器",
|
||||
"settingPromptEditorDesc": "提供簡易的提示詞編輯器位於快捷設置側邊欄頂部",
|
||||
"settingPromptHighlight": "Prompt 語法高亮",
|
||||
"settingPromptHighlightDesc": "按照 Stable Diffusion 語法規則,自動著色 prompt 顯示",
|
||||
"settingQuickSettingSidebarDefaultExpand": "默認展開",
|
||||
"settingQuickSettingSidebarDefaultExpandDesc": "是否在啟動時將側邊欄默認展開",
|
||||
"settingQuickSettingSidebarDefaultWidth": "默認寬度",
|
||||
"settingQuickSettingSidebarDefaultWidthDesc": "側邊欄在啟動時的默認寬度",
|
||||
"settingQuickSettingSidebarDisplayMode": "顯示模式",
|
||||
"settingQuickSettingSidebarDisplayModeDesc": "固定為格模式常駐顯示,懸浮模式時當鼠標移到側邊時自動展開",
|
||||
"settingQuickSettingSidebarEnable": "啟用",
|
||||
"settingQuickSettingSidebarEnableDesc": "啟用位於左側的快捷設置側邊欄",
|
||||
"settingReduceAnimation": "減少動畫效果",
|
||||
"settingReduceAnimationDesc": "減少毛玻璃效果和背景流動色,可以提升流暢度並節省 CPU 使用",
|
||||
"settingSplitPreviewer": "雙列模式",
|
||||
"settingSplitPreviewerDesc": "將提示詞輸入框放在左側,生成按鈕於右側,確保在滾動時生成的圖像始終顯示在頂部 (實驗性)",
|
||||
"settingSvgIcons": "使用 SVG 圖標",
|
||||
"settingSvgIconsDesc": "將 stable diffusion webui 中的 Emoji 圖標全局替換為 SVG 圖標",
|
||||
"switchTheme": "切換亮暗色主題",
|
||||
"sync": "與 WebUI 設置同步",
|
||||
"themeFeedback": "主題反饋",
|
||||
"themeSetting": "主題設置"
|
||||
"brand": {
|
||||
"kitchen": "Kitchen",
|
||||
"lobe": "LobeHub",
|
||||
"custom": "自定義"
|
||||
},
|
||||
"custom": {
|
||||
"initializing": "StableDiffusion / LobeTheme 正在初始化,請稍候..."
|
||||
},
|
||||
"footer": {
|
||||
"resources": "資源",
|
||||
"community": "社區",
|
||||
"help": "幫助",
|
||||
"moreProducts": "更多產品"
|
||||
},
|
||||
"header": {
|
||||
"feedback": "反饋",
|
||||
"switchTheme": "切換明/暗主題",
|
||||
"setting": "設置"
|
||||
},
|
||||
"modal": {
|
||||
"themeFeedback": {
|
||||
"title": "主題反饋"
|
||||
},
|
||||
"themeSetting": {
|
||||
"title": "主題設置"
|
||||
}
|
||||
},
|
||||
"prompt": {
|
||||
"load": "載入提示",
|
||||
"set": "設置提示",
|
||||
"negative": "否定",
|
||||
"positive": "肯定"
|
||||
},
|
||||
"setting": {
|
||||
"button": {
|
||||
"reset": "重置",
|
||||
"submit": "應用並重新啟動界面"
|
||||
},
|
||||
"confirmPageUnload": {
|
||||
"title": "離開頁面確認",
|
||||
"desc": "幫助防止未保存數據的丟失"
|
||||
},
|
||||
"customFont": {
|
||||
"title": "自定義字體",
|
||||
"desc": "啟用後,將自動加載網頁字體,以增強中文、英文和代碼的顯示效果"
|
||||
},
|
||||
"customLogo": {
|
||||
"title": "自定義標誌",
|
||||
"desc": "支援 URL / Base64 / 表情符號"
|
||||
},
|
||||
"customTitle": {
|
||||
"title": "自定義標題",
|
||||
"desc": "自定義標誌標題"
|
||||
},
|
||||
"extraNetworkSidebar": {
|
||||
"defaultCardSize": {
|
||||
"title": "模型封面大小",
|
||||
"desc": "啟動時的模型封面大小的默認值"
|
||||
},
|
||||
"defaultExpand": {
|
||||
"title": "默認展開",
|
||||
"desc": "啟動時是否默認展開側邊欄"
|
||||
},
|
||||
"defaultWidth": {
|
||||
"title": "默認寬度",
|
||||
"desc": "啟動時側邊欄的默認寬度"
|
||||
},
|
||||
"displayMode": {
|
||||
"title": "顯示模式",
|
||||
"desc": "固定為網格模式以保持恆定顯示,在浮動模式下當滑鼠移至側邊時自動展開"
|
||||
},
|
||||
"enable": {
|
||||
"title": "啟用",
|
||||
"desc": "在右側啟用額外的網絡側邊欄"
|
||||
}
|
||||
},
|
||||
"group": {
|
||||
"extraNetworkSidebar": "額外網絡側邊欄",
|
||||
"layout": "佈局設置",
|
||||
"promptTextarea": "提示文本框",
|
||||
"quickSettingSidebar": "快速設置側邊欄",
|
||||
"theme": "主題設置"
|
||||
},
|
||||
"hideFooter": {
|
||||
"title": "隱藏頁腳",
|
||||
"desc": "隱藏主題頁腳,僅顯示 Stable Diffusion webui 的默認頁腳"
|
||||
},
|
||||
"language": {
|
||||
"title": "語言",
|
||||
"desc": "Lobe 主題語言"
|
||||
},
|
||||
"logoType": {
|
||||
"title": "標誌類型",
|
||||
"desc": "標誌類型",
|
||||
"preview": "預覽"
|
||||
},
|
||||
"neutralColor": {
|
||||
"title": "中性顏色",
|
||||
"desc": "使用不同的色調自定義不同的灰色,第二個是原始 Kitchen 中性顏色"
|
||||
},
|
||||
"primaryColor": {
|
||||
"title": "主要顏色",
|
||||
"desc": "自定義主要顏色,第二個是原始 Kitchen 主題顏色"
|
||||
},
|
||||
"promptDisplayMode": {
|
||||
"title": "提示顯示模式",
|
||||
"desc": "固定高度或可拖動調整大小的自動高度",
|
||||
"resizable": "可調整大小",
|
||||
"scroll": "滾動"
|
||||
},
|
||||
"promptEditor": {
|
||||
"title": "提示編輯器",
|
||||
"desc": "在快速設置側邊欄頂部提供簡單的提示編輯器"
|
||||
},
|
||||
"promptHighlight": {
|
||||
"title": "提示語法高亮",
|
||||
"desc": "根據 Stable Diffusion 語法規則自動著色提示顯示"
|
||||
},
|
||||
"quickSettingSidebar": {
|
||||
"defaultExpand": {
|
||||
"title": "默認展開",
|
||||
"desc": "啟動時是否默認展開側邊欄"
|
||||
},
|
||||
"defaultWidth": {
|
||||
"title": "默認寬度",
|
||||
"desc": "啟動時側邊欄的默認寬度"
|
||||
},
|
||||
"displayMode": {
|
||||
"title": "顯示模式",
|
||||
"desc": "固定為網格模式以保持恆定顯示,在浮動模式下當滑鼠移至側邊時自動展開"
|
||||
},
|
||||
"enable": {
|
||||
"title": "啟用",
|
||||
"desc": "在左側啟用快速設置側邊欄"
|
||||
}
|
||||
},
|
||||
"reduceAnimation": {
|
||||
"title": "減少動畫",
|
||||
"desc": "減少模糊效果和背景流動顏色,可提高流暢度並節省 CPU 使用率"
|
||||
},
|
||||
"splitPreviewer": {
|
||||
"title": "分割預覽器",
|
||||
"desc": "將提示輸入框放在左側,生成按鈕放在右側,確保滾動時始終在頂部顯示生成的圖像(實驗性)"
|
||||
},
|
||||
"svgIcons": {
|
||||
"title": "SVG 圖示",
|
||||
"desc": "全局替換 Stable Diffusion webui 中的所有表情符號為 SVG 圖示"
|
||||
}
|
||||
},
|
||||
"sidebar": {
|
||||
"extraNetwork": "額外網絡",
|
||||
"quickSetting": "快速設置",
|
||||
"mode": {
|
||||
"fixed": "固定",
|
||||
"float": "浮動"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@
|
|||
"ahooks": "^3",
|
||||
"antd": "^5",
|
||||
"antd-style": "latest",
|
||||
"consola": "^3.2.3",
|
||||
"i18next": "^23",
|
||||
"i18next-http-backend": "^2",
|
||||
"lodash-es": "^4",
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { consola } from 'consola';
|
||||
|
||||
import RefreshRuntime from '/@react-refresh';
|
||||
|
||||
const RefreshSig = (type) => type;
|
||||
|
|
@ -7,4 +9,4 @@ window.$RefreshReg$ = () => {};
|
|||
window.$RefreshSig$ = () => RefreshSig;
|
||||
window.__vite_plugin_react_preamble_installed__ = true;
|
||||
|
||||
console.debug('🤯 Injecting React Refresh');
|
||||
consola.success('🤯 Injecting React Refresh');
|
||||
|
|
|
|||
|
|
@ -18,51 +18,51 @@ import { useStyles } from './style';
|
|||
const HEADER_HEIGHT = 64;
|
||||
|
||||
const Index = memo(() => {
|
||||
const setting = useAppStore(selectors.currentSetting, isEqual);
|
||||
const { cx, styles } = useStyles({
|
||||
headerHeight: HEADER_HEIGHT,
|
||||
isPrimaryColor: Boolean(setting.primaryColor),
|
||||
});
|
||||
const setting = useAppStore(selectors.currentSetting, isEqual);
|
||||
const { cx, styles } = useStyles({
|
||||
headerHeight: HEADER_HEIGHT,
|
||||
isPrimaryColor: Boolean(setting.primaryColor),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (setting.enableHighlight) {
|
||||
PromptHighlight('#txt2img_prompt', '#lobe_txt2img_prompt');
|
||||
PromptHighlight('#img2img_prompt', '#lobe_img2img_prompt');
|
||||
}
|
||||
if (setting.svgIcon) replaceIcon();
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
if (setting.enableHighlight) {
|
||||
PromptHighlight('#txt2img_prompt', '#lobe_txt2img_prompt');
|
||||
PromptHighlight('#img2img_prompt', '#lobe_img2img_prompt');
|
||||
}
|
||||
if (setting.svgIcon) replaceIcon();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<GlobalStyle />
|
||||
<LayoutHeader headerHeight={HEADER_HEIGHT}>
|
||||
<Header />
|
||||
</LayoutHeader>
|
||||
<LayoutMain>
|
||||
{<div className={setting.liteAnimation ? styles.backgroundLite : styles.background} />}
|
||||
{setting.enableSidebar && (
|
||||
<LayoutSidebar
|
||||
className={styles.sidebar}
|
||||
headerHeight={HEADER_HEIGHT}
|
||||
style={{ flex: 0, zIndex: 50 }}
|
||||
>
|
||||
<QuickSettingSidebar headerHeight={HEADER_HEIGHT} />
|
||||
</LayoutSidebar>
|
||||
)}
|
||||
<Content className={cx(!setting.enableSidebar && styles.quicksettings)} />
|
||||
{setting?.enableExtraNetworkSidebar && (
|
||||
<LayoutSidebar
|
||||
className={styles.sidebar}
|
||||
headerHeight={HEADER_HEIGHT}
|
||||
style={{ flex: 0, zIndex: 50 }}
|
||||
>
|
||||
<ExtraNetworkSidebar headerHeight={HEADER_HEIGHT} />
|
||||
</LayoutSidebar>
|
||||
)}
|
||||
</LayoutMain>
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<GlobalStyle />
|
||||
<LayoutHeader headerHeight={HEADER_HEIGHT}>
|
||||
<Header />
|
||||
</LayoutHeader>
|
||||
<LayoutMain>
|
||||
{<div className={setting.liteAnimation ? styles.backgroundLite : styles.background} />}
|
||||
{setting.enableSidebar && (
|
||||
<LayoutSidebar
|
||||
className={styles.sidebar}
|
||||
headerHeight={HEADER_HEIGHT}
|
||||
style={{ flex: 0, zIndex: 50 }}
|
||||
>
|
||||
<QuickSettingSidebar headerHeight={HEADER_HEIGHT} />
|
||||
</LayoutSidebar>
|
||||
)}
|
||||
<Content className={cx(!setting.enableSidebar && styles.quicksettings)} />
|
||||
{setting?.enableExtraNetworkSidebar && (
|
||||
<LayoutSidebar
|
||||
className={styles.sidebar}
|
||||
headerHeight={HEADER_HEIGHT}
|
||||
style={{ flex: 0, zIndex: 50 }}
|
||||
>
|
||||
<ExtraNetworkSidebar headerHeight={HEADER_HEIGHT} />
|
||||
</LayoutSidebar>
|
||||
)}
|
||||
</LayoutMain>
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
export default Index;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { consola } from 'consola';
|
||||
import { PropsWithChildren, Suspense, memo, useEffect, useState } from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
|
@ -9,61 +10,60 @@ import { useAppStore } from '@/store';
|
|||
import manifest from './manifest';
|
||||
|
||||
export const Layouts = memo<PropsWithChildren>(({ children }) => {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const { setCurrentTab, onInit, storeLoading } = useAppStore(
|
||||
(st) => ({
|
||||
onInit: st.onInit,
|
||||
setCurrentTab: st.setCurrentTab,
|
||||
storeLoading: st.loading,
|
||||
}),
|
||||
shallow,
|
||||
);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const { setCurrentTab, onInit, storeLoading } = useAppStore(
|
||||
(st) => ({
|
||||
onInit: st.onInit,
|
||||
setCurrentTab: st.setCurrentTab,
|
||||
storeLoading: st.loading,
|
||||
}),
|
||||
shallow,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
console.time('🤯 Lobe Theme loading');
|
||||
onInit();
|
||||
onUiLoaded(() => {
|
||||
setLoading(false);
|
||||
console.timeEnd('🤯 Lobe Theme loading');
|
||||
});
|
||||
onUiTabChange(() => {
|
||||
setCurrentTab();
|
||||
});
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
onInit();
|
||||
onUiLoaded(() => {
|
||||
setLoading(false);
|
||||
consola.success('🤯 Lobe Theme loading');
|
||||
});
|
||||
onUiTabChange(() => {
|
||||
setCurrentTab();
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Suspense fallback="loading...">
|
||||
<Helmet>
|
||||
<link
|
||||
href="https://registry.npmmirror.com/@lobehub/assets-favicons/1.1.0/files/assets/apple-touch-icon.png"
|
||||
rel="apple-touch-icon"
|
||||
sizes="180x180"
|
||||
/>
|
||||
<link
|
||||
href="https://registry.npmmirror.com/@lobehub/assets-favicons/1.1.0/files/assets/favicon-32x32.png"
|
||||
rel="icon"
|
||||
sizes="32x32"
|
||||
type="image/png"
|
||||
/>
|
||||
<link
|
||||
href="https://registry.npmmirror.com/@lobehub/assets-favicons/1.1.0/files/assets/favicon-16x16.png"
|
||||
rel="icon"
|
||||
sizes="16x16"
|
||||
type="image/png"
|
||||
/>
|
||||
<link
|
||||
href="https://registry.npmmirror.com/@lobehub/assets-favicons/1.1.0/files/assets/site.webmanifest"
|
||||
rel="manifest"
|
||||
/>
|
||||
<meta content="Stable Diffusion · LobeHub" name="apple-mobile-web-app-title" />
|
||||
<meta content="Stable Diffusion · LobeHub" name="application-name" />
|
||||
<meta content="#000000" name="msapplication-TileColor" />
|
||||
<meta content="#000000" name="theme-color" />
|
||||
<link href={manifest} rel="manifest" />
|
||||
</Helmet>
|
||||
<Layout>{storeLoading === false && loading === false ? children : <Loading />}</Layout>
|
||||
</Suspense>
|
||||
);
|
||||
return (
|
||||
<Suspense fallback="loading...">
|
||||
<Helmet>
|
||||
<link
|
||||
href="https://registry.npmmirror.com/@lobehub/assets-favicons/1.1.0/files/assets/apple-touch-icon.png"
|
||||
rel="apple-touch-icon"
|
||||
sizes="180x180"
|
||||
/>
|
||||
<link
|
||||
href="https://registry.npmmirror.com/@lobehub/assets-favicons/1.1.0/files/assets/favicon-32x32.png"
|
||||
rel="icon"
|
||||
sizes="32x32"
|
||||
type="image/png"
|
||||
/>
|
||||
<link
|
||||
href="https://registry.npmmirror.com/@lobehub/assets-favicons/1.1.0/files/assets/favicon-16x16.png"
|
||||
rel="icon"
|
||||
sizes="16x16"
|
||||
type="image/png"
|
||||
/>
|
||||
<link
|
||||
href="https://registry.npmmirror.com/@lobehub/assets-favicons/1.1.0/files/assets/site.webmanifest"
|
||||
rel="manifest"
|
||||
/>
|
||||
<meta content="Stable Diffusion · LobeHub" name="apple-mobile-web-app-title" />
|
||||
<meta content="Stable Diffusion · LobeHub" name="application-name" />
|
||||
<meta content="#000000" name="msapplication-TileColor" />
|
||||
<meta content="#000000" name="theme-color" />
|
||||
<link href={manifest} rel="manifest" />
|
||||
</Helmet>
|
||||
<Layout>{storeLoading === false && loading === false ? children : <Loading />}</Layout>
|
||||
</Suspense>
|
||||
);
|
||||
});
|
||||
|
||||
export default Layouts;
|
||||
|
|
|
|||
|
|
@ -1,28 +1,28 @@
|
|||
const manifest = {
|
||||
background_color: '#000000',
|
||||
description:
|
||||
background_color: '#000000',
|
||||
description:
|
||||
'The modern theme for stable diffusion webui, exquisite interface design, highly customizable UI, and efficiency boosting features.',
|
||||
display: 'standalone',
|
||||
icons: [
|
||||
{
|
||||
sizes: '192x192',
|
||||
src: 'https://registry.npmmirror.com/@lobehub/assets-favicons/1.1.0/files/assets/android-chrome-192x192.png',
|
||||
type: 'image/png',
|
||||
},
|
||||
{
|
||||
sizes: '512x512',
|
||||
src: 'https://registry.npmmirror.com/@lobehub/assets-favicons/1.1.0/files/assets/android-chrome-512x512.png',
|
||||
type: 'image/png',
|
||||
},
|
||||
],
|
||||
id: '/',
|
||||
name: 'Stable Diffusion',
|
||||
orientation: 'portrait',
|
||||
scope: '/',
|
||||
short_name: 'Stable Diffusion',
|
||||
splash_pages: null,
|
||||
start_url: location.origin,
|
||||
theme_color: '#000000',
|
||||
display: 'standalone',
|
||||
icons: [
|
||||
{
|
||||
sizes: '192x192',
|
||||
src: 'https://registry.npmmirror.com/@lobehub/assets-favicons/1.1.0/files/assets/android-chrome-192x192.png',
|
||||
type: 'image/png',
|
||||
},
|
||||
{
|
||||
sizes: '512x512',
|
||||
src: 'https://registry.npmmirror.com/@lobehub/assets-favicons/1.1.0/files/assets/android-chrome-512x512.png',
|
||||
type: 'image/png',
|
||||
},
|
||||
],
|
||||
id: '/',
|
||||
name: 'Stable Diffusion',
|
||||
orientation: 'portrait',
|
||||
scope: '/',
|
||||
short_name: 'Stable Diffusion',
|
||||
splash_pages: null,
|
||||
start_url: location.origin,
|
||||
theme_color: '#000000',
|
||||
};
|
||||
|
||||
export default `data:application/manifest+json;base64,${btoa(JSON.stringify(manifest))}`;
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ import Index from './index';
|
|||
import Layout from './layout';
|
||||
|
||||
export default () => {
|
||||
return (
|
||||
<Layout>
|
||||
<Index />
|
||||
</Layout>
|
||||
);
|
||||
return (
|
||||
<Layout>
|
||||
<Index />
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@ import { createStyles } from 'antd-style';
|
|||
import { adjustHue } from 'polished';
|
||||
|
||||
export const useStyles = createStyles(
|
||||
(
|
||||
{ cx, css, stylish, token, isDarkMode },
|
||||
{ headerHeight, isPrimaryColor }: { headerHeight: number; isPrimaryColor: boolean },
|
||||
) => ({
|
||||
background: cx(
|
||||
stylish.gradientAnimation,
|
||||
isPrimaryColor &&
|
||||
(
|
||||
{ cx, css, stylish, token, isDarkMode },
|
||||
{ headerHeight, isPrimaryColor }: { headerHeight: number; isPrimaryColor: boolean },
|
||||
) => ({
|
||||
background: cx(
|
||||
stylish.gradientAnimation,
|
||||
isPrimaryColor &&
|
||||
css`
|
||||
background-image: linear-gradient(
|
||||
-45deg,
|
||||
|
|
@ -18,7 +18,7 @@ export const useStyles = createStyles(
|
|||
${adjustHue(-45, token.colorPrimary)}
|
||||
);
|
||||
`,
|
||||
css`
|
||||
css`
|
||||
pointer-events: none;
|
||||
|
||||
position: absolute !important;
|
||||
|
|
@ -32,8 +32,8 @@ export const useStyles = createStyles(
|
|||
opacity: 0.2;
|
||||
filter: blur(100px);
|
||||
`,
|
||||
),
|
||||
backgroundLite: css`
|
||||
),
|
||||
backgroundLite: css`
|
||||
pointer-events: none;
|
||||
|
||||
position: absolute !important;
|
||||
|
|
@ -50,19 +50,19 @@ export const useStyles = createStyles(
|
|||
transparent
|
||||
);
|
||||
`,
|
||||
panel: css`
|
||||
panel: css`
|
||||
.draggable-panel {
|
||||
border-style: dashed;
|
||||
}
|
||||
`,
|
||||
quicksettings: css`
|
||||
quicksettings: css`
|
||||
#quicksettings {
|
||||
align-items: start;
|
||||
padding: 16px !important;
|
||||
}
|
||||
`,
|
||||
sidebar: css`
|
||||
sidebar: css`
|
||||
height: calc(100vh - ${headerHeight}px);
|
||||
`,
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import {
|
||||
ActionIcon,
|
||||
DiscordIcon,
|
||||
Giscus as G,
|
||||
GradientButton,
|
||||
Icon,
|
||||
Modal,
|
||||
type ModalProps,
|
||||
ActionIcon,
|
||||
DiscordIcon,
|
||||
Giscus as G,
|
||||
GradientButton,
|
||||
Icon,
|
||||
Modal,
|
||||
type ModalProps,
|
||||
} from '@lobehub/ui';
|
||||
import { Button, Space } from 'antd';
|
||||
import { useTheme } from 'antd-style';
|
||||
|
|
@ -27,49 +27,49 @@ export interface GiscusProps {
|
|||
const repoName = homepage.replace('https://github.com/', '') as `${string}/${string}`;
|
||||
|
||||
const Giscus = memo<GiscusProps>(({ open, onCancel }) => {
|
||||
const setting = useAppStore(selectors.currentSetting, isEqual);
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Modal
|
||||
footer={false}
|
||||
onCancel={onCancel}
|
||||
open={open}
|
||||
title={
|
||||
<Flexbox align={'center'} gap={4} horizontal>
|
||||
<a href={'https://discord.gg/AYFPHvv2jT'} rel="noreferrer" target="_blank">
|
||||
<ActionIcon icon={DiscordIcon} title={'Discord'} />
|
||||
</a>
|
||||
<a href={homepage} rel="noreferrer" target="_blank">
|
||||
<ActionIcon icon={Github} title={repoName} />
|
||||
</a>
|
||||
<Space>
|
||||
{t('themeFeedback')}
|
||||
<VersionTag />
|
||||
</Space>
|
||||
</Flexbox>
|
||||
}
|
||||
>
|
||||
<Flexbox gap={32}>
|
||||
<Center
|
||||
gap={16}
|
||||
horizontal
|
||||
style={{
|
||||
background: theme.colorBgLayout,
|
||||
border: `1px solid ${theme.colorBorderSecondary}`,
|
||||
borderRadius: theme.borderRadiusLG,
|
||||
padding: '16px 0',
|
||||
}}
|
||||
const setting = useAppStore(selectors.currentSetting, isEqual);
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Modal
|
||||
footer={false}
|
||||
onCancel={onCancel}
|
||||
open={open}
|
||||
title={
|
||||
<Flexbox align={'center'} gap={4} horizontal>
|
||||
<a href={'https://discord.gg/AYFPHvv2jT'} rel="noreferrer" target="_blank">
|
||||
<ActionIcon icon={DiscordIcon} title={'Discord'} />
|
||||
</a>
|
||||
<a href={homepage} rel="noreferrer" target="_blank">
|
||||
<ActionIcon icon={Github} title={repoName} />
|
||||
</a>
|
||||
<Space>
|
||||
{t('modal.themeFeedback.title')}
|
||||
<VersionTag />
|
||||
</Space>
|
||||
</Flexbox>
|
||||
}
|
||||
>
|
||||
<Button icon={<Icon icon={DiscordIcon} />} size={'large'}>
|
||||
<Flexbox gap={32}>
|
||||
<Center
|
||||
gap={16}
|
||||
horizontal
|
||||
style={{
|
||||
background: theme.colorBgLayout,
|
||||
border: `1px solid ${theme.colorBorderSecondary}`,
|
||||
borderRadius: theme.borderRadiusLG,
|
||||
padding: '16px 0',
|
||||
}}
|
||||
>
|
||||
<Button icon={<Icon icon={DiscordIcon} />} size={'large'}>
|
||||
Join Discover
|
||||
</Button>
|
||||
<GradientButton icon={<Icon icon={Github} />}>LobeTheme Github</GradientButton>
|
||||
</Center>
|
||||
<G lang={setting.i18n} mapping="number" repo={repoName} repoId="R_kgDOJCPcNg" term="53" />
|
||||
</Flexbox>
|
||||
</Modal>
|
||||
);
|
||||
</Button>
|
||||
<GradientButton icon={<Icon icon={Github} />}>LobeTheme Github</GradientButton>
|
||||
</Center>
|
||||
<G lang={setting.i18n} mapping="number" repo={repoName} repoId="R_kgDOJCPcNg" term="53" />
|
||||
</Flexbox>
|
||||
</Modal>
|
||||
);
|
||||
});
|
||||
|
||||
export default Giscus;
|
||||
|
|
|
|||
|
|
@ -5,19 +5,19 @@ import { useTranslation } from 'react-i18next';
|
|||
import { Center, Flexbox } from 'react-layout-kit';
|
||||
|
||||
const Loading = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Flexbox height={'100vh'} width={'100%'}>
|
||||
<Center flex={1} gap={12} width={'100%'}>
|
||||
<Logo extra={'SD'} size={48} type={'combine'} />
|
||||
<Center gap={16} horizontal>
|
||||
<Icon icon={Loader2} spin />
|
||||
{t('appInitializing')}
|
||||
</Center>
|
||||
</Center>
|
||||
</Flexbox>
|
||||
);
|
||||
return (
|
||||
<Flexbox height={'100vh'} width={'100%'}>
|
||||
<Center flex={1} gap={12} width={'100%'}>
|
||||
<Logo extra={'SD'} size={48} type={'combine'} />
|
||||
<Center gap={16} horizontal>
|
||||
<Icon icon={Loader2} spin />
|
||||
{t('custom.initializing')}
|
||||
</Center>
|
||||
</Center>
|
||||
</Flexbox>
|
||||
);
|
||||
});
|
||||
|
||||
export default Loading;
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@ import { createStyles } from 'antd-style';
|
|||
import { adjustHue, rgba } from 'polished';
|
||||
|
||||
export const useStyles = createStyles(
|
||||
(
|
||||
{ css, stylish, cx, token },
|
||||
{ isPrimaryColor, liteAnimation }: { isPrimaryColor?: boolean; liteAnimation?: boolean },
|
||||
) => ({
|
||||
canvas: cx(
|
||||
stylish.gradientAnimation,
|
||||
isPrimaryColor &&
|
||||
(
|
||||
{ css, stylish, cx, token },
|
||||
{ isPrimaryColor, liteAnimation }: { isPrimaryColor?: boolean; liteAnimation?: boolean },
|
||||
) => ({
|
||||
canvas: cx(
|
||||
stylish.gradientAnimation,
|
||||
isPrimaryColor &&
|
||||
css`
|
||||
background-image: linear-gradient(
|
||||
-45deg,
|
||||
|
|
@ -18,7 +18,7 @@ export const useStyles = createStyles(
|
|||
${adjustHue(-45, token.colorPrimary)}
|
||||
);
|
||||
`,
|
||||
css`
|
||||
css`
|
||||
pointer-events: none;
|
||||
|
||||
position: absolute;
|
||||
|
|
@ -33,10 +33,10 @@ export const useStyles = createStyles(
|
|||
opacity: 0.2;
|
||||
filter: blur(100px);
|
||||
`,
|
||||
),
|
||||
container: cx(
|
||||
!liteAnimation && stylish.blur,
|
||||
css`
|
||||
),
|
||||
container: cx(
|
||||
!liteAnimation && stylish.blur,
|
||||
css`
|
||||
position: fixed;
|
||||
z-index: 9999;
|
||||
inset: 0;
|
||||
|
|
@ -50,11 +50,11 @@ export const useStyles = createStyles(
|
|||
|
||||
background: ${liteAnimation ? token.colorBgLayout : rgba(token.colorBgLayout, 0.5)};
|
||||
`,
|
||||
),
|
||||
icon: css`
|
||||
),
|
||||
icon: css`
|
||||
color: ${token.colorPrimary};
|
||||
`,
|
||||
inner: css`
|
||||
inner: css`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
|
|
@ -63,5 +63,5 @@ export const useStyles = createStyles(
|
|||
|
||||
width: min(50%, 580px);
|
||||
`,
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -10,25 +10,25 @@ export interface CustomLogoProps {
|
|||
}
|
||||
|
||||
const CustomLogo = memo<CustomLogoProps>(({ size = 32, style, logoCustomUrl, logoCustomTitle }) => {
|
||||
let customLogo = <LobeLogo size={size} style={style} />;
|
||||
let customLogo = <LobeLogo size={size} style={style} />;
|
||||
|
||||
if (logoCustomUrl) {
|
||||
if (logoCustomUrl.includes('http') || logoCustomUrl.includes('data')) {
|
||||
customLogo = <img alt="logo" src={logoCustomUrl} style={{ height: size, ...style }} />;
|
||||
} else {
|
||||
const pureEmoji = getEmoji(logoCustomUrl);
|
||||
if (pureEmoji) {
|
||||
customLogo = <FluentEmoji emoji={pureEmoji} size={size} style={style} />;
|
||||
}
|
||||
if (logoCustomUrl) {
|
||||
if (logoCustomUrl.includes('http') || logoCustomUrl.includes('data')) {
|
||||
customLogo = <img alt="logo" src={logoCustomUrl} style={{ height: size, ...style }} />;
|
||||
} else {
|
||||
const pureEmoji = getEmoji(logoCustomUrl);
|
||||
if (pureEmoji) {
|
||||
customLogo = <FluentEmoji emoji={pureEmoji} size={size} style={style} />;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Space align="center" size={size * 0.3}>
|
||||
{customLogo}
|
||||
<b style={{ fontSize: size * 0.6, whiteSpace: 'nowrap' }}>{logoCustomTitle}</b>
|
||||
</Space>
|
||||
);
|
||||
return (
|
||||
<Space align="center" size={size * 0.3}>
|
||||
{customLogo}
|
||||
<b style={{ fontSize: size * 0.6, whiteSpace: 'nowrap' }}>{logoCustomTitle}</b>
|
||||
</Space>
|
||||
);
|
||||
});
|
||||
|
||||
export default CustomLogo;
|
||||
|
|
|
|||
|
|
@ -9,13 +9,13 @@ export interface KitchenLogoProps {
|
|||
}
|
||||
|
||||
const KitchenLogo = memo<KitchenLogoProps>(({ size = 32, style, themeMode }) => {
|
||||
return (
|
||||
<img
|
||||
alt="logo"
|
||||
src={themeMode === 'dark' ? darkLogo : lightLogo}
|
||||
style={{ height: size, ...style }}
|
||||
/>
|
||||
);
|
||||
return (
|
||||
<img
|
||||
alt="logo"
|
||||
src={themeMode === 'dark' ? darkLogo : lightLogo}
|
||||
style={{ height: size, ...style }}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export default KitchenLogo;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { Logo as LobeLogo } from '@lobehub/ui';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { type CSSProperties, memo } from 'react';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import { selectors, useAppStore } from '@/store';
|
||||
|
||||
|
|
@ -14,25 +13,25 @@ export interface LogoProps {
|
|||
}
|
||||
|
||||
const Logo = memo<LogoProps>(({ size = 32, style }) => {
|
||||
const setting = useAppStore(selectors.currentSetting, isEqual);
|
||||
const themeMode = useAppStore(selectors.themeMode, shallow);
|
||||
const setting = useAppStore(selectors.currentSetting, isEqual);
|
||||
const themeMode = useAppStore(selectors.themeMode);
|
||||
|
||||
if (setting.logoType === 'kitchen') {
|
||||
return <KitchenLogo size={size * 0.75} style={style} themeMode={themeMode} />;
|
||||
}
|
||||
if (setting.logoType === 'kitchen') {
|
||||
return <KitchenLogo size={size * 0.75} style={style} themeMode={themeMode} />;
|
||||
}
|
||||
|
||||
if (setting.logoType === 'custom') {
|
||||
return (
|
||||
<CustomLogo
|
||||
logoCustomTitle={setting.logoCustomTitle}
|
||||
logoCustomUrl={setting.logoCustomUrl}
|
||||
size={size}
|
||||
style={style}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (setting.logoType === 'custom') {
|
||||
return (
|
||||
<CustomLogo
|
||||
logoCustomTitle={setting.logoCustomTitle}
|
||||
logoCustomUrl={setting.logoCustomUrl}
|
||||
size={size}
|
||||
style={style}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return <LobeLogo extra="SD" size={size} style={style} type="combine" />;
|
||||
return <LobeLogo extra="SD" size={size} style={style} type="combine" />;
|
||||
});
|
||||
|
||||
export default Logo;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { consola } from 'consola';
|
||||
import { memo, useCallback, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
|
|
@ -10,66 +11,66 @@ interface PromptProps {
|
|||
}
|
||||
|
||||
const Prompt = memo<PromptProps>(({ type }) => {
|
||||
const [tags, setTags] = useState<TagItem[]>([]);
|
||||
const { styles } = useStyles();
|
||||
const { t } = useTranslation();
|
||||
const [tags, setTags] = useState<TagItem[]>([]);
|
||||
const { styles } = useStyles();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const id =
|
||||
const id =
|
||||
type === 'positive' ? "[id$='2img_prompt'] textarea" : "[id$='2img_neg_prompt'] textarea";
|
||||
|
||||
const getValue = useCallback(() => {
|
||||
try {
|
||||
const textarea = get_uiCurrentTabContent().querySelector(id) as HTMLTextAreaElement;
|
||||
if (textarea) setTags(formatPrompt(textarea.value));
|
||||
} catch (error) {
|
||||
console.debug(error);
|
||||
}
|
||||
}, []);
|
||||
const getValue = useCallback(() => {
|
||||
try {
|
||||
const textarea = get_uiCurrentTabContent().querySelector(id) as HTMLTextAreaElement;
|
||||
if (textarea) setTags(formatPrompt(textarea.value));
|
||||
} catch (error) {
|
||||
consola.error('🤯 [prompt]', error);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const setValue = useCallback(() => {
|
||||
try {
|
||||
const newValue = tags.map((t) => t.text).join(', ');
|
||||
const textarea = get_uiCurrentTabContent().querySelector(id) as HTMLTextAreaElement;
|
||||
if (textarea) textarea.value = newValue;
|
||||
updateInput(textarea);
|
||||
} catch (error) {
|
||||
console.debug(error);
|
||||
}
|
||||
}, [tags, type]);
|
||||
const setValue = useCallback(() => {
|
||||
try {
|
||||
const newValue = tags.map((t) => t.text).join(', ');
|
||||
const textarea = get_uiCurrentTabContent().querySelector(id) as HTMLTextAreaElement;
|
||||
if (textarea) textarea.value = newValue;
|
||||
updateInput(textarea);
|
||||
} catch (error) {
|
||||
consola.error('🤯 [prompt]', error);
|
||||
}
|
||||
}, [tags, type]);
|
||||
|
||||
const setCurrentValue = useCallback((currentTags: TagItem[]) => {
|
||||
try {
|
||||
const textarea = get_uiCurrentTabContent().querySelector(id) as HTMLTextAreaElement;
|
||||
if (textarea) textarea.value = currentTags.map((t) => t.text).join(', ');
|
||||
updateInput(textarea);
|
||||
} catch (error) {
|
||||
console.debug(error);
|
||||
}
|
||||
}, []);
|
||||
const setCurrentValue = useCallback((currentTags: TagItem[]) => {
|
||||
try {
|
||||
const textarea = get_uiCurrentTabContent().querySelector(id) as HTMLTextAreaElement;
|
||||
if (textarea) textarea.value = currentTags.map((t) => t.text).join(', ');
|
||||
updateInput(textarea);
|
||||
} catch (error) {
|
||||
consola.error('🤯 [prompt]', error);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={styles.promptView}>
|
||||
<TagList setTags={setTags} setValue={setCurrentValue} tags={tags} type={type} />
|
||||
<div className={styles.buttonGroup}>
|
||||
<button
|
||||
className="lg secondary gradio-button tool svelte-1ipelgc"
|
||||
onClick={getValue}
|
||||
title={t('loadPrompt')}
|
||||
type="button"
|
||||
>
|
||||
return (
|
||||
<div className={styles.promptView}>
|
||||
<TagList setTags={setTags} setValue={setCurrentValue} tags={tags} type={type} />
|
||||
<div className={styles.buttonGroup}>
|
||||
<button
|
||||
className="lg secondary gradio-button tool svelte-1ipelgc"
|
||||
onClick={getValue}
|
||||
title={t('prompt.load')}
|
||||
type="button"
|
||||
>
|
||||
🔄
|
||||
</button>
|
||||
<button
|
||||
className="lg secondary gradio-button tool svelte-1ipelgc"
|
||||
onClick={setValue}
|
||||
title={t('setPrompt')}
|
||||
type="button"
|
||||
>
|
||||
</button>
|
||||
<button
|
||||
className="lg secondary gradio-button tool svelte-1ipelgc"
|
||||
onClick={setValue}
|
||||
title={t('prompt.set')}
|
||||
type="button"
|
||||
>
|
||||
➡️
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export default Prompt;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { consola } from 'consola';
|
||||
import { type FC, memo, useCallback, useEffect, useState } from 'react';
|
||||
import { WithContext, ReactTagsProps as WithContextProps } from 'react-tag-input';
|
||||
|
||||
|
|
@ -21,8 +22,8 @@ interface ReactTagsProps extends WithContextProps {
|
|||
const ReactTags: FC<ReactTagsProps> = WithContext;
|
||||
|
||||
const KeyCodes = {
|
||||
comma: 188,
|
||||
enter: 13,
|
||||
comma: 188,
|
||||
enter: 13,
|
||||
};
|
||||
|
||||
const delimiters = [KeyCodes.comma, KeyCodes.enter];
|
||||
|
|
@ -35,86 +36,85 @@ interface TagListProps {
|
|||
}
|
||||
|
||||
const TagList = memo<TagListProps>(({ tags, setTags, type, setValue }) => {
|
||||
const id = `${type}_tag_editor`;
|
||||
const [bind, setBind] = useState(false);
|
||||
const { styles } = useStyles(type);
|
||||
const handleDelete = useCallback(
|
||||
(index_: number) => {
|
||||
const newTags = tags.filter((tag, index) => index !== index_);
|
||||
setTags(newTags);
|
||||
setValue(newTags);
|
||||
},
|
||||
[tags],
|
||||
);
|
||||
const id = `${type}_tag_editor`;
|
||||
const [bind, setBind] = useState(false);
|
||||
const { styles } = useStyles(type);
|
||||
const handleDelete = useCallback(
|
||||
(index_: number) => {
|
||||
const newTags = tags.filter((tag, index) => index !== index_);
|
||||
setTags(newTags);
|
||||
setValue(newTags);
|
||||
},
|
||||
[tags],
|
||||
);
|
||||
|
||||
const handleAddition = useCallback(
|
||||
(tag: TagItem) => {
|
||||
const newTags = [...tags, genTagType(tag)];
|
||||
setTags(newTags);
|
||||
setValue(newTags);
|
||||
},
|
||||
[tags],
|
||||
);
|
||||
const handleAddition = useCallback(
|
||||
(tag: TagItem) => {
|
||||
const newTags = [...tags, genTagType(tag)];
|
||||
setTags(newTags);
|
||||
setValue(newTags);
|
||||
},
|
||||
[tags],
|
||||
);
|
||||
|
||||
const handleDrag = useCallback(
|
||||
(tag: TagItem, currentPos: number, newPos: number) => {
|
||||
const newTags = [...tags];
|
||||
newTags.splice(currentPos, 1);
|
||||
newTags.splice(newPos, 0, genTagType(tag));
|
||||
setTags(newTags);
|
||||
setValue(newTags);
|
||||
},
|
||||
[tags],
|
||||
);
|
||||
const handleDrag = useCallback(
|
||||
(tag: TagItem, currentPos: number, newPos: number) => {
|
||||
const newTags = [...tags];
|
||||
newTags.splice(currentPos, 1);
|
||||
newTags.splice(newPos, 0, genTagType(tag));
|
||||
setTags(newTags);
|
||||
setValue(newTags);
|
||||
},
|
||||
[tags],
|
||||
);
|
||||
|
||||
const handleTagUpdate = useCallback(
|
||||
(index: number, tag: TagItem) => {
|
||||
const newTags = [...tags];
|
||||
newTags[index] = genTagType(tag);
|
||||
setTags(newTags);
|
||||
setValue(newTags);
|
||||
},
|
||||
[tags],
|
||||
);
|
||||
const handleTagUpdate = useCallback(
|
||||
(index: number, tag: TagItem) => {
|
||||
const newTags = [...tags];
|
||||
newTags[index] = genTagType(tag);
|
||||
setTags(newTags);
|
||||
setValue(newTags);
|
||||
},
|
||||
[tags],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
if (!addAutocompleteToArea || bind) return;
|
||||
let retryTimes = 0;
|
||||
const bindInterval = setInterval(() => {
|
||||
console.time('🤯 [promptTagEditor] inject');
|
||||
if (bind || retryTimes > 10) {
|
||||
const inputDom = document.querySelector(`#${id}`) as HTMLInputElement;
|
||||
if (inputDom) {
|
||||
setBind(true);
|
||||
addAutocompleteToArea(inputDom);
|
||||
clearInterval(bindInterval);
|
||||
console.timeEnd('🤯 [promptTagEditor] inject');
|
||||
}
|
||||
useEffect(() => {
|
||||
try {
|
||||
if (!addAutocompleteToArea || bind) return;
|
||||
let retryTimes = 0;
|
||||
const bindInterval = setInterval(() => {
|
||||
if (bind || retryTimes > 10) {
|
||||
const inputDom = document.querySelector(`#${id}`) as HTMLInputElement;
|
||||
if (inputDom) {
|
||||
setBind(true);
|
||||
addAutocompleteToArea(inputDom);
|
||||
clearInterval(bindInterval);
|
||||
consola.success('🤯 [promptTagEditor] inject');
|
||||
}
|
||||
}
|
||||
retryTimes++;
|
||||
}, 1000);
|
||||
} catch (error) {
|
||||
consola.error('🤯 [promptTagEditor]', error);
|
||||
}
|
||||
retryTimes++;
|
||||
}, 1000);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}, [bind]);
|
||||
}, [bind]);
|
||||
|
||||
return (
|
||||
<div className={styles}>
|
||||
<ReactTags
|
||||
delimiters={delimiters}
|
||||
editable
|
||||
handleAddition={handleAddition}
|
||||
handleDelete={handleDelete}
|
||||
handleDrag={handleDrag}
|
||||
id={id}
|
||||
inline
|
||||
inputFieldPosition="bottom"
|
||||
onTagUpdate={handleTagUpdate}
|
||||
tags={tags}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div className={styles}>
|
||||
<ReactTags
|
||||
delimiters={delimiters}
|
||||
editable
|
||||
handleAddition={handleAddition}
|
||||
handleDelete={handleDelete}
|
||||
handleDrag={handleDrag}
|
||||
id={id}
|
||||
inline
|
||||
inputFieldPosition="bottom"
|
||||
onTagUpdate={handleTagUpdate}
|
||||
tags={tags}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export default TagList;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { createStyles } from 'antd-style';
|
||||
|
||||
export const useStyles = createStyles(
|
||||
({ css, token }, type: 'positive' | 'negative') => css`
|
||||
({ css, token }, type: 'positive' | 'negative') => css`
|
||||
.autocompleteResults {
|
||||
left: 16px !important;
|
||||
min-width: 200px !important;
|
||||
|
|
|
|||
|
|
@ -6,16 +6,16 @@ import { useStyles } from '@/components/PromptEditor/style';
|
|||
import Prompt from './Prompt';
|
||||
|
||||
const PromptEditor = memo(() => {
|
||||
const { styles } = useStyles();
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className={styles.view}>
|
||||
<span style={{ marginBottom: -10 }}>{t('positive')}</span>
|
||||
<Prompt type="positive" />
|
||||
<span style={{ marginBottom: -10 }}>{t('negative')}</span>
|
||||
<Prompt type="negative" />
|
||||
</div>
|
||||
);
|
||||
const { styles } = useStyles();
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className={styles.view}>
|
||||
<span style={{ marginBottom: -10 }}>{t('prompt.positive')}</span>
|
||||
<Prompt type="positive" />
|
||||
<span style={{ marginBottom: -10 }}>{t('prompt.negative')}</span>
|
||||
<Prompt type="negative" />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export default PromptEditor;
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
import { createStyles } from 'antd-style';
|
||||
|
||||
export const useStyles = createStyles(({ css }) => ({
|
||||
buttonGroup: css`
|
||||
buttonGroup: css`
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
`,
|
||||
promptView: css`
|
||||
promptView: css`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
`,
|
||||
view: css`
|
||||
view: css`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
|
|
|
|||
|
|
@ -3,31 +3,31 @@ import { Converter } from '@/scripts/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;
|
||||
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 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 }));
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,26 +7,26 @@ import { homepage } from '@/../package.json';
|
|||
import { useAppStore } from '@/store';
|
||||
|
||||
const VersionTag = memo<TagProps>((props) => {
|
||||
const { version, latestVersion } = useAppStore(
|
||||
(st) => ({ latestVersion: st.latestVersion, version: st.version }),
|
||||
shallow,
|
||||
);
|
||||
const { version, latestVersion } = useAppStore(
|
||||
(st) => ({ latestVersion: st.latestVersion, version: st.version }),
|
||||
shallow,
|
||||
);
|
||||
|
||||
const isLatest = semver.gte(version, latestVersion);
|
||||
const isLatest = semver.gte(version, latestVersion);
|
||||
|
||||
return (
|
||||
<a href={homepage} rel="noreferrer" target="_blank">
|
||||
{isLatest ? (
|
||||
<Tag color="success" {...props}>
|
||||
return (
|
||||
<a href={homepage} rel="noreferrer" target="_blank">
|
||||
{isLatest ? (
|
||||
<Tag color="success" {...props}>
|
||||
v{version}
|
||||
</Tag>
|
||||
) : (
|
||||
<Tag color="warning" {...props}>
|
||||
</Tag>
|
||||
) : (
|
||||
<Tag color="warning" {...props}>
|
||||
v{version} / latest v{latestVersion}
|
||||
</Tag>
|
||||
)}
|
||||
</a>
|
||||
);
|
||||
</Tag>
|
||||
)}
|
||||
</a>
|
||||
);
|
||||
});
|
||||
|
||||
export default VersionTag;
|
||||
|
|
|
|||
|
|
@ -1,43 +1,47 @@
|
|||
import { consola } from 'consola';
|
||||
import { memo, useEffect } from 'react';
|
||||
|
||||
const Preview = memo(() => {
|
||||
useEffect(() => {
|
||||
console.time('🤯 [layout] inject - Split Previewer');
|
||||
// tab_txt2img
|
||||
const txt2imgToprow = gradioApp().querySelector('#txt2img_toprow') as HTMLDivElement;
|
||||
const txt2imgSettings = gradioApp().querySelector('#txt2img_settings') as HTMLDivElement;
|
||||
const txt2imgGenerate = gradioApp().querySelector('#txt2img_generate_box') as HTMLDivElement;
|
||||
const txt2imgPreview = gradioApp().querySelector(
|
||||
'#txt2img_gallery_container',
|
||||
) as HTMLDivElement;
|
||||
if (txt2imgToprow && txt2imgSettings && txt2imgGenerate && txt2imgPreview) {
|
||||
txt2imgSettings.prepend(txt2imgToprow);
|
||||
txt2imgPreview.prepend(txt2imgGenerate);
|
||||
}
|
||||
// tab_img2img
|
||||
const img2imgToprow = gradioApp().querySelector('#img2img_toprow') as HTMLDivElement;
|
||||
const img2imgSettings = gradioApp().querySelector('#img2img_settings') as HTMLDivElement;
|
||||
const img2imgGenerate = gradioApp().querySelector('#img2img_generate_box') as HTMLDivElement;
|
||||
const img2imgPreview = gradioApp().querySelector(
|
||||
'#img2img_gallery_container',
|
||||
) as HTMLDivElement;
|
||||
if (img2imgSettings && img2imgToprow && img2imgGenerate && img2imgPreview) {
|
||||
img2imgSettings.prepend(img2imgToprow);
|
||||
img2imgPreview.prepend(img2imgGenerate);
|
||||
}
|
||||
useEffect(() => {
|
||||
try {
|
||||
// tab_txt2img
|
||||
const txt2imgToprow = gradioApp().querySelector('#txt2img_toprow') as HTMLDivElement;
|
||||
const txt2imgSettings = gradioApp().querySelector('#txt2img_settings') as HTMLDivElement;
|
||||
const txt2imgGenerate = gradioApp().querySelector('#txt2img_generate_box') as HTMLDivElement;
|
||||
const txt2imgPreview = gradioApp().querySelector(
|
||||
'#txt2img_gallery_container',
|
||||
) as HTMLDivElement;
|
||||
if (txt2imgToprow && txt2imgSettings && txt2imgGenerate && txt2imgPreview) {
|
||||
txt2imgSettings.prepend(txt2imgToprow);
|
||||
txt2imgPreview.prepend(txt2imgGenerate);
|
||||
}
|
||||
// tab_img2img
|
||||
const img2imgToprow = gradioApp().querySelector('#img2img_toprow') as HTMLDivElement;
|
||||
const img2imgSettings = gradioApp().querySelector('#img2img_settings') as HTMLDivElement;
|
||||
const img2imgGenerate = gradioApp().querySelector('#img2img_generate_box') as HTMLDivElement;
|
||||
const img2imgPreview = gradioApp().querySelector(
|
||||
'#img2img_gallery_container',
|
||||
) as HTMLDivElement;
|
||||
if (img2imgSettings && img2imgToprow && img2imgGenerate && img2imgPreview) {
|
||||
img2imgSettings.prepend(img2imgToprow);
|
||||
img2imgPreview.prepend(img2imgGenerate);
|
||||
}
|
||||
|
||||
// extras_img2img
|
||||
const extrasGenerate = gradioApp().querySelector('#extras_generate') as HTMLDivElement;
|
||||
const extrasPreview = gradioApp().querySelector('#extras_results') as HTMLDivElement;
|
||||
if (extrasGenerate && extrasPreview) {
|
||||
(extrasPreview?.parentNode as HTMLDivElement).id = '#extras_gallery_container';
|
||||
extrasPreview.prepend(extrasGenerate);
|
||||
}
|
||||
// extras_img2img
|
||||
const extrasGenerate = gradioApp().querySelector('#extras_generate') as HTMLDivElement;
|
||||
const extrasPreview = gradioApp().querySelector('#extras_results') as HTMLDivElement;
|
||||
if (extrasGenerate && extrasPreview) {
|
||||
(extrasPreview?.parentNode as HTMLDivElement).id = '#extras_gallery_container';
|
||||
extrasPreview.prepend(extrasGenerate);
|
||||
}
|
||||
|
||||
console.timeEnd('🤯 [layout] inject - Split Previewer');
|
||||
}, []);
|
||||
consola.success('🤯 [layout] inject - Split Previewer');
|
||||
} catch (error) {
|
||||
consola.error('🤯 [layout] inject - Split Previewer', error);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
return null;
|
||||
});
|
||||
|
||||
export default Preview;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { useResponsive } from 'antd-style';
|
||||
import { consola } from 'consola';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { memo, useEffect, useRef } from 'react';
|
||||
|
||||
|
|
@ -10,63 +11,66 @@ import SplitView from './SplitView';
|
|||
import { useStyles } from './style';
|
||||
|
||||
const Content = memo<DivProps>(({ className, ...props }) => {
|
||||
const mainReference = useRef<HTMLDivElement>(null);
|
||||
const { mobile } = useResponsive();
|
||||
const setting = useAppStore(selectors.currentSetting, isEqual);
|
||||
const mainReference = useRef<HTMLDivElement>(null);
|
||||
const { mobile } = useResponsive();
|
||||
const setting = useAppStore(selectors.currentSetting, isEqual);
|
||||
|
||||
const { cx, styles } = useStyles({
|
||||
isPromptResizable: setting.promptTextareaType === 'resizable',
|
||||
layoutSplitPreview: setting.layoutSplitPreview,
|
||||
});
|
||||
const { cx, styles } = useStyles({
|
||||
isPromptResizable: setting.promptTextareaType === 'resizable',
|
||||
layoutSplitPreview: setting.layoutSplitPreview,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
console.time('🤯 [layout] inject - Content');
|
||||
// Content
|
||||
const main = gradioApp().querySelector('.app');
|
||||
if (main) {
|
||||
mainReference.current?.append(main);
|
||||
}
|
||||
useEffect(() => {
|
||||
try {
|
||||
// Content
|
||||
const main = gradioApp().querySelector('.app');
|
||||
if (main) {
|
||||
mainReference.current?.append(main);
|
||||
}
|
||||
|
||||
// remove prompt scroll-hide
|
||||
const textares = gradioApp().querySelectorAll(
|
||||
`[id$="_prompt_container"] textarea`,
|
||||
) as NodeListOf<HTMLTextAreaElement>;
|
||||
if (textares) {
|
||||
for (const textarea of textares) {
|
||||
textarea.classList.remove('scroll-hide');
|
||||
textarea.style.height = 'auto';
|
||||
}
|
||||
}
|
||||
// remove prompt scroll-hide
|
||||
const textares = gradioApp().querySelectorAll(
|
||||
`[id$="_prompt_container"] textarea`,
|
||||
) as NodeListOf<HTMLTextAreaElement>;
|
||||
if (textares) {
|
||||
for (const textarea of textares) {
|
||||
textarea.classList.remove('scroll-hide');
|
||||
textarea.style.height = 'auto';
|
||||
}
|
||||
}
|
||||
|
||||
// textarea
|
||||
const interrogate = gradioApp().querySelector(
|
||||
'#img2img_toprow .interrogate-col',
|
||||
) as HTMLDivElement;
|
||||
const actions = gradioApp().querySelector('#img2img_actions_column') as HTMLDivElement;
|
||||
if (interrogate && actions) {
|
||||
actions.append(interrogate);
|
||||
}
|
||||
// textarea
|
||||
const interrogate = gradioApp().querySelector(
|
||||
'#img2img_toprow .interrogate-col',
|
||||
) as HTMLDivElement;
|
||||
const actions = gradioApp().querySelector('#img2img_actions_column') as HTMLDivElement;
|
||||
if (interrogate && actions) {
|
||||
actions.append(interrogate);
|
||||
}
|
||||
|
||||
formatPrompt();
|
||||
console.timeEnd('🤯 [layout] inject - Content');
|
||||
}, []);
|
||||
formatPrompt();
|
||||
consola.success('🤯 [layout] inject - Content');
|
||||
} catch (error) {
|
||||
consola.error('🤯 [layout] inject - Content', error);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={cx(
|
||||
styles.container,
|
||||
styles.textares,
|
||||
styles.text2img,
|
||||
setting.layoutSplitPreview && styles.splitView,
|
||||
className,
|
||||
)}
|
||||
ref={mainReference}
|
||||
{...props}
|
||||
/>
|
||||
{setting.layoutSplitPreview && mobile === false && <SplitView />}
|
||||
</>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={cx(
|
||||
styles.container,
|
||||
styles.textares,
|
||||
styles.text2img,
|
||||
setting.layoutSplitPreview && styles.splitView,
|
||||
className,
|
||||
)}
|
||||
ref={mainReference}
|
||||
{...props}
|
||||
/>
|
||||
{setting.layoutSplitPreview && mobile === false && <SplitView />}
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
export default Content;
|
||||
|
|
|
|||
|
|
@ -3,15 +3,15 @@ import { createStyles } from 'antd-style';
|
|||
const TEXT2IMG_PROMPT_HEIGHT = 74;
|
||||
const IMG2IMG_PROMPT_HEIGHT = 98;
|
||||
export const useStyles = createStyles(
|
||||
(
|
||||
{ css, token, stylish, isDarkMode, responsive },
|
||||
{
|
||||
isPromptResizable,
|
||||
layoutSplitPreview,
|
||||
}: { isPromptResizable: boolean; layoutSplitPreview: boolean },
|
||||
) => {
|
||||
return {
|
||||
container: css`
|
||||
(
|
||||
{ css, token, stylish, isDarkMode, responsive },
|
||||
{
|
||||
isPromptResizable,
|
||||
layoutSplitPreview,
|
||||
}: { isPromptResizable: boolean; layoutSplitPreview: boolean },
|
||||
) => {
|
||||
return {
|
||||
container: css`
|
||||
position: relative;
|
||||
flex: 1;
|
||||
min-width: ${layoutSplitPreview ? '200px' : '0'};
|
||||
|
|
@ -191,7 +191,7 @@ export const useStyles = createStyles(
|
|||
}
|
||||
}
|
||||
`,
|
||||
splitView: css`
|
||||
splitView: css`
|
||||
#txt2img_toprow,
|
||||
#img2img_toprow {
|
||||
flex-direction: column !important;
|
||||
|
|
@ -199,7 +199,7 @@ export const useStyles = createStyles(
|
|||
background: transparent !important;
|
||||
}
|
||||
`,
|
||||
text2img: css`
|
||||
text2img: css`
|
||||
button[id$='_generate'] {
|
||||
height: var(--button-lg-height) !important;
|
||||
min-height: var(--button-lg-height) !important;
|
||||
|
|
@ -364,7 +364,7 @@ export const useStyles = createStyles(
|
|||
box-shadow: none;
|
||||
}
|
||||
`,
|
||||
textares: css`
|
||||
textares: css`
|
||||
[id$='2img_prompt'],
|
||||
[id$='2img_neg_prompt'] {
|
||||
textarea {
|
||||
|
|
@ -452,6 +452,6 @@ export const useStyles = createStyles(
|
|||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
},
|
||||
};
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,136 +1,141 @@
|
|||
import { ActionIcon, DraggablePanelBody, DraggablePanelFooter } from '@lobehub/ui';
|
||||
import { useTimeout } from 'ahooks';
|
||||
import { Skeleton, Slider } from 'antd';
|
||||
import { consola } from 'consola';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { ZoomIn, ZoomOut } from 'lucide-react';
|
||||
import { memo, useEffect, useRef, useState } from 'react';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import { useStyles } from '@/features/ExtraNetworkSidebar/style';
|
||||
import civitaiHelperFix from '@/scripts/civitaiHelperFix';
|
||||
import { selectors, useAppStore } from '@/store';
|
||||
|
||||
const Inner = memo(() => {
|
||||
const txt2imgExtraNetworkSidebarReference = useRef<HTMLDivElement>(null);
|
||||
const img2imgExtraNetworkSidebarReference = useRef<HTMLDivElement>(null);
|
||||
const [extraLoading, setExtraLoading] = useState(true);
|
||||
const setting = useAppStore(selectors.currentSetting, isEqual);
|
||||
const currentTab = useAppStore(selectors.currentTab, shallow);
|
||||
const [size, setSize] = useState<number>(setting.extraNetworkCardSize || 86);
|
||||
const { styles } = useStyles({ size });
|
||||
const txt2imgExtraNetworkSidebarReference = useRef<HTMLDivElement>(null);
|
||||
const img2imgExtraNetworkSidebarReference = useRef<HTMLDivElement>(null);
|
||||
const [extraLoading, setExtraLoading] = useState(true);
|
||||
const setting = useAppStore(selectors.currentSetting, isEqual);
|
||||
const currentTab = useAppStore(selectors.currentTab);
|
||||
const [size, setSize] = useState<number>(setting.extraNetworkCardSize || 86);
|
||||
const { styles } = useStyles({ size });
|
||||
|
||||
useEffect(() => {
|
||||
console.time('🤯 [layout] inject - ExtraNetworkSidebar');
|
||||
if (setting.enableExtraNetworkSidebar) {
|
||||
const image2imageExtraNetworkButton = gradioApp().querySelectorAll(
|
||||
'#txt2img_extra_tabs > .tab-nav > button',
|
||||
)[1] as HTMLButtonElement;
|
||||
const text2imageExtraNetworkButton = gradioApp().querySelectorAll(
|
||||
'#img2img_extra_tabs > .tab-nav > button',
|
||||
)[1] as HTMLButtonElement;
|
||||
useEffect(() => {
|
||||
try {
|
||||
if (setting.enableExtraNetworkSidebar) {
|
||||
const image2imageExtraNetworkButton = gradioApp().querySelectorAll(
|
||||
'#txt2img_extra_tabs > .tab-nav > button',
|
||||
)[1] as HTMLButtonElement;
|
||||
const text2imageExtraNetworkButton = gradioApp().querySelectorAll(
|
||||
'#img2img_extra_tabs > .tab-nav > button',
|
||||
)[1] as HTMLButtonElement;
|
||||
|
||||
if (image2imageExtraNetworkButton) {
|
||||
image2imageExtraNetworkButton.click();
|
||||
}
|
||||
if (text2imageExtraNetworkButton) {
|
||||
text2imageExtraNetworkButton.click();
|
||||
}
|
||||
if (image2imageExtraNetworkButton) {
|
||||
image2imageExtraNetworkButton.click();
|
||||
}
|
||||
if (text2imageExtraNetworkButton) {
|
||||
text2imageExtraNetworkButton.click();
|
||||
}
|
||||
|
||||
const txt2imgTab = gradioApp().querySelector('div#tab_txt2img') as HTMLDivElement;
|
||||
const txt2imgExtraNetworks = gradioApp().querySelector(
|
||||
'div#txt2img_extra_tabs',
|
||||
) as HTMLDivElement;
|
||||
const txt2imgRender = txt2imgExtraNetworks.querySelectorAll(
|
||||
'div.tabitem.gradio-tabitem',
|
||||
)[0] as HTMLDivElement;
|
||||
const txt2imgTab = gradioApp().querySelector('div#tab_txt2img') as HTMLDivElement;
|
||||
const txt2imgExtraNetworks = gradioApp().querySelector(
|
||||
'div#txt2img_extra_tabs',
|
||||
) as HTMLDivElement;
|
||||
const txt2imgRender = txt2imgExtraNetworks.querySelectorAll(
|
||||
'div.tabitem.gradio-tabitem',
|
||||
)[0] as HTMLDivElement;
|
||||
|
||||
const img2imgTab = gradioApp().querySelector('div#tab_img2img');
|
||||
const img2imgExtraNetworks = gradioApp().querySelector(
|
||||
'div#img2img_extra_tabs',
|
||||
) as HTMLDivElement;
|
||||
const img2imgRender = img2imgExtraNetworks.querySelectorAll(
|
||||
'div.tabitem.gradio-tabitem',
|
||||
)[0] as HTMLDivElement;
|
||||
const img2imgTab = gradioApp().querySelector('div#tab_img2img');
|
||||
const img2imgExtraNetworks = gradioApp().querySelector(
|
||||
'div#img2img_extra_tabs',
|
||||
) as HTMLDivElement;
|
||||
const img2imgRender = img2imgExtraNetworks.querySelectorAll(
|
||||
'div.tabitem.gradio-tabitem',
|
||||
)[0] as HTMLDivElement;
|
||||
|
||||
if (txt2imgExtraNetworks && img2imgExtraNetworks) {
|
||||
txt2imgExtraNetworkSidebarReference.current?.append(txt2imgExtraNetworks);
|
||||
txt2imgRender.id = 'txt2img_render';
|
||||
txt2imgTab?.append(txt2imgRender);
|
||||
if (txt2imgExtraNetworks && img2imgExtraNetworks) {
|
||||
txt2imgExtraNetworkSidebarReference.current?.append(txt2imgExtraNetworks);
|
||||
txt2imgRender.id = 'txt2img_render';
|
||||
txt2imgTab?.append(txt2imgRender);
|
||||
|
||||
img2imgExtraNetworkSidebarReference.current?.append(img2imgExtraNetworks);
|
||||
img2imgRender.id = 'img2img_render';
|
||||
img2imgTab?.append(img2imgRender);
|
||||
}
|
||||
if (document.querySelector('.extra-network-cards')) {
|
||||
civitaiHelperFix();
|
||||
setExtraLoading(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
console.timeEnd('🤯 [layout] inject - ExtraNetworkSidebar');
|
||||
}, []);
|
||||
img2imgExtraNetworkSidebarReference.current?.append(img2imgExtraNetworks);
|
||||
img2imgRender.id = 'img2img_render';
|
||||
img2imgTab?.append(img2imgRender);
|
||||
}
|
||||
if (document.querySelector('.extra-network-cards')) {
|
||||
civitaiHelperFix();
|
||||
setExtraLoading(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
consola.success('🤯 [layout] inject - ExtraNetworkSidebar');
|
||||
} catch (error) {
|
||||
consola.error('🤯 [layout] inject - ExtraNetworkSidebar', error);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useTimeout(() => {
|
||||
console.time('🤯 [extranetwork] force reload');
|
||||
const t2indexButton = document.querySelector('#txt2img_extra_refresh') as HTMLButtonElement;
|
||||
const index2indexButton = document.querySelector('#img2img_extra_refresh') as HTMLButtonElement;
|
||||
t2indexButton.click();
|
||||
index2indexButton.click();
|
||||
setExtraLoading(false);
|
||||
try {
|
||||
const civitaiText2ImgButton = document.querySelector('#txt2img_extra_refresh')
|
||||
?.nextSibling as HTMLButtonElement;
|
||||
if (civitaiText2ImgButton) {
|
||||
civitaiText2ImgButton.onclick = civitaiHelperFix;
|
||||
}
|
||||
const civitaiImg2ImgButton = document.querySelector('#img2img_extra_refresh')
|
||||
?.nextSibling as HTMLButtonElement;
|
||||
if (civitaiImg2ImgButton) {
|
||||
civitaiImg2ImgButton.onclick = civitaiHelperFix;
|
||||
}
|
||||
useTimeout(() => {
|
||||
try {
|
||||
const t2indexButton = document.querySelector('#txt2img_extra_refresh') as HTMLButtonElement;
|
||||
const index2indexButton = document.querySelector(
|
||||
'#img2img_extra_refresh',
|
||||
) as HTMLButtonElement;
|
||||
t2indexButton.click();
|
||||
index2indexButton.click();
|
||||
setExtraLoading(false);
|
||||
|
||||
civitaiHelperFix();
|
||||
} catch (error) {
|
||||
console.debug(error);
|
||||
}
|
||||
console.timeEnd('🤯 [extranetwork] force reload');
|
||||
}, 2000);
|
||||
const civitaiText2ImgButton = document.querySelector('#txt2img_extra_refresh')
|
||||
?.nextSibling as HTMLButtonElement;
|
||||
if (civitaiText2ImgButton) {
|
||||
civitaiText2ImgButton.onclick = civitaiHelperFix;
|
||||
}
|
||||
const civitaiImg2ImgButton = document.querySelector('#img2img_extra_refresh')
|
||||
?.nextSibling as HTMLButtonElement;
|
||||
if (civitaiImg2ImgButton) {
|
||||
civitaiImg2ImgButton.onclick = civitaiHelperFix;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<DraggablePanelBody className={styles.body}>
|
||||
{extraLoading && <Skeleton active />}
|
||||
<div style={extraLoading ? { display: 'none' } : {}}>
|
||||
<div
|
||||
id="txt2img-extra-network-sidebar"
|
||||
ref={txt2imgExtraNetworkSidebarReference}
|
||||
style={currentTab === 'tab_img2img' ? { display: 'none' } : {}}
|
||||
/>
|
||||
<div
|
||||
id="img2img-extra-network-sidebar"
|
||||
ref={img2imgExtraNetworkSidebarReference}
|
||||
style={currentTab === 'tab_img2img' ? {} : { display: 'none' }}
|
||||
/>
|
||||
</div>
|
||||
</DraggablePanelBody>
|
||||
<DraggablePanelFooter>
|
||||
<ActionIcon
|
||||
icon={setting.extraNetworkCardSize < size ? ZoomOut : ZoomIn}
|
||||
onClick={() => setSize(setting.extraNetworkCardSize)}
|
||||
size={{ blockSize: 24, fontSize: 16 }}
|
||||
/>
|
||||
<Slider
|
||||
defaultValue={size}
|
||||
max={256}
|
||||
min={64}
|
||||
onChange={setSize}
|
||||
step={8}
|
||||
style={{ flex: 1 }}
|
||||
value={size}
|
||||
/>
|
||||
</DraggablePanelFooter>
|
||||
</>
|
||||
);
|
||||
civitaiHelperFix();
|
||||
consola.success('🤯 [extranetwork] force reload');
|
||||
} catch (error) {
|
||||
consola.error('🤯 [extranetwork] force reload', error);
|
||||
}
|
||||
}, 2000);
|
||||
|
||||
return (
|
||||
<>
|
||||
<DraggablePanelBody className={styles.body}>
|
||||
{extraLoading && <Skeleton active />}
|
||||
<div style={extraLoading ? { display: 'none' } : {}}>
|
||||
<div
|
||||
id="txt2img-extra-network-sidebar"
|
||||
ref={txt2imgExtraNetworkSidebarReference}
|
||||
style={currentTab === 'tab_img2img' ? { display: 'none' } : {}}
|
||||
/>
|
||||
<div
|
||||
id="img2img-extra-network-sidebar"
|
||||
ref={img2imgExtraNetworkSidebarReference}
|
||||
style={currentTab === 'tab_img2img' ? {} : { display: 'none' }}
|
||||
/>
|
||||
</div>
|
||||
</DraggablePanelBody>
|
||||
<DraggablePanelFooter>
|
||||
<ActionIcon
|
||||
icon={setting.extraNetworkCardSize < size ? ZoomOut : ZoomIn}
|
||||
onClick={() => setSize(setting.extraNetworkCardSize)}
|
||||
size={{ blockSize: 24, fontSize: 16 }}
|
||||
/>
|
||||
<Slider
|
||||
defaultValue={size}
|
||||
max={256}
|
||||
min={64}
|
||||
onChange={setSize}
|
||||
step={8}
|
||||
style={{ flex: 1 }}
|
||||
value={size}
|
||||
/>
|
||||
</DraggablePanelFooter>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
export default Inner;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import {
|
||||
DraggablePanel,
|
||||
DraggablePanelContainer,
|
||||
DraggablePanelHeader,
|
||||
LayoutSidebarInner,
|
||||
DraggablePanel,
|
||||
DraggablePanelContainer,
|
||||
DraggablePanelHeader,
|
||||
LayoutSidebarInner,
|
||||
} from '@lobehub/ui';
|
||||
import { useResponsive } from 'antd-style';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
|
|
@ -20,50 +20,50 @@ export interface ExtraNetworkSidebarProps extends DivProps {
|
|||
}
|
||||
|
||||
const ExtraNetworkSidebar = memo<ExtraNetworkSidebarProps>(({ headerHeight }) => {
|
||||
const { mobile } = useResponsive();
|
||||
const setting = useAppStore(selectors.currentSetting, isEqual);
|
||||
const [expand, setExpand] = useState<boolean>(mobile ? false : setting.extraNetworkSidebarExpand);
|
||||
const [pin, setPin] = useState<boolean>(setting.extraNetworkFixedMode === 'fixed');
|
||||
const { styles, theme } = useStyles({ headerHeight });
|
||||
const { t } = useTranslation();
|
||||
const { mobile } = useResponsive();
|
||||
const setting = useAppStore(selectors.currentSetting, isEqual);
|
||||
const [expand, setExpand] = useState<boolean>(mobile ? false : setting.extraNetworkSidebarExpand);
|
||||
const [pin, setPin] = useState<boolean>(setting.extraNetworkFixedMode === 'fixed');
|
||||
const { styles, theme } = useStyles({ headerHeight });
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
if (mobile) setExpand(false);
|
||||
}, [mobile]);
|
||||
useEffect(() => {
|
||||
if (mobile) setExpand(false);
|
||||
}, [mobile]);
|
||||
|
||||
const mode = mobile ? 'fixed' : pin ? 'fixed' : 'float';
|
||||
const mode = mobile ? 'fixed' : pin ? 'fixed' : 'float';
|
||||
|
||||
return (
|
||||
<DraggablePanel
|
||||
defaultSize={{ width: setting.extraNetworkSidebarWidth }}
|
||||
expand={expand}
|
||||
minWidth={setting.extraNetworkSidebarWidth}
|
||||
mode={mode}
|
||||
onExpandChange={setExpand}
|
||||
pin={pin}
|
||||
placement="right"
|
||||
>
|
||||
<LayoutSidebarInner>
|
||||
<DraggablePanelContainer
|
||||
className={styles.container}
|
||||
style={
|
||||
mode === 'float' ?
|
||||
{ background: theme.colorBgContainer, minWidth: setting.extraNetworkSidebarWidth } :
|
||||
{ minWidth: setting.extraNetworkSidebarWidth }
|
||||
}
|
||||
>
|
||||
<DraggablePanelHeader
|
||||
return (
|
||||
<DraggablePanel
|
||||
defaultSize={{ width: setting.extraNetworkSidebarWidth }}
|
||||
expand={expand}
|
||||
minWidth={setting.extraNetworkSidebarWidth}
|
||||
mode={mode}
|
||||
onExpandChange={setExpand}
|
||||
pin={pin}
|
||||
position="right"
|
||||
setExpand={setExpand}
|
||||
setPin={setPin}
|
||||
title={t('extraNetwork')}
|
||||
/>
|
||||
<Inner />
|
||||
</DraggablePanelContainer>
|
||||
</LayoutSidebarInner>
|
||||
</DraggablePanel>
|
||||
);
|
||||
placement="right"
|
||||
>
|
||||
<LayoutSidebarInner>
|
||||
<DraggablePanelContainer
|
||||
className={styles.container}
|
||||
style={
|
||||
mode === 'float' ?
|
||||
{ background: theme.colorBgContainer, minWidth: setting.extraNetworkSidebarWidth } :
|
||||
{ minWidth: setting.extraNetworkSidebarWidth }
|
||||
}
|
||||
>
|
||||
<DraggablePanelHeader
|
||||
pin={pin}
|
||||
position="right"
|
||||
setExpand={setExpand}
|
||||
setPin={setPin}
|
||||
title={t('sidebar.extraNetwork')}
|
||||
/>
|
||||
<Inner />
|
||||
</DraggablePanelContainer>
|
||||
</LayoutSidebarInner>
|
||||
</DraggablePanel>
|
||||
);
|
||||
});
|
||||
|
||||
export default ExtraNetworkSidebar;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { createStyles } from 'antd-style';
|
||||
|
||||
export const useStyles = createStyles(
|
||||
({ css, token }, { headerHeight = 64, size = 86 }: { headerHeight?: number; size?: number }) => ({
|
||||
body: css`
|
||||
({ css, token }, { headerHeight = 64, size = 86 }: { headerHeight?: number; size?: number }) => ({
|
||||
body: css`
|
||||
.hide {
|
||||
display: none;
|
||||
}
|
||||
|
|
@ -222,8 +222,8 @@ export const useStyles = createStyles(
|
|||
font-family: ${token.fontFamily};
|
||||
}
|
||||
`,
|
||||
container: css`
|
||||
container: css`
|
||||
height: calc(100vh - ${headerHeight}px);
|
||||
`,
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -4,91 +4,91 @@ import { Bug, FileClock, GitFork, Github } from 'lucide-react';
|
|||
import { homepage } from '../../../package.json';
|
||||
|
||||
export const Resources = [
|
||||
{
|
||||
description: 'AUTOMATIC111',
|
||||
openExternal: true,
|
||||
title: 'Stable Diffusion Webui',
|
||||
url: 'https://github.com/AUTOMATIC1111/stable-diffusion-webui',
|
||||
},
|
||||
{
|
||||
description: 'WebUI extension',
|
||||
openExternal: true,
|
||||
title: 'Controlnet',
|
||||
url: 'https://github.com/Mikubill/sd-webui-controlnet',
|
||||
},
|
||||
{
|
||||
description: 'Art models',
|
||||
openExternal: true,
|
||||
title: 'Civitai',
|
||||
url: 'https://civitai.com/',
|
||||
},
|
||||
{
|
||||
description: 'Artist Inspired Styles',
|
||||
openExternal: true,
|
||||
title: 'Cheat Sheet',
|
||||
url: 'https://supagruen.github.io/StableDiffusion-CheatSheet',
|
||||
},
|
||||
{
|
||||
description: 'Image Resizing',
|
||||
openExternal: true,
|
||||
title: 'Birme',
|
||||
url: 'https://www.birme.net/?target_width=512&target_height=512',
|
||||
},
|
||||
{
|
||||
description: 'AUTOMATIC111',
|
||||
openExternal: true,
|
||||
title: 'Stable Diffusion Webui',
|
||||
url: 'https://github.com/AUTOMATIC1111/stable-diffusion-webui',
|
||||
},
|
||||
{
|
||||
description: 'WebUI extension',
|
||||
openExternal: true,
|
||||
title: 'Controlnet',
|
||||
url: 'https://github.com/Mikubill/sd-webui-controlnet',
|
||||
},
|
||||
{
|
||||
description: 'Art models',
|
||||
openExternal: true,
|
||||
title: 'Civitai',
|
||||
url: 'https://civitai.com/',
|
||||
},
|
||||
{
|
||||
description: 'Artist Inspired Styles',
|
||||
openExternal: true,
|
||||
title: 'Cheat Sheet',
|
||||
url: 'https://supagruen.github.io/StableDiffusion-CheatSheet',
|
||||
},
|
||||
{
|
||||
description: 'Image Resizing',
|
||||
openExternal: true,
|
||||
title: 'Birme',
|
||||
url: 'https://www.birme.net/?target_width=512&target_height=512',
|
||||
},
|
||||
];
|
||||
|
||||
export const Community = [
|
||||
{
|
||||
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`,
|
||||
},
|
||||
{
|
||||
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`,
|
||||
},
|
||||
];
|
||||
|
||||
export const Help = [
|
||||
{
|
||||
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`,
|
||||
},
|
||||
{
|
||||
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`,
|
||||
},
|
||||
];
|
||||
|
||||
export const MoreProducts = [
|
||||
{
|
||||
description: 'Minifier ExtraNetwrok Covers',
|
||||
openExternal: true,
|
||||
title: '✂️ Cover Minifier',
|
||||
url: 'https://github.com/canisminor1990/sd-webui-cover-minifier',
|
||||
},
|
||||
{
|
||||
description: 'OpenAI Chat Bot',
|
||||
openExternal: true,
|
||||
title: '🤖 Lobe Chat',
|
||||
url: 'https://chat.lobehub.com',
|
||||
},
|
||||
{
|
||||
description: 'AIGC Components',
|
||||
openExternal: true,
|
||||
title: '🍭 Lobe UI',
|
||||
url: 'https://ui.lobehub.com',
|
||||
},
|
||||
{
|
||||
description: 'AI Commit CLI',
|
||||
openExternal: true,
|
||||
title: '💌 Lobe Commit',
|
||||
url: 'https://github.com/lobehub/lobe-commit',
|
||||
},
|
||||
{
|
||||
description: 'Minifier ExtraNetwrok Covers',
|
||||
openExternal: true,
|
||||
title: '✂️ Cover Minifier',
|
||||
url: 'https://github.com/canisminor1990/sd-webui-cover-minifier',
|
||||
},
|
||||
{
|
||||
description: 'OpenAI Chat Bot',
|
||||
openExternal: true,
|
||||
title: '🤖 Lobe Chat',
|
||||
url: 'https://chat.lobehub.com',
|
||||
},
|
||||
{
|
||||
description: 'AIGC Components',
|
||||
openExternal: true,
|
||||
title: '🍭 Lobe UI',
|
||||
url: 'https://ui.lobehub.com',
|
||||
},
|
||||
{
|
||||
description: 'AI Commit CLI',
|
||||
openExternal: true,
|
||||
title: '💌 Lobe Commit',
|
||||
url: 'https://github.com/lobehub/lobe-commit',
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { Footer as F } from '@lobehub/ui';
|
||||
import { consola } from 'consola';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { memo, useEffect, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
|
@ -10,54 +11,57 @@ import { Community, Help, MoreProducts, Resources } from './data';
|
|||
import { useStyles } from './style';
|
||||
|
||||
const Footer = memo<DivProps>(({ className, ...props }) => {
|
||||
const setting = useAppStore(selectors.currentSetting, isEqual);
|
||||
const { cx, styles } = useStyles();
|
||||
const { t } = useTranslation();
|
||||
const footerReference = useRef<HTMLDivElement>(null);
|
||||
const setting = useAppStore(selectors.currentSetting, isEqual);
|
||||
const { cx, styles } = useStyles();
|
||||
const { t } = useTranslation();
|
||||
const footerReference = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
console.time('🤯 [layout] inject - Footer');
|
||||
const footer = gradioApp().querySelector('#footer');
|
||||
if (footer) footerReference.current?.append(footer);
|
||||
if (setting.confirmPageUnload) {
|
||||
window.addEventListener('beforeunload', (event) => {
|
||||
if (footer?.isConnected) {
|
||||
event.preventDefault();
|
||||
return (event.returnValue = '');
|
||||
useEffect(() => {
|
||||
try {
|
||||
const footer = gradioApp().querySelector('#footer');
|
||||
if (footer) footerReference.current?.append(footer);
|
||||
if (setting.confirmPageUnload) {
|
||||
window.addEventListener('beforeunload', (event) => {
|
||||
if (footer?.isConnected) {
|
||||
event.preventDefault();
|
||||
return (event.returnValue = '');
|
||||
}
|
||||
});
|
||||
}
|
||||
consola.success('🤯 [layout] inject - Footer');
|
||||
} catch (error) {
|
||||
consola.error('🤯 [layout] inject - Footer', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
console.timeEnd('🤯 [layout] inject - Footer');
|
||||
}, []);
|
||||
return (
|
||||
<div className={cx(styles.footer, className)} {...props}>
|
||||
<F
|
||||
bottom={<div ref={footerReference} />}
|
||||
columns={
|
||||
setting.layoutHideFooter ?
|
||||
[] :
|
||||
[
|
||||
{
|
||||
items: Resources,
|
||||
title: t('resources'),
|
||||
},
|
||||
{
|
||||
items: Community,
|
||||
title: t('community'),
|
||||
},
|
||||
{
|
||||
items: Help,
|
||||
title: t('help'),
|
||||
},
|
||||
{
|
||||
items: MoreProducts,
|
||||
title: t('moreProducts'),
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}, []);
|
||||
return (
|
||||
<div className={cx(styles.footer, className)} {...props}>
|
||||
<F
|
||||
bottom={<div ref={footerReference} />}
|
||||
columns={
|
||||
setting.layoutHideFooter ?
|
||||
[] :
|
||||
[
|
||||
{
|
||||
items: Resources,
|
||||
title: t('footer.resources'),
|
||||
},
|
||||
{
|
||||
items: Community,
|
||||
title: t('footer.community'),
|
||||
},
|
||||
{
|
||||
items: Help,
|
||||
title: t('footer.help'),
|
||||
},
|
||||
{
|
||||
items: MoreProducts,
|
||||
title: t('footer.moreProducts'),
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export default Footer;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { createStyles } from 'antd-style';
|
||||
|
||||
export const useStyles = createStyles(({ css }) => ({
|
||||
footer: css`
|
||||
footer: css`
|
||||
footer {
|
||||
display: block !important;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,16 +5,15 @@ import { Github, LayoutGrid, LucideIcon, Moon, Settings, Sun } from 'lucide-reac
|
|||
import qs from 'query-string';
|
||||
import { memo, useCallback, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import { Giscus } from '@/components';
|
||||
import Setting from '@/features/Setting';
|
||||
import { selectors, useAppStore } from '@/store';
|
||||
|
||||
const CivitaiLogo: LucideIcon | any = ({ size }: any) => (
|
||||
<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>
|
||||
<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 {
|
||||
|
|
@ -22,48 +21,56 @@ interface ActionsProps {
|
|||
}
|
||||
|
||||
const Actions = memo<ActionsProps>(() => {
|
||||
const [isSettingOpen, setIsSettingOpen] = useState(false);
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const themeMode = useAppStore(selectors.themeMode, shallow);
|
||||
const { mobile } = useResponsive();
|
||||
const { t } = useTranslation();
|
||||
const [isSettingOpen, setIsSettingOpen] = useState(false);
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const themeMode = useAppStore(selectors.themeMode);
|
||||
const { mobile } = useResponsive();
|
||||
const { t } = useTranslation();
|
||||
|
||||
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 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]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Space.Compact>
|
||||
{!mobile && (
|
||||
<>
|
||||
<a href="https://civitai.com/" rel="noreferrer" target="_blank">
|
||||
<ActionIcon icon={CivitaiLogo} title="Civitai" />
|
||||
</a>
|
||||
<a
|
||||
href="https://supagruen.github.io/StableDiffusion-CheatSheet/"
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
<ActionIcon icon={LayoutGrid} title="Cheat Sheet" />
|
||||
</a>
|
||||
<ActionIcon icon={Github} onClick={() => setIsModalOpen(true)} title={t('feedback')} />
|
||||
</>
|
||||
)}
|
||||
<ActionIcon
|
||||
icon={themeMode === 'light' ? Sun : Moon}
|
||||
onClick={handleSetTheme}
|
||||
title={t('switchTheme')}
|
||||
/>
|
||||
<ActionIcon icon={Settings} onClick={() => setIsSettingOpen(true)} title={t('setting')} />
|
||||
</Space.Compact>
|
||||
<Setting onCancel={() => setIsSettingOpen(false)} open={isSettingOpen} />
|
||||
<Giscus onCancel={() => setIsModalOpen(false)} open={isModalOpen} />
|
||||
</>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<Space.Compact>
|
||||
{!mobile && (
|
||||
<>
|
||||
<a href="https://civitai.com/" rel="noreferrer" target="_blank">
|
||||
<ActionIcon icon={CivitaiLogo} title="Civitai" />
|
||||
</a>
|
||||
<a
|
||||
href="https://supagruen.github.io/StableDiffusion-CheatSheet/"
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
<ActionIcon icon={LayoutGrid} title="Cheat Sheet" />
|
||||
</a>
|
||||
<ActionIcon
|
||||
icon={Github}
|
||||
onClick={() => setIsModalOpen(true)}
|
||||
title={t('header.feedback')}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<ActionIcon
|
||||
icon={themeMode === 'light' ? Sun : Moon}
|
||||
onClick={handleSetTheme}
|
||||
title={t('header.switchTheme')}
|
||||
/>
|
||||
<ActionIcon
|
||||
icon={Settings}
|
||||
onClick={() => setIsSettingOpen(true)}
|
||||
title={t('header.setting')}
|
||||
/>
|
||||
</Space.Compact>
|
||||
<Setting onCancel={() => setIsSettingOpen(false)} open={isSettingOpen} />
|
||||
<Giscus onCancel={() => setIsModalOpen(false)} open={isModalOpen} />
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
export default Actions;
|
||||
|
|
|
|||
|
|
@ -1,26 +1,26 @@
|
|||
import { Burger, TabsNav, type TabsNavProps } from '@lobehub/ui';
|
||||
import { useResponsive } from 'antd-style';
|
||||
import { consola } from 'consola';
|
||||
import { startCase } from 'lodash-es';
|
||||
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import { selectors, useAppStore } from '@/store';
|
||||
|
||||
const hideOriganlNav = () => {
|
||||
(gradioApp().querySelector('#tabs > .tab-nav:first-of-type') as HTMLDivElement).style.display =
|
||||
(gradioApp().querySelector('#tabs > .tab-nav:first-of-type') as HTMLDivElement).style.display =
|
||||
'none';
|
||||
};
|
||||
|
||||
const getNavTabs = (): HTMLDivElement[] =>
|
||||
Array.prototype.slice.call(
|
||||
Array.prototype.slice.call(
|
||||
gradioApp().querySelectorAll('#tabs > [id^="tab_"]') as NodeListOf<HTMLDivElement>,
|
||||
);
|
||||
);
|
||||
const getNavButtons = (): HTMLButtonElement[] =>
|
||||
Array.prototype.slice.call(
|
||||
Array.prototype.slice.call(
|
||||
gradioApp().querySelectorAll(
|
||||
'#tabs > .tab-nav:first-of-type button',
|
||||
'#tabs > .tab-nav:first-of-type button',
|
||||
) as NodeListOf<HTMLButtonElement>,
|
||||
);
|
||||
);
|
||||
|
||||
interface NavItem {
|
||||
id: string;
|
||||
|
|
@ -28,53 +28,56 @@ interface NavItem {
|
|||
label: string;
|
||||
}
|
||||
const genNavList = (): NavItem[] => {
|
||||
console.debug('🤯 [nav] generate nav list');
|
||||
const navList = getNavTabs();
|
||||
const buttons = getNavButtons();
|
||||
return buttons.map((button, index) => {
|
||||
const id = navList[index].id;
|
||||
return {
|
||||
id,
|
||||
index,
|
||||
label: startCase(String(button.textContent)),
|
||||
};
|
||||
});
|
||||
const navList = getNavTabs();
|
||||
const buttons = getNavButtons();
|
||||
consola.debug('🤯 [nav] generate nav list');
|
||||
return buttons.map((button, index) => {
|
||||
const id = navList[index].id;
|
||||
return {
|
||||
id,
|
||||
index,
|
||||
label: startCase(String(button.textContent)),
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const Nav = memo(() => {
|
||||
const currentTab = useAppStore(selectors.currentTab, shallow);
|
||||
const { mobile } = useResponsive();
|
||||
const [opened, setOpened] = useState(false);
|
||||
const [items, setItems] = useState<TabsNavProps['items']>([]);
|
||||
const currentTab = useAppStore(selectors.currentTab);
|
||||
const { mobile } = useResponsive();
|
||||
const [opened, setOpened] = useState(false);
|
||||
const [items, setItems] = useState<TabsNavProps['items']>([]);
|
||||
|
||||
const navList = useMemo(() => genNavList(), []);
|
||||
const navList = useMemo(() => genNavList(), []);
|
||||
|
||||
const onChange: TabsNavProps['onChange'] = useCallback(
|
||||
(id: string) => {
|
||||
console.debug('🤯 [nav] onClick', id);
|
||||
const index = navList.find((nav) => nav.id === id)?.index || 0;
|
||||
const buttonList = getNavButtons();
|
||||
buttonList[index].click();
|
||||
},
|
||||
[navList],
|
||||
);
|
||||
const onChange: TabsNavProps['onChange'] = useCallback(
|
||||
(id: string) => {
|
||||
consola.debug('🤯 [nav] onClick', id);
|
||||
const index = navList.find((nav) => nav.id === id)?.index || 0;
|
||||
const buttonList = getNavButtons();
|
||||
buttonList[index].click();
|
||||
},
|
||||
[navList],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
console.time('🤯 [layout] inject - Header');
|
||||
hideOriganlNav();
|
||||
const list: TabsNavProps['items'] = navList.map((item) => {
|
||||
return {
|
||||
key: item.id,
|
||||
label: mobile ? <div onClick={() => onChange(item.id)}>{item.label}</div> : item.label,
|
||||
};
|
||||
});
|
||||
setItems(list.filter(Boolean));
|
||||
console.timeEnd('🤯 [layout] inject - Header');
|
||||
}, [mobile]);
|
||||
useEffect(() => {
|
||||
try {
|
||||
hideOriganlNav();
|
||||
const list: TabsNavProps['items'] = navList.map((item) => {
|
||||
return {
|
||||
key: item.id,
|
||||
label: mobile ? <div onClick={() => onChange(item.id)}>{item.label}</div> : item.label,
|
||||
};
|
||||
});
|
||||
setItems(list.filter(Boolean));
|
||||
consola.success('🤯 [layout] inject - Header');
|
||||
} catch (error) {
|
||||
consola.error('🤯 [layout] inject - Header', error);
|
||||
}
|
||||
}, [mobile]);
|
||||
|
||||
if (mobile) return <Burger items={items} opened={opened} setOpened={setOpened} />;
|
||||
if (mobile) return <Burger items={items} opened={opened} setOpened={setOpened} />;
|
||||
|
||||
return <TabsNav activeKey={currentTab} items={items} onChange={onChange} />;
|
||||
return <TabsNav activeKey={currentTab} items={items} onChange={onChange} />;
|
||||
});
|
||||
|
||||
export default Nav;
|
||||
|
|
|
|||
|
|
@ -12,36 +12,36 @@ import Actions from './Actions';
|
|||
import Nav from './Nav';
|
||||
|
||||
const Header = memo<DivProps>(({ children }) => {
|
||||
const { themeMode, version } = useAppStore(
|
||||
(st) => ({ themeMode: st.themeMode, version: st.version }),
|
||||
shallow,
|
||||
);
|
||||
const theme = useTheme();
|
||||
const { themeMode, version } = useAppStore(
|
||||
(st) => ({ themeMode: st.themeMode, version: st.version }),
|
||||
shallow,
|
||||
);
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<H
|
||||
actions={<Actions themeMode={themeMode} />}
|
||||
actionsStyle={{ flex: 0 }}
|
||||
logo={
|
||||
<a
|
||||
href={`${homepage}/releases`}
|
||||
rel="noreferrer"
|
||||
style={{ alignItems: 'center', color: theme.colorText, display: 'flex' }}
|
||||
target="_blank"
|
||||
>
|
||||
<Tooltip title={`${name} v${version}`}>
|
||||
<Logo />
|
||||
</Tooltip>
|
||||
</a>
|
||||
}
|
||||
nav={
|
||||
<>
|
||||
<Nav />
|
||||
{children}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
return (
|
||||
<H
|
||||
actions={<Actions themeMode={themeMode} />}
|
||||
actionsStyle={{ flex: 0 }}
|
||||
logo={
|
||||
<a
|
||||
href={`${homepage}/releases`}
|
||||
rel="noreferrer"
|
||||
style={{ alignItems: 'center', color: theme.colorText, display: 'flex' }}
|
||||
target="_blank"
|
||||
>
|
||||
<Tooltip title={`${name} v${version}`}>
|
||||
<Logo />
|
||||
</Tooltip>
|
||||
</a>
|
||||
}
|
||||
nav={
|
||||
<>
|
||||
<Nav />
|
||||
{children}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export default Header;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { DraggablePanelBody } from '@lobehub/ui';
|
||||
import { consola } from 'consola';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { memo, useEffect, useRef } from 'react';
|
||||
|
||||
|
|
@ -7,22 +8,21 @@ import { selectors, useAppStore } from '@/store';
|
|||
import { type DivProps } from '@/types';
|
||||
|
||||
const Inner = memo<DivProps>(() => {
|
||||
const setting = useAppStore(selectors.currentSetting, isEqual);
|
||||
const sidebarReference = useRef<HTMLDivElement>(null);
|
||||
const setting = useAppStore(selectors.currentSetting, isEqual);
|
||||
const sidebarReference = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
console.time('🤯 [layout] inject - QuickSettingSidebar');
|
||||
const sidebar = gradioApp().querySelector('#quicksettings');
|
||||
if (sidebar) sidebarReference.current?.append(sidebar);
|
||||
console.timeEnd('🤯 [layout] inject - QuickSettingSidebar');
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
const sidebar = gradioApp().querySelector('#quicksettings');
|
||||
if (sidebar) sidebarReference.current?.append(sidebar);
|
||||
consola.success('🤯 [layout] inject - QuickSettingSidebar');
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<DraggablePanelBody>
|
||||
{setting.promptEditor && <PromptEditor />}
|
||||
<div ref={sidebarReference} />
|
||||
</DraggablePanelBody>
|
||||
);
|
||||
return (
|
||||
<DraggablePanelBody>
|
||||
{setting.promptEditor && <PromptEditor />}
|
||||
<div ref={sidebarReference} />
|
||||
</DraggablePanelBody>
|
||||
);
|
||||
});
|
||||
|
||||
export default Inner;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import {
|
||||
type DivProps,
|
||||
DraggablePanel,
|
||||
DraggablePanelContainer,
|
||||
DraggablePanelHeader,
|
||||
LayoutSidebarInner,
|
||||
type DivProps,
|
||||
DraggablePanel,
|
||||
DraggablePanelContainer,
|
||||
DraggablePanelHeader,
|
||||
LayoutSidebarInner,
|
||||
} from '@lobehub/ui';
|
||||
import { useResponsive } from 'antd-style';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
|
|
@ -20,56 +20,56 @@ export interface QuickSettingSidebarProps extends DivProps {
|
|||
}
|
||||
|
||||
const QuickSettingSidebar = memo<QuickSettingSidebarProps>(({ headerHeight }) => {
|
||||
const { mobile } = useResponsive();
|
||||
const setting = useAppStore(selectors.currentSetting, isEqual);
|
||||
const [expand, setExpand] = useState<boolean>(mobile ? false : setting.sidebarExpand);
|
||||
const [pin, setPin] = useState<boolean>(setting.sidebarFixedMode === 'fixed');
|
||||
const [width, setWidth] = useState<number>(setting.sidebarWidth);
|
||||
const { styles, theme } = useStyles({ headerHeight, width });
|
||||
const { t } = useTranslation();
|
||||
const { mobile } = useResponsive();
|
||||
const setting = useAppStore(selectors.currentSetting, isEqual);
|
||||
const [expand, setExpand] = useState<boolean>(mobile ? false : setting.sidebarExpand);
|
||||
const [pin, setPin] = useState<boolean>(setting.sidebarFixedMode === 'fixed');
|
||||
const [width, setWidth] = useState<number>(setting.sidebarWidth);
|
||||
const { styles, theme } = useStyles({ headerHeight, width });
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
if (mobile) setExpand(false);
|
||||
}, [mobile]);
|
||||
useEffect(() => {
|
||||
if (mobile) setExpand(false);
|
||||
}, [mobile]);
|
||||
|
||||
const mode = mobile ? 'fixed' : pin ? 'fixed' : 'float';
|
||||
const mode = mobile ? 'fixed' : pin ? 'fixed' : 'float';
|
||||
|
||||
return (
|
||||
<DraggablePanel
|
||||
defaultSize={{ width: setting.sidebarWidth }}
|
||||
expand={expand}
|
||||
minWidth={setting.sidebarWidth}
|
||||
mode={mode}
|
||||
onExpandChange={setExpand}
|
||||
onSizeChange={(_, size) => size?.width && setWidth(Number.parseInt(String(size.width)))}
|
||||
pin={pin}
|
||||
placement="left"
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
}}
|
||||
>
|
||||
<LayoutSidebarInner>
|
||||
<DraggablePanelContainer
|
||||
className={styles.container}
|
||||
style={
|
||||
mode === 'float' ?
|
||||
{ background: theme.colorBgContainer, minWidth: setting.sidebarWidth } :
|
||||
{ minWidth: setting.sidebarWidth }
|
||||
}
|
||||
>
|
||||
<DraggablePanelHeader
|
||||
return (
|
||||
<DraggablePanel
|
||||
defaultSize={{ width: setting.sidebarWidth }}
|
||||
expand={expand}
|
||||
minWidth={setting.sidebarWidth}
|
||||
mode={mode}
|
||||
onExpandChange={setExpand}
|
||||
onSizeChange={(_, size) => size?.width && setWidth(Number.parseInt(String(size.width)))}
|
||||
pin={pin}
|
||||
position="left"
|
||||
setExpand={setExpand}
|
||||
setPin={setPin}
|
||||
title={t('quickSetting')}
|
||||
/>
|
||||
<Inner />
|
||||
</DraggablePanelContainer>
|
||||
</LayoutSidebarInner>
|
||||
</DraggablePanel>
|
||||
);
|
||||
placement="left"
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
}}
|
||||
>
|
||||
<LayoutSidebarInner>
|
||||
<DraggablePanelContainer
|
||||
className={styles.container}
|
||||
style={
|
||||
mode === 'float' ?
|
||||
{ background: theme.colorBgContainer, minWidth: setting.sidebarWidth } :
|
||||
{ minWidth: setting.sidebarWidth }
|
||||
}
|
||||
>
|
||||
<DraggablePanelHeader
|
||||
pin={pin}
|
||||
position="left"
|
||||
setExpand={setExpand}
|
||||
setPin={setPin}
|
||||
title={t('sidebar.quickSetting')}
|
||||
/>
|
||||
<Inner />
|
||||
</DraggablePanelContainer>
|
||||
</LayoutSidebarInner>
|
||||
</DraggablePanel>
|
||||
);
|
||||
});
|
||||
|
||||
export default QuickSettingSidebar;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { createStyles } from 'antd-style';
|
||||
|
||||
export const useStyles = createStyles(
|
||||
({ css }, { headerHeight = 64, width }: { headerHeight?: number; width: number }) => ({
|
||||
container: css`
|
||||
({ css }, { headerHeight = 64, width }: { headerHeight?: number; width: number }) => ({
|
||||
container: css`
|
||||
height: calc(100vh - ${headerHeight}px);
|
||||
|
||||
ul.options {
|
||||
|
|
@ -68,5 +68,5 @@ export const useStyles = createStyles(
|
|||
}
|
||||
}
|
||||
`,
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -8,21 +8,21 @@ import { shallow } from 'zustand/shallow';
|
|||
|
||||
import { CustomLogo } from '@/components';
|
||||
import {
|
||||
DEFAULT_SETTING,
|
||||
type WebuiSetting,
|
||||
type WebuiSettingKeys,
|
||||
selectors,
|
||||
useAppStore,
|
||||
DEFAULT_SETTING,
|
||||
type WebuiSetting,
|
||||
type WebuiSettingKeys,
|
||||
selectors,
|
||||
useAppStore,
|
||||
} from '@/store';
|
||||
|
||||
import {
|
||||
type NeutralColor,
|
||||
type PrimaryColor,
|
||||
findCustomThemeName,
|
||||
neutralColors,
|
||||
neutralColorsSwatches,
|
||||
primaryColors,
|
||||
primaryColorsSwatches,
|
||||
type NeutralColor,
|
||||
type PrimaryColor,
|
||||
findCustomThemeName,
|
||||
neutralColors,
|
||||
neutralColorsSwatches,
|
||||
primaryColors,
|
||||
primaryColorsSwatches,
|
||||
} from './data';
|
||||
import { useStyles } from './style';
|
||||
|
||||
|
|
@ -33,353 +33,353 @@ type SettingItemGroup = ItemGroup & {
|
|||
};
|
||||
|
||||
const SettingForm = memo(() => {
|
||||
const setting = useAppStore(selectors.currentSetting, isEqual);
|
||||
const { onSetSetting, localeOptions } = useAppStore(
|
||||
(st) => ({ localeOptions: st.localeOptions, onSetSetting: st.onSetSetting }),
|
||||
shallow,
|
||||
);
|
||||
const [rawSetting, setRawSetting] = useState<WebuiSetting>(setting);
|
||||
const [primaryColor, setPrimaryColor] = useState<PrimaryColor | undefined>(
|
||||
setting.primaryColor || undefined,
|
||||
);
|
||||
const [neutralColor, setNeutralColor] = useState<NeutralColor | undefined>(
|
||||
setting.neutralColor || undefined,
|
||||
);
|
||||
const { styles } = useStyles();
|
||||
const { t } = useTranslation();
|
||||
const setting = useAppStore(selectors.currentSetting, isEqual);
|
||||
const { onSetSetting, localeOptions } = useAppStore(
|
||||
(st) => ({ localeOptions: st.localeOptions, onSetSetting: st.onSetSetting }),
|
||||
shallow,
|
||||
);
|
||||
const [rawSetting, setRawSetting] = useState<WebuiSetting>(setting);
|
||||
const [primaryColor, setPrimaryColor] = useState<PrimaryColor | undefined>(
|
||||
setting.primaryColor || undefined,
|
||||
);
|
||||
const [neutralColor, setNeutralColor] = useState<NeutralColor | undefined>(
|
||||
setting.neutralColor || undefined,
|
||||
);
|
||||
const { styles } = useStyles();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const onReset = useCallback(() => {
|
||||
onSetSetting(DEFAULT_SETTING);
|
||||
location.reload();
|
||||
}, []);
|
||||
const onReset = useCallback(() => {
|
||||
onSetSetting(DEFAULT_SETTING);
|
||||
location.reload();
|
||||
}, []);
|
||||
|
||||
const onFinish = useCallback(
|
||||
(value: WebuiSetting) => {
|
||||
onSetSetting({ ...value, neutralColor, primaryColor });
|
||||
location.reload();
|
||||
},
|
||||
[primaryColor, neutralColor],
|
||||
);
|
||||
const onFinish = useCallback(
|
||||
(value: WebuiSetting) => {
|
||||
onSetSetting({ ...value, neutralColor, primaryColor });
|
||||
location.reload();
|
||||
},
|
||||
[primaryColor, neutralColor],
|
||||
);
|
||||
|
||||
const theme: SettingItemGroup = useMemo(
|
||||
() => ({
|
||||
children: [
|
||||
{
|
||||
children: <Select options={localeOptions} />,
|
||||
desc: t('settingLanguageDesc'),
|
||||
label: t('settingLanguage'),
|
||||
name: 'i18n',
|
||||
},
|
||||
{
|
||||
children: <Switch />,
|
||||
desc: t('settingReduceAnimationDesc'),
|
||||
label: t('settingReduceAnimation'),
|
||||
name: 'liteAnimation',
|
||||
valuePropName: 'checked',
|
||||
},
|
||||
{
|
||||
children: (
|
||||
<Swatches
|
||||
activeColor={primaryColor ? primaryColors[primaryColor] : undefined}
|
||||
colors={primaryColorsSwatches}
|
||||
onSelect={(c) => setPrimaryColor(findCustomThemeName('primary', c))}
|
||||
/>
|
||||
),
|
||||
desc: t('settingPrimaryColorDesc'),
|
||||
label: t('settingPrimaryColor'),
|
||||
},
|
||||
{
|
||||
children: (
|
||||
<Swatches
|
||||
activeColor={neutralColor ? neutralColors[neutralColor] : undefined}
|
||||
colors={neutralColorsSwatches}
|
||||
onSelect={(c) => setNeutralColor(findCustomThemeName('neutral', c))}
|
||||
/>
|
||||
),
|
||||
desc: t('settingNeutralColorDesc'),
|
||||
label: t('settingNeutralColor'),
|
||||
},
|
||||
{
|
||||
children: (
|
||||
<Segmented
|
||||
options={[
|
||||
const theme: SettingItemGroup = useMemo(
|
||||
() => ({
|
||||
children: [
|
||||
{
|
||||
label: t('lobe'),
|
||||
value: 'lobe',
|
||||
children: <Select options={localeOptions} />,
|
||||
desc: t('setting.language.desc'),
|
||||
label: t('setting.language.title'),
|
||||
name: 'i18n',
|
||||
},
|
||||
{
|
||||
label: t('kitchen'),
|
||||
value: 'kitchen',
|
||||
children: <Switch />,
|
||||
desc: t('setting.reduceAnimation.desc'),
|
||||
label: t('setting.reduceAnimation.title'),
|
||||
name: 'liteAnimation',
|
||||
valuePropName: 'checked',
|
||||
},
|
||||
{
|
||||
label: t('custom'),
|
||||
value: 'custom',
|
||||
children: (
|
||||
<Swatches
|
||||
activeColor={primaryColor ? primaryColors[primaryColor] : undefined}
|
||||
colors={primaryColorsSwatches}
|
||||
onSelect={(c) => setPrimaryColor(findCustomThemeName('primary', c))}
|
||||
/>
|
||||
),
|
||||
desc: t('setting.primaryColor.desc'),
|
||||
label: t('setting.primaryColor.title'),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
),
|
||||
desc: t('settingLogoTypeDesc'),
|
||||
label: t('settingLogoType'),
|
||||
name: 'logoType',
|
||||
},
|
||||
{
|
||||
children: <Input />,
|
||||
desc: t('settingCustomLogoDesc'),
|
||||
divider: false,
|
||||
hidden: rawSetting.logoType !== 'custom',
|
||||
label: t('settingCustomLogo'),
|
||||
name: 'logoCustomUrl',
|
||||
},
|
||||
{
|
||||
children: <Input />,
|
||||
desc: t('settingCustomTitleDesc'),
|
||||
divider: false,
|
||||
hidden: rawSetting.logoType !== 'custom',
|
||||
label: t('settingCustomTitle'),
|
||||
name: 'logoCustomTitle',
|
||||
},
|
||||
{
|
||||
children: (
|
||||
<CustomLogo
|
||||
logoCustomTitle={rawSetting.logoCustomTitle}
|
||||
logoCustomUrl={rawSetting.logoCustomUrl}
|
||||
/>
|
||||
),
|
||||
divider: false,
|
||||
hidden: rawSetting.logoType !== 'custom',
|
||||
label: t('settingLogoPreview'),
|
||||
},
|
||||
{
|
||||
children: <Switch />,
|
||||
desc: t('settingSvgIconsDesc'),
|
||||
label: t('settingSvgIcons'),
|
||||
name: 'svgIcon',
|
||||
valuePropName: 'checked',
|
||||
},
|
||||
{
|
||||
children: <Switch />,
|
||||
desc: t('settingCustomFontDesc'),
|
||||
label: t('settingCustomFont'),
|
||||
name: 'enableWebFont',
|
||||
valuePropName: 'checked',
|
||||
},
|
||||
{
|
||||
children: <Switch />,
|
||||
desc: t('settingConfirmPageUnloadDesc'),
|
||||
label: t('settingConfirmPageUnload'),
|
||||
name: 'confirmPageUnload',
|
||||
valuePropName: 'checked',
|
||||
},
|
||||
],
|
||||
icon: Palette,
|
||||
title: t('settingGroupTheme'),
|
||||
}),
|
||||
[
|
||||
primaryColor,
|
||||
neutralColor,
|
||||
rawSetting.logoType,
|
||||
rawSetting.logoCustomTitle,
|
||||
rawSetting.logoCustomUrl,
|
||||
],
|
||||
);
|
||||
{
|
||||
children: (
|
||||
<Swatches
|
||||
activeColor={neutralColor ? neutralColors[neutralColor] : undefined}
|
||||
colors={neutralColorsSwatches}
|
||||
onSelect={(c) => setNeutralColor(findCustomThemeName('neutral', c))}
|
||||
/>
|
||||
),
|
||||
desc: t('setting.neutralColor.desc'),
|
||||
label: t('setting.neutralColor.title'),
|
||||
},
|
||||
{
|
||||
children: (
|
||||
<Segmented
|
||||
options={[
|
||||
{
|
||||
label: t('brand.lobe'),
|
||||
value: 'lobe',
|
||||
},
|
||||
{
|
||||
label: t('brand.kitchen'),
|
||||
value: 'kitchen',
|
||||
},
|
||||
{
|
||||
label: t('brand.custom'),
|
||||
value: 'custom',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
),
|
||||
desc: t('setting.logoType.desc'),
|
||||
label: t('setting.logoType.title'),
|
||||
name: 'logoType',
|
||||
},
|
||||
{
|
||||
children: <Input />,
|
||||
desc: t('setting.customLogo.desc'),
|
||||
divider: false,
|
||||
hidden: rawSetting.logoType !== 'custom',
|
||||
label: t('setting.customLogo.title'),
|
||||
name: 'logoCustomUrl',
|
||||
},
|
||||
{
|
||||
children: <Input />,
|
||||
desc: t('setting.customTitle.desc'),
|
||||
divider: false,
|
||||
hidden: rawSetting.logoType !== 'custom',
|
||||
label: t('setting.customTitle.title'),
|
||||
name: 'logoCustomTitle',
|
||||
},
|
||||
{
|
||||
children: (
|
||||
<CustomLogo
|
||||
logoCustomTitle={rawSetting.logoCustomTitle}
|
||||
logoCustomUrl={rawSetting.logoCustomUrl}
|
||||
/>
|
||||
),
|
||||
divider: false,
|
||||
hidden: rawSetting.logoType !== 'custom',
|
||||
label: t('setting.logoType.preview'),
|
||||
},
|
||||
{
|
||||
children: <Switch />,
|
||||
desc: t('setting.svgIcons.desc'),
|
||||
label: t('setting.svgIcons.title'),
|
||||
name: 'svgIcon',
|
||||
valuePropName: 'checked',
|
||||
},
|
||||
{
|
||||
children: <Switch />,
|
||||
desc: t('setting.customFont.desc'),
|
||||
label: t('setting.customFont.title'),
|
||||
name: 'enableWebFont',
|
||||
valuePropName: 'checked',
|
||||
},
|
||||
{
|
||||
children: <Switch />,
|
||||
desc: t('setting.confirmPageUnload.desc'),
|
||||
label: t('setting.confirmPageUnload.title'),
|
||||
name: 'confirmPageUnload',
|
||||
valuePropName: 'checked',
|
||||
},
|
||||
],
|
||||
icon: Palette,
|
||||
title: t('setting.group.theme'),
|
||||
}),
|
||||
[
|
||||
primaryColor,
|
||||
neutralColor,
|
||||
rawSetting.logoType,
|
||||
rawSetting.logoCustomTitle,
|
||||
rawSetting.logoCustomUrl,
|
||||
],
|
||||
);
|
||||
|
||||
const promptTextarea: SettingItemGroup = useMemo(
|
||||
() => ({
|
||||
children: [
|
||||
{
|
||||
children: (
|
||||
<Segmented
|
||||
options={[
|
||||
const promptTextarea: SettingItemGroup = useMemo(
|
||||
() => ({
|
||||
children: [
|
||||
{
|
||||
label: t('scroll'),
|
||||
value: 'scroll',
|
||||
children: (
|
||||
<Segmented
|
||||
options={[
|
||||
{
|
||||
label: t('setting.promptDisplayMode.scroll'),
|
||||
value: 'scroll',
|
||||
},
|
||||
{
|
||||
label: t('setting.promptDisplayMode.resizable'),
|
||||
value: 'resizable',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
),
|
||||
desc: t('setting.promptDisplayMode.desc'),
|
||||
label: t('setting.promptDisplayMode.title'),
|
||||
name: 'promptTextareaType',
|
||||
},
|
||||
{
|
||||
label: t('resizable'),
|
||||
value: 'resizable',
|
||||
children: <Switch />,
|
||||
desc: t('setting.promptHighlight.desc'),
|
||||
label: t('setting.promptHighlight.title'),
|
||||
name: 'enableHighlight',
|
||||
valuePropName: 'checked',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
),
|
||||
desc: t('settingPromptDisplayModeDesc'),
|
||||
label: t('settingPromptDisplayMode'),
|
||||
name: 'promptTextareaType',
|
||||
},
|
||||
{
|
||||
children: <Switch />,
|
||||
desc: t('settingPromptHighlightDesc'),
|
||||
label: t('settingPromptHighlight'),
|
||||
name: 'enableHighlight',
|
||||
valuePropName: 'checked',
|
||||
},
|
||||
{
|
||||
children: <Switch />,
|
||||
desc: t('settingPromptEditorDesc'),
|
||||
label: t('settingPromptEditor'),
|
||||
name: 'promptEditor',
|
||||
valuePropName: 'checked',
|
||||
},
|
||||
],
|
||||
icon: TextCursorInput,
|
||||
title: t('settingGroupPromptTextarea'),
|
||||
}),
|
||||
[],
|
||||
);
|
||||
{
|
||||
children: <Switch />,
|
||||
desc: t('setting.promptEditor.desc'),
|
||||
label: t('setting.promptEditor.title'),
|
||||
name: 'promptEditor',
|
||||
valuePropName: 'checked',
|
||||
},
|
||||
],
|
||||
icon: TextCursorInput,
|
||||
title: t('setting.group.promptTextarea'),
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
const layout: SettingItemGroup = useMemo(
|
||||
() => ({
|
||||
children: [
|
||||
{
|
||||
children: <Switch />,
|
||||
desc: t('settingSplitPreviewerDesc'),
|
||||
label: t('settingSplitPreviewer'),
|
||||
name: 'layoutSplitPreview',
|
||||
valuePropName: 'checked',
|
||||
},
|
||||
{
|
||||
children: <Switch />,
|
||||
desc: t('settingHideFooterDesc'),
|
||||
label: t('settingHideFooter'),
|
||||
name: 'layoutHideFooter',
|
||||
valuePropName: 'checked',
|
||||
},
|
||||
],
|
||||
icon: Layout,
|
||||
title: t('settingGroupLayout'),
|
||||
}),
|
||||
[],
|
||||
);
|
||||
const layout: SettingItemGroup = useMemo(
|
||||
() => ({
|
||||
children: [
|
||||
{
|
||||
children: <Switch />,
|
||||
desc: t('setting.splitPreviewer.desc'),
|
||||
label: t('setting.splitPreviewer.title'),
|
||||
name: 'layoutSplitPreview',
|
||||
valuePropName: 'checked',
|
||||
},
|
||||
{
|
||||
children: <Switch />,
|
||||
desc: t('setting.hideFooter.desc'),
|
||||
label: t('setting.hideFooter.title'),
|
||||
name: 'layoutHideFooter',
|
||||
valuePropName: 'checked',
|
||||
},
|
||||
],
|
||||
icon: Layout,
|
||||
title: t('setting.group.layout'),
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
const quickSettingSidebar: SettingItemGroup = useMemo(
|
||||
() => ({
|
||||
children: [
|
||||
{
|
||||
children: <Switch />,
|
||||
desc: t('settingQuickSettingSidebarEnableDesc'),
|
||||
label: t('settingQuickSettingSidebarEnable'),
|
||||
name: 'enableSidebar',
|
||||
valuePropName: 'checked',
|
||||
},
|
||||
{
|
||||
children: <Switch />,
|
||||
desc: t('settingQuickSettingSidebarDefaultExpandDesc'),
|
||||
hidden: !rawSetting.enableSidebar,
|
||||
label: t('settingQuickSettingSidebarDefaultExpand'),
|
||||
name: 'sidebarExpand',
|
||||
valuePropName: 'checked',
|
||||
},
|
||||
{
|
||||
children: (
|
||||
<Segmented
|
||||
options={[
|
||||
const quickSettingSidebar: SettingItemGroup = useMemo(
|
||||
() => ({
|
||||
children: [
|
||||
{
|
||||
label: t('fixed'),
|
||||
value: 'fixed',
|
||||
children: <Switch />,
|
||||
desc: t('setting.quickSettingSidebar.displayMode.desc'),
|
||||
label: t('setting.quickSettingSidebar.displayMode.title'),
|
||||
name: 'enableSidebar',
|
||||
valuePropName: 'checked',
|
||||
},
|
||||
{
|
||||
label: t('float'),
|
||||
value: 'float',
|
||||
children: <Switch />,
|
||||
desc: t('setting.quickSettingSidebar.defaultExpand.desc'),
|
||||
hidden: !rawSetting.enableSidebar,
|
||||
label: t('setting.quickSettingSidebar.defaultExpand.title'),
|
||||
name: 'sidebarExpand',
|
||||
valuePropName: 'checked',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
),
|
||||
desc: t('settingQuickSettingSidebarDisplayModeDesc'),
|
||||
hidden: !rawSetting.enableSidebar,
|
||||
label: t('settingQuickSettingSidebarDisplayMode'),
|
||||
name: 'sidebarFixedMode',
|
||||
},
|
||||
{
|
||||
children: <InputNumber />,
|
||||
desc: t('settingQuickSettingSidebarDefaultWidthDesc'),
|
||||
hidden: !rawSetting.enableSidebar,
|
||||
label: t('settingQuickSettingSidebarDefaultWidth'),
|
||||
name: 'sidebarWidth',
|
||||
},
|
||||
],
|
||||
icon: PanelLeftClose,
|
||||
title: t('settingGroupQuickSettingSidebar'),
|
||||
}),
|
||||
[rawSetting.enableSidebar],
|
||||
);
|
||||
{
|
||||
children: (
|
||||
<Segmented
|
||||
options={[
|
||||
{
|
||||
label: t('sidebar.mode.fixed'),
|
||||
value: 'fixed',
|
||||
},
|
||||
{
|
||||
label: t('sidebar.mode.float'),
|
||||
value: 'float',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
),
|
||||
desc: t('setting.quickSettingSidebar.displayMode.desc'),
|
||||
hidden: !rawSetting.enableSidebar,
|
||||
label: t('setting.quickSettingSidebar.displayMode.title'),
|
||||
name: 'sidebarFixedMode',
|
||||
},
|
||||
{
|
||||
children: <InputNumber />,
|
||||
desc: t('setting.quickSettingSidebar.defaultWidth.desc'),
|
||||
hidden: !rawSetting.enableSidebar,
|
||||
label: t('setting.quickSettingSidebar.defaultWidth.title'),
|
||||
name: 'sidebarWidth',
|
||||
},
|
||||
],
|
||||
icon: PanelLeftClose,
|
||||
title: t('setting.group.quickSettingSidebar'),
|
||||
}),
|
||||
[rawSetting.enableSidebar],
|
||||
);
|
||||
|
||||
const extraNetworkSidebar: SettingItemGroup = useMemo(
|
||||
() => ({
|
||||
children: [
|
||||
{
|
||||
children: <Switch />,
|
||||
desc: t('settingExtraNetworkSidebarEnableDesc'),
|
||||
label: t('settingExtraNetworkSidebarEnable'),
|
||||
name: 'enableExtraNetworkSidebar',
|
||||
valuePropName: 'checked',
|
||||
},
|
||||
{
|
||||
children: (
|
||||
<Segmented
|
||||
options={[
|
||||
const extraNetworkSidebar: SettingItemGroup = useMemo(
|
||||
() => ({
|
||||
children: [
|
||||
{
|
||||
label: t('fixed'),
|
||||
value: 'fixed',
|
||||
children: <Switch />,
|
||||
desc: t('setting.extraNetworkSidebar.enable.desc'),
|
||||
label: t('setting.extraNetworkSidebar.enable.title'),
|
||||
name: 'enableExtraNetworkSidebar',
|
||||
valuePropName: 'checked',
|
||||
},
|
||||
{
|
||||
label: t('float'),
|
||||
value: 'float',
|
||||
children: (
|
||||
<Segmented
|
||||
options={[
|
||||
{
|
||||
label: t('sidebar.mode.fixed'),
|
||||
value: 'fixed',
|
||||
},
|
||||
{
|
||||
label: t('sidebar.mode.float'),
|
||||
value: 'float',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
),
|
||||
desc: t('setting.extraNetworkSidebar.displayMode.desc'),
|
||||
hidden: !rawSetting.enableExtraNetworkSidebar,
|
||||
label: t('setting.extraNetworkSidebar.displayMode.title'),
|
||||
name: 'extraNetworkFixedMode',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
),
|
||||
desc: t('settingExtraNetworkSidebarDisplayModeDesc'),
|
||||
hidden: !rawSetting.enableExtraNetworkSidebar,
|
||||
label: t('settingExtraNetworkSidebarDisplayMode'),
|
||||
name: 'extraNetworkFixedMode',
|
||||
},
|
||||
{
|
||||
children: <Switch />,
|
||||
desc: t('settingExtraNetworkSidebarDefaultExpandDesc'),
|
||||
hidden: !rawSetting.enableExtraNetworkSidebar,
|
||||
label: t('settingExtraNetworkSidebarDefaultExpand'),
|
||||
name: 'extraNetworkSidebarExpand',
|
||||
valuePropName: 'checked',
|
||||
},
|
||||
{
|
||||
children: <InputNumber />,
|
||||
desc: t('settingExtraNetworkSidebarDefaultWidthDesc'),
|
||||
hidden: !rawSetting.enableExtraNetworkSidebar,
|
||||
label: t('settingExtraNetworkSidebarDefaultWidth'),
|
||||
name: 'extraNetworkSidebarWidth',
|
||||
},
|
||||
{
|
||||
children: <InputNumber />,
|
||||
desc: t('settingExtraNetworkSidebarDefaultCardSizeDesc'),
|
||||
hidden: !rawSetting.enableExtraNetworkSidebar,
|
||||
label: t('settingExtraNetworkSidebarDefaultCardSize'),
|
||||
name: 'extraNetworkCardSize',
|
||||
},
|
||||
],
|
||||
icon: PanelRightClose,
|
||||
title: t('settingGroupExtraNetworkSidebar'),
|
||||
}),
|
||||
[rawSetting.enableExtraNetworkSidebar],
|
||||
);
|
||||
{
|
||||
children: <Switch />,
|
||||
desc: t('setting.extraNetworkSidebar.defaultExpand.desc'),
|
||||
hidden: !rawSetting.enableExtraNetworkSidebar,
|
||||
label: t('setting.extraNetworkSidebar.defaultExpand.title'),
|
||||
name: 'extraNetworkSidebarExpand',
|
||||
valuePropName: 'checked',
|
||||
},
|
||||
{
|
||||
children: <InputNumber />,
|
||||
desc: t('setting.extraNetworkSidebar.defaultWidth.desc'),
|
||||
hidden: !rawSetting.enableExtraNetworkSidebar,
|
||||
label: t('setting.extraNetworkSidebar.defaultWidth.title'),
|
||||
name: 'extraNetworkSidebarWidth',
|
||||
},
|
||||
{
|
||||
children: <InputNumber />,
|
||||
desc: t('setting.extraNetworkSidebar.defaultCardSize.desc'),
|
||||
hidden: !rawSetting.enableExtraNetworkSidebar,
|
||||
label: t('setting.extraNetworkSidebar.defaultCardSize.title'),
|
||||
name: 'extraNetworkCardSize',
|
||||
},
|
||||
],
|
||||
icon: PanelRightClose,
|
||||
title: t('setting.group.extraNetworkSidebar'),
|
||||
}),
|
||||
[rawSetting.enableExtraNetworkSidebar],
|
||||
);
|
||||
|
||||
return (
|
||||
<Form
|
||||
className={styles}
|
||||
footer={
|
||||
<>
|
||||
<Button htmlType="button" onClick={onReset} style={{ borderRadius: 4 }}>
|
||||
{t('settingButtonReset')}
|
||||
</Button>
|
||||
<Button htmlType="submit" style={{ borderRadius: 4 }} type="primary">
|
||||
{t('settingButtonSubmit')}
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
initialValues={setting}
|
||||
items={[theme, promptTextarea, layout, quickSettingSidebar, extraNetworkSidebar]}
|
||||
onFinish={onFinish}
|
||||
onValuesChange={(_, v) => setRawSetting(v)}
|
||||
/>
|
||||
);
|
||||
return (
|
||||
<Form
|
||||
className={styles}
|
||||
footer={
|
||||
<>
|
||||
<Button htmlType="button" onClick={onReset} style={{ borderRadius: 4 }}>
|
||||
{t('setting.button.reset')}
|
||||
</Button>
|
||||
<Button htmlType="submit" style={{ borderRadius: 4 }} type="primary">
|
||||
{t('setting.button.submit')}
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
initialValues={setting}
|
||||
items={[theme, promptTextarea, layout, quickSettingSidebar, extraNetworkSidebar]}
|
||||
onFinish={onFinish}
|
||||
onValuesChange={(_, v) => setRawSetting(v)}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export default SettingForm;
|
||||
|
|
|
|||
|
|
@ -1,33 +1,33 @@
|
|||
import {
|
||||
neutralColors as nc,
|
||||
neutralColorsSwatches as ncs,
|
||||
primaryColorsSwatches as pcs,
|
||||
primaryColors as ps,
|
||||
neutralColors as nc,
|
||||
neutralColorsSwatches as ncs,
|
||||
primaryColorsSwatches as pcs,
|
||||
primaryColors as ps,
|
||||
} from '@lobehub/ui';
|
||||
|
||||
import { kitchenNeutral, kitchenPrimary } from '@/styles/kitchenColors';
|
||||
|
||||
export const primaryColors = {
|
||||
kitchen: kitchenPrimary.dark.colorPrimary,
|
||||
...ps,
|
||||
kitchen: kitchenPrimary.dark.colorPrimary,
|
||||
...ps,
|
||||
};
|
||||
|
||||
export const primaryColorsSwatches = [primaryColors.kitchen, ...pcs];
|
||||
|
||||
export const neutralColors = {
|
||||
kitchen: kitchenNeutral.dark.colorNeutral,
|
||||
...nc,
|
||||
kitchen: kitchenNeutral.dark.colorNeutral,
|
||||
...nc,
|
||||
};
|
||||
|
||||
export const neutralColorsSwatches = [neutralColors.kitchen, ...ncs];
|
||||
|
||||
export const findCustomThemeName = (type: 'primary' | 'neutral', value?: string): any => {
|
||||
if (!value) return '';
|
||||
let res = type === 'primary' ? primaryColors : neutralColors;
|
||||
let result = Object.entries(res).find((item) => {
|
||||
return item[1] === value;
|
||||
});
|
||||
return result === null || result === void 0 ? void 0 : result[0];
|
||||
if (!value) return '';
|
||||
let res = type === 'primary' ? primaryColors : neutralColors;
|
||||
let result = Object.entries(res).find((item) => {
|
||||
return item[1] === value;
|
||||
});
|
||||
return result === null || result === void 0 ? void 0 : result[0];
|
||||
};
|
||||
|
||||
export type PrimaryColor = keyof typeof primaryColors;
|
||||
|
|
|
|||
|
|
@ -16,27 +16,27 @@ export interface SettingProps {
|
|||
}
|
||||
|
||||
const Setting = memo<SettingProps>(({ open, onCancel }) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Modal
|
||||
footer={false}
|
||||
onCancel={onCancel}
|
||||
open={open}
|
||||
title={
|
||||
<Flexbox align={'center'} gap={4} horizontal>
|
||||
<a href={homepage} rel="noreferrer" target="_blank">
|
||||
<ActionIcon icon={Book} title="Setting Documents" />
|
||||
</a>
|
||||
<Space>
|
||||
{t('themeSetting')}
|
||||
<VersionTag />
|
||||
</Space>
|
||||
</Flexbox>
|
||||
}
|
||||
>
|
||||
<SettingForm />
|
||||
</Modal>
|
||||
);
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Modal
|
||||
footer={false}
|
||||
onCancel={onCancel}
|
||||
open={open}
|
||||
title={
|
||||
<Flexbox align={'center'} gap={4} horizontal>
|
||||
<a href={homepage} rel="noreferrer" target="_blank">
|
||||
<ActionIcon icon={Book} title="Setting Documents" />
|
||||
</a>
|
||||
<Space>
|
||||
{t('modal.themeSetting.title')}
|
||||
<VersionTag />
|
||||
</Space>
|
||||
</Flexbox>
|
||||
}
|
||||
>
|
||||
<SettingForm />
|
||||
</Modal>
|
||||
);
|
||||
});
|
||||
|
||||
export default Setting;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { createStyles } from 'antd-style';
|
||||
|
||||
export const useStyles = createStyles(
|
||||
({ css, responsive }) => css`
|
||||
({ css, responsive }) => css`
|
||||
${responsive.mobile} {
|
||||
.ant-row.ant-form-item-row {
|
||||
flex-direction: column !important;
|
||||
|
|
|
|||
|
|
@ -1,37 +1,37 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
|
||||
export const useExternalTextareaObserver = (textareaSelector: string) => {
|
||||
const [value, setValue] = useState('');
|
||||
const [value, setValue] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
const observerCallback: MutationCallback = (mutationsList) => {
|
||||
for (const mutation of mutationsList) {
|
||||
if (mutation.type === 'attributes') {
|
||||
const externalTextarea = document.querySelector(textareaSelector) as HTMLTextAreaElement;
|
||||
setValue(externalTextarea.value);
|
||||
useEffect(() => {
|
||||
const observerCallback: MutationCallback = (mutationsList) => {
|
||||
for (const mutation of mutationsList) {
|
||||
if (mutation.type === 'attributes') {
|
||||
const externalTextarea = document.querySelector(textareaSelector) as HTMLTextAreaElement;
|
||||
setValue(externalTextarea.value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const observerOptions: MutationObserverInit = {
|
||||
attributes: true,
|
||||
characterData: true,
|
||||
childList: true,
|
||||
subtree: true,
|
||||
};
|
||||
|
||||
const observer = new MutationObserver(observerCallback);
|
||||
const externalTextarea = document.querySelector(textareaSelector) as HTMLTextAreaElement | null;
|
||||
|
||||
if (externalTextarea) {
|
||||
observer.observe(externalTextarea, observerOptions);
|
||||
setValue(externalTextarea.value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const observerOptions: MutationObserverInit = {
|
||||
attributes: true,
|
||||
characterData: true,
|
||||
childList: true,
|
||||
subtree: true,
|
||||
};
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
};
|
||||
}, [textareaSelector]);
|
||||
|
||||
const observer = new MutationObserver(observerCallback);
|
||||
const externalTextarea = document.querySelector(textareaSelector) as HTMLTextAreaElement | null;
|
||||
|
||||
if (externalTextarea) {
|
||||
observer.observe(externalTextarea, observerOptions);
|
||||
setValue(externalTextarea.value);
|
||||
}
|
||||
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
};
|
||||
}, [textareaSelector]);
|
||||
|
||||
return value;
|
||||
return value;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,29 +1,29 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
|
||||
const checkIsDarkMode = () => {
|
||||
try {
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export const useIsDarkMode = () => {
|
||||
const [isDarkMode, setIsDarkMode] = useState(checkIsDarkMode());
|
||||
const [isDarkMode, setIsDarkMode] = useState(checkIsDarkMode());
|
||||
|
||||
useEffect(() => {
|
||||
const mqList = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
useEffect(() => {
|
||||
const mqList = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
|
||||
const listener = (event: any) => {
|
||||
setIsDarkMode(event.matches);
|
||||
};
|
||||
const listener = (event: any) => {
|
||||
setIsDarkMode(event.matches);
|
||||
};
|
||||
|
||||
mqList.addEventListener('change', listener);
|
||||
mqList.addEventListener('change', listener);
|
||||
|
||||
return () => {
|
||||
mqList.removeEventListener('change', listener);
|
||||
};
|
||||
}, []);
|
||||
return () => {
|
||||
mqList.removeEventListener('change', listener);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return isDarkMode;
|
||||
return isDarkMode;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import {
|
||||
type DivProps,
|
||||
ThemeProvider,
|
||||
colorScales,
|
||||
generateColorNeutralPalette,
|
||||
generateColorPalette,
|
||||
neutralColorScales,
|
||||
type DivProps,
|
||||
ThemeProvider,
|
||||
colorScales,
|
||||
generateColorNeutralPalette,
|
||||
generateColorPalette,
|
||||
neutralColorScales,
|
||||
} from '@lobehub/ui';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import qs from 'query-string';
|
||||
|
|
@ -16,58 +16,58 @@ import { selectors, useAppStore } from '@/store';
|
|||
import { kitchenNeutral, kitchenPrimary } from '@/styles/kitchenColors';
|
||||
|
||||
const Layout = memo<DivProps>(({ children }) => {
|
||||
const { onSetThemeMode, themeMode } = useAppStore(
|
||||
(st) => ({ onInit: st.onInit, onSetThemeMode: st.onSetThemeMode, themeMode: st.themeMode }),
|
||||
shallow,
|
||||
);
|
||||
const setting = useAppStore(selectors.currentSetting, isEqual);
|
||||
const isDarkMode = useIsDarkMode();
|
||||
const { onSetThemeMode, themeMode } = useAppStore(
|
||||
(st) => ({ onInit: st.onInit, onSetThemeMode: st.onSetThemeMode, themeMode: st.themeMode }),
|
||||
shallow,
|
||||
);
|
||||
const setting = useAppStore(selectors.currentSetting, isEqual);
|
||||
const isDarkMode = useIsDarkMode();
|
||||
|
||||
useEffect(() => {
|
||||
const queryTheme: any = String(qs.parseUrl(window.location.href).query.__theme || '');
|
||||
if (queryTheme) {
|
||||
document.body.classList.add(queryTheme);
|
||||
onSetThemeMode(queryTheme);
|
||||
} else {
|
||||
document.body.classList.add(isDarkMode ? 'dark' : 'light');
|
||||
onSetThemeMode(isDarkMode ? 'dark' : 'light');
|
||||
}
|
||||
}, [isDarkMode]);
|
||||
useEffect(() => {
|
||||
const queryTheme: any = String(qs.parseUrl(window.location.href).query.__theme || '');
|
||||
if (queryTheme) {
|
||||
document.body.classList.add(queryTheme);
|
||||
onSetThemeMode(queryTheme);
|
||||
} else {
|
||||
document.body.classList.add(isDarkMode ? 'dark' : 'light');
|
||||
onSetThemeMode(isDarkMode ? 'dark' : 'light');
|
||||
}
|
||||
}, [isDarkMode]);
|
||||
|
||||
const genCustomToken = useCallback(() => {
|
||||
let primaryTokens = {};
|
||||
let neutralTokens = {};
|
||||
if (setting.primaryColor) {
|
||||
if (setting.primaryColor === 'kitchen') {
|
||||
primaryTokens = kitchenPrimary[themeMode];
|
||||
} else {
|
||||
const scale = colorScales[setting.primaryColor];
|
||||
primaryTokens = generateColorPalette({ appearance: themeMode, scale, type: 'Primary' });
|
||||
}
|
||||
}
|
||||
if (setting.neutralColor) {
|
||||
if (setting.neutralColor === 'kitchen') {
|
||||
neutralTokens = kitchenNeutral[themeMode];
|
||||
} else {
|
||||
const scale = neutralColorScales[setting.neutralColor];
|
||||
neutralTokens = generateColorNeutralPalette({ appearance: themeMode, scale });
|
||||
}
|
||||
}
|
||||
const genCustomToken = useCallback(() => {
|
||||
let primaryTokens = {};
|
||||
let neutralTokens = {};
|
||||
if (setting.primaryColor) {
|
||||
if (setting.primaryColor === 'kitchen') {
|
||||
primaryTokens = kitchenPrimary[themeMode];
|
||||
} else {
|
||||
const scale = colorScales[setting.primaryColor];
|
||||
primaryTokens = generateColorPalette({ appearance: themeMode, scale, type: 'Primary' });
|
||||
}
|
||||
}
|
||||
if (setting.neutralColor) {
|
||||
if (setting.neutralColor === 'kitchen') {
|
||||
neutralTokens = kitchenNeutral[themeMode];
|
||||
} else {
|
||||
const scale = neutralColorScales[setting.neutralColor];
|
||||
neutralTokens = generateColorNeutralPalette({ appearance: themeMode, scale });
|
||||
}
|
||||
}
|
||||
|
||||
return { ...primaryTokens, ...neutralTokens };
|
||||
}, [setting.primaryColor, setting.neutralColor, themeMode]);
|
||||
return { ...primaryTokens, ...neutralTokens };
|
||||
}, [setting.primaryColor, setting.neutralColor, themeMode]);
|
||||
|
||||
return (
|
||||
setting && (
|
||||
<ThemeProvider
|
||||
customToken={genCustomToken}
|
||||
enableWebfonts={setting.enableWebFont}
|
||||
themeMode={themeMode}
|
||||
>
|
||||
{children}
|
||||
</ThemeProvider>
|
||||
)
|
||||
);
|
||||
return (
|
||||
setting && (
|
||||
<ThemeProvider
|
||||
customToken={genCustomToken}
|
||||
enableWebfonts={setting.enableWebFont}
|
||||
themeMode={themeMode}
|
||||
>
|
||||
{children}
|
||||
</ThemeProvider>
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
export default Layout;
|
||||
|
|
|
|||
|
|
@ -7,13 +7,13 @@ import { SETTING_KEY, type WebuiSetting } from '@/store';
|
|||
const localSetting = JSON.parse(localStorage.getItem(SETTING_KEY) as any) as WebuiSetting;
|
||||
|
||||
i18next
|
||||
.use(initReactI18next)
|
||||
.use(HttpBackend)
|
||||
.init<HttpBackendOptions>({
|
||||
backend: {
|
||||
loadPath: '/lobe/locales/{{lng}}',
|
||||
},
|
||||
debug: process.env.NODE_ENV === 'development',
|
||||
fallbackLng: 'en_US',
|
||||
lng: localSetting?.i18n || 'en_US',
|
||||
});
|
||||
.use(initReactI18next)
|
||||
.use(HttpBackend)
|
||||
.init<HttpBackendOptions>({
|
||||
backend: {
|
||||
loadPath: '/lobe/locales/{{lng}}',
|
||||
},
|
||||
debug: process.env.NODE_ENV === 'development',
|
||||
fallbackLng: 'en_US',
|
||||
lng: localSetting?.i18n || 'en_US',
|
||||
});
|
||||
|
|
|
|||
34
src/main.tsx
34
src/main.tsx
|
|
@ -1,3 +1,4 @@
|
|||
import { consola } from 'consola';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
|
||||
import Page from './app/page';
|
||||
|
|
@ -7,21 +8,20 @@ if (window.global === undefined) window.global = window;
|
|||
const skipLoad = window.location.href.includes('dev') && process.env.NODE_ENV === 'production';
|
||||
|
||||
if (!skipLoad) {
|
||||
document.addEventListener(
|
||||
'DOMContentLoaded',
|
||||
() => {
|
||||
console.time('🤯 Lobe Theme load in DevMode');
|
||||
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(<Page />);
|
||||
console.timeEnd('🤯 Lobe Theme load in DevMode');
|
||||
},
|
||||
{ once: true },
|
||||
);
|
||||
document.addEventListener(
|
||||
'DOMContentLoaded',
|
||||
() => {
|
||||
consola.start(`🤯 Lobe Theme load in ${process.env.NODE_ENV}`);
|
||||
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(<Page />);
|
||||
},
|
||||
{ once: true },
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,15 +9,15 @@ import { themeConfig } from './promptTheme';
|
|||
import { useStyles } from './style';
|
||||
|
||||
const options: any = {
|
||||
langs: [
|
||||
{
|
||||
aliases: ['prompt'],
|
||||
grammar,
|
||||
id: 'prompt',
|
||||
scopeName: 'source.prompt',
|
||||
},
|
||||
],
|
||||
themes: [themeConfig(true), themeConfig(false)],
|
||||
langs: [
|
||||
{
|
||||
aliases: ['prompt'],
|
||||
grammar,
|
||||
id: 'prompt',
|
||||
scopeName: 'source.prompt',
|
||||
},
|
||||
],
|
||||
themes: [themeConfig(true), themeConfig(false)],
|
||||
};
|
||||
|
||||
interface AppProps {
|
||||
|
|
@ -25,63 +25,63 @@ interface AppProps {
|
|||
}
|
||||
|
||||
const App = memo<AppProps>(({ parentId }) => {
|
||||
const ref: any = useRef(null);
|
||||
const [prompt, setPrompt] = useState<string>('');
|
||||
const { styles, theme } = useStyles();
|
||||
const nativeTextareaValue = useExternalTextareaObserver(`${parentId} label textarea`);
|
||||
const nativeTextarea = useMemo(
|
||||
() => gradioApp().querySelector(`${parentId} label textarea`) as HTMLTextAreaElement,
|
||||
[parentId],
|
||||
);
|
||||
const size = useSize(nativeTextarea);
|
||||
const scroll = useScroll(nativeTextarea);
|
||||
const ref: any = useRef(null);
|
||||
const [prompt, setPrompt] = useState<string>('');
|
||||
const { styles, theme } = useStyles();
|
||||
const nativeTextareaValue = useExternalTextareaObserver(`${parentId} label textarea`);
|
||||
const nativeTextarea = useMemo(
|
||||
() => gradioApp().querySelector(`${parentId} label textarea`) as HTMLTextAreaElement,
|
||||
[parentId],
|
||||
);
|
||||
const size = useSize(nativeTextarea);
|
||||
const scroll = useScroll(nativeTextarea);
|
||||
|
||||
const handlePromptChange = useCallback((event: any) => {
|
||||
setPrompt(event.target.value);
|
||||
}, []);
|
||||
const handlePromptChange = useCallback((event: any) => {
|
||||
setPrompt(event.target.value);
|
||||
}, []);
|
||||
|
||||
const handlePromptResize = useCallback(() => {
|
||||
if (nativeTextarea.clientHeight < nativeTextarea.scrollHeight) {
|
||||
return size?.width === undefined ? '' : size?.width + 6;
|
||||
} else {
|
||||
return size?.width === undefined ? '' : size?.width + 2;
|
||||
}
|
||||
}, [nativeTextarea.clientWidth]);
|
||||
const handlePromptResize = useCallback(() => {
|
||||
if (nativeTextarea.clientHeight < nativeTextarea.scrollHeight) {
|
||||
return size?.width === undefined ? '' : size?.width + 6;
|
||||
} else {
|
||||
return size?.width === undefined ? '' : size?.width + 2;
|
||||
}
|
||||
}, [nativeTextarea.clientWidth]);
|
||||
|
||||
useEffect(() => {
|
||||
ref.current.scroll(0, scroll?.top || 0);
|
||||
}, [scroll?.top]);
|
||||
useEffect(() => {
|
||||
ref.current.scroll(0, scroll?.top || 0);
|
||||
}, [scroll?.top]);
|
||||
|
||||
useEffect(() => {
|
||||
nativeTextarea.addEventListener('change', handlePromptChange);
|
||||
return () => {
|
||||
nativeTextarea.removeEventListener('change', handlePromptChange);
|
||||
};
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
nativeTextarea.addEventListener('change', handlePromptChange);
|
||||
return () => {
|
||||
nativeTextarea.removeEventListener('change', handlePromptChange);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (theme) {
|
||||
nativeTextarea.style.color = 'transparent';
|
||||
nativeTextarea.style.caretColor = theme.colorSuccess;
|
||||
}
|
||||
}, [theme]);
|
||||
useEffect(() => {
|
||||
if (theme) {
|
||||
nativeTextarea.style.color = 'transparent';
|
||||
nativeTextarea.style.caretColor = theme.colorSuccess;
|
||||
}
|
||||
}, [theme]);
|
||||
|
||||
useEffect(() => {
|
||||
setPrompt(nativeTextareaValue);
|
||||
}, [nativeTextareaValue]);
|
||||
useEffect(() => {
|
||||
setPrompt(nativeTextareaValue);
|
||||
}, [nativeTextareaValue]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={styles.container}
|
||||
data-code-type="highlighter"
|
||||
ref={ref}
|
||||
style={{ height: size?.height, width: handlePromptResize() }}
|
||||
>
|
||||
<SyntaxHighlighter language="prompt" options={options}>
|
||||
{prompt.trim()}
|
||||
</SyntaxHighlighter>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div
|
||||
className={styles.container}
|
||||
data-code-type="highlighter"
|
||||
ref={ref}
|
||||
style={{ height: size?.height, width: handlePromptResize() }}
|
||||
>
|
||||
<SyntaxHighlighter language="prompt" options={options}>
|
||||
{prompt.trim()}
|
||||
</SyntaxHighlighter>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export default App;
|
||||
|
|
|
|||
|
|
@ -15,35 +15,35 @@ export interface SyntaxHighlighterProps {
|
|||
}
|
||||
|
||||
const SyntaxHighlighter = memo<SyntaxHighlighterProps>(({ children, language, options }) => {
|
||||
const { styles } = useStyles();
|
||||
const { isDarkMode } = useThemeMode();
|
||||
const [codeToHtml, isLoading] = useHighlight((s) => [s.codeToHtml, !s.highlighter]);
|
||||
const { styles } = useStyles();
|
||||
const { isDarkMode } = useThemeMode();
|
||||
const [codeToHtml, isLoading] = useHighlight((s) => [s.codeToHtml, !s.highlighter]);
|
||||
|
||||
useEffect(() => {
|
||||
useHighlight.getState().initHighlighter(options);
|
||||
}, [options]);
|
||||
useEffect(() => {
|
||||
useHighlight.getState().initHighlighter(options);
|
||||
}, [options]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{isLoading ? (
|
||||
<code>{children}</code>
|
||||
) : (
|
||||
<div
|
||||
className={styles.shiki}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: codeToHtml(children, language, isDarkMode) || '',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
return (
|
||||
<>
|
||||
{isLoading ? (
|
||||
<code>{children}</code>
|
||||
) : (
|
||||
<div
|
||||
className={styles.shiki}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: codeToHtml(children, language, isDarkMode) || '',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isLoading && (
|
||||
<Center className={styles.loading} gap={8} horizontal>
|
||||
<Icon icon={Loader2} spin />
|
||||
{isLoading && (
|
||||
<Center className={styles.loading} gap={8} horizontal>
|
||||
<Icon icon={Loader2} spin />
|
||||
Highlighting...
|
||||
</Center>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
</Center>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
export default SyntaxHighlighter;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { StrictMode, Suspense, memo } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import Layout from '@/layouts';
|
||||
import { useAppStore } from '@/store';
|
||||
|
|
@ -8,26 +7,26 @@ import { useAppStore } from '@/store';
|
|||
import App from './App';
|
||||
|
||||
const Main = memo<{ parentId: string }>(({ parentId }) => {
|
||||
const { loading } = useAppStore((st) => ({ loading: st.loading }), shallow);
|
||||
const loading = useAppStore((st) => st.loading);
|
||||
|
||||
return <Layout>{loading === false && <App parentId={parentId} />}</Layout>;
|
||||
return <Layout>{loading === false && <App parentId={parentId} />}</Layout>;
|
||||
});
|
||||
|
||||
export const PromptHighlight = (parentId: string, containerId: string) => {
|
||||
if (document.querySelector(containerId)) return;
|
||||
const settingsDiv = document.createElement('div') as HTMLDivElement;
|
||||
settingsDiv.id = containerId.replace('#', '');
|
||||
if (document.querySelector(containerId)) return;
|
||||
const settingsDiv = document.createElement('div') as HTMLDivElement;
|
||||
settingsDiv.id = containerId.replace('#', '');
|
||||
|
||||
(gradioApp().querySelector(parentId) as HTMLDivElement).insertBefore(
|
||||
settingsDiv,
|
||||
(gradioApp().querySelector(parentId) as HTMLDivElement).firstChild,
|
||||
);
|
||||
(gradioApp().querySelector(parentId) as HTMLDivElement).insertBefore(
|
||||
settingsDiv,
|
||||
(gradioApp().querySelector(parentId) as HTMLDivElement).firstChild,
|
||||
);
|
||||
|
||||
createRoot(settingsDiv).render(
|
||||
<StrictMode>
|
||||
<Suspense fallback="loading...">
|
||||
<Main parentId={parentId} />
|
||||
</Suspense>
|
||||
</StrictMode>,
|
||||
);
|
||||
createRoot(settingsDiv).render(
|
||||
<StrictMode>
|
||||
<Suspense fallback="loading...">
|
||||
<Main parentId={parentId} />
|
||||
</Suspense>
|
||||
</StrictMode>,
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,76 +2,76 @@ import { colors as colorScales } from '@lobehub/ui';
|
|||
import { ThemeAppearance } from 'antd-style';
|
||||
|
||||
export const themeConfig: any = (isDarkMode: ThemeAppearance) => {
|
||||
const type = isDarkMode ? 'dark' : 'light';
|
||||
const type = isDarkMode ? 'dark' : 'light';
|
||||
|
||||
const colorYellow = isDarkMode ? colorScales.yellow[type][9] : colorScales.gold[type][10];
|
||||
const colorOrange = isDarkMode ? colorScales.gold[type][9] : colorScales.orange[type][10];
|
||||
const colorVolcano = isDarkMode ? colorScales.volcano[type][10] : colorScales.volcano[type][8];
|
||||
const colorGreen = isDarkMode ? colorScales.lime[type][9] : colorScales.green[type][10];
|
||||
const colorBlue = isDarkMode ? colorScales.blue[type][9] : colorScales.geekblue[type][9];
|
||||
const colorPurple = isDarkMode ? colorScales.purple[type][11] : colorScales.purple[type][8];
|
||||
return {
|
||||
colors: {
|
||||
'editor.foreground': colorGreen,
|
||||
},
|
||||
name: type,
|
||||
tokenColors: [
|
||||
{
|
||||
scope: 'comma',
|
||||
settings: {
|
||||
foreground: colorGreen,
|
||||
const colorYellow = isDarkMode ? colorScales.yellow[type][9] : colorScales.gold[type][10];
|
||||
const colorOrange = isDarkMode ? colorScales.gold[type][9] : colorScales.orange[type][10];
|
||||
const colorVolcano = isDarkMode ? colorScales.volcano[type][10] : colorScales.volcano[type][8];
|
||||
const colorGreen = isDarkMode ? colorScales.lime[type][9] : colorScales.green[type][10];
|
||||
const colorBlue = isDarkMode ? colorScales.blue[type][9] : colorScales.geekblue[type][9];
|
||||
const colorPurple = isDarkMode ? colorScales.purple[type][11] : colorScales.purple[type][8];
|
||||
return {
|
||||
colors: {
|
||||
'editor.foreground': colorGreen,
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'func',
|
||||
settings: {
|
||||
foreground: colorPurple,
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['and', 'break'],
|
||||
settings: {
|
||||
foreground: colorBlue,
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'bracket',
|
||||
settings: {
|
||||
foreground: colorPurple,
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'model-type',
|
||||
settings: {
|
||||
fontStyle: 'italic',
|
||||
foreground: colorVolcano,
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'model-name',
|
||||
settings: {
|
||||
foreground: colorOrange,
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'model-bracket',
|
||||
settings: {
|
||||
foreground: colorPurple,
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'number',
|
||||
settings: {
|
||||
foreground: colorPurple,
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'wildcards',
|
||||
settings: {
|
||||
foreground: colorYellow,
|
||||
},
|
||||
},
|
||||
],
|
||||
type,
|
||||
};
|
||||
name: type,
|
||||
tokenColors: [
|
||||
{
|
||||
scope: 'comma',
|
||||
settings: {
|
||||
foreground: colorGreen,
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'func',
|
||||
settings: {
|
||||
foreground: colorPurple,
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['and', 'break'],
|
||||
settings: {
|
||||
foreground: colorBlue,
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'bracket',
|
||||
settings: {
|
||||
foreground: colorPurple,
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'model-type',
|
||||
settings: {
|
||||
fontStyle: 'italic',
|
||||
foreground: colorVolcano,
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'model-name',
|
||||
settings: {
|
||||
foreground: colorOrange,
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'model-bracket',
|
||||
settings: {
|
||||
foreground: colorPurple,
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'number',
|
||||
settings: {
|
||||
foreground: colorPurple,
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'wildcards',
|
||||
settings: {
|
||||
foreground: colorYellow,
|
||||
},
|
||||
},
|
||||
],
|
||||
type,
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { createStyles } from 'antd-style';
|
||||
|
||||
export const useStyles = createStyles(({ css, token, cx, stylish, prefixCls }) => {
|
||||
const prefix = `${prefixCls}-highlighter`;
|
||||
return {
|
||||
container: css`
|
||||
const prefix = `${prefixCls}-highlighter`;
|
||||
return {
|
||||
container: css`
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
overflow: hidden auto;
|
||||
|
|
@ -18,9 +18,9 @@ export const useStyles = createStyles(({ css, token, cx, stylish, prefixCls }) =
|
|||
vertical-align: bottom !important;
|
||||
}
|
||||
`,
|
||||
loading: cx(
|
||||
stylish.blur,
|
||||
css`
|
||||
loading: cx(
|
||||
stylish.blur,
|
||||
css`
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
top: 0;
|
||||
|
|
@ -38,10 +38,10 @@ export const useStyles = createStyles(({ css, token, cx, stylish, prefixCls }) =
|
|||
|
||||
border-radius: ${token.borderRadius};
|
||||
`,
|
||||
),
|
||||
shiki: cx(
|
||||
`${prefix}-shiki`,
|
||||
css`
|
||||
),
|
||||
shiki: cx(
|
||||
`${prefix}-shiki`,
|
||||
css`
|
||||
margin: 0;
|
||||
|
||||
.shiki {
|
||||
|
|
@ -55,6 +55,6 @@ export const useStyles = createStyles(({ css, token, cx, stylish, prefixCls }) =
|
|||
}
|
||||
}
|
||||
`,
|
||||
),
|
||||
};
|
||||
),
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -28,33 +28,33 @@ interface Store {
|
|||
}
|
||||
|
||||
export const useHighlight = createWithEqualityFn<Store>(
|
||||
(set, get) => ({
|
||||
codeToHtml: (text, language, isDarkMode) => {
|
||||
const { highlighter } = get();
|
||||
(set, get) => ({
|
||||
codeToHtml: (text, language, isDarkMode) => {
|
||||
const { highlighter } = get();
|
||||
|
||||
if (!highlighter) return '';
|
||||
if (!highlighter) return '';
|
||||
|
||||
try {
|
||||
return highlighter?.codeToHtml(text, {
|
||||
lang: language,
|
||||
theme: isDarkMode ? 'dark' : 'light',
|
||||
});
|
||||
} catch {
|
||||
return text;
|
||||
}
|
||||
},
|
||||
highlighter: undefined,
|
||||
try {
|
||||
return highlighter?.codeToHtml(text, {
|
||||
lang: language,
|
||||
theme: isDarkMode ? 'dark' : 'light',
|
||||
});
|
||||
} catch {
|
||||
return text;
|
||||
}
|
||||
},
|
||||
highlighter: undefined,
|
||||
|
||||
initHighlighter: async(options) => {
|
||||
if (!get().highlighter) {
|
||||
const highlighter = await getHighlighter({
|
||||
langs: options?.langs,
|
||||
themes: options?.themes,
|
||||
});
|
||||
initHighlighter: async(options) => {
|
||||
if (!get().highlighter) {
|
||||
const highlighter = await getHighlighter({
|
||||
langs: options?.langs,
|
||||
themes: options?.themes,
|
||||
});
|
||||
|
||||
set({ highlighter });
|
||||
}
|
||||
},
|
||||
}),
|
||||
shallow,
|
||||
set({ highlighter });
|
||||
}
|
||||
},
|
||||
}),
|
||||
shallow,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,17 +1,19 @@
|
|||
import { consola } from 'consola';
|
||||
|
||||
const TAB_PREFIX_LIST = ['txt2img', 'img2img'] as const;
|
||||
const MODEL_TYPE_LIST = [
|
||||
'textual_inversion',
|
||||
'hypernetworks',
|
||||
'checkpoints',
|
||||
'lora',
|
||||
'lycoris',
|
||||
'textual_inversion',
|
||||
'hypernetworks',
|
||||
'checkpoints',
|
||||
'lora',
|
||||
'lycoris',
|
||||
] as const;
|
||||
const MODEL_TYPE = {
|
||||
checkpoints: 'ckp',
|
||||
hypernetworks: 'hyper',
|
||||
lora: 'lora',
|
||||
lycoris: 'lycoris',
|
||||
textual_inversion: 'ti',
|
||||
checkpoints: 'ckp',
|
||||
hypernetworks: 'hyper',
|
||||
lora: 'lora',
|
||||
lycoris: 'lycoris',
|
||||
textual_inversion: 'ti',
|
||||
} as const satisfies Record<(typeof MODEL_TYPE_LIST)[number], string>;
|
||||
const CARDID_SUFFIX = 'cards' as const;
|
||||
|
||||
|
|
@ -30,50 +32,50 @@ const DOM_CACHE_KEY = `${DOM_CACHE_PREFIX}Done` as const;
|
|||
const DOM_CACHE_VALUE = '1' as const;
|
||||
|
||||
const styleButton = (node: HTMLElement, isThumbMode: boolean) => {
|
||||
if (isThumbMode) {
|
||||
node.style.display = BTN_THUMB_DISPLAY;
|
||||
node.style.fontSize = BTN_THUMB_FONT_SIZE;
|
||||
node.style.position = BTN_THUMB_POS;
|
||||
node.style.backgroundImage = BTN_THUMB_BACKGROUND_IMAGE;
|
||||
} else {
|
||||
node.style.fontSize = BTN_FONT_SIZE;
|
||||
node.style.margin = BTN_MARGIN;
|
||||
}
|
||||
if (isThumbMode) {
|
||||
node.style.display = BTN_THUMB_DISPLAY;
|
||||
node.style.fontSize = BTN_THUMB_FONT_SIZE;
|
||||
node.style.position = BTN_THUMB_POS;
|
||||
node.style.backgroundImage = BTN_THUMB_BACKGROUND_IMAGE;
|
||||
} else {
|
||||
node.style.fontSize = BTN_FONT_SIZE;
|
||||
node.style.margin = BTN_MARGIN;
|
||||
}
|
||||
};
|
||||
|
||||
type IStrictNullable<T> = T | null;
|
||||
type INullable<T> = T | null | undefined;
|
||||
|
||||
function is_nullable<T>(v: INullable<T>): v is null | undefined {
|
||||
return v === undefined || v === null;
|
||||
return v === undefined || v === null;
|
||||
}
|
||||
|
||||
const updateCardForCivitai = () => {
|
||||
if (!document.querySelector('#tab_civitai_helper')) return;
|
||||
if (!document.querySelector('#tab_civitai_helper')) return;
|
||||
|
||||
const replacePreviewText = getTranslation('replace preview') || 'replace preview';
|
||||
const replacePreviewText = getTranslation('replace preview') || 'replace preview';
|
||||
|
||||
// Get component
|
||||
const chAlwaysDisplayCkb = document.querySelector(
|
||||
'#ch_always_display_ckb input',
|
||||
) as HTMLInputElement;
|
||||
const chShowButtonOnThumbCkb = document.querySelector(
|
||||
'#ch_show_btn_on_thumb_ckb input',
|
||||
) as HTMLInputElement;
|
||||
const chAlwaysDisplay = chAlwaysDisplayCkb?.checked || false;
|
||||
const chShowButtonOnThumb = chShowButtonOnThumbCkb?.checked || false;
|
||||
// Get component
|
||||
const chAlwaysDisplayCkb = document.querySelector(
|
||||
'#ch_always_display_ckb input',
|
||||
) as HTMLInputElement;
|
||||
const chShowButtonOnThumbCkb = document.querySelector(
|
||||
'#ch_show_btn_on_thumb_ckb input',
|
||||
) as HTMLInputElement;
|
||||
const chAlwaysDisplay = chAlwaysDisplayCkb?.checked || false;
|
||||
const chShowButtonOnThumb = chShowButtonOnThumbCkb?.checked || false;
|
||||
|
||||
// Change all "replace preview" into an icon
|
||||
let extraNetworkId: `${(typeof TAB_PREFIX_LIST)[number]}_${(typeof MODEL_TYPE_LIST)[number]}_${typeof CARDID_SUFFIX}`;
|
||||
let extraNetworkNode: IStrictNullable<HTMLElement>;
|
||||
let metadataButton: IStrictNullable<HTMLElement>;
|
||||
let additionalNode: HTMLElement;
|
||||
let replacePreviewButton: IStrictNullable<HTMLElement>;
|
||||
let ulNode: IStrictNullable<HTMLElement>;
|
||||
let searchTermNode: IStrictNullable<HTMLElement>;
|
||||
let searchTerm = '';
|
||||
let modelType: (typeof MODEL_TYPE)[keyof typeof MODEL_TYPE];
|
||||
let cards: INullable<
|
||||
// Change all "replace preview" into an icon
|
||||
let extraNetworkId: `${(typeof TAB_PREFIX_LIST)[number]}_${(typeof MODEL_TYPE_LIST)[number]}_${typeof CARDID_SUFFIX}`;
|
||||
let extraNetworkNode: IStrictNullable<HTMLElement>;
|
||||
let metadataButton: IStrictNullable<HTMLElement>;
|
||||
let additionalNode: HTMLElement;
|
||||
let replacePreviewButton: IStrictNullable<HTMLElement>;
|
||||
let ulNode: IStrictNullable<HTMLElement>;
|
||||
let searchTermNode: IStrictNullable<HTMLElement>;
|
||||
let searchTerm = '';
|
||||
let modelType: (typeof MODEL_TYPE)[keyof typeof MODEL_TYPE];
|
||||
let cards: INullable<
|
||||
NodeListOf<
|
||||
HTMLElement & {
|
||||
dataset: {
|
||||
|
|
@ -82,203 +84,203 @@ const updateCardForCivitai = () => {
|
|||
}
|
||||
>
|
||||
>;
|
||||
let needToAddButtons = false;
|
||||
let isThumbMode = false;
|
||||
let needToAddButtons = false;
|
||||
let isThumbMode = false;
|
||||
|
||||
const modelTypeHasCards: (typeof MODEL_TYPE_LIST)[number][] = [];
|
||||
const modelTypeHasCards: (typeof MODEL_TYPE_LIST)[number][] = [];
|
||||
|
||||
// Get current tab
|
||||
for (const activeTabType of TAB_PREFIX_LIST) {
|
||||
for (const jsModelType of MODEL_TYPE_LIST) {
|
||||
modelType = MODEL_TYPE[jsModelType];
|
||||
// Get model_type for python side
|
||||
// Get current tab
|
||||
for (const activeTabType of TAB_PREFIX_LIST) {
|
||||
for (const jsModelType of MODEL_TYPE_LIST) {
|
||||
modelType = MODEL_TYPE[jsModelType];
|
||||
// Get model_type for python side
|
||||
|
||||
extraNetworkId = `${activeTabType}_${jsModelType}_${CARDID_SUFFIX}`;
|
||||
extraNetworkNode = document.querySelector(`#${extraNetworkId}` as const);
|
||||
extraNetworkId = `${activeTabType}_${jsModelType}_${CARDID_SUFFIX}`;
|
||||
extraNetworkNode = document.querySelector(`#${extraNetworkId}` as const);
|
||||
|
||||
// Check if extra network node exists
|
||||
if (is_nullable(extraNetworkNode)) continue;
|
||||
// Check if extra network node exists
|
||||
if (is_nullable(extraNetworkNode)) continue;
|
||||
|
||||
// Check if extr network is under thumbnail mode
|
||||
isThumbMode = extraNetworkNode.classList.contains('extra-network-thumbs');
|
||||
// Check if extr network is under thumbnail mode
|
||||
isThumbMode = extraNetworkNode.classList.contains('extra-network-thumbs');
|
||||
|
||||
// Get all card nodes
|
||||
cards = extraNetworkNode.querySelectorAll('.card');
|
||||
const pending = !!document.querySelector(`#${extraNetworkId}_html .pending`);
|
||||
if (!cards?.length || pending) {
|
||||
if (!pending && extraNetworkNode.querySelector('.nocards')) {
|
||||
modelTypeHasCards.push(jsModelType);
|
||||
}
|
||||
// Get all card nodes
|
||||
cards = extraNetworkNode.querySelectorAll('.card');
|
||||
const pending = !!document.querySelector(`#${extraNetworkId}_html .pending`);
|
||||
if (!cards?.length || pending) {
|
||||
if (!pending && extraNetworkNode.querySelector('.nocards')) {
|
||||
modelTypeHasCards.push(jsModelType);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
modelTypeHasCards.push(jsModelType);
|
||||
|
||||
for (const card of cards) {
|
||||
if (card.dataset[DOM_CACHE_KEY] === DOM_CACHE_VALUE) break;
|
||||
card.dataset[DOM_CACHE_KEY] = DOM_CACHE_VALUE;
|
||||
if (card.querySelectorAll('.actions .additional a').length > 2) continue;
|
||||
// Metadata_buttoncard
|
||||
metadataButton = card.querySelector('.metadata-button');
|
||||
// Additional node
|
||||
additionalNode = card.querySelector('.actions .additional')!;
|
||||
|
||||
// Get ul node, which is the parent of all buttons
|
||||
ulNode = card.querySelector('.actions .additional ul');
|
||||
if (is_nullable(ulNode)) {
|
||||
ulNode = document.createElement('ul');
|
||||
additionalNode.append(ulNode);
|
||||
}
|
||||
|
||||
// Replace preview text button
|
||||
replacePreviewButton = card.querySelector('.actions .additional a');
|
||||
if (is_nullable(replacePreviewButton)) {
|
||||
replacePreviewButton = document.createElement('a');
|
||||
additionalNode.append(replacePreviewButton);
|
||||
}
|
||||
|
||||
// Remove br tag
|
||||
ulNode.querySelector('br')?.remove();
|
||||
|
||||
// Check thumb mode
|
||||
if (isThumbMode && additionalNode) {
|
||||
additionalNode.style.display = undefined as any as string;
|
||||
|
||||
if (chShowButtonOnThumb) {
|
||||
ulNode.style.background = BTN_THUMB_BACKGROUND;
|
||||
} else {
|
||||
// Reset
|
||||
ulNode.style.background = undefined as any as string;
|
||||
// Remove existed buttons
|
||||
|
||||
// Find all .a child nodes
|
||||
const atags = ulNode.querySelectorAll('a');
|
||||
if (!atags?.length) continue;
|
||||
for (const atag of atags) {
|
||||
// Reset display
|
||||
atag.style.display = undefined as any;
|
||||
// Remove extension's button
|
||||
if (CH_BTN_TXTS.has(atag.innerHTML)) {
|
||||
// Need to remove
|
||||
atag.remove();
|
||||
} else {
|
||||
// Do not remove, just reset
|
||||
atag.innerHTML = replacePreviewText;
|
||||
atag.style.display = undefined as any;
|
||||
atag.style.fontSize = undefined as any;
|
||||
atag.style.position = undefined as any;
|
||||
atag.style.backgroundImage = undefined as any;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Just reset and remove nodes, do nothing else
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// Full preview mode
|
||||
additionalNode.style.display = chAlwaysDisplay ? 'block' : (undefined as any as string);
|
||||
modelTypeHasCards.push(jsModelType);
|
||||
|
||||
for (const card of cards) {
|
||||
if (card.dataset[DOM_CACHE_KEY] === DOM_CACHE_VALUE) break;
|
||||
card.dataset[DOM_CACHE_KEY] = DOM_CACHE_VALUE;
|
||||
if (card.querySelectorAll('.actions .additional a').length > 2) continue;
|
||||
// Metadata_buttoncard
|
||||
metadataButton = card.querySelector('.metadata-button');
|
||||
// Additional node
|
||||
additionalNode = card.querySelector('.actions .additional')!;
|
||||
|
||||
// Get ul node, which is the parent of all buttons
|
||||
ulNode = card.querySelector('.actions .additional ul');
|
||||
if (is_nullable(ulNode)) {
|
||||
ulNode = document.createElement('ul');
|
||||
additionalNode.append(ulNode);
|
||||
}
|
||||
|
||||
// Replace preview text button
|
||||
replacePreviewButton = card.querySelector('.actions .additional a');
|
||||
if (is_nullable(replacePreviewButton)) {
|
||||
replacePreviewButton = document.createElement('a');
|
||||
additionalNode.append(replacePreviewButton);
|
||||
}
|
||||
|
||||
// Remove br tag
|
||||
ulNode.querySelector('br')?.remove();
|
||||
|
||||
// Check thumb mode
|
||||
if (isThumbMode && additionalNode) {
|
||||
additionalNode.style.display = undefined as any as string;
|
||||
|
||||
if (chShowButtonOnThumb) {
|
||||
ulNode.style.background = BTN_THUMB_BACKGROUND;
|
||||
} else {
|
||||
// Reset
|
||||
ulNode.style.background = undefined as any as string;
|
||||
// Remove existed buttons
|
||||
|
||||
// Find all .a child nodes
|
||||
const atags = ulNode.querySelectorAll('a');
|
||||
if (!atags?.length) continue;
|
||||
for (const atag of atags) {
|
||||
// Reset display
|
||||
atag.style.display = undefined as any;
|
||||
// Remove extension's button
|
||||
if (CH_BTN_TXTS.has(atag.innerHTML)) {
|
||||
// Need to remove
|
||||
atag.remove();
|
||||
} else {
|
||||
// Do not remove, just reset
|
||||
atag.innerHTML = replacePreviewText;
|
||||
atag.style.display = undefined as any;
|
||||
atag.style.fontSize = undefined as any;
|
||||
atag.style.position = undefined as any;
|
||||
atag.style.backgroundImage = undefined as any;
|
||||
}
|
||||
}
|
||||
|
||||
// Just reset and remove nodes, do nothing else
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// Full preview mode
|
||||
additionalNode.style.display = chAlwaysDisplay ? 'block' : (undefined as any as string);
|
||||
}
|
||||
|
||||
// Change replace preview text button into icon
|
||||
if (replacePreviewButton.innerHTML !== '🖼️') {
|
||||
needToAddButtons = true;
|
||||
replacePreviewButton.innerHTML = '🖼️';
|
||||
styleButton(replacePreviewButton, isThumbMode);
|
||||
}
|
||||
|
||||
if (!needToAddButtons) continue;
|
||||
// Search_term node
|
||||
// Search_term = subfolder path + model name + ext
|
||||
searchTermNode = card.querySelector('.actions .additional .search_term');
|
||||
if (!searchTermNode) return;
|
||||
// Get search_term
|
||||
searchTerm = searchTermNode.innerHTML;
|
||||
if (!searchTerm) continue;
|
||||
|
||||
// Then we need to add 3 buttons to each ul node:
|
||||
const openUrlNode = document.createElement('a');
|
||||
openUrlNode.href = '#';
|
||||
openUrlNode.innerHTML = '🌐';
|
||||
styleButton(openUrlNode, isThumbMode);
|
||||
openUrlNode.title = "Open this model's civitai url";
|
||||
openUrlNode.setAttribute(
|
||||
'onclick',
|
||||
`open_model_url(event, '${modelType}', '${searchTerm}')`,
|
||||
);
|
||||
|
||||
const addTriggerWordsNode = document.createElement('a');
|
||||
addTriggerWordsNode.href = '#';
|
||||
addTriggerWordsNode.innerHTML = '💡';
|
||||
styleButton(addTriggerWordsNode, isThumbMode);
|
||||
addTriggerWordsNode.title = 'Add trigger words to prompt';
|
||||
addTriggerWordsNode.setAttribute(
|
||||
'onclick',
|
||||
`add_trigger_words(event, '${modelType}', '${searchTerm}')`,
|
||||
);
|
||||
|
||||
const usePreviewPromptNode = document.createElement('a');
|
||||
usePreviewPromptNode.href = '#';
|
||||
usePreviewPromptNode.innerHTML = '🏷️';
|
||||
styleButton(usePreviewPromptNode, isThumbMode);
|
||||
usePreviewPromptNode.title = 'Use prompt from preview image';
|
||||
usePreviewPromptNode.setAttribute(
|
||||
'onclick',
|
||||
`use_preview_prompt(event, '${modelType}', '${searchTerm}')`,
|
||||
);
|
||||
|
||||
// Add to card
|
||||
ulNode.append(openUrlNode);
|
||||
// Add br if metadata_button exists
|
||||
if (isThumbMode && metadataButton) ulNode.append(document.createElement('br'));
|
||||
ulNode.append(addTriggerWordsNode);
|
||||
ulNode.append(usePreviewPromptNode);
|
||||
}
|
||||
}
|
||||
|
||||
// Change replace preview text button into icon
|
||||
if (replacePreviewButton.innerHTML !== '🖼️') {
|
||||
needToAddButtons = true;
|
||||
replacePreviewButton.innerHTML = '🖼️';
|
||||
styleButton(replacePreviewButton, isThumbMode);
|
||||
}
|
||||
|
||||
if (!needToAddButtons) continue;
|
||||
// Search_term node
|
||||
// Search_term = subfolder path + model name + ext
|
||||
searchTermNode = card.querySelector('.actions .additional .search_term');
|
||||
if (!searchTermNode) return;
|
||||
// Get search_term
|
||||
searchTerm = searchTermNode.innerHTML;
|
||||
if (!searchTerm) continue;
|
||||
|
||||
// Then we need to add 3 buttons to each ul node:
|
||||
const openUrlNode = document.createElement('a');
|
||||
openUrlNode.href = '#';
|
||||
openUrlNode.innerHTML = '🌐';
|
||||
styleButton(openUrlNode, isThumbMode);
|
||||
openUrlNode.title = "Open this model's civitai url";
|
||||
openUrlNode.setAttribute(
|
||||
'onclick',
|
||||
`open_model_url(event, '${modelType}', '${searchTerm}')`,
|
||||
);
|
||||
|
||||
const addTriggerWordsNode = document.createElement('a');
|
||||
addTriggerWordsNode.href = '#';
|
||||
addTriggerWordsNode.innerHTML = '💡';
|
||||
styleButton(addTriggerWordsNode, isThumbMode);
|
||||
addTriggerWordsNode.title = 'Add trigger words to prompt';
|
||||
addTriggerWordsNode.setAttribute(
|
||||
'onclick',
|
||||
`add_trigger_words(event, '${modelType}', '${searchTerm}')`,
|
||||
);
|
||||
|
||||
const usePreviewPromptNode = document.createElement('a');
|
||||
usePreviewPromptNode.href = '#';
|
||||
usePreviewPromptNode.innerHTML = '🏷️';
|
||||
styleButton(usePreviewPromptNode, isThumbMode);
|
||||
usePreviewPromptNode.title = 'Use prompt from preview image';
|
||||
usePreviewPromptNode.setAttribute(
|
||||
'onclick',
|
||||
`use_preview_prompt(event, '${modelType}', '${searchTerm}')`,
|
||||
);
|
||||
|
||||
// Add to card
|
||||
ulNode.append(openUrlNode);
|
||||
// Add br if metadata_button exists
|
||||
if (isThumbMode && metadataButton) ulNode.append(document.createElement('br'));
|
||||
ulNode.append(addTriggerWordsNode);
|
||||
ulNode.append(usePreviewPromptNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return modelTypeHasCards;
|
||||
return modelTypeHasCards;
|
||||
};
|
||||
|
||||
export default () => {
|
||||
let checkDomCurrent: INullable<HTMLElement>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
let x: number = 0;
|
||||
let fn: () => any;
|
||||
// eslint-disable-next-line unicorn/consistent-function-scoping
|
||||
const fnClick = () => {
|
||||
let checkDomCurrent: INullable<HTMLElement>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
let x: number = 0;
|
||||
let fn: () => any;
|
||||
// eslint-disable-next-line unicorn/consistent-function-scoping
|
||||
const fnClick = () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
||||
setTimeout(fn, 2000);
|
||||
};
|
||||
fn = () => {
|
||||
let retryTimes = 0;
|
||||
const fixInterval = setInterval(() => {
|
||||
console.debug('🤯 [civitai helper] update card for civitai');
|
||||
const checkDom = document.querySelector('#txt2img_lora_cards') as any;
|
||||
if (checkDom || retryTimes > 10) {
|
||||
if (checkDomCurrent !== checkDom) {
|
||||
x = 0;
|
||||
checkDomCurrent = checkDom;
|
||||
for (const activeTabType of TAB_PREFIX_LIST) {
|
||||
const elems = document.querySelectorAll(`#${activeTabType}_extra_tabs .tab-nav button`);
|
||||
if (elems) {
|
||||
for (const elem of elems) {
|
||||
elem.removeEventListener('click', fnClick);
|
||||
elem.addEventListener('click', fnClick);
|
||||
}
|
||||
setTimeout(fn, 2000);
|
||||
};
|
||||
fn = () => {
|
||||
let retryTimes = 0;
|
||||
const fixInterval = setInterval(() => {
|
||||
consola.info('🤯 [civitai helper] update card for civitai');
|
||||
const checkDom = document.querySelector('#txt2img_lora_cards') as any;
|
||||
if (checkDom || retryTimes > 10) {
|
||||
if (checkDomCurrent !== checkDom) {
|
||||
x = 0;
|
||||
checkDomCurrent = checkDom;
|
||||
for (const activeTabType of TAB_PREFIX_LIST) {
|
||||
const elems = document.querySelectorAll(`#${activeTabType}_extra_tabs .tab-nav button`);
|
||||
if (elems) {
|
||||
for (const elem of elems) {
|
||||
elem.removeEventListener('click', fnClick);
|
||||
elem.addEventListener('click', fnClick);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const y = updateCardForCivitai()?.length as number;
|
||||
if (typeof y === 'number' && y < x) x = y;
|
||||
if (retryTimes > 10 || !checkDom || y >= MODEL_TYPE_LIST.length || y > x) {
|
||||
clearInterval(fixInterval);
|
||||
x = y ?? x;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const y = updateCardForCivitai()?.length as number;
|
||||
if (typeof y === 'number' && y < x) x = y;
|
||||
if (retryTimes > 10 || !checkDom || y >= MODEL_TYPE_LIST.length || y > x) {
|
||||
clearInterval(fixInterval);
|
||||
x = y ?? x;
|
||||
}
|
||||
}
|
||||
retryTimes++;
|
||||
}, 2000);
|
||||
};
|
||||
retryTimes++;
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
return fn();
|
||||
return fn();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,80 +1,85 @@
|
|||
import { consola } from 'consola';
|
||||
|
||||
const MIN_WIDTH = 240;
|
||||
|
||||
const addDraggable = (tabId: string) => {
|
||||
const settings = document.querySelector(`#${tabId}_settings`) as HTMLDivElement;
|
||||
const checkDraggableLine = document.querySelector(
|
||||
`#tab_${tabId} .draggable-line`,
|
||||
) as HTMLDivElement;
|
||||
if (!settings || checkDraggableLine) return;
|
||||
const settings = document.querySelector(`#${tabId}_settings`) as HTMLDivElement;
|
||||
const checkDraggableLine = document.querySelector(
|
||||
`#tab_${tabId} .draggable-line`,
|
||||
) as HTMLDivElement;
|
||||
if (!settings || checkDraggableLine) return;
|
||||
|
||||
settings.style.minWidth = `min(${MIN_WIDTH}px, 100%)`;
|
||||
settings.style.minWidth = `min(${MIN_WIDTH}px, 100%)`;
|
||||
|
||||
const lineWrapper = document.createElement('div');
|
||||
lineWrapper.classList.add('draggable-line');
|
||||
const lineWrapper = document.createElement('div');
|
||||
lineWrapper.classList.add('draggable-line');
|
||||
|
||||
settings.after(lineWrapper);
|
||||
settings.after(lineWrapper);
|
||||
|
||||
const container: HTMLElement | any = settings.parentElement;
|
||||
container.classList.add('draggable-container');
|
||||
const container: HTMLElement | any = settings.parentElement;
|
||||
container.classList.add('draggable-container');
|
||||
|
||||
let results: HTMLDivElement = document.querySelector(`#${tabId}_results`) as HTMLDivElement;
|
||||
let results: HTMLDivElement = document.querySelector(`#${tabId}_results`) as HTMLDivElement;
|
||||
|
||||
if (!results) return;
|
||||
if (!results) return;
|
||||
|
||||
if (tabId === 'extras') results = results.parentElement as HTMLDivElement;
|
||||
if (tabId === 'extras') results = results.parentElement as HTMLDivElement;
|
||||
|
||||
results.style.minWidth = `${MIN_WIDTH}px`;
|
||||
|
||||
let linePosition = 50;
|
||||
settings.style.flexBasis = `${linePosition}%`;
|
||||
results.style.flexBasis = `${100 - linePosition}%`;
|
||||
|
||||
let isDragging = false;
|
||||
|
||||
lineWrapper.addEventListener('mousedown', (e) => {
|
||||
isDragging = true;
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
document.addEventListener('mousemove', (event) => {
|
||||
if (!isDragging) return;
|
||||
|
||||
const tab = document.querySelector(`#tab_${tabId}`) as HTMLDivElement;
|
||||
if (!tab) return;
|
||||
|
||||
let offsetX = tab.offsetLeft;
|
||||
let parent = tab.offsetParent as HTMLDivElement;
|
||||
|
||||
while (parent) {
|
||||
offsetX += parent.offsetLeft;
|
||||
parent = parent.offsetParent as HTMLDivElement;
|
||||
}
|
||||
|
||||
const containerWidth = container.offsetWidth;
|
||||
const mouseX = event.clientX;
|
||||
const linePosition = ((mouseX - offsetX) / containerWidth) * 100;
|
||||
|
||||
if (linePosition <= (MIN_WIDTH / containerWidth) * 100) return;
|
||||
if (linePosition >= (1 - MIN_WIDTH / containerWidth) * 100) return;
|
||||
results.style.minWidth = `${MIN_WIDTH}px`;
|
||||
|
||||
let linePosition = 50;
|
||||
settings.style.flexBasis = `${linePosition}%`;
|
||||
results.style.flexBasis = `${100 - linePosition}%`;
|
||||
});
|
||||
|
||||
document.addEventListener('mouseup', () => {
|
||||
isDragging = false;
|
||||
});
|
||||
let isDragging = false;
|
||||
|
||||
lineWrapper.addEventListener('mousedown', (e) => {
|
||||
isDragging = true;
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
document.addEventListener('mousemove', (event) => {
|
||||
if (!isDragging) return;
|
||||
|
||||
const tab = document.querySelector(`#tab_${tabId}`) as HTMLDivElement;
|
||||
if (!tab) return;
|
||||
|
||||
let offsetX = tab.offsetLeft;
|
||||
let parent = tab.offsetParent as HTMLDivElement;
|
||||
|
||||
while (parent) {
|
||||
offsetX += parent.offsetLeft;
|
||||
parent = parent.offsetParent as HTMLDivElement;
|
||||
}
|
||||
|
||||
const containerWidth = container.offsetWidth;
|
||||
const mouseX = event.clientX;
|
||||
const linePosition = ((mouseX - offsetX) / containerWidth) * 100;
|
||||
|
||||
if (linePosition <= (MIN_WIDTH / containerWidth) * 100) return;
|
||||
if (linePosition >= (1 - MIN_WIDTH / containerWidth) * 100) return;
|
||||
|
||||
settings.style.flexBasis = `${linePosition}%`;
|
||||
results.style.flexBasis = `${100 - linePosition}%`;
|
||||
});
|
||||
|
||||
document.addEventListener('mouseup', () => {
|
||||
isDragging = false;
|
||||
});
|
||||
};
|
||||
|
||||
export default () => {
|
||||
console.time('🤯 [layout] inject - DraggablePanel');
|
||||
addDraggable('txt2img');
|
||||
addDraggable('img2img');
|
||||
const extrasSetting = document.querySelector('#extras_results')?.parentElement
|
||||
?.previousElementSibling as HTMLDivElement;
|
||||
if (extrasSetting) {
|
||||
extrasSetting.id = 'extras_settings';
|
||||
addDraggable('extras');
|
||||
}
|
||||
console.timeEnd('🤯 [layout] inject - DraggablePanel');
|
||||
try {
|
||||
addDraggable('txt2img');
|
||||
addDraggable('img2img');
|
||||
const extrasSetting = document.querySelector('#extras_results')?.parentElement
|
||||
?.previousElementSibling as HTMLDivElement;
|
||||
if (extrasSetting) {
|
||||
extrasSetting.id = 'extras_settings';
|
||||
addDraggable('extras');
|
||||
}
|
||||
consola.success('🤯 [layout] inject - DraggablePanel');
|
||||
} catch (error) {
|
||||
consola.error('🤯 [layout] inject - DraggablePanel', error);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,283 +1,284 @@
|
|||
import { consola } from 'consola';
|
||||
|
||||
/**
|
||||
* 转换器工具类
|
||||
*/
|
||||
export const Converter = {
|
||||
/**
|
||||
/**
|
||||
* 添加转换按钮
|
||||
* @param type - 组件类型
|
||||
*/
|
||||
addPromptButton(type: string): void {
|
||||
console.debug('🤯 [formatPrompt] inject', type);
|
||||
const actionsColumn: HTMLElement | null = gradioApp().querySelector(
|
||||
`#${type}_tools > div.form`,
|
||||
);
|
||||
const formatBtn: HTMLElement | null = gradioApp().querySelector(`#${type}_formatconvert`);
|
||||
if (!actionsColumn || formatBtn) return;
|
||||
const convertButton: HTMLElement = Converter.createButton(`${type}_formatconvert`, '🪄', () =>
|
||||
Converter.onClickConvert(type));
|
||||
actionsColumn.append(convertButton);
|
||||
},
|
||||
addPromptButton(type: string): void {
|
||||
consola.info('🤯 [formatPrompt] inject', type);
|
||||
const actionsColumn: HTMLElement | null = gradioApp().querySelector(
|
||||
`#${type}_tools > div.form`,
|
||||
);
|
||||
const formatBtn: HTMLElement | null = gradioApp().querySelector(`#${type}_formatconvert`);
|
||||
if (!actionsColumn || formatBtn) return;
|
||||
const convertButton: HTMLElement = Converter.createButton(`${type}_formatconvert`, '🪄', () =>
|
||||
Converter.onClickConvert(type));
|
||||
actionsColumn.append(convertButton);
|
||||
},
|
||||
|
||||
/**
|
||||
/**
|
||||
* 将输入的字符串转换成特定格式的字符串
|
||||
* @param input 输入的字符串
|
||||
* @returns 转换后的字符串
|
||||
*/
|
||||
convert(input: string): string {
|
||||
const re_attention = /\{|\[|\}|\]|[^{}[\]]+/gmu;
|
||||
convert(input: string): string {
|
||||
const re_attention = /\{|\[|\}|\]|[^{}[\]]+/gmu;
|
||||
|
||||
let text = Converter.convertStr(input);
|
||||
const textArray = Converter.convertStr2Array(text);
|
||||
text = Converter.convertArray2Str(textArray);
|
||||
let text = Converter.convertStr(input);
|
||||
const textArray = Converter.convertStr2Array(text);
|
||||
text = Converter.convertArray2Str(textArray);
|
||||
|
||||
let res: [string, number][] = [];
|
||||
let res: [string, number][] = [];
|
||||
|
||||
const curly_bracket_multiplier = 1.05;
|
||||
const square_bracket_multiplier = 1 / 1.05;
|
||||
const curly_bracket_multiplier = 1.05;
|
||||
const square_bracket_multiplier = 1 / 1.05;
|
||||
|
||||
const brackets: Record<string, { multiplier: number; stack: number[] }> = {
|
||||
'[': { multiplier: square_bracket_multiplier, stack: [] },
|
||||
'{': { multiplier: curly_bracket_multiplier, stack: [] },
|
||||
};
|
||||
const brackets: Record<string, { multiplier: number; stack: number[] }> = {
|
||||
'[': { multiplier: square_bracket_multiplier, stack: [] },
|
||||
'{': { multiplier: curly_bracket_multiplier, stack: [] },
|
||||
};
|
||||
|
||||
/**
|
||||
/**
|
||||
* 将指定范围内的数字乘以指定倍数
|
||||
* @param start_position 起始位置
|
||||
* @param multiplier 倍数
|
||||
*/
|
||||
function multiply_range(start_position: number, multiplier: number) {
|
||||
for (let pos = start_position; pos < res.length; pos++) {
|
||||
res[pos][1] = Converter.round(res[pos][1] * multiplier);
|
||||
}
|
||||
}
|
||||
|
||||
for (const match of text.matchAll(re_attention)) {
|
||||
let word = match[0];
|
||||
|
||||
if (word in brackets) {
|
||||
brackets[word].stack.push(res.length);
|
||||
} else if (word === '}' || word === ']') {
|
||||
const bracket = brackets[word === '}' ? '{' : '['];
|
||||
if (bracket.stack.length > 0) {
|
||||
multiply_range(bracket.stack.pop()!, bracket.multiplier);
|
||||
function multiply_range(start_position: number, multiplier: number) {
|
||||
for (let pos = start_position; pos < res.length; pos++) {
|
||||
res[pos][1] = Converter.round(res[pos][1] * multiplier);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res.push([word, 1]);
|
||||
}
|
||||
}
|
||||
|
||||
for (const bracketType of Object.keys(brackets)) {
|
||||
for (const pos of brackets[bracketType].stack) {
|
||||
multiply_range(pos, brackets[bracketType].multiplier);
|
||||
}
|
||||
}
|
||||
for (const match of text.matchAll(re_attention)) {
|
||||
let word = match[0];
|
||||
|
||||
if (res.length === 0) {
|
||||
res = [['', 1]];
|
||||
}
|
||||
if (word in brackets) {
|
||||
brackets[word].stack.push(res.length);
|
||||
} else if (word === '}' || word === ']') {
|
||||
const bracket = brackets[word === '}' ? '{' : '['];
|
||||
if (bracket.stack.length > 0) {
|
||||
multiply_range(bracket.stack.pop()!, bracket.multiplier);
|
||||
}
|
||||
} else {
|
||||
res.push([word, 1]);
|
||||
}
|
||||
}
|
||||
|
||||
let index = 0;
|
||||
while (index + 1 < res.length) {
|
||||
if (res[index][1] === res[index + 1][1]) {
|
||||
res[index][0] += res[index + 1][0];
|
||||
res.splice(index + 1, 1);
|
||||
} else {
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
for (const bracketType of Object.keys(brackets)) {
|
||||
for (const pos of brackets[bracketType].stack) {
|
||||
multiply_range(pos, brackets[bracketType].multiplier);
|
||||
}
|
||||
}
|
||||
|
||||
let result = '';
|
||||
for (const [word, value] of res) {
|
||||
result += value === 1 ? word : `(${word}:${value.toString()})`;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
if (res.length === 0) {
|
||||
res = [['', 1]];
|
||||
}
|
||||
|
||||
/**
|
||||
let index = 0;
|
||||
while (index + 1 < res.length) {
|
||||
if (res[index][1] === res[index + 1][1]) {
|
||||
res[index][0] += res[index + 1][0];
|
||||
res.splice(index + 1, 1);
|
||||
} else {
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let result = '';
|
||||
for (const [word, value] of res) {
|
||||
result += value === 1 ? word : `(${word}:${value.toString()})`;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* 将数组转换成字符串
|
||||
* @param array 数组
|
||||
* @returns 转换后的字符串
|
||||
*/
|
||||
convertArray2Str(array: string[]): string {
|
||||
const newArray = array.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 newArray.join(', ');
|
||||
},
|
||||
convertArray2Str(array: string[]): string {
|
||||
const newArray = array.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 newArray.join(', ');
|
||||
},
|
||||
|
||||
/**
|
||||
/**
|
||||
* 将字符串中的中文冒号和括号转换成英文冒号和括号
|
||||
* @param srt 字符串
|
||||
* @returns 转换后的字符串
|
||||
*/
|
||||
convertStr(srt: string): string {
|
||||
return srt.replaceAll(':', ':').replaceAll('(', '(').replaceAll(')', ')');
|
||||
},
|
||||
convertStr(srt: string): string {
|
||||
return srt.replaceAll(':', ':').replaceAll('(', '(').replaceAll(')', ')');
|
||||
},
|
||||
|
||||
/**
|
||||
/**
|
||||
* 将字符串按照括号分割成数组
|
||||
* @param str 字符串
|
||||
* @returns 分割后的数组
|
||||
*/
|
||||
convertStr2Array(string_: string): string[] {
|
||||
convertStr2Array(string_: string): string[] {
|
||||
// 匹配各种括号中的内容,包括括号本身
|
||||
const bracketRegex = /([()<>[\]])/g;
|
||||
const bracketRegex = /([()<>[\]])/g;
|
||||
|
||||
/**
|
||||
/**
|
||||
* 将字符串按照各种括号分割成数组
|
||||
* @param str 字符串
|
||||
* @returns 分割后的数组
|
||||
*/
|
||||
const splitByBracket = (string__: string): string[] => {
|
||||
const array: string[] = [];
|
||||
let start = 0;
|
||||
let depth = 0;
|
||||
let match;
|
||||
while ((match = bracketRegex.exec(string__)) !== null) {
|
||||
if (depth === 0 && match.index > start) {
|
||||
array.push(string__.slice(start, match.index));
|
||||
start = match.index;
|
||||
}
|
||||
if (match[0] === '(' || match[0] === '<' || match[0] === '[') {
|
||||
depth++;
|
||||
} else if (match[0] === ')' || match[0] === '>' || match[0] === ']') {
|
||||
depth--;
|
||||
}
|
||||
if (depth === 0) {
|
||||
array.push(string__.slice(start, match.index + 1));
|
||||
start = match.index + 1;
|
||||
}
|
||||
}
|
||||
if (start < string__.length) {
|
||||
array.push(string__.slice(Math.max(0, start)));
|
||||
}
|
||||
return array;
|
||||
};
|
||||
const splitByBracket = (string__: string): string[] => {
|
||||
const array: string[] = [];
|
||||
let start = 0;
|
||||
let depth = 0;
|
||||
let match;
|
||||
while ((match = bracketRegex.exec(string__)) !== null) {
|
||||
if (depth === 0 && match.index > start) {
|
||||
array.push(string__.slice(start, match.index));
|
||||
start = match.index;
|
||||
}
|
||||
if (match[0] === '(' || match[0] === '<' || match[0] === '[') {
|
||||
depth++;
|
||||
} else if (match[0] === ')' || match[0] === '>' || match[0] === ']') {
|
||||
depth--;
|
||||
}
|
||||
if (depth === 0) {
|
||||
array.push(string__.slice(start, match.index + 1));
|
||||
start = match.index + 1;
|
||||
}
|
||||
}
|
||||
if (start < string__.length) {
|
||||
array.push(string__.slice(Math.max(0, start)));
|
||||
}
|
||||
return array;
|
||||
};
|
||||
|
||||
/**
|
||||
/**
|
||||
* 将字符串按照逗号和各种括号分割成数组
|
||||
* @param str 字符串
|
||||
* @returns 分割后的数组
|
||||
*/
|
||||
const splitByComma = (string__: string): string[] => {
|
||||
const array: string[] = [];
|
||||
let start = 0;
|
||||
let inBracket = false;
|
||||
for (let index = 0; index < string__.length; index++) {
|
||||
if (string__[index] === ',' && !inBracket) {
|
||||
array.push(string__.slice(start, index).trim());
|
||||
start = index + 1;
|
||||
} else if (bracketRegex.test(string__[index])) {
|
||||
inBracket = !inBracket;
|
||||
}
|
||||
}
|
||||
array.push(string__.slice(Math.max(0, start)).trim());
|
||||
return array;
|
||||
};
|
||||
const splitByComma = (string__: string): string[] => {
|
||||
const array: string[] = [];
|
||||
let start = 0;
|
||||
let inBracket = false;
|
||||
for (let index = 0; index < string__.length; index++) {
|
||||
if (string__[index] === ',' && !inBracket) {
|
||||
array.push(string__.slice(start, index).trim());
|
||||
start = index + 1;
|
||||
} else if (bracketRegex.test(string__[index])) {
|
||||
inBracket = !inBracket;
|
||||
}
|
||||
}
|
||||
array.push(string__.slice(Math.max(0, start)).trim());
|
||||
return array;
|
||||
};
|
||||
|
||||
/**
|
||||
/**
|
||||
* 清洗字符串并输出数组
|
||||
* @param str 字符串
|
||||
* @returns 清洗后的数组
|
||||
*/
|
||||
const cleanString = (string__: string): string[] => {
|
||||
let array = splitByBracket(string__);
|
||||
array = array.flatMap((s) => splitByComma(s));
|
||||
return array.filter((s) => s !== '');
|
||||
};
|
||||
const cleanString = (string__: string): string[] => {
|
||||
let array = splitByBracket(string__);
|
||||
array = array.flatMap((s) => splitByComma(s));
|
||||
return array.filter((s) => s !== '');
|
||||
};
|
||||
|
||||
return cleanString(string_)
|
||||
.filter((item) => {
|
||||
const pattern = /^[\s,,]+$/;
|
||||
return !pattern.test(item);
|
||||
})
|
||||
.filter(Boolean)
|
||||
.sort((a, b) => {
|
||||
return a.includes('<') && !b.includes('<') ?
|
||||
1 :
|
||||
b.includes('<') && !a.includes('<') ?
|
||||
-1 :
|
||||
0;
|
||||
});
|
||||
},
|
||||
return cleanString(string_)
|
||||
.filter((item) => {
|
||||
const pattern = /^[\s,,]+$/;
|
||||
return !pattern.test(item);
|
||||
})
|
||||
.filter(Boolean)
|
||||
.sort((a, b) => {
|
||||
return a.includes('<') && !b.includes('<') ?
|
||||
1 :
|
||||
b.includes('<') && !a.includes('<') ?
|
||||
-1 :
|
||||
0;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
/**
|
||||
* 创建转换按钮
|
||||
* @param id 按钮 id
|
||||
* @param innerHTML 按钮文本
|
||||
* @param onClick 点击事件处理函数
|
||||
* @returns 新建的按钮元素
|
||||
*/
|
||||
createButton(id: string, innerHTML: string, onClick: () => void): HTMLButtonElement {
|
||||
const button = document.createElement('button');
|
||||
button.id = id;
|
||||
button.type = 'button';
|
||||
button.innerHTML = innerHTML;
|
||||
button.title = 'Format prompt~🪄';
|
||||
button.className = 'lg secondary gradio-button tool svelte-cmf5ev';
|
||||
button.addEventListener('click', onClick);
|
||||
return button;
|
||||
},
|
||||
createButton(id: string, innerHTML: string, onClick: () => void): HTMLButtonElement {
|
||||
const button = document.createElement('button');
|
||||
button.id = id;
|
||||
button.type = 'button';
|
||||
button.innerHTML = innerHTML;
|
||||
button.title = 'Format prompt~🪄';
|
||||
button.className = 'lg secondary gradio-button tool svelte-cmf5ev';
|
||||
button.addEventListener('click', onClick);
|
||||
return button;
|
||||
},
|
||||
|
||||
/**
|
||||
/**
|
||||
* 触发 input 事件
|
||||
* @param target 目标元素
|
||||
*/
|
||||
dispatchInputEvent(target: EventTarget) {
|
||||
let inputEvent = new Event('input');
|
||||
Object.defineProperty(inputEvent, 'target', { value: target });
|
||||
target.dispatchEvent(inputEvent);
|
||||
},
|
||||
dispatchInputEvent(target: EventTarget) {
|
||||
let inputEvent = new Event('input');
|
||||
Object.defineProperty(inputEvent, 'target', { value: target });
|
||||
target.dispatchEvent(inputEvent);
|
||||
},
|
||||
|
||||
/**
|
||||
/**
|
||||
* 点击转换按钮的事件处理函数
|
||||
* @param type 类型
|
||||
*/
|
||||
onClickConvert(type: string) {
|
||||
const default_prompt = '';
|
||||
const default_negative = '';
|
||||
onClickConvert(type: string) {
|
||||
const default_prompt = '';
|
||||
const default_negative = '';
|
||||
|
||||
const prompt = gradioApp().querySelector(
|
||||
`#${type}_prompt > label > textarea`,
|
||||
) as HTMLTextAreaElement;
|
||||
const result = Converter.convert(prompt.value);
|
||||
prompt.value =
|
||||
const prompt = gradioApp().querySelector(
|
||||
`#${type}_prompt > label > textarea`,
|
||||
) as HTMLTextAreaElement;
|
||||
const result = Converter.convert(prompt.value);
|
||||
prompt.value =
|
||||
result.match(/^masterpiece, best quality,/) === null ? default_prompt + result : result;
|
||||
Converter.dispatchInputEvent(prompt);
|
||||
const negprompt = gradioApp().querySelector(
|
||||
`#${type}_neg_prompt > label > textarea`,
|
||||
) as HTMLTextAreaElement;
|
||||
const negResult = Converter.convert(negprompt.value);
|
||||
negprompt.value =
|
||||
Converter.dispatchInputEvent(prompt);
|
||||
const negprompt = gradioApp().querySelector(
|
||||
`#${type}_neg_prompt > label > textarea`,
|
||||
) as HTMLTextAreaElement;
|
||||
const negResult = Converter.convert(negprompt.value);
|
||||
negprompt.value =
|
||||
negResult.match(/^lowres,/) === null ?
|
||||
negResult.length === 0 ?
|
||||
default_negative :
|
||||
default_negative + negResult :
|
||||
negResult;
|
||||
Converter.dispatchInputEvent(negprompt);
|
||||
},
|
||||
negResult.length === 0 ?
|
||||
default_negative :
|
||||
default_negative + negResult :
|
||||
negResult;
|
||||
Converter.dispatchInputEvent(negprompt);
|
||||
},
|
||||
|
||||
/**
|
||||
/**
|
||||
* 将数字四舍五入到小数点后四位
|
||||
* @param value 数字
|
||||
* @returns 四舍五入后的数字
|
||||
*/
|
||||
round(value: number): number {
|
||||
return Math.round(value * 10_000) / 10_000;
|
||||
},
|
||||
round(value: number): number {
|
||||
return Math.round(value * 10_000) / 10_000;
|
||||
},
|
||||
};
|
||||
|
||||
export default () => {
|
||||
console.time('🤯 [formatPrompt] inject');
|
||||
Converter.addPromptButton('txt2img');
|
||||
Converter.addPromptButton('img2img');
|
||||
console.timeEnd('🤯 [formatPrompt] inject');
|
||||
Converter.addPromptButton('txt2img');
|
||||
Converter.addPromptButton('img2img');
|
||||
consola.success('🤯 [formatPrompt] inject');
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,95 +1,95 @@
|
|||
import { consola } from 'consola';
|
||||
import {
|
||||
archiveRestore,
|
||||
arrowDown,
|
||||
arrowDownLeft,
|
||||
arrowDownWideNarrow,
|
||||
arrowLeft,
|
||||
arrowRight,
|
||||
arrowRightLeft,
|
||||
arrowUpDown,
|
||||
book,
|
||||
box,
|
||||
brush,
|
||||
clipboardList,
|
||||
cornerRightUp,
|
||||
dices,
|
||||
download,
|
||||
fileArchive,
|
||||
folderClosed,
|
||||
grid2x2,
|
||||
image,
|
||||
laptop2,
|
||||
maximize,
|
||||
panelRight,
|
||||
paperclip,
|
||||
penSquare,
|
||||
pencilRuler,
|
||||
play,
|
||||
refreshCcw,
|
||||
save,
|
||||
settings,
|
||||
trash,
|
||||
undo,
|
||||
wand2,
|
||||
webcam,
|
||||
x, // @ts-ignore
|
||||
archiveRestore,
|
||||
arrowDown,
|
||||
arrowDownLeft,
|
||||
arrowDownWideNarrow,
|
||||
arrowLeft,
|
||||
arrowRight,
|
||||
arrowRightLeft,
|
||||
arrowUpDown,
|
||||
book,
|
||||
box,
|
||||
brush,
|
||||
clipboardList,
|
||||
cornerRightUp,
|
||||
dices,
|
||||
download,
|
||||
fileArchive,
|
||||
folderClosed,
|
||||
grid2x2,
|
||||
image,
|
||||
laptop2,
|
||||
maximize,
|
||||
panelRight,
|
||||
paperclip,
|
||||
penSquare,
|
||||
pencilRuler,
|
||||
play,
|
||||
refreshCcw,
|
||||
save,
|
||||
settings,
|
||||
trash,
|
||||
undo,
|
||||
wand2,
|
||||
webcam,
|
||||
x, // @ts-ignore
|
||||
} from 'lucide-static';
|
||||
|
||||
const replaceIcon = (element: HTMLElement, emoji: string[], svg: string, size: number) => {
|
||||
if (!element?.textContent || !svg) return;
|
||||
for (const e of emoji) {
|
||||
if (element?.textContent?.includes(e)) {
|
||||
element.innerHTML = svg
|
||||
.replace(`width="24"`, `width="${size}"`)
|
||||
.replace(`height="24"`, `height="${size}"`);
|
||||
if (!element?.textContent || !svg) return;
|
||||
for (const e of emoji) {
|
||||
if (element?.textContent?.includes(e)) {
|
||||
element.innerHTML = svg
|
||||
.replace(`width="24"`, `width="${size}"`)
|
||||
.replace(`height="24"`, `height="${size}"`);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default () => {
|
||||
console.time('🤯 [svgIcon] replace');
|
||||
for (const button of document.querySelectorAll('button')) {
|
||||
replaceIcon(button, ['🖌️'], penSquare, 16);
|
||||
replaceIcon(button, ['🗃️'], fileArchive, 16);
|
||||
replaceIcon(button, ['🖼️'], image, 16);
|
||||
replaceIcon(button, ['🎨️'], brush, 16);
|
||||
replaceIcon(button, ['📂'], folderClosed, 16);
|
||||
replaceIcon(button, ['🔄', '🔁', '♻️'], refreshCcw, 16);
|
||||
replaceIcon(button, ['↙️'], arrowDownLeft, 16);
|
||||
replaceIcon(button, ['⤴'], cornerRightUp, 16);
|
||||
replaceIcon(button, ['↕️'], arrowDownWideNarrow, 16);
|
||||
replaceIcon(button, ['🗑️'], trash, 16);
|
||||
replaceIcon(button, ['📋'], clipboardList, 16);
|
||||
replaceIcon(button, ['💾'], save, 16);
|
||||
replaceIcon(button, ['🎲️'], dices, 16);
|
||||
replaceIcon(button, ['🪄'], wand2, 16);
|
||||
replaceIcon(button, ['⚙️'], settings, 16);
|
||||
replaceIcon(button, ['➡️'], arrowRight, 16);
|
||||
replaceIcon(button, ['⇅'], arrowUpDown, 16);
|
||||
replaceIcon(button, ['⇄'], arrowRightLeft, 16);
|
||||
replaceIcon(button, ['🎴'], panelRight, 16);
|
||||
replaceIcon(button, ['🌀'], archiveRestore, 16);
|
||||
replaceIcon(button, ['💥'], play, 16);
|
||||
replaceIcon(button, ['📷'], webcam, 16);
|
||||
replaceIcon(button, ['📝'], laptop2, 16);
|
||||
replaceIcon(button, ['📐'], pencilRuler, 16);
|
||||
replaceIcon(button, ['⬇️'], arrowDown, 16);
|
||||
replaceIcon(button, ['↩'], undo, 16);
|
||||
replaceIcon(button, ['📒'], book, 16);
|
||||
replaceIcon(button, ['📎'], paperclip, 16);
|
||||
replaceIcon(button, ['📦'], box, 16);
|
||||
}
|
||||
for (const button of document.querySelectorAll('button')) {
|
||||
replaceIcon(button, ['🖌️'], penSquare, 16);
|
||||
replaceIcon(button, ['🗃️'], fileArchive, 16);
|
||||
replaceIcon(button, ['🖼️'], image, 16);
|
||||
replaceIcon(button, ['🎨️'], brush, 16);
|
||||
replaceIcon(button, ['📂'], folderClosed, 16);
|
||||
replaceIcon(button, ['🔄', '🔁', '♻️'], refreshCcw, 16);
|
||||
replaceIcon(button, ['↙️'], arrowDownLeft, 16);
|
||||
replaceIcon(button, ['⤴'], cornerRightUp, 16);
|
||||
replaceIcon(button, ['↕️'], arrowDownWideNarrow, 16);
|
||||
replaceIcon(button, ['🗑️'], trash, 16);
|
||||
replaceIcon(button, ['📋'], clipboardList, 16);
|
||||
replaceIcon(button, ['💾'], save, 16);
|
||||
replaceIcon(button, ['🎲️'], dices, 16);
|
||||
replaceIcon(button, ['🪄'], wand2, 16);
|
||||
replaceIcon(button, ['⚙️'], settings, 16);
|
||||
replaceIcon(button, ['➡️'], arrowRight, 16);
|
||||
replaceIcon(button, ['⇅'], arrowUpDown, 16);
|
||||
replaceIcon(button, ['⇄'], arrowRightLeft, 16);
|
||||
replaceIcon(button, ['🎴'], panelRight, 16);
|
||||
replaceIcon(button, ['🌀'], archiveRestore, 16);
|
||||
replaceIcon(button, ['💥'], play, 16);
|
||||
replaceIcon(button, ['📷'], webcam, 16);
|
||||
replaceIcon(button, ['📝'], laptop2, 16);
|
||||
replaceIcon(button, ['📐'], pencilRuler, 16);
|
||||
replaceIcon(button, ['⬇️'], arrowDown, 16);
|
||||
replaceIcon(button, ['↩'], undo, 16);
|
||||
replaceIcon(button, ['📒'], book, 16);
|
||||
replaceIcon(button, ['📎'], paperclip, 16);
|
||||
replaceIcon(button, ['📦'], box, 16);
|
||||
}
|
||||
|
||||
for (const span of document.querySelectorAll('span')) {
|
||||
replaceIcon(span, ['⤡'], maximize, 36);
|
||||
replaceIcon(span, ['⊞'], grid2x2, 36);
|
||||
replaceIcon(span, ['🖫'], download, 36);
|
||||
replaceIcon(span, ['×'], x, 36);
|
||||
}
|
||||
for (const span of document.querySelectorAll('span')) {
|
||||
replaceIcon(span, ['⤡'], maximize, 36);
|
||||
replaceIcon(span, ['⊞'], grid2x2, 36);
|
||||
replaceIcon(span, ['🖫'], download, 36);
|
||||
replaceIcon(span, ['×'], x, 36);
|
||||
}
|
||||
|
||||
for (const a of document.querySelectorAll('a')) {
|
||||
replaceIcon(a, ['❮'], arrowLeft, 36);
|
||||
replaceIcon(a, ['❯'], arrowRight, 36);
|
||||
}
|
||||
console.timeEnd('🤯 [svgIcon] replace');
|
||||
for (const a of document.querySelectorAll('a')) {
|
||||
replaceIcon(a, ['❮'], arrowLeft, 36);
|
||||
replaceIcon(a, ['❯'], arrowRight, 36);
|
||||
}
|
||||
consola.success('🤯 [svgIcon] replace');
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { consola } from 'consola';
|
||||
import type { StateCreator } from 'zustand/vanilla';
|
||||
|
||||
import { getLatestVersion, getLocaleOptions, getSetting, getVersion, postSetting } from './api';
|
||||
|
|
@ -18,83 +19,82 @@ export interface StoreAction {
|
|||
}
|
||||
|
||||
export const createSettings: StateCreator<Store, [['zustand/devtools', never]], [], StoreAction> = (
|
||||
set,
|
||||
get,
|
||||
set,
|
||||
get,
|
||||
) => ({
|
||||
onInit: async() => {
|
||||
set(() => ({ loading: true }), false, 'onInit');
|
||||
const { onLoadSetting, onLoadVersion, onLoadLatestVersion, onLoadLocalOptions } = get();
|
||||
await onLoadLocalOptions();
|
||||
await onLoadVersion();
|
||||
await onLoadLatestVersion();
|
||||
await onLoadSetting();
|
||||
set(() => ({ loading: false }), false, 'onInit');
|
||||
},
|
||||
onLoadLatestVersion: async() => {
|
||||
const latestVersion = await getLatestVersion();
|
||||
set(() => ({ latestVersion }), false, 'onLoadLatestVersion');
|
||||
},
|
||||
onLoadLocalOptions: async() => {
|
||||
const localeOptions = await getLocaleOptions();
|
||||
set(() => ({ localeOptions }), false, 'onLoadLocalOptions');
|
||||
},
|
||||
onLoadSetting: async() => {
|
||||
console.time('🤯 [setting] loaded');
|
||||
let themeSetting;
|
||||
const webuiSetting: any = await getSetting();
|
||||
onInit: async() => {
|
||||
set(() => ({ loading: true }), false, 'onInit');
|
||||
const { onLoadSetting, onLoadVersion, onLoadLatestVersion, onLoadLocalOptions } = get();
|
||||
await onLoadLocalOptions();
|
||||
await onLoadVersion();
|
||||
await onLoadLatestVersion();
|
||||
await onLoadSetting();
|
||||
set(() => ({ loading: false }), false, 'onInit');
|
||||
},
|
||||
onLoadLatestVersion: async() => {
|
||||
const latestVersion = await getLatestVersion();
|
||||
set(() => ({ latestVersion }), false, 'onLoadLatestVersion');
|
||||
},
|
||||
onLoadLocalOptions: async() => {
|
||||
const localeOptions = await getLocaleOptions();
|
||||
set(() => ({ localeOptions }), false, 'onLoadLocalOptions');
|
||||
},
|
||||
onLoadSetting: async() => {
|
||||
let themeSetting;
|
||||
const webuiSetting: any = await getSetting();
|
||||
|
||||
if (webuiSetting) {
|
||||
console.info('🤯 [setting] loaded webui setting');
|
||||
themeSetting = webuiSetting;
|
||||
}
|
||||
if (webuiSetting) {
|
||||
consola.start('🤯 [setting] loaded webui setting');
|
||||
themeSetting = webuiSetting;
|
||||
}
|
||||
|
||||
if (!themeSetting) {
|
||||
const localSetting: any = localStorage.getItem(SETTING_KEY);
|
||||
if (localSetting) {
|
||||
console.info('🤯 [setting] loaded local setting');
|
||||
themeSetting = JSON.parse(localSetting);
|
||||
}
|
||||
}
|
||||
if (!themeSetting) {
|
||||
const localSetting: any = localStorage.getItem(SETTING_KEY);
|
||||
if (localSetting) {
|
||||
consola.info('🤯 [setting] loaded local setting');
|
||||
themeSetting = JSON.parse(localSetting);
|
||||
}
|
||||
}
|
||||
|
||||
if (!themeSetting) {
|
||||
const fallbackLocalSetting: any = localStorage.getItem(FALLBACK_SETTING_KEY);
|
||||
if (fallbackLocalSetting) {
|
||||
console.info('🤯 [setting] loaded fallback local setting');
|
||||
themeSetting = JSON.parse(fallbackLocalSetting);
|
||||
}
|
||||
}
|
||||
if (!themeSetting) {
|
||||
const fallbackLocalSetting: any = localStorage.getItem(FALLBACK_SETTING_KEY);
|
||||
if (fallbackLocalSetting) {
|
||||
consola.info('🤯 [setting] loaded fallback local setting');
|
||||
themeSetting = JSON.parse(fallbackLocalSetting);
|
||||
}
|
||||
}
|
||||
|
||||
if (!themeSetting) {
|
||||
console.info('🤯 [setting] loaded default setting');
|
||||
themeSetting = DEFAULT_SETTING;
|
||||
}
|
||||
if (!themeSetting) {
|
||||
consola.info('🤯 [setting] loaded default setting');
|
||||
themeSetting = DEFAULT_SETTING;
|
||||
}
|
||||
|
||||
const setting = { ...DEFAULT_SETTING, ...themeSetting };
|
||||
const setting = { ...DEFAULT_SETTING, ...themeSetting };
|
||||
|
||||
await postSetting(setting);
|
||||
set(() => ({ setting }), false, 'onLoadSetting');
|
||||
console.table(setting);
|
||||
console.timeEnd('🤯 [setting] loaded');
|
||||
},
|
||||
onLoadVersion: async() => {
|
||||
const version = await getVersion();
|
||||
set(() => ({ version }), false, 'onLoadVersion');
|
||||
},
|
||||
onSetSetting: async(setting) => {
|
||||
const oldSetting = get().setting;
|
||||
const newSetting = { ...oldSetting, ...setting };
|
||||
localStorage.setItem(SETTING_KEY, JSON.stringify(newSetting));
|
||||
await postSetting(newSetting);
|
||||
set(() => ({ setting: newSetting }), false, 'onSetSetting');
|
||||
},
|
||||
onSetThemeMode: (themeMode) => {
|
||||
set(() => ({ themeMode }), false, 'onSetThemeMode');
|
||||
},
|
||||
setCurrentTab: () => {
|
||||
const currentTab = get_uiCurrentTabContent()?.id;
|
||||
console.debug('🤯 [tab] onChange', currentTab);
|
||||
if (currentTab && currentTab !== get().currentTab) {
|
||||
set({ currentTab }, false, 'setCurrentTab');
|
||||
}
|
||||
},
|
||||
await postSetting(setting);
|
||||
set(() => ({ setting }), false, 'onLoadSetting');
|
||||
consola.success('🤯 [setting] loaded');
|
||||
console.table(setting);
|
||||
},
|
||||
onLoadVersion: async() => {
|
||||
const version = await getVersion();
|
||||
set(() => ({ version }), false, 'onLoadVersion');
|
||||
},
|
||||
onSetSetting: async(setting) => {
|
||||
const oldSetting = get().setting;
|
||||
const newSetting = { ...oldSetting, ...setting };
|
||||
localStorage.setItem(SETTING_KEY, JSON.stringify(newSetting));
|
||||
await postSetting(newSetting);
|
||||
set(() => ({ setting: newSetting }), false, 'onSetSetting');
|
||||
},
|
||||
onSetThemeMode: (themeMode) => {
|
||||
set(() => ({ themeMode }), false, 'onSetThemeMode');
|
||||
},
|
||||
setCurrentTab: () => {
|
||||
const currentTab = get_uiCurrentTabContent()?.id;
|
||||
consola.info('🤯 [tab] onChange', currentTab);
|
||||
if (currentTab && currentTab !== get().currentTab) {
|
||||
set({ currentTab }, false, 'setCurrentTab');
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -9,41 +9,41 @@ import type { WebuiSetting } from './initialState';
|
|||
export const DEFAULT_VERSION: string = version;
|
||||
export const DEFAULT_LOCALE_OPTIONS: SelectProps['options'] = defualtLocaleOptions;
|
||||
export const getSetting = async(): Promise<WebuiSetting | undefined> => {
|
||||
const res = await fetch('/lobe/config');
|
||||
const data = (await res.json()) as WebuiSetting;
|
||||
if (!data || (data as any)?.empty) return undefined;
|
||||
return data;
|
||||
const res = await fetch('/lobe/config');
|
||||
const data = (await res.json()) as WebuiSetting;
|
||||
if (!data || (data as any)?.empty) return undefined;
|
||||
return data;
|
||||
};
|
||||
|
||||
export const postSetting = async(setting: WebuiSetting) => {
|
||||
await fetch('/lobe/config', {
|
||||
body: JSON.stringify(setting),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
method: 'POST',
|
||||
});
|
||||
await fetch('/lobe/config', {
|
||||
body: JSON.stringify(setting),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
method: 'POST',
|
||||
});
|
||||
};
|
||||
|
||||
export const getVersion = async(): Promise<string> => {
|
||||
const res = await fetch('/lobe/package');
|
||||
const data = (await res.json()) as any;
|
||||
if (!data || data.empty || !data.version) return DEFAULT_VERSION;
|
||||
return data.version;
|
||||
const res = await fetch('/lobe/package');
|
||||
const data = (await res.json()) as any;
|
||||
if (!data || data.empty || !data.version) return DEFAULT_VERSION;
|
||||
return data.version;
|
||||
};
|
||||
|
||||
export const getLocaleOptions = async(): Promise<SelectProps['options']> => {
|
||||
const res = await fetch('/lobe/locales/options');
|
||||
const data = (await res.json()) as SelectProps['options'];
|
||||
if (!data || data?.length === 0) return DEFAULT_LOCALE_OPTIONS;
|
||||
return data;
|
||||
const res = await fetch('/lobe/locales/options');
|
||||
const data = (await res.json()) as SelectProps['options'];
|
||||
if (!data || data?.length === 0) return DEFAULT_LOCALE_OPTIONS;
|
||||
return data;
|
||||
};
|
||||
|
||||
export const getLatestVersion = async(): Promise<string> => {
|
||||
const res = await fetch(
|
||||
`https://api.github.com/repos/${homepage.replace('https://github.com/', '')}/releases/latest`,
|
||||
);
|
||||
const data = (await res.json()) as any;
|
||||
if (!data || !data.tag_name) return DEFAULT_VERSION;
|
||||
return semver.clean(data.tag_name as string) || DEFAULT_VERSION;
|
||||
const res = await fetch(
|
||||
`https://api.github.com/repos/${homepage.replace('https://github.com/', '')}/releases/latest`,
|
||||
);
|
||||
const data = (await res.json()) as any;
|
||||
if (!data || !data.tag_name) return DEFAULT_VERSION;
|
||||
return semver.clean(data.tag_name as string) || DEFAULT_VERSION;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import { create } from 'zustand';
|
||||
import { devtools } from 'zustand/middleware';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
import { createWithEqualityFn } from 'zustand/traditional';
|
||||
|
||||
import { type Store, createStore } from './store';
|
||||
|
||||
export const useAppStore = create<Store>()(devtools(createStore));
|
||||
export const useAppStore = createWithEqualityFn<Store>()(devtools(createStore), shallow);
|
||||
|
||||
export * from './action';
|
||||
export * from './initialState';
|
||||
|
|
|
|||
|
|
@ -34,30 +34,30 @@ export interface WebuiSetting {
|
|||
export type WebuiSettingKeys = keyof WebuiSetting;
|
||||
|
||||
export const DEFAULT_SETTING: WebuiSetting = {
|
||||
confirmPageUnload: false,
|
||||
enableExtraNetworkSidebar: true,
|
||||
enableHighlight: false,
|
||||
enableSidebar: true,
|
||||
enableWebFont: true,
|
||||
extraNetworkCardSize: 86,
|
||||
extraNetworkFixedMode: 'fixed',
|
||||
extraNetworkSidebarExpand: true,
|
||||
extraNetworkSidebarWidth: 340,
|
||||
i18n: 'en_US',
|
||||
layoutHideFooter: false,
|
||||
layoutSplitPreview: false,
|
||||
liteAnimation: false,
|
||||
logoCustomTitle: '',
|
||||
logoCustomUrl: '',
|
||||
logoType: 'lobe',
|
||||
neutralColor: undefined,
|
||||
primaryColor: undefined,
|
||||
promptEditor: false,
|
||||
promptTextareaType: 'resizable',
|
||||
sidebarExpand: true,
|
||||
sidebarFixedMode: 'fixed',
|
||||
sidebarWidth: 280,
|
||||
svgIcon: true,
|
||||
confirmPageUnload: false,
|
||||
enableExtraNetworkSidebar: true,
|
||||
enableHighlight: false,
|
||||
enableSidebar: true,
|
||||
enableWebFont: true,
|
||||
extraNetworkCardSize: 86,
|
||||
extraNetworkFixedMode: 'fixed',
|
||||
extraNetworkSidebarExpand: true,
|
||||
extraNetworkSidebarWidth: 340,
|
||||
i18n: 'en_US',
|
||||
layoutHideFooter: false,
|
||||
layoutSplitPreview: false,
|
||||
liteAnimation: false,
|
||||
logoCustomTitle: '',
|
||||
logoCustomUrl: '',
|
||||
logoType: 'lobe',
|
||||
neutralColor: undefined,
|
||||
primaryColor: undefined,
|
||||
promptEditor: false,
|
||||
promptTextareaType: 'resizable',
|
||||
sidebarExpand: true,
|
||||
sidebarFixedMode: 'fixed',
|
||||
sidebarWidth: 280,
|
||||
svgIcon: true,
|
||||
};
|
||||
|
||||
export interface StroeState {
|
||||
|
|
@ -71,11 +71,11 @@ export interface StroeState {
|
|||
}
|
||||
|
||||
export const initialState: StroeState = {
|
||||
currentTab: 'tab_txt2img',
|
||||
latestVersion: DEFAULT_VERSION,
|
||||
loading: true,
|
||||
localeOptions: DEFAULT_LOCALE_OPTIONS,
|
||||
setting: DEFAULT_SETTING,
|
||||
themeMode: 'dark',
|
||||
version: DEFAULT_VERSION,
|
||||
currentTab: 'tab_txt2img',
|
||||
latestVersion: DEFAULT_VERSION,
|
||||
loading: true,
|
||||
localeOptions: DEFAULT_LOCALE_OPTIONS,
|
||||
setting: DEFAULT_SETTING,
|
||||
themeMode: 'dark',
|
||||
version: DEFAULT_VERSION,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ const currentSetting = (s: Store) => ({ ...DEFAULT_SETTING, ...s.setting });
|
|||
const currentTab = (s: Store) => s.currentTab;
|
||||
const themeMode = (s: Store) => s.themeMode;
|
||||
export const selectors = {
|
||||
currentSetting,
|
||||
currentTab,
|
||||
themeMode,
|
||||
currentSetting,
|
||||
currentTab,
|
||||
themeMode,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,6 +6,6 @@ import { type StroeState, initialState } from './initialState';
|
|||
export type Store = StoreAction & StroeState;
|
||||
|
||||
export const createStore: StateCreator<Store, [['zustand/devtools', never]]> = (...parameters) => ({
|
||||
...initialState,
|
||||
...createSettings(...parameters),
|
||||
...initialState,
|
||||
...createSettings(...parameters),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Theme, css } from 'antd-style';
|
||||
|
||||
export default (token: Theme) => {
|
||||
return css`
|
||||
return css`
|
||||
.label-wrap {
|
||||
transition: padding 400ms ${token.motionEaseOut};
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { Theme, css } from 'antd-style';
|
|||
import { readableColor } from 'polished';
|
||||
|
||||
export default (token: Theme) => {
|
||||
return css`
|
||||
return css`
|
||||
.gradio-group,
|
||||
.gradio-row {
|
||||
gap: 12px !important;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Theme, css } from 'antd-style';
|
||||
|
||||
export default (token: Theme) => {
|
||||
return css`
|
||||
return css`
|
||||
#root {
|
||||
/* sd-webui-prompt-all-in-one */
|
||||
.physton-highlight-prompt {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Theme, css } from 'antd-style';
|
||||
|
||||
export default (token: Theme) => {
|
||||
const galleryBackground = css`
|
||||
const galleryBackground = css`
|
||||
background-color: ${token.colorBgContainer};
|
||||
background-image: linear-gradient(45deg, ${token.colorFillTertiary} 25%, transparent 25%),
|
||||
linear-gradient(-45deg, ${token.colorFillTertiary} 25%, transparent 25%),
|
||||
|
|
@ -16,7 +16,7 @@ export default (token: Theme) => {
|
|||
border: 2px solid ${token.colorBorderSecondary} !important;
|
||||
border-radius: ${token.borderRadius}px !important;
|
||||
`;
|
||||
return css`
|
||||
return css`
|
||||
.livePreview,
|
||||
.gradio-gallery,
|
||||
.gradio-image,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Theme, css } from 'antd-style';
|
||||
|
||||
export default (token: Theme) => {
|
||||
return css`
|
||||
return css`
|
||||
.block.gradio-checkbox {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { css } from 'antd-style';
|
||||
|
||||
export default () => {
|
||||
return css`
|
||||
return css`
|
||||
[id$='_settings'] {
|
||||
label.svelte-1ojmf70 {
|
||||
overflow: hidden;
|
||||
|
|
|
|||
|
|
@ -17,21 +17,21 @@ import tabs from './components/tabs';
|
|||
import tokens from './tokens';
|
||||
|
||||
const GlobalStyle = createGlobalStyle(({ theme }) => [
|
||||
tokens(theme),
|
||||
antdOverride(theme),
|
||||
button(theme),
|
||||
container(theme),
|
||||
gallery(theme),
|
||||
input(theme),
|
||||
label(),
|
||||
collapse(theme),
|
||||
options(theme),
|
||||
progress(theme),
|
||||
slider(theme),
|
||||
table(theme),
|
||||
tabs(theme),
|
||||
extensions(theme),
|
||||
lightboxModal(theme),
|
||||
tokens(theme),
|
||||
antdOverride(theme),
|
||||
button(theme),
|
||||
container(theme),
|
||||
gallery(theme),
|
||||
input(theme),
|
||||
label(),
|
||||
collapse(theme),
|
||||
options(theme),
|
||||
progress(theme),
|
||||
slider(theme),
|
||||
table(theme),
|
||||
tabs(theme),
|
||||
extensions(theme),
|
||||
lightboxModal(theme),
|
||||
]);
|
||||
|
||||
export default GlobalStyle;
|
||||
|
|
|
|||
|
|
@ -1,63 +1,63 @@
|
|||
export const kitchenPrimary = {
|
||||
dark: {
|
||||
colorPrimary: '#007AFF',
|
||||
colorPrimaryActive: '#1554ad',
|
||||
colorPrimaryBg: '#111a2c',
|
||||
colorPrimaryBgHover: '#112545',
|
||||
colorPrimaryBorder: '#15325b',
|
||||
colorPrimaryBorderHover: '#15417e',
|
||||
colorPrimaryHover: '#3c89e8',
|
||||
colorPrimaryText: '#1668dc',
|
||||
colorPrimaryTextActive: '#1554ad',
|
||||
colorPrimaryTextHover: '#3c89e8',
|
||||
},
|
||||
light: {
|
||||
colorPrimary: '#007AFF',
|
||||
colorPrimaryActive: '#0958d9',
|
||||
colorPrimaryBg: '#e6f4ff',
|
||||
colorPrimaryBgHover: '#bae0ff',
|
||||
colorPrimaryBorder: '#91caff',
|
||||
colorPrimaryBorderHover: '#69b1ff',
|
||||
colorPrimaryHover: '#4096ff',
|
||||
colorPrimaryText: '#1677ff',
|
||||
colorPrimaryTextActive: '#0958d9',
|
||||
colorPrimaryTextHover: '#4096ff',
|
||||
},
|
||||
dark: {
|
||||
colorPrimary: '#007AFF',
|
||||
colorPrimaryActive: '#1554ad',
|
||||
colorPrimaryBg: '#111a2c',
|
||||
colorPrimaryBgHover: '#112545',
|
||||
colorPrimaryBorder: '#15325b',
|
||||
colorPrimaryBorderHover: '#15417e',
|
||||
colorPrimaryHover: '#3c89e8',
|
||||
colorPrimaryText: '#1668dc',
|
||||
colorPrimaryTextActive: '#1554ad',
|
||||
colorPrimaryTextHover: '#3c89e8',
|
||||
},
|
||||
light: {
|
||||
colorPrimary: '#007AFF',
|
||||
colorPrimaryActive: '#0958d9',
|
||||
colorPrimaryBg: '#e6f4ff',
|
||||
colorPrimaryBgHover: '#bae0ff',
|
||||
colorPrimaryBorder: '#91caff',
|
||||
colorPrimaryBorderHover: '#69b1ff',
|
||||
colorPrimaryHover: '#4096ff',
|
||||
colorPrimaryText: '#1677ff',
|
||||
colorPrimaryTextActive: '#0958d9',
|
||||
colorPrimaryTextHover: '#4096ff',
|
||||
},
|
||||
};
|
||||
|
||||
export const kitchenNeutral = {
|
||||
dark: {
|
||||
colorBgContainer: '#1f1f1f',
|
||||
colorBgElevated: '#222',
|
||||
colorBgLayout: '#181818',
|
||||
colorBgSpotlight: '#444',
|
||||
colorBorder: '#444',
|
||||
colorBorderSecondary: '#333',
|
||||
colorFill: 'rgb(255 255 255 / 18%)',
|
||||
colorFillQuaternary: 'rgb(255 255 255 / 4%)',
|
||||
colorFillSecondary: 'rgb(255 255 255 / 12%)',
|
||||
colorFillTertiary: 'rgb(255 255 255 / 8%)',
|
||||
colorNeutral: '#666',
|
||||
colorText: 'rgb(255 255 255 / 85%)',
|
||||
colorTextQuaternary: 'rgb(255 255 255 / 25%)',
|
||||
colorTextSecondary: 'rgb(255 255 255 / 65%)',
|
||||
colorTextTertiary: 'rgb(255 255 255 / 45%)',
|
||||
},
|
||||
light: {
|
||||
colorBgContainer: '#fff',
|
||||
colorBgElevated: '#fff',
|
||||
colorBgLayout: '#f7f7f7',
|
||||
colorBgSpotlight: 'rgb(0 0 0 / 85%)',
|
||||
colorBorder: '#ddd',
|
||||
colorBorderSecondary: '#eee',
|
||||
colorFill: 'rgb(0 0 0 / 15%)',
|
||||
colorFillQuaternary: 'rgb(0 0 0 / 2%)',
|
||||
colorFillSecondary: 'rgb(0 0 0 / 6%)',
|
||||
colorFillTertiary: 'rgb(0 0 0 / 4%)',
|
||||
colorNeutral: '#666',
|
||||
colorText: 'rgb(0 0 0 / 88%)',
|
||||
colorTextQuaternary: 'rgb(0 0 0 / 25%)',
|
||||
colorTextSecondary: 'rgb(0 0 0 / 65%)',
|
||||
colorTextTertiary: 'rgb(0 0 0 / 45%)',
|
||||
},
|
||||
dark: {
|
||||
colorBgContainer: '#1f1f1f',
|
||||
colorBgElevated: '#222',
|
||||
colorBgLayout: '#181818',
|
||||
colorBgSpotlight: '#444',
|
||||
colorBorder: '#444',
|
||||
colorBorderSecondary: '#333',
|
||||
colorFill: 'rgb(255 255 255 / 18%)',
|
||||
colorFillQuaternary: 'rgb(255 255 255 / 4%)',
|
||||
colorFillSecondary: 'rgb(255 255 255 / 12%)',
|
||||
colorFillTertiary: 'rgb(255 255 255 / 8%)',
|
||||
colorNeutral: '#666',
|
||||
colorText: 'rgb(255 255 255 / 85%)',
|
||||
colorTextQuaternary: 'rgb(255 255 255 / 25%)',
|
||||
colorTextSecondary: 'rgb(255 255 255 / 65%)',
|
||||
colorTextTertiary: 'rgb(255 255 255 / 45%)',
|
||||
},
|
||||
light: {
|
||||
colorBgContainer: '#fff',
|
||||
colorBgElevated: '#fff',
|
||||
colorBgLayout: '#f7f7f7',
|
||||
colorBgSpotlight: 'rgb(0 0 0 / 85%)',
|
||||
colorBorder: '#ddd',
|
||||
colorBorderSecondary: '#eee',
|
||||
colorFill: 'rgb(0 0 0 / 15%)',
|
||||
colorFillQuaternary: 'rgb(0 0 0 / 2%)',
|
||||
colorFillSecondary: 'rgb(0 0 0 / 6%)',
|
||||
colorFillTertiary: 'rgb(0 0 0 / 4%)',
|
||||
colorNeutral: '#666',
|
||||
colorText: 'rgb(0 0 0 / 88%)',
|
||||
colorTextQuaternary: 'rgb(0 0 0 / 25%)',
|
||||
colorTextSecondary: 'rgb(0 0 0 / 65%)',
|
||||
colorTextTertiary: 'rgb(0 0 0 / 45%)',
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { type Theme, css } from 'antd-style';
|
|||
import { readableColor } from 'polished';
|
||||
|
||||
export default (token: Theme) => {
|
||||
return css`
|
||||
return css`
|
||||
:root,
|
||||
.dark {
|
||||
--primary-50: ${token.geekblue1};
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import i18nOptions from '@/../locales/options.json';
|
|||
export type I18n = (typeof i18nOptions)[number]['value'];
|
||||
|
||||
export const resources = {
|
||||
translation,
|
||||
translation,
|
||||
} as const;
|
||||
|
||||
type TranslationKeys = keyof typeof translation;
|
||||
|
|
|
|||
Loading…
Reference in New Issue