Skip to content

Commit 1a9a7dc

Browse files
committed
2 parents 856ba12 + 00f6189 commit 1a9a7dc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+378
-112
lines changed

components/Chat/Chat.tsx

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { IconArrowDown, IconClearAll, IconSettings } from '@tabler/icons-react';
1+
import { IconClearAll, IconSettings } from '@tabler/icons-react';
22
import {
33
MutableRefObject,
44
memo,
@@ -467,6 +467,11 @@ export const Chat = memo(({ stopConversationRef }: Props) => {
467467
key={index}
468468
message={message}
469469
messageIndex={index}
470+
onEdit={(editedMessage) => {
471+
setCurrentMessage(editedMessage);
472+
// discard edited message and the ones that come after then resend
473+
handleSend(editedMessage, selectedConversation?.messages.length - index);
474+
}}
470475
/>
471476
))}
472477

@@ -487,24 +492,16 @@ export const Chat = memo(({ stopConversationRef }: Props) => {
487492
setCurrentMessage(message);
488493
handleSend(message, 0, plugin);
489494
}}
495+
onScrollDownClick={handleScrollDown}
490496
onRegenerate={() => {
491497
if (currentMessage) {
492498
handleSend(currentMessage, 2, null);
493499
}
494500
}}
501+
showScrollDownButton={showScrollDownButton}
495502
/>
496503
</>
497504
)}
498-
{showScrollDownButton && (
499-
<div className="absolute bottom-0 right-0 mb-4 mr-4 pb-20">
500-
<button
501-
className="flex h-7 w-7 items-center justify-center rounded-full bg-neutral-300 text-gray-800 shadow-md hover:shadow-lg focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-neutral-200"
502-
onClick={handleScrollDown}
503-
>
504-
<IconArrowDown size={18} />
505-
</button>
506-
</div>
507-
)}
508505
</div>
509506
);
510507
});

components/Chat/ChatInput.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
IconArrowDown,
23
IconBolt,
34
IconBrandGoogle,
45
IconPlayerStop,
@@ -30,15 +31,19 @@ import { VariableModal } from './VariableModal';
3031
interface Props {
3132
onSend: (message: Message, plugin: Plugin | null) => void;
3233
onRegenerate: () => void;
34+
onScrollDownClick: () => void;
3335
stopConversationRef: MutableRefObject<boolean>;
3436
textareaRef: MutableRefObject<HTMLTextAreaElement | null>;
37+
showScrollDownButton: boolean;
3538
}
3639

3740
export const ChatInput = ({
3841
onSend,
3942
onRegenerate,
43+
onScrollDownClick,
4044
stopConversationRef,
4145
textareaRef,
46+
showScrollDownButton
4247
}: Props) => {
4348
const { t } = useTranslation('chat');
4449

@@ -341,6 +346,17 @@ export const ChatInput = ({
341346
)}
342347
</button>
343348

349+
{showScrollDownButton && (
350+
<div className="absolute bottom-12 right-0 lg:bottom-0 lg:-right-10">
351+
<button
352+
className="flex h-7 w-7 items-center justify-center rounded-full bg-neutral-300 text-gray-800 shadow-md hover:shadow-lg focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-neutral-200"
353+
onClick={onScrollDownClick}
354+
>
355+
<IconArrowDown size={18} />
356+
</button>
357+
</div>
358+
)}
359+
344360
{showPromptList && filteredPrompts.length > 0 && (
345361
<div className="absolute bottom-12 w-full">
346362
<PromptList

components/Chat/ChatMessage.tsx

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,10 @@ import remarkMath from 'remark-math';
2626
interface Props {
2727
message: Message;
2828
messageIndex: number;
29+
onEdit?: (editedMessage: Message) => void
2930
}
3031

31-
export const ChatMessage: FC<Props> = memo(({ message, messageIndex }) => {
32+
export const ChatMessage: FC<Props> = memo(({ message, messageIndex, onEdit }) => {
3233
const { t } = useTranslation('chat');
3334

3435
const {
@@ -57,31 +58,8 @@ export const ChatMessage: FC<Props> = memo(({ message, messageIndex }) => {
5758

5859
const handleEditMessage = () => {
5960
if (message.content != messageContent) {
60-
if (selectedConversation) {
61-
const updatedMessages = selectedConversation.messages
62-
.map((m, i) => {
63-
if (i < messageIndex) {
64-
return m;
65-
}
66-
})
67-
.filter((m) => m) as Message[];
68-
69-
const updatedConversation = {
70-
...selectedConversation,
71-
messages: updatedMessages,
72-
};
73-
74-
const { single, all } = updateConversation(
75-
updatedConversation,
76-
conversations,
77-
);
78-
79-
homeDispatch({ field: 'selectedConversation', value: single });
80-
homeDispatch({ field: 'conversations', value: all });
81-
homeDispatch({
82-
field: 'currentMessage',
83-
value: { ...message, content: messageContent },
84-
});
61+
if (selectedConversation && onEdit) {
62+
onEdit({ ...message, content: messageContent });
8563
}
8664
}
8765
setIsEditing(false);

components/Chat/Temperature.tsx

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
import { FC, useState } from 'react';
1+
import { FC, useContext, useState } from 'react';
22

33
import { useTranslation } from 'next-i18next';
44

55
import { DEFAULT_TEMPERATURE } from '@/utils/app/const';
66

7+
import HomeContext from '@/pages/api/home/home.context';
8+
79
interface Props {
810
label: string;
911
onChangeTemperature: (temperature: number) => void;
@@ -13,7 +15,13 @@ export const TemperatureSlider: FC<Props> = ({
1315
label,
1416
onChangeTemperature,
1517
}) => {
16-
const [temperature, setTemperature] = useState(DEFAULT_TEMPERATURE);
18+
const {
19+
state: { conversations },
20+
} = useContext(HomeContext);
21+
const lastConversation = conversations[conversations.length - 1];
22+
const [temperature, setTemperature] = useState(
23+
lastConversation?.temperature ?? DEFAULT_TEMPERATURE,
24+
);
1725
const { t } = useTranslation('chat');
1826
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
1927
const newValue = parseFloat(event.target.value);
@@ -31,7 +39,9 @@ export const TemperatureSlider: FC<Props> = ({
3139
'Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.',
3240
)}
3341
</span>
34-
<span className="mt-2 mb-1 text-center">{temperature.toFixed(1)}</span>
42+
<span className="mt-2 mb-1 text-center text-neutral-900 dark:text-neutral-100">
43+
{temperature.toFixed(1)}
44+
</span>
3545
<input
3646
className="cursor-pointer"
3747
type="range"
@@ -41,7 +51,7 @@ export const TemperatureSlider: FC<Props> = ({
4151
value={temperature}
4252
onChange={handleChange}
4353
/>
44-
<ul className="w mt-2 pb-8 flex justify-between px-[24px]">
54+
<ul className="w mt-2 pb-8 flex justify-between px-[24px] text-neutral-900 dark:text-neutral-100">
4555
<li className="relative flex justify-center">
4656
<span className="absolute">{t('Precise')}</span>
4757
</li>

components/Chatbar/components/ChatbarSettings.tsx

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
1-
import { IconFileExport, IconMoon, IconSun } from '@tabler/icons-react';
2-
import { useContext } from 'react';
1+
import {
2+
IconFileExport,
3+
IconMoon,
4+
IconSettings,
5+
IconSun,
6+
} from '@tabler/icons-react';
7+
import { useContext, useState } from 'react';
38

49
import { useTranslation } from 'next-i18next';
510

611
import HomeContext from '@/pages/api/home/home.context';
712

13+
import { SettingDialog } from '@/components/Settings/SettingDialog';
14+
815
import { Import } from '../../Settings/Import';
916
import { Key } from '../../Settings/Key';
1017
import { SidebarButton } from '../../Sidebar/SidebarButton';
@@ -14,6 +21,7 @@ import { PluginKeys } from './PluginKeys';
1421

1522
export const ChatbarSettings = () => {
1623
const { t } = useTranslation('sidebar');
24+
const [isSettingDialogOpen, setIsSettingDialog] = useState<boolean>(false);
1725

1826
const {
1927
state: {
@@ -49,23 +57,23 @@ export const ChatbarSettings = () => {
4957
/>
5058

5159
<SidebarButton
52-
text={lightMode === 'light' ? t('Dark mode') : t('Light mode')}
53-
icon={
54-
lightMode === 'light' ? <IconMoon size={18} /> : <IconSun size={18} />
55-
}
56-
onClick={() =>
57-
homeDispatch({
58-
field: 'lightMode',
59-
value: lightMode === 'light' ? 'dark' : 'light',
60-
})
61-
}
60+
text={t('Settings')}
61+
icon={<IconSettings size={18} />}
62+
onClick={() => setIsSettingDialog(true)}
6263
/>
6364

6465
{!serverSideApiKeyIsSet ? (
6566
<Key apiKey={apiKey} onApiKeyChange={handleApiKeyChange} />
6667
) : null}
6768

6869
{!serverSidePluginKeysSet ? <PluginKeys /> : null}
70+
71+
<SettingDialog
72+
open={isSettingDialogOpen}
73+
onClose={() => {
74+
setIsSettingDialog(false);
75+
}}
76+
/>
6977
</div>
7078
);
7179
};

components/Settings/SettingDialog.tsx

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import { FC, useContext, useEffect, useReducer, useRef } from 'react';
2+
3+
import { useTranslation } from 'next-i18next';
4+
5+
import { useCreateReducer } from '@/hooks/useCreateReducer';
6+
7+
import { getSettings, saveSettings } from '@/utils/app/settings';
8+
9+
import { Settings } from '@/types/settings';
10+
11+
import HomeContext from '@/pages/api/home/home.context';
12+
13+
interface Props {
14+
open: boolean;
15+
onClose: () => void;
16+
}
17+
18+
export const SettingDialog: FC<Props> = ({ open, onClose }) => {
19+
const { t } = useTranslation('settings');
20+
const settings: Settings = getSettings();
21+
const { state, dispatch } = useCreateReducer<Settings>({
22+
initialState: settings,
23+
});
24+
const { dispatch: homeDispatch } = useContext(HomeContext);
25+
const modalRef = useRef<HTMLDivElement>(null);
26+
27+
useEffect(() => {
28+
const handleMouseDown = (e: MouseEvent) => {
29+
if (modalRef.current && !modalRef.current.contains(e.target as Node)) {
30+
window.addEventListener('mouseup', handleMouseUp);
31+
}
32+
};
33+
34+
const handleMouseUp = (e: MouseEvent) => {
35+
window.removeEventListener('mouseup', handleMouseUp);
36+
onClose();
37+
};
38+
39+
window.addEventListener('mousedown', handleMouseDown);
40+
41+
return () => {
42+
window.removeEventListener('mousedown', handleMouseDown);
43+
};
44+
}, [onClose]);
45+
46+
const handleSave = () => {
47+
homeDispatch({ field: 'lightMode', value: state.theme });
48+
saveSettings(state);
49+
};
50+
51+
// Render nothing if the dialog is not open.
52+
if (!open) {
53+
return <></>;
54+
}
55+
56+
// Render the dialog.
57+
return (
58+
<div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50 z-50">
59+
<div className="fixed inset-0 z-10 overflow-hidden">
60+
<div className="flex items-center justify-center min-h-screen px-4 pt-4 pb-20 text-center sm:block sm:p-0">
61+
<div
62+
className="hidden sm:inline-block sm:h-screen sm:align-middle"
63+
aria-hidden="true"
64+
/>
65+
66+
<div
67+
ref={modalRef}
68+
className="dark:border-netural-400 inline-block max-h-[400px] transform overflow-y-auto rounded-lg border border-gray-300 bg-white px-4 pt-5 pb-4 text-left align-bottom shadow-xl transition-all dark:bg-[#202123] sm:my-8 sm:max-h-[600px] sm:w-full sm:max-w-lg sm:p-6 sm:align-middle"
69+
role="dialog"
70+
>
71+
<div className="text-lg pb-4 font-bold text-black dark:text-neutral-200">
72+
{t('Settings')}
73+
</div>
74+
75+
<div className="text-sm font-bold mb-2 text-black dark:text-neutral-200">
76+
{t('Theme')}
77+
</div>
78+
79+
<select
80+
className="w-full cursor-pointer bg-transparent p-2 text-neutral-700 dark:text-neutral-200"
81+
value={state.theme}
82+
onChange={(event) =>
83+
dispatch({ field: 'theme', value: event.target.value })
84+
}
85+
>
86+
<option value="dark">{t('Dark mode')}</option>
87+
<option value="light">{t('Light mode')}</option>
88+
</select>
89+
90+
<button
91+
type="button"
92+
className="w-full px-4 py-2 mt-6 border rounded-lg shadow border-neutral-500 text-neutral-900 hover:bg-neutral-100 focus:outline-none dark:border-neutral-800 dark:border-opacity-50 dark:bg-white dark:text-black dark:hover:bg-neutral-300"
93+
onClick={() => {
94+
handleSave();
95+
onClose();
96+
}}
97+
>
98+
{t('Save')}
99+
</button>
100+
</div>
101+
</div>
102+
</div>
103+
</div>
104+
);
105+
};

next-i18next.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ module.exports = {
2121
"vi",
2222
"zh",
2323
"ar",
24+
"tr",
2425
],
2526
},
2627
localePath:

0 commit comments

Comments
 (0)