Spaces:
Paused
Paused
matt HOFFNER
commited on
Commit
·
a646f5a
1
Parent(s):
2670b73
cleanup
Browse files- components/Chatbar/Chatbar.tsx +1 -23
- components/Chatbar/components/ChatFolders.tsx +0 -64
- components/Chatbar/components/ChatbarSettings.tsx +0 -16
- components/Chatbar/components/ClearConversations.tsx +1 -6
- components/Chatbar/components/PluginKeys.tsx +0 -235
- components/Folder/Folder.tsx +0 -192
- components/Folder/index.ts +0 -1
- components/Promptbar/Promptbar.tsx +2 -25
- components/Promptbar/components/PromptFolders.tsx +0 -64
- components/Settings/Import.tsx +1 -13
- components/Settings/Key.tsx +1 -6
- components/Sidebar/Sidebar.tsx +0 -123
- components/Sidebar/SidebarButton.tsx +0 -19
- components/Sidebar/components/OpenCloseButton.tsx +0 -42
- components/Sidebar/index.ts +0 -1
- next.config.js +0 -4
- package-lock.json +3 -3
components/Chatbar/Chatbar.tsx
CHANGED
@@ -16,11 +16,6 @@ import { PluginKey } from '@/types/plugin';
|
|
16 |
|
17 |
import HomeContext from '@/pages/api/home/home.context';
|
18 |
|
19 |
-
import { ChatFolders } from './components/ChatFolders';
|
20 |
-
import { ChatbarSettings } from './components/ChatbarSettings';
|
21 |
-
import { Conversations } from './components/Conversations';
|
22 |
-
|
23 |
-
import Sidebar from '../Sidebar';
|
24 |
import ChatbarContext from './Chatbar.context';
|
25 |
import { ChatbarInitialState, initialState } from './Chatbar.state';
|
26 |
|
@@ -36,7 +31,6 @@ export const Chatbar = () => {
|
|
36 |
const {
|
37 |
state: { conversations, showChatbar, defaultModelId, folders, pluginKeys },
|
38 |
dispatch: homeDispatch,
|
39 |
-
handleCreateFolder,
|
40 |
handleNewConversation,
|
41 |
handleUpdateConversation,
|
42 |
} = useContext(HomeContext);
|
@@ -219,23 +213,7 @@ export const Chatbar = () => {
|
|
219 |
handleApiKeyChange,
|
220 |
}}
|
221 |
>
|
222 |
-
|
223 |
-
side={'left'}
|
224 |
-
isOpen={showChatbar}
|
225 |
-
addItemButtonTitle={t('New chat')}
|
226 |
-
itemComponent={<Conversations conversations={filteredConversations} />}
|
227 |
-
folderComponent={<ChatFolders searchTerm={searchTerm} />}
|
228 |
-
items={filteredConversations}
|
229 |
-
searchTerm={searchTerm}
|
230 |
-
handleSearchTerm={(searchTerm: string) =>
|
231 |
-
chatDispatch({ field: 'searchTerm', value: searchTerm })
|
232 |
-
}
|
233 |
-
toggleOpen={handleToggleChatbar}
|
234 |
-
handleCreateItem={handleNewConversation}
|
235 |
-
handleCreateFolder={() => handleCreateFolder(t('New folder'), 'chat')}
|
236 |
-
handleDrop={handleDrop}
|
237 |
-
footerComponent={<ChatbarSettings />}
|
238 |
-
/>
|
239 |
</ChatbarContext.Provider>
|
240 |
);
|
241 |
};
|
|
|
16 |
|
17 |
import HomeContext from '@/pages/api/home/home.context';
|
18 |
|
|
|
|
|
|
|
|
|
|
|
19 |
import ChatbarContext from './Chatbar.context';
|
20 |
import { ChatbarInitialState, initialState } from './Chatbar.state';
|
21 |
|
|
|
31 |
const {
|
32 |
state: { conversations, showChatbar, defaultModelId, folders, pluginKeys },
|
33 |
dispatch: homeDispatch,
|
|
|
34 |
handleNewConversation,
|
35 |
handleUpdateConversation,
|
36 |
} = useContext(HomeContext);
|
|
|
213 |
handleApiKeyChange,
|
214 |
}}
|
215 |
>
|
216 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
217 |
</ChatbarContext.Provider>
|
218 |
);
|
219 |
};
|
components/Chatbar/components/ChatFolders.tsx
DELETED
@@ -1,64 +0,0 @@
|
|
1 |
-
import { useContext } from 'react';
|
2 |
-
|
3 |
-
import { FolderInterface } from '@/types/folder';
|
4 |
-
|
5 |
-
import HomeContext from '@/pages/api/home/home.context';
|
6 |
-
|
7 |
-
import Folder from '@/components/Folder';
|
8 |
-
|
9 |
-
import { ConversationComponent } from './Conversation';
|
10 |
-
|
11 |
-
interface Props {
|
12 |
-
searchTerm: string;
|
13 |
-
}
|
14 |
-
|
15 |
-
export const ChatFolders = ({ searchTerm }: Props) => {
|
16 |
-
const {
|
17 |
-
state: { folders, conversations },
|
18 |
-
handleUpdateConversation,
|
19 |
-
} = useContext(HomeContext);
|
20 |
-
|
21 |
-
const handleDrop = (e: any, folder: FolderInterface) => {
|
22 |
-
if (e.dataTransfer) {
|
23 |
-
const conversation = JSON.parse(e.dataTransfer.getData('conversation'));
|
24 |
-
handleUpdateConversation(conversation, {
|
25 |
-
key: 'folderId',
|
26 |
-
value: folder.id,
|
27 |
-
});
|
28 |
-
}
|
29 |
-
};
|
30 |
-
|
31 |
-
const ChatFolders = (currentFolder: FolderInterface) => {
|
32 |
-
return (
|
33 |
-
conversations &&
|
34 |
-
conversations
|
35 |
-
.filter((conversation) => conversation.folderId)
|
36 |
-
.map((conversation, index) => {
|
37 |
-
if (conversation.folderId === currentFolder.id) {
|
38 |
-
return (
|
39 |
-
<div key={index} className="ml-5 gap-2 border-l pl-2">
|
40 |
-
<ConversationComponent conversation={conversation} />
|
41 |
-
</div>
|
42 |
-
);
|
43 |
-
}
|
44 |
-
})
|
45 |
-
);
|
46 |
-
};
|
47 |
-
|
48 |
-
return (
|
49 |
-
<div className="flex w-full flex-col pt-2">
|
50 |
-
{folders
|
51 |
-
.filter((folder) => folder.type === 'chat')
|
52 |
-
.sort((a, b) => a.name.localeCompare(b.name))
|
53 |
-
.map((folder, index) => (
|
54 |
-
<Folder
|
55 |
-
key={index}
|
56 |
-
searchTerm={searchTerm}
|
57 |
-
currentFolder={folder}
|
58 |
-
handleDrop={handleDrop}
|
59 |
-
folderComponent={ChatFolders(folder)}
|
60 |
-
/>
|
61 |
-
))}
|
62 |
-
</div>
|
63 |
-
);
|
64 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
components/Chatbar/components/ChatbarSettings.tsx
CHANGED
@@ -9,10 +9,8 @@ import { SettingDialog } from '@/components/Settings/SettingDialog';
|
|
9 |
|
10 |
import { Import } from '../../Settings/Import';
|
11 |
import { Key } from '../../Settings/Key';
|
12 |
-
import { SidebarButton } from '../../Sidebar/SidebarButton';
|
13 |
import ChatbarContext from '../Chatbar.context';
|
14 |
import { ClearConversations } from './ClearConversations';
|
15 |
-
import { PluginKeys } from './PluginKeys';
|
16 |
|
17 |
export const ChatbarSettings = () => {
|
18 |
const { t } = useTranslation('sidebar');
|
@@ -44,24 +42,10 @@ export const ChatbarSettings = () => {
|
|
44 |
|
45 |
<Import onImport={handleImportConversations} />
|
46 |
|
47 |
-
<SidebarButton
|
48 |
-
text={t('Export data')}
|
49 |
-
icon={<IconFileExport size={18} />}
|
50 |
-
onClick={() => handleExportData()}
|
51 |
-
/>
|
52 |
-
|
53 |
-
<SidebarButton
|
54 |
-
text={t('Settings')}
|
55 |
-
icon={<IconSettings size={18} />}
|
56 |
-
onClick={() => setIsSettingDialog(true)}
|
57 |
-
/>
|
58 |
-
|
59 |
{!serverSideApiKeyIsSet ? (
|
60 |
<Key apiKey={apiKey} onApiKeyChange={handleApiKeyChange} />
|
61 |
) : null}
|
62 |
|
63 |
-
{!serverSidePluginKeysSet ? <PluginKeys /> : null}
|
64 |
-
|
65 |
<SettingDialog
|
66 |
open={isSettingDialogOpen}
|
67 |
onClose={() => {
|
|
|
9 |
|
10 |
import { Import } from '../../Settings/Import';
|
11 |
import { Key } from '../../Settings/Key';
|
|
|
12 |
import ChatbarContext from '../Chatbar.context';
|
13 |
import { ClearConversations } from './ClearConversations';
|
|
|
14 |
|
15 |
export const ChatbarSettings = () => {
|
16 |
const { t } = useTranslation('sidebar');
|
|
|
42 |
|
43 |
<Import onImport={handleImportConversations} />
|
44 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
45 |
{!serverSideApiKeyIsSet ? (
|
46 |
<Key apiKey={apiKey} onApiKeyChange={handleApiKeyChange} />
|
47 |
) : null}
|
48 |
|
|
|
|
|
49 |
<SettingDialog
|
50 |
open={isSettingDialogOpen}
|
51 |
onClose={() => {
|
components/Chatbar/components/ClearConversations.tsx
CHANGED
@@ -3,7 +3,6 @@ import { FC, useState } from 'react';
|
|
3 |
|
4 |
import { useTranslation } from 'next-i18next';
|
5 |
|
6 |
-
import { SidebarButton } from '@/components/Sidebar/SidebarButton';
|
7 |
|
8 |
interface Props {
|
9 |
onClearConversations: () => void;
|
@@ -48,10 +47,6 @@ export const ClearConversations: FC<Props> = ({ onClearConversations }) => {
|
|
48 |
</div>
|
49 |
</div>
|
50 |
) : (
|
51 |
-
|
52 |
-
text={t('Clear conversations')}
|
53 |
-
icon={<IconTrash size={18} />}
|
54 |
-
onClick={() => setIsConfirming(true)}
|
55 |
-
/>
|
56 |
);
|
57 |
};
|
|
|
3 |
|
4 |
import { useTranslation } from 'next-i18next';
|
5 |
|
|
|
6 |
|
7 |
interface Props {
|
8 |
onClearConversations: () => void;
|
|
|
47 |
</div>
|
48 |
</div>
|
49 |
) : (
|
50 |
+
null
|
|
|
|
|
|
|
|
|
51 |
);
|
52 |
};
|
components/Chatbar/components/PluginKeys.tsx
DELETED
@@ -1,235 +0,0 @@
|
|
1 |
-
import { IconKey } from '@tabler/icons-react';
|
2 |
-
import { KeyboardEvent, useContext, useEffect, useRef, useState } from 'react';
|
3 |
-
import { useTranslation } from 'react-i18next';
|
4 |
-
|
5 |
-
import { PluginID, PluginKey } from '@/types/plugin';
|
6 |
-
|
7 |
-
import HomeContext from '@/pages/api/home/home.context';
|
8 |
-
|
9 |
-
import { SidebarButton } from '@/components/Sidebar/SidebarButton';
|
10 |
-
|
11 |
-
import ChatbarContext from '../Chatbar.context';
|
12 |
-
|
13 |
-
export const PluginKeys = () => {
|
14 |
-
const { t } = useTranslation('sidebar');
|
15 |
-
|
16 |
-
const {
|
17 |
-
state: { pluginKeys },
|
18 |
-
} = useContext(HomeContext);
|
19 |
-
|
20 |
-
const { handlePluginKeyChange, handleClearPluginKey } =
|
21 |
-
useContext(ChatbarContext);
|
22 |
-
|
23 |
-
const [isChanging, setIsChanging] = useState(false);
|
24 |
-
|
25 |
-
const modalRef = useRef<HTMLDivElement>(null);
|
26 |
-
|
27 |
-
const handleEnter = (e: KeyboardEvent<HTMLDivElement>) => {
|
28 |
-
if (e.key === 'Enter' && !e.shiftKey) {
|
29 |
-
e.preventDefault();
|
30 |
-
setIsChanging(false);
|
31 |
-
}
|
32 |
-
};
|
33 |
-
|
34 |
-
useEffect(() => {
|
35 |
-
const handleMouseDown = (e: MouseEvent) => {
|
36 |
-
if (modalRef.current && !modalRef.current.contains(e.target as Node)) {
|
37 |
-
window.addEventListener('mouseup', handleMouseUp);
|
38 |
-
}
|
39 |
-
};
|
40 |
-
|
41 |
-
const handleMouseUp = (e: MouseEvent) => {
|
42 |
-
window.removeEventListener('mouseup', handleMouseUp);
|
43 |
-
setIsChanging(false);
|
44 |
-
};
|
45 |
-
|
46 |
-
window.addEventListener('mousedown', handleMouseDown);
|
47 |
-
|
48 |
-
return () => {
|
49 |
-
window.removeEventListener('mousedown', handleMouseDown);
|
50 |
-
};
|
51 |
-
}, []);
|
52 |
-
|
53 |
-
return (
|
54 |
-
<>
|
55 |
-
<SidebarButton
|
56 |
-
text={t('Plugin Keys')}
|
57 |
-
icon={<IconKey size={18} />}
|
58 |
-
onClick={() => setIsChanging(true)}
|
59 |
-
/>
|
60 |
-
|
61 |
-
{isChanging && (
|
62 |
-
<div
|
63 |
-
className="z-100 fixed inset-0 flex items-center justify-center bg-black bg-opacity-50"
|
64 |
-
onKeyDown={handleEnter}
|
65 |
-
>
|
66 |
-
<div className="fixed inset-0 z-10 overflow-hidden">
|
67 |
-
<div className="flex min-h-screen items-center justify-center px-4 pt-4 pb-20 text-center sm:block sm:p-0">
|
68 |
-
<div
|
69 |
-
className="hidden sm:inline-block sm:h-screen sm:align-middle"
|
70 |
-
aria-hidden="true"
|
71 |
-
/>
|
72 |
-
|
73 |
-
<div
|
74 |
-
ref={modalRef}
|
75 |
-
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"
|
76 |
-
role="dialog"
|
77 |
-
>
|
78 |
-
<div className="mb-10 text-4xl">Plugin Keys</div>
|
79 |
-
|
80 |
-
<div className="mt-6 rounded border p-4">
|
81 |
-
<div className="text-xl font-bold">Google Search Plugin</div>
|
82 |
-
<div className="mt-4 italic">
|
83 |
-
Please enter your Google API Key and Google CSE ID to enable
|
84 |
-
the Google Search Plugin.
|
85 |
-
</div>
|
86 |
-
|
87 |
-
<div className="mt-6 text-sm font-bold text-black dark:text-neutral-200">
|
88 |
-
Google API Key
|
89 |
-
</div>
|
90 |
-
<input
|
91 |
-
className="mt-2 w-full rounded-lg border border-neutral-500 px-4 py-2 text-neutral-900 shadow focus:outline-none dark:border-neutral-800 dark:border-opacity-50 dark:bg-[#40414F] dark:text-neutral-100"
|
92 |
-
type="password"
|
93 |
-
value={
|
94 |
-
pluginKeys
|
95 |
-
.find((p) => p.pluginId === PluginID.GOOGLE_SEARCH)
|
96 |
-
?.requiredKeys.find((k) => k.key === 'GOOGLE_API_KEY')
|
97 |
-
?.value
|
98 |
-
}
|
99 |
-
onChange={(e) => {
|
100 |
-
const pluginKey = pluginKeys.find(
|
101 |
-
(p) => p.pluginId === PluginID.GOOGLE_SEARCH,
|
102 |
-
);
|
103 |
-
|
104 |
-
if (pluginKey) {
|
105 |
-
const requiredKey = pluginKey.requiredKeys.find(
|
106 |
-
(k) => k.key === 'GOOGLE_API_KEY',
|
107 |
-
);
|
108 |
-
|
109 |
-
if (requiredKey) {
|
110 |
-
const updatedPluginKey = {
|
111 |
-
...pluginKey,
|
112 |
-
requiredKeys: pluginKey.requiredKeys.map((k) => {
|
113 |
-
if (k.key === 'GOOGLE_API_KEY') {
|
114 |
-
return {
|
115 |
-
...k,
|
116 |
-
value: e.target.value,
|
117 |
-
};
|
118 |
-
}
|
119 |
-
|
120 |
-
return k;
|
121 |
-
}),
|
122 |
-
};
|
123 |
-
|
124 |
-
handlePluginKeyChange(updatedPluginKey);
|
125 |
-
}
|
126 |
-
} else {
|
127 |
-
const newPluginKey: PluginKey = {
|
128 |
-
pluginId: PluginID.GOOGLE_SEARCH,
|
129 |
-
requiredKeys: [
|
130 |
-
{
|
131 |
-
key: 'GOOGLE_API_KEY',
|
132 |
-
value: e.target.value,
|
133 |
-
},
|
134 |
-
{
|
135 |
-
key: 'GOOGLE_CSE_ID',
|
136 |
-
value: '',
|
137 |
-
},
|
138 |
-
],
|
139 |
-
};
|
140 |
-
|
141 |
-
handlePluginKeyChange(newPluginKey);
|
142 |
-
}
|
143 |
-
}}
|
144 |
-
/>
|
145 |
-
|
146 |
-
<div className="mt-6 text-sm font-bold text-black dark:text-neutral-200">
|
147 |
-
Google CSE ID
|
148 |
-
</div>
|
149 |
-
<input
|
150 |
-
className="mt-2 w-full rounded-lg border border-neutral-500 px-4 py-2 text-neutral-900 shadow focus:outline-none dark:border-neutral-800 dark:border-opacity-50 dark:bg-[#40414F] dark:text-neutral-100"
|
151 |
-
type="password"
|
152 |
-
value={
|
153 |
-
pluginKeys
|
154 |
-
.find((p) => p.pluginId === PluginID.GOOGLE_SEARCH)
|
155 |
-
?.requiredKeys.find((k) => k.key === 'GOOGLE_CSE_ID')
|
156 |
-
?.value
|
157 |
-
}
|
158 |
-
onChange={(e) => {
|
159 |
-
const pluginKey = pluginKeys.find(
|
160 |
-
(p) => p.pluginId === PluginID.GOOGLE_SEARCH,
|
161 |
-
);
|
162 |
-
|
163 |
-
if (pluginKey) {
|
164 |
-
const requiredKey = pluginKey.requiredKeys.find(
|
165 |
-
(k) => k.key === 'GOOGLE_CSE_ID',
|
166 |
-
);
|
167 |
-
|
168 |
-
if (requiredKey) {
|
169 |
-
const updatedPluginKey = {
|
170 |
-
...pluginKey,
|
171 |
-
requiredKeys: pluginKey.requiredKeys.map((k) => {
|
172 |
-
if (k.key === 'GOOGLE_CSE_ID') {
|
173 |
-
return {
|
174 |
-
...k,
|
175 |
-
value: e.target.value,
|
176 |
-
};
|
177 |
-
}
|
178 |
-
|
179 |
-
return k;
|
180 |
-
}),
|
181 |
-
};
|
182 |
-
|
183 |
-
handlePluginKeyChange(updatedPluginKey);
|
184 |
-
}
|
185 |
-
} else {
|
186 |
-
const newPluginKey: PluginKey = {
|
187 |
-
pluginId: PluginID.GOOGLE_SEARCH,
|
188 |
-
requiredKeys: [
|
189 |
-
{
|
190 |
-
key: 'GOOGLE_API_KEY',
|
191 |
-
value: '',
|
192 |
-
},
|
193 |
-
{
|
194 |
-
key: 'GOOGLE_CSE_ID',
|
195 |
-
value: e.target.value,
|
196 |
-
},
|
197 |
-
],
|
198 |
-
};
|
199 |
-
|
200 |
-
handlePluginKeyChange(newPluginKey);
|
201 |
-
}
|
202 |
-
}}
|
203 |
-
/>
|
204 |
-
|
205 |
-
<button
|
206 |
-
className="mt-6 w-full rounded-lg border border-neutral-500 px-4 py-2 text-neutral-900 shadow 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"
|
207 |
-
onClick={() => {
|
208 |
-
const pluginKey = pluginKeys.find(
|
209 |
-
(p) => p.pluginId === PluginID.GOOGLE_SEARCH,
|
210 |
-
);
|
211 |
-
|
212 |
-
if (pluginKey) {
|
213 |
-
handleClearPluginKey(pluginKey);
|
214 |
-
}
|
215 |
-
}}
|
216 |
-
>
|
217 |
-
Clear Google Search Plugin Keys
|
218 |
-
</button>
|
219 |
-
</div>
|
220 |
-
|
221 |
-
<button
|
222 |
-
type="button"
|
223 |
-
className="mt-6 w-full rounded-lg border border-neutral-500 px-4 py-2 text-neutral-900 shadow 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"
|
224 |
-
onClick={() => setIsChanging(false)}
|
225 |
-
>
|
226 |
-
{t('Save')}
|
227 |
-
</button>
|
228 |
-
</div>
|
229 |
-
</div>
|
230 |
-
</div>
|
231 |
-
</div>
|
232 |
-
)}
|
233 |
-
</>
|
234 |
-
);
|
235 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
components/Folder/Folder.tsx
DELETED
@@ -1,192 +0,0 @@
|
|
1 |
-
import {
|
2 |
-
IconCaretDown,
|
3 |
-
IconCaretRight,
|
4 |
-
IconCheck,
|
5 |
-
IconPencil,
|
6 |
-
IconTrash,
|
7 |
-
IconX,
|
8 |
-
} from '@tabler/icons-react';
|
9 |
-
import {
|
10 |
-
KeyboardEvent,
|
11 |
-
ReactElement,
|
12 |
-
useContext,
|
13 |
-
useEffect,
|
14 |
-
useState,
|
15 |
-
} from 'react';
|
16 |
-
|
17 |
-
import { FolderInterface } from '@/types/folder';
|
18 |
-
|
19 |
-
import HomeContext from '@/pages/api/home/home.context';
|
20 |
-
|
21 |
-
import SidebarActionButton from '@/components/Buttons/SidebarActionButton';
|
22 |
-
|
23 |
-
interface Props {
|
24 |
-
currentFolder: FolderInterface;
|
25 |
-
searchTerm: string;
|
26 |
-
handleDrop: (e: any, folder: FolderInterface) => void;
|
27 |
-
folderComponent: (ReactElement | undefined)[];
|
28 |
-
}
|
29 |
-
|
30 |
-
const Folder = ({
|
31 |
-
currentFolder,
|
32 |
-
searchTerm,
|
33 |
-
handleDrop,
|
34 |
-
folderComponent,
|
35 |
-
}: Props) => {
|
36 |
-
const { handleDeleteFolder, handleUpdateFolder } = useContext(HomeContext);
|
37 |
-
|
38 |
-
const [isDeleting, setIsDeleting] = useState(false);
|
39 |
-
const [isRenaming, setIsRenaming] = useState(false);
|
40 |
-
const [renameValue, setRenameValue] = useState('');
|
41 |
-
const [isOpen, setIsOpen] = useState(false);
|
42 |
-
|
43 |
-
const handleEnterDown = (e: KeyboardEvent<HTMLDivElement>) => {
|
44 |
-
if (e.key === 'Enter') {
|
45 |
-
e.preventDefault();
|
46 |
-
handleRename();
|
47 |
-
}
|
48 |
-
};
|
49 |
-
|
50 |
-
const handleRename = () => {
|
51 |
-
handleUpdateFolder(currentFolder.id, renameValue);
|
52 |
-
setRenameValue('');
|
53 |
-
setIsRenaming(false);
|
54 |
-
};
|
55 |
-
|
56 |
-
const dropHandler = (e: any) => {
|
57 |
-
if (e.dataTransfer) {
|
58 |
-
setIsOpen(true);
|
59 |
-
|
60 |
-
handleDrop(e, currentFolder);
|
61 |
-
|
62 |
-
e.target.style.background = 'none';
|
63 |
-
}
|
64 |
-
};
|
65 |
-
|
66 |
-
const allowDrop = (e: any) => {
|
67 |
-
e.preventDefault();
|
68 |
-
};
|
69 |
-
|
70 |
-
const highlightDrop = (e: any) => {
|
71 |
-
e.target.style.background = '#343541';
|
72 |
-
};
|
73 |
-
|
74 |
-
const removeHighlight = (e: any) => {
|
75 |
-
e.target.style.background = 'none';
|
76 |
-
};
|
77 |
-
|
78 |
-
useEffect(() => {
|
79 |
-
if (isRenaming) {
|
80 |
-
setIsDeleting(false);
|
81 |
-
} else if (isDeleting) {
|
82 |
-
setIsRenaming(false);
|
83 |
-
}
|
84 |
-
}, [isRenaming, isDeleting]);
|
85 |
-
|
86 |
-
useEffect(() => {
|
87 |
-
if (searchTerm) {
|
88 |
-
setIsOpen(true);
|
89 |
-
} else {
|
90 |
-
setIsOpen(false);
|
91 |
-
}
|
92 |
-
}, [searchTerm]);
|
93 |
-
|
94 |
-
return (
|
95 |
-
<>
|
96 |
-
<div className="relative flex items-center">
|
97 |
-
{isRenaming ? (
|
98 |
-
<div className="flex w-full items-center gap-3 bg-[#343541]/90 p-3">
|
99 |
-
{isOpen ? (
|
100 |
-
<IconCaretDown size={18} />
|
101 |
-
) : (
|
102 |
-
<IconCaretRight size={18} />
|
103 |
-
)}
|
104 |
-
<input
|
105 |
-
className="mr-12 flex-1 overflow-hidden overflow-ellipsis border-neutral-400 bg-transparent text-left text-[12.5px] leading-3 text-white outline-none focus:border-neutral-100"
|
106 |
-
type="text"
|
107 |
-
value={renameValue}
|
108 |
-
onChange={(e) => setRenameValue(e.target.value)}
|
109 |
-
onKeyDown={handleEnterDown}
|
110 |
-
autoFocus
|
111 |
-
/>
|
112 |
-
</div>
|
113 |
-
) : (
|
114 |
-
<button
|
115 |
-
className={`flex w-full cursor-pointer items-center gap-3 rounded-lg p-3 text-sm transition-colors duration-200 hover:bg-[#343541]/90`}
|
116 |
-
onClick={() => setIsOpen(!isOpen)}
|
117 |
-
onDrop={(e) => dropHandler(e)}
|
118 |
-
onDragOver={allowDrop}
|
119 |
-
onDragEnter={highlightDrop}
|
120 |
-
onDragLeave={removeHighlight}
|
121 |
-
>
|
122 |
-
{isOpen ? (
|
123 |
-
<IconCaretDown size={18} />
|
124 |
-
) : (
|
125 |
-
<IconCaretRight size={18} />
|
126 |
-
)}
|
127 |
-
|
128 |
-
<div className="relative max-h-5 flex-1 overflow-hidden text-ellipsis whitespace-nowrap break-all text-left text-[12.5px] leading-3">
|
129 |
-
{currentFolder.name}
|
130 |
-
</div>
|
131 |
-
</button>
|
132 |
-
)}
|
133 |
-
|
134 |
-
{(isDeleting || isRenaming) && (
|
135 |
-
<div className="absolute right-1 z-10 flex text-gray-300">
|
136 |
-
<SidebarActionButton
|
137 |
-
handleClick={(e) => {
|
138 |
-
e.stopPropagation();
|
139 |
-
|
140 |
-
if (isDeleting) {
|
141 |
-
handleDeleteFolder(currentFolder.id);
|
142 |
-
} else if (isRenaming) {
|
143 |
-
handleRename();
|
144 |
-
}
|
145 |
-
|
146 |
-
setIsDeleting(false);
|
147 |
-
setIsRenaming(false);
|
148 |
-
}}
|
149 |
-
>
|
150 |
-
<IconCheck size={18} />
|
151 |
-
</SidebarActionButton>
|
152 |
-
<SidebarActionButton
|
153 |
-
handleClick={(e) => {
|
154 |
-
e.stopPropagation();
|
155 |
-
setIsDeleting(false);
|
156 |
-
setIsRenaming(false);
|
157 |
-
}}
|
158 |
-
>
|
159 |
-
<IconX size={18} />
|
160 |
-
</SidebarActionButton>
|
161 |
-
</div>
|
162 |
-
)}
|
163 |
-
|
164 |
-
{!isDeleting && !isRenaming && (
|
165 |
-
<div className="absolute right-1 z-10 flex text-gray-300">
|
166 |
-
<SidebarActionButton
|
167 |
-
handleClick={(e) => {
|
168 |
-
e.stopPropagation();
|
169 |
-
setIsRenaming(true);
|
170 |
-
setRenameValue(currentFolder.name);
|
171 |
-
}}
|
172 |
-
>
|
173 |
-
<IconPencil size={18} />
|
174 |
-
</SidebarActionButton>
|
175 |
-
<SidebarActionButton
|
176 |
-
handleClick={(e) => {
|
177 |
-
e.stopPropagation();
|
178 |
-
setIsDeleting(true);
|
179 |
-
}}
|
180 |
-
>
|
181 |
-
<IconTrash size={18} />
|
182 |
-
</SidebarActionButton>
|
183 |
-
</div>
|
184 |
-
)}
|
185 |
-
</div>
|
186 |
-
|
187 |
-
{isOpen ? folderComponent : null}
|
188 |
-
</>
|
189 |
-
);
|
190 |
-
};
|
191 |
-
|
192 |
-
export default Folder;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
components/Folder/index.ts
DELETED
@@ -1 +0,0 @@
|
|
1 |
-
export { default } from './Folder';
|
|
|
|
components/Promptbar/Promptbar.tsx
CHANGED
@@ -10,11 +10,8 @@ import { Prompt } from '@/types/prompt';
|
|
10 |
|
11 |
import HomeContext from '@/pages/api/home/home.context';
|
12 |
|
13 |
-
import { PromptFolders } from './components/PromptFolders';
|
14 |
-
import { PromptbarSettings } from './components/PromptbarSettings';
|
15 |
import { Prompts } from './components/Prompts';
|
16 |
|
17 |
-
import Sidebar from '../Sidebar';
|
18 |
import PromptbarContext from './PromptBar.context';
|
19 |
import { PromptbarInitialState, initialState } from './Promptbar.state';
|
20 |
|
@@ -29,8 +26,7 @@ const Promptbar = () => {
|
|
29 |
|
30 |
const {
|
31 |
state: { prompts, defaultModelId, showPromptbar },
|
32 |
-
dispatch: homeDispatch
|
33 |
-
handleCreateFolder,
|
34 |
} = useContext(HomeContext);
|
35 |
|
36 |
const {
|
@@ -125,26 +121,7 @@ const Promptbar = () => {
|
|
125 |
handleUpdatePrompt,
|
126 |
}}
|
127 |
>
|
128 |
-
|
129 |
-
side={'right'}
|
130 |
-
isOpen={showPromptbar}
|
131 |
-
addItemButtonTitle={t('New prompt')}
|
132 |
-
itemComponent={
|
133 |
-
<Prompts
|
134 |
-
prompts={filteredPrompts.filter((prompt) => !prompt.folderId)}
|
135 |
-
/>
|
136 |
-
}
|
137 |
-
folderComponent={<PromptFolders />}
|
138 |
-
items={filteredPrompts}
|
139 |
-
searchTerm={searchTerm}
|
140 |
-
handleSearchTerm={(searchTerm: string) =>
|
141 |
-
promptDispatch({ field: 'searchTerm', value: searchTerm })
|
142 |
-
}
|
143 |
-
toggleOpen={handleTogglePromptbar}
|
144 |
-
handleCreateItem={handleCreatePrompt}
|
145 |
-
handleCreateFolder={() => handleCreateFolder(t('New folder'), 'prompt')}
|
146 |
-
handleDrop={handleDrop}
|
147 |
-
/>
|
148 |
</PromptbarContext.Provider>
|
149 |
);
|
150 |
};
|
|
|
10 |
|
11 |
import HomeContext from '@/pages/api/home/home.context';
|
12 |
|
|
|
|
|
13 |
import { Prompts } from './components/Prompts';
|
14 |
|
|
|
15 |
import PromptbarContext from './PromptBar.context';
|
16 |
import { PromptbarInitialState, initialState } from './Promptbar.state';
|
17 |
|
|
|
26 |
|
27 |
const {
|
28 |
state: { prompts, defaultModelId, showPromptbar },
|
29 |
+
dispatch: homeDispatch
|
|
|
30 |
} = useContext(HomeContext);
|
31 |
|
32 |
const {
|
|
|
121 |
handleUpdatePrompt,
|
122 |
}}
|
123 |
>
|
124 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
125 |
</PromptbarContext.Provider>
|
126 |
);
|
127 |
};
|
components/Promptbar/components/PromptFolders.tsx
DELETED
@@ -1,64 +0,0 @@
|
|
1 |
-
import { useContext } from 'react';
|
2 |
-
|
3 |
-
import { FolderInterface } from '@/types/folder';
|
4 |
-
|
5 |
-
import HomeContext from '@/pages/api/home/home.context';
|
6 |
-
|
7 |
-
import Folder from '@/components/Folder';
|
8 |
-
import { PromptComponent } from '@/components/Promptbar/components/Prompt';
|
9 |
-
|
10 |
-
import PromptbarContext from '../PromptBar.context';
|
11 |
-
|
12 |
-
export const PromptFolders = () => {
|
13 |
-
const {
|
14 |
-
state: { folders },
|
15 |
-
} = useContext(HomeContext);
|
16 |
-
|
17 |
-
const {
|
18 |
-
state: { searchTerm, filteredPrompts },
|
19 |
-
handleUpdatePrompt,
|
20 |
-
} = useContext(PromptbarContext);
|
21 |
-
|
22 |
-
const handleDrop = (e: any, folder: FolderInterface) => {
|
23 |
-
if (e.dataTransfer) {
|
24 |
-
const prompt = JSON.parse(e.dataTransfer.getData('prompt'));
|
25 |
-
|
26 |
-
const updatedPrompt = {
|
27 |
-
...prompt,
|
28 |
-
folderId: folder.id,
|
29 |
-
};
|
30 |
-
|
31 |
-
handleUpdatePrompt(updatedPrompt);
|
32 |
-
}
|
33 |
-
};
|
34 |
-
|
35 |
-
const PromptFolders = (currentFolder: FolderInterface) =>
|
36 |
-
filteredPrompts
|
37 |
-
.filter((p) => p.folderId)
|
38 |
-
.map((prompt, index) => {
|
39 |
-
if (prompt.folderId === currentFolder.id) {
|
40 |
-
return (
|
41 |
-
<div key={index} className="ml-5 gap-2 border-l pl-2">
|
42 |
-
<PromptComponent prompt={prompt} />
|
43 |
-
</div>
|
44 |
-
);
|
45 |
-
}
|
46 |
-
});
|
47 |
-
|
48 |
-
return (
|
49 |
-
<div className="flex w-full flex-col pt-2">
|
50 |
-
{folders
|
51 |
-
.filter((folder) => folder.type === 'prompt')
|
52 |
-
.sort((a, b) => a.name.localeCompare(b.name))
|
53 |
-
.map((folder, index) => (
|
54 |
-
<Folder
|
55 |
-
key={index}
|
56 |
-
searchTerm={searchTerm}
|
57 |
-
currentFolder={folder}
|
58 |
-
handleDrop={handleDrop}
|
59 |
-
folderComponent={PromptFolders(folder)}
|
60 |
-
/>
|
61 |
-
))}
|
62 |
-
</div>
|
63 |
-
);
|
64 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
components/Settings/Import.tsx
CHANGED
@@ -5,7 +5,6 @@ import { useTranslation } from 'next-i18next';
|
|
5 |
|
6 |
import { SupportedExportFormats } from '@/types/export';
|
7 |
|
8 |
-
import { SidebarButton } from '../Sidebar/SidebarButton';
|
9 |
|
10 |
interface Props {
|
11 |
onImport: (data: SupportedExportFormats) => void;
|
@@ -34,18 +33,7 @@ export const Import: FC<Props> = ({ onImport }) => {
|
|
34 |
}}
|
35 |
/>
|
36 |
|
37 |
-
|
38 |
-
text={t('Import data')}
|
39 |
-
icon={<IconFileImport size={18} />}
|
40 |
-
onClick={() => {
|
41 |
-
const importFile = document.querySelector(
|
42 |
-
'#import-file',
|
43 |
-
) as HTMLInputElement;
|
44 |
-
if (importFile) {
|
45 |
-
importFile.click();
|
46 |
-
}
|
47 |
-
}}
|
48 |
-
/>
|
49 |
</>
|
50 |
);
|
51 |
};
|
|
|
5 |
|
6 |
import { SupportedExportFormats } from '@/types/export';
|
7 |
|
|
|
8 |
|
9 |
interface Props {
|
10 |
onImport: (data: SupportedExportFormats) => void;
|
|
|
33 |
}}
|
34 |
/>
|
35 |
|
36 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
37 |
</>
|
38 |
);
|
39 |
};
|
components/Settings/Key.tsx
CHANGED
@@ -1,9 +1,4 @@
|
|
1 |
-
import {
|
2 |
-
import { FC, KeyboardEvent, useEffect, useRef, useState } from 'react';
|
3 |
-
|
4 |
-
import { useTranslation } from 'next-i18next';
|
5 |
-
|
6 |
-
import { SidebarButton } from '../Sidebar/SidebarButton';
|
7 |
|
8 |
interface Props {
|
9 |
apiKey: string;
|
|
|
1 |
+
import { FC } from 'react';
|
|
|
|
|
|
|
|
|
|
|
2 |
|
3 |
interface Props {
|
4 |
apiKey: string;
|
components/Sidebar/Sidebar.tsx
DELETED
@@ -1,123 +0,0 @@
|
|
1 |
-
import { IconFolderPlus, IconMistOff, IconPlus } from '@tabler/icons-react';
|
2 |
-
import { ReactNode } from 'react';
|
3 |
-
import { useTranslation } from 'react-i18next';
|
4 |
-
|
5 |
-
import {
|
6 |
-
CloseSidebarButton,
|
7 |
-
OpenSidebarButton,
|
8 |
-
} from './components/OpenCloseButton';
|
9 |
-
|
10 |
-
import Search from '../Search';
|
11 |
-
|
12 |
-
interface Props<T> {
|
13 |
-
isOpen: boolean;
|
14 |
-
addItemButtonTitle: string;
|
15 |
-
side: 'left' | 'right';
|
16 |
-
items: T[];
|
17 |
-
itemComponent: ReactNode;
|
18 |
-
folderComponent: ReactNode;
|
19 |
-
footerComponent?: ReactNode;
|
20 |
-
searchTerm: string;
|
21 |
-
handleSearchTerm: (searchTerm: string) => void;
|
22 |
-
toggleOpen: () => void;
|
23 |
-
handleCreateItem: () => void;
|
24 |
-
handleCreateFolder: () => void;
|
25 |
-
handleDrop: (e: any) => void;
|
26 |
-
}
|
27 |
-
|
28 |
-
const Sidebar = <T,>({
|
29 |
-
isOpen,
|
30 |
-
addItemButtonTitle,
|
31 |
-
side,
|
32 |
-
items,
|
33 |
-
itemComponent,
|
34 |
-
folderComponent,
|
35 |
-
footerComponent,
|
36 |
-
searchTerm,
|
37 |
-
handleSearchTerm,
|
38 |
-
toggleOpen,
|
39 |
-
handleCreateItem,
|
40 |
-
handleCreateFolder,
|
41 |
-
handleDrop,
|
42 |
-
}: Props<T>) => {
|
43 |
-
const { t } = useTranslation('promptbar');
|
44 |
-
|
45 |
-
const allowDrop = (e: any) => {
|
46 |
-
e.preventDefault();
|
47 |
-
};
|
48 |
-
|
49 |
-
const highlightDrop = (e: any) => {
|
50 |
-
e.target.style.background = '#343541';
|
51 |
-
};
|
52 |
-
|
53 |
-
const removeHighlight = (e: any) => {
|
54 |
-
e.target.style.background = 'none';
|
55 |
-
};
|
56 |
-
|
57 |
-
return isOpen ? (
|
58 |
-
<div>
|
59 |
-
<div
|
60 |
-
className={`fixed top-0 ${side}-0 z-40 flex h-full w-[260px] flex-none flex-col space-y-2 bg-[#202123] p-2 text-[14px] transition-all sm:relative sm:top-0`}
|
61 |
-
>
|
62 |
-
<div className="flex items-center">
|
63 |
-
<button
|
64 |
-
className="text-sidebar flex w-[190px] flex-shrink-0 cursor-pointer select-none items-center gap-3 rounded-md border border-white/20 p-3 text-white transition-colors duration-200 hover:bg-gray-500/10"
|
65 |
-
onClick={() => {
|
66 |
-
handleCreateItem();
|
67 |
-
handleSearchTerm('');
|
68 |
-
}}
|
69 |
-
>
|
70 |
-
<IconPlus size={16} />
|
71 |
-
{addItemButtonTitle}
|
72 |
-
</button>
|
73 |
-
|
74 |
-
<button
|
75 |
-
className="ml-2 flex flex-shrink-0 cursor-pointer items-center gap-3 rounded-md border border-white/20 p-3 text-sm text-white transition-colors duration-200 hover:bg-gray-500/10"
|
76 |
-
onClick={handleCreateFolder}
|
77 |
-
>
|
78 |
-
<IconFolderPlus size={16} />
|
79 |
-
</button>
|
80 |
-
</div>
|
81 |
-
<Search
|
82 |
-
placeholder={t('Search...') || ''}
|
83 |
-
searchTerm={searchTerm}
|
84 |
-
onSearch={handleSearchTerm}
|
85 |
-
/>
|
86 |
-
|
87 |
-
<div className="flex-grow overflow-auto">
|
88 |
-
{items?.length > 0 && (
|
89 |
-
<div className="flex border-b border-white/20 pb-2">
|
90 |
-
{folderComponent}
|
91 |
-
</div>
|
92 |
-
)}
|
93 |
-
|
94 |
-
{items?.length > 0 ? (
|
95 |
-
<div
|
96 |
-
className="pt-2"
|
97 |
-
onDrop={handleDrop}
|
98 |
-
onDragOver={allowDrop}
|
99 |
-
onDragEnter={highlightDrop}
|
100 |
-
onDragLeave={removeHighlight}
|
101 |
-
>
|
102 |
-
{itemComponent}
|
103 |
-
</div>
|
104 |
-
) : (
|
105 |
-
<div className="mt-8 select-none text-center text-white opacity-50">
|
106 |
-
<IconMistOff className="mx-auto mb-3" />
|
107 |
-
<span className="text-[14px] leading-normal">
|
108 |
-
{t('No data.')}
|
109 |
-
</span>
|
110 |
-
</div>
|
111 |
-
)}
|
112 |
-
</div>
|
113 |
-
{footerComponent}
|
114 |
-
</div>
|
115 |
-
|
116 |
-
<CloseSidebarButton onClick={toggleOpen} side={side} />
|
117 |
-
</div>
|
118 |
-
) : (
|
119 |
-
<OpenSidebarButton onClick={toggleOpen} side={side} />
|
120 |
-
);
|
121 |
-
};
|
122 |
-
|
123 |
-
export default Sidebar;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
components/Sidebar/SidebarButton.tsx
DELETED
@@ -1,19 +0,0 @@
|
|
1 |
-
import { FC } from 'react';
|
2 |
-
|
3 |
-
interface Props {
|
4 |
-
text: string;
|
5 |
-
icon: JSX.Element;
|
6 |
-
onClick: () => void;
|
7 |
-
}
|
8 |
-
|
9 |
-
export const SidebarButton: FC<Props> = ({ text, icon, onClick }) => {
|
10 |
-
return (
|
11 |
-
<button
|
12 |
-
className="flex w-full cursor-pointer select-none items-center gap-3 rounded-md py-3 px-3 text-[14px] leading-3 text-white transition-colors duration-200 hover:bg-gray-500/10"
|
13 |
-
onClick={onClick}
|
14 |
-
>
|
15 |
-
<div>{icon}</div>
|
16 |
-
<span>{text}</span>
|
17 |
-
</button>
|
18 |
-
);
|
19 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
components/Sidebar/components/OpenCloseButton.tsx
DELETED
@@ -1,42 +0,0 @@
|
|
1 |
-
import { IconArrowBarLeft, IconArrowBarRight } from '@tabler/icons-react';
|
2 |
-
|
3 |
-
interface Props {
|
4 |
-
onClick: any;
|
5 |
-
side: 'left' | 'right';
|
6 |
-
}
|
7 |
-
|
8 |
-
export const CloseSidebarButton = ({ onClick, side }: Props) => {
|
9 |
-
return (
|
10 |
-
<>
|
11 |
-
<button
|
12 |
-
className={`fixed top-5 ${
|
13 |
-
side === 'right' ? 'right-[270px]' : 'left-[270px]'
|
14 |
-
} z-50 h-7 w-7 hover:text-gray-400 dark:text-white dark:hover:text-gray-300 sm:top-0.5 sm:${
|
15 |
-
side === 'right' ? 'right-[270px]' : 'left-[270px]'
|
16 |
-
} sm:h-8 sm:w-8 sm:text-neutral-700`}
|
17 |
-
onClick={onClick}
|
18 |
-
>
|
19 |
-
{side === 'right' ? <IconArrowBarRight /> : <IconArrowBarLeft />}
|
20 |
-
</button>
|
21 |
-
<div
|
22 |
-
onClick={onClick}
|
23 |
-
className="absolute top-0 left-0 z-10 h-full w-full bg-black opacity-70 sm:hidden"
|
24 |
-
></div>
|
25 |
-
</>
|
26 |
-
);
|
27 |
-
};
|
28 |
-
|
29 |
-
export const OpenSidebarButton = ({ onClick, side }: Props) => {
|
30 |
-
return (
|
31 |
-
<button
|
32 |
-
className={`fixed top-2.5 ${
|
33 |
-
side === 'right' ? 'right-2' : 'left-2'
|
34 |
-
} z-50 h-7 w-7 text-white hover:text-gray-400 dark:text-white dark:hover:text-gray-300 sm:top-0.5 sm:${
|
35 |
-
side === 'right' ? 'right-2' : 'left-2'
|
36 |
-
} sm:h-8 sm:w-8 sm:text-neutral-700`}
|
37 |
-
onClick={onClick}
|
38 |
-
>
|
39 |
-
{side === 'right' ? <IconArrowBarLeft /> : <IconArrowBarRight />}
|
40 |
-
</button>
|
41 |
-
);
|
42 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
components/Sidebar/index.ts
DELETED
@@ -1 +0,0 @@
|
|
1 |
-
export { default } from './Sidebar';
|
|
|
|
next.config.js
CHANGED
@@ -1,9 +1,5 @@
|
|
1 |
-
const { i18n } = require('./next-i18next.config');
|
2 |
-
|
3 |
/** @type {import('next').NextConfig} */
|
4 |
const nextConfig = {
|
5 |
-
i18n,
|
6 |
-
output: "standalone",
|
7 |
reactStrictMode: true,
|
8 |
|
9 |
webpack(config, { isServer, dev }) {
|
|
|
|
|
|
|
1 |
/** @type {import('next').NextConfig} */
|
2 |
const nextConfig = {
|
|
|
|
|
3 |
reactStrictMode: true,
|
4 |
|
5 |
webpack(config, { isServer, dev }) {
|
package-lock.json
CHANGED
@@ -1,18 +1,18 @@
|
|
1 |
{
|
2 |
-
"name": "
|
3 |
"version": "0.1.0",
|
4 |
"lockfileVersion": 2,
|
5 |
"requires": true,
|
6 |
"packages": {
|
7 |
"": {
|
8 |
-
"name": "
|
9 |
"version": "0.1.0",
|
10 |
"dependencies": {
|
11 |
"@dqbd/tiktoken": "^1.0.2",
|
12 |
"@tabler/icons-react": "^2.9.0",
|
13 |
"eventsource-parser": "^0.1.0",
|
14 |
"i18next": "^22.4.13",
|
15 |
-
"next": "13.2.4",
|
16 |
"next-i18next": "^13.2.2",
|
17 |
"openai": "^3.2.1",
|
18 |
"react": "18.2.0",
|
|
|
1 |
{
|
2 |
+
"name": "chatbot-mini",
|
3 |
"version": "0.1.0",
|
4 |
"lockfileVersion": 2,
|
5 |
"requires": true,
|
6 |
"packages": {
|
7 |
"": {
|
8 |
+
"name": "chatbot-mini",
|
9 |
"version": "0.1.0",
|
10 |
"dependencies": {
|
11 |
"@dqbd/tiktoken": "^1.0.2",
|
12 |
"@tabler/icons-react": "^2.9.0",
|
13 |
"eventsource-parser": "^0.1.0",
|
14 |
"i18next": "^22.4.13",
|
15 |
+
"next": "^13.2.4",
|
16 |
"next-i18next": "^13.2.2",
|
17 |
"openai": "^3.2.1",
|
18 |
"react": "18.2.0",
|