@lobehub/chat 0.40.5 → 0.40.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +58 -0
- package/package.json +1 -1
- package/src/pages/chat/SessionList/List/index.tsx +3 -10
- package/src/pages/chat/[id]/Conversation/ChatList/index.tsx +8 -1
- package/src/pages/chat/[id]/Header/index.tsx +40 -26
- package/src/pages/chat/[id]/Sidebar/Inner.tsx +46 -26
- package/src/store/session/hooks/useSessionHydrated.ts +17 -0
- package/src/store/session/index.ts +2 -35
- package/src/store/session/store.ts +33 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,64 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
### [Version 0.40.7](https://github.com/lobehub/lobe-chat/compare/v0.40.6...v0.40.7)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2023-08-10**</sup>
|
|
8
|
+
|
|
9
|
+
#### 💄 Styles
|
|
10
|
+
|
|
11
|
+
- **misc**: 优化 Topic 的水合加载效果.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### Styles
|
|
19
|
+
|
|
20
|
+
- **misc**: 优化 Topic 的水合加载效果 ([0cd0088](https://github.com/lobehub/lobe-chat/commit/0cd0088))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
### [Version 0.40.6](https://github.com/lobehub/lobe-chat/compare/v0.40.5...v0.40.6)
|
|
31
|
+
|
|
32
|
+
<sup>Released on **2023-08-10**</sup>
|
|
33
|
+
|
|
34
|
+
#### ♻ Code Refactoring
|
|
35
|
+
|
|
36
|
+
- **misc**: 重构优化 hydrated 的判断逻辑.
|
|
37
|
+
|
|
38
|
+
#### 💄 Styles
|
|
39
|
+
|
|
40
|
+
- **misc**: 优化水合前的加载效果.
|
|
41
|
+
|
|
42
|
+
<br/>
|
|
43
|
+
|
|
44
|
+
<details>
|
|
45
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
46
|
+
|
|
47
|
+
#### Code refactoring
|
|
48
|
+
|
|
49
|
+
- **misc**: 重构优化 hydrated 的判断逻辑 ([1781119](https://github.com/lobehub/lobe-chat/commit/1781119))
|
|
50
|
+
|
|
51
|
+
#### Styles
|
|
52
|
+
|
|
53
|
+
- **misc**: 优化水合前的加载效果 ([6bbd978](https://github.com/lobehub/lobe-chat/commit/6bbd978))
|
|
54
|
+
|
|
55
|
+
</details>
|
|
56
|
+
|
|
57
|
+
<div align="right">
|
|
58
|
+
|
|
59
|
+
[](#readme-top)
|
|
60
|
+
|
|
61
|
+
</div>
|
|
62
|
+
|
|
5
63
|
### [Version 0.40.5](https://github.com/lobehub/lobe-chat/compare/v0.40.4...v0.40.5)
|
|
6
64
|
|
|
7
65
|
<sup>Released on **2023-08-10**</sup>
|
package/package.json
CHANGED
|
@@ -1,28 +1,21 @@
|
|
|
1
1
|
import isEqual from 'fast-deep-equal';
|
|
2
2
|
import Link from 'next/link';
|
|
3
|
-
import { memo
|
|
3
|
+
import { memo } from 'react';
|
|
4
4
|
import { shallow } from 'zustand/shallow';
|
|
5
5
|
|
|
6
|
-
import { sessionSelectors, useSessionStore } from '@/store/session';
|
|
6
|
+
import { sessionSelectors, useSessionHydrated, useSessionStore } from '@/store/session';
|
|
7
7
|
|
|
8
8
|
import SessionItem from './SessionItem';
|
|
9
9
|
import SkeletonItem from './SkeletonItem';
|
|
10
10
|
|
|
11
11
|
const SessionList = memo(() => {
|
|
12
|
-
const [isInit, setInit] = useState(false);
|
|
13
12
|
const list = useSessionStore((s) => sessionSelectors.sessionList(s), isEqual);
|
|
14
13
|
const [activeId, loading] = useSessionStore(
|
|
15
14
|
(s) => [s.activeId, s.autocompleteLoading.title],
|
|
16
15
|
shallow,
|
|
17
16
|
);
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
const hasRehydrated = useSessionStore.persist.hasHydrated();
|
|
21
|
-
|
|
22
|
-
if (hasRehydrated) {
|
|
23
|
-
setInit(true);
|
|
24
|
-
}
|
|
25
|
-
}, []);
|
|
18
|
+
const isInit = useSessionHydrated();
|
|
26
19
|
|
|
27
20
|
return !isInit ? (
|
|
28
21
|
<SkeletonItem />
|
|
@@ -5,7 +5,12 @@ import { useTranslation } from 'react-i18next';
|
|
|
5
5
|
import { Flexbox } from 'react-layout-kit';
|
|
6
6
|
import { shallow } from 'zustand/shallow';
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
agentSelectors,
|
|
10
|
+
chatSelectors,
|
|
11
|
+
useSessionHydrated,
|
|
12
|
+
useSessionStore,
|
|
13
|
+
} from '@/store/session';
|
|
9
14
|
import { ChatMessage } from '@/types/chatMessage';
|
|
10
15
|
import { ErrorType } from '@/types/fetch';
|
|
11
16
|
import { isFunctionMessage } from '@/utils/message';
|
|
@@ -29,7 +34,9 @@ const renderErrorMessage: RenderErrorMessage = (error, message) => {
|
|
|
29
34
|
|
|
30
35
|
const List = () => {
|
|
31
36
|
const { t } = useTranslation('common');
|
|
37
|
+
const hydrated = useSessionHydrated();
|
|
32
38
|
const data = useSessionStore(chatSelectors.currentChats, isEqual);
|
|
39
|
+
console.log(hydrated, data);
|
|
33
40
|
const [displayMode, chatLoadingId, deleteMessage, resendMessage, dispatchMessage] =
|
|
34
41
|
useSessionStore(
|
|
35
42
|
(s) => [
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SiOpenai } from '@icons-pack/react-simple-icons';
|
|
2
2
|
import { ActionIcon, Avatar, ChatHeader } from '@lobehub/ui';
|
|
3
|
+
import { Skeleton } from 'antd';
|
|
3
4
|
import { PanelRightClose, PanelRightOpen, Settings } from 'lucide-react';
|
|
4
5
|
import Router from 'next/router';
|
|
5
6
|
import { memo } from 'react';
|
|
@@ -9,12 +10,13 @@ import { shallow } from 'zustand/shallow';
|
|
|
9
10
|
|
|
10
11
|
import HeaderTitle from '@/components/HeaderTitle';
|
|
11
12
|
import Tag from '@/components/Tag';
|
|
12
|
-
import { agentSelectors, useSessionStore } from '@/store/session';
|
|
13
|
+
import { agentSelectors, useSessionHydrated, useSessionStore } from '@/store/session';
|
|
13
14
|
import { useSettings } from '@/store/settings';
|
|
14
15
|
|
|
15
16
|
import PluginTag from './PluginTag';
|
|
16
17
|
|
|
17
18
|
const Header = memo(() => {
|
|
19
|
+
const init = useSessionHydrated();
|
|
18
20
|
const { t } = useTranslation('common');
|
|
19
21
|
|
|
20
22
|
const [title, description, avatar, backgroundColor, id, model, plugins] = useSessionStore(
|
|
@@ -38,31 +40,43 @@ const Header = memo(() => {
|
|
|
38
40
|
return (
|
|
39
41
|
<ChatHeader
|
|
40
42
|
left={
|
|
41
|
-
|
|
42
|
-
<
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
<
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
43
|
+
!init ? (
|
|
44
|
+
<Flexbox horizontal>
|
|
45
|
+
<Skeleton
|
|
46
|
+
active
|
|
47
|
+
avatar={{ shape: 'circle', size: 'default' }}
|
|
48
|
+
paragraph={false}
|
|
49
|
+
round
|
|
50
|
+
title={{ style: { margin: 0, marginTop: 8 }, width: 200 }}
|
|
51
|
+
/>
|
|
52
|
+
</Flexbox>
|
|
53
|
+
) : (
|
|
54
|
+
<Flexbox align={'flex-start'} gap={12} horizontal>
|
|
55
|
+
<Avatar
|
|
56
|
+
avatar={avatar}
|
|
57
|
+
background={backgroundColor}
|
|
58
|
+
onClick={() => {
|
|
59
|
+
Router.push(`/chat/${id}/setting`);
|
|
60
|
+
}}
|
|
61
|
+
size={40}
|
|
62
|
+
style={{ cursor: 'pointer', flex: 'none' }}
|
|
63
|
+
title={title}
|
|
64
|
+
/>
|
|
65
|
+
<HeaderTitle
|
|
66
|
+
desc={description}
|
|
67
|
+
tag={
|
|
68
|
+
<>
|
|
69
|
+
<Tag>
|
|
70
|
+
<SiOpenai size={'11px'} style={{ marginTop: 3 }} />
|
|
71
|
+
{model}
|
|
72
|
+
</Tag>
|
|
73
|
+
{plugins?.length > 0 && <PluginTag plugins={plugins} />}
|
|
74
|
+
</>
|
|
75
|
+
}
|
|
76
|
+
title={title}
|
|
77
|
+
/>
|
|
78
|
+
</Flexbox>
|
|
79
|
+
)
|
|
66
80
|
}
|
|
67
81
|
right={
|
|
68
82
|
id && (
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ActionIcon, DraggablePanelBody, EditableMessage, SearchBar } from '@lobehub/ui';
|
|
2
|
+
import { Skeleton } from 'antd';
|
|
2
3
|
import { createStyles } from 'antd-style';
|
|
3
4
|
import { Maximize2Icon } from 'lucide-react';
|
|
4
5
|
import { memo, useState } from 'react';
|
|
@@ -6,7 +7,7 @@ import { useTranslation } from 'react-i18next';
|
|
|
6
7
|
import { Flexbox } from 'react-layout-kit';
|
|
7
8
|
import { shallow } from 'zustand/shallow';
|
|
8
9
|
|
|
9
|
-
import { agentSelectors, useSessionStore } from '@/store/session';
|
|
10
|
+
import { agentSelectors, useSessionHydrated, useSessionStore } from '@/store/session';
|
|
10
11
|
|
|
11
12
|
import Header from './Header';
|
|
12
13
|
import { Topic } from './Topic';
|
|
@@ -21,13 +22,7 @@ const useStyles = createStyles(({ css, token }) => ({
|
|
|
21
22
|
prompt: css`
|
|
22
23
|
overflow-x: hidden;
|
|
23
24
|
overflow-y: auto;
|
|
24
|
-
|
|
25
|
-
height: 200px;
|
|
26
|
-
padding: 0 16px 16px;
|
|
27
|
-
|
|
28
25
|
opacity: 0.75;
|
|
29
|
-
border-bottom: 1px solid ${token.colorBorder};
|
|
30
|
-
|
|
31
26
|
transition: opacity 200ms ${token.motionEaseOut};
|
|
32
27
|
|
|
33
28
|
&:hover {
|
|
@@ -38,6 +33,9 @@ const useStyles = createStyles(({ css, token }) => ({
|
|
|
38
33
|
font-size: ${token.fontSizeHeading4}px;
|
|
39
34
|
font-weight: bold;
|
|
40
35
|
`,
|
|
36
|
+
topic: css`
|
|
37
|
+
border-top: 1px solid ${token.colorBorder};
|
|
38
|
+
`,
|
|
41
39
|
}));
|
|
42
40
|
|
|
43
41
|
const Inner = memo(() => {
|
|
@@ -48,6 +46,7 @@ const Inner = memo(() => {
|
|
|
48
46
|
shallow,
|
|
49
47
|
);
|
|
50
48
|
|
|
49
|
+
const hydrated = useSessionHydrated();
|
|
51
50
|
const { t } = useTranslation('common');
|
|
52
51
|
return (
|
|
53
52
|
<DraggablePanelBody style={{ padding: 0 }}>
|
|
@@ -62,26 +61,47 @@ const Inner = memo(() => {
|
|
|
62
61
|
}
|
|
63
62
|
title={t('settingAgent.prompt.title', { ns: 'setting' })}
|
|
64
63
|
/>
|
|
65
|
-
<
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
64
|
+
<Flexbox height={200} padding={'0 16px 16px'}>
|
|
65
|
+
{hydrated ? (
|
|
66
|
+
<EditableMessage
|
|
67
|
+
classNames={{ markdown: styles.prompt }}
|
|
68
|
+
onChange={(e) => {
|
|
69
|
+
updateAgentConfig({ systemRole: e });
|
|
70
|
+
}}
|
|
71
|
+
onOpenChange={setOpenModal}
|
|
72
|
+
openModal={openModal}
|
|
73
|
+
placeholder={`${t('settingAgent.prompt.placeholder', { ns: 'setting' })}...`}
|
|
74
|
+
styles={{ markdown: systemRole ? {} : { opacity: 0.5 } }}
|
|
75
|
+
text={{
|
|
76
|
+
cancel: t('cancel'),
|
|
77
|
+
confirm: t('ok'),
|
|
78
|
+
edit: t('edit'),
|
|
79
|
+
title: t('settingAgent.prompt.title', { ns: 'setting' }),
|
|
80
|
+
}}
|
|
81
|
+
value={systemRole}
|
|
82
|
+
/>
|
|
83
|
+
) : (
|
|
84
|
+
<Skeleton active avatar={false} style={{ marginTop: 12 }} title={false} />
|
|
85
|
+
)}
|
|
86
|
+
</Flexbox>
|
|
87
|
+
<Flexbox className={styles.topic} gap={16} padding={16}>
|
|
83
88
|
<SearchBar placeholder={t('topic.searchPlaceholder')} spotlight type={'ghost'} />
|
|
84
|
-
|
|
89
|
+
{!hydrated ? (
|
|
90
|
+
<Flexbox gap={8} style={{ marginTop: 12 }}>
|
|
91
|
+
{Array.from({ length: 8 }).map((_, i) => (
|
|
92
|
+
<Skeleton
|
|
93
|
+
active
|
|
94
|
+
avatar={false}
|
|
95
|
+
key={i}
|
|
96
|
+
paragraph={{ rows: 1, width: '100%' }}
|
|
97
|
+
round
|
|
98
|
+
title={false}
|
|
99
|
+
/>
|
|
100
|
+
))}
|
|
101
|
+
</Flexbox>
|
|
102
|
+
) : (
|
|
103
|
+
<Topic />
|
|
104
|
+
)}
|
|
85
105
|
</Flexbox>
|
|
86
106
|
</DraggablePanelBody>
|
|
87
107
|
);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { useSessionStore } from '@/store/session';
|
|
4
|
+
|
|
5
|
+
export const useSessionHydrated = () => {
|
|
6
|
+
const [isInit, setInit] = useState(false);
|
|
7
|
+
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
const hasRehydrated = useSessionStore.persist.hasHydrated();
|
|
10
|
+
|
|
11
|
+
if (hasRehydrated) {
|
|
12
|
+
setInit(true);
|
|
13
|
+
}
|
|
14
|
+
}, []);
|
|
15
|
+
|
|
16
|
+
return isInit;
|
|
17
|
+
};
|
|
@@ -1,39 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
import { PersistOptions, devtools, persist } from 'zustand/middleware';
|
|
3
|
-
import { createWithEqualityFn } from 'zustand/traditional';
|
|
4
|
-
|
|
5
|
-
import { isDev } from '@/utils/env';
|
|
6
|
-
|
|
7
|
-
import { SessionStore, createStore } from './store';
|
|
8
|
-
|
|
9
|
-
type SessionPersist = Pick<SessionStore, 'sessions'>;
|
|
10
|
-
|
|
11
|
-
export const LOBE_CHAT = 'LOBE_CHAT';
|
|
12
|
-
|
|
13
|
-
const persistOptions: PersistOptions<SessionStore, SessionPersist> = {
|
|
14
|
-
name: LOBE_CHAT,
|
|
15
|
-
|
|
16
|
-
partialize: (s) => ({
|
|
17
|
-
sessions: s.sessions,
|
|
18
|
-
}),
|
|
19
|
-
|
|
20
|
-
// 手动控制 Hydration ,避免 ssr 报错
|
|
21
|
-
skipHydration: true,
|
|
22
|
-
version: 0,
|
|
23
|
-
// version: Migration.targetVersion,
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
export const useSessionStore = createWithEqualityFn<SessionStore>()(
|
|
27
|
-
persist(
|
|
28
|
-
devtools(createStore, {
|
|
29
|
-
name: LOBE_CHAT + (isDev ? '_DEV' : ''),
|
|
30
|
-
}),
|
|
31
|
-
persistOptions,
|
|
32
|
-
),
|
|
33
|
-
equal,
|
|
34
|
-
);
|
|
35
|
-
|
|
1
|
+
export * from './hooks/useSessionHydrated';
|
|
36
2
|
export * from './selectors';
|
|
37
3
|
export { initialLobeAgentConfig } from './slices/agentConfig';
|
|
38
4
|
export { initLobeSession } from './slices/session';
|
|
39
5
|
export type { SessionStore } from './store';
|
|
6
|
+
export { useSessionStore } from './store';
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
+
import equal from 'fast-deep-equal';
|
|
2
|
+
import { PersistOptions, devtools, persist } from 'zustand/middleware';
|
|
3
|
+
import { createWithEqualityFn } from 'zustand/traditional';
|
|
1
4
|
import { StateCreator } from 'zustand/vanilla';
|
|
2
5
|
|
|
6
|
+
import { isDev } from '@/utils/env';
|
|
7
|
+
|
|
3
8
|
import { SessionStoreState, initialState } from './initialState';
|
|
4
9
|
import { AgentAction, createAgentSlice } from './slices/agentConfig';
|
|
5
10
|
import { ChatAction, createChatSlice } from './slices/chat';
|
|
@@ -7,11 +12,36 @@ import { SessionAction, createSessionSlice } from './slices/session';
|
|
|
7
12
|
|
|
8
13
|
export type SessionStore = SessionAction & AgentAction & ChatAction & SessionStoreState;
|
|
9
14
|
|
|
10
|
-
|
|
11
|
-
...parameters
|
|
12
|
-
) => ({
|
|
15
|
+
const createStore: StateCreator<SessionStore, [['zustand/devtools', never]]> = (...parameters) => ({
|
|
13
16
|
...initialState,
|
|
14
17
|
...createAgentSlice(...parameters),
|
|
15
18
|
...createSessionSlice(...parameters),
|
|
16
19
|
...createChatSlice(...parameters),
|
|
17
20
|
});
|
|
21
|
+
|
|
22
|
+
type SessionPersist = Pick<SessionStore, 'sessions'>;
|
|
23
|
+
|
|
24
|
+
const LOBE_CHAT = 'LOBE_CHAT';
|
|
25
|
+
|
|
26
|
+
const persistOptions: PersistOptions<SessionStore, SessionPersist> = {
|
|
27
|
+
name: LOBE_CHAT,
|
|
28
|
+
|
|
29
|
+
partialize: (s) => ({
|
|
30
|
+
sessions: s.sessions,
|
|
31
|
+
}),
|
|
32
|
+
|
|
33
|
+
// 手动控制 Hydration ,避免 ssr 报错
|
|
34
|
+
skipHydration: true,
|
|
35
|
+
version: 0,
|
|
36
|
+
// version: Migration.targetVersion,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const useSessionStore = createWithEqualityFn<SessionStore>()(
|
|
40
|
+
persist(
|
|
41
|
+
devtools(createStore, {
|
|
42
|
+
name: LOBE_CHAT + (isDev ? '_DEV' : ''),
|
|
43
|
+
}),
|
|
44
|
+
persistOptions,
|
|
45
|
+
),
|
|
46
|
+
equal,
|
|
47
|
+
);
|