feat: OpenAI settings page
Add a new settings page for OpenAI configuration, including a dedicated tab in the settings layout, translations, and routing.
This commit is contained in:
		
							parent
							
								
									2e97f6470d
								
							
						
					
					
						commit
						e2e3655c47
					
				
							
								
								
									
										38
									
								
								src/assets/locale/en/openai.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/assets/locale/en/openai.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | { | ||||||
|  |     "settings": "OpenAI API Settings", | ||||||
|  |     "heading": "OpenAI API Settings", | ||||||
|  |     "subheading": "Manage and configure your OpenAI API Compatible providers here.", | ||||||
|  |     "addBtn": "Add Provider", | ||||||
|  |     "table": { | ||||||
|  |         "name": "Provider Name", | ||||||
|  |         "baseUrl": "Base URL", | ||||||
|  |         "actions": "Action" | ||||||
|  |     }, | ||||||
|  |     "modal": { | ||||||
|  |         "titleAdd": "Add New Provider", | ||||||
|  |         "name": { | ||||||
|  |             "label": "Provider Name", | ||||||
|  |             "required": "Provider name is required.", | ||||||
|  |             "placeholder": "Enter provider name" | ||||||
|  |         }, | ||||||
|  |         "baseUrl": { | ||||||
|  |             "label": "Base URL", | ||||||
|  |             "help": "The base URL of the OpenAI API provider. eg (http://loocalhost:8080/v1)", | ||||||
|  |             "required": "Base URL is required.", | ||||||
|  |             "placeholder": "Enter base URL" | ||||||
|  |         }, | ||||||
|  |         "apiKey": { | ||||||
|  |             "label": "API Key", | ||||||
|  |             "required": "API Key is required.", | ||||||
|  |             "placeholder": "Enter API Key" | ||||||
|  |         }, | ||||||
|  |         "submit": "Submit", | ||||||
|  |         "update": "Update", | ||||||
|  |         "deleteConfirm": "Are you sure you want to delete this provider?" | ||||||
|  |     }, | ||||||
|  |     "addSuccess":  "Provider added successfully.", | ||||||
|  |     "deleteSuccess": "Provider deleted successfully.", | ||||||
|  |     "updateSuccess": "Provider updated successfully.", | ||||||
|  |     "delete": "Delete", | ||||||
|  |     "edit": "Edit" | ||||||
|  | } | ||||||
| @ -6,12 +6,12 @@ import { | |||||||
|   BlocksIcon, |   BlocksIcon, | ||||||
|   InfoIcon, |   InfoIcon, | ||||||
|   CombineIcon, |   CombineIcon, | ||||||
|   ChromeIcon |   ChromeIcon, | ||||||
|  |   CloudCogIcon | ||||||
| } from "lucide-react" | } from "lucide-react" | ||||||
| import { useTranslation } from "react-i18next" | import { useTranslation } from "react-i18next" | ||||||
| import { Link, useLocation } from "react-router-dom" | import { Link, useLocation } from "react-router-dom" | ||||||
| import { OllamaIcon } from "../Icons/Ollama" | import { OllamaIcon } from "../Icons/Ollama" | ||||||
| import { Tag } from "antd" |  | ||||||
| import { BetaTag } from "../Common/Beta" | import { BetaTag } from "../Common/Beta" | ||||||
| 
 | 
 | ||||||
| function classNames(...classes: string[]) { | function classNames(...classes: string[]) { | ||||||
| @ -22,12 +22,11 @@ const LinkComponent = (item: { | |||||||
|   href: string |   href: string | ||||||
|   name: string | JSX.Element |   name: string | JSX.Element | ||||||
|   icon: any |   icon: any | ||||||
|   current: string, |   current: string | ||||||
|   beta?: boolean |   beta?: boolean | ||||||
| }) => { | }) => { | ||||||
|   return ( |   return ( | ||||||
|     <li className="inline-flex items-center"> |     <li className="inline-flex items-center"> | ||||||
| 
 |  | ||||||
|       <Link |       <Link | ||||||
|         to={item.href} |         to={item.href} | ||||||
|         className={classNames( |         className={classNames( | ||||||
| @ -47,16 +46,14 @@ const LinkComponent = (item: { | |||||||
|         /> |         /> | ||||||
|         {item.name} |         {item.name} | ||||||
|       </Link> |       </Link> | ||||||
|       { |       {item.beta && <BetaTag />} | ||||||
|         item.beta && <BetaTag /> |  | ||||||
|         } |  | ||||||
|     </li> |     </li> | ||||||
|   ) |   ) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export const SettingsLayout = ({ children }: { children: React.ReactNode }) => { | export const SettingsLayout = ({ children }: { children: React.ReactNode }) => { | ||||||
|   const location = useLocation() |   const location = useLocation() | ||||||
|   const { t } = useTranslation(["settings", "common"]) |   const { t } = useTranslation(["settings", "common", "openai"]) | ||||||
| 
 | 
 | ||||||
|   return ( |   return ( | ||||||
|     <> |     <> | ||||||
| @ -93,6 +90,13 @@ export const SettingsLayout = ({ children }: { children: React.ReactNode }) => { | |||||||
|                   beta |                   beta | ||||||
|                 /> |                 /> | ||||||
|               )} |               )} | ||||||
|  |               <LinkComponent | ||||||
|  |                 href="/settings/openai" | ||||||
|  |                 name={t("openai:settings")} | ||||||
|  |                 icon={CloudCogIcon} | ||||||
|  |                 current={location.pathname} | ||||||
|  |                 beta | ||||||
|  |               /> | ||||||
|               <LinkComponent |               <LinkComponent | ||||||
|                 href="/settings/model" |                 href="/settings/model" | ||||||
|                 name={t("manageModels.title")} |                 name={t("manageModels.title")} | ||||||
|  | |||||||
							
								
								
									
										218
									
								
								src/components/Option/Settings/openai.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										218
									
								
								src/components/Option/Settings/openai.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,218 @@ | |||||||
|  | import { Form, Input, Modal, Table, message, Tooltip } from "antd" | ||||||
|  | import { useState } from "react" | ||||||
|  | import { useTranslation } from "react-i18next" | ||||||
|  | import { | ||||||
|  |   addOpenAICofig, | ||||||
|  |   getAllOpenAIConfig, | ||||||
|  |   deleteOpenAIConfig, | ||||||
|  |   updateOpenAIConfig | ||||||
|  | } from "@/db/openai" | ||||||
|  | import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query" | ||||||
|  | import { Pencil, Trash2, Plus } from "lucide-react" | ||||||
|  | 
 | ||||||
|  | export const OpenAIApp = () => { | ||||||
|  |   const { t } = useTranslation("openai") | ||||||
|  |   const [open, setOpen] = useState(false) | ||||||
|  |   const [editingConfig, setEditingConfig] = useState(null) | ||||||
|  |   const queryClient = useQueryClient() | ||||||
|  |   const [form] = Form.useForm() | ||||||
|  | 
 | ||||||
|  |   const { data: configs, isLoading } = useQuery({ | ||||||
|  |     queryKey: ["openAIConfigs"], | ||||||
|  |     queryFn: getAllOpenAIConfig | ||||||
|  |   }) | ||||||
|  | 
 | ||||||
|  |   const addMutation = useMutation({ | ||||||
|  |     mutationFn: addOpenAICofig, | ||||||
|  |     onSuccess: () => { | ||||||
|  |       queryClient.invalidateQueries({ | ||||||
|  |         queryKey: ["openAIConfigs"] | ||||||
|  |       }) | ||||||
|  |       setOpen(false) | ||||||
|  |       message.success(t("addSuccess")) | ||||||
|  |     } | ||||||
|  |   }) | ||||||
|  | 
 | ||||||
|  |   const updateMutation = useMutation({ | ||||||
|  |     mutationFn: updateOpenAIConfig, | ||||||
|  |     onSuccess: () => { | ||||||
|  |       queryClient.invalidateQueries({ | ||||||
|  |         queryKey: ["openAIConfigs"] | ||||||
|  |       }) | ||||||
|  |       setOpen(false) | ||||||
|  |       message.success(t("updateSuccess")) | ||||||
|  |     } | ||||||
|  |   }) | ||||||
|  | 
 | ||||||
|  |   const deleteMutation = useMutation({ | ||||||
|  |     mutationFn: deleteOpenAIConfig, | ||||||
|  |     onSuccess: () => { | ||||||
|  |       queryClient.invalidateQueries({ | ||||||
|  |         queryKey: ["openAIConfigs"] | ||||||
|  |       }) | ||||||
|  |       message.success(t("deleteSuccess")) | ||||||
|  |     } | ||||||
|  |   }) | ||||||
|  | 
 | ||||||
|  |   const handleSubmit = (values: { | ||||||
|  |     id?: string | ||||||
|  |     name: string | ||||||
|  |     baseUrl: string | ||||||
|  |     apiKey: string | ||||||
|  |   }) => { | ||||||
|  |     if (editingConfig) { | ||||||
|  |       updateMutation.mutate({ id: editingConfig.id, ...values }) | ||||||
|  |     } else { | ||||||
|  |       addMutation.mutate(values) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   const handleEdit = (record: any) => { | ||||||
|  |     setEditingConfig(record) | ||||||
|  |     setOpen(true) | ||||||
|  |     form.setFieldsValue(record) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   const handleDelete = (id: string) => { | ||||||
|  |     deleteMutation.mutate(id) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return ( | ||||||
|  |     <div> | ||||||
|  |       <div> | ||||||
|  |         <div> | ||||||
|  |           <h2 className="text-base font-semibold leading-7 text-gray-900 dark:text-white"> | ||||||
|  |             {t("heading")} | ||||||
|  |           </h2> | ||||||
|  |           <p className="mt-1 text-sm leading-6 text-gray-600 dark:text-gray-400"> | ||||||
|  |             {t("subheading")} | ||||||
|  |           </p> | ||||||
|  |           <div className="border border-b border-gray-200 dark:border-gray-600 mt-3 mb-6"></div> | ||||||
|  |         </div> | ||||||
|  |         <div className="mb-6"> | ||||||
|  |           <div className="-ml-4 -mt-2 flex flex-wrap items-center justify-end sm:flex-nowrap"> | ||||||
|  |             <div className="ml-4 mt-2 flex-shrink-0"> | ||||||
|  |               <button | ||||||
|  |                 onClick={() => { | ||||||
|  |                   setEditingConfig(null) | ||||||
|  |                   setOpen(true) | ||||||
|  |                   form.resetFields() | ||||||
|  |                 }} | ||||||
|  |                 className="inline-flex items-center rounded-md border border-transparent bg-black px-2 py-2 text-md font-medium leading-4 text-white shadow-sm hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 dark:bg-white dark:text-gray-800 dark:hover:bg-gray-100 dark:focus:ring-gray-500 dark:focus:ring-offset-gray-100 disabled:opacity-50"> | ||||||
|  |                 {t("addBtn")} | ||||||
|  |               </button> | ||||||
|  |             </div> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  | 
 | ||||||
|  |         <Table | ||||||
|  |           columns={[ | ||||||
|  |             { | ||||||
|  |               title: t("table.name"), | ||||||
|  |               dataIndex: "name", | ||||||
|  |               key: "name" | ||||||
|  |             }, | ||||||
|  |             { | ||||||
|  |               title: t("table.baseUrl"), | ||||||
|  |               dataIndex: "baseUrl", | ||||||
|  |               key: "baseUrl" | ||||||
|  |             }, | ||||||
|  |             { | ||||||
|  |               title: t("table.actions"), | ||||||
|  |               key: "actions", | ||||||
|  |               render: (_, record) => ( | ||||||
|  |                 <div className="flex gap-4"> | ||||||
|  |                   <Tooltip title={t("edit")}> | ||||||
|  |                     <button | ||||||
|  |                       className="text-gray-700 dark:text-gray-400" | ||||||
|  |                       onClick={() => handleEdit(record)}> | ||||||
|  |                       <Pencil className="size-4" /> | ||||||
|  |                     </button> | ||||||
|  |                   </Tooltip> | ||||||
|  |                   <Tooltip title={t("delete")}> | ||||||
|  |                     <button | ||||||
|  |                       className="text-red-500 dark:text-red-400" | ||||||
|  |                       onClick={() => { | ||||||
|  |                         // add confirmation here
 | ||||||
|  |                         if ( | ||||||
|  |                           confirm( | ||||||
|  |                             t("modal.deleteConfirm", { | ||||||
|  |                               name: record.name | ||||||
|  |                             }) | ||||||
|  |                           ) | ||||||
|  |                         ) { | ||||||
|  |                           handleDelete(record.id) | ||||||
|  |                         } | ||||||
|  |                       }}> | ||||||
|  |                       <Trash2 className="size-4" /> | ||||||
|  |                     </button> | ||||||
|  |                   </Tooltip> | ||||||
|  |                 </div> | ||||||
|  |               ) | ||||||
|  |             } | ||||||
|  |           ]} | ||||||
|  |           dataSource={configs} | ||||||
|  |           loading={isLoading} | ||||||
|  |           rowKey="id" | ||||||
|  |         /> | ||||||
|  | 
 | ||||||
|  |         <Modal | ||||||
|  |           open={open} | ||||||
|  |           title={editingConfig ? t("modal.titleEdit") : t("modal.titleAdd")} | ||||||
|  |           onCancel={() => { | ||||||
|  |             setOpen(false) | ||||||
|  |             setEditingConfig(null) | ||||||
|  |             form.resetFields() | ||||||
|  |           }} | ||||||
|  |           footer={null}> | ||||||
|  |           <Form | ||||||
|  |             form={form} | ||||||
|  |             layout="vertical" | ||||||
|  |             onFinish={handleSubmit} | ||||||
|  |             initialValues={editingConfig}> | ||||||
|  |             <Form.Item | ||||||
|  |               name="name" | ||||||
|  |               label={t("modal.name.label")} | ||||||
|  |               rules={[ | ||||||
|  |                 { | ||||||
|  |                   required: true, | ||||||
|  |                   message: t("modal.name.required") | ||||||
|  |                 } | ||||||
|  |               ]}> | ||||||
|  |               <Input size="large" placeholder={t("modal.name.placeholder")} /> | ||||||
|  |             </Form.Item> | ||||||
|  | 
 | ||||||
|  |             <Form.Item | ||||||
|  |               name="baseUrl" | ||||||
|  |               label={t("modal.baseUrl.label")} | ||||||
|  |               help={t("modal.baseUrl.help")} | ||||||
|  |               rules={[ | ||||||
|  |                 { | ||||||
|  |                   required: true, | ||||||
|  |                   message: t("modal.baseUrl.required") | ||||||
|  |                 } | ||||||
|  |               ]}> | ||||||
|  |               <Input | ||||||
|  |                 size="large" | ||||||
|  |                 placeholder={t("modal.baseUrl.placeholder")} | ||||||
|  |               /> | ||||||
|  |             </Form.Item> | ||||||
|  | 
 | ||||||
|  |             <Form.Item name="apiKey" label={t("modal.apiKey.label")}> | ||||||
|  |               <Input.Password | ||||||
|  |                 size="large" | ||||||
|  |                 placeholder={t("modal.apiKey.placeholder")} | ||||||
|  |               /> | ||||||
|  |             </Form.Item> | ||||||
|  | 
 | ||||||
|  |             <button | ||||||
|  |               type="submit" | ||||||
|  |               className="inline-flex justify-center w-full text-center mt-4 items-center rounded-md border border-transparent bg-black px-2 py-2 text-sm font-medium leading-4 text-white shadow-sm hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 dark:bg-white dark:text-gray-800 dark:hover:bg-gray-100 dark:focus:ring-gray-500 dark:focus:ring-offset-gray-100 disabled:opacity-50"> | ||||||
|  |               {editingConfig ? t("modal.update") : t("modal.submit")} | ||||||
|  |             </button> | ||||||
|  |           </Form> | ||||||
|  |         </Modal> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   ) | ||||||
|  | } | ||||||
							
								
								
									
										146
									
								
								src/db/openai.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								src/db/openai.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,146 @@ | |||||||
|  | type OpenAIModelConfig = { | ||||||
|  |     id: string | ||||||
|  |     name: string | ||||||
|  |     baseUrl: string | ||||||
|  |     apiKey?: string | ||||||
|  |     createdAt: number | ||||||
|  | } | ||||||
|  | export const generateID = () => { | ||||||
|  |     return "openai-xxxx-xxx-xxxx".replace(/[x]/g, () => { | ||||||
|  |         const r = Math.floor(Math.random() * 16) | ||||||
|  |         return r.toString(16) | ||||||
|  |     }) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export class OpenAIModelDb { | ||||||
|  |     db: chrome.storage.StorageArea | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     constructor() { | ||||||
|  |         this.db = chrome.storage.local | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     getAll = async (): Promise<OpenAIModelConfig[]> => { | ||||||
|  |         return new Promise((resolve, reject) => { | ||||||
|  |             this.db.get(null, (result) => { | ||||||
|  |                 if (chrome.runtime.lastError) { | ||||||
|  |                     reject(chrome.runtime.lastError) | ||||||
|  |                 } else { | ||||||
|  |                     const data = Object.keys(result).map((key) => result[key]) | ||||||
|  |                     resolve(data) | ||||||
|  |                 } | ||||||
|  |             }) | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     create = async (config: OpenAIModelConfig): Promise<void> => { | ||||||
|  |         return new Promise((resolve, reject) => { | ||||||
|  |             this.db.set({ [config.id]: config }, () => { | ||||||
|  |                 if (chrome.runtime.lastError) { | ||||||
|  |                     reject(chrome.runtime.lastError) | ||||||
|  |                 } else { | ||||||
|  |                     resolve() | ||||||
|  |                 } | ||||||
|  |             }) | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     getById = async (id: string): Promise<OpenAIModelConfig> => { | ||||||
|  |         return new Promise((resolve, reject) => { | ||||||
|  |             this.db.get(id, (result) => { | ||||||
|  |                 if (chrome.runtime.lastError) { | ||||||
|  |                     reject(chrome.runtime.lastError) | ||||||
|  |                 } else { | ||||||
|  |                     resolve(result[id]) | ||||||
|  |                 } | ||||||
|  |             }) | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     update = async (config: OpenAIModelConfig): Promise<void> => { | ||||||
|  |         return new Promise((resolve, reject) => { | ||||||
|  |             this.db.set({ [config.id]: config }, () => { | ||||||
|  |                 if (chrome.runtime.lastError) { | ||||||
|  |                     reject(chrome.runtime.lastError) | ||||||
|  |                 } else { | ||||||
|  |                     resolve() | ||||||
|  |                 } | ||||||
|  |             }) | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     delete = async (id: string): Promise<void> => { | ||||||
|  |         return new Promise((resolve, reject) => { | ||||||
|  |             this.db.remove(id, () => { | ||||||
|  |                 if (chrome.runtime.lastError) { | ||||||
|  |                     reject(chrome.runtime.lastError) | ||||||
|  |                 } else { | ||||||
|  |                     resolve() | ||||||
|  |                 } | ||||||
|  |             }) | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | export const addOpenAICofig = async ({ name, baseUrl, apiKey }: { name: string, baseUrl: string, apiKey: string }) => { | ||||||
|  |     const openaiDb = new OpenAIModelDb() | ||||||
|  |     const id = generateID() | ||||||
|  |     const config: OpenAIModelConfig = { | ||||||
|  |         id, | ||||||
|  |         name, | ||||||
|  |         baseUrl, | ||||||
|  |         apiKey, | ||||||
|  |         createdAt: Date.now() | ||||||
|  |     } | ||||||
|  |     await openaiDb.create(config) | ||||||
|  |     return id | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | export const getAllOpenAIConfig = async () => { | ||||||
|  |     const openaiDb = new OpenAIModelDb() | ||||||
|  |     const configs = await openaiDb.getAll() | ||||||
|  |     return configs | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export const updateOpenAIConfig = async ({ id, name, baseUrl, apiKey }: { id: string, name: string, baseUrl: string, apiKey: string }) => { | ||||||
|  |     const openaiDb = new OpenAIModelDb() | ||||||
|  |     const config: OpenAIModelConfig = { | ||||||
|  |         id, | ||||||
|  |         name, | ||||||
|  |         baseUrl, | ||||||
|  |         apiKey, | ||||||
|  |         createdAt: Date.now() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     await openaiDb.update(config) | ||||||
|  | 
 | ||||||
|  |     return config | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | export const deleteOpenAIConfig = async (id: string) => { | ||||||
|  |     const openaiDb = new OpenAIModelDb() | ||||||
|  |     await openaiDb.delete(id) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | export const updateOpenAIConfigApiKey = async (id: string, { name, baseUrl, apiKey }: { name: string, baseUrl: string, apiKey: string }) => { | ||||||
|  |     const openaiDb = new OpenAIModelDb() | ||||||
|  |     const config: OpenAIModelConfig = { | ||||||
|  |         id, | ||||||
|  |         name, | ||||||
|  |         baseUrl, | ||||||
|  |         apiKey, | ||||||
|  |         createdAt: Date.now() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     await openaiDb.update(config) | ||||||
|  | } | ||||||
| @ -5,6 +5,7 @@ import sidepanel from "@/assets/locale/en/sidepanel.json"; | |||||||
| import settings from "@/assets/locale/en/settings.json"; | import settings from "@/assets/locale/en/settings.json"; | ||||||
| import knowledge from "@/assets/locale/en/knowledge.json"; | import knowledge from "@/assets/locale/en/knowledge.json"; | ||||||
| import chrome from "@/assets/locale/en/chrome.json"; | import chrome from "@/assets/locale/en/chrome.json"; | ||||||
|  | import openai from "@/assets/locale/en/openai.json"; | ||||||
| 
 | 
 | ||||||
| export const en = { | export const en = { | ||||||
|     option, |     option, | ||||||
| @ -13,5 +14,6 @@ export const en = { | |||||||
|     sidepanel, |     sidepanel, | ||||||
|     settings, |     settings, | ||||||
|     knowledge, |     knowledge, | ||||||
|     chrome |     chrome, | ||||||
|  |     openai | ||||||
| } | } | ||||||
| @ -11,6 +11,7 @@ import SidepanelChat from "./sidepanel-chat" | |||||||
| import SidepanelSettings from "./sidepanel-settings" | import SidepanelSettings from "./sidepanel-settings" | ||||||
| import OptionRagSettings from "./option-rag" | import OptionRagSettings from "./option-rag" | ||||||
| import OptionChrome from "./option-settings-chrome" | import OptionChrome from "./option-settings-chrome" | ||||||
|  | import OptionOpenAI from "./option-settings-openai" | ||||||
| 
 | 
 | ||||||
| export const OptionRoutingChrome = () => { | export const OptionRoutingChrome = () => { | ||||||
|   return ( |   return ( | ||||||
| @ -21,6 +22,7 @@ export const OptionRoutingChrome = () => { | |||||||
|       <Route path="/settings/prompt" element={<OptionPrompt />} /> |       <Route path="/settings/prompt" element={<OptionPrompt />} /> | ||||||
|       <Route path="/settings/ollama" element={<OptionOllamaSettings />} /> |       <Route path="/settings/ollama" element={<OptionOllamaSettings />} /> | ||||||
|       <Route path="/settings/chrome" element={<OptionChrome />} /> |       <Route path="/settings/chrome" element={<OptionChrome />} /> | ||||||
|  |       <Route path="/settings/openai" element={<OptionOpenAI />} /> | ||||||
|       <Route path="/settings/share" element={<OptionShare />} /> |       <Route path="/settings/share" element={<OptionShare />} /> | ||||||
|       <Route path="/settings/knowledge" element={<OptionKnowledgeBase />} /> |       <Route path="/settings/knowledge" element={<OptionKnowledgeBase />} /> | ||||||
|       <Route path="/settings/rag" element={<OptionRagSettings />} /> |       <Route path="/settings/rag" element={<OptionRagSettings />} /> | ||||||
|  | |||||||
| @ -14,6 +14,7 @@ const OptionShare = lazy(() => import("./option-settings-share")) | |||||||
| const OptionKnowledgeBase = lazy(() => import("./option-settings-knowledge")) | const OptionKnowledgeBase = lazy(() => import("./option-settings-knowledge")) | ||||||
| const OptionAbout = lazy(() => import("./option-settings-about")) | const OptionAbout = lazy(() => import("./option-settings-about")) | ||||||
| const OptionRagSettings = lazy(() => import("./option-rag")) | const OptionRagSettings = lazy(() => import("./option-rag")) | ||||||
|  | const OptionOpenAI = lazy(() => import("./option-settings-openai")) | ||||||
| 
 | 
 | ||||||
| export const OptionRoutingFirefox = () => { | export const OptionRoutingFirefox = () => { | ||||||
|   return ( |   return ( | ||||||
| @ -23,6 +24,7 @@ export const OptionRoutingFirefox = () => { | |||||||
|       <Route path="/settings/model" element={<OptionModal />} /> |       <Route path="/settings/model" element={<OptionModal />} /> | ||||||
|       <Route path="/settings/prompt" element={<OptionPrompt />} /> |       <Route path="/settings/prompt" element={<OptionPrompt />} /> | ||||||
|       <Route path="/settings/ollama" element={<OptionOllamaSettings />} /> |       <Route path="/settings/ollama" element={<OptionOllamaSettings />} /> | ||||||
|  |       <Route path="/settings/openai" element={<OptionOpenAI />} /> | ||||||
|       <Route path="/settings/share" element={<OptionShare />} /> |       <Route path="/settings/share" element={<OptionShare />} /> | ||||||
|       <Route path="/settings/knowledge" element={<OptionKnowledgeBase />} /> |       <Route path="/settings/knowledge" element={<OptionKnowledgeBase />} /> | ||||||
|       <Route path="/settings/about" element={<OptionAbout />} /> |       <Route path="/settings/about" element={<OptionAbout />} /> | ||||||
|  | |||||||
							
								
								
									
										15
									
								
								src/routes/option-settings-openai.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/routes/option-settings-openai.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | |||||||
|  | import { SettingsLayout } from "~/components/Layouts/SettingsOptionLayout" | ||||||
|  | import OptionLayout from "~/components/Layouts/Layout" | ||||||
|  | import { OpenAIApp } from "@/components/Option/Settings/openai" | ||||||
|  | 
 | ||||||
|  | const OptionOpenAI = () => { | ||||||
|  |   return ( | ||||||
|  |     <OptionLayout> | ||||||
|  |       <SettingsLayout> | ||||||
|  |         <OpenAIApp /> | ||||||
|  |       </SettingsLayout> | ||||||
|  |     </OptionLayout> | ||||||
|  |   ) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export default OptionOpenAI | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user