✨ feat(i18n): Support i18n and welcome PR contributions
parent
fe32de2204
commit
a61f3b4d30
|
|
@ -56,6 +56,7 @@
|
|||
- [x] 🎛️ 高定制侧边栏,左侧为快捷设置侧边栏,右侧为模型侧边栏
|
||||
- [x] 🖼️ 可调节画板比例,使生成图像始终置顶
|
||||
- [x] 📱 移动端友好,针对手机屏幕完成部分优化
|
||||
- [x] 🇨🇳 支持 i18n 并欢迎提交 [PR](https://github.com/canisminor1990/sd-webui-lobe-theme/tree/main/src/i18n/lang) 贡献
|
||||
- [ ] 📝 语法高亮的 Prompt 输入框
|
||||
- [ ] 🆗 i18n 多语言支持
|
||||
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ English · [简体中文](./README-zh_CN.md) · [Changelog](./CHANGELOG.md) · [
|
|||
- [x] 🎛️ Highly customizable sidebar, with a quick settings sidebar on the left and a model sidebar on the right
|
||||
- [x] 🖼️ Adjustable canvas ratio, ensuring that generated images are always displayed at the top
|
||||
- [x] 📱 Mobile-friendly, with partial optimization for mobile screens
|
||||
- [x] 🇨🇳 Support i18n and welcome [PR](https://github.com/canisminor1990/sd-webui-lobe-theme/tree/main/src/i18n/lang) contributions
|
||||
- [ ] 📝 Syntax highlighting in the prompt input box
|
||||
- [ ] 🆗 Multilingual support with i18n
|
||||
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@
|
|||
"eslint": "^8",
|
||||
"fast-deep-equal": "^3",
|
||||
"husky": "^8",
|
||||
"i18next": "^23",
|
||||
"lightningcss": "^1",
|
||||
"lint-staged": "^13",
|
||||
"lodash-es": "^4",
|
||||
|
|
@ -95,6 +96,7 @@
|
|||
"react": "^18",
|
||||
"react-dom": "^18",
|
||||
"react-helmet": "^6",
|
||||
"react-i18next": "^13",
|
||||
"react-layout-kit": "^1",
|
||||
"react-rnd": "^10",
|
||||
"react-tag-input": "^6",
|
||||
|
|
|
|||
76
src/App.tsx
76
src/App.tsx
|
|
@ -1,4 +1,4 @@
|
|||
import { memo, useEffect, useState } from 'react';
|
||||
import { StrictMode, Suspense, memo, useEffect, useState } from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
|
|
@ -7,6 +7,8 @@ import Index from '@/pages';
|
|||
import Loading from '@/slots/Loading';
|
||||
import { useAppStore } from '@/store';
|
||||
|
||||
import './i18n/config';
|
||||
|
||||
const App = memo(() => {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const setCurrentTab = useAppStore((st) => st.setCurrentTab, shallow);
|
||||
|
|
@ -22,41 +24,43 @@ const App = memo(() => {
|
|||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<link
|
||||
href="https://npm.elemecdn.com/@lobehub/assets-favicons/assets/apple-touch-icon.png"
|
||||
rel="apple-touch-icon"
|
||||
sizes="180x180"
|
||||
/>
|
||||
<link
|
||||
href="https://npm.elemecdn.com/@lobehub/assets-favicons/assets/favicon-32x32.png"
|
||||
rel="icon"
|
||||
sizes="32x32"
|
||||
type="image/png"
|
||||
/>
|
||||
<link
|
||||
href="https://npm.elemecdn.com/@lobehub/assets-favicons/assets/favicon-16x16.png"
|
||||
rel="icon"
|
||||
sizes="16x16"
|
||||
type="image/png"
|
||||
/>
|
||||
<link
|
||||
href="https://npm.elemecdn.com/@lobehub/assets-favicons/assets/site.webmanifest"
|
||||
rel="manifest"
|
||||
/>
|
||||
<link
|
||||
color="#000000"
|
||||
href="https://npm.elemecdn.com/@lobehub/assets-favicons/assets/safari-pinned-tab.svg"
|
||||
rel="mask-icon"
|
||||
/>
|
||||
<meta content="LobeHub" name="apple-mobile-web-app-title" />
|
||||
<meta content="LobeHub" name="application-name" />
|
||||
<meta content="#000000" name="msapplication-TileColor" />
|
||||
<meta content="#000000" name="theme-color" />
|
||||
</Helmet>
|
||||
<Layout>{loading ? <Loading /> : <Index />}</Layout>
|
||||
</>
|
||||
<StrictMode>
|
||||
<Suspense fallback={<Loading />}>
|
||||
<Helmet>
|
||||
<link
|
||||
href="https://npm.elemecdn.com/@lobehub/assets-favicons/assets/apple-touch-icon.png"
|
||||
rel="apple-touch-icon"
|
||||
sizes="180x180"
|
||||
/>
|
||||
<link
|
||||
href="https://npm.elemecdn.com/@lobehub/assets-favicons/assets/favicon-32x32.png"
|
||||
rel="icon"
|
||||
sizes="32x32"
|
||||
type="image/png"
|
||||
/>
|
||||
<link
|
||||
href="https://npm.elemecdn.com/@lobehub/assets-favicons/assets/favicon-16x16.png"
|
||||
rel="icon"
|
||||
sizes="16x16"
|
||||
type="image/png"
|
||||
/>
|
||||
<link
|
||||
href="https://npm.elemecdn.com/@lobehub/assets-favicons/assets/site.webmanifest"
|
||||
rel="manifest"
|
||||
/>
|
||||
<link
|
||||
color="#000000"
|
||||
href="https://npm.elemecdn.com/@lobehub/assets-favicons/assets/safari-pinned-tab.svg"
|
||||
rel="mask-icon"
|
||||
/>
|
||||
<meta content="LobeHub" name="apple-mobile-web-app-title" />
|
||||
<meta content="LobeHub" name="application-name" />
|
||||
<meta content="#000000" name="msapplication-TileColor" />
|
||||
<meta content="#000000" name="theme-color" />
|
||||
</Helmet>
|
||||
<Layout>{loading ? <Loading /> : <Index />}</Layout>
|
||||
</Suspense>
|
||||
</StrictMode>
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { memo } from 'react';
|
||||
|
||||
import { DivProps } from '@/types/index';
|
||||
import { type DivProps } from '@/types';
|
||||
|
||||
import { useStyles } from './style';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { memo } from 'react';
|
||||
|
||||
import { DivProps } from '@/types/index';
|
||||
import { type DivProps } from '@/types';
|
||||
|
||||
import { useStyles } from './style';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { memo } from 'react';
|
||||
|
||||
import { DivProps } from '@/types/index';
|
||||
import { type DivProps } from '@/types';
|
||||
|
||||
import { useStyles } from './style';
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { ActionIcon } from '@lobehub/ui';
|
|||
import { PanelLeft, Pin, PinOff } from 'lucide-react';
|
||||
import { memo } from 'react';
|
||||
|
||||
import { DivProps } from '@/types/index';
|
||||
import { type DivProps } from '@/types';
|
||||
|
||||
import { useStyles } from './style';
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
import i18next from 'i18next';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
|
||||
import en from './lang/en';
|
||||
import ja_JP from './lang/ja_JP';
|
||||
import ko_KR from './lang/ko_KR';
|
||||
import zh_CN from './lang/zh_CN';
|
||||
import zh_HK from './lang/zh_HK';
|
||||
|
||||
i18next.use(initReactI18next).init({
|
||||
debug: process.env.NODE_ENV === 'development',
|
||||
fallbackLng: 'en',
|
||||
lng: 'en',
|
||||
resources: {
|
||||
en: {
|
||||
translation: en,
|
||||
},
|
||||
ja_JP: {
|
||||
translation: ja_JP,
|
||||
},
|
||||
ko_KR: {
|
||||
translation: ko_KR,
|
||||
},
|
||||
zh_CN: {
|
||||
translation: zh_CN,
|
||||
},
|
||||
zh_HK: {
|
||||
translation: zh_HK,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
import { type SelectProps } from 'antd';
|
||||
|
||||
export const i18nOptions: SelectProps['options'] = [
|
||||
{
|
||||
label: 'English',
|
||||
value: 'en',
|
||||
},
|
||||
{
|
||||
label: '简体中文',
|
||||
value: 'zh_CN',
|
||||
},
|
||||
{
|
||||
label: '繁體中文',
|
||||
value: 'zh_HK',
|
||||
},
|
||||
{
|
||||
label: '日本語',
|
||||
value: 'ja_JP',
|
||||
},
|
||||
];
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
const translation = {
|
||||
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',
|
||||
settingButtomReset: 'Reset',
|
||||
settingButtomSubmit: 'Apply and Restart Interface',
|
||||
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',
|
||||
settingGroupPromotTextarea: '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',
|
||||
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',
|
||||
themeFeedback: 'Theme Feedback',
|
||||
themeSetting: 'Theme Settings',
|
||||
};
|
||||
|
||||
export default translation;
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
import type { Translation } from '@/types';
|
||||
|
||||
const translation: Translation = {
|
||||
community: 'コミュニティ',
|
||||
custom: 'カスタム',
|
||||
extraNetwork: '追加ネットワーク',
|
||||
feedback: 'フィードバック',
|
||||
fixed: '固定',
|
||||
float: 'フロート',
|
||||
help: 'ヘルプ',
|
||||
kitchen: 'キッチン',
|
||||
loadPrompt: 'プロンプトをロード',
|
||||
lobe: 'ローブ',
|
||||
moreProducts: 'その他の製品',
|
||||
negative: 'ネガティブなヒント',
|
||||
positive: 'ポジティブなヒント',
|
||||
quickSetting: 'クイック設定',
|
||||
resizable: 'リサイズ可能',
|
||||
resources: '関連リソース',
|
||||
scroll: 'スクロール',
|
||||
setPrompt: 'プロンプトを設定',
|
||||
setting: '設定',
|
||||
settingButtomReset: 'リセット',
|
||||
settingButtomSubmit: '適用して再起動',
|
||||
settingCustomLogo: 'カスタムロゴ',
|
||||
settingCustomLogoDesc: 'URL / Base64 / 絵文字をサポート',
|
||||
settingCustomTitle: 'カスタムタイトル',
|
||||
settingCustomTitleDesc: 'カスタムロゴのタイトル名',
|
||||
settingExtraNetworkSidebarDefaultCardSize: 'モデルカバーサイズ',
|
||||
settingExtraNetworkSidebarDefaultCardSizeDesc: '起動時のモデルカバーサイズのデフォルト値',
|
||||
settingExtraNetworkSidebarDefaultExpand: 'デフォルトで展開',
|
||||
settingExtraNetworkSidebarDefaultExpandDesc: '起動時にサイドバーをデフォルトで展開しますか?',
|
||||
settingExtraNetworkSidebarDefaultWidth: 'デフォルト幅',
|
||||
settingExtraNetworkSidebarDefaultWidthDesc: '起動時のサイドバーのデフォルト幅',
|
||||
settingExtraNetworkSidebarDisplayMode: '表示モード',
|
||||
settingExtraNetworkSidebarDisplayModeDesc:
|
||||
'グリッドモードで常に表示するか、ホバー時に自動的に展開するフロートモードで表示するか',
|
||||
settingExtraNetworkSidebarEnable: '有効にする',
|
||||
settingExtraNetworkSidebarEnableDesc: '右側の追加ネットワークサイドバーを有効にする',
|
||||
settingGroupExtraNetworkSidebar: '追加ネットワークサイドバー',
|
||||
settingGroupLayout: 'レイアウト設定',
|
||||
settingGroupPromotTextarea: 'プロンプトテキストエリア',
|
||||
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: 'クイック設定サイドバーの上部に簡単なプロンプトエディタを提供します',
|
||||
settingQuickSettingSidebarDefaultExpand: 'デフォルトで展開',
|
||||
settingQuickSettingSidebarDefaultExpandDesc: '起動時にサイドバーをデフォルトで展開しますか?',
|
||||
settingQuickSettingSidebarDefaultWidth: 'デフォルト幅',
|
||||
settingQuickSettingSidebarDefaultWidthDesc: '起動時のサイドバーのデフォルト幅',
|
||||
settingQuickSettingSidebarDisplayMode: '表示モード',
|
||||
settingQuickSettingSidebarDisplayModeDesc:
|
||||
'グリッドモードで常に表示するか、ホバー時に自動的に展開するフロートモードで表示するか',
|
||||
settingQuickSettingSidebarEnable: '有効にする',
|
||||
settingQuickSettingSidebarEnableDesc: '左側のクイック設定サイドバーを有効にする',
|
||||
settingReduceAnimation: 'アニメーションを削減',
|
||||
settingReduceAnimationDesc:
|
||||
'ガラスのエフェクトと背景の流れる色を削減し、スムーズさを向上させ、CPUの使用量を節約できます',
|
||||
settingSplitPreviewer: '2列モード',
|
||||
settingSplitPreviewerDesc:
|
||||
'プロンプト入力ボックスを左側に配置し、生成ボタンを右側に配置し、スクロール時に生成された画像が常にトップに表示されるようにします(実験的)',
|
||||
settingSvgIcons: 'SVGアイコンを使用',
|
||||
settingSvgIconsDesc: 'stable diffusion webuiの絵文字アイコンをすべてSVGアイコンに置き換えます',
|
||||
switchTheme: '明暗テーマを切り替える',
|
||||
themeFeedback: 'テーマのフィードバック',
|
||||
themeSetting: 'テーマ設定',
|
||||
};
|
||||
|
||||
export default translation;
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
import type { Translation } from '@/types';
|
||||
|
||||
const translation: Translation = {
|
||||
community: '커뮤니티',
|
||||
custom: '사용자 정의',
|
||||
extraNetwork: '추가 네트워크',
|
||||
feedback: '피드백',
|
||||
fixed: '고정',
|
||||
float: '부유',
|
||||
help: '도움말',
|
||||
kitchen: 'Kitchen',
|
||||
loadPrompt: '로드 프롬프트',
|
||||
lobe: 'Lobe',
|
||||
moreProducts: '더 많은 제품',
|
||||
negative: '부정적인',
|
||||
positive: '긍정적인',
|
||||
quickSetting: '빠른 설정',
|
||||
resizable: '크기 조절 가능',
|
||||
resources: '관련 자료',
|
||||
scroll: '스크롤',
|
||||
setPrompt: '프롬프트 설정',
|
||||
setting: '설정',
|
||||
settingButtomReset: '재설정',
|
||||
settingButtomSubmit: '적용 및 인터페이스 재시작',
|
||||
settingCustomLogo: '사용자 정의 로고',
|
||||
settingCustomLogoDesc: 'URL / Base64 / 이모지 표정을 지원합니다.',
|
||||
settingCustomTitle: '사용자 정의 제목',
|
||||
settingCustomTitleDesc: '사용자 정의 로고 제목',
|
||||
settingExtraNetworkSidebarDefaultCardSize: '모델 커버 크기',
|
||||
settingExtraNetworkSidebarDefaultCardSizeDesc: '시작시 모델 커버 크기 기본값',
|
||||
settingExtraNetworkSidebarDefaultExpand: '기본 확장',
|
||||
settingExtraNetworkSidebarDefaultExpandDesc: '시작시 사이드바 기본 확장 여부',
|
||||
settingExtraNetworkSidebarDefaultWidth: '기본 너비',
|
||||
settingExtraNetworkSidebarDefaultWidthDesc: '시작시 사이드바 기본 너비',
|
||||
settingExtraNetworkSidebarDisplayMode: '표시 모드',
|
||||
settingExtraNetworkSidebarDisplayModeDesc:
|
||||
'그리드 모드로 고정하여 항상 표시하거나, 부유 모드로 설정하여 사이드바에 마우스를 가져가면 자동으로 확장',
|
||||
settingExtraNetworkSidebarEnable: '사용',
|
||||
settingExtraNetworkSidebarEnableDesc: '오른쪽에 추가 네트워크 사이드바 활성화',
|
||||
settingGroupExtraNetworkSidebar: '추가 네트워크 사이드바',
|
||||
settingGroupLayout: '레이아웃 설정',
|
||||
settingGroupPromotTextarea: '프롬프트 텍스트 영역',
|
||||
settingGroupQuickSettingSidebar: '빠른 설정 사이드바',
|
||||
settingGroupTheme: '테마 설정',
|
||||
settingHideFooter: '푸터 숨기기',
|
||||
settingHideFooterDesc: '테마 푸터를 숨기고 stable diffusion webui의 기본 푸터만 표시',
|
||||
settingLanguage: '언어',
|
||||
settingLanguageDesc: 'Lobe Theme 테마 언어',
|
||||
settingLogoPreview: '미리보기',
|
||||
settingLogoType: '로고 유형',
|
||||
settingLogoTypeDesc: '로고 유형',
|
||||
settingNeutralColor: '중립색',
|
||||
settingNeutralColorDesc:
|
||||
'다른 색상 경향의 그레이 스케일 사용자 정의, 두 번째는 원래 Kitchen의 중립색',
|
||||
settingPrimaryColor: '기본 색상',
|
||||
settingPrimaryColorDesc: '사용자 정의 기본 색상, 두 번째는 원래 Kitchen의 기본 색상',
|
||||
settingPromptDisplayMode: '표시 모드',
|
||||
settingPromptDisplayModeDesc: '고정 높이 또는 자동 높이 및 드래그 조절 지원',
|
||||
settingPromptEditor: '프롬프트 편집기',
|
||||
settingPromptEditorDesc: '빠른 설정 사이드바 상단에 간단한 프롬프트 편집기 제공',
|
||||
settingQuickSettingSidebarDefaultExpand: '기본 확장',
|
||||
settingQuickSettingSidebarDefaultExpandDesc: '시작시 사이드바 기본 확장 여부',
|
||||
settingQuickSettingSidebarDefaultWidth: '기본 너비',
|
||||
settingQuickSettingSidebarDefaultWidthDesc: '시작시 사이드바 기본 너비',
|
||||
settingQuickSettingSidebarDisplayMode: '표시 모드',
|
||||
settingQuickSettingSidebarDisplayModeDesc:
|
||||
'그리드 모드로 고정하여 항상 표시하거나, 부유 모드로 설정하여 사이드바에 마우스를 가져가면 자동으로 확장',
|
||||
settingQuickSettingSidebarEnable: '사용',
|
||||
settingQuickSettingSidebarEnableDesc: '왼쪽에 빠른 설정 사이드바 활성화',
|
||||
settingReduceAnimation: '애니메이션 줄이기',
|
||||
settingReduceAnimationDesc:
|
||||
'유리 효과와 배경 흐름 색상을 줄여서 부드러움을 향상시키고 CPU 사용량을 줄일 수 있습니다.',
|
||||
settingSplitPreviewer: '이중 열 모드',
|
||||
settingSplitPreviewerDesc:
|
||||
'프롬프트 입력 상자를 왼쪽에 배치하고, 우측에 생성 버튼을 두어 스크롤 시 생성된 이미지가 항상 위에 표시되도록 합니다 (실험적)',
|
||||
settingSvgIcons: 'SVG 아이콘 사용',
|
||||
settingSvgIconsDesc:
|
||||
'stable diffusion webui의 이모지 아이콘을 전역적으로 SVG 아이콘으로 교체합니다.',
|
||||
switchTheme: '밝기 테마 전환',
|
||||
themeFeedback: '테마 피드백',
|
||||
themeSetting: '테마 설정',
|
||||
};
|
||||
|
||||
export default translation;
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
import type { Translation } from '@/types';
|
||||
|
||||
const translation: Translation = {
|
||||
community: '社区',
|
||||
custom: '自定义',
|
||||
extraNetwork: '附加网络',
|
||||
feedback: '反馈',
|
||||
fixed: '固定',
|
||||
float: '悬浮',
|
||||
help: '帮助',
|
||||
kitchen: 'Kitchen',
|
||||
loadPrompt: '加载提示',
|
||||
lobe: 'Lobe',
|
||||
moreProducts: '更多产品',
|
||||
negative: '反向提示词',
|
||||
positive: '正面提示词',
|
||||
quickSetting: '快捷设置',
|
||||
resizable: '缩放',
|
||||
resources: '相关资源',
|
||||
scroll: '滚动',
|
||||
setPrompt: '发送提示词',
|
||||
setting: '设置',
|
||||
settingButtomReset: '重置',
|
||||
settingButtomSubmit: '应用并重启界面',
|
||||
settingCustomLogo: '自定义 Logo',
|
||||
settingCustomLogoDesc: '支持 URL / Base64 / Emoji 表情符号',
|
||||
settingCustomTitle: '自定义标题',
|
||||
settingCustomTitleDesc: '自定义 Logo 标题名称',
|
||||
settingExtraNetworkSidebarDefaultCardSize: '模型封面尺寸',
|
||||
settingExtraNetworkSidebarDefaultCardSizeDesc: '启动时模型封面尺寸默认值',
|
||||
settingExtraNetworkSidebarDefaultExpand: '默认展开',
|
||||
settingExtraNetworkSidebarDefaultExpandDesc: '是否在启动时将侧边栏默认展开',
|
||||
settingExtraNetworkSidebarDefaultWidth: '默认宽度',
|
||||
settingExtraNetworkSidebarDefaultWidthDesc: '侧边栏在启动时的默认宽度',
|
||||
settingExtraNetworkSidebarDisplayMode: '显示模式',
|
||||
settingExtraNetworkSidebarDisplayModeDesc:
|
||||
'固定为栅格模式常驻显示,悬浮模式时当鼠标移到侧边时自动展开',
|
||||
settingExtraNetworkSidebarEnable: '启用',
|
||||
settingExtraNetworkSidebarEnableDesc: '启用位于右侧的附加网络侧边栏',
|
||||
settingGroupExtraNetworkSidebar: '附加网络侧边栏',
|
||||
settingGroupLayout: '布局设置',
|
||||
settingGroupPromotTextarea: '提示词文本框',
|
||||
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: '提供简易的提示词编辑器位于快捷设置侧边栏顶部',
|
||||
settingQuickSettingSidebarDefaultExpand: '默认展开',
|
||||
settingQuickSettingSidebarDefaultExpandDesc: '是否在启动时将侧边栏默认展开',
|
||||
settingQuickSettingSidebarDefaultWidth: '默认宽度',
|
||||
settingQuickSettingSidebarDefaultWidthDesc: '侧边栏在启动时的默认宽度',
|
||||
settingQuickSettingSidebarDisplayMode: '显示模式',
|
||||
settingQuickSettingSidebarDisplayModeDesc:
|
||||
'固定为栅格模式常驻显示,悬浮模式时当鼠标移到侧边时自动展开',
|
||||
settingQuickSettingSidebarEnable: '启用',
|
||||
settingQuickSettingSidebarEnableDesc: '启用位于左侧的快捷设置侧边栏',
|
||||
settingReduceAnimation: '减少动画效果',
|
||||
settingReduceAnimationDesc: '减少毛玻璃效果和背景流动色,可以提升流畅度并节省 CPU 使用',
|
||||
settingSplitPreviewer: '双列模式',
|
||||
settingSplitPreviewerDesc:
|
||||
'将提示词输入框放在左侧,生成按钮于右侧,确保在滚动时生成的图像始终显示在顶部 (实验性)',
|
||||
settingSvgIcons: '使用 SVG 图标',
|
||||
settingSvgIconsDesc: '将 stable diffusion webui 中的 Emoji 图标全局替换为 SVG 图标',
|
||||
switchTheme: '切换亮暗色主题',
|
||||
themeFeedback: '主题反馈',
|
||||
themeSetting: '主题设置',
|
||||
};
|
||||
|
||||
export default translation;
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
import type { Translation } from '@/types';
|
||||
|
||||
const translation: Translation = {
|
||||
community: '社區',
|
||||
custom: '自訂',
|
||||
extraNetwork: '附加網絡',
|
||||
feedback: '反饋',
|
||||
fixed: '固定',
|
||||
float: '懸浮',
|
||||
help: '幫助',
|
||||
kitchen: 'Kitchen',
|
||||
loadPrompt: '加載提示',
|
||||
lobe: 'Lobe',
|
||||
moreProducts: '更多產品',
|
||||
negative: '反向提示詞',
|
||||
positive: '正面提示詞',
|
||||
quickSetting: '快捷設置',
|
||||
resizable: '縮放',
|
||||
resources: '相關資源',
|
||||
scroll: '滾動',
|
||||
setPrompt: '發送提示詞',
|
||||
setting: '設置',
|
||||
settingButtomReset: '重置',
|
||||
settingButtomSubmit: '應用並重啟界面',
|
||||
settingCustomLogo: '自定義 Logo',
|
||||
settingCustomLogoDesc: '支持 URL / Base64 / Emoji 表情符號',
|
||||
settingCustomTitle: '自定義標題',
|
||||
settingCustomTitleDesc: '自定義 Logo 標題名稱',
|
||||
settingExtraNetworkSidebarDefaultCardSize: '模型封面尺寸',
|
||||
settingExtraNetworkSidebarDefaultCardSizeDesc: '啟動時模型封面尺寸默認值',
|
||||
settingExtraNetworkSidebarDefaultExpand: '默認展開',
|
||||
settingExtraNetworkSidebarDefaultExpandDesc: '是否在啟動時將側邊欄默認展開',
|
||||
settingExtraNetworkSidebarDefaultWidth: '默認寬度',
|
||||
settingExtraNetworkSidebarDefaultWidthDesc: '側邊欄在啟動時的默認寬度',
|
||||
settingExtraNetworkSidebarDisplayMode: '顯示模式',
|
||||
settingExtraNetworkSidebarDisplayModeDesc:
|
||||
'固定為格模式常駐顯示,懸浮模式時當鼠標移到側邊時自動展開',
|
||||
settingExtraNetworkSidebarEnable: '啟用',
|
||||
settingExtraNetworkSidebarEnableDesc: '啟用位於右側的附加網絡側邊欄',
|
||||
settingGroupExtraNetworkSidebar: '附加網絡側邊欄',
|
||||
settingGroupLayout: '佈局設置',
|
||||
settingGroupPromotTextarea: '提示詞文本框',
|
||||
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: '提供簡易的提示詞編輯器位於快捷設置側邊欄頂部',
|
||||
settingQuickSettingSidebarDefaultExpand: '默認展開',
|
||||
settingQuickSettingSidebarDefaultExpandDesc: '是否在啟動時將側邊欄默認展開',
|
||||
settingQuickSettingSidebarDefaultWidth: '默認寬度',
|
||||
settingQuickSettingSidebarDefaultWidthDesc: '側邊欄在啟動時的默認寬度',
|
||||
settingQuickSettingSidebarDisplayMode: '顯示模式',
|
||||
settingQuickSettingSidebarDisplayModeDesc:
|
||||
'固定為格模式常駐顯示,懸浮模式時當鼠標移到側邊時自動展開',
|
||||
settingQuickSettingSidebarEnable: '啟用',
|
||||
settingQuickSettingSidebarEnableDesc: '啟用位於左側的快捷設置側邊欄',
|
||||
settingReduceAnimation: '減少動畫效果',
|
||||
settingReduceAnimationDesc: '減少毛玻璃效果和背景流動色,可以提升流暢度並節省 CPU 使用',
|
||||
settingSplitPreviewer: '雙列模式',
|
||||
settingSplitPreviewerDesc:
|
||||
'將提示詞輸入框放在左側,生成按鈕於右側,確保在滾動時生成的圖像始終顯示在頂部 (實驗性)',
|
||||
settingSvgIcons: '使用 SVG 圖標',
|
||||
settingSvgIconsDesc: '將 stable diffusion webui 中的 Emoji 圖標全局替換為 SVG 圖標',
|
||||
switchTheme: '切換亮暗色主題',
|
||||
themeFeedback: '主題反饋',
|
||||
themeSetting: '主題設置',
|
||||
};
|
||||
|
||||
export default translation;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { DivProps, ThemeProvider, colors } from '@lobehub/ui';
|
||||
import { type DivProps, ThemeProvider, colors } from '@lobehub/ui';
|
||||
import {
|
||||
generateColorNeutralPalette,
|
||||
generateColorPalette,
|
||||
|
|
@ -6,6 +6,7 @@ import {
|
|||
import isEqual from 'fast-deep-equal';
|
||||
import qs from 'query-string';
|
||||
import { memo, useCallback, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import { useIsDarkMode } from '@/hooks/useIsDarkMode';
|
||||
|
|
@ -21,10 +22,14 @@ const Layout = memo<DivProps>(({ children }) => {
|
|||
);
|
||||
const setting = useAppStore((st) => st.setting, isEqual);
|
||||
const isDarkMode = useIsDarkMode();
|
||||
const { i18n } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
onInit();
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
i18n.changeLanguage(setting.i18n);
|
||||
}, [setting.i18n]);
|
||||
useEffect(() => {
|
||||
const queryTheme: any = String(qs.parseUrl(window.location.href).query.__theme || '');
|
||||
if (queryTheme) {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { memo, useEffect, useRef } from 'react';
|
|||
import draggablePanel from '@/script/draggablePanel';
|
||||
import formatPrompt from '@/script/formatPrompt';
|
||||
import { useAppStore } from '@/store';
|
||||
import { DivProps } from '@/types';
|
||||
import { type DivProps } from '@/types';
|
||||
|
||||
import { useStyles as usePreviewStyles } from '../Preview/style';
|
||||
import { useStyles } from './style';
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ const Inner = memo(() => {
|
|||
const [setting, currentTab] = useAppStore((st) => [st.setting, st.currentTab], shallow);
|
||||
const [size, setSize] = useState<number>(setting?.extraNetworkCardSize || 86);
|
||||
const { styles } = useStyles({ size });
|
||||
|
||||
useEffect(() => {
|
||||
console.time('🤯 [layout] inject - ExtraNetworkSidebar');
|
||||
if (setting.enableExtraNetworkSidebar) {
|
||||
|
|
|
|||
|
|
@ -2,10 +2,11 @@ import { DraggablePanel, LayoutSidebarInner } from '@lobehub/ui';
|
|||
import { useResponsive } from 'antd-style';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { memo, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { SidebarContainer, SidebarHeader } from '@/components';
|
||||
import { useAppStore } from '@/store';
|
||||
import { DivProps } from '@/types/index';
|
||||
import { type DivProps } from '@/types';
|
||||
|
||||
import Inner from './Inner';
|
||||
import { useStyles } from './style';
|
||||
|
|
@ -20,6 +21,7 @@ const ExtraNetworkSidebar = memo<ExtraNetworkSidebarProps>(({ headerHeight }) =>
|
|||
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(() => {
|
||||
const index2indexExtraNetworkButton = gradioApp().querySelector(
|
||||
|
|
@ -63,7 +65,7 @@ const ExtraNetworkSidebar = memo<ExtraNetworkSidebarProps>(({ headerHeight }) =>
|
|||
position="right"
|
||||
setExpand={setExpand}
|
||||
setPin={setPin}
|
||||
title="ExraNetworks"
|
||||
title={t('extraNetwork')}
|
||||
/>
|
||||
<Inner />
|
||||
</SidebarContainer>
|
||||
|
|
|
|||
|
|
@ -1,100 +1,88 @@
|
|||
import { Icon } from '@lobehub/ui';
|
||||
import { FooterProps as FProps } from '@lobehub/ui/es/Footer';
|
||||
import { Bug, FileClock, GitFork, Github } from 'lucide-react';
|
||||
|
||||
import { homepage } from '@/../package.json';
|
||||
|
||||
export const columns: FProps['columns'] = [
|
||||
export const Resources = [
|
||||
{
|
||||
items: [
|
||||
{
|
||||
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',
|
||||
},
|
||||
],
|
||||
title: 'Resources',
|
||||
description: 'AUTOMATIC111',
|
||||
openExternal: true,
|
||||
title: 'Stable Diffusion Webui',
|
||||
url: 'https://github.com/AUTOMATIC1111/stable-diffusion-webui',
|
||||
},
|
||||
{
|
||||
items: [
|
||||
{
|
||||
icon: <Icon icon={Bug} size="small" />,
|
||||
openExternal: true,
|
||||
title: 'Report Bug',
|
||||
url: `${homepage}/issues/new/choose`,
|
||||
},
|
||||
{
|
||||
icon: <Icon icon={GitFork} size="small" />,
|
||||
openExternal: true,
|
||||
title: 'Request Feature',
|
||||
url: `${homepage}/issues/new/choose`,
|
||||
},
|
||||
],
|
||||
title: 'Community',
|
||||
description: 'WebUI extension',
|
||||
openExternal: true,
|
||||
title: 'Controlnet',
|
||||
url: 'https://github.com/Mikubill/sd-webui-controlnet',
|
||||
},
|
||||
{
|
||||
items: [
|
||||
{
|
||||
icon: <Icon icon={Github} size="small" />,
|
||||
openExternal: true,
|
||||
title: 'GitHub',
|
||||
url: homepage,
|
||||
},
|
||||
{
|
||||
icon: <Icon icon={FileClock} size="small" />,
|
||||
openExternal: true,
|
||||
title: 'Changelog',
|
||||
url: `${homepage}/blob/main/CHANGELOG.md`,
|
||||
},
|
||||
],
|
||||
title: 'Help',
|
||||
description: 'Art models',
|
||||
openExternal: true,
|
||||
title: 'Civitai',
|
||||
url: 'https://civitai.com/',
|
||||
},
|
||||
{
|
||||
items: [
|
||||
{
|
||||
description: 'Minifier ExtraNetwrok Covers',
|
||||
openExternal: true,
|
||||
title: '✂️ Cover Minifier',
|
||||
url: 'https://github.com/canisminor1990/sd-webui-cover-minifier',
|
||||
},
|
||||
{
|
||||
description: 'AIGC Components',
|
||||
openExternal: true,
|
||||
title: '🤯 Lobe UI',
|
||||
url: 'https://github.com/lobehub/lobe-ui',
|
||||
},
|
||||
{
|
||||
description: 'AI Commit CLI',
|
||||
openExternal: true,
|
||||
title: '💌 Lobe Commit',
|
||||
url: 'https://github.com/lobehub/lobe-commit',
|
||||
},
|
||||
],
|
||||
title: 'More Products',
|
||||
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`,
|
||||
},
|
||||
];
|
||||
|
||||
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`,
|
||||
},
|
||||
];
|
||||
|
||||
export const MoreProducts = [
|
||||
{
|
||||
description: 'Minifier ExtraNetwrok Covers',
|
||||
openExternal: true,
|
||||
title: '✂️ Cover Minifier',
|
||||
url: 'https://github.com/canisminor1990/sd-webui-cover-minifier',
|
||||
},
|
||||
{
|
||||
description: 'AIGC Components',
|
||||
openExternal: true,
|
||||
title: '🤯 Lobe UI',
|
||||
url: 'https://github.com/lobehub/lobe-ui',
|
||||
},
|
||||
{
|
||||
description: 'AI Commit CLI',
|
||||
openExternal: true,
|
||||
title: '💌 Lobe Commit',
|
||||
url: 'https://github.com/lobehub/lobe-commit',
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,16 +1,18 @@
|
|||
import { Footer as F } from '@lobehub/ui';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { memo, useEffect, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { useAppStore } from '@/store';
|
||||
import { DivProps } from '@/types/index';
|
||||
import { type DivProps } from '@/types';
|
||||
|
||||
import { columns } from './data';
|
||||
import { Community, Help, MoreProducts, Resources } from './data';
|
||||
import { useStyles } from './style';
|
||||
|
||||
const Footer = memo<DivProps>(({ className, ...props }) => {
|
||||
const setting = useAppStore((st) => st.setting, isEqual);
|
||||
const { cx, styles } = useStyles();
|
||||
const { t } = useTranslation();
|
||||
const footerReference = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -24,7 +26,27 @@ const Footer = memo<DivProps>(({ className, ...props }) => {
|
|||
{setting.layoutHideFooter ? (
|
||||
<div ref={footerReference} />
|
||||
) : (
|
||||
<F bottom={<div ref={footerReference} />} columns={columns} />
|
||||
<F
|
||||
bottom={<div ref={footerReference} />}
|
||||
columns={[
|
||||
{
|
||||
items: Resources,
|
||||
title: t('resources'),
|
||||
},
|
||||
{
|
||||
items: Community,
|
||||
title: t('community'),
|
||||
},
|
||||
{
|
||||
items: Help,
|
||||
title: t('help'),
|
||||
},
|
||||
{
|
||||
items: MoreProducts,
|
||||
title: t('moreProducts'),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { useResponsive } from 'antd-style';
|
|||
import { Github, LucideIcon, Moon, Settings, Sun } from 'lucide-react';
|
||||
import qs from 'query-string';
|
||||
import { memo, useCallback, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import Giscus from '@/slots/Giscus';
|
||||
|
|
@ -25,6 +26,7 @@ const Actions = memo<ActionsProps>(() => {
|
|||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const themeMode = useAppStore((st) => st.themeMode, shallow);
|
||||
const { mobile } = useResponsive();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleSetTheme = useCallback(() => {
|
||||
const theme = themeMode === 'light' ? 'dark' : 'light';
|
||||
|
|
@ -37,7 +39,7 @@ const Actions = memo<ActionsProps>(() => {
|
|||
<ActionIcon
|
||||
icon={themeMode === 'light' ? Sun : Moon}
|
||||
onClick={handleSetTheme}
|
||||
title="Switch Theme"
|
||||
title={t('switchTheme')}
|
||||
/>
|
||||
);
|
||||
|
||||
|
|
@ -49,9 +51,9 @@ const Actions = memo<ActionsProps>(() => {
|
|||
<a href="https://civitai.com/" rel="noreferrer" target="_blank">
|
||||
<ActionIcon icon={CivitaiLogo} title="Civitai" />
|
||||
</a>
|
||||
<ActionIcon icon={Github} onClick={() => setIsModalOpen(true)} title="Feedback" />
|
||||
<ActionIcon icon={Github} onClick={() => setIsModalOpen(true)} title={t('feedback')} />
|
||||
{themeSwitch}
|
||||
<ActionIcon icon={Settings} onClick={() => setIsSettingOpen(true)} title="Setting" />
|
||||
<ActionIcon icon={Settings} onClick={() => setIsSettingOpen(true)} title={t('setting')} />
|
||||
</Space.Compact>
|
||||
<Setting onCancel={() => setIsSettingOpen(false)} open={isSettingOpen} />
|
||||
<Giscus onCancel={() => setIsModalOpen(false)} open={isModalOpen} themeMode={themeMode} />
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { shallow } from 'zustand/shallow';
|
|||
import { homepage, name, version } from '@/../package.json';
|
||||
import { Logo } from '@/components';
|
||||
import { useAppStore } from '@/store';
|
||||
import { DivProps } from '@/types/index';
|
||||
import { type DivProps } from '@/types';
|
||||
|
||||
import Actions from './Actions';
|
||||
import Nav from './Nav';
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { memo, useEffect, useRef } from 'react';
|
|||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import { useAppStore } from '@/store';
|
||||
import { DivProps } from '@/types/index';
|
||||
import { type DivProps } from '@/types';
|
||||
|
||||
import { useStyles } from './style';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { DivProps } from '@lobehub/ui';
|
||||
import { type DivProps } from '@lobehub/ui';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { memo, useEffect, useRef } from 'react';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import { DivProps, DraggablePanel, LayoutSidebarInner } from '@lobehub/ui';
|
||||
import { type DivProps, DraggablePanel, LayoutSidebarInner } from '@lobehub/ui';
|
||||
import { useResponsive } from 'antd-style';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { memo, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { SidebarContainer, SidebarHeader } from '@/components';
|
||||
import { useAppStore } from '@/store';
|
||||
|
|
@ -19,6 +20,7 @@ const QuickSettingSidebar = memo<QuickSettingSidebarProps>(({ headerHeight }) =>
|
|||
const [expand, setExpand] = useState<boolean>(mobile ? false : setting.sidebarExpand);
|
||||
const [pin, setPin] = useState<boolean>(setting.sidebarFixedMode === 'fixed');
|
||||
const { styles, theme } = useStyles({ headerHeight });
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
if (mobile) setExpand(false);
|
||||
|
|
@ -50,7 +52,7 @@ const QuickSettingSidebar = memo<QuickSettingSidebarProps>(({ headerHeight }) =>
|
|||
position="left"
|
||||
setExpand={setExpand}
|
||||
setPin={setPin}
|
||||
title="Quick Settings"
|
||||
title={t('quickSetting')}
|
||||
/>
|
||||
<Inner />
|
||||
</SidebarContainer>
|
||||
|
|
|
|||
|
|
@ -37,14 +37,16 @@ const Index = memo(() => {
|
|||
</LayoutHeader>
|
||||
<LayoutMain>
|
||||
{!setting.liteAnimation && <div className={styles.background} />}
|
||||
<LayoutSidebar
|
||||
className={styles.sidebar}
|
||||
headerHeight={HEADER_HEIGHT}
|
||||
style={{ flex: 0, zIndex: 50 }}
|
||||
>
|
||||
<QuickSettingSidebar headerHeight={HEADER_HEIGHT} />
|
||||
</LayoutSidebar>
|
||||
<Content />
|
||||
{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.layoutSplitPreview && mobile === false && (
|
||||
<LayoutSidebar
|
||||
className={cx(styles.sidebar, styles.panel)}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,11 @@ export const useStyles = createStyles(
|
|||
border-style: dashed;
|
||||
}
|
||||
`,
|
||||
quicksettings: css`
|
||||
#quicksettings {
|
||||
padding: 16px !important;
|
||||
}
|
||||
`,
|
||||
sidebar: css`
|
||||
height: calc(100vh - ${headerHeight}px);
|
||||
`,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import GiscusComponent from '@giscus/react';
|
|||
import { ActionIcon } from '@lobehub/ui';
|
||||
import { Github } from 'lucide-react';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { homepage, name } from '@/../package.json';
|
||||
import { Modal, type ModalProps } from '@/components';
|
||||
|
|
@ -12,6 +13,7 @@ interface GiscusProps {
|
|||
themeMode: 'light' | 'dark';
|
||||
}
|
||||
const Giscus = memo<GiscusProps>(({ themeMode, open, onCancel }) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Modal
|
||||
onCancel={onCancel}
|
||||
|
|
@ -21,7 +23,7 @@ const Giscus = memo<GiscusProps>(({ themeMode, open, onCancel }) => {
|
|||
<a href={homepage} rel="noreferrer" target="_blank">
|
||||
<ActionIcon icon={Github} title={`canisminor1990/${name}`} />
|
||||
</a>
|
||||
Theme Feedback
|
||||
{t('themeFeedback')}
|
||||
</>
|
||||
}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { memo, useCallback, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import TagList, { PromptType, TagItem } from './TagList';
|
||||
import { useStyles } from './style';
|
||||
|
|
@ -11,6 +12,7 @@ interface PromptProps {
|
|||
const Prompt = memo<PromptProps>(({ type }) => {
|
||||
const [tags, setTags] = useState<TagItem[]>([]);
|
||||
const { styles } = useStyles();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const id =
|
||||
type === 'positive' ? "[id$='2img_prompt'] textarea" : "[id$='2img_neg_prompt'] textarea";
|
||||
|
|
@ -51,7 +53,7 @@ const Prompt = memo<PromptProps>(({ type }) => {
|
|||
<button
|
||||
className="lg secondary gradio-button tool svelte-1ipelgc"
|
||||
onClick={getValue}
|
||||
title="Load Prompt"
|
||||
title={t('loadPrompt')}
|
||||
type="button"
|
||||
>
|
||||
🔄
|
||||
|
|
@ -59,7 +61,7 @@ const Prompt = memo<PromptProps>(({ type }) => {
|
|||
<button
|
||||
className="lg secondary gradio-button tool svelte-1ipelgc"
|
||||
onClick={setValue}
|
||||
title="Set Prompt"
|
||||
title={t('setPrompt')}
|
||||
type="button"
|
||||
>
|
||||
➡️
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { useStyles } from '@/slots/PromptEditor/style';
|
||||
|
||||
|
|
@ -6,11 +7,12 @@ import Prompt from './Prompt';
|
|||
|
||||
const PromptEditor = memo(() => {
|
||||
const { styles } = useStyles();
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className={styles.view}>
|
||||
<span style={{ marginBottom: -10 }}>Positive</span>
|
||||
<span style={{ marginBottom: -10 }}>{t('positive')}</span>
|
||||
<Prompt type="positive" />
|
||||
<span style={{ marginBottom: -10 }}>Negative</span>
|
||||
<span style={{ marginBottom: -10 }}>{t('negative')}</span>
|
||||
<Prompt type="negative" />
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
import { Divider } from 'antd';
|
||||
|
||||
export default () => <Divider style={{ margin: 0 }} />;
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
import { Icon, type IconProps } from '@lobehub/ui';
|
||||
import { Collapse, type CollapseProps } from 'antd';
|
||||
import { type ReactNode, memo } from 'react';
|
||||
|
||||
import { useStyles } from './style';
|
||||
|
||||
export interface FormGroupProps extends CollapseProps {
|
||||
children: ReactNode;
|
||||
icon: IconProps['icon'];
|
||||
title: string;
|
||||
}
|
||||
|
||||
const FormGroup = memo<FormGroupProps>(({ icon, title, children, ...props }) => {
|
||||
const { styles } = useStyles();
|
||||
return (
|
||||
<Collapse
|
||||
className={styles.group}
|
||||
defaultActiveKey={[1]}
|
||||
items={[
|
||||
{
|
||||
children,
|
||||
key: 1,
|
||||
label: (
|
||||
<div className={styles.title}>
|
||||
<Icon icon={icon} />
|
||||
{title}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
]}
|
||||
key={1}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export default FormGroup;
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
import { FormItemProps as AntdFormItemProps, Form } from 'antd';
|
||||
import { memo } from 'react';
|
||||
|
||||
import FormDivider from '@/slots/Setting/FormDivider';
|
||||
import FormTitle from '@/slots/Setting/FormTitle';
|
||||
|
||||
import { useStyles } from './style';
|
||||
|
||||
const { Item } = Form;
|
||||
|
||||
export interface FormItemProps extends AntdFormItemProps {
|
||||
desc?: string;
|
||||
divider?: boolean;
|
||||
}
|
||||
|
||||
const FormItem = memo<FormItemProps>(({ desc, label, children, divider, ...props }) => {
|
||||
const { styles } = useStyles();
|
||||
return (
|
||||
<>
|
||||
{divider && <FormDivider />}
|
||||
<Item
|
||||
className={styles.item}
|
||||
label={desc ? <FormTitle desc={desc} title={String(label)} /> : label}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</Item>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
export default FormItem;
|
||||
|
|
@ -1,32 +1,23 @@
|
|||
import { Icon, Swatches } from '@lobehub/ui';
|
||||
import { Button, Divider, Form, Input, InputNumber, Segmented, Switch } from 'antd';
|
||||
import { Swatches } from '@lobehub/ui';
|
||||
import { Button, Form, Input, InputNumber, Segmented, Select, Switch } from 'antd';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { Layout, Palette, PanelLeftClose, PanelRightClose, TextCursorInput } from 'lucide-react';
|
||||
import { memo, useCallback, useMemo, useState } from 'react';
|
||||
import { memo, useCallback, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import { CustomLogo } from '@/components';
|
||||
import { i18nOptions } from '@/i18n';
|
||||
import { NeutralColor, PrimaryColor, WebuiSetting, defaultSetting, useAppStore } from '@/store';
|
||||
import { kitchenNeutral, kitchenPrimary } from '@/styles/kitchenColors';
|
||||
import { neutralColorScales } from '@/styles/neutralColors';
|
||||
|
||||
import FormTitle from './FormTitle';
|
||||
import FormGroup from './FormGroup';
|
||||
import FormItem from './FormItem';
|
||||
import { colors, findKey, neutralColors, primaryColors } from './data';
|
||||
import { useStyles } from './style';
|
||||
|
||||
const { Item } = Form;
|
||||
|
||||
const findKey = (object: { [key in string]: string }, value: string): any => {
|
||||
const res: { [key: string]: PrimaryColor } = {};
|
||||
for (const key of Object.keys(object)) {
|
||||
// @ts-ignore
|
||||
res[object[key]] = key;
|
||||
}
|
||||
return res[value];
|
||||
};
|
||||
|
||||
const SettingForm = memo(() => {
|
||||
const setting = useAppStore((st) => st.setting, isEqual);
|
||||
const { onSetSetting, themeMode } = useAppStore(
|
||||
const { onSetSetting } = useAppStore(
|
||||
(st) => ({ onSetSetting: st.onSetSetting, themeMode: st.themeMode }),
|
||||
shallow,
|
||||
);
|
||||
|
|
@ -37,39 +28,8 @@ const SettingForm = memo(() => {
|
|||
const [neutralColor, setNeutralColor] = useState<NeutralColor | undefined>(
|
||||
setting.neutralColor || undefined,
|
||||
);
|
||||
|
||||
const { styles, theme } = useStyles();
|
||||
|
||||
const colors = useMemo(
|
||||
() => ({
|
||||
blue: theme.blue,
|
||||
cyan: theme.cyan,
|
||||
geekblue: theme.geekblue,
|
||||
gold: theme.gold,
|
||||
green: theme.green,
|
||||
kitchen: kitchenPrimary.light.colorPrimary,
|
||||
lime: theme.lime,
|
||||
magenta: theme.magenta,
|
||||
orange: theme.orange,
|
||||
purple: theme.purple,
|
||||
red: theme.red,
|
||||
volcano: theme.volcano,
|
||||
yellow: theme.yellow,
|
||||
}),
|
||||
[theme],
|
||||
);
|
||||
|
||||
const neutralColors = useMemo(
|
||||
() => ({
|
||||
kitchen: kitchenNeutral[themeMode].colorNeutral,
|
||||
mauve: neutralColorScales.mauve[themeMode][9],
|
||||
olive: neutralColorScales.olive[themeMode][9],
|
||||
sage: neutralColorScales.sage[themeMode][9],
|
||||
sand: neutralColorScales.sand[themeMode][9],
|
||||
slate: neutralColorScales.slate[themeMode][9],
|
||||
}),
|
||||
[themeMode],
|
||||
);
|
||||
const { styles } = useStyles();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const onReset = useCallback(() => {
|
||||
onSetSetting(defaultSetting);
|
||||
|
|
@ -93,186 +53,252 @@ const SettingForm = memo(() => {
|
|||
onFinish={onFinish}
|
||||
onValuesChange={(_, v) => setRawSetting(v)}
|
||||
>
|
||||
<div className={styles.group}>
|
||||
<div className={styles.title}>
|
||||
<Icon icon={TextCursorInput} />
|
||||
Promot Textarea
|
||||
</div>
|
||||
<Item
|
||||
className={styles.item}
|
||||
label={<FormTitle desc="Fixed height / Auto height" title="Display mode" />}
|
||||
name="promotTextarea"
|
||||
>
|
||||
<Segmented options={['scroll', 'resizable']} />
|
||||
</Item>
|
||||
<Divider style={{ margin: 0 }} />
|
||||
<Item
|
||||
className={styles.item}
|
||||
label={<FormTitle desc="Top in left sidebar" title="Prompt editor" />}
|
||||
name="promptEditor"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Item>
|
||||
</div>
|
||||
<div className={styles.group}>
|
||||
<div className={styles.title}>
|
||||
<Icon icon={Layout} />
|
||||
Layout
|
||||
</div>
|
||||
<Item
|
||||
className={styles.item}
|
||||
label="Split Previewer"
|
||||
name="layoutSplitPreview"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Item>
|
||||
<Divider style={{ margin: 0 }} />
|
||||
<Item
|
||||
className={styles.item}
|
||||
label="Hide Footer"
|
||||
name="layoutHideFooter"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Item>
|
||||
</div>
|
||||
<div className={styles.group}>
|
||||
<div className={styles.title}>
|
||||
<Icon icon={PanelLeftClose} />
|
||||
QuickSetting Sidebar
|
||||
</div>
|
||||
<Item
|
||||
className={styles.item}
|
||||
label="Default expand"
|
||||
name="sidebarExpand"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Item>
|
||||
<Divider style={{ margin: 0 }} />
|
||||
<Item className={styles.item} label="Display mode" name="sidebarFixedMode">
|
||||
<Segmented options={['fixed', 'float']} />
|
||||
</Item>
|
||||
<Divider style={{ margin: 0 }} />
|
||||
<Item className={styles.item} label="Default width" name="sidebarWidth">
|
||||
<InputNumber />
|
||||
</Item>
|
||||
</div>
|
||||
<div className={styles.group}>
|
||||
<div className={styles.title}>
|
||||
<Icon icon={PanelRightClose} />
|
||||
ExtraNetwork Sidebar
|
||||
</div>
|
||||
<Item
|
||||
className={styles.item}
|
||||
label="Enable"
|
||||
name="enableExtraNetworkSidebar"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Item>
|
||||
<Divider style={{ margin: 0 }} />
|
||||
<Item className={styles.item} label="Display mode" name="extraNetworkFixedMode">
|
||||
<Segmented options={['fixed', 'float']} />
|
||||
</Item>
|
||||
<Divider style={{ margin: 0 }} />
|
||||
<Item
|
||||
className={styles.item}
|
||||
label="Default expand"
|
||||
name="extraNetworkSidebarExpand"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Item>
|
||||
<Divider style={{ margin: 0 }} />
|
||||
<Item className={styles.item} label="Default width" name="extraNetworkSidebarWidth">
|
||||
<InputNumber />
|
||||
</Item>
|
||||
<Divider style={{ margin: 0 }} />
|
||||
<Item className={styles.item} label="Default card size" name="extraNetworkCardSize">
|
||||
<InputNumber />
|
||||
</Item>
|
||||
</div>
|
||||
<div className={styles.group}>
|
||||
<div className={styles.title}>
|
||||
<Icon icon={Palette} />
|
||||
Theme
|
||||
</div>
|
||||
<Item
|
||||
className={styles.item}
|
||||
label={<FormTitle desc="Save cpu usage" title="Remove animation" />}
|
||||
<FormGroup icon={Palette} title={t('settingGroupTheme')}>
|
||||
<FormItem desc={t('settingLanguageDesc')} label={t('settingLanguage')} name="i18n">
|
||||
<Select options={i18nOptions} />
|
||||
</FormItem>
|
||||
<FormItem
|
||||
desc={t('settingReduceAnimationDesc')}
|
||||
divider
|
||||
label={t('settingReduceAnimation')}
|
||||
name="liteAnimation"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Item>
|
||||
<Divider style={{ margin: 0 }} />
|
||||
<Item className={styles.item} label="Primary color">
|
||||
</FormItem>
|
||||
<FormItem desc={t('settingPrimaryColorDesc')} divider label={t('settingPrimaryColor')}>
|
||||
<Swatches
|
||||
activeColor={primaryColor ? colors[primaryColor] : undefined}
|
||||
colors={[
|
||||
kitchenPrimary.light.colorPrimary,
|
||||
theme.red,
|
||||
theme.orange,
|
||||
theme.gold,
|
||||
theme.yellow,
|
||||
theme.lime,
|
||||
theme.green,
|
||||
theme.cyan,
|
||||
theme.blue,
|
||||
theme.geekblue,
|
||||
theme.purple,
|
||||
theme.magenta,
|
||||
theme.volcano,
|
||||
]}
|
||||
colors={primaryColors}
|
||||
onSelect={(c) => setPrimaryColor(c ? findKey(colors, c) : undefined)}
|
||||
/>
|
||||
</Item>
|
||||
<Divider style={{ margin: 0 }} />
|
||||
<Item className={styles.item} label="Neutral Color">
|
||||
</FormItem>
|
||||
<FormItem desc={t('settingNeutralColorDesc')} divider label={t('settingNeutralColor')}>
|
||||
<Swatches
|
||||
activeColor={neutralColor ? neutralColors[neutralColor] : undefined}
|
||||
colors={Object.values(neutralColors)}
|
||||
onSelect={(c) => setNeutralColor(c ? findKey(neutralColors, c) : undefined)}
|
||||
/>
|
||||
</Item>
|
||||
<Divider style={{ margin: 0 }} />
|
||||
<Item className={styles.item} label="Logo type" name="logoType">
|
||||
<Segmented options={['lobe', 'kitchen', 'custom']} />
|
||||
</Item>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
desc={t('settingLogoTypeDesc')}
|
||||
divider
|
||||
label={t('settingLogoType')}
|
||||
name="logoType"
|
||||
>
|
||||
<Segmented
|
||||
options={[
|
||||
{
|
||||
label: t('lobe'),
|
||||
value: 'lobe',
|
||||
},
|
||||
{
|
||||
label: t('kitchen'),
|
||||
value: 'kitchen',
|
||||
},
|
||||
{
|
||||
label: t('custom'),
|
||||
value: 'custom',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</FormItem>
|
||||
{rawSetting.logoType === 'custom' && (
|
||||
<>
|
||||
<Item
|
||||
className={styles.item}
|
||||
label="Logo ( Url / Base64 / Emoji )"
|
||||
<FormItem
|
||||
desc={t('settingCustomLogoDesc')}
|
||||
label={t('settingCustomLogo')}
|
||||
name="logoCustomUrl"
|
||||
>
|
||||
<Input />
|
||||
</Item>
|
||||
<Item className={styles.item} label="Title" name="logoCustomTitle">
|
||||
</FormItem>
|
||||
<FormItem
|
||||
desc={t('settingCustomTitleDesc')}
|
||||
label={t('settingCustomTitle')}
|
||||
name="logoCustomTitle"
|
||||
>
|
||||
<Input />
|
||||
</Item>
|
||||
<Item className={styles.item} label="Preview">
|
||||
</FormItem>
|
||||
<FormItem label={t('settingLogoPreview')}>
|
||||
<CustomLogo
|
||||
logoCustomTitle={rawSetting.logoCustomTitle}
|
||||
logoCustomUrl={rawSetting.logoCustomUrl}
|
||||
/>
|
||||
</Item>
|
||||
</FormItem>
|
||||
</>
|
||||
)}
|
||||
<Divider style={{ margin: 0 }} />
|
||||
<Item className={styles.item} label="Use svg icons" name="svgIcon" valuePropName="checked">
|
||||
<FormItem
|
||||
desc={t('settingSvgIconsDesc')}
|
||||
divider
|
||||
label={t('settingSvgIcons')}
|
||||
name="svgIcon"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Item>
|
||||
</div>
|
||||
</FormItem>
|
||||
</FormGroup>
|
||||
<FormGroup icon={TextCursorInput} title={t('settingGroupPromotTextarea')}>
|
||||
<FormItem
|
||||
desc={t('settingPromptDisplayModeDesc')}
|
||||
label={t('settingPromptDisplayMode')}
|
||||
name="promotTextarea"
|
||||
>
|
||||
<Segmented
|
||||
options={[
|
||||
{
|
||||
label: t('scroll'),
|
||||
value: 'scroll',
|
||||
},
|
||||
{
|
||||
label: t('resizable'),
|
||||
value: 'resizable',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
desc={t('settingPromptEditorDesc')}
|
||||
divider
|
||||
label={t('settingPromptEditor')}
|
||||
name="promptEditor"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</FormItem>
|
||||
</FormGroup>
|
||||
<FormGroup icon={Layout} title={t('settingGroupLayout')}>
|
||||
<FormItem
|
||||
desc={t('settingSplitPreviewerDesc')}
|
||||
label={t('settingSplitPreviewer')}
|
||||
name="layoutSplitPreview"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</FormItem>
|
||||
<FormItem
|
||||
desc={t('settingHideFooterDesc')}
|
||||
divider
|
||||
label={t('settingHideFooter')}
|
||||
name="layoutHideFooter"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</FormItem>
|
||||
</FormGroup>
|
||||
<FormGroup icon={PanelLeftClose} title={t('settingGroupQuickSettingSidebar')}>
|
||||
<FormItem
|
||||
desc={t('settingQuickSettingSidebarEnableDesc')}
|
||||
label={t('settingQuickSettingSidebarEnable')}
|
||||
name="enableSidebar"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</FormItem>
|
||||
{rawSetting.enableSidebar && (
|
||||
<>
|
||||
<FormItem
|
||||
desc={t('settingQuickSettingSidebarDefaultExpandDesc')}
|
||||
divider
|
||||
label={t('settingQuickSettingSidebarDefaultExpand')}
|
||||
name="sidebarExpand"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</FormItem>
|
||||
<FormItem
|
||||
desc={t('settingQuickSettingSidebarDisplayModeDesc')}
|
||||
divider
|
||||
label={t('settingQuickSettingSidebarDisplayMode')}
|
||||
name="sidebarFixedMode"
|
||||
>
|
||||
<Segmented
|
||||
options={[
|
||||
{
|
||||
label: t('fixed'),
|
||||
value: 'fixed',
|
||||
},
|
||||
{
|
||||
label: t('float'),
|
||||
value: 'float',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
desc={t('settingQuickSettingSidebarDefaultWidthDesc')}
|
||||
divider
|
||||
label={t('settingQuickSettingSidebarDefaultWidth')}
|
||||
name="sidebarWidth"
|
||||
>
|
||||
<InputNumber />
|
||||
</FormItem>
|
||||
</>
|
||||
)}
|
||||
</FormGroup>
|
||||
<FormGroup icon={PanelRightClose} title={t('settingGroupExtraNetworkSidebar')}>
|
||||
<FormItem
|
||||
desc={t('settingExtraNetworkSidebarEnableDesc')}
|
||||
label={t('settingExtraNetworkSidebarEnable')}
|
||||
name="enableExtraNetworkSidebar"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</FormItem>
|
||||
{rawSetting.enableExtraNetworkSidebar && (
|
||||
<>
|
||||
<FormItem
|
||||
desc={t('settingExtraNetworkSidebarDisplayModeDesc')}
|
||||
divider
|
||||
label={t('settingExtraNetworkSidebarDisplayMode')}
|
||||
name="extraNetworkFixedMode"
|
||||
>
|
||||
<Segmented
|
||||
options={[
|
||||
{
|
||||
label: t('fixed'),
|
||||
value: 'fixed',
|
||||
},
|
||||
{
|
||||
label: t('float'),
|
||||
value: 'float',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
desc={t('settingExtraNetworkSidebarDefaultExpandDesc')}
|
||||
divider
|
||||
label={t('settingExtraNetworkSidebarDefaultExpand')}
|
||||
name="extraNetworkSidebarExpand"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</FormItem>
|
||||
<FormItem
|
||||
desc={t('settingExtraNetworkSidebarDefaultWidthDesc')}
|
||||
divider
|
||||
label={t('settingExtraNetworkSidebarDefaultWidth')}
|
||||
name="extraNetworkSidebarWidth"
|
||||
>
|
||||
<InputNumber />
|
||||
</FormItem>
|
||||
<FormItem
|
||||
desc={t('settingExtraNetworkSidebarDefaultCardSizeDesc')}
|
||||
divider
|
||||
label={t('settingExtraNetworkSidebarDefaultCardSize')}
|
||||
name="extraNetworkCardSize"
|
||||
>
|
||||
<InputNumber />
|
||||
</FormItem>
|
||||
</>
|
||||
)}
|
||||
</FormGroup>
|
||||
<div className={styles.footer}>
|
||||
<Button htmlType="button" onClick={onReset} style={{ borderRadius: 4 }}>
|
||||
Reset
|
||||
{t('settingButtomReset')}
|
||||
</Button>
|
||||
<Button htmlType="submit" style={{ borderRadius: 4 }} type="primary">
|
||||
Apply and restart UI
|
||||
{t('settingButtomSubmit')}
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
import { colors as lobeColor } from '@lobehub/ui';
|
||||
|
||||
import { PrimaryColor } from '@/store';
|
||||
import { kitchenNeutral, kitchenPrimary } from '@/styles/kitchenColors';
|
||||
import { neutralColorScales } from '@/styles/neutralColors';
|
||||
|
||||
export const colors = {
|
||||
blue: lobeColor.blue.dark[9],
|
||||
cyan: lobeColor.cyan.dark[9],
|
||||
geekblue: lobeColor.geekblue.dark[9],
|
||||
gold: lobeColor.gold.dark[9],
|
||||
green: lobeColor.green.dark[9],
|
||||
kitchen: kitchenPrimary.dark.colorPrimary,
|
||||
lime: lobeColor.lime.dark[9],
|
||||
magenta: lobeColor.magenta.dark[9],
|
||||
orange: lobeColor.orange.dark[9],
|
||||
purple: lobeColor.purple.dark[9],
|
||||
red: lobeColor.red.dark[9],
|
||||
volcano: lobeColor.volcano.dark[9],
|
||||
yellow: lobeColor.yellow.dark[9],
|
||||
};
|
||||
|
||||
export const primaryColors = [
|
||||
kitchenPrimary.dark.colorPrimary,
|
||||
lobeColor.red.dark[9],
|
||||
lobeColor.orange.dark[9],
|
||||
lobeColor.gold.dark[9],
|
||||
lobeColor.yellow.dark[9],
|
||||
lobeColor.lime.dark[9],
|
||||
lobeColor.green.dark[9],
|
||||
lobeColor.cyan.dark[9],
|
||||
lobeColor.blue.dark[9],
|
||||
lobeColor.geekblue.dark[9],
|
||||
lobeColor.purple.dark[9],
|
||||
lobeColor.magenta.dark[9],
|
||||
lobeColor.volcano.dark[9],
|
||||
];
|
||||
|
||||
export const neutralColors = {
|
||||
kitchen: kitchenNeutral.dark.colorNeutral,
|
||||
mauve: neutralColorScales.mauve.dark[9],
|
||||
olive: neutralColorScales.olive.dark[9],
|
||||
sage: neutralColorScales.sage.dark[9],
|
||||
sand: neutralColorScales.sand.dark[9],
|
||||
slate: neutralColorScales.slate.dark[9],
|
||||
};
|
||||
|
||||
export const findKey = (object: { [key in string]: string }, value: string): any => {
|
||||
const res: { [key: string]: PrimaryColor } = {};
|
||||
for (const key of Object.keys(object)) {
|
||||
// @ts-ignore
|
||||
res[object[key]] = key;
|
||||
}
|
||||
return res[value];
|
||||
};
|
||||
|
|
@ -2,6 +2,7 @@ import { ActionIcon } from '@lobehub/ui';
|
|||
import { Space, Tag } from 'antd';
|
||||
import { Book } from 'lucide-react';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { homepage, version } from '@/../package.json';
|
||||
import { Modal, type ModalProps } from '@/components';
|
||||
|
|
@ -14,9 +15,9 @@ interface SettingProps {
|
|||
}
|
||||
|
||||
const Setting = memo<SettingProps>(({ open, onCancel }) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Modal
|
||||
okText={'Save and Refresh'}
|
||||
onCancel={onCancel}
|
||||
open={open}
|
||||
title={
|
||||
|
|
@ -25,7 +26,7 @@ const Setting = memo<SettingProps>(({ open, onCancel }) => {
|
|||
<ActionIcon icon={Book} title="Setting Documents" />
|
||||
</a>
|
||||
<Space>
|
||||
Theme Setting
|
||||
{t('themeSetting')}
|
||||
<Tag color="success">v{version}</Tag>
|
||||
</Space>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -29,12 +29,23 @@ export const useStyles = createStyles(({ css, token }) => ({
|
|||
`,
|
||||
group: css`
|
||||
overflow: hidden;
|
||||
background: ${token.colorFillQuaternary};
|
||||
border: 1px solid ${token.colorBorder};
|
||||
border-radius: ${token.borderRadius}px;
|
||||
|
||||
.ant-collapse-header {
|
||||
background: ${token.colorFillTertiary};
|
||||
border-radius: unset;
|
||||
}
|
||||
|
||||
.ant-collapse-content {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.ant-collapse-content-box {
|
||||
padding-top: 0 !important;
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
`,
|
||||
item: css`
|
||||
padding: 8px 16px;
|
||||
padding: 8px 0;
|
||||
|
||||
.ant-row {
|
||||
justify-content: space-between;
|
||||
|
|
@ -50,15 +61,8 @@ export const useStyles = createStyles(({ css, token }) => ({
|
|||
gap: 8px;
|
||||
align-items: center;
|
||||
|
||||
margin: 0;
|
||||
margin-bottom: 4px;
|
||||
padding: 16px;
|
||||
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
|
||||
background: ${token.colorFillTertiary};
|
||||
|
||||
.anticon {
|
||||
color: ${token.colorPrimary};
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ import { devtools } from 'zustand/middleware';
|
|||
|
||||
const SETTING_KEY = 'SD-KITCHEN-SETTING';
|
||||
|
||||
export type I18n = 'en' | 'zh-CN';
|
||||
|
||||
export type PrimaryColor =
|
||||
| 'blue'
|
||||
| 'cyan'
|
||||
|
|
@ -22,10 +24,12 @@ export type NeutralColor = 'mauve' | 'slate' | 'sage' | 'olive' | 'sand' | 'kitc
|
|||
|
||||
export interface WebuiSetting {
|
||||
enableExtraNetworkSidebar: boolean;
|
||||
enableSidebar: boolean;
|
||||
extraNetworkCardSize: number;
|
||||
extraNetworkFixedMode: 'fixed' | 'float';
|
||||
extraNetworkSidebarExpand: boolean;
|
||||
extraNetworkSidebarWidth: number;
|
||||
i18n: I18n;
|
||||
layoutHideFooter: boolean;
|
||||
layoutSplitPreview: boolean;
|
||||
liteAnimation: boolean;
|
||||
|
|
@ -44,10 +48,12 @@ export interface WebuiSetting {
|
|||
|
||||
export const defaultSetting: WebuiSetting = {
|
||||
enableExtraNetworkSidebar: true,
|
||||
enableSidebar: true,
|
||||
extraNetworkCardSize: 86,
|
||||
extraNetworkFixedMode: 'fixed',
|
||||
extraNetworkSidebarExpand: true,
|
||||
extraNetworkSidebarWidth: 340,
|
||||
i18n: 'en',
|
||||
layoutHideFooter: false,
|
||||
layoutSplitPreview: false,
|
||||
liteAnimation: false,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
import type { resources } from './index';
|
||||
|
||||
declare module 'i18next' {
|
||||
interface CustomTypeOptions {
|
||||
resources: typeof resources;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,17 @@
|
|||
import { type HTMLAttributes } from 'react';
|
||||
|
||||
import translation from '@/i18n/lang/en';
|
||||
|
||||
export const resources = {
|
||||
translation,
|
||||
} as const;
|
||||
|
||||
type TranslationKeys = keyof typeof translation;
|
||||
|
||||
export type Translation = {
|
||||
[key in TranslationKeys]: string;
|
||||
};
|
||||
|
||||
export type DivProps = HTMLAttributes<HTMLDivElement>;
|
||||
|
||||
export type SvgProps = HTMLAttributes<SVGSVGElement>;
|
||||
|
|
|
|||
Loading…
Reference in New Issue