|
| 1 | +import { motion } from 'framer-motion' |
1 | 2 | import { FC, PropsWithChildren, useCallback, useEffect, useState } from 'react' |
2 | 3 | import toast, { Toaster } from 'react-hot-toast' |
3 | 4 | import { useTranslation } from 'react-i18next' |
4 | 5 | import Browser from 'webextension-polyfill' |
5 | | -import { MotionButton } from '~app/components/Button' |
| 6 | +import Button from '~app/components/Button' |
6 | 7 | import { Input } from '~app/components/Input' |
7 | 8 | import RadioGroup from '~app/components/RadioGroup' |
8 | 9 | import Select from '~app/components/Select' |
@@ -93,125 +94,121 @@ function SettingPage() { |
93 | 94 |
|
94 | 95 | return ( |
95 | 96 | <PagePanel title={`${t('Settings')} (v${getVersion()})`}> |
96 | | - <div className="flex flex-row mt-3 mb-5 gap-3"> |
97 | | - <div className="flex flex-col gap-5"> |
98 | | - <div> |
99 | | - <p className="font-bold mb-2 text-lg">{t('Startup page')}</p> |
100 | | - <div className="w-[200px]"> |
101 | | - <Select |
102 | | - options={[ |
103 | | - { name: 'All-In-One', value: ALL_IN_ONE_PAGE_ID }, |
104 | | - ...Object.entries(CHATBOTS).map(([botId, bot]) => ({ name: bot.name, value: botId })), |
105 | | - ]} |
106 | | - value={userConfig.startupPage} |
107 | | - onChange={(v) => updateConfigValue({ startupPage: v })} |
108 | | - /> |
109 | | - </div> |
110 | | - </div> |
111 | | - <div className="flex flex-col gap-2 max-w-[700px]"> |
112 | | - <p className="font-bold text-lg">{t('Chatbots')}</p> |
113 | | - <EnabledBotsSettings userConfig={userConfig} updateConfigValue={updateConfigValue} /> |
| 97 | + <div className="flex flex-col gap-5 mt-3 mb-10 px-10"> |
| 98 | + <div> |
| 99 | + <p className="font-bold mb-2 text-lg">{t('Startup page')}</p> |
| 100 | + <div className="w-[200px]"> |
| 101 | + <Select |
| 102 | + options={[ |
| 103 | + { name: 'All-In-One', value: ALL_IN_ONE_PAGE_ID }, |
| 104 | + ...Object.entries(CHATBOTS).map(([botId, bot]) => ({ name: bot.name, value: botId })), |
| 105 | + ]} |
| 106 | + value={userConfig.startupPage} |
| 107 | + onChange={(v) => updateConfigValue({ startupPage: v })} |
| 108 | + /> |
114 | 109 | </div> |
115 | | - <div className="flex flex-col gap-5 w-fit max-w-[700px]"> |
116 | | - <ChatBotSettingPanel title="ChatGPT"> |
117 | | - <RadioGroup |
118 | | - options={Object.entries(ChatGPTMode).map(([k, v]) => ({ label: `${k} ${t('Mode')}`, value: v }))} |
119 | | - value={userConfig.chatgptMode} |
120 | | - onChange={(v) => updateConfigValue({ chatgptMode: v as ChatGPTMode })} |
121 | | - /> |
122 | | - {userConfig.chatgptMode === ChatGPTMode.API ? ( |
123 | | - <ChatGPTAPISettings userConfig={userConfig} updateConfigValue={updateConfigValue} /> |
124 | | - ) : userConfig.chatgptMode === ChatGPTMode.Azure ? ( |
125 | | - <ChatGPTAzureSettings userConfig={userConfig} updateConfigValue={updateConfigValue} /> |
126 | | - ) : userConfig.chatgptMode === ChatGPTMode.Poe ? ( |
127 | | - <ChatGPTPoeSettings userConfig={userConfig} updateConfigValue={updateConfigValue} /> |
128 | | - ) : userConfig.chatgptMode === ChatGPTMode.OpenRouter ? ( |
129 | | - <ChatGPTOpenRouterSettings userConfig={userConfig} updateConfigValue={updateConfigValue} /> |
130 | | - ) : ( |
131 | | - <ChatGPWebSettings userConfig={userConfig} updateConfigValue={updateConfigValue} /> |
132 | | - )} |
133 | | - </ChatBotSettingPanel> |
134 | | - <ChatBotSettingPanel title="Claude"> |
135 | | - <RadioGroup |
136 | | - options={Object.entries(ClaudeMode).map(([k, v]) => ({ label: `${k} ${t('Mode')}`, value: v }))} |
137 | | - value={userConfig.claudeMode} |
138 | | - onChange={(v) => updateConfigValue({ claudeMode: v as ClaudeMode })} |
| 110 | + </div> |
| 111 | + <div className="flex flex-col gap-2 max-w-[700px]"> |
| 112 | + <p className="font-bold text-lg">{t('Chatbots')}</p> |
| 113 | + <EnabledBotsSettings userConfig={userConfig} updateConfigValue={updateConfigValue} /> |
| 114 | + </div> |
| 115 | + <div className="flex flex-col gap-5 w-fit max-w-[700px]"> |
| 116 | + <ChatBotSettingPanel title="ChatGPT"> |
| 117 | + <RadioGroup |
| 118 | + options={Object.entries(ChatGPTMode).map(([k, v]) => ({ label: `${k} ${t('Mode')}`, value: v }))} |
| 119 | + value={userConfig.chatgptMode} |
| 120 | + onChange={(v) => updateConfigValue({ chatgptMode: v as ChatGPTMode })} |
| 121 | + /> |
| 122 | + {userConfig.chatgptMode === ChatGPTMode.API ? ( |
| 123 | + <ChatGPTAPISettings userConfig={userConfig} updateConfigValue={updateConfigValue} /> |
| 124 | + ) : userConfig.chatgptMode === ChatGPTMode.Azure ? ( |
| 125 | + <ChatGPTAzureSettings userConfig={userConfig} updateConfigValue={updateConfigValue} /> |
| 126 | + ) : userConfig.chatgptMode === ChatGPTMode.Poe ? ( |
| 127 | + <ChatGPTPoeSettings userConfig={userConfig} updateConfigValue={updateConfigValue} /> |
| 128 | + ) : userConfig.chatgptMode === ChatGPTMode.OpenRouter ? ( |
| 129 | + <ChatGPTOpenRouterSettings userConfig={userConfig} updateConfigValue={updateConfigValue} /> |
| 130 | + ) : ( |
| 131 | + <ChatGPWebSettings userConfig={userConfig} updateConfigValue={updateConfigValue} /> |
| 132 | + )} |
| 133 | + </ChatBotSettingPanel> |
| 134 | + <ChatBotSettingPanel title="Claude"> |
| 135 | + <RadioGroup |
| 136 | + options={Object.entries(ClaudeMode).map(([k, v]) => ({ label: `${k} ${t('Mode')}`, value: v }))} |
| 137 | + value={userConfig.claudeMode} |
| 138 | + onChange={(v) => updateConfigValue({ claudeMode: v as ClaudeMode })} |
| 139 | + /> |
| 140 | + {userConfig.claudeMode === ClaudeMode.API ? ( |
| 141 | + <ClaudeAPISettings userConfig={userConfig} updateConfigValue={updateConfigValue} /> |
| 142 | + ) : userConfig.claudeMode === ClaudeMode.Webapp ? ( |
| 143 | + <ClaudeWebappSettings userConfig={userConfig} updateConfigValue={updateConfigValue} /> |
| 144 | + ) : userConfig.claudeMode === ClaudeMode.OpenRouter ? ( |
| 145 | + <ClaudeOpenRouterSettings userConfig={userConfig} updateConfigValue={updateConfigValue} /> |
| 146 | + ) : ( |
| 147 | + <ClaudePoeSettings userConfig={userConfig} updateConfigValue={updateConfigValue} /> |
| 148 | + )} |
| 149 | + </ChatBotSettingPanel> |
| 150 | + <ChatBotSettingPanel title="Gemini Pro"> |
| 151 | + <div className="flex flex-col gap-1"> |
| 152 | + <p className="font-medium text-sm"> |
| 153 | + API Key ( |
| 154 | + <a |
| 155 | + href="https://makersuite.google.com/app/apikey" |
| 156 | + target="_blank" |
| 157 | + rel="noreferrer" |
| 158 | + className="underline" |
| 159 | + > |
| 160 | + how to create key |
| 161 | + </a> |
| 162 | + ) |
| 163 | + </p> |
| 164 | + <Input |
| 165 | + className="w-[400px]" |
| 166 | + placeholder="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" |
| 167 | + value={userConfig.geminiApiKey} |
| 168 | + onChange={(e) => updateConfigValue({ geminiApiKey: e.currentTarget.value })} |
| 169 | + type="password" |
139 | 170 | /> |
140 | | - {userConfig.claudeMode === ClaudeMode.API ? ( |
141 | | - <ClaudeAPISettings userConfig={userConfig} updateConfigValue={updateConfigValue} /> |
142 | | - ) : userConfig.claudeMode === ClaudeMode.Webapp ? ( |
143 | | - <ClaudeWebappSettings userConfig={userConfig} updateConfigValue={updateConfigValue} /> |
144 | | - ) : userConfig.claudeMode === ClaudeMode.OpenRouter ? ( |
145 | | - <ClaudeOpenRouterSettings userConfig={userConfig} updateConfigValue={updateConfigValue} /> |
146 | | - ) : ( |
147 | | - <ClaudePoeSettings userConfig={userConfig} updateConfigValue={updateConfigValue} /> |
148 | | - )} |
149 | | - </ChatBotSettingPanel> |
150 | | - <ChatBotSettingPanel title="Gemini Pro"> |
151 | | - <div className="flex flex-col gap-1"> |
152 | | - <p className="font-medium text-sm"> |
153 | | - API Key ( |
154 | | - <a |
155 | | - href="https://makersuite.google.com/app/apikey" |
156 | | - target="_blank" |
157 | | - rel="noreferrer" |
158 | | - className="underline" |
159 | | - > |
160 | | - how to create key |
161 | | - </a> |
162 | | - ) |
163 | | - </p> |
164 | | - <Input |
165 | | - className="w-[400px]" |
166 | | - placeholder="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" |
167 | | - value={userConfig.geminiApiKey} |
168 | | - onChange={(e) => updateConfigValue({ geminiApiKey: e.currentTarget.value })} |
169 | | - type="password" |
| 171 | + <Blockquote className="mt-1">{t('Your keys are stored locally')}</Blockquote> |
| 172 | + </div> |
| 173 | + </ChatBotSettingPanel> |
| 174 | + <ChatBotSettingPanel title="Bing"> |
| 175 | + <div className="flex flex-row gap-5 items-center"> |
| 176 | + <p className="font-medium">{t('Chat style')}</p> |
| 177 | + <div className="w-[150px]"> |
| 178 | + <Select |
| 179 | + options={BING_STYLE_OPTIONS} |
| 180 | + value={userConfig.bingConversationStyle} |
| 181 | + onChange={(v) => updateConfigValue({ bingConversationStyle: v })} |
| 182 | + position="top" |
170 | 183 | /> |
171 | | - <Blockquote className="mt-1">{t('Your keys are stored locally')}</Blockquote> |
172 | 184 | </div> |
173 | | - </ChatBotSettingPanel> |
174 | | - <ChatBotSettingPanel title="Bing"> |
175 | | - <div className="flex flex-row gap-5 items-center"> |
176 | | - <p className="font-medium">{t('Chat style')}</p> |
177 | | - <div className="w-[150px]"> |
178 | | - <Select |
179 | | - options={BING_STYLE_OPTIONS} |
180 | | - value={userConfig.bingConversationStyle} |
181 | | - onChange={(v) => updateConfigValue({ bingConversationStyle: v })} |
182 | | - position="top" |
183 | | - /> |
184 | | - </div> |
185 | | - </div> |
186 | | - </ChatBotSettingPanel> |
187 | | - <ChatBotSettingPanel title="Perplexity"> |
188 | | - <RadioGroup |
189 | | - options={Object.entries(PerplexityMode).map(([k, v]) => ({ label: `${k} ${t('Mode')}`, value: v }))} |
190 | | - value={userConfig.perplexityMode} |
191 | | - onChange={(v) => updateConfigValue({ perplexityMode: v as PerplexityMode })} |
192 | | - /> |
193 | | - {userConfig.perplexityMode === PerplexityMode.API && ( |
194 | | - <PerplexityAPISettings userConfig={userConfig} updateConfigValue={updateConfigValue} /> |
195 | | - )} |
196 | | - </ChatBotSettingPanel> |
197 | | - </div> |
198 | | - <ShortcutPanel /> |
199 | | - <ExportDataPanel /> |
200 | | - </div> |
201 | | - <div> |
202 | | - {dirty && ( |
203 | | - <MotionButton |
204 | | - color="primary" |
205 | | - text={t('Save')} |
206 | | - className="w-fit fixed bottom-10" |
207 | | - onClick={save} |
208 | | - animate={{ opacity: [0, 1] }} |
209 | | - transition={{ duration: 0.3 }} |
| 185 | + </div> |
| 186 | + </ChatBotSettingPanel> |
| 187 | + <ChatBotSettingPanel title="Perplexity"> |
| 188 | + <RadioGroup |
| 189 | + options={Object.entries(PerplexityMode).map(([k, v]) => ({ label: `${k} ${t('Mode')}`, value: v }))} |
| 190 | + value={userConfig.perplexityMode} |
| 191 | + onChange={(v) => updateConfigValue({ perplexityMode: v as PerplexityMode })} |
210 | 192 | /> |
211 | | - )} |
| 193 | + {userConfig.perplexityMode === PerplexityMode.API && ( |
| 194 | + <PerplexityAPISettings userConfig={userConfig} updateConfigValue={updateConfigValue} /> |
| 195 | + )} |
| 196 | + </ChatBotSettingPanel> |
212 | 197 | </div> |
| 198 | + <ShortcutPanel /> |
| 199 | + <ExportDataPanel /> |
213 | 200 | </div> |
214 | | - <Toaster position="bottom-right" /> |
| 201 | + {dirty && ( |
| 202 | + <motion.div |
| 203 | + className="sticky bottom-0 w-full bg-primary-background border-t-2 border-primary-border px-5 py-4 drop-shadow flex flex-row items-center justify-center" |
| 204 | + initial={{ y: 100 }} |
| 205 | + animate={{ y: 0 }} |
| 206 | + transition={{ type: 'tween', ease: 'easeInOut' }} |
| 207 | + > |
| 208 | + <Button color="primary" size="small" text={t('Save changes')} onClick={save} className="py-2" /> |
| 209 | + </motion.div> |
| 210 | + )} |
| 211 | + <Toaster position="bottom-center" /> |
215 | 212 | </PagePanel> |
216 | 213 | ) |
217 | 214 | } |
|
0 commit comments