diff --git a/src/app/(main)/chat/(workspace)/@conversation/default.tsx b/src/app/(main)/chat/(workspace)/@conversation/default.tsx
index 734bd06e2c796..9acfb8ee32062 100644
--- a/src/app/(main)/chat/(workspace)/@conversation/default.tsx
+++ b/src/app/(main)/chat/(workspace)/@conversation/default.tsx
@@ -1,9 +1,9 @@
-import Conversation from '@/features/Conversation';
import { isMobileDevice } from '@/utils/server/responsive';
import ChatHydration from './features/ChatHydration';
import DesktopChatInput from './features/ChatInput/Desktop';
import MobileChatInput from './features/ChatInput/Mobile';
+import ChatList from './features/ChatList';
import ZenModeToast from './features/ZenModeToast';
const ChatConversation = () => {
@@ -13,7 +13,7 @@ const ChatConversation = () => {
return (
<>
-
+
>
diff --git a/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Header/index.tsx b/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Header/index.tsx
index c5d68d61887cc..562b66ca88456 100644
--- a/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Header/index.tsx
+++ b/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Header/index.tsx
@@ -3,14 +3,19 @@ import { Maximize2, Minimize2 } from 'lucide-react';
import { memo } from 'react';
import ActionBar from '@/features/ChatInput/ActionBar';
+import { ActionKeys } from '@/features/ChatInput/types';
interface HeaderProps {
expand: boolean;
+ leftActions: ActionKeys[];
+ rightActions: ActionKeys[];
setExpand: (expand: boolean) => void;
}
-const Header = memo(({ expand, setExpand }) => (
+const Header = memo(({ expand, setExpand, leftActions, rightActions }) => (
{
- const [expand, setExpand] = useState(false);
+const defaultLeftActions = [
+ 'model',
+ 'fileUpload',
+ 'knowledgeBase',
+ 'temperature',
+ 'history',
+ 'stt',
+ 'tools',
+ 'token',
+] as ActionKeys[];
- const [inputHeight, updatePreference] = useGlobalStore((s) => [
- systemStatusSelectors.inputHeight(s),
- s.updateSystemStatus,
- ]);
+const defaultRightActions = ['clear'] as ActionKeys[];
- return (
- <>
- {!expand && }
- {
- if (!size) return;
+interface DesktopChatInputProps {
+ leftActions?: ActionKeys[];
+ rightActions?: ActionKeys[];
+}
+const DesktopChatInput = memo(
+ ({ leftActions = defaultLeftActions, rightActions = defaultRightActions }) => {
+ const [expand, setExpand] = useState(false);
- updatePreference({
- inputHeight:
- typeof size.height === 'string' ? Number.parseInt(size.height) : size.height,
- });
- }}
- placement="bottom"
- size={{ height: inputHeight, width: '100%' }}
- style={{ zIndex: 10 }}
- >
- [
+ systemStatusSelectors.inputHeight(s),
+ s.updateSystemStatus,
+ ]);
+
+ return (
+ <>
+ {!expand && }
+ {
+ if (!size) return;
+
+ updatePreference({
+ inputHeight:
+ typeof size.height === 'string' ? Number.parseInt(size.height) : size.height,
+ });
+ }}
+ placement="bottom"
+ size={{ height: inputHeight, width: '100%' }}
+ style={{ zIndex: 10 }}
>
-
-
-
-
-
- >
- );
-});
+
+
+
+
+
+
+ >
+ );
+ },
+);
DesktopChatInput.displayName = 'DesktopChatInput';
diff --git a/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/index.tsx b/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/index.tsx
index 793601bfcfd05..5e0c4bc80ef45 100644
--- a/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/index.tsx
+++ b/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/index.tsx
@@ -5,6 +5,7 @@ import { TextAreaRef } from 'antd/es/input/TextArea';
import { memo, useRef, useState } from 'react';
import ActionBar from '@/features/ChatInput/ActionBar';
+import { ActionKeys } from '@/features/ChatInput/ActionBar/config';
import STT from '@/features/ChatInput/STT';
import SaveTopic from '@/features/ChatInput/Topic';
import { useSendMessage } from '@/features/ChatInput/useSend';
@@ -15,6 +16,18 @@ import Files from './Files';
import InputArea from './InputArea';
import SendButton from './Send';
+const defaultLeftActions = [
+ 'model',
+ 'fileUpload',
+ 'knowledgeBase',
+ 'temperature',
+ 'history',
+ 'tools',
+ 'token',
+] as ActionKeys[];
+
+const defaultRightActions = ['clear'] as ActionKeys[];
+
const MobileChatInput = memo(() => {
const theme = useTheme();
const ref = useRef(null);
@@ -52,7 +65,12 @@ const MobileChatInput = memo(() => {
topAddons={
<>
- } />
+ }
+ />
>
}
value={value}
diff --git a/src/app/(main)/chat/(workspace)/@conversation/features/ChatList/Content.tsx b/src/app/(main)/chat/(workspace)/@conversation/features/ChatList/Content.tsx
new file mode 100644
index 0000000000000..1565f44340bb4
--- /dev/null
+++ b/src/app/(main)/chat/(workspace)/@conversation/features/ChatList/Content.tsx
@@ -0,0 +1,35 @@
+'use client';
+
+import isEqual from 'fast-deep-equal';
+import React, { memo } from 'react';
+
+import { WELCOME_GUIDE_CHAT_ID } from '@/const/session';
+import { VirtualizedList } from '@/features/Conversation';
+import { useChatStore } from '@/store/chat';
+import { chatSelectors } from '@/store/chat/selectors';
+import { useSessionStore } from '@/store/session';
+
+interface ListProps {
+ mobile?: boolean;
+}
+
+const Content = memo(({ mobile }) => {
+ const [activeTopicId, useFetchMessages] = useChatStore((s) => [
+ s.activeTopicId,
+ s.useFetchMessages,
+ ]);
+
+ const [sessionId] = useSessionStore((s) => [s.activeId]);
+ useFetchMessages(sessionId, activeTopicId);
+
+ const data = useChatStore((s) => {
+ const showInboxWelcome = chatSelectors.showInboxWelcome(s);
+ if (showInboxWelcome) return [WELCOME_GUIDE_CHAT_ID];
+
+ return chatSelectors.currentChatIDsWithGuideMessage(s);
+ }, isEqual);
+
+ return ;
+});
+
+export default Content;
diff --git a/src/app/(main)/chat/(workspace)/@conversation/features/ChatList/index.tsx b/src/app/(main)/chat/(workspace)/@conversation/features/ChatList/index.tsx
new file mode 100644
index 0000000000000..57c561cab0b25
--- /dev/null
+++ b/src/app/(main)/chat/(workspace)/@conversation/features/ChatList/index.tsx
@@ -0,0 +1,28 @@
+import { Suspense, lazy } from 'react';
+import { Flexbox } from 'react-layout-kit';
+
+import { SkeletonList } from '@/features/Conversation';
+
+const Content = lazy(() => import('./Content'));
+
+interface ChatListProps {
+ mobile?: boolean;
+}
+
+const ChatList = ({ mobile }: ChatListProps) => (
+
+ }>
+
+
+
+);
+
+export default ChatList;
diff --git a/src/features/ChatInput/ActionBar/config.ts b/src/features/ChatInput/ActionBar/config.ts
index 24915ee988cc9..3d86c2595aa3d 100644
--- a/src/features/ChatInput/ActionBar/config.ts
+++ b/src/features/ChatInput/ActionBar/config.ts
@@ -20,23 +20,4 @@ export const actionMap = {
tools: Tools,
} as const;
-type ActionMap = typeof actionMap;
-
-export type ActionKeys = keyof ActionMap;
-
-type getActionList = (mobile?: boolean) => ActionKeys[];
-
-// we can make these action lists configurable in the future
-export const getLeftActionList: getActionList = (mobile) =>
- [
- 'model',
- 'fileUpload',
- 'knowledgeBase',
- 'temperature',
- 'history',
- !mobile && 'stt',
- 'tools',
- 'token',
- ].filter(Boolean) as ActionKeys[];
-
-export const getRightActionList: getActionList = () => ['clear'].filter(Boolean) as ActionKeys[];
+export type ActionKeys = keyof typeof actionMap;
diff --git a/src/features/ChatInput/ActionBar/index.tsx b/src/features/ChatInput/ActionBar/index.tsx
index 3ef9ee73b7ef7..87045fd94395b 100644
--- a/src/features/ChatInput/ActionBar/index.tsx
+++ b/src/features/ChatInput/ActionBar/index.tsx
@@ -1,7 +1,7 @@
import { ChatInputActionBar } from '@bentwnghk/ui';
-import { ReactNode, memo, useMemo } from 'react';
+import { ReactNode, memo } from 'react';
-import { ActionKeys, actionMap, getLeftActionList, getRightActionList } from './config';
+import { ActionKeys, actionMap } from './config';
const RenderActionList = ({ dataSource }: { dataSource: ActionKeys[] }) => (
<>
@@ -13,10 +13,11 @@ const RenderActionList = ({ dataSource }: { dataSource: ActionKeys[] }) => (
);
export interface ActionBarProps {
+ leftActions: ActionKeys[];
leftAreaEndRender?: ReactNode;
leftAreaStartRender?: ReactNode;
- mobile?: boolean;
padding?: number | string;
+ rightActions: ActionKeys[];
rightAreaEndRender?: ReactNode;
rightAreaStartRender?: ReactNode;
}
@@ -24,35 +25,31 @@ export interface ActionBarProps {
const ActionBar = memo(
({
padding = '0 16px',
- mobile,
rightAreaStartRender,
rightAreaEndRender,
leftAreaStartRender,
leftAreaEndRender,
- }) => {
- const leftActionList = useMemo(() => getLeftActionList(mobile), [mobile]);
- const rightActionList = useMemo(() => getRightActionList(mobile), [mobile]);
-
- return (
-
- {leftAreaStartRender}
-
- {leftAreaEndRender}
- >
- }
- padding={padding}
- rightAddons={
- <>
- {rightAreaStartRender}
-
- {rightAreaEndRender}
- >
- }
- />
- );
- },
+ leftActions,
+ rightActions,
+ }) => (
+
+ {leftAreaStartRender}
+
+ {leftAreaEndRender}
+ >
+ }
+ padding={padding}
+ rightAddons={
+ <>
+ {rightAreaStartRender}
+
+ {rightAreaEndRender}
+ >
+ }
+ />
+ ),
);
export default ActionBar;
diff --git a/src/features/ChatInput/types.ts b/src/features/ChatInput/types.ts
new file mode 100644
index 0000000000000..755d6c687f2ea
--- /dev/null
+++ b/src/features/ChatInput/types.ts
@@ -0,0 +1 @@
+export type { ActionKeys } from './ActionBar/config';
diff --git a/src/features/Conversation/components/ChatItem/index.tsx b/src/features/Conversation/components/ChatItem/index.tsx
index b73b5e413fd57..db883d5f1371b 100644
--- a/src/features/Conversation/components/ChatItem/index.tsx
+++ b/src/features/Conversation/components/ChatItem/index.tsx
@@ -166,7 +166,7 @@ const Item = memo(({ index, id }) => {
return (
item && (
<>
- {enableHistoryDivider && }
+ {enableHistoryDivider && }
(({ mobile }) => {
+
+const VirtualizedList = memo(({ mobile, dataSource }) => {
const virtuosoRef = useRef(null);
const [atBottom, setAtBottom] = useState(true);
const [isScrolling, setIsScrolling] = useState(false);
const [id] = useChatStore((s) => [chatSelectors.currentChatKey(s)]);
-
- const [activeTopicId, useFetchMessages, isFirstLoading, isCurrentChatLoaded] = useChatStore(
- (s) => [
- s.activeTopicId,
- s.useFetchMessages,
- chatSelectors.currentChatLoadingState(s),
- chatSelectors.isCurrentChatLoaded(s),
- ],
- );
-
- const [sessionId] = useSessionStore((s) => [s.activeId]);
- useFetchMessages(sessionId, activeTopicId);
-
- const data = useChatStore((s) => {
- const showInboxWelcome = chatSelectors.showInboxWelcome(s);
- return showInboxWelcome
- ? [WELCOME_GUIDE_CHAT_ID]
- : chatSelectors.currentChatIDsWithGuideMessage(s);
- }, isEqual);
+ const [isFirstLoading, isCurrentChatLoaded] = useChatStore((s) => [
+ chatSelectors.currentChatLoadingState(s),
+ chatSelectors.isCurrentChatLoaded(s),
+ ]);
useEffect(() => {
if (virtuosoRef.current) {
@@ -54,13 +39,13 @@ const VirtualizedList = memo(({ mobile }) => {
}
}, [id]);
- const prevDataLengthRef = useRef(data.length);
+ const prevDataLengthRef = useRef(dataSource.length);
const getFollowOutput = useCallback(() => {
- const newFollowOutput = data.length > prevDataLengthRef.current ? 'auto' : false;
- prevDataLengthRef.current = data.length;
+ const newFollowOutput = dataSource.length > prevDataLengthRef.current ? 'auto' : false;
+ prevDataLengthRef.current = dataSource.length;
return newFollowOutput;
- }, [data.length]);
+ }, [dataSource.length]);
const theme = useTheme();
// overscan should be 3 times the height of the window
@@ -100,10 +85,10 @@ const VirtualizedList = memo(({ mobile }) => {
atBottomStateChange={setAtBottom}
atBottomThreshold={50 * (mobile ? 2 : 1)}
computeItemKey={(_, item) => item}
- data={data}
+ data={dataSource}
followOutput={getFollowOutput}
increaseViewportBy={overscan}
- initialTopMostItemIndex={data?.length - 1}
+ initialTopMostItemIndex={dataSource?.length - 1}
isScrolling={setIsScrolling}
itemContent={itemContent}
overscan={overscan}
diff --git a/src/features/Conversation/index.ts b/src/features/Conversation/index.ts
new file mode 100644
index 0000000000000..41edbb27ea078
--- /dev/null
+++ b/src/features/Conversation/index.ts
@@ -0,0 +1,2 @@
+export { default as SkeletonList } from './components/SkeletonList';
+export { default as VirtualizedList } from './components/VirtualizedList';
diff --git a/src/features/Conversation/index.tsx b/src/features/Conversation/index.tsx
deleted file mode 100644
index 09d2c265ede37..0000000000000
--- a/src/features/Conversation/index.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-import { Suspense, lazy } from 'react';
-import { Flexbox } from 'react-layout-kit';
-
-import SkeletonList from './components/SkeletonList';
-
-const ChatList = lazy(() => import('./components/VirtualizedList'));
-
-interface ConversationProps {
- mobile?: boolean;
-}
-
-const Conversation = ({ mobile }: ConversationProps) => {
- return (
-
- }>
-
-
-
- );
-};
-
-export default Conversation;