Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
mckaywrigley committed Apr 18, 2023
2 parents 856ba12 + 00f6189 commit 1a9a7dc
Show file tree
Hide file tree
Showing 53 changed files with 378 additions and 112 deletions.
19 changes: 8 additions & 11 deletions components/Chat/Chat.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IconArrowDown, IconClearAll, IconSettings } from '@tabler/icons-react';
import { IconClearAll, IconSettings } from '@tabler/icons-react';
import {
MutableRefObject,
memo,
Expand Down Expand Up @@ -467,6 +467,11 @@ export const Chat = memo(({ stopConversationRef }: Props) => {
key={index}
message={message}
messageIndex={index}
onEdit={(editedMessage) => {
setCurrentMessage(editedMessage);
// discard edited message and the ones that come after then resend
handleSend(editedMessage, selectedConversation?.messages.length - index);
}}
/>
))}

Expand All @@ -487,24 +492,16 @@ export const Chat = memo(({ stopConversationRef }: Props) => {
setCurrentMessage(message);
handleSend(message, 0, plugin);
}}
onScrollDownClick={handleScrollDown}
onRegenerate={() => {
if (currentMessage) {
handleSend(currentMessage, 2, null);
}
}}
showScrollDownButton={showScrollDownButton}
/>
</>
)}
{showScrollDownButton && (
<div className="absolute bottom-0 right-0 mb-4 mr-4 pb-20">
<button
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"
onClick={handleScrollDown}
>
<IconArrowDown size={18} />
</button>
</div>
)}
</div>
);
});
Expand Down
16 changes: 16 additions & 0 deletions components/Chat/ChatInput.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
IconArrowDown,
IconBolt,
IconBrandGoogle,
IconPlayerStop,
Expand Down Expand Up @@ -30,15 +31,19 @@ import { VariableModal } from './VariableModal';
interface Props {
onSend: (message: Message, plugin: Plugin | null) => void;
onRegenerate: () => void;
onScrollDownClick: () => void;
stopConversationRef: MutableRefObject<boolean>;
textareaRef: MutableRefObject<HTMLTextAreaElement | null>;
showScrollDownButton: boolean;
}

export const ChatInput = ({
onSend,
onRegenerate,
onScrollDownClick,
stopConversationRef,
textareaRef,
showScrollDownButton
}: Props) => {
const { t } = useTranslation('chat');

Expand Down Expand Up @@ -341,6 +346,17 @@ export const ChatInput = ({
)}
</button>

{showScrollDownButton && (
<div className="absolute bottom-12 right-0 lg:bottom-0 lg:-right-10">
<button
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"
onClick={onScrollDownClick}
>
<IconArrowDown size={18} />
</button>
</div>
)}

{showPromptList && filteredPrompts.length > 0 && (
<div className="absolute bottom-12 w-full">
<PromptList
Expand Down
30 changes: 4 additions & 26 deletions components/Chat/ChatMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ import remarkMath from 'remark-math';
interface Props {
message: Message;
messageIndex: number;
onEdit?: (editedMessage: Message) => void
}

export const ChatMessage: FC<Props> = memo(({ message, messageIndex }) => {
export const ChatMessage: FC<Props> = memo(({ message, messageIndex, onEdit }) => {
const { t } = useTranslation('chat');

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

const handleEditMessage = () => {
if (message.content != messageContent) {
if (selectedConversation) {
const updatedMessages = selectedConversation.messages
.map((m, i) => {
if (i < messageIndex) {
return m;
}
})
.filter((m) => m) as Message[];

const updatedConversation = {
...selectedConversation,
messages: updatedMessages,
};

const { single, all } = updateConversation(
updatedConversation,
conversations,
);

homeDispatch({ field: 'selectedConversation', value: single });
homeDispatch({ field: 'conversations', value: all });
homeDispatch({
field: 'currentMessage',
value: { ...message, content: messageContent },
});
if (selectedConversation && onEdit) {
onEdit({ ...message, content: messageContent });
}
}
setIsEditing(false);
Expand Down
18 changes: 14 additions & 4 deletions components/Chat/Temperature.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { FC, useState } from 'react';
import { FC, useContext, useState } from 'react';

import { useTranslation } from 'next-i18next';

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

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

interface Props {
label: string;
onChangeTemperature: (temperature: number) => void;
Expand All @@ -13,7 +15,13 @@ export const TemperatureSlider: FC<Props> = ({
label,
onChangeTemperature,
}) => {
const [temperature, setTemperature] = useState(DEFAULT_TEMPERATURE);
const {
state: { conversations },
} = useContext(HomeContext);
const lastConversation = conversations[conversations.length - 1];
const [temperature, setTemperature] = useState(
lastConversation?.temperature ?? DEFAULT_TEMPERATURE,
);
const { t } = useTranslation('chat');
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const newValue = parseFloat(event.target.value);
Expand All @@ -31,7 +39,9 @@ export const TemperatureSlider: FC<Props> = ({
'Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.',
)}
</span>
<span className="mt-2 mb-1 text-center">{temperature.toFixed(1)}</span>
<span className="mt-2 mb-1 text-center text-neutral-900 dark:text-neutral-100">
{temperature.toFixed(1)}
</span>
<input
className="cursor-pointer"
type="range"
Expand All @@ -41,7 +51,7 @@ export const TemperatureSlider: FC<Props> = ({
value={temperature}
onChange={handleChange}
/>
<ul className="w mt-2 pb-8 flex justify-between px-[24px]">
<ul className="w mt-2 pb-8 flex justify-between px-[24px] text-neutral-900 dark:text-neutral-100">
<li className="relative flex justify-center">
<span className="absolute">{t('Precise')}</span>
</li>
Expand Down
32 changes: 20 additions & 12 deletions components/Chatbar/components/ChatbarSettings.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import { IconFileExport, IconMoon, IconSun } from '@tabler/icons-react';
import { useContext } from 'react';
import {
IconFileExport,
IconMoon,
IconSettings,
IconSun,
} from '@tabler/icons-react';
import { useContext, useState } from 'react';

import { useTranslation } from 'next-i18next';

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

import { SettingDialog } from '@/components/Settings/SettingDialog';

import { Import } from '../../Settings/Import';
import { Key } from '../../Settings/Key';
import { SidebarButton } from '../../Sidebar/SidebarButton';
Expand All @@ -14,6 +21,7 @@ import { PluginKeys } from './PluginKeys';

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

const {
state: {
Expand Down Expand Up @@ -49,23 +57,23 @@ export const ChatbarSettings = () => {
/>

<SidebarButton
text={lightMode === 'light' ? t('Dark mode') : t('Light mode')}
icon={
lightMode === 'light' ? <IconMoon size={18} /> : <IconSun size={18} />
}
onClick={() =>
homeDispatch({
field: 'lightMode',
value: lightMode === 'light' ? 'dark' : 'light',
})
}
text={t('Settings')}
icon={<IconSettings size={18} />}
onClick={() => setIsSettingDialog(true)}
/>

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

{!serverSidePluginKeysSet ? <PluginKeys /> : null}

<SettingDialog
open={isSettingDialogOpen}
onClose={() => {
setIsSettingDialog(false);
}}
/>
</div>
);
};
105 changes: 105 additions & 0 deletions components/Settings/SettingDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { FC, useContext, useEffect, useReducer, useRef } from 'react';

import { useTranslation } from 'next-i18next';

import { useCreateReducer } from '@/hooks/useCreateReducer';

import { getSettings, saveSettings } from '@/utils/app/settings';

import { Settings } from '@/types/settings';

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

interface Props {
open: boolean;
onClose: () => void;
}

export const SettingDialog: FC<Props> = ({ open, onClose }) => {
const { t } = useTranslation('settings');
const settings: Settings = getSettings();
const { state, dispatch } = useCreateReducer<Settings>({
initialState: settings,
});
const { dispatch: homeDispatch } = useContext(HomeContext);
const modalRef = useRef<HTMLDivElement>(null);

useEffect(() => {
const handleMouseDown = (e: MouseEvent) => {
if (modalRef.current && !modalRef.current.contains(e.target as Node)) {
window.addEventListener('mouseup', handleMouseUp);
}
};

const handleMouseUp = (e: MouseEvent) => {
window.removeEventListener('mouseup', handleMouseUp);
onClose();
};

window.addEventListener('mousedown', handleMouseDown);

return () => {
window.removeEventListener('mousedown', handleMouseDown);
};
}, [onClose]);

const handleSave = () => {
homeDispatch({ field: 'lightMode', value: state.theme });
saveSettings(state);
};

// Render nothing if the dialog is not open.
if (!open) {
return <></>;
}

// Render the dialog.
return (
<div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50 z-50">
<div className="fixed inset-0 z-10 overflow-hidden">
<div className="flex items-center justify-center min-h-screen px-4 pt-4 pb-20 text-center sm:block sm:p-0">
<div
className="hidden sm:inline-block sm:h-screen sm:align-middle"
aria-hidden="true"
/>

<div
ref={modalRef}
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"
role="dialog"
>
<div className="text-lg pb-4 font-bold text-black dark:text-neutral-200">
{t('Settings')}
</div>

<div className="text-sm font-bold mb-2 text-black dark:text-neutral-200">
{t('Theme')}
</div>

<select
className="w-full cursor-pointer bg-transparent p-2 text-neutral-700 dark:text-neutral-200"
value={state.theme}
onChange={(event) =>
dispatch({ field: 'theme', value: event.target.value })
}
>
<option value="dark">{t('Dark mode')}</option>
<option value="light">{t('Light mode')}</option>
</select>

<button
type="button"
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"
onClick={() => {
handleSave();
onClose();
}}
>
{t('Save')}
</button>
</div>
</div>
</div>
</div>
);
};
1 change: 1 addition & 0 deletions next-i18next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ module.exports = {
"vi",
"zh",
"ar",
"tr",
],
},
localePath:
Expand Down

1 comment on commit 1a9a7dc

@vercel
Copy link

@vercel vercel bot commented on 1a9a7dc Apr 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.