Skip to content

Commit 80f7248

Browse files
committed
feat(fe): add resizable desktop chat sidebar with persisted width sync
1 parent 77a8c6c commit 80f7248

8 files changed

Lines changed: 274 additions & 29 deletions

File tree

src/FE/actions/setting.actions.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { saveSettings, getSettings } from '@/utils/settings';
1+
import {
2+
clampChatbarWidth,
3+
getSettings,
4+
saveSettings,
5+
} from '@/utils/settings';
26
import {
37
SettingActionTypes,
48
SettingsAction,
@@ -15,6 +19,23 @@ export const setShowChatBar = (showChatBar: boolean): SettingsAction => {
1519
};
1620
};
1721

22+
export const setChatBarWidth = (
23+
chatBarWidth: number,
24+
persist: boolean = true,
25+
): SettingsAction => {
26+
const nextWidth = clampChatbarWidth(chatBarWidth);
27+
28+
if (persist) {
29+
const currentSettings = getSettings();
30+
saveSettings({ ...currentSettings, chatBarWidth: nextWidth });
31+
}
32+
33+
return {
34+
type: SettingActionTypes.CHAT_BAR_WIDTH,
35+
payload: nextWidth,
36+
};
37+
};
38+
1839
export const setShowChatInput = (showChatInput: boolean): SettingsAction => ({
1940
type: SettingActionTypes.SHOW_CHAT_INPUT,
2041
payload: showChatInput,

src/FE/components/Chat/ChatView.tsx

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,8 @@ import {
4848
ResponseMessageTempId,
4949
SseResponseKind,
5050
SseResponseLine,
51-
getMessageContents,
5251
} from '@/types/chatMessage';
5352
import { ChatSpanDto } from '@/types/clientApis';
54-
import { Prompt } from '@/types/prompt';
5553

5654
import {
5755
setChats,
@@ -73,7 +71,6 @@ import NoModel from './NoModel';
7371

7472
import {
7573
deleteMessage,
76-
getTurnGenerateInfo,
7774
putChats,
7875
putMessageReactionClear,
7976
putMessageReactionUp,
@@ -82,7 +79,6 @@ import {
8279
responseContentToRequest,
8380
} from '@/apis/clientApis';
8481
import { streamGeneralChat, streamRegenerateAssistant, streamRegenerateAllAssistant, ChatApiError } from '@/apis/chatApi';
85-
import { cn } from '@/lib/utils';
8682

8783
const ChatView = memo(() => {
8884
const { t } = useTranslation();
@@ -94,7 +90,7 @@ const ChatView = memo(() => {
9490
models,
9591
modelMap,
9692
showChatBar,
97-
showChatInput,
93+
effectiveChatBarWidth,
9894
isMessagesLoading,
9995
},
10096
selectedChat,
@@ -505,15 +501,6 @@ const ChatView = memo(() => {
505501
};
506502

507503
// Helper to add a new step to a message (used when EndStep is received)
508-
const addNewStepToMessage = (
509-
msg: IChatMessage,
510-
stepData: IStep,
511-
): IChatMessage => {
512-
return {
513-
...msg,
514-
steps: [...msg.steps, { ...stepData, contents: [] }],
515-
};
516-
};
517504

518505
// Handle EndStep event: finalize current step and start a new one
519506
const changeSelectedResponseEndStep = (
@@ -1076,7 +1063,7 @@ const ChatView = memo(() => {
10761063
if (!selectedChat) return;
10771064
if (!checkSelectChatModelIsExist(selectedChat.spans)) return;
10781065
startChat();
1079-
let { id: chatId, spans: chatSpans } = selectedChat;
1066+
let { id: chatId } = selectedChat;
10801067
let index = selectedMessages.findIndex(
10811068
(x) => x.findIndex((m) => m.id === messageId) !== -1,
10821069
);
@@ -1275,7 +1262,7 @@ const ChatView = memo(() => {
12751262
],
12761263
);
12771264

1278-
const handleChangePrompt = (prompt: Prompt) => {
1265+
const handleChangePrompt = () => {
12791266
// to do
12801267
};
12811268

@@ -1684,7 +1671,7 @@ const ChatView = memo(() => {
16841671
<div
16851672
className="sm:w-full chat-container"
16861673
style={{
1687-
width: `calc(100vw - ${showChatBar ? 280 : 0}px)`,
1674+
width: `calc(100vw - ${showChatBar ? effectiveChatBarWidth : 0}px)`,
16881675
}}
16891676
>
16901677
{selectedMessages.length === 0 && <ChatPresetList />}

src/FE/components/Chatbar/Chatbar.tsx

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@ import {
1414
setChats,
1515
setChatsSelectType,
1616
} from '@/actions/chat.actions';
17-
import { setShowChatBar } from '@/actions/setting.actions';
17+
import {
18+
setChatBarWidth,
19+
setShowChatBar,
20+
} from '@/actions/setting.actions';
21+
import { MIN_CHATBAR_WIDTH } from '@/utils/settings';
1822
import HomeContext from '@/contexts/home.context';
1923
import Sidebar from '../Sidebar/Sidebar';
2024
import ChatActionConfirm from './ChatActionConfirm';
@@ -39,6 +43,9 @@ const Chatbar = () => {
3943
chatGroups,
4044
chatPaging,
4145
showChatBar,
46+
effectiveChatBarWidth,
47+
chatBarMaxWidth,
48+
chatBarWidth,
4249
isChatsLoading,
4350
chatsSelectType,
4451
},
@@ -63,6 +70,13 @@ const Chatbar = () => {
6370
settingDispatch(setShowChatBar(!showChatBar));
6471
};
6572

73+
const handleDesktopWidthChange = (
74+
width: number,
75+
options?: { persist?: boolean },
76+
) => {
77+
settingDispatch(setChatBarWidth(width, options?.persist ?? false));
78+
};
79+
6680
const handleAddGroup = () => {
6781
const groupNames = chatGroups.map((x) => x.name);
6882
const name = getNextName(groupNames, t('New Group'));
@@ -147,6 +161,11 @@ const Chatbar = () => {
147161
messageIsStreaming={selectedChat?.status === ChatStatus.Chatting}
148162
side={'left'}
149163
isOpen={showChatBar}
164+
resizable
165+
desktopWidth={effectiveChatBarWidth || chatBarWidth}
166+
desktopMinWidth={MIN_CHATBAR_WIDTH}
167+
desktopMaxWidth={chatBarMaxWidth}
168+
onDesktopWidthChange={handleDesktopWidthChange}
150169
addItemButtonTitle={t('New chat')}
151170
hasModel={hasModel}
152171
folderComponent={<ChatGroups onShowMore={handleShowMore} />}

src/FE/components/Home/HomeContent.tsx

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import { useEffect, useReducer, useState, useMemo } from 'react';
33
import { useRouter } from 'next/router';
44

55
import { useCreateReducer } from '@/hooks/useCreateReducer';
6+
import { useIsMobile } from '@/hooks/useMobile';
67
import useTranslation from '@/hooks/useTranslation';
78

89
import { currentISODateString } from '@/utils/date';
910
import { findSelectedMessageByLeafId } from '@/utils/message';
10-
import { getSettings } from '@/utils/settings';
1111
import { getUserSession, redirectToLoginPage } from '@/utils/user';
1212

1313
import {
@@ -36,6 +36,7 @@ import {
3636
import { setModelMap, setModels } from '@/actions/model.actions';
3737
import { setDefaultPrompt, setPrompts } from '@/actions/prompt.actions';
3838
import {
39+
setChatBarWidth,
3940
setShowChatBar,
4041
} from '@/actions/setting.actions';
4142
import HomeContext, {
@@ -67,10 +68,16 @@ import {
6768
postChats,
6869
stopChat,
6970
} from '@/apis/clientApis';
71+
import {
72+
getDesktopChatbarMaxWidth,
73+
getEffectiveChatbarWidth,
74+
getSettings,
75+
} from '@/utils/settings';
7076

7177
const HomeContent = () => {
7278
const router = useRouter();
7379
const { t } = useTranslation();
80+
const isMobile = useIsMobile();
7481
const [chatState, chatDispatch] = useReducer(chatReducer, chatInitialState);
7582
const [messageState, messageDispatch] = useReducer(
7683
messageReducer,
@@ -88,9 +95,27 @@ const HomeContent = () => {
8895
promptReducer,
8996
promptInitialState,
9097
);
98+
const [viewportWidth, setViewportWidth] = useState<number>(
99+
typeof window !== 'undefined' ? window.innerWidth : 1200,
100+
);
91101

92102
const { chats, chatPaging, stopIds, selectedChatId, chatGroups } = chatState;
93103
const { models } = modelState;
104+
const { showChatBar, chatBarWidth } = settingState;
105+
const chatBarMaxWidth = useMemo(
106+
() => getDesktopChatbarMaxWidth(viewportWidth),
107+
[viewportWidth],
108+
);
109+
const effectiveChatBarWidth = useMemo(
110+
() =>
111+
getEffectiveChatbarWidth({
112+
preferredWidth: chatBarWidth,
113+
viewportWidth,
114+
isMobileView: isMobile,
115+
isOpen: showChatBar,
116+
}),
117+
[chatBarWidth, isMobile, showChatBar, viewportWidth],
118+
);
94119

95120
// 解析 hash 中的 chatId,例如 "#/abc" -> "abc"
96121
const getHashChatId = (): string | undefined => {
@@ -378,8 +403,24 @@ const HomeContent = () => {
378403

379404
useEffect(() => {
380405
// 加载设置
381-
const { showChatBar } = getSettings();
382-
settingDispatch(setShowChatBar(showChatBar));
406+
const settings = getSettings();
407+
settingDispatch(setShowChatBar(settings.showChatBar));
408+
settingDispatch(setChatBarWidth(settings.chatBarWidth, false));
409+
}, []);
410+
411+
useEffect(() => {
412+
if (typeof window === 'undefined') return undefined;
413+
414+
const handleResize = () => {
415+
setViewportWidth(window.innerWidth);
416+
};
417+
418+
handleResize();
419+
window.addEventListener('resize', handleResize);
420+
421+
return () => {
422+
window.removeEventListener('resize', handleResize);
423+
};
383424
}, []);
384425

385426
useEffect(() => {
@@ -438,6 +479,8 @@ const HomeContent = () => {
438479
...modelState,
439480
...settingState,
440481
...promptState,
482+
effectiveChatBarWidth,
483+
chatBarMaxWidth,
441484
},
442485
selectedChat,
443486
chatDispatch: chatDispatch,

0 commit comments

Comments
 (0)