@lobehub/chat 0.40.4 → 0.40.6

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 CHANGED
@@ -2,6 +2,64 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 0.40.6](https://github.com/lobehub/lobe-chat/compare/v0.40.5...v0.40.6)
6
+
7
+ <sup>Released on **2023-08-10**</sup>
8
+
9
+ #### ♻ Code Refactoring
10
+
11
+ - **misc**: 重构优化 hydrated 的判断逻辑.
12
+
13
+ #### 💄 Styles
14
+
15
+ - **misc**: 优化水合前的加载效果.
16
+
17
+ <br/>
18
+
19
+ <details>
20
+ <summary><kbd>Improvements and Fixes</kbd></summary>
21
+
22
+ #### Code refactoring
23
+
24
+ - **misc**: 重构优化 hydrated 的判断逻辑 ([1781119](https://github.com/lobehub/lobe-chat/commit/1781119))
25
+
26
+ #### Styles
27
+
28
+ - **misc**: 优化水合前的加载效果 ([6bbd978](https://github.com/lobehub/lobe-chat/commit/6bbd978))
29
+
30
+ </details>
31
+
32
+ <div align="right">
33
+
34
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
35
+
36
+ </div>
37
+
38
+ ### [Version 0.40.5](https://github.com/lobehub/lobe-chat/compare/v0.40.4...v0.40.5)
39
+
40
+ <sup>Released on **2023-08-10**</sup>
41
+
42
+ #### 💄 Styles
43
+
44
+ - **misc**: 增加未初始化的 loading 态.
45
+
46
+ <br/>
47
+
48
+ <details>
49
+ <summary><kbd>Improvements and Fixes</kbd></summary>
50
+
51
+ #### Styles
52
+
53
+ - **misc**: 增加未初始化的 loading 态 ([dcb7c07](https://github.com/lobehub/lobe-chat/commit/dcb7c07))
54
+
55
+ </details>
56
+
57
+ <div align="right">
58
+
59
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
60
+
61
+ </div>
62
+
5
63
  ### [Version 0.40.4](https://github.com/lobehub/lobe-chat/compare/v0.40.3...v0.40.4)
6
64
 
7
65
  <sup>Released on **2023-08-10**</sup>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "0.40.4",
3
+ "version": "0.40.6",
4
4
  "description": "Lobe Chat is an open-source chatbot client using LangChain, Typescript and Next.js",
5
5
  "keywords": [
6
6
  "chatbot",
@@ -0,0 +1,47 @@
1
+ import { Skeleton } from 'antd';
2
+ import { createStyles } from 'antd-style';
3
+ import { Flexbox } from 'react-layout-kit';
4
+
5
+ const useStyles = createStyles(({ css }) => ({
6
+ avatar: css``,
7
+ paragraph: css`
8
+ height: 12px !important;
9
+ margin-top: 12px !important;
10
+
11
+ > li {
12
+ height: 12px !important;
13
+ }
14
+ `,
15
+ title: css`
16
+ height: 14px !important;
17
+ margin-top: 4px !important;
18
+ margin-bottom: 12px !important;
19
+
20
+ > li {
21
+ height: 14px !important;
22
+ }
23
+ `,
24
+ }));
25
+
26
+ // 从 3~10 随机取一个整数
27
+
28
+ const SkeletonItem = () => {
29
+ const { styles } = useStyles();
30
+
31
+ const list = Array.from({ length: 4 }).fill('');
32
+ return (
33
+ <Flexbox gap={8} paddingInline={16}>
34
+ {list.map((_, index) => (
35
+ <Skeleton
36
+ active
37
+ avatar
38
+ key={index}
39
+ paragraph={{ className: styles.paragraph, rows: 1 }}
40
+ round
41
+ title={{ className: styles.title }}
42
+ />
43
+ ))}
44
+ </Flexbox>
45
+ );
46
+ };
47
+ export default SkeletonItem;
@@ -3,9 +3,10 @@ import Link from 'next/link';
3
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
+ import SkeletonItem from './SkeletonItem';
9
10
 
10
11
  const SessionList = memo(() => {
11
12
  const list = useSessionStore((s) => sessionSelectors.sessionList(s), isEqual);
@@ -14,7 +15,11 @@ const SessionList = memo(() => {
14
15
  shallow,
15
16
  );
16
17
 
17
- return (
18
+ const isInit = useSessionHydrated();
19
+
20
+ return !isInit ? (
21
+ <SkeletonItem />
22
+ ) : (
18
23
  <>
19
24
  {list.map(({ id }) => (
20
25
  <Link href={`/chat/${id}`} key={id}>
@@ -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 { agentSelectors, chatSelectors, useSessionStore } from '@/store/session';
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
- <Flexbox align={'flex-start'} horizontal>
42
- <Avatar
43
- avatar={avatar}
44
- background={backgroundColor}
45
- onClick={() => {
46
- Router.push(`/chat/${id}/setting`);
47
- }}
48
- size={40}
49
- style={{ cursor: 'pointer', flex: 'none' }}
50
- title={title}
51
- />
52
- <HeaderTitle
53
- desc={description}
54
- tag={
55
- <>
56
- <Tag>
57
- <SiOpenai size={'11px'} style={{ marginTop: 3 }} />
58
- {model}
59
- </Tag>
60
- {plugins?.length > 0 && <PluginTag plugins={plugins} />}
61
- </>
62
- }
63
- title={title}
64
- />
65
- </Flexbox>
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,24 +61,30 @@ const Inner = memo(() => {
62
61
  }
63
62
  title={t('settingAgent.prompt.title', { ns: 'setting' })}
64
63
  />
65
- <EditableMessage
66
- classNames={{ markdown: styles.prompt }}
67
- onChange={(e) => {
68
- updateAgentConfig({ systemRole: e });
69
- }}
70
- onOpenChange={setOpenModal}
71
- openModal={openModal}
72
- placeholder={`${t('settingAgent.prompt.placeholder', { ns: 'setting' })}...`}
73
- styles={{ markdown: systemRole ? {} : { opacity: 0.5 } }}
74
- text={{
75
- cancel: t('cancel'),
76
- confirm: t('ok'),
77
- edit: t('edit'),
78
- title: t('settingAgent.prompt.title', { ns: 'setting' }),
79
- }}
80
- value={systemRole}
81
- />
82
- <Flexbox gap={16} style={{ padding: 16 }}>
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
  <Topic />
85
90
  </Flexbox>
@@ -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
- import equal from 'fast-deep-equal';
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
- export const createStore: StateCreator<SessionStore, [['zustand/devtools', never]]> = (
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
+ );