🔧 chore: Update

pull/290/head
canisminor1990 2023-07-20 01:03:21 +08:00
parent e65b93fb1e
commit 68ab3d2bd6
34 changed files with 677 additions and 973 deletions

File diff suppressed because one or more lines are too long

View File

@ -18,8 +18,8 @@
"scroll": "Scroll",
"setPrompt": "Set Prompt",
"setting": "Setting",
"settingButtomReset": "Reset",
"settingButtomSubmit": "Apply and Restart Interface",
"settingButtonReset": "Reset",
"settingButtonSubmit": "Apply and Restart Interface",
"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",
@ -38,7 +38,7 @@
"settingExtraNetworkSidebarEnableDesc": "Enable the extra network sidebar on the right side",
"settingGroupExtraNetworkSidebar": "Extra Network Sidebar",
"settingGroupLayout": "Layout Settings",
"settingGroupPromotTextarea": "Prompt Textbox",
"settingGroupPromptTextarea": "Prompt Textbox",
"settingGroupQuickSettingSidebar": "Quick Setting Sidebar",
"settingGroupTheme": "Theme Settings",
"settingHideFooter": "Hide Footer",

View File

@ -18,8 +18,8 @@
"scroll": "スクロール",
"setPrompt": "プロンプトを設定",
"setting": "設定",
"settingButtomReset": "リセット",
"settingButtomSubmit": "適用して再起動",
"settingButtonReset": "リセット",
"settingButtonSubmit": "適用して再起動",
"settingCustomFont": "カスタムフォントの読み込み",
"settingCustomFontDesc": "有効にすると、Webフォントを自動的に読み込んで、英語、中国語、およびコードの表示効果を最適化します",
"settingCustomLogo": "カスタムロゴ",
@ -38,7 +38,7 @@
"settingExtraNetworkSidebarEnableDesc": "右側の追加ネットワークサイドバーを有効にする",
"settingGroupExtraNetworkSidebar": "追加ネットワークサイドバー",
"settingGroupLayout": "レイアウト設定",
"settingGroupPromotTextarea": "プロンプトテキストエリア",
"settingGroupPromptTextarea": "プロンプトテキストエリア",
"settingGroupQuickSettingSidebar": "クイック設定サイドバー",
"settingGroupTheme": "テーマ設定",
"settingHideFooter": "フッターを非表示にする",

View File

@ -18,8 +18,8 @@
"scroll": "스크롤",
"setPrompt": "프롬프트 설정",
"setting": "설정",
"settingButtomReset": "재설정",
"settingButtomSubmit": "적용 및 인터페이스 재시작",
"settingButtonReset": "재설정",
"settingButtonSubmit": "적용 및 인터페이스 재시작",
"settingCustomFont": "폰트 로드",
"settingCustomFontDesc": "이 기능을 사용하면 웹 폰트를 자동으로 로드하여 영문, 한글 및 코드 표시 효과를 최적화합니다",
"settingCustomLogo": "사용자 정의 로고",
@ -38,7 +38,7 @@
"settingExtraNetworkSidebarEnableDesc": "오른쪽에 추가 네트워크 사이드바 활성화",
"settingGroupExtraNetworkSidebar": "추가 네트워크 사이드바",
"settingGroupLayout": "레이아웃 설정",
"settingGroupPromotTextarea": "프롬프트 텍스트 영역",
"settingGroupPromptTextarea": "프롬프트 텍스트 영역",
"settingGroupQuickSettingSidebar": "빠른 설정 사이드바",
"settingGroupTheme": "테마 설정",
"settingHideFooter": "푸터 숨기기",

View File

@ -18,8 +18,8 @@
"scroll": "Скрол",
"setPrompt": "Вставить промт",
"setting": "Настройки",
"settingButtomReset": "Сбросить",
"settingButtomSubmit": "Применить и перезапустить интерфейс",
"settingButtonReset": "Сбросить",
"settingButtonSubmit": "Применить и перезапустить интерфейс",
"settingCustomFont": "Загружать спец. шрифт",
"settingCustomFontDesc": "Когда он включен, он автоматически загружает веб-шрифт для улучшения отображения текста на китайском, английском и кодовом языках.",
"settingCustomLogo": "Пользовательский логотип",
@ -38,7 +38,7 @@
"settingExtraNetworkSidebarEnableDesc": "Включить Панель Доп. моделей справа",
"settingGroupExtraNetworkSidebar": "Боковая панель с доп сетями",
"settingGroupLayout": "Настройки макета",
"settingGroupPromotTextarea": "Промт(подсказка)",
"settingGroupPromptTextarea": "Промт(подсказка)",
"settingGroupQuickSettingSidebar": "Боковая панель быстрых настроек",
"settingGroupTheme": "Настройка темы",
"settingHideFooter": "Скрыть футтер",

View File

@ -18,8 +18,8 @@
"scroll": "滚动",
"setPrompt": "发送提示词",
"setting": "设置",
"settingButtomReset": "重置",
"settingButtomSubmit": "应用并重启界面",
"settingButtonReset": "重置",
"settingButtonSubmit": "应用并重启界面",
"settingCustomFont": "加载字体美化",
"settingCustomFontDesc": "开启后会自动加载 Webfont 美化字体,优化中英文和代码显示效果",
"settingCustomLogo": "自定义 Logo",
@ -38,7 +38,7 @@
"settingExtraNetworkSidebarEnableDesc": "启用位于右侧的附加网络侧边栏",
"settingGroupExtraNetworkSidebar": "附加网络侧边栏",
"settingGroupLayout": "布局设置",
"settingGroupPromotTextarea": "提示词文本框",
"settingGroupPromptTextarea": "提示词文本框",
"settingGroupQuickSettingSidebar": "快捷设置侧边栏",
"settingGroupTheme": "主题设置",
"settingHideFooter": "隐藏页脚",

View File

@ -18,8 +18,8 @@
"scroll": "滾動",
"setPrompt": "發送提示詞",
"setting": "設置",
"settingButtomReset": "重置",
"settingButtomSubmit": "應用並重啟界面",
"settingButtonReset": "重置",
"settingButtonSubmit": "應用並重啟界面",
"settingCustomFont": "載入字型美化",
"settingCustomFontDesc": "開啟後會自動載入 Webfont 美化字型,優化中英文和程式碼顯示效果",
"settingCustomLogo": "自定義 Logo",
@ -38,7 +38,7 @@
"settingExtraNetworkSidebarEnableDesc": "啟用位於右側的附加網絡側邊欄",
"settingGroupExtraNetworkSidebar": "附加網絡側邊欄",
"settingGroupLayout": "佈局設置",
"settingGroupPromotTextarea": "提示詞文本框",
"settingGroupPromptTextarea": "提示詞文本框",
"settingGroupQuickSettingSidebar": "快捷設置側邊欄",
"settingGroupTheme": "主題設置",
"settingHideFooter": "隱藏頁腳",

View File

@ -8,7 +8,7 @@ import { useTranslation } from 'react-i18next';
import { homepage } from '@/../package.json';
import Modal, { type ModalProps } from '@/components/Modal';
import VersionTag from '@/components/VersionTag';
import { useAppStore } from '@/store';
import { selectors, useAppStore } from '@/store';
export interface GiscusProps {
onCancel?: ModalProps['onCancel'];
@ -18,7 +18,7 @@ export interface GiscusProps {
const repoName = homepage.replace('https://github.com/', '') as `${string}/${string}`;
const Giscus = memo<GiscusProps>(({ open, onCancel }) => {
const setting = useAppStore((st) => st.setting, isEqual);
const setting = useAppStore(selectors.currentSetting, isEqual);
const { t } = useTranslation();
return (
<Modal

View File

@ -4,12 +4,12 @@ import { Loader2 } from 'lucide-react';
import { memo } from 'react';
import { Logo } from '@/components';
import { useAppStore } from '@/store';
import { selectors, useAppStore } from '@/store';
import { useStyles } from './style';
const Loading = memo(() => {
const setting = useAppStore((st) => st.setting, isEqual);
const setting = useAppStore(selectors.currentSetting, isEqual);
const { styles } = useStyles({
isPrimaryColor: Boolean(setting.primaryColor),
liteAnimation: setting.liteAnimation,

View File

@ -3,7 +3,7 @@ import isEqual from 'fast-deep-equal';
import { type CSSProperties, memo } from 'react';
import { shallow } from 'zustand/shallow';
import { useAppStore } from '@/store';
import { selectors, useAppStore } from '@/store';
import CustomLogo from './CustomLogo';
import KitchenLogo from './KitchenLogo';
@ -14,8 +14,8 @@ export interface LogoProps {
}
const Logo = memo<LogoProps>(({ size = 32, style }) => {
const setting = useAppStore((st) => st.setting, isEqual);
const themeMode = useAppStore((st) => st.themeMode, shallow);
const setting = useAppStore(selectors.currentSetting, isEqual);
const themeMode = useAppStore(selectors.themeMode, shallow);
if (setting.logoType === 'kitchen') {
return <KitchenLogo size={size * 0.75} style={style} themeMode={themeMode} />;

View File

@ -1,4 +1,4 @@
import { Form, type FormItemProps, FormProps, Swatches } from '@lobehub/ui';
import { Form, type ItemGroup, Swatches } from '@lobehub/ui';
import { Button, Input, InputNumber, Segmented, Select, Switch } from 'antd';
import isEqual from 'fast-deep-equal';
import { Layout, Palette, PanelLeftClose, PanelRightClose, TextCursorInput } from 'lucide-react';
@ -7,13 +7,28 @@ import { useTranslation } from 'react-i18next';
import { shallow } from 'zustand/shallow';
import { CustomLogo } from '@/components';
import { NeutralColor, PrimaryColor, WebuiSetting, defaultSetting, useAppStore } from '@/store';
import { selectors, useAppStore } from '@/store';
import { DEFAULT_SETTING, type WebuiSetting, type WebuiSettingKeys } from '@/store/initialState';
import { colors, findKey, neutralColors, primaryColors } from './data';
import {
type NeutralColor,
type PrimaryColor,
findCustomThemeName,
neutralColors,
neutralColorsSwatches,
primaryColors,
primaryColorsSwatches,
} from './data';
import { useStyles } from './style';
type SettingItemGroup = ItemGroup & {
children: {
name?: WebuiSettingKeys;
}[];
};
const SettingForm = memo(() => {
const setting = useAppStore((st) => st.setting, isEqual);
const setting = useAppStore(selectors.currentSetting, isEqual);
const { onSetSetting, localeOptions } = useAppStore(
(st) => ({ localeOptions: st.localeOptions, onSetSetting: st.onSetSetting }),
shallow,
@ -29,7 +44,7 @@ const SettingForm = memo(() => {
const { t } = useTranslation();
const onReset = useCallback(() => {
onSetSetting(defaultSetting);
onSetSetting(DEFAULT_SETTING);
location.reload();
}, []);
@ -41,292 +56,314 @@ const SettingForm = memo(() => {
[primaryColor, neutralColor],
);
const items: FormProps['items'] = 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 ? colors[primaryColor] : undefined}
colors={primaryColors}
onSelect={(c) => setPrimaryColor(c ? findKey(colors, c) : undefined)}
/>
),
desc: t('settingPrimaryColorDesc'),
label: t('settingPrimaryColor'),
},
{
children: (
<Swatches
activeColor={neutralColor ? neutralColors[neutralColor] : undefined}
colors={Object.values(neutralColors)}
onSelect={(c) => setNeutralColor(c ? findKey(neutralColors, c) : undefined)}
/>
),
desc: t('settingNeutralColorDesc'),
label: t('settingNeutralColor'),
},
{
children: (
<Segmented
options={[
{
label: t('lobe'),
value: 'lobe',
},
{
label: t('kitchen'),
value: 'kitchen',
},
{
label: t('custom'),
value: 'custom',
},
]}
/>
),
desc: t('settingLogoTypeDesc'),
label: t('settingLogoType'),
name: 'logoType',
},
rawSetting.logoType === 'custom' && {
children: <Input />,
desc: t('settingCustomLogoDesc'),
divider: false,
label: t('settingCustomLogo'),
name: 'logoCustomUrl',
},
rawSetting.logoType === 'custom' && {
children: <Input />,
desc: t('settingCustomTitleDesc'),
divider: false,
label: t('settingCustomTitle'),
name: 'logoCustomTitle',
},
rawSetting.logoType === 'custom' && {
children: (
<CustomLogo
logoCustomTitle={rawSetting.logoCustomTitle}
logoCustomUrl={rawSetting.logoCustomUrl}
/>
),
divider: false,
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',
},
].filter(Boolean) as FormItemProps[],
icon: Palette,
title: t('settingGroupTheme'),
},
{
children: [
{
children: (
<Segmented
options={[
{
label: t('scroll'),
value: 'scroll',
},
{
label: t('resizable'),
value: 'resizable',
},
]}
/>
),
desc: t('settingPromptDisplayModeDesc'),
label: t('settingPromptDisplayMode'),
name: 'promotTextarea',
},
{
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('settingGroupPromotTextarea'),
},
{
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'),
},
{
children: [
{
children: <Switch />,
desc: t('settingQuickSettingSidebarEnableDesc'),
label: t('settingQuickSettingSidebarEnable'),
name: 'enableSidebar',
valuePropName: 'checked',
},
rawSetting.enableSidebar && {
children: <Switch />,
desc: t('settingQuickSettingSidebarDefaultExpandDesc'),
label: t('settingQuickSettingSidebarDefaultExpand'),
name: 'sidebarExpand',
valuePropName: 'checked',
},
rawSetting.enableSidebar && {
children: (
<Segmented
options={[
{
label: t('fixed'),
value: 'fixed',
},
{
label: t('float'),
value: 'float',
},
]}
/>
),
desc: t('settingQuickSettingSidebarDisplayModeDesc'),
label: t('settingQuickSettingSidebarDisplayMode'),
name: 'sidebarFixedMode',
},
rawSetting.enableSidebar && {
children: <InputNumber />,
desc: t('settingQuickSettingSidebarDefaultWidthDesc'),
label: t('settingQuickSettingSidebarDefaultWidth'),
name: 'sidebarWidth',
},
].filter(Boolean) as FormItemProps[],
icon: PanelLeftClose,
title: t('settingGroupQuickSettingSidebar'),
},
{
children: [
{
children: <Switch />,
desc: t('settingExtraNetworkSidebarEnableDesc'),
label: t('settingExtraNetworkSidebarEnable'),
name: 'enableExtraNetworkSidebar',
valuePropName: 'checked',
},
rawSetting.enableExtraNetworkSidebar && {
children: (
<Segmented
options={[
{
label: t('fixed'),
value: 'fixed',
},
{
label: t('float'),
value: 'float',
},
]}
/>
),
desc: t('settingExtraNetworkSidebarDisplayModeDesc'),
label: t('settingExtraNetworkSidebarDisplayMode'),
name: 'extraNetworkFixedMode',
},
rawSetting.enableExtraNetworkSidebar && {
children: <Switch />,
desc: t('settingExtraNetworkSidebarDefaultExpandDesc'),
label: t('settingExtraNetworkSidebarDefaultExpand'),
name: 'extraNetworkSidebarExpand',
valuePropName: 'checked',
},
rawSetting.enableExtraNetworkSidebar && {
children: <InputNumber />,
desc: t('settingExtraNetworkSidebarDefaultWidthDesc'),
label: t('settingExtraNetworkSidebarDefaultWidth'),
name: 'extraNetworkSidebarWidth',
},
rawSetting.enableExtraNetworkSidebar && {
children: <InputNumber />,
desc: t('settingExtraNetworkSidebarDefaultCardSizeDesc'),
label: t('settingExtraNetworkSidebarDefaultCardSize'),
name: 'extraNetworkCardSize',
},
].filter(Boolean) as FormItemProps[],
icon: PanelRightClose,
title: t('settingGroupExtraNetworkSidebar'),
},
],
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={[
{
label: t('lobe'),
value: 'lobe',
},
{
label: t('kitchen'),
value: 'kitchen',
},
{
label: t('custom'),
value: 'custom',
},
]}
/>
),
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',
},
],
icon: Palette,
title: t('settingGroupTheme'),
}),
[
rawSetting.logoType,
rawSetting.enableExtraNetworkSidebar,
rawSetting.enableSidebar,
rawSetting.logoCustomTitle,
rawSetting.logoCustomUrl,
primaryColor,
neutralColor,
rawSetting.logoType,
rawSetting.logoCustomTitle,
rawSetting.logoCustomUrl,
],
);
const promptTextarea: SettingItemGroup = useMemo(
() => ({
children: [
{
children: (
<Segmented
options={[
{
label: t('scroll'),
value: 'scroll',
},
{
label: t('resizable'),
value: 'resizable',
},
]}
/>
),
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'),
}),
[],
);
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 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={[
{
label: t('fixed'),
value: 'fixed',
},
{
label: t('float'),
value: 'float',
},
]}
/>
),
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],
);
const extraNetworkSidebar: SettingItemGroup = useMemo(
() => ({
children: [
{
children: <Switch />,
desc: t('settingExtraNetworkSidebarEnableDesc'),
label: t('settingExtraNetworkSidebarEnable'),
name: 'enableExtraNetworkSidebar',
valuePropName: 'checked',
},
{
children: (
<Segmented
options={[
{
label: t('fixed'),
value: 'fixed',
},
{
label: t('float'),
value: 'float',
},
]}
/>
),
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],
);
return (
<Form
className={styles}
footer={
<>
<Button htmlType="button" onClick={onReset} style={{ borderRadius: 4 }}>
{t('settingButtomReset')}
{t('settingButtonReset')}
</Button>
<Button htmlType="submit" style={{ borderRadius: 4 }} type="primary">
{t('settingButtomSubmit')}
{t('settingButtonSubmit')}
</Button>
</>
}
initialValues={setting}
items={items}
items={[theme, promptTextarea, layout, quickSettingSidebar, extraNetworkSidebar]}
onFinish={onFinish}
onValuesChange={(_, v) => setRawSetting(v)}
/>

View File

@ -1,55 +1,35 @@
import { colors as lobeColor } from '@lobehub/ui';
import {
neutralColors as nc,
neutralColorsSwatches as ncs,
primaryColorsSwatches as pcs,
primaryColors as ps,
} 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],
export const primaryColors = {
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],
...ps,
};
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 primaryColorsSwatches = [primaryColors.kitchen, ...pcs];
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],
...nc,
};
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];
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];
};
export type PrimaryColor = keyof typeof primaryColors;
export type NeutralColor = keyof typeof neutralColors;

View File

@ -2,7 +2,8 @@ import i18next from 'i18next';
import HttpBackend, { HttpBackendOptions } from 'i18next-http-backend';
import { initReactI18next } from 'react-i18next';
import { SETTING_KEY, WebuiSetting } from '@/store';
import { SETTING_KEY } from '@/store/action';
import type { WebuiSetting } from '@/store/initialState';
const localSetting = JSON.parse(localStorage.getItem(SETTING_KEY) as any) as WebuiSetting;

View File

@ -1,24 +1,25 @@
import { type DivProps, ThemeProvider, colors } from '@lobehub/ui';
import { type DivProps, ThemeProvider } from '@lobehub/ui';
import {
generateColorNeutralPalette,
generateColorPalette,
} from '@lobehub/ui/es/styles/algorithms/generateColorPalette';
import { colorScales } from '@lobehub/ui/es/styles/colors';
import { neutralColorScales } from '@lobehub/ui/es/styles/neutralColors';
import isEqual from 'fast-deep-equal';
import qs from 'query-string';
import { memo, useCallback, useEffect } from 'react';
import { shallow } from 'zustand/shallow';
import { useIsDarkMode } from '@/hooks/useIsDarkMode';
import { useAppStore } from '@/store';
import { selectors, useAppStore } from '@/store';
import { kitchenNeutral, kitchenPrimary } from '@/styles/kitchenColors';
import { neutralColorScales } from '@/styles/neutralColors';
const Layout = memo<DivProps>(({ children }) => {
const { onSetThemeMode, themeMode } = useAppStore(
(st) => ({ onInit: st.onInit, onSetThemeMode: st.onSetThemeMode, themeMode: st.themeMode }),
shallow,
);
const setting = useAppStore((st) => st.setting, isEqual);
const setting = useAppStore(selectors.currentSetting, isEqual);
const isDarkMode = useIsDarkMode();
useEffect(() => {
@ -39,7 +40,7 @@ const Layout = memo<DivProps>(({ children }) => {
if (setting.primaryColor === 'kitchen') {
primaryTokens = kitchenPrimary[themeMode];
} else {
const scale = colors[setting.primaryColor];
const scale = colorScales[setting.primaryColor];
primaryTokens = generateColorPalette({ appearance: themeMode, scale, type: 'Primary' });
}
}
@ -53,7 +54,7 @@ const Layout = memo<DivProps>(({ children }) => {
}
return { ...primaryTokens, ...neutralTokens };
}, [setting.primaryColor, themeMode]);
}, [setting.primaryColor, setting.neutralColor, themeMode]);
return (
setting && (

View File

@ -1,10 +1,8 @@
import { SyntaxHighlighter } from '@lobehub/ui';
import { useScroll, useSize } from 'ahooks';
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { shallow } from 'zustand/shallow';
import { useExternalTextareaObserver } from '@/hooks/useExternalTextareaObserver';
import { useAppStore } from '@/store';
import grammar from './prompt.tmLanguage.json';
import { themeConfig } from './promptTheme';
@ -29,7 +27,6 @@ interface AppProps {
const App = memo<AppProps>(({ parentId }) => {
const ref: any = useRef(null);
const [prompt, setPrompt] = useState<string>('');
const { themeMode } = useAppStore((st) => ({ themeMode: st.themeMode }), shallow);
const { styles, theme } = useStyles();
const nativeTextareaValue = useExternalTextareaObserver(`${parentId} label textarea`);
const nativeTextarea = useMemo(
@ -72,7 +69,7 @@ const App = memo<AppProps>(({ parentId }) => {
ref={ref}
style={{ height: size?.height, width: size?.width }}
>
<SyntaxHighlighter language="prompt" options={options} theme={themeMode}>
<SyntaxHighlighter language="prompt" options={options}>
{prompt.trim()}
</SyntaxHighlighter>
</div>

View File

@ -4,7 +4,7 @@ import { memo, useEffect, useRef } from 'react';
import draggablePanel from '@/script/draggablePanel';
import formatPrompt from '@/script/formatPrompt';
import { useAppStore } from '@/store';
import { selectors, useAppStore } from '@/store';
import { type DivProps } from '@/types';
import { useStyles as usePreviewStyles } from '../Preview/style';
@ -12,7 +12,7 @@ import { useStyles } from './style';
const Content = memo<DivProps>(({ className, ...props }) => {
const mainReference = useRef<HTMLDivElement>(null);
const setting = useAppStore((st) => st.setting, isEqual);
const setting = useAppStore(selectors.currentSetting, isEqual);
const { mobile } = useResponsive();
const { cx, styles } = useStyles({
isPromptResizable: setting.promptTextareaType === 'resizable',

View File

@ -1,19 +1,21 @@
import { ActionIcon, DraggablePanelBody, DraggablePanelFooter } from '@lobehub/ui';
import { useTimeout } from 'ahooks';
import { Skeleton, Slider } from 'antd';
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 '@/pages/ExtraNetworkSidebar/style';
import civitaiHelperFix from '@/script/civitaiHelperFix';
import { useAppStore } from '@/store';
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, currentTab] = useAppStore((st) => [st.setting, st.currentTab], shallow);
const setting = useAppStore(selectors.currentSetting, isEqual);
const currentTab = useAppStore(selectors.currentTab, shallow);
const [size, setSize] = useState<number>(setting.extraNetworkCardSize || 86);
const { styles } = useStyles({ size });

View File

@ -9,7 +9,7 @@ import isEqual from 'fast-deep-equal';
import { memo, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useAppStore } from '@/store';
import { selectors, useAppStore } from '@/store';
import { type DivProps } from '@/types';
import Inner from './Inner';
@ -21,7 +21,7 @@ export interface ExtraNetworkSidebarProps extends DivProps {
const ExtraNetworkSidebar = memo<ExtraNetworkSidebarProps>(({ headerHeight }) => {
const { mobile } = useResponsive();
const setting = useAppStore((st) => st.setting, isEqual);
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 });

View File

@ -3,14 +3,14 @@ import isEqual from 'fast-deep-equal';
import { memo, useEffect, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { useAppStore } from '@/store';
import { selectors, useAppStore } from '@/store';
import { type DivProps } from '@/types';
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 setting = useAppStore(selectors.currentSetting, isEqual);
const { cx, styles } = useStyles();
const { t } = useTranslation();
const footerReference = useRef<HTMLDivElement>(null);

View File

@ -8,7 +8,7 @@ import { useTranslation } from 'react-i18next';
import { shallow } from 'zustand/shallow';
import { Giscus, Setting } from '@/components';
import { useAppStore } from '@/store';
import { selectors, useAppStore } from '@/store';
const CivitaiLogo: LucideIcon | any = ({ size }: any) => (
<svg fill="currentColor" height={size} viewBox="0 0 16 16" width={size}>
@ -23,7 +23,7 @@ interface ActionsProps {
const Actions = memo<ActionsProps>(() => {
const [isSettingOpen, setIsSettingOpen] = useState(false);
const [isModalOpen, setIsModalOpen] = useState(false);
const themeMode = useAppStore((st) => st.themeMode, shallow);
const themeMode = useAppStore(selectors.themeMode, shallow);
const { mobile } = useResponsive();
const { t } = useTranslation();

View File

@ -4,7 +4,7 @@ import { startCase } from 'lodash-es';
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
import { shallow } from 'zustand/shallow';
import { useAppStore } from '@/store';
import { selectors, useAppStore } from '@/store';
const hideOriganlNav = () => {
(gradioApp().querySelector('#tabs > .tab-nav:first-of-type') as HTMLDivElement).style.display =
@ -42,7 +42,7 @@ const genNavList = (): NavItem[] => {
};
const Nav = memo(() => {
const currentTab = useAppStore((st) => st.currentTab, shallow);
const currentTab = useAppStore(selectors.currentTab, shallow);
const { mobile } = useResponsive();
const [opened, setOpened] = useState(false);
const [items, setItems] = useState<TabsNavProps['items']>([]);

View File

@ -3,7 +3,7 @@ import isEqual from 'fast-deep-equal';
import { memo, useEffect, useRef } from 'react';
import { shallow } from 'zustand/shallow';
import { useAppStore } from '@/store';
import { selectors, useAppStore } from '@/store';
import { type DivProps } from '@/types';
import { useStyles } from './style';
@ -13,8 +13,8 @@ export interface PreviewProps extends DivProps {
}
const Preview = memo<PreviewProps>(({ headerHeight }) => {
const currentTab = useAppStore((st) => st.currentTab, shallow);
const setting = useAppStore((st) => st.setting, isEqual);
const currentTab = useAppStore(selectors.currentTab, shallow);
const setting = useAppStore(selectors.currentSetting, isEqual);
const { cx, styles } = useStyles({
headerHeight,
isPrimaryColor: Boolean(setting.primaryColor),

View File

@ -3,11 +3,11 @@ import isEqual from 'fast-deep-equal';
import { memo, useEffect, useRef } from 'react';
import { PromptEditor } from '@/components';
import { useAppStore } from '@/store';
import { selectors, useAppStore } from '@/store';
import { type DivProps } from '@/types';
const Inner = memo<DivProps>(() => {
const setting = useAppStore((st) => st.setting, isEqual);
const setting = useAppStore(selectors.currentSetting, isEqual);
const sidebarReference = useRef<HTMLDivElement>(null);
useEffect(() => {

View File

@ -10,7 +10,7 @@ import isEqual from 'fast-deep-equal';
import { memo, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useAppStore } from '@/store';
import { selectors, useAppStore } from '@/store';
import Inner from './Inner';
import { useStyles } from './style';
@ -21,7 +21,7 @@ export interface QuickSettingSidebarProps extends DivProps {
const QuickSettingSidebar = memo<QuickSettingSidebarProps>(({ headerHeight }) => {
const { mobile } = useResponsive();
const setting = useAppStore((st) => st.setting, isEqual);
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);

View File

@ -7,7 +7,7 @@ import { shallow } from 'zustand/shallow';
import '@/i18n/config';
import { PromptHighlight } from '@/modules/PromptHighlight';
import replaceIcon from '@/script/replaceIcon';
import { useAppStore } from '@/store';
import { selectors, useAppStore } from '@/store';
import GlobalStyle from '@/styles';
import Content from './Content';
@ -21,8 +21,8 @@ import { useStyles } from './style';
const HEADER_HEIGHT = 64;
const Index = memo(() => {
const currentTab = useAppStore((st) => st.currentTab, shallow);
const setting = useAppStore((st) => st.setting, isEqual);
const currentTab = useAppStore(selectors.currentTab, shallow);
const setting = useAppStore(selectors.currentSetting, isEqual);
const { mobile } = useResponsive();
const { cx, styles } = useStyles({
headerHeight: HEADER_HEIGHT,

View File

@ -1,67 +0,0 @@
import { SelectProps } from 'antd';
import { I18n } from '@/types';
export const SETTING_KEY = 'SD-LOBE-SETTING';
export const FALLBACK_SETTING_KEY = 'SD-KITCHEN-SETTING';
export type PrimaryColor =
| 'blue'
| 'cyan'
| 'geekblue'
| 'gold'
| 'green'
| 'lime'
| 'magenta'
| 'orange'
| 'purple'
| 'red'
| 'volcano'
| 'yellow'
| 'kitchen';
export type NeutralColor = 'mauve' | 'slate' | 'sage' | 'olive' | 'sand' | 'kitchen';
export interface WebuiSetting {
enableExtraNetworkSidebar: boolean;
enableHighlight: boolean;
enableSidebar: boolean;
enableWebFont: boolean;
extraNetworkCardSize: number;
extraNetworkFixedMode: 'fixed' | 'float';
extraNetworkSidebarExpand: boolean;
extraNetworkSidebarWidth: number;
i18n: I18n;
layoutHideFooter: boolean;
layoutSplitPreview: boolean;
liteAnimation: boolean;
logoCustomTitle: string | undefined;
logoCustomUrl: string | undefined;
logoType: 'lobe' | 'kitchen' | 'custom';
neutralColor: NeutralColor | undefined;
primaryColor: PrimaryColor | undefined;
promptEditor: boolean;
promptTextareaType: 'scroll' | 'resizable';
sidebarExpand: boolean;
sidebarFixedMode: 'fixed' | 'float';
sidebarWidth: number;
svgIcon: boolean;
}
export interface AppState {
currentTab: string;
latestVersion: string;
loading: boolean;
localeOptions: SelectProps['options'];
onInit: () => void;
onLoadLatestVersion: () => void;
onLoadLocalOptions: () => void;
onLoadSetting: () => void;
onLoadVersion: () => void;
onSetSetting: (setting: WebuiSetting) => void;
onSetThemeMode: (themeMode: 'light' | 'dark') => void;
setCurrentTab: () => void;
setting: WebuiSetting;
themeMode: 'light' | 'dark';
version: string;
}

100
src/store/action.ts Normal file
View File

@ -0,0 +1,100 @@
import type { StateCreator } from 'zustand/vanilla';
import { getLatestVersion, getLocaleOptions, getSetting, getVersion, postSetting } from './api';
import { DEFAULT_SETTING, type WebuiSetting } from './initialState';
import type { Store } from './store';
export const SETTING_KEY = 'SD-LOBE-SETTING';
export const FALLBACK_SETTING_KEY = 'SD-KITCHEN-SETTING';
export interface StoreAction {
onInit: () => void;
onLoadLatestVersion: () => void;
onLoadLocalOptions: () => void;
onLoadSetting: () => void;
onLoadVersion: () => void;
onSetSetting: (setting: Partial<WebuiSetting>) => void;
onSetThemeMode: (themeMode: 'light' | 'dark') => void;
setCurrentTab: () => void;
}
export const createSettings: StateCreator<Store, [['zustand/devtools', never]], [], StoreAction> = (
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();
if (webuiSetting) {
console.info('🤯 [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 fallbackLocalSetting: any = localStorage.getItem(FALLBACK_SETTING_KEY);
if (fallbackLocalSetting) {
console.info('🤯 [setting] loaded fallback local setting');
themeSetting = JSON.parse(fallbackLocalSetting);
}
}
if (!themeSetting) {
console.info('🤯 [setting] loaded default setting');
themeSetting = DEFAULT_SETTING;
}
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');
}
},
});

View File

@ -3,7 +3,8 @@ import semver from 'semver';
import defualtLocaleOptions from '@/../locales/options.json';
import { homepage, version } from '@/../package.json';
import { WebuiSetting } from '@/store/AppState';
import { WebuiSetting } from './initialState';
export const DEFAULT_VERSION: string = version;
export const DEFAULT_LOCALE_OPTIONS: SelectProps['options'] = defualtLocaleOptions;

View File

@ -1,127 +1,9 @@
import { create } from 'zustand';
import { devtools } from 'zustand/middleware';
import { AppState, FALLBACK_SETTING_KEY, SETTING_KEY, WebuiSetting } from './AppState';
import {
DEFAULT_LOCALE_OPTIONS,
DEFAULT_VERSION,
getLatestVersion,
getLocaleOptions,
getSetting,
getVersion,
postSetting,
} from './api';
import { type Store, createStore } from './store';
export * from './AppState';
export const useAppStore = create<Store>()(devtools(createStore));
export const defaultSetting: WebuiSetting = {
enableExtraNetworkSidebar: true,
enableHighlight: true,
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 const useAppStore = create<AppState>()(
devtools((set, get) => ({
currentTab: 'tab_txt2img',
latestVersion: DEFAULT_VERSION,
loading: true,
localeOptions: DEFAULT_LOCALE_OPTIONS,
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();
if (webuiSetting) {
console.info('🤯 [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 fallbackLocalSetting: any = localStorage.getItem(FALLBACK_SETTING_KEY);
if (fallbackLocalSetting) {
console.info('🤯 [setting] loaded fallback local setting');
themeSetting = JSON.parse(fallbackLocalSetting);
}
}
if (!themeSetting) {
console.info('🤯 [setting] loaded default setting');
themeSetting = defaultSetting;
}
const setting = { ...defaultSetting, ...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) => {
localStorage.setItem(SETTING_KEY, JSON.stringify(setting));
await postSetting(setting);
set(() => ({ setting }), 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');
}
},
setting: defaultSetting,
themeMode: 'dark',
version: DEFAULT_VERSION,
})),
);
export { selectors } from './selectors';
export { type Store } from './store';

79
src/store/initialState.ts Normal file
View File

@ -0,0 +1,79 @@
import { SelectProps } from 'antd';
import type { NeutralColor, PrimaryColor } from '@/components/Setting/data';
import { DEFAULT_LOCALE_OPTIONS, DEFAULT_VERSION } from '@/store/api';
import { I18n } from '@/types';
export interface WebuiSetting {
enableExtraNetworkSidebar: boolean;
enableHighlight: boolean;
enableSidebar: boolean;
enableWebFont: boolean;
extraNetworkCardSize: number;
extraNetworkFixedMode: 'fixed' | 'float';
extraNetworkSidebarExpand: boolean;
extraNetworkSidebarWidth: number;
i18n: I18n;
layoutHideFooter: boolean;
layoutSplitPreview: boolean;
liteAnimation: boolean;
logoCustomTitle: string | undefined;
logoCustomUrl: string | undefined;
logoType: 'lobe' | 'kitchen' | 'custom';
neutralColor: NeutralColor | undefined;
primaryColor: PrimaryColor | undefined;
promptEditor: boolean;
promptTextareaType: 'scroll' | 'resizable';
sidebarExpand: boolean;
sidebarFixedMode: 'fixed' | 'float';
sidebarWidth: number;
svgIcon: boolean;
}
export type WebuiSettingKeys = keyof WebuiSetting;
export const DEFAULT_SETTING: WebuiSetting = {
enableExtraNetworkSidebar: true,
enableHighlight: true,
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 {
currentTab: string;
latestVersion: string;
loading: boolean;
localeOptions: SelectProps['options'];
setting: WebuiSetting;
themeMode: 'light' | 'dark';
version: string;
}
export const initialState: StroeState = {
currentTab: 'tab_txt2img',
latestVersion: DEFAULT_VERSION,
loading: true,
localeOptions: DEFAULT_LOCALE_OPTIONS,
setting: DEFAULT_SETTING,
themeMode: 'dark',
version: DEFAULT_VERSION,
};

13
src/store/selectors.ts Normal file
View File

@ -0,0 +1,13 @@
import { defaults } from 'lodash-es';
import { DEFAULT_SETTING } from './initialState';
import { Store } from './store';
const currentSetting = (s: Store) => defaults(s.setting, DEFAULT_SETTING);
const currentTab = (s: Store) => s.currentTab;
const themeMode = (s: Store) => s.themeMode;
export const selectors = {
currentSetting,
currentTab,
themeMode,
};

11
src/store/store.ts Normal file
View File

@ -0,0 +1,11 @@
import { StateCreator } from 'zustand/vanilla';
import { type StoreAction, createSettings } from './action';
import { type StroeState, initialState } from './initialState';
export type Store = StoreAction & StroeState;
export const createStore: StateCreator<Store, [['zustand/devtools', never]]> = (...parameters) => ({
...initialState,
...createSettings(...parameters),
});

View File

@ -1,4 +1,5 @@
import { Theme, css } from 'antd-style';
import { readableColor } from 'polished';
export default (token: Theme) => css`
body {
@ -8,30 +9,24 @@ export default (token: Theme) => css`
}
}
.ant-tooltip-inner {
display: flex;
align-items: center;
justify-content: center;
min-height: unset;
padding: 4px 8px;
color: ${token.colorBgLayout};
background-color: ${token.colorText} !important;
border-radius: ${token.borderRadiusSM}px;
}
.ant-tooltip-arrow {
&::before,
&::after {
background: ${token.colorText} !important;
}
}
.ant-slider-track,
.ant-tabs-ink-bar,
.ant-btn-primary,
.ant-switch-checked {
background: ${token.colorPrimary} !important;
}
.ant-btn-primary:not(.ant-btn-dangerous) {
color: ${readableColor(token.colorPrimary)};
background: ${token.colorPrimary};
&:hover {
color: ${readableColor(token.colorPrimary)} !important;
background: ${token.colorPrimaryHover} !important;
}
&:active {
color: ${readableColor(token.colorPrimaryActive)} !important;
background: ${token.colorPrimaryActive} !important;
}
}
`;

View File

@ -1,322 +0,0 @@
import type { ColorScaleItem } from '@lobehub/ui/es/styles/colors';
export interface NeutralColorScales {
mauve: ColorScaleItem;
olive: ColorScaleItem;
sage: ColorScaleItem;
sand: ColorScaleItem;
slate: ColorScaleItem;
}
export const neutralColorScales: NeutralColorScales = {
mauve: {
dark: [
'#1c1b1e',
'#252528',
'#2f2f32',
'#3a393d',
'#454448',
'#504f53',
'#5b5a5f',
'#67666a',
'#737177',
'#7f7d83',
'#bbb9bd',
'#fcf8fb',
'#ffffff',
],
darkA: [
'rgba(233, 225, 250, 0.12)',
'rgba(231, 231, 250, 0.16)',
'rgba(235, 235, 250, 0.2)',
'rgba(242, 237, 254, 0.24)',
'rgba(238, 234, 248, 0.29)',
'rgba(242, 239, 252, 0.33)',
'rgba(239, 237, 250, 0.38)',
'rgba(245, 243, 252, 0.42)',
'rgba(245, 240, 253, 0.47)',
'rgba(244, 240, 252, 0.52)',
'rgba(253, 250, 255, 0.74)',
'rgba(255, 251, 254, 0.99)',
'#ffffff',
],
light: [
'#fcf8fb',
'#edeaed',
'#dfdcdf',
'#d1ced2',
'#c2c0c4',
'#b4b2b7',
'#a7a4a9',
'#99979c',
'#8c8a90',
'#7f7d83',
'#4a494d',
'#1c1b1e',
'#111',
],
lightA: [
'rgba(155, 22, 122, 0.03)',
'rgba(55, 22, 55, 0.09)',
'rgba(26, 5, 26, 0.14)',
'rgba(25, 10, 30, 0.2)',
'rgba(11, 3, 19, 0.25)',
'rgba(13, 7, 23, 0.31)',
'rgba(11, 2, 16, 0.36)',
'rgba(6, 1, 14, 0.41)',
'rgba(5, 1, 14, 0.46)',
'rgba(4, 0, 12, 0.51)',
'rgba(4, 2, 8, 0.72)',
'rgba(3, 2, 5, 0.9)',
'#111',
],
},
olive: {
dark: [
'#1a1c1b',
'#232624',
'#2d302e',
'#383a38',
'#424542',
'#4d504d',
'#585c58',
'#646763',
'#70736e',
'#7c7f79',
'#b9bab5',
'#faf9f4',
'#ffffff',
],
darkA: [
'rgba(236, 255, 245, 0.11)',
'rgba(233, 253, 240, 0.15)',
'rgba(237, 253, 242, 0.19)',
'rgba(243, 252, 243, 0.23)',
'rgba(236, 246, 236, 0.28)',
'rgba(241, 250, 241, 0.32)',
'rgba(238, 249, 238, 0.37)',
'rgba(244, 251, 241, 0.41)',
'rgba(243, 250, 239, 0.46)',
'rgba(248, 254, 242, 0.5)',
'rgba(253, 255, 248, 0.73)',
'rgba(255, 254, 249, 0.98)',
'#ffffff',
],
light: [
'#faf9f4',
'#ecebe6',
'#ddddd7',
'#cfcfc9',
'#c0c1bb',
'#b2b4ae',
'#a4a6a0',
'#969993',
'#898c86',
'#7c7f79',
'#484b48',
'#1a1c1b',
'#111',
],
lightA: [
'rgba(155, 135, 35, 0.05)',
'rgba(65, 55, 5, 0.1)',
'rgba(43, 43, 5, 0.16)',
'rgba(37, 37, 10, 0.22)',
'rgba(22, 25, 3, 0.27)',
'rgba(14, 21, 2, 0.32)',
'rgba(16, 21, 5, 0.38)',
'rgba(11, 18, 4, 0.43)',
'rgba(9, 15, 3, 0.48)',
'rgba(8, 13, 2, 0.53)',
'rgba(1, 5, 1, 0.72)',
'rgba(1, 3, 2, 0.9)',
'#111',
],
},
sage: {
dark: [
'#1a1c1b',
'#232625',
'#2d302f',
'#373a39',
'#424543',
'#4d504e',
'#585c59',
'#636765',
'#6f7370',
'#7a7f7c',
'#b8bab7',
'#f9f9f7',
'#ffffff',
],
darkA: [
'rgba(236, 255, 245, 0.11)',
'rgba(233, 253, 247, 0.15)',
'rgba(237, 253, 247, 0.19)',
'rgba(239, 252, 248, 0.23)',
'rgba(236, 246, 239, 0.28)',
'rgba(241, 250, 244, 0.32)',
'rgba(238, 249, 241, 0.37)',
'rgba(241, 251, 246, 0.41)',
'rgba(241, 250, 243, 0.46)',
'rgba(244, 254, 248, 0.5)',
'rgba(252, 255, 251, 0.73)',
'rgba(254, 254, 252, 0.98)',
'#ffffff',
],
light: [
'#f9f9f7',
'#eaebe8',
'#dcddda',
'#cdcfcc',
'#bfc1be',
'#b1b4b0',
'#a3a6a3',
'#959996',
'#888c89',
'#7a7f7c',
'#474b49',
'#1a1c1b',
'#111',
],
lightA: [
'rgba(105, 105, 55, 0.04)',
'rgba(45, 55, 25, 0.1)',
'rgba(22, 28, 8, 0.15)',
'rgba(5, 15, 0, 0.2)',
'rgba(9, 17, 5, 0.26)',
'rgba(3, 13, 0, 0.31)',
'rgba(6, 14, 6, 0.37)',
'rgba(3, 12, 5, 0.42)',
'rgba(2, 10, 4, 0.47)',
'rgba(4, 13, 8, 0.53)',
'rgba(3, 8, 6, 0.73)',
'rgba(1, 3, 2, 0.9)',
'#111',
],
},
sand: {
dark: [
'#1c1c18',
'#262521',
'#30302b',
'#3a3a35',
'#45453f',
'#505049',
'#5c5b54',
'#67675f',
'#73726a',
'#7f7e76',
'#bcbab2',
'#fcf9f3',
'#ffffff',
],
darkA: [
'rgba(255, 255, 218, 0.11)',
'rgba(253, 247, 220, 0.15)',
'rgba(253, 253, 226, 0.19)',
'rgba(252, 252, 230, 0.23)',
'rgba(246, 246, 225, 0.28)',
'rgba(250, 250, 228, 0.32)',
'rgba(249, 246, 227, 0.37)',
'rgba(251, 251, 232, 0.41)',
'rgba(250, 248, 230, 0.46)',
'rgba(254, 252, 236, 0.5)',
'rgba(254, 251, 241, 0.74)',
'rgba(255, 252, 245, 0.99)',
'#ffffff',
],
light: [
'#fcf9f3',
'#edebe4',
'#dfddd5',
'#d1cfc7',
'#c3c1b9',
'#b5b3ab',
'#a7a69d',
'#999890',
'#8c8b83',
'#7f7e76',
'#4b4a44',
'#1c1c18',
'#111',
],
lightA: [
'rgba(195, 135, 15, 0.05)',
'rgba(91, 73, 10, 0.11)',
'rgba(67, 55, 8, 0.17)',
'rgba(46, 37, 0, 0.22)',
'rgba(41, 34, 5, 0.28)',
'rgba(31, 25, 0, 0.33)',
'rgba(29, 27, 4, 0.39)',
'rgba(23, 21, 3, 0.44)',
'rgba(20, 18, 2, 0.49)',
'rgba(18, 16, 1, 0.54)',
'rgba(12, 10, 2, 0.74)',
'rgba(6, 6, 1, 0.91)',
'#111',
],
},
slate: {
dark: [
'#1b1c1d',
'#242527',
'#2e2f32',
'#383a3c',
'#434547',
'#4e5052',
'#595b5e',
'#64676a',
'#707276',
'#7b7e82',
'#b8babc',
'#f9f9fa',
'#ffffff',
],
darkA: [
'rgba(225, 233, 242, 0.12)',
'rgba(225, 231, 244, 0.16)',
'rgba(230, 235, 250, 0.2)',
'rgba(233, 242, 250, 0.24)',
'rgba(239, 246, 254, 0.28)',
'rgba(236, 242, 248, 0.33)',
'rgba(241, 246, 254, 0.37)',
'rgba(238, 245, 252, 0.42)',
'rgba(238, 243, 251, 0.47)',
'rgba(241, 247, 255, 0.51)',
'rgba(249, 251, 254, 0.74)',
'rgba(254, 254, 255, 0.98)',
'#ffffff',
],
light: [
'#f9f9fa',
'#ebebec',
'#dcddde',
'#cecfd0',
'#bfc1c3',
'#b1b3b5',
'#a4a6a8',
'#96989b',
'#898b8e',
'#7b7e82',
'#484a4d',
'#1b1c1d',
'#111',
],
lightA: [
'rgba(55, 55, 88, 0.03)',
'rgba(5, 5, 17, 0.08)',
'rgba(5, 12, 19, 0.14)',
'rgba(10, 15, 20, 0.2)',
'rgba(9, 17, 24, 0.26)',
'rgba(3, 10, 16, 0.31)',
'rgba(2, 8, 13, 0.36)',
'rgba(5, 10, 17, 0.42)',
'rgba(4, 8, 15, 0.47)',
'rgba(1, 7, 15, 0.52)',
'rgba(1, 4, 8, 0.72)',
'rgba(2, 3, 4, 0.9)',
'#111',
],
},
};