From 09d53292ce8dd5abe37ec81d6f0ed7711bf91272 Mon Sep 17 00:00:00 2001 From: rdmclin2 Date: Tue, 21 May 2024 22:50:23 +0800 Subject: [PATCH 01/11] =?UTF-8?q?:art:=20chore:=20extract=20=E9=9D=99?= =?UTF-8?q?=E6=80=81=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/layout/StyleRegistry.tsx | 24 ++++++++++++++++++++++++ src/layout/index.tsx | 31 +++++++++++++++++-------------- 2 files changed, 41 insertions(+), 14 deletions(-) create mode 100644 src/layout/StyleRegistry.tsx diff --git a/src/layout/StyleRegistry.tsx b/src/layout/StyleRegistry.tsx new file mode 100644 index 00000000..bf625ae9 --- /dev/null +++ b/src/layout/StyleRegistry.tsx @@ -0,0 +1,24 @@ +'use client'; + +import { StyleProvider, extractStaticStyle } from 'antd-style'; +import { useServerInsertedHTML } from 'next/navigation'; +import { PropsWithChildren, useRef } from 'react'; + +const StyleRegistry = ({ children }: PropsWithChildren) => { + const isInsert = useRef(false); + + useServerInsertedHTML(() => { + // avoid duplicate css insert + // refs: https://github.com/vercel/next.js/discussions/49354#discussioncomment-6279917 + if (isInsert.current) return; + + isInsert.current = true; + + // @ts-ignore + return extractStaticStyle().map((item) => item.style); + }); + + return {children}; +}; + +export default StyleRegistry; diff --git a/src/layout/index.tsx b/src/layout/index.tsx index 89cd87b3..d499fdbb 100644 --- a/src/layout/index.tsx +++ b/src/layout/index.tsx @@ -5,6 +5,7 @@ import { ThemeAppearance, createStyles } from 'antd-style'; import { ReactNode } from 'react'; import { VIDOL_THEME_APPEARANCE } from '@/constants/theme'; +import StyleRegistry from '@/layout/StyleRegistry'; import { useConfigStore } from '@/store/config'; import { useThemeStore } from '@/store/theme'; import { GlobalStyle } from '@/styles'; @@ -35,20 +36,22 @@ const Layout = (props: LayoutProps) => { const [primaryColor] = useConfigStore((s) => [s.config.primaryColor]); return ( - { - setCookie(VIDOL_THEME_APPEARANCE, appearance); - }} - themeMode={themeMode} - > - - -
{children}
-
+ + { + setCookie(VIDOL_THEME_APPEARANCE, appearance); + }} + themeMode={themeMode} + > + + +
{children}
+
+
); }; From 6b22c4a4eff353b4366c7529ec735ea6264eaafe Mon Sep 17 00:00:00 2001 From: rdmclin2 Date: Tue, 21 May 2024 23:39:17 +0800 Subject: [PATCH 02/11] =?UTF-8?q?:art:=20chore:=20=E8=BF=81=E7=A7=BB=20pan?= =?UTF-8?q?el=20=E7=9B=B8=E5=85=B3=E9=85=8D=E7=BD=AE=E5=88=B0=20global=20?= =?UTF-8?q?=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/chat/Apps.tsx | 4 +- src/features/Actions/Dance.tsx | 4 +- src/features/Actions/Market.tsx | 4 +- src/features/ChatInfo/Operations/index.tsx | 4 +- src/features/MarketInfo/index.tsx | 4 +- src/panels/PanelContainer.tsx | 6 +- src/store/config/index.ts | 55 +------- src/store/config/initialState.ts | 25 +--- src/store/config/selectors/config.ts | 10 +- src/store/global.ts | 56 --------- src/store/global/index.ts | 138 +++++++++++++++++++++ src/store/global/selectors/panel.ts | 13 ++ 12 files changed, 167 insertions(+), 156 deletions(-) delete mode 100644 src/store/global.ts create mode 100644 src/store/global/index.ts create mode 100644 src/store/global/selectors/panel.ts diff --git a/src/app/chat/Apps.tsx b/src/app/chat/Apps.tsx index 4b6f064c..dfee8570 100644 --- a/src/app/chat/Apps.tsx +++ b/src/app/chat/Apps.tsx @@ -1,5 +1,5 @@ import { DancePanel, MarketPanel } from '@/panels'; -import { useConfigStore } from '@/store/config'; +import { useGlobalStore } from '@/store/global'; import { PanelKey } from '@/types/config'; export const apps = [ @@ -16,7 +16,7 @@ export const apps = [ ]; export default () => { - const [panel] = useConfigStore((s) => [s.panel]); + const [panel] = useGlobalStore((s) => [s.panel]); return ( <> diff --git a/src/features/Actions/Dance.tsx b/src/features/Actions/Dance.tsx index e153baae..9691a1d0 100644 --- a/src/features/Actions/Dance.tsx +++ b/src/features/Actions/Dance.tsx @@ -1,10 +1,10 @@ import { ActionIcon } from '@lobehub/ui'; import { Music2 } from 'lucide-react'; -import { useConfigStore } from '@/store/config'; +import { useGlobalStore } from '@/store/global'; export default () => { - const [openPanel] = useConfigStore((s) => [s.openPanel]); + const [openPanel] = useGlobalStore((s) => [s.openPanel]); return ( { - const openPanel = useConfigStore((s) => s.openPanel); + const openPanel = useGlobalStore((s) => s.openPanel); return ( (({ mobile }) => { - const [openPanel] = useConfigStore((s) => [s.openPanel]); + const [openPanel] = useGlobalStore((s) => [s.openPanel]); const [clearHistory] = useSessionStore((s) => [s.clearHistory]); const items = [ diff --git a/src/features/MarketInfo/index.tsx b/src/features/MarketInfo/index.tsx index 1b9d1d08..fb967267 100644 --- a/src/features/MarketInfo/index.tsx +++ b/src/features/MarketInfo/index.tsx @@ -9,7 +9,7 @@ import AgentCard from '@/components/agent/AgentCard'; import SystemRole from '@/components/agent/SystemRole'; import { SIDEBAR_MAX_WIDTH, SIDEBAR_WIDTH } from '@/constants/token'; import { agentSelectors, useAgentStore } from '@/store/agent'; -import { useConfigStore } from '@/store/config'; +import { useGlobalStore } from '@/store/global'; import { marketStoreSelectors, useMarketStore } from '@/store/market'; import { useSessionStore } from '@/store/session'; @@ -37,7 +37,7 @@ const Header = () => { marketStoreSelectors.currentAgentItem(s), ], ); - const [closePanel] = useConfigStore((s) => [s.closePanel]); + const [closePanel] = useGlobalStore((s) => [s.closePanel]); const [subscribe, unsubscribe, subscribed] = useAgentStore((s) => [ s.subscribe, s.unsubscribe, diff --git a/src/panels/PanelContainer.tsx b/src/panels/PanelContainer.tsx index 213c2726..123bc3c1 100644 --- a/src/panels/PanelContainer.tsx +++ b/src/panels/PanelContainer.tsx @@ -3,7 +3,7 @@ import React, { PropsWithChildren } from 'react'; import Panel from '@/components/Panel'; -import { configSelectors, useConfigStore } from '@/store/config'; +import { globalSelectors, useGlobalStore } from '@/store/global'; import { PanelKey } from '@/types/config'; import { PanelContext } from './PanelContext'; @@ -20,13 +20,13 @@ interface PanelContainerProps { const PanelContainer = (props: PropsWithChildren) => { const { style, className, panelKey, title, children, toolbar, extra, footer } = props; - const [panel, setPanel, focusPanel, closePanel] = useConfigStore((s) => [ + const [panel, setPanel, focusPanel, closePanel] = useGlobalStore((s) => [ s.panel, s.setPanel, s.focusPanel, s.closePanel, ]); - const zIndex = useConfigStore((s) => configSelectors.getPanelZIndex(s, panelKey)); + const zIndex = useGlobalStore((s) => globalSelectors.getPanelZIndex(s, panelKey)); return ( void; - /** - * Focus panel - * @param key - */ - focusPanel: (key: PanelKey) => void; - /** - * Open panel - * @param key - */ - openPanel: (key: PanelKey) => void; /** * Reset config */ @@ -41,36 +26,12 @@ export interface ConfigAction { * @param config */ setOpenAIConfig: (config: Partial) => void; - /** - * Set panel config - * @param panel - * @param config - */ - setPanel: (panel: PanelKey, config: Partial) => void; } export interface ConfigStore extends ConfigState, ConfigAction {} const createStore: StateCreator = (set, get) => ({ ...initialState, - closePanel: (key: PanelKey) => { - const { setPanel, focusList } = get(); - setPanel(key, { open: false }); - const nextSetting = focusList.filter((item) => item !== key); - set({ focusList: nextSetting }); - }, - - focusPanel: (key: PanelKey) => { - const { focusList } = get(); - const nextSetting: PanelKey[] = focusList.filter((item) => item !== key).concat(key); - set({ focusList: nextSetting }); - }, - - openPanel: (key: PanelKey) => { - const { setPanel, focusPanel } = get(); - setPanel(key, { open: true }); - focusPanel(key); - }, resetConfig: () => { localStorage.removeItem(CONFIG_STORAGE_KEY); @@ -89,20 +50,6 @@ const createStore: StateCreator = (s setOpenAIConfig: (config) => { get().setConfig({ languageModel: { openAI: config } }); }, - setPanel: (panel, config) => { - const prevSetting = get().panel[panel]; - const nextSetting = produce(prevSetting, (draftState) => { - merge(draftState, config); - }); - - if (isEqual(prevSetting, nextSetting)) return; - set((state) => ({ - panel: { - ...state.panel, - [panel]: nextSetting, - }, - })); - }, }); export const useConfigStore = createWithEqualityFn()( diff --git a/src/store/config/initialState.ts b/src/store/config/initialState.ts index deb2c39c..ad6f08b0 100644 --- a/src/store/config/initialState.ts +++ b/src/store/config/initialState.ts @@ -1,35 +1,12 @@ import { DEFAULT_SETTINGS } from '@/constants/settings'; -import { INITIAL_COORDINATES } from '@/constants/token'; -import { Config, PanelConfig, PanelKey } from '@/types/config'; +import { Config } from '@/types/config'; export interface ConfigState { config: Config; - focusList: PanelKey[]; - panel: PanelConfig; } const initialState: ConfigState = { config: DEFAULT_SETTINGS, - focusList: [], - - panel: { - agent: { - coordinates: INITIAL_COORDINATES, - open: false, - }, - dance: { - coordinates: INITIAL_COORDINATES, - open: false, - }, - role: { - coordinates: INITIAL_COORDINATES, - open: false, - }, - market: { - coordinates: INITIAL_COORDINATES, - open: false, - }, - }, }; export { initialState }; diff --git a/src/store/config/selectors/config.ts b/src/store/config/selectors/config.ts index 28d4c833..32e15f33 100644 --- a/src/store/config/selectors/config.ts +++ b/src/store/config/selectors/config.ts @@ -1,18 +1,10 @@ -import { INITIAL_Z_INDEX } from '@/constants/token'; import { ConfigStore } from '@/store/config'; -import { OpenAIConfig, PanelKey } from '@/types/config'; +import { OpenAIConfig } from '@/types/config'; const currentOpenAIConfig = (s: ConfigStore): OpenAIConfig | undefined => { return s.config.languageModel.openAI; }; -const getPanelZIndex = (s: ConfigStore, panelKey: PanelKey) => { - const focusList = s.focusList; - const index = focusList.indexOf(panelKey); - return index === -1 ? INITIAL_Z_INDEX : INITIAL_Z_INDEX + index; -}; - export const configSelectors = { currentOpenAIConfig, - getPanelZIndex, }; diff --git a/src/store/global.ts b/src/store/global.ts deleted file mode 100644 index a4c8ebca..00000000 --- a/src/store/global.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { shallow } from 'zustand/shallow'; -import { createWithEqualityFn } from 'zustand/traditional'; - -interface GlobalStore { - setChatDialog: (show: boolean) => void; - setChatSidebar: (show: boolean) => void; - setRoleList: (show: boolean) => void; - setSessionList: (show: boolean) => void; - showChatDialog: boolean; - showChatSidebar: boolean; - showRoleList: boolean; - showSessionList: boolean; - toggleChatDialog: () => void; - toggleChatSideBar: () => void; - toggleRoleList: () => void; - toggleSessionList: () => void; -} - -const initialState = { - showChatSidebar: true, - showSessionList: true, - showChatDialog: true, - showRoleList: true, -}; - -export const useGlobalStore = createWithEqualityFn()( - (set) => ({ - ...initialState, - setChatSidebar: (show) => { - set({ showChatSidebar: show }); - }, - setRoleList: (show) => { - set({ showRoleList: show }); - }, - toggleRoleList: () => { - set((state) => ({ showRoleList: !state.showRoleList })); - }, - toggleChatSideBar: () => { - set((state) => ({ showChatSidebar: !state.showChatSidebar })); - }, - setSessionList: (show) => { - set({ showSessionList: show }); - }, - toggleSessionList: () => { - set((state) => ({ showSessionList: !state.showSessionList })); - }, - - setChatDialog: (show) => { - set({ showChatDialog: show }); - }, - toggleChatDialog: () => { - set((state) => ({ showChatDialog: !state.showChatDialog })); - }, - }), - shallow, -); diff --git a/src/store/global/index.ts b/src/store/global/index.ts new file mode 100644 index 00000000..dc503e1d --- /dev/null +++ b/src/store/global/index.ts @@ -0,0 +1,138 @@ +import { produce } from 'immer'; +import { isEqual, merge } from 'lodash-es'; +import { shallow } from 'zustand/shallow'; +import { createWithEqualityFn } from 'zustand/traditional'; + +import { INITIAL_COORDINATES } from '@/constants/token'; +import { Panel, PanelConfig, PanelKey } from '@/types/config'; + +export * from './selectors/panel'; + +export interface GlobalStore { + /** + * Close panel + * @param key + */ + closePanel: (key: PanelKey) => void; + focusList: PanelKey[]; + /** + * Focus panel + * @param key + */ + focusPanel: (key: PanelKey) => void; + /** + * Open panel + * @param key + */ + openPanel: (key: PanelKey) => void; + panel: PanelConfig; + setChatDialog: (show: boolean) => void; + setChatSidebar: (show: boolean) => void; + /** + * Set panel config + * @param panel + * @param config + */ + setPanel: (panel: PanelKey, config: Partial) => void; + setRoleList: (show: boolean) => void; + setSessionList: (show: boolean) => void; + showChatDialog: boolean; + showChatSidebar: boolean; + showRoleList: boolean; + showSessionList: boolean; + toggleChatDialog: () => void; + toggleChatSideBar: () => void; + toggleRoleList: () => void; + toggleSessionList: () => void; +} + +const initialState = { + showChatSidebar: true, + showSessionList: true, + showChatDialog: true, + showRoleList: true, + focusList: [], + panel: { + agent: { + coordinates: INITIAL_COORDINATES, + open: false, + }, + dance: { + coordinates: INITIAL_COORDINATES, + open: false, + }, + role: { + coordinates: INITIAL_COORDINATES, + open: false, + }, + market: { + coordinates: INITIAL_COORDINATES, + open: false, + }, + }, +}; + +export const useGlobalStore = createWithEqualityFn()( + (set, get) => ({ + ...initialState, + closePanel: (key: PanelKey) => { + const { setPanel, focusList } = get(); + setPanel(key, { open: false }); + const nextSetting = focusList.filter((item) => item !== key); + set({ focusList: nextSetting }); + }, + + focusPanel: (key: PanelKey) => { + const { focusList } = get(); + const nextSetting: PanelKey[] = focusList.filter((item) => item !== key).concat(key); + set({ focusList: nextSetting }); + }, + + openPanel: (key: PanelKey) => { + const { setPanel, focusPanel } = get(); + setPanel(key, { open: true }); + focusPanel(key); + }, + setPanel: (panel, config) => { + const prevSetting = get().panel[panel]; + const nextSetting = produce(prevSetting, (draftState) => { + merge(draftState, config); + }); + + if (isEqual(prevSetting, nextSetting)) return; + set((state) => ({ + panel: { + ...state.panel, + [panel]: nextSetting, + }, + })); + }, + + setChatSidebar: (show) => { + set({ showChatSidebar: show }); + }, + setRoleList: (show) => { + set({ showRoleList: show }); + }, + toggleRoleList: () => { + set((state) => ({ showRoleList: !state.showRoleList })); + }, + toggleChatSideBar: () => { + set((state) => ({ showChatSidebar: !state.showChatSidebar })); + }, + setSessionList: (show) => { + set({ showSessionList: show }); + }, + toggleSessionList: () => { + set((state) => ({ showSessionList: !state.showSessionList })); + }, + + setChatDialog: (show) => { + set({ showChatDialog: show }); + }, + toggleChatDialog: () => { + set((state) => ({ showChatDialog: !state.showChatDialog })); + }, + }), + shallow, +); diff --git a/src/store/global/selectors/panel.ts b/src/store/global/selectors/panel.ts new file mode 100644 index 00000000..aa6fcd82 --- /dev/null +++ b/src/store/global/selectors/panel.ts @@ -0,0 +1,13 @@ +import { INITIAL_Z_INDEX } from '@/constants/token'; +import { GlobalStore } from '@/store/global'; +import { PanelKey } from '@/types/config'; + +const getPanelZIndex = (s: GlobalStore, panelKey: PanelKey) => { + const focusList = s.focusList; + const index = focusList.indexOf(panelKey); + return index === -1 ? INITIAL_Z_INDEX : INITIAL_Z_INDEX + index; +}; + +export const globalSelectors = { + getPanelZIndex, +}; From e1546155fa83dfe347de840a775848da815f054f Mon Sep 17 00:00:00 2001 From: rdmclin2 Date: Wed, 22 May 2024 23:22:10 +0800 Subject: [PATCH 03/11] =?UTF-8?q?:art:=20chore:=20=E9=87=8D=E6=9E=84=20set?= =?UTF-8?q?ting=20=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/welcome/loading.tsx | 7 +- src/components/PageLoading/index.tsx | 4 +- src/constants/settings.ts | 17 ---- src/features/Actions/Token.tsx | 4 +- src/features/Actions/TokenMini.tsx | 4 +- src/features/ChatItem/Error/ApiKeyForm.tsx | 4 +- src/features/Settings/common.tsx | 84 +++---------------- .../features/AvatarWithUpload/index.tsx | 6 +- .../features/BackgroundEffect/index.tsx | 38 +++++++++ .../Settings/features/NickName/index.tsx | 30 +++++++ .../Settings/features/ThemeMode/index.tsx | 38 +++++++++ .../features/ThemeSwatchesPrimary.tsx | 9 +- src/features/Settings/model/openai.tsx | 6 +- src/features/Settings/useSyncSettings.ts | 23 ----- src/layout/Background/index.tsx | 4 +- src/layout/StoreHydration.tsx | 4 +- src/layout/index.tsx | 6 +- src/services/chat.ts | 6 +- src/store/config/index.ts | 67 --------------- src/store/config/initialState.ts | 12 --- src/store/config/selectors/config.ts | 10 --- src/store/session/selectors.ts | 6 +- src/store/theme.ts | 16 ---- src/types/config.ts | 25 +----- 24 files changed, 161 insertions(+), 269 deletions(-) delete mode 100644 src/constants/settings.ts create mode 100644 src/features/Settings/features/BackgroundEffect/index.tsx create mode 100644 src/features/Settings/features/NickName/index.tsx create mode 100644 src/features/Settings/features/ThemeMode/index.tsx delete mode 100644 src/features/Settings/useSyncSettings.ts delete mode 100644 src/store/config/index.ts delete mode 100644 src/store/config/initialState.ts delete mode 100644 src/store/config/selectors/config.ts delete mode 100644 src/store/theme.ts diff --git a/src/app/welcome/loading.tsx b/src/app/welcome/loading.tsx index 136fe6bb..a5d173c7 100644 --- a/src/app/welcome/loading.tsx +++ b/src/app/welcome/loading.tsx @@ -17,7 +17,12 @@ const Loading = () => { const { styles } = useStyles(); return (
- +
); }; diff --git a/src/components/PageLoading/index.tsx b/src/components/PageLoading/index.tsx index 0873482d..2c0a42b3 100644 --- a/src/components/PageLoading/index.tsx +++ b/src/components/PageLoading/index.tsx @@ -5,12 +5,13 @@ import { Center, Flexbox } from 'react-layout-kit'; interface PageLoadingProps { className?: string; + description?: string; style?: React.CSSProperties; title: string; } const PageLoading = (props: PageLoadingProps) => { - const { title, className, style } = props; + const { title, className, style, description } = props; return (
@@ -19,6 +20,7 @@ const PageLoading = (props: PageLoadingProps) => { {title}
+ {description &&
{description}
}
); diff --git a/src/constants/settings.ts b/src/constants/settings.ts deleted file mode 100644 index e218ec52..00000000 --- a/src/constants/settings.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { DEFAULT_USER_AVATAR } from '@/constants/common'; -import { DEFAULT_PRIMARY_COLOR } from '@/constants/theme'; -import { Config } from '@/types/config'; - -export const DEFAULT_SETTINGS: Config = { - backgroundEffect: 'glow', - languageModel: { - openAI: { - apikey: '', - endpoint: '', - model: 'gpt-3.5-turbo', - }, - }, - primaryColor: DEFAULT_PRIMARY_COLOR, - avatar: DEFAULT_USER_AVATAR, - nickName: '', -}; diff --git a/src/features/Actions/Token.tsx b/src/features/Actions/Token.tsx index 92c28232..f73b0967 100644 --- a/src/features/Actions/Token.tsx +++ b/src/features/Actions/Token.tsx @@ -3,10 +3,10 @@ import { isEqual } from 'lodash-es'; import { OPENAI_MODEL_LIST } from '@/constants/openai'; import { useCalculateToken } from '@/hooks/useCalculateToken'; -import { configSelectors, useConfigStore } from '@/store/config'; +import { configSelectors, useSettingStore } from '@/store/setting'; const Token = () => { - const config = useConfigStore((s) => configSelectors.currentOpenAIConfig(s), isEqual); + const config = useSettingStore((s) => configSelectors.currentOpenAIConfig(s), isEqual); const usedTokens = useCalculateToken(); return ( diff --git a/src/features/Actions/TokenMini.tsx b/src/features/Actions/TokenMini.tsx index 1489ccbf..46183b1f 100644 --- a/src/features/Actions/TokenMini.tsx +++ b/src/features/Actions/TokenMini.tsx @@ -3,10 +3,10 @@ import { isEqual } from 'lodash-es'; import { OPENAI_MODEL_LIST } from '@/constants/openai'; import { useCalculateToken } from '@/hooks/useCalculateToken'; -import { configSelectors, useConfigStore } from '@/store/config'; +import { configSelectors, useSettingStore } from '@/store/setting'; const TokenMini = () => { - const config = useConfigStore((s) => configSelectors.currentOpenAIConfig(s), isEqual); + const config = useSettingStore((s) => configSelectors.currentOpenAIConfig(s), isEqual); const usedTokens = useCalculateToken(); const maxValue = OPENAI_MODEL_LIST.find((item) => item.name === config?.model)?.maxToken || 4096; diff --git a/src/features/ChatItem/Error/ApiKeyForm.tsx b/src/features/ChatItem/Error/ApiKeyForm.tsx index 19bf7c2a..a6e1b17f 100644 --- a/src/features/ChatItem/Error/ApiKeyForm.tsx +++ b/src/features/ChatItem/Error/ApiKeyForm.tsx @@ -4,8 +4,8 @@ import { Network } from 'lucide-react'; import { memo, useState } from 'react'; import { Center, Flexbox } from 'react-layout-kit'; -import { configSelectors, useConfigStore } from '@/store/config'; import { useSessionStore } from '@/store/session'; +import { configSelectors, useSettingStore } from '@/store/setting'; import { FormAction } from './style'; @@ -16,7 +16,7 @@ interface APIKeyFormProps { const APIKeyForm = ({ id }: APIKeyFormProps) => { const [showProxy, setShow] = useState(false); - const [currentOpenAIConfig, setConfig] = useConfigStore((s) => [ + const [currentOpenAIConfig, setConfig] = useSettingStore((s) => [ configSelectors.currentOpenAIConfig(s), s.setOpenAIConfig, ]); diff --git a/src/features/Settings/common.tsx b/src/features/Settings/common.tsx index 482ab053..5d1c667e 100644 --- a/src/features/Settings/common.tsx +++ b/src/features/Settings/common.tsx @@ -1,19 +1,17 @@ import { Form, FormGroup, FormItem } from '@lobehub/ui'; -import { App, Button, Input, Segmented } from 'antd'; -import { ThemeMode, createStyles } from 'antd-style'; +import { App, Button } from 'antd'; +import { createStyles } from 'antd-style'; import classNames from 'classnames'; -import { isEqual } from 'lodash-es'; import { Monitor, Settings2, User2Icon } from 'lucide-react'; import React from 'react'; -import { MAX_NAME_LENGTH } from '@/constants/common'; +import BackgroundEffect from '@/features/Settings/features/BackgroundEffect'; +import NickName from '@/features/Settings/features/NickName'; +import ThemeMode from '@/features/Settings/features/ThemeMode'; import ThemeSwatchesPrimary from '@/features/Settings/features/ThemeSwatchesPrimary'; -import { useSyncSettings } from '@/features/Settings/useSyncSettings'; import { useAgentStore } from '@/store/agent'; -import { useConfigStore } from '@/store/config'; import { useSessionStore } from '@/store/session'; -import { useThemeStore } from '@/store/theme'; -import { BackgroundEffect } from '@/types/config'; +import { useSettingStore } from '@/store/setting'; import AvatarWithUpload from './features/AvatarWithUpload'; @@ -37,17 +35,11 @@ const useStyles = createStyles(({ css }) => ({ const CommonConfig = (props: CommonConfigProps) => { const { style, className } = props; const { styles } = useStyles(); - const [config, setConfig] = useConfigStore((s) => [s.config, s.setConfig], isEqual); const clearAgentStorage = useAgentStore((s) => s.clearAgentStorage); - const [themeMode, setThemeMode] = useThemeStore((s) => [s.themeMode, s.setThemeMode]); const clearSessions = useSessionStore((s) => s.clearSessions); - const resetConfig = useConfigStore((s) => s.resetConfig); + const resetConfig = useSettingStore((s) => s.resetConfig); const { message, modal } = App.useApp(); - const [form] = Form.useForm(); - - useSyncSettings(form); - const handleClear = () => { modal.confirm({ cancelText: '取消', @@ -85,26 +77,13 @@ const CommonConfig = (props: CommonConfigProps) => { return (
-
+ - { - setConfig({ nickName: e.target.value }); - }} - /> + @@ -112,49 +91,10 @@ const CommonConfig = (props: CommonConfigProps) => { - { - setThemeMode(value as ThemeMode); - }} - options={[ - { - label: '🔆 亮色模式', - value: 'light', - }, - { - label: '🌙 暗色模式', - value: 'dark', - }, - { - label: '💻 跟随系统', - value: 'auto', - }, - ]} - /> + - - { - setConfig({ backgroundEffect: value }); - }} - options={[ - { - label: '光辉', - value: 'glow', - }, - { - label: '无背景', - value: 'none', - }, - ]} - /> + + diff --git a/src/features/Settings/features/AvatarWithUpload/index.tsx b/src/features/Settings/features/AvatarWithUpload/index.tsx index 421d525c..7d39d072 100644 --- a/src/features/Settings/features/AvatarWithUpload/index.tsx +++ b/src/features/Settings/features/AvatarWithUpload/index.tsx @@ -4,7 +4,7 @@ import NextImage from 'next/image'; import { CSSProperties, memo, useCallback } from 'react'; import { DEFAULT_USER_AVATAR_URL } from '@/constants/common'; -import { useConfigStore } from '@/store/config'; +import { useSettingStore } from '@/store/setting'; import { createUploadImageHandler } from '@/utils/common'; import { imageToBase64 } from '@/utils/imageToBase64'; @@ -37,7 +37,7 @@ interface AvatarWithUploadProps { const AvatarWithUpload = memo( ({ size = 40, compressSize = 256, style, id }) => { const { styles } = useStyle(); - const [avatar, setConfig] = useConfigStore((s) => [s.config.avatar, s.setConfig]); + const [avatar, setAvatar] = useSettingStore((s) => [s.avatar, s.setAvatar]); const handleUploadAvatar = useCallback( createUploadImageHandler((avatar) => { @@ -45,7 +45,7 @@ const AvatarWithUpload = memo( img.src = avatar; img.addEventListener('load', () => { const webpBase64 = imageToBase64({ img, size: compressSize }); - setConfig({ avatar: webpBase64 }); + setAvatar(webpBase64); }); }), [], diff --git a/src/features/Settings/features/BackgroundEffect/index.tsx b/src/features/Settings/features/BackgroundEffect/index.tsx new file mode 100644 index 00000000..8597e607 --- /dev/null +++ b/src/features/Settings/features/BackgroundEffect/index.tsx @@ -0,0 +1,38 @@ +import { Segmented } from 'antd'; +import { isEqual } from 'lodash-es'; +import React, { CSSProperties, memo } from 'react'; + +import { useSettingStore } from '@/store/setting'; +import { BackgroundEffect } from '@/types/config'; + +interface Props { + style?: CSSProperties; +} + +export default memo((props) => { + const { style } = props; + const [backgroundEffect, setBackgroundEffect] = useSettingStore( + (s) => [s.backgroundEffect, s.setBackgroundEffect], + isEqual, + ); + + return ( + { + setBackgroundEffect(value); + }} + options={[ + { + label: '光辉', + value: 'glow', + }, + { + label: '无背景', + value: 'none', + }, + ]} + /> + ); +}); diff --git a/src/features/Settings/features/NickName/index.tsx b/src/features/Settings/features/NickName/index.tsx new file mode 100644 index 00000000..2bc22f25 --- /dev/null +++ b/src/features/Settings/features/NickName/index.tsx @@ -0,0 +1,30 @@ +import { Input } from 'antd'; +import { isEqual } from 'lodash-es'; +import React, { CSSProperties, memo } from 'react'; + +import { MAX_NAME_LENGTH } from '@/constants/common'; +import { useSettingStore } from '@/store/setting'; + +interface Props { + style?: CSSProperties; +} + +const NickName = memo((props) => { + const { style } = props; + const [nickName, setNickName] = useSettingStore((s) => [s.nickName, s.setNickName], isEqual); + + return ( + { + setNickName(e.target.value); + }} + /> + ); +}); + +export default NickName; diff --git a/src/features/Settings/features/ThemeMode/index.tsx b/src/features/Settings/features/ThemeMode/index.tsx new file mode 100644 index 00000000..ab685b59 --- /dev/null +++ b/src/features/Settings/features/ThemeMode/index.tsx @@ -0,0 +1,38 @@ +import { Segmented } from 'antd'; +import { ThemeMode } from 'antd-style'; +import React, { CSSProperties, memo } from 'react'; + +import { useSettingStore } from '@/store/setting'; + +interface Props { + style?: CSSProperties; +} + +export default memo((props) => { + const { style } = props; + const [themeMode, setThemeMode] = useSettingStore((s) => [s.themeMode, s.setThemeMode]); + + return ( + { + setThemeMode(value as ThemeMode); + }} + options={[ + { + label: '🔆 亮色模式', + value: 'light', + }, + { + label: '🌙 暗色模式', + value: 'dark', + }, + { + label: '💻 跟随系统', + value: 'auto', + }, + ]} + /> + ); +}); diff --git a/src/features/Settings/features/ThemeSwatchesPrimary.tsx b/src/features/Settings/features/ThemeSwatchesPrimary.tsx index 026474bc..eae2a02c 100644 --- a/src/features/Settings/features/ThemeSwatchesPrimary.tsx +++ b/src/features/Settings/features/ThemeSwatchesPrimary.tsx @@ -7,14 +7,17 @@ import { } from '@lobehub/ui'; import { memo } from 'react'; -import { useConfigStore } from '@/store/config'; +import { useSettingStore } from '@/store/setting'; const ThemeSwatchesPrimary = memo(() => { - const [primaryColor, setConfig] = useConfigStore((s) => [s.config.primaryColor, s.setConfig]); + const [primaryColor, setPrimaryColor] = useSettingStore((s) => [ + s.primaryColor, + s.setPrimaryColor, + ]); const handleSelect = (v: any) => { const name = findCustomThemeName('primary', v) as PrimaryColors; - setConfig({ primaryColor: name || '' }); + setPrimaryColor(name || ''); }; return ( diff --git a/src/features/Settings/model/openai.tsx b/src/features/Settings/model/openai.tsx index c55605ea..8a4676d9 100644 --- a/src/features/Settings/model/openai.tsx +++ b/src/features/Settings/model/openai.tsx @@ -9,7 +9,7 @@ import React, { useEffect } from 'react'; import { OPENAI_MODEL_LIST } from '@/constants/openai'; import { chatCompletion } from '@/services/chat'; -import { configSelectors, useConfigStore } from '@/store/config'; +import { configSelectors, useSettingStore } from '@/store/setting'; import { ChatMessage } from '@/types/chat'; interface ConfigProps { @@ -29,8 +29,8 @@ const Config = (props: ConfigProps) => { const { style, className } = props; const { styles } = useStyles(); const [form] = AForm.useForm(); - const openAIConfig = useConfigStore((s) => configSelectors.currentOpenAIConfig(s), isEqual); - const setOpenAIConfig = useConfigStore((s) => s.setOpenAIConfig); + const openAIConfig = useSettingStore((s) => configSelectors.currentOpenAIConfig(s), isEqual); + const setOpenAIConfig = useSettingStore((s) => s.setOpenAIConfig); useEffect(() => { form.setFieldsValue(openAIConfig); diff --git a/src/features/Settings/useSyncSettings.ts b/src/features/Settings/useSyncSettings.ts deleted file mode 100644 index c74d7179..00000000 --- a/src/features/Settings/useSyncSettings.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { FormInstance } from 'antd/es/form/hooks/useForm'; -import { useEffect } from 'react'; - -import { useConfigStore } from '@/store/config'; - -export const useSyncSettings = (form: FormInstance) => { - useEffect(() => { - // set the first time - form.setFieldsValue(useConfigStore.getState().config); - - // sync with later updated settings - const unsubscribe = useConfigStore.subscribe( - (s) => s.config, - (config) => { - form.setFieldsValue(config); - }, - ); - - return () => { - unsubscribe(); - }; - }, []); -}; diff --git a/src/layout/Background/index.tsx b/src/layout/Background/index.tsx index d9fe0dff..0b8f67e9 100644 --- a/src/layout/Background/index.tsx +++ b/src/layout/Background/index.tsx @@ -1,10 +1,10 @@ -import { useConfigStore } from '@/store/config'; +import { useSettingStore } from '@/store/setting'; import { useStyles } from './style'; const Background = () => { const { styles } = useStyles(); - const backgroundEffect = useConfigStore((s) => s.config.backgroundEffect); + const backgroundEffect = useSettingStore((s) => s.backgroundEffect); return backgroundEffect === 'glow' ?
: null; }; diff --git a/src/layout/StoreHydration.tsx b/src/layout/StoreHydration.tsx index f508a95a..e6f0b165 100644 --- a/src/layout/StoreHydration.tsx +++ b/src/layout/StoreHydration.tsx @@ -1,8 +1,8 @@ import { useRouter } from 'next/navigation'; import { memo, useEffect } from 'react'; -import { useConfigStore } from '@/store/config'; import { useSessionStore } from '@/store/session'; +import { useSettingStore } from '@/store/setting'; const StoreHydration = () => { const router = useRouter(); @@ -10,7 +10,7 @@ const StoreHydration = () => { useEffect(() => { // refs: https://github.com/pmndrs/zustand/blob/main/docs/integrations/persisting-store-data.md#hashydrated useSessionStore.persist.rehydrate(); - useConfigStore.persist.rehydrate(); + useSettingStore.persist.rehydrate(); }, []); useEffect(() => { diff --git a/src/layout/index.tsx b/src/layout/index.tsx index d499fdbb..32c0d6a2 100644 --- a/src/layout/index.tsx +++ b/src/layout/index.tsx @@ -6,8 +6,7 @@ import { ReactNode } from 'react'; import { VIDOL_THEME_APPEARANCE } from '@/constants/theme'; import StyleRegistry from '@/layout/StyleRegistry'; -import { useConfigStore } from '@/store/config'; -import { useThemeStore } from '@/store/theme'; +import { useSettingStore } from '@/store/setting'; import { GlobalStyle } from '@/styles'; import { setCookie } from '@/utils/cookie'; @@ -32,8 +31,7 @@ const useStyles = createStyles(({ css }) => ({ const Layout = (props: LayoutProps) => { const { children, defaultAppearance } = props; const { styles } = useStyles(); - const themeMode = useThemeStore((s) => s.themeMode); - const [primaryColor] = useConfigStore((s) => [s.config.primaryColor]); + const [primaryColor, themeMode] = useSettingStore((s) => [s.primaryColor, s.themeMode]); return ( diff --git a/src/services/chat.ts b/src/services/chat.ts index 12132a79..fea99d03 100644 --- a/src/services/chat.ts +++ b/src/services/chat.ts @@ -1,13 +1,13 @@ import { OPENAI_API_KEY, OPENAI_END_POINT } from '@/constants/openai'; import { speakCharacter } from '@/features/messages/speakCharacter'; -import { configSelectors, useConfigStore } from '@/store/config'; import { sessionSelectors, useSessionStore } from '@/store/session'; +import { configSelectors, useSettingStore } from '@/store/setting'; import { useViewerStore } from '@/store/viewer'; import { ChatMessage } from '@/types/chat'; import { ChatStreamPayload } from '@/types/openai/chat'; const createHeader = (header?: any) => { - const config = configSelectors.currentOpenAIConfig(useConfigStore.getState()); + const config = configSelectors.currentOpenAIConfig(useSettingStore.getState()); return { 'Content-Type': 'application/json', [OPENAI_API_KEY]: config?.apikey || '', @@ -21,7 +21,7 @@ interface ChatCompletionPayload extends Partial { - const config = configSelectors.currentOpenAIConfig(useConfigStore.getState()); + const config = configSelectors.currentOpenAIConfig(useSettingStore.getState()); const { messages } = payload; const postMessages = messages.map((m) => ({ content: m.content, role: m.role })); diff --git a/src/store/config/index.ts b/src/store/config/index.ts deleted file mode 100644 index 89e73bdd..00000000 --- a/src/store/config/index.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { produce } from 'immer'; -import { isEqual, merge } from 'lodash-es'; -import { devtools, persist, subscribeWithSelector } from 'zustand/middleware'; -import { shallow } from 'zustand/shallow'; -import { createWithEqualityFn } from 'zustand/traditional'; -import { StateCreator } from 'zustand/vanilla'; - -import { Config } from '@/types/config'; - -import { ConfigState, initialState } from './initialState'; - -const CONFIG_STORAGE_KEY = 'vidol-chat-config-storage'; - -export interface ConfigAction { - /** - * Reset config - */ - resetConfig: () => void; - /** - * Set config - * @param config - */ - setConfig: (config: Partial) => void; - /** - * Set OpenAI config - * @param config - */ - setOpenAIConfig: (config: Partial) => void; -} - -export interface ConfigStore extends ConfigState, ConfigAction {} - -const createStore: StateCreator = (set, get) => ({ - ...initialState, - - resetConfig: () => { - localStorage.removeItem(CONFIG_STORAGE_KEY); - set({ ...initialState }); - }, - - setConfig: (config) => { - const prevSetting = get().config; - const nextSetting = produce(prevSetting, (draftState) => { - merge(draftState, config); - }); - if (isEqual(prevSetting, nextSetting)) return; - set({ config: nextSetting }); - }, - - setOpenAIConfig: (config) => { - get().setConfig({ languageModel: { openAI: config } }); - }, -}); - -export const useConfigStore = createWithEqualityFn()( - subscribeWithSelector( - persist( - devtools(createStore, { - name: 'VIDOL_CONFIG_STORE', - }), - { name: CONFIG_STORAGE_KEY }, - ), - ), - shallow, -); - -export { configSelectors } from './selectors/config'; diff --git a/src/store/config/initialState.ts b/src/store/config/initialState.ts deleted file mode 100644 index ad6f08b0..00000000 --- a/src/store/config/initialState.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { DEFAULT_SETTINGS } from '@/constants/settings'; -import { Config } from '@/types/config'; - -export interface ConfigState { - config: Config; -} - -const initialState: ConfigState = { - config: DEFAULT_SETTINGS, -}; - -export { initialState }; diff --git a/src/store/config/selectors/config.ts b/src/store/config/selectors/config.ts deleted file mode 100644 index 32e15f33..00000000 --- a/src/store/config/selectors/config.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { ConfigStore } from '@/store/config'; -import { OpenAIConfig } from '@/types/config'; - -const currentOpenAIConfig = (s: ConfigStore): OpenAIConfig | undefined => { - return s.config.languageModel.openAI; -}; - -export const configSelectors = { - currentOpenAIConfig, -}; diff --git a/src/store/session/selectors.ts b/src/store/session/selectors.ts index 79c82e53..ab33c8e9 100644 --- a/src/store/session/selectors.ts +++ b/src/store/session/selectors.ts @@ -1,7 +1,7 @@ import { LOBE_VIDOL_DEFAULT_AGENT_ID } from '@/constants/agent'; import { DEFAULT_USER_AVATAR } from '@/constants/common'; import { useAgentStore } from '@/store/agent'; -import { useConfigStore } from '@/store/config'; +import { useSettingStore } from '@/store/setting'; import { Agent } from '@/types/agent'; import { ChatMessage } from '@/types/chat'; import { Session } from '@/types/session'; @@ -63,8 +63,8 @@ const currentChats = (s: SessionStore): ChatMessage[] => { const { messages } = session; return messages?.map((message) => { - const userAvatar = useConfigStore.getState().config.avatar; - const userNickName = useConfigStore.getState().config.nickName; + const userAvatar = useSettingStore.getState().avatar; + const userNickName = useSettingStore.getState().nickName; return { ...message, meta: { diff --git a/src/store/theme.ts b/src/store/theme.ts deleted file mode 100644 index 6f6222e1..00000000 --- a/src/store/theme.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { ThemeMode } from 'antd-style'; -import { shallow } from 'zustand/shallow'; -import { createWithEqualityFn } from 'zustand/traditional'; - -interface ThemeStore { - setThemeMode: (themeMode: ThemeMode) => void; - themeMode: ThemeMode; -} - -export const useThemeStore = createWithEqualityFn()( - (set) => ({ - setThemeMode: (themeMode: ThemeMode) => set({ themeMode }), - themeMode: 'auto' as ThemeMode, - }), - shallow, -); diff --git a/src/types/config.ts b/src/types/config.ts index 98a63cef..88efbb5d 100644 --- a/src/types/config.ts +++ b/src/types/config.ts @@ -1,5 +1,4 @@ import { Coordinates } from '@dnd-kit/utilities'; -import { PrimaryColors } from '@lobehub/ui'; export type BackgroundEffect = 'glow' | 'none'; @@ -23,25 +22,6 @@ export interface PanelConfig { export type PanelKey = keyof PanelConfig; -export interface CommonConfig { - /** - * 用户头像 - */ - avatar: string; - /** - * 背景类型 - */ - backgroundEffect: BackgroundEffect; - /** - * 用户昵称 - */ - nickName: string; - /** - * 主题色 - */ - primaryColor: PrimaryColors; -} - export interface OpenAIConfig { apikey?: string; endpoint?: string; @@ -52,6 +32,9 @@ export interface LanguageModelConfig { openAI: OpenAIConfig; } -export interface Config extends CommonConfig { +export interface Config { + /** + * 语言模型配置 + */ languageModel: LanguageModelConfig; } From 7ccca77e8641f58b00337bda74cdbf675273aac7 Mon Sep 17 00:00:00 2001 From: rdmclin2 Date: Thu, 23 May 2024 00:12:11 +0800 Subject: [PATCH 04/11] :art: fix: config data lost --- .../features/AvatarWithUpload/index.tsx | 2 +- .../features/BackgroundEffect/index.tsx | 2 +- .../Settings/features/NickName/index.tsx | 7 ++++-- .../Settings/features/ThemeMode/index.tsx | 2 +- .../features/ThemeSwatchesPrimary.tsx | 2 +- src/layout/Background/index.tsx | 2 +- src/layout/index.tsx | 5 +++- src/store/session/selectors.ts | 4 +-- src/types/config.ts | 25 ++++++++++++++++++- 9 files changed, 40 insertions(+), 11 deletions(-) diff --git a/src/features/Settings/features/AvatarWithUpload/index.tsx b/src/features/Settings/features/AvatarWithUpload/index.tsx index 7d39d072..06ef3552 100644 --- a/src/features/Settings/features/AvatarWithUpload/index.tsx +++ b/src/features/Settings/features/AvatarWithUpload/index.tsx @@ -37,7 +37,7 @@ interface AvatarWithUploadProps { const AvatarWithUpload = memo( ({ size = 40, compressSize = 256, style, id }) => { const { styles } = useStyle(); - const [avatar, setAvatar] = useSettingStore((s) => [s.avatar, s.setAvatar]); + const [avatar, setAvatar] = useSettingStore((s) => [s.config.avatar, s.setAvatar]); const handleUploadAvatar = useCallback( createUploadImageHandler((avatar) => { diff --git a/src/features/Settings/features/BackgroundEffect/index.tsx b/src/features/Settings/features/BackgroundEffect/index.tsx index 8597e607..76b7eb8d 100644 --- a/src/features/Settings/features/BackgroundEffect/index.tsx +++ b/src/features/Settings/features/BackgroundEffect/index.tsx @@ -12,7 +12,7 @@ interface Props { export default memo((props) => { const { style } = props; const [backgroundEffect, setBackgroundEffect] = useSettingStore( - (s) => [s.backgroundEffect, s.setBackgroundEffect], + (s) => [s.config.backgroundEffect, s.setBackgroundEffect], isEqual, ); diff --git a/src/features/Settings/features/NickName/index.tsx b/src/features/Settings/features/NickName/index.tsx index 2bc22f25..3364ea56 100644 --- a/src/features/Settings/features/NickName/index.tsx +++ b/src/features/Settings/features/NickName/index.tsx @@ -11,12 +11,15 @@ interface Props { const NickName = memo((props) => { const { style } = props; - const [nickName, setNickName] = useSettingStore((s) => [s.nickName, s.setNickName], isEqual); + const [nickName, setNickName] = useSettingStore( + (s) => [s.config.nickName, s.setNickName], + isEqual, + ); return ( ((props) => { const { style } = props; - const [themeMode, setThemeMode] = useSettingStore((s) => [s.themeMode, s.setThemeMode]); + const [themeMode, setThemeMode] = useSettingStore((s) => [s.config.themeMode, s.setThemeMode]); return ( { const [primaryColor, setPrimaryColor] = useSettingStore((s) => [ - s.primaryColor, + s.config.primaryColor, s.setPrimaryColor, ]); diff --git a/src/layout/Background/index.tsx b/src/layout/Background/index.tsx index 0b8f67e9..fcba83a3 100644 --- a/src/layout/Background/index.tsx +++ b/src/layout/Background/index.tsx @@ -4,7 +4,7 @@ import { useStyles } from './style'; const Background = () => { const { styles } = useStyles(); - const backgroundEffect = useSettingStore((s) => s.backgroundEffect); + const backgroundEffect = useSettingStore((s) => s.config.backgroundEffect); return backgroundEffect === 'glow' ?
: null; }; diff --git a/src/layout/index.tsx b/src/layout/index.tsx index 32c0d6a2..bbd8d39b 100644 --- a/src/layout/index.tsx +++ b/src/layout/index.tsx @@ -31,7 +31,10 @@ const useStyles = createStyles(({ css }) => ({ const Layout = (props: LayoutProps) => { const { children, defaultAppearance } = props; const { styles } = useStyles(); - const [primaryColor, themeMode] = useSettingStore((s) => [s.primaryColor, s.themeMode]); + const [primaryColor, themeMode] = useSettingStore((s) => [ + s.config.primaryColor, + s.config.themeMode, + ]); return ( diff --git a/src/store/session/selectors.ts b/src/store/session/selectors.ts index ab33c8e9..8d59749b 100644 --- a/src/store/session/selectors.ts +++ b/src/store/session/selectors.ts @@ -63,8 +63,8 @@ const currentChats = (s: SessionStore): ChatMessage[] => { const { messages } = session; return messages?.map((message) => { - const userAvatar = useSettingStore.getState().avatar; - const userNickName = useSettingStore.getState().nickName; + const userAvatar = useSettingStore.getState().config.avatar; + const userNickName = useSettingStore.getState().config.nickName; return { ...message, meta: { diff --git a/src/types/config.ts b/src/types/config.ts index 88efbb5d..879496e2 100644 --- a/src/types/config.ts +++ b/src/types/config.ts @@ -1,4 +1,6 @@ import { Coordinates } from '@dnd-kit/utilities'; +import { PrimaryColors } from '@lobehub/ui'; +import type { ThemeMode } from 'antd-style'; export type BackgroundEffect = 'glow' | 'none'; @@ -32,9 +34,30 @@ export interface LanguageModelConfig { openAI: OpenAIConfig; } -export interface Config { +export interface UserConfig { + /** + * 用户头像 + */ + avatar?: string; + /** + * 背景类型 + */ + backgroundEffect: BackgroundEffect; + /** * 语言模型配置 */ languageModel: LanguageModelConfig; + /** + * 用户昵称 + */ + nickName: string; + /** + * 主题色 + */ + primaryColor?: PrimaryColors; + /** + * 主题模式 + */ + themeMode: ThemeMode; } From ac7a337c7341b9d78c1a4ef786ba0d73dfa368c9 Mon Sep 17 00:00:00 2001 From: rdmclin2 Date: Thu, 23 May 2024 01:37:37 +0800 Subject: [PATCH 05/11] =?UTF-8?q?:art:=20chore:=20=E5=8F=82=E8=80=83=20lob?= =?UTF-8?q?e=20=E8=AE=BE=E7=BD=AE=20app=20theme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/layout.tsx | 6 +-- src/layout/AppTheme.tsx | 78 +++++++++++++++++++++++++++++++++++ src/layout/StoreHydration.tsx | 5 +++ src/layout/index.tsx | 62 +++++++++++----------------- src/types/config.ts | 6 ++- 5 files changed, 112 insertions(+), 45 deletions(-) create mode 100644 src/layout/AppTheme.tsx diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 44eea653..8cd5742d 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,22 +1,18 @@ import { Analytics } from '@vercel/analytics/react'; -import { cookies } from 'next/headers'; import { PropsWithChildren } from 'react'; -import { VIDOL_THEME_APPEARANCE } from '@/constants/theme'; import Layout from '@/layout'; import StyleRegistry from './StyleRegistry'; const RootLayout = ({ children }: PropsWithChildren) => { // get default theme config to use with ssr - const cookieStore = cookies(); - const appearance = cookieStore.get(VIDOL_THEME_APPEARANCE); return ( - {children} + {children} diff --git a/src/layout/AppTheme.tsx b/src/layout/AppTheme.tsx new file mode 100644 index 00000000..f344b64a --- /dev/null +++ b/src/layout/AppTheme.tsx @@ -0,0 +1,78 @@ +'use client'; + +import { NeutralColors, PrimaryColors, ThemeProvider } from '@lobehub/ui'; +import { ThemeAppearance, createStyles } from 'antd-style'; +import { ReactNode, memo, useEffect } from 'react'; + +import { + VIDOL_THEME_APPEARANCE, + VIDOL_THEME_NEUTRAL_COLOR, + VIDOL_THEME_PRIMARY_COLOR, +} from '@/constants/theme'; +import { useSettingStore } from '@/store/setting'; +import { GlobalStyle } from '@/styles'; +import { setCookie } from '@/utils/cookie'; + +export interface LayoutProps { + children?: ReactNode; + defaultAppearance?: ThemeAppearance; + defaultPrimaryColor?: PrimaryColors; +} + +export interface AppThemeProps { + children?: ReactNode; + defaultAppearance?: ThemeAppearance; + defaultNeutralColor?: NeutralColors; + defaultPrimaryColor?: PrimaryColors; +} + +const useStyles = createStyles(({ css }) => ({ + content: css` + overflow-y: hidden; + display: flex; + flex-direction: column; + align-items: center; + + height: 100%; + `, +})); + +const AppTheme = memo((props: AppThemeProps) => { + const { children, defaultAppearance, defaultNeutralColor, defaultPrimaryColor } = props; + const [primaryColor, neutralColor, themeMode] = useSettingStore((s) => [ + s.config.primaryColor, + s.config.neutralColor, + s.config.themeMode, + ]); + + const { styles } = useStyles(); + + useEffect(() => { + console.log('primaryColor', primaryColor); + setCookie(VIDOL_THEME_PRIMARY_COLOR, primaryColor); + }, [primaryColor]); + + useEffect(() => { + setCookie(VIDOL_THEME_NEUTRAL_COLOR, neutralColor); + }, [neutralColor]); + + return ( + { + console.log('onAppearanceChange', appearance); + setCookie(VIDOL_THEME_APPEARANCE, appearance); + }} + themeMode={themeMode} + > + +
{children}
+
+ ); +}); + +export default AppTheme; diff --git a/src/layout/StoreHydration.tsx b/src/layout/StoreHydration.tsx index e6f0b165..803485b8 100644 --- a/src/layout/StoreHydration.tsx +++ b/src/layout/StoreHydration.tsx @@ -1,3 +1,5 @@ +'use client'; + import { useRouter } from 'next/navigation'; import { memo, useEffect } from 'react'; @@ -15,6 +17,9 @@ const StoreHydration = () => { useEffect(() => { router.prefetch('/chat'); + router.prefetch('/settings'); + router.prefetch('/role'); + router.prefetch('/market'); }, [router]); return null; }; diff --git a/src/layout/index.tsx b/src/layout/index.tsx index bbd8d39b..32309034 100644 --- a/src/layout/index.tsx +++ b/src/layout/index.tsx @@ -1,57 +1,41 @@ -'use client'; - -import { ThemeProvider } from '@lobehub/ui'; -import { ThemeAppearance, createStyles } from 'antd-style'; +import { PrimaryColors } from '@lobehub/ui'; +import { ThemeAppearance } from 'antd-style'; +import { cookies } from 'next/headers'; import { ReactNode } from 'react'; -import { VIDOL_THEME_APPEARANCE } from '@/constants/theme'; +import { + VIDOL_THEME_APPEARANCE, + VIDOL_THEME_NEUTRAL_COLOR, + VIDOL_THEME_PRIMARY_COLOR, +} from '@/constants/theme'; +import AppTheme from '@/layout/AppTheme'; +import StoreHydration from '@/layout/StoreHydration'; import StyleRegistry from '@/layout/StyleRegistry'; -import { useSettingStore } from '@/store/setting'; -import { GlobalStyle } from '@/styles'; -import { setCookie } from '@/utils/cookie'; - -import StoreHydration from './StoreHydration'; export interface LayoutProps { children?: ReactNode; defaultAppearance?: ThemeAppearance; + defaultPrimaryColor?: PrimaryColors; } -const useStyles = createStyles(({ css }) => ({ - content: css` - overflow-y: hidden; - display: flex; - flex-direction: column; - align-items: center; - - height: 100%; - `, -})); - const Layout = (props: LayoutProps) => { - const { children, defaultAppearance } = props; - const { styles } = useStyles(); - const [primaryColor, themeMode] = useSettingStore((s) => [ - s.config.primaryColor, - s.config.themeMode, - ]); + const { children } = props; + + const cookieStore = cookies(); + const appearance = cookieStore.get(VIDOL_THEME_APPEARANCE); + const primaryColor = cookieStore.get(VIDOL_THEME_PRIMARY_COLOR); + const neutralColor = cookieStore.get(VIDOL_THEME_NEUTRAL_COLOR); return ( - { - setCookie(VIDOL_THEME_APPEARANCE, appearance); - }} - themeMode={themeMode} + - -
{children}
-
+ {children} +
); }; diff --git a/src/types/config.ts b/src/types/config.ts index 879496e2..f8ce37ac 100644 --- a/src/types/config.ts +++ b/src/types/config.ts @@ -1,5 +1,5 @@ import { Coordinates } from '@dnd-kit/utilities'; -import { PrimaryColors } from '@lobehub/ui'; +import { NeutralColors, PrimaryColors } from '@lobehub/ui'; import type { ThemeMode } from 'antd-style'; export type BackgroundEffect = 'glow' | 'none'; @@ -48,6 +48,10 @@ export interface UserConfig { * 语言模型配置 */ languageModel: LanguageModelConfig; + /** + * 中性色 + */ + neutralColor?: NeutralColors; /** * 用户昵称 */ From 6242abde7e3e0284d669e50a2d8533923334c16f Mon Sep 17 00:00:00 2001 From: rdmclin2 Date: Thu, 23 May 2024 01:38:01 +0800 Subject: [PATCH 06/11] =?UTF-8?q?:art:=20chore:=20=E5=8F=82=E8=80=83=20lob?= =?UTF-8?q?e=20=E8=AE=BE=E7=BD=AE=20app=20theme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/store/setting/index.ts | 109 ++++++++++++++++++++++++++ src/store/setting/initialState.ts | 24 ++++++ src/store/setting/selectors/config.ts | 10 +++ 3 files changed, 143 insertions(+) create mode 100644 src/store/setting/index.ts create mode 100644 src/store/setting/initialState.ts create mode 100644 src/store/setting/selectors/config.ts diff --git a/src/store/setting/index.ts b/src/store/setting/index.ts new file mode 100644 index 00000000..2ad7bfbf --- /dev/null +++ b/src/store/setting/index.ts @@ -0,0 +1,109 @@ +import { PrimaryColors } from '@lobehub/ui'; +import type { ThemeMode } from 'antd-style'; +import { produce } from 'immer'; +import { isEqual, merge } from 'lodash-es'; +import { devtools, persist, subscribeWithSelector } from 'zustand/middleware'; +import { shallow } from 'zustand/shallow'; +import { createWithEqualityFn } from 'zustand/traditional'; +import { StateCreator } from 'zustand/vanilla'; + +import { BackgroundEffect, OpenAIConfig, UserConfig } from '@/types/config'; + +import { SettingState, initialState } from './initialState'; + +const SETTING_STORAGE_KEY = 'vidol-chat-config-storage'; + +export interface SettingAction { + /** + * Reset config + */ + resetConfig: () => void; + /** + * Set avatar + * @param avatar + */ + setAvatar: (avatar: string) => void; + /** + * Set background effect + * @param backgroundEffect + */ + setBackgroundEffect: (backgroundEffect: BackgroundEffect) => void; + /** + * Set config + * @param config + */ + setConfig: (config: Partial) => void; + /** + * Set nick name + * @param nickName + */ + setNickName: (nickName: string) => void; + /** + * Set OpenAI config + * @param config + */ + setOpenAIConfig: (config: Partial) => void; + + /** + * Set primary color + * @param primaryColor + */ + setPrimaryColor: (primaryColor: PrimaryColors) => void; + /** + * Set theme mode + * @param themeMode + */ + setThemeMode: (themeMode: ThemeMode) => void; +} + +export interface SettingStore extends SettingState, SettingAction {} + +const createStore: StateCreator = (set, get) => ({ + ...initialState, + + resetConfig: () => { + localStorage.removeItem(SETTING_STORAGE_KEY); + set({ ...initialState }); + }, + setAvatar: (avatar) => { + get().setConfig({ avatar }); + }, + setPrimaryColor: (primaryColor) => { + get().setConfig({ primaryColor }); + }, + setBackgroundEffect: (backgroundEffect) => { + get().setConfig({ backgroundEffect }); + }, + setNickName: (nickName) => { + get().setConfig({ nickName }); + }, + setThemeMode: (themeMode) => { + get().setConfig({ themeMode }); + }, + setConfig: (config) => { + const prevSetting = get().config; + const nextSetting = produce(prevSetting, (draftState) => { + merge(draftState, config); + }); + if (isEqual(prevSetting, nextSetting)) return; + set({ config: nextSetting }); + }, + + setOpenAIConfig: (config) => { + get().setConfig({ languageModel: { openAI: config } }); + }, +}); + +export const useSettingStore = createWithEqualityFn()( + subscribeWithSelector( + persist( + devtools(createStore, { + name: 'VIDOL_CONFIG_STORE', + }), + { name: SETTING_STORAGE_KEY }, + ), + ), + shallow, +); + +export { configSelectors } from './selectors/config'; diff --git a/src/store/setting/initialState.ts b/src/store/setting/initialState.ts new file mode 100644 index 00000000..2249558e --- /dev/null +++ b/src/store/setting/initialState.ts @@ -0,0 +1,24 @@ +import type { ThemeMode } from 'antd-style'; + +import { UserConfig } from '@/types/config'; + +export interface SettingState { + config: UserConfig; +} + +const initialState: SettingState = { + config: { + backgroundEffect: 'glow', + languageModel: { + openAI: { + apikey: '', + endpoint: '', + model: 'gpt-3.5-turbo', + }, + }, + nickName: '', + themeMode: 'auto' as ThemeMode, + }, +}; + +export { initialState }; diff --git a/src/store/setting/selectors/config.ts b/src/store/setting/selectors/config.ts new file mode 100644 index 00000000..65fcbd4b --- /dev/null +++ b/src/store/setting/selectors/config.ts @@ -0,0 +1,10 @@ +import { SettingStore } from '@/store/setting'; +import { OpenAIConfig } from '@/types/config'; + +const currentOpenAIConfig = (s: SettingStore): OpenAIConfig | undefined => { + return s.config.languageModel.openAI; +}; + +export const configSelectors = { + currentOpenAIConfig, +}; From b7dd7928d026d8185da8386f665382734e956706 Mon Sep 17 00:00:00 2001 From: rdmclin2 Date: Thu, 23 May 2024 23:49:45 +0800 Subject: [PATCH 07/11] =?UTF-8?q?:sparkles:=20feat:=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E7=81=B0=E9=98=B6=E8=AE=BE=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/Settings/common.tsx | 11 ++++++- .../features/ThemeSwatchesNetural.tsx | 32 +++++++++++++++++++ src/store/setting/index.ts | 11 ++++++- 3 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 src/features/Settings/features/ThemeSwatchesNetural.tsx diff --git a/src/features/Settings/common.tsx b/src/features/Settings/common.tsx index 5d1c667e..fc0b628c 100644 --- a/src/features/Settings/common.tsx +++ b/src/features/Settings/common.tsx @@ -8,6 +8,7 @@ import React from 'react'; import BackgroundEffect from '@/features/Settings/features/BackgroundEffect'; import NickName from '@/features/Settings/features/NickName'; import ThemeMode from '@/features/Settings/features/ThemeMode'; +import ThemeSwatchesNetural from '@/features/Settings/features/ThemeSwatchesNetural'; import ThemeSwatchesPrimary from '@/features/Settings/features/ThemeSwatchesPrimary'; import { useAgentStore } from '@/store/agent'; import { useSessionStore } from '@/store/session'; @@ -78,7 +79,7 @@ const CommonConfig = (props: CommonConfigProps) => { return (
- + @@ -90,6 +91,14 @@ const CommonConfig = (props: CommonConfigProps) => { + + + diff --git a/src/features/Settings/features/ThemeSwatchesNetural.tsx b/src/features/Settings/features/ThemeSwatchesNetural.tsx new file mode 100644 index 00000000..23e94084 --- /dev/null +++ b/src/features/Settings/features/ThemeSwatchesNetural.tsx @@ -0,0 +1,32 @@ +import { + NeutralColors, + Swatches, + findCustomThemeName, + neutralColors, + neutralColorsSwatches, +} from '@lobehub/ui'; +import { memo } from 'react'; + +import { useSettingStore } from '@/store/setting'; + +const ThemeSwatchesNeutral = memo(() => { + const [neutralColor, setNeutralColor] = useSettingStore((s) => [ + s.config.neutralColor, + s.setNeutralColor, + ]); + + const handleSelect = (v: any) => { + const name = findCustomThemeName('neutral', v) as NeutralColors; + setNeutralColor(name || ''); + }; + + return ( + + ); +}); + +export default ThemeSwatchesNeutral; diff --git a/src/store/setting/index.ts b/src/store/setting/index.ts index 2ad7bfbf..82028157 100644 --- a/src/store/setting/index.ts +++ b/src/store/setting/index.ts @@ -1,4 +1,4 @@ -import { PrimaryColors } from '@lobehub/ui'; +import { NeutralColors, PrimaryColors } from '@lobehub/ui'; import type { ThemeMode } from 'antd-style'; import { produce } from 'immer'; import { isEqual, merge } from 'lodash-es'; @@ -33,11 +33,17 @@ export interface SettingAction { * @param config */ setConfig: (config: Partial) => void; + /** + * Set neutral color + * @param neutralColor + */ + setNeutralColor: (neutralColor: NeutralColors) => void; /** * Set nick name * @param nickName */ setNickName: (nickName: string) => void; + /** * Set OpenAI config * @param config @@ -71,6 +77,9 @@ const createStore: StateCreator = ( setPrimaryColor: (primaryColor) => { get().setConfig({ primaryColor }); }, + setNeutralColor: (neutralColor) => { + get().setConfig({ neutralColor }); + }, setBackgroundEffect: (backgroundEffect) => { get().setConfig({ backgroundEffect }); }, From 2023dd0cb97c4208de16ad7cc3a3e787191b39e0 Mon Sep 17 00:00:00 2001 From: rdmclin2 Date: Fri, 24 May 2024 00:05:17 +0800 Subject: [PATCH 08/11] =?UTF-8?q?:art:=20chore:=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E4=B8=BB=E9=A2=98=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../features => Actions}/ThemeMode/index.tsx | 4 +-- src/features/Settings/common.tsx | 4 --- src/layout/AppTheme.tsx | 29 +++++-------------- src/layout/Header/index.tsx | 2 ++ src/layout/index.tsx | 8 +---- src/store/global/index.ts | 15 ++++++++++ src/store/setting/index.ts | 13 ++------- src/store/setting/initialState.ts | 8 ++--- src/types/config.ts | 23 +++++++-------- 9 files changed, 41 insertions(+), 65 deletions(-) rename src/features/{Settings/features => Actions}/ThemeMode/index.tsx (82%) diff --git a/src/features/Settings/features/ThemeMode/index.tsx b/src/features/Actions/ThemeMode/index.tsx similarity index 82% rename from src/features/Settings/features/ThemeMode/index.tsx rename to src/features/Actions/ThemeMode/index.tsx index d78b8cd9..5af3e781 100644 --- a/src/features/Settings/features/ThemeMode/index.tsx +++ b/src/features/Actions/ThemeMode/index.tsx @@ -2,7 +2,7 @@ import { Segmented } from 'antd'; import { ThemeMode } from 'antd-style'; import React, { CSSProperties, memo } from 'react'; -import { useSettingStore } from '@/store/setting'; +import { useGlobalStore } from '@/store/global'; interface Props { style?: CSSProperties; @@ -10,7 +10,7 @@ interface Props { export default memo((props) => { const { style } = props; - const [themeMode, setThemeMode] = useSettingStore((s) => [s.config.themeMode, s.setThemeMode]); + const [themeMode, setThemeMode] = useGlobalStore((s) => [s.themeMode, s.setThemeMode]); return ( { > - - - diff --git a/src/layout/AppTheme.tsx b/src/layout/AppTheme.tsx index f344b64a..6880d66a 100644 --- a/src/layout/AppTheme.tsx +++ b/src/layout/AppTheme.tsx @@ -1,27 +1,17 @@ 'use client'; import { NeutralColors, PrimaryColors, ThemeProvider } from '@lobehub/ui'; -import { ThemeAppearance, createStyles } from 'antd-style'; +import { createStyles } from 'antd-style'; import { ReactNode, memo, useEffect } from 'react'; -import { - VIDOL_THEME_APPEARANCE, - VIDOL_THEME_NEUTRAL_COLOR, - VIDOL_THEME_PRIMARY_COLOR, -} from '@/constants/theme'; +import { VIDOL_THEME_NEUTRAL_COLOR, VIDOL_THEME_PRIMARY_COLOR } from '@/constants/theme'; +import { useGlobalStore } from '@/store/global'; import { useSettingStore } from '@/store/setting'; import { GlobalStyle } from '@/styles'; import { setCookie } from '@/utils/cookie'; -export interface LayoutProps { - children?: ReactNode; - defaultAppearance?: ThemeAppearance; - defaultPrimaryColor?: PrimaryColors; -} - export interface AppThemeProps { children?: ReactNode; - defaultAppearance?: ThemeAppearance; defaultNeutralColor?: NeutralColors; defaultPrimaryColor?: PrimaryColors; } @@ -38,17 +28,17 @@ const useStyles = createStyles(({ css }) => ({ })); const AppTheme = memo((props: AppThemeProps) => { - const { children, defaultAppearance, defaultNeutralColor, defaultPrimaryColor } = props; - const [primaryColor, neutralColor, themeMode] = useSettingStore((s) => [ + const { children, defaultNeutralColor, defaultPrimaryColor } = props; + const [primaryColor, neutralColor] = useSettingStore((s) => [ s.config.primaryColor, s.config.neutralColor, - s.config.themeMode, ]); + const themeMode = useGlobalStore((s) => s.themeMode); + const { styles } = useStyles(); useEffect(() => { - console.log('primaryColor', primaryColor); setCookie(VIDOL_THEME_PRIMARY_COLOR, primaryColor); }, [primaryColor]); @@ -62,11 +52,6 @@ const AppTheme = memo((props: AppThemeProps) => { primaryColor: primaryColor ?? defaultPrimaryColor, neutralColor: neutralColor ?? defaultNeutralColor, }} - defaultAppearance={defaultAppearance as ThemeAppearance} - onAppearanceChange={(appearance) => { - console.log('onAppearanceChange', appearance); - setCookie(VIDOL_THEME_APPEARANCE, appearance); - }} themeMode={themeMode} > diff --git a/src/layout/Header/index.tsx b/src/layout/Header/index.tsx index 2126f1a6..1fff23ad 100644 --- a/src/layout/Header/index.tsx +++ b/src/layout/Header/index.tsx @@ -7,6 +7,7 @@ import Link from 'next/link'; import { useRouter } from 'next/navigation'; import { memo } from 'react'; +import ThemeMode from '@/features/Actions/ThemeMode'; import { HeaderNavKey } from '@/layout/type'; interface Props { @@ -34,6 +35,7 @@ const Header = (props: Props) => { onClick={() => window.open('https://github.com/lobehub/lobe-vidol', '_blank')} size="large" />, + , ]} logo={ diff --git a/src/layout/index.tsx b/src/layout/index.tsx index 32309034..18bc8d8a 100644 --- a/src/layout/index.tsx +++ b/src/layout/index.tsx @@ -3,11 +3,7 @@ import { ThemeAppearance } from 'antd-style'; import { cookies } from 'next/headers'; import { ReactNode } from 'react'; -import { - VIDOL_THEME_APPEARANCE, - VIDOL_THEME_NEUTRAL_COLOR, - VIDOL_THEME_PRIMARY_COLOR, -} from '@/constants/theme'; +import { VIDOL_THEME_NEUTRAL_COLOR, VIDOL_THEME_PRIMARY_COLOR } from '@/constants/theme'; import AppTheme from '@/layout/AppTheme'; import StoreHydration from '@/layout/StoreHydration'; import StyleRegistry from '@/layout/StyleRegistry'; @@ -22,14 +18,12 @@ const Layout = (props: LayoutProps) => { const { children } = props; const cookieStore = cookies(); - const appearance = cookieStore.get(VIDOL_THEME_APPEARANCE); const primaryColor = cookieStore.get(VIDOL_THEME_PRIMARY_COLOR); const neutralColor = cookieStore.get(VIDOL_THEME_NEUTRAL_COLOR); return ( diff --git a/src/store/global/index.ts b/src/store/global/index.ts index dc503e1d..ba30b327 100644 --- a/src/store/global/index.ts +++ b/src/store/global/index.ts @@ -1,3 +1,4 @@ +import type { ThemeMode } from 'antd-style'; import { produce } from 'immer'; import { isEqual, merge } from 'lodash-es'; import { shallow } from 'zustand/shallow'; @@ -36,10 +37,16 @@ export interface GlobalStore { setPanel: (panel: PanelKey, config: Partial) => void; setRoleList: (show: boolean) => void; setSessionList: (show: boolean) => void; + setThemeMode: (themeMode: ThemeMode) => void; showChatDialog: boolean; showChatSidebar: boolean; showRoleList: boolean; + showSessionList: boolean; + /** + * 主题模式 + */ + themeMode: ThemeMode; toggleChatDialog: () => void; toggleChatSideBar: () => void; toggleRoleList: () => void; @@ -47,6 +54,10 @@ export interface GlobalStore { } const initialState = { + /** + * 主题模式 + */ + themeMode: 'auto' as ThemeMode, showChatSidebar: true, showSessionList: true, showChatDialog: true, @@ -82,6 +93,10 @@ export const useGlobalStore = createWithEqualityFn()( set({ focusList: nextSetting }); }, + setThemeMode: (themeMode) => { + set({ themeMode }); + }, + focusPanel: (key: PanelKey) => { const { focusList } = get(); const nextSetting: PanelKey[] = focusList.filter((item) => item !== key).concat(key); diff --git a/src/store/setting/index.ts b/src/store/setting/index.ts index 82028157..75c7c44c 100644 --- a/src/store/setting/index.ts +++ b/src/store/setting/index.ts @@ -1,5 +1,4 @@ import { NeutralColors, PrimaryColors } from '@lobehub/ui'; -import type { ThemeMode } from 'antd-style'; import { produce } from 'immer'; import { isEqual, merge } from 'lodash-es'; import { devtools, persist, subscribeWithSelector } from 'zustand/middleware'; @@ -7,7 +6,7 @@ import { shallow } from 'zustand/shallow'; import { createWithEqualityFn } from 'zustand/traditional'; import { StateCreator } from 'zustand/vanilla'; -import { BackgroundEffect, OpenAIConfig, UserConfig } from '@/types/config'; +import { BackgroundEffect, Config, OpenAIConfig } from '@/types/config'; import { SettingState, initialState } from './initialState'; @@ -32,7 +31,7 @@ export interface SettingAction { * Set config * @param config */ - setConfig: (config: Partial) => void; + setConfig: (config: Partial) => void; /** * Set neutral color * @param neutralColor @@ -55,11 +54,6 @@ export interface SettingAction { * @param primaryColor */ setPrimaryColor: (primaryColor: PrimaryColors) => void; - /** - * Set theme mode - * @param themeMode - */ - setThemeMode: (themeMode: ThemeMode) => void; } export interface SettingStore extends SettingState, SettingAction {} @@ -86,9 +80,6 @@ const createStore: StateCreator = ( setNickName: (nickName) => { get().setConfig({ nickName }); }, - setThemeMode: (themeMode) => { - get().setConfig({ themeMode }); - }, setConfig: (config) => { const prevSetting = get().config; const nextSetting = produce(prevSetting, (draftState) => { diff --git a/src/store/setting/initialState.ts b/src/store/setting/initialState.ts index 2249558e..dab69b2b 100644 --- a/src/store/setting/initialState.ts +++ b/src/store/setting/initialState.ts @@ -1,9 +1,7 @@ -import type { ThemeMode } from 'antd-style'; - -import { UserConfig } from '@/types/config'; +import { Config } from '@/types/config'; export interface SettingState { - config: UserConfig; + config: Config; } const initialState: SettingState = { @@ -16,8 +14,6 @@ const initialState: SettingState = { model: 'gpt-3.5-turbo', }, }, - nickName: '', - themeMode: 'auto' as ThemeMode, }, }; diff --git a/src/types/config.ts b/src/types/config.ts index f8ce37ac..9e09cfae 100644 --- a/src/types/config.ts +++ b/src/types/config.ts @@ -1,6 +1,5 @@ import { Coordinates } from '@dnd-kit/utilities'; import { NeutralColors, PrimaryColors } from '@lobehub/ui'; -import type { ThemeMode } from 'antd-style'; export type BackgroundEffect = 'glow' | 'none'; @@ -34,7 +33,14 @@ export interface LanguageModelConfig { openAI: OpenAIConfig; } -export interface UserConfig { +export interface Config extends CommonConfig { + /** + * 语言模型配置 + */ + languageModel: LanguageModelConfig; +} + +export interface CommonConfig { /** * 用户头像 */ @@ -42,12 +48,7 @@ export interface UserConfig { /** * 背景类型 */ - backgroundEffect: BackgroundEffect; - - /** - * 语言模型配置 - */ - languageModel: LanguageModelConfig; + backgroundEffect?: BackgroundEffect; /** * 中性色 */ @@ -55,13 +56,9 @@ export interface UserConfig { /** * 用户昵称 */ - nickName: string; + nickName?: string; /** * 主题色 */ primaryColor?: PrimaryColors; - /** - * 主题模式 - */ - themeMode: ThemeMode; } From d6cd5de54200520ce2ca68cb325f544f9b56aa59 Mon Sep 17 00:00:00 2001 From: rdmclin2 Date: Fri, 24 May 2024 00:54:14 +0800 Subject: [PATCH 09/11] =?UTF-8?q?:art:=20chore:=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E5=AF=BC=E8=88=AA=E6=A0=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- next.config.mjs | 3 +- package.json | 1 + src/app/role/style.ts | 2 +- src/components/Avatar.tsx | 43 ++++++++ src/components/Menu/index.tsx | 97 +++++++++++++++++++ src/features/Actions/Discord.tsx | 16 +++ src/features/Actions/Github.tsx | 16 +++ src/features/Actions/ThemeMode/index.tsx | 81 ++++++++++------ src/features/Actions/UserAvatar.tsx | 15 +++ src/features/Settings/common.tsx | 2 +- .../features/AvatarWithUpload/index.tsx | 86 +++++----------- src/layout/Header/index.tsx | 31 +++--- 12 files changed, 282 insertions(+), 111 deletions(-) create mode 100644 src/components/Avatar.tsx create mode 100644 src/components/Menu/index.tsx create mode 100644 src/features/Actions/Discord.tsx create mode 100644 src/features/Actions/Github.tsx create mode 100644 src/features/Actions/UserAvatar.tsx diff --git a/next.config.mjs b/next.config.mjs index a9fd4d4d..0aaa214b 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -20,8 +20,7 @@ const nextConfig = { compress: isProd, pageExtensions: ['tsx', 'ts'], experimental: { - optimizePackageImports: ['@lobehub/ui', '@lobehub/icons', 'chroma-js', 'shiki'], - webVitalsAttribution: ['CLS', 'LCP'], + optimizePackageImports: ['@lobehub/ui', '@lobehub/icons', 'chroma-js', 'shiki', '@icons-pack/react-simple-icons','gpt-tokenizer'], }, reactStrictMode: true, webpack(config) { diff --git a/package.json b/package.json index a1afa583..6c13f191 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "@dnd-kit/core": "^6.1.0", "@dnd-kit/utilities": "^3.2.2", "@gltf-transform/core": "^3.10.1", + "@icons-pack/react-simple-icons": "^9.5.0", "@lobehub/tts": "^1.24.1", "@lobehub/ui": "^1.138.17", "@pixiv/three-vrm": "^2.1.1", diff --git a/src/app/role/style.ts b/src/app/role/style.ts index 2d780607..b9c25802 100644 --- a/src/app/role/style.ts +++ b/src/app/role/style.ts @@ -4,7 +4,7 @@ export const useStyles = createStyles(({ css }) => ({ preview: css` overflow: auto; width: 80rem; - margin: 32px auto; + margin: 0 auto; `, edit: css` padding: 0 24px; diff --git a/src/components/Avatar.tsx b/src/components/Avatar.tsx new file mode 100644 index 00000000..a730528f --- /dev/null +++ b/src/components/Avatar.tsx @@ -0,0 +1,43 @@ +import { createStyles } from 'antd-style'; +import NextImage from 'next/image'; + +import { AVATAR_IMAGE_SIZE, DEFAULT_USER_AVATAR_URL } from '@/constants/common'; + +const useStyle = createStyles( + ({ css, token }) => css` + cursor: pointer; + overflow: hidden; + border-radius: 50%; + transition: + scale 400ms ${token.motionEaseOut}, + box-shadow 100ms ${token.motionEaseOut}; + + &:hover { + box-shadow: 0 0 0 3px ${token.colorText}; + } + + &:active { + scale: 0.8; + } + `, +); + +interface Props { + avatar?: string; + size?: number; +} + +export default (props: Props) => { + const { size = AVATAR_IMAGE_SIZE, avatar } = props; + const { styles } = useStyle(); + return ( + + ); +}; diff --git a/src/components/Menu/index.tsx b/src/components/Menu/index.tsx new file mode 100644 index 00000000..4a3d89da --- /dev/null +++ b/src/components/Menu/index.tsx @@ -0,0 +1,97 @@ +import { Menu as AntdMenu, MenuProps as AntdMenuProps, ConfigProvider } from 'antd'; +import { createStyles } from 'antd-style'; +import { memo } from 'react'; + +const useStyles = createStyles(({ css, token, prefixCls }) => ({ + compact: css` + display: flex; + flex-direction: column; + gap: 0.125rem; + `, + menu: css` + flex: 1; + background: transparent; + border: none !important; + + .${prefixCls}-menu-item-divider { + margin-block: 0.125rem; + border-color: ${token.colorFillTertiary}; + + &:first-child { + margin-top: 0; + } + + &:last-child { + margin-bottom: 0; + } + } + + .${prefixCls}-menu-item, .${prefixCls}-menu-submenu-title { + display: flex; + gap: 0.75rem; + align-items: center; + + height: unset; + min-height: 2rem; + padding: 0.375rem 0.75rem; + + line-height: 2; + + .anticon + .${prefixCls}-menu-title-content { + margin-inline-start: 0; + } + } + + .${prefixCls}-menu-item-selected { + .${prefixCls}-menu-item-icon svg { + color: ${token.colorText}; + } + } + + .${prefixCls}-menu-item-icon svg { + color: ${token.colorTextSecondary}; + } + + .${prefixCls}-menu-title-content { + flex: 1; + } + `, +})); + +export interface MenuProps extends AntdMenuProps { + variant?: 'default' | 'compact'; +} + +const Menu = memo(({ className, selectable = false, variant, ...rest }) => { + const isCompact = variant === 'compact'; + const { cx, styles, theme } = useStyles(); + return ( + + + + ); +}); + +export default Menu; diff --git a/src/features/Actions/Discord.tsx b/src/features/Actions/Discord.tsx new file mode 100644 index 00000000..955b2a48 --- /dev/null +++ b/src/features/Actions/Discord.tsx @@ -0,0 +1,16 @@ +import { SiDiscord } from '@icons-pack/react-simple-icons'; +import { ActionIcon } from '@lobehub/ui'; +import { useTheme } from 'antd-style'; + +export default () => { + const theme = useTheme(); + return ( + window.open('https://discord.gg/AYFPHvv2jT', '_blank')} + style={{ border: `1px solid ${theme.colorFillSecondary}` }} + /> + ); +}; diff --git a/src/features/Actions/Github.tsx b/src/features/Actions/Github.tsx new file mode 100644 index 00000000..3926eebe --- /dev/null +++ b/src/features/Actions/Github.tsx @@ -0,0 +1,16 @@ +import { SiGithub } from '@icons-pack/react-simple-icons'; +import { ActionIcon } from '@lobehub/ui'; +import { useTheme } from 'antd-style'; + +export default () => { + const theme = useTheme(); + return ( + window.open('https://github.com/lobehub/lobe-vidol', '_blank')} + style={{ border: `1px solid ${theme.colorFillSecondary}` }} + /> + ); +}; diff --git a/src/features/Actions/ThemeMode/index.tsx b/src/features/Actions/ThemeMode/index.tsx index 5af3e781..6c8925bb 100644 --- a/src/features/Actions/ThemeMode/index.tsx +++ b/src/features/Actions/ThemeMode/index.tsx @@ -1,38 +1,61 @@ -import { Segmented } from 'antd'; -import { ThemeMode } from 'antd-style'; -import React, { CSSProperties, memo } from 'react'; +import { ActionIcon, Icon } from '@lobehub/ui'; +import { Popover } from 'antd'; +import { useTheme } from 'antd-style'; +import { Monitor, Moon, Sun } from 'lucide-react'; +import { memo, useMemo } from 'react'; +import Menu, { type MenuProps } from '@/components/Menu'; import { useGlobalStore } from '@/store/global'; -interface Props { - style?: CSSProperties; -} +const themeIcons = { + auto: Monitor, + dark: Moon, + light: Sun, +}; -export default memo((props) => { - const { style } = props; - const [themeMode, setThemeMode] = useGlobalStore((s) => [s.themeMode, s.setThemeMode]); +const ThemeButton = memo(() => { + const theme = useTheme(); + const [themeMode, switchThemeMode] = useGlobalStore((s) => [s.themeMode, s.setThemeMode]); + + const items: MenuProps['items'] = useMemo( + () => [ + { + icon: , + key: 'auto', + label: '跟随系统', + onClick: () => switchThemeMode('auto'), + }, + { + icon: , + key: 'light', + label: '亮色模式', + onClick: () => switchThemeMode('light'), + }, + { + icon: , + key: 'dark', + label: '暗黑模式', + onClick: () => switchThemeMode('dark'), + }, + ], + [], + ); return ( - { - setThemeMode(value as ThemeMode); + } + overlayInnerStyle={{ + padding: 0, }} - options={[ - { - label: '🔆 亮色模式', - value: 'light', - }, - { - label: '🌙 暗色模式', - value: 'dark', - }, - { - label: '💻 跟随系统', - value: 'auto', - }, - ]} - /> + trigger={['click', 'hover']} + > + + ); }); + +export default ThemeButton; diff --git a/src/features/Actions/UserAvatar.tsx b/src/features/Actions/UserAvatar.tsx new file mode 100644 index 00000000..f96b4e93 --- /dev/null +++ b/src/features/Actions/UserAvatar.tsx @@ -0,0 +1,15 @@ +import { Space, Typography } from 'antd'; + +import Avatar from '@/components/Avatar'; +import { useSettingStore } from '@/store/setting'; + +export default () => { + const [avatar, nickName] = useSettingStore((s) => [s.config.avatar, s.config.nickName]); + + return ( + + + {nickName} + + ); +}; diff --git a/src/features/Settings/common.tsx b/src/features/Settings/common.tsx index eb3e30b7..4492a361 100644 --- a/src/features/Settings/common.tsx +++ b/src/features/Settings/common.tsx @@ -113,7 +113,7 @@ const CommonConfig = (props: CommonConfigProps) => { diff --git a/src/features/Settings/features/AvatarWithUpload/index.tsx b/src/features/Settings/features/AvatarWithUpload/index.tsx index 06ef3552..7cee9922 100644 --- a/src/features/Settings/features/AvatarWithUpload/index.tsx +++ b/src/features/Settings/features/AvatarWithUpload/index.tsx @@ -1,70 +1,32 @@ import { Upload } from 'antd'; -import { createStyles } from 'antd-style'; -import NextImage from 'next/image'; -import { CSSProperties, memo, useCallback } from 'react'; +import { memo, useCallback } from 'react'; -import { DEFAULT_USER_AVATAR_URL } from '@/constants/common'; +import Avatar from '@/components/Avatar'; +import { AVATAR_COMPRESS_SIZE, AVATAR_IMAGE_SIZE } from '@/constants/common'; import { useSettingStore } from '@/store/setting'; import { createUploadImageHandler } from '@/utils/common'; import { imageToBase64 } from '@/utils/imageToBase64'; -const useStyle = createStyles( - ({ css, token }) => css` - cursor: pointer; - overflow: hidden; - border-radius: 50%; - transition: - scale 400ms ${token.motionEaseOut}, - box-shadow 100ms ${token.motionEaseOut}; - - &:hover { - box-shadow: 0 0 0 3px ${token.colorText}; - } - - &:active { - scale: 0.8; - } - `, -); - -interface AvatarWithUploadProps { - compressSize?: number; - id?: string; - size?: number; - style?: CSSProperties; -} - -const AvatarWithUpload = memo( - ({ size = 40, compressSize = 256, style, id }) => { - const { styles } = useStyle(); - const [avatar, setAvatar] = useSettingStore((s) => [s.config.avatar, s.setAvatar]); - - const handleUploadAvatar = useCallback( - createUploadImageHandler((avatar) => { - const img = new Image(); - img.src = avatar; - img.addEventListener('load', () => { - const webpBase64 = imageToBase64({ img, size: compressSize }); - setAvatar(webpBase64); - }); - }), - [], - ); - - return ( -
- void 0} maxCount={1}> - - -
- ); - }, -); +const AvatarWithUpload = memo(() => { + const [avatar, setAvatar] = useSettingStore((s) => [s.config.avatar, s.setAvatar]); + + const handleUploadAvatar = useCallback( + createUploadImageHandler((avatar) => { + const img = new Image(); + img.src = avatar; + img.addEventListener('load', () => { + const webpBase64 = imageToBase64({ img, size: AVATAR_COMPRESS_SIZE }); + setAvatar(webpBase64); + }); + }), + [], + ); + + return ( + void 0} maxCount={1}> + + + ); +}); export default AvatarWithUpload; diff --git a/src/layout/Header/index.tsx b/src/layout/Header/index.tsx index 1fff23ad..1c81ff07 100644 --- a/src/layout/Header/index.tsx +++ b/src/layout/Header/index.tsx @@ -1,13 +1,15 @@ 'use client'; -import { ActionIcon, Header as LobeHeader, Logo, TabsNav } from '@lobehub/ui'; +import { Header as LobeHeader, Logo, TabsNav } from '@lobehub/ui'; import { Space, Tag, Tooltip } from 'antd'; -import { GithubIcon, UserRoundPlusIcon } from 'lucide-react'; import Link from 'next/link'; import { useRouter } from 'next/navigation'; import { memo } from 'react'; +import Discord from '@/features/Actions/Discord'; +import Github from '@/features/Actions/Github'; import ThemeMode from '@/features/Actions/ThemeMode'; +import UserAvatar from '@/features/Actions/UserAvatar'; import { HeaderNavKey } from '@/layout/type'; interface Props { @@ -21,21 +23,18 @@ const Header = (props: Props) => { return ( window.open('https://github.com/lobehub/lobe-vidol-market', '_blank')} - size="large" - />, - window.open('https://github.com/lobehub/lobe-vidol', '_blank')} - size="large" - />, + // window.open('https://github.com/lobehub/lobe-vidol-market', '_blank')} + // style={{ border: `1px solid ${theme.colorFillSecondary}` }} + // />, + + , + , , + , ]} logo={ From ffdccc28fde178dc909a3c27b24db9542db2882a Mon Sep 17 00:00:00 2001 From: rdmclin2 Date: Fri, 24 May 2024 01:00:47 +0800 Subject: [PATCH 10/11] =?UTF-8?q?:sparkles:=20feat:=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=20Discord=20Icon?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/Actions/Discord.tsx | 20 +++++++++++++++++++- src/features/Actions/Github.tsx | 21 ++++++++++++++++++++- src/layout/Header/index.tsx | 8 -------- 3 files changed, 39 insertions(+), 10 deletions(-) diff --git a/src/features/Actions/Discord.tsx b/src/features/Actions/Discord.tsx index 955b2a48..4c57f36b 100644 --- a/src/features/Actions/Discord.tsx +++ b/src/features/Actions/Discord.tsx @@ -1,11 +1,29 @@ import { SiDiscord } from '@icons-pack/react-simple-icons'; import { ActionIcon } from '@lobehub/ui'; -import { useTheme } from 'antd-style'; +import { createStyles, useTheme } from 'antd-style'; + +const useStyles = createStyles(({ css, token }) => { + return { + icon: css` + svg { + fill: ${token.colorTextDescription}; + } + + &:hover { + svg { + fill: ${token.colorText}; + } + } + `, + }; +}); export default () => { + const { styles } = useStyles(); const theme = useTheme(); return ( { + return { + icon: css` + svg { + fill: ${token.colorTextDescription}; + } + + &:hover { + svg { + fill: ${token.colorText}; + } + } + `, + }; +}); export default () => { const theme = useTheme(); + + const { styles } = useStyles(); return ( { return ( window.open('https://github.com/lobehub/lobe-vidol-market', '_blank')} - // style={{ border: `1px solid ${theme.colorFillSecondary}` }} - // />, - , , , From 23b2d5d6d13725bd556118923d15ee3de51e19da Mon Sep 17 00:00:00 2001 From: rdmclin2 Date: Fri, 24 May 2024 23:47:37 +0800 Subject: [PATCH 11/11] =?UTF-8?q?:sparkles:=20feat:=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/error.tsx | 5 +++ src/app/global-error.tsx | 22 ++++++++++ src/app/welcome/loading.tsx | 7 +-- src/components/Error/index.tsx | 61 +++++++++++++++++++++++++++ src/constants/common.ts | 2 + src/features/Actions/ClearSession.tsx | 41 ++++++++++++++++++ src/features/Actions/ResetConfig.tsx | 38 +++++++++++++++++ src/features/DebugUI/index.tsx | 20 +++++++++ src/features/Settings/common.tsx | 53 ++--------------------- src/layout/Header/index.tsx | 3 +- src/layout/index.tsx | 10 ++++- 11 files changed, 204 insertions(+), 58 deletions(-) create mode 100644 src/app/error.tsx create mode 100644 src/app/global-error.tsx create mode 100644 src/components/Error/index.tsx create mode 100644 src/features/Actions/ClearSession.tsx create mode 100644 src/features/Actions/ResetConfig.tsx create mode 100644 src/features/DebugUI/index.tsx diff --git a/src/app/error.tsx b/src/app/error.tsx new file mode 100644 index 00000000..07149103 --- /dev/null +++ b/src/app/error.tsx @@ -0,0 +1,5 @@ +'use client'; + +import dynamic from 'next/dynamic'; + +export default dynamic(() => import('@/components/Error')); diff --git a/src/app/global-error.tsx b/src/app/global-error.tsx new file mode 100644 index 00000000..5f6fc47d --- /dev/null +++ b/src/app/global-error.tsx @@ -0,0 +1,22 @@ +'use client'; + +import Error from 'next/error'; +import { useEffect } from 'react'; + +export default function GlobalError({ + error, +}: { + error: Error & { digest?: string }; + reset: () => void; +}) { + useEffect(() => { + console.log('error', error); + }, [error]); + return ( + + + + + + ); +} diff --git a/src/app/welcome/loading.tsx b/src/app/welcome/loading.tsx index a5d173c7..136fe6bb 100644 --- a/src/app/welcome/loading.tsx +++ b/src/app/welcome/loading.tsx @@ -17,12 +17,7 @@ const Loading = () => { const { styles } = useStyles(); return (
- +
); }; diff --git a/src/components/Error/index.tsx b/src/components/Error/index.tsx new file mode 100644 index 00000000..44a72044 --- /dev/null +++ b/src/components/Error/index.tsx @@ -0,0 +1,61 @@ +'use client'; + +import { FluentEmoji } from '@lobehub/ui'; +import { Button } from 'antd'; +import Link from 'next/link'; +import { memo, useEffect } from 'react'; +import { Flexbox } from 'react-layout-kit'; + +import { MAX_WIDTH } from '@/constants/common'; +import ResetConfig from '@/features/Actions/ClearSession'; +import ClearChat from '@/features/Actions/ResetConfig'; + +interface ErrorCaptureProps { + error: Error & { digest?: string }; + reset: () => void; +} + +const ErrorCapture = memo(({ reset, error }) => { + useEffect(() => { + // Log the error to an error reporting service + console.error(error); + }, [error]); + + return ( + +

+ ERROR +

+ +

+ 页面遇到一点问题... +

+

+ 项目当前正在施工中,不保证数据稳定性,如果遇到问题可以尝试 + + 或 + ,造成地不便敬请谅解 +

+ + + + + + +
+ ); +}); + +ErrorCapture.displayName = 'ErrorCapture'; + +export default ErrorCapture; diff --git a/src/constants/common.ts b/src/constants/common.ts index 511e59e8..81047fb4 100644 --- a/src/constants/common.ts +++ b/src/constants/common.ts @@ -9,6 +9,8 @@ export const LOADING_FLAG = '...'; export const DEFAULT_USER_AVATAR = '😀'; export const MAX_NAME_LENGTH = 20; + +export const MAX_WIDTH = 1024; export const MAX_DESCRIPTION_LENGTH = 100; export const MAX_GREETING_LENGTH = 200; export const MAX_README_LENGTH = 800; diff --git a/src/features/Actions/ClearSession.tsx b/src/features/Actions/ClearSession.tsx new file mode 100644 index 00000000..213715dd --- /dev/null +++ b/src/features/Actions/ClearSession.tsx @@ -0,0 +1,41 @@ +import { App, Button } from 'antd'; +import { ButtonType } from 'antd/es/button'; +import React from 'react'; + +import { useAgentStore } from '@/store/agent'; +import { useSessionStore } from '@/store/session'; + +interface Props { + text?: string; + type?: ButtonType; +} +export default (props: Props) => { + const { text = '立即清除', type = 'primary' } = props; + const clearAgentStorage = useAgentStore((s) => s.clearAgentStorage); + const clearSessions = useSessionStore((s) => s.clearSessions); + const { message, modal } = App.useApp(); + + const handleClear = () => { + modal.confirm({ + cancelText: '取消', + centered: true, + content: '操作无法撤销,清除后数据将无法恢复,请慎重操作', + okButtonProps: { + danger: true, + }, + okText: '确定', + onOk: () => { + clearSessions(); + clearAgentStorage(); + message.success('清除成功'); + }, + title: '确认清除所有会话消息?', + }); + }; + + return ( + + ); +}; diff --git a/src/features/Actions/ResetConfig.tsx b/src/features/Actions/ResetConfig.tsx new file mode 100644 index 00000000..3e80e4f4 --- /dev/null +++ b/src/features/Actions/ResetConfig.tsx @@ -0,0 +1,38 @@ +import { App, Button } from 'antd'; +import { ButtonType } from 'antd/es/button'; +import React from 'react'; + +import { useSettingStore } from '@/store/setting'; + +interface Props { + text?: string; + type?: ButtonType; +} +export default (props: Props) => { + const { text = '立即重置', type = 'primary' } = props; + const resetConfig = useSettingStore((s) => s.resetConfig); + const { message, modal } = App.useApp(); + + const handleReset = () => { + modal.confirm({ + cancelText: '取消', + centered: true, + content: '操作无法撤销,重置后数据将无法恢复,请慎重操作', + okButtonProps: { + danger: true, + }, + okText: '确定', + onOk: () => { + resetConfig(); + message.success('重置成功'); + }, + title: '确认重置所有系统设置?', + }); + }; + + return ( + + ); +}; diff --git a/src/features/DebugUI/index.tsx b/src/features/DebugUI/index.tsx new file mode 100644 index 00000000..6ebe6bf9 --- /dev/null +++ b/src/features/DebugUI/index.tsx @@ -0,0 +1,20 @@ +'use client'; + +import { Icon } from '@lobehub/ui'; +import { FloatButton } from 'antd'; +import { LucideBugPlay } from 'lucide-react'; +import { memo } from 'react'; + +const DebugUI = memo(() => { + return ( + } + onClick={async () => { + throw new Error('触发错误'); + }} + tooltip={'触发错误'} + /> + ); +}); + +export default DebugUI; diff --git a/src/features/Settings/common.tsx b/src/features/Settings/common.tsx index 4492a361..0db070fb 100644 --- a/src/features/Settings/common.tsx +++ b/src/features/Settings/common.tsx @@ -1,17 +1,15 @@ import { Form, FormGroup, FormItem } from '@lobehub/ui'; -import { App, Button } from 'antd'; import { createStyles } from 'antd-style'; import classNames from 'classnames'; import { Monitor, Settings2, User2Icon } from 'lucide-react'; import React from 'react'; +import ResetConfig from '@/features/Actions/ClearSession'; +import ClearChat from '@/features/Actions/ResetConfig'; import BackgroundEffect from '@/features/Settings/features/BackgroundEffect'; import NickName from '@/features/Settings/features/NickName'; import ThemeSwatchesNetural from '@/features/Settings/features/ThemeSwatchesNetural'; import ThemeSwatchesPrimary from '@/features/Settings/features/ThemeSwatchesPrimary'; -import { useAgentStore } from '@/store/agent'; -import { useSessionStore } from '@/store/session'; -import { useSettingStore } from '@/store/setting'; import AvatarWithUpload from './features/AvatarWithUpload'; @@ -35,45 +33,6 @@ const useStyles = createStyles(({ css }) => ({ const CommonConfig = (props: CommonConfigProps) => { const { style, className } = props; const { styles } = useStyles(); - const clearAgentStorage = useAgentStore((s) => s.clearAgentStorage); - const clearSessions = useSessionStore((s) => s.clearSessions); - const resetConfig = useSettingStore((s) => s.resetConfig); - const { message, modal } = App.useApp(); - - const handleClear = () => { - modal.confirm({ - cancelText: '取消', - centered: true, - content: '操作无法撤销,清除后数据将无法恢复,请慎重操作', - okButtonProps: { - danger: true, - }, - okText: '确定', - onOk: () => { - clearSessions(); - clearAgentStorage(); - message.success('清除成功'); - }, - title: '确认清除所有会话消息?', - }); - }; - - const handleReset = () => { - modal.confirm({ - cancelText: '取消', - centered: true, - content: '操作无法撤销,重置后数据将无法恢复,请慎重操作', - okButtonProps: { - danger: true, - }, - okText: '确定', - onOk: () => { - resetConfig(); - message.success('重置成功'); - }, - title: '确认重置所有系统设置?', - }); - }; return (
@@ -108,18 +67,14 @@ const CommonConfig = (props: CommonConfigProps) => { divider label={'清除所有会话消息'} > - + - + diff --git a/src/layout/Header/index.tsx b/src/layout/Header/index.tsx index 54f592f9..0a0ac453 100644 --- a/src/layout/Header/index.tsx +++ b/src/layout/Header/index.tsx @@ -9,7 +9,6 @@ import { memo } from 'react'; import Discord from '@/features/Actions/Discord'; import Github from '@/features/Actions/Github'; import ThemeMode from '@/features/Actions/ThemeMode'; -import UserAvatar from '@/features/Actions/UserAvatar'; import { HeaderNavKey } from '@/layout/type'; interface Props { @@ -23,10 +22,10 @@ const Header = (props: Props) => { return ( , , , , + // , ]} logo={ diff --git a/src/layout/index.tsx b/src/layout/index.tsx index 18bc8d8a..f3473050 100644 --- a/src/layout/index.tsx +++ b/src/layout/index.tsx @@ -1,13 +1,20 @@ import { PrimaryColors } from '@lobehub/ui'; import { ThemeAppearance } from 'antd-style'; +import dynamic from 'next/dynamic'; import { cookies } from 'next/headers'; -import { ReactNode } from 'react'; +import { FC, ReactNode } from 'react'; import { VIDOL_THEME_NEUTRAL_COLOR, VIDOL_THEME_PRIMARY_COLOR } from '@/constants/theme'; import AppTheme from '@/layout/AppTheme'; import StoreHydration from '@/layout/StoreHydration'; import StyleRegistry from '@/layout/StyleRegistry'; +let DebugUI: FC = () => null; + +if (process.env.NODE_ENV === 'development') { + DebugUI = dynamic(() => import('@/features/DebugUI'), { ssr: false }) as FC; +} + export interface LayoutProps { children?: ReactNode; defaultAppearance?: ThemeAppearance; @@ -27,6 +34,7 @@ const Layout = (props: LayoutProps) => { defaultNeutralColor={neutralColor?.value as any} defaultPrimaryColor={primaryColor?.value as any} > + {children}