From f389e454013903d2e1921ba1795a4aaa33e8b0d7 Mon Sep 17 00:00:00 2001 From: n4ze3m Date: Wed, 8 May 2024 10:16:10 +0530 Subject: [PATCH 1/5] chore: Update message display logic to include cursor indicator --- src/hooks/useMessage.tsx | 6 +++--- src/hooks/useMessageOption.tsx | 8 ++++---- wxt.config.ts | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/hooks/useMessage.tsx b/src/hooks/useMessage.tsx index 1e32c5f..3f99135 100644 --- a/src/hooks/useMessage.tsx +++ b/src/hooks/useMessage.tsx @@ -235,7 +235,7 @@ export const useMessage = () => { if (message.id === generateMessageId) { return { ...message, - message: fullText.slice(0, -1) + "▋" + message: fullText + "▋" } } return message @@ -431,7 +431,7 @@ export const useMessage = () => { if (message.id === generateMessageId) { return { ...message, - message: fullText.slice(0, -1) + "▋" + message: fullText + "▋" } } return message @@ -445,7 +445,7 @@ export const useMessage = () => { if (message.id === generateMessageId) { return { ...message, - message: fullText.slice(0, -1) + message: fullText } } return message diff --git a/src/hooks/useMessageOption.tsx b/src/hooks/useMessageOption.tsx index 23f2433..64100fa 100644 --- a/src/hooks/useMessageOption.tsx +++ b/src/hooks/useMessageOption.tsx @@ -230,7 +230,7 @@ export const useMessageOption = () => { if (message.id === generateMessageId) { return { ...message, - message: fullText.slice(0, -1) + "▋" + message: fullText + "▋" } } return message @@ -436,7 +436,7 @@ export const useMessageOption = () => { if (message.id === generateMessageId) { return { ...message, - message: fullText.slice(0, -1) + "▋" + message: fullText + "▋" } } return message @@ -450,7 +450,7 @@ export const useMessageOption = () => { if (message.id === generateMessageId) { return { ...message, - message: fullText.slice(0, -1) + message: fullText } } return message @@ -646,7 +646,7 @@ export const useMessageOption = () => { if (message.id === generateMessageId) { return { ...message, - message: fullText.slice(0, -1) + "▋" + message: fullText + "▋" } } return message diff --git a/wxt.config.ts b/wxt.config.ts index a3d4010..d80478d 100644 --- a/wxt.config.ts +++ b/wxt.config.ts @@ -24,7 +24,7 @@ export default defineConfig({ srcDir: "src", outDir: "build", manifest: { - version: "1.1.5", + version: "1.1.6", name: '__MSG_extName__', description: '__MSG_extDescription__', default_locale: 'en', From f630addefca8d0b375969d0b2f3e50294b6cb4a8 Mon Sep 17 00:00:00 2001 From: n4ze3m Date: Wed, 8 May 2024 10:22:50 +0530 Subject: [PATCH 2/5] refactor: Update handlePromptChange logic to handle undefined value --- src/components/Layouts/Layout.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/Layouts/Layout.tsx b/src/components/Layouts/Layout.tsx index 296c641..03916be 100644 --- a/src/components/Layouts/Layout.tsx +++ b/src/components/Layouts/Layout.tsx @@ -63,13 +63,18 @@ export default function OptionLayout({ return prompts?.find((prompt) => prompt.id === id) } - const handlePromptChange = (value: string) => { + const handlePromptChange = (value?: string) => { + if (!value) { + setSelectedSystemPrompt(undefined) + setSelectedQuickPrompt(undefined) + return + } const prompt = getPromptInfoById(value) if (prompt?.is_system) { setSelectedSystemPrompt(prompt.id) } else { + setSelectedSystemPrompt(undefined) setSelectedQuickPrompt(prompt!.content) - setSelectedSystemPrompt("") } } From 88ad1fcab72cd99c22ee325c04a8590f8d4ff053 Mon Sep 17 00:00:00 2001 From: n4ze3m Date: Wed, 8 May 2024 10:44:51 +0530 Subject: [PATCH 3/5] feat: Add ModelSelect component to SidepanelForm --- src/components/Common/ModelSelect.tsx | 63 +++++++++++++++++++++++++ src/components/Sidepanel/Chat/empty.tsx | 7 ++- src/components/Sidepanel/Chat/form.tsx | 2 + 3 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 src/components/Common/ModelSelect.tsx diff --git a/src/components/Common/ModelSelect.tsx b/src/components/Common/ModelSelect.tsx new file mode 100644 index 0000000..22160b8 --- /dev/null +++ b/src/components/Common/ModelSelect.tsx @@ -0,0 +1,63 @@ +import { useQuery } from "@tanstack/react-query" +import { Dropdown, Tooltip } from "antd" +import { LucideBrain } from "lucide-react" +import React from "react" +import { useTranslation } from "react-i18next" +import { OllamaIcon } from "../Icons/Ollama" +import { fetchChatModels } from "@/services/ollama" +import { useMessage } from "@/hooks/useMessage" + +export const ModelSelect: React.FC = () => { + const { t } = useTranslation("common") + const { setSelectedModel, selectedModel } = useMessage() + const { data } = useQuery({ + queryKey: ["getAllModelsForSelect"], + queryFn: async () => { + const models = await fetchChatModels({ returnEmpty: false }) + return models + } + }) + + return ( + <> + {data && data.length > 0 && ( + ({ + key: d.name, + label: ( +
+
+ +
+ {d.name} +
+ ), + onClick: () => { + if (selectedModel === d.name) { + setSelectedModel(null) + } else { + setSelectedModel(d.name) + } + } + })) || [], + style: { + maxHeight: 500, + overflowY: "scroll" + }, + className: "no-scrollbar", + activeKey: selectedModel + }} + placement={"topLeft"} + trigger={["click"]}> + + + +
+ )} + + ) +} diff --git a/src/components/Sidepanel/Chat/empty.tsx b/src/components/Sidepanel/Chat/empty.tsx index 41305b8..499e320 100644 --- a/src/components/Sidepanel/Chat/empty.tsx +++ b/src/components/Sidepanel/Chat/empty.tsx @@ -1,4 +1,4 @@ -import { useQuery } from "@tanstack/react-query" +import { useQuery, useQueryClient } from "@tanstack/react-query" import { Select } from "antd" import { RotateCcw } from "lucide-react" import { useEffect, useState } from "react" @@ -15,6 +15,7 @@ import { export const EmptySidePanel = () => { const [ollamaURL, setOllamaURL] = useState("") const { t } = useTranslation(["playground", "common"]) + const queryClient = useQueryClient() const { data: ollamaInfo, status: ollamaStatus, @@ -26,7 +27,9 @@ export const EmptySidePanel = () => { const ollamaURL = await getOllamaURL() const isOk = await isOllamaRunning() const models = await fetchChatModels({ returnEmpty: false }) - + queryClient.invalidateQueries({ + queryKey: ["getAllModelsForSelect"] + }) return { isOk, models, diff --git a/src/components/Sidepanel/Chat/form.tsx b/src/components/Sidepanel/Chat/form.tsx index b8b3fc9..45ec340 100644 --- a/src/components/Sidepanel/Chat/form.tsx +++ b/src/components/Sidepanel/Chat/form.tsx @@ -10,6 +10,7 @@ import { useWebUI } from "~/store/webui" import { defaultEmbeddingModelForRag } from "~/services/ollama" import { ImageIcon, MicIcon, StopCircleIcon, X } from "lucide-react" import { useTranslation } from "react-i18next" +import { ModelSelect } from "@/components/Common/ModelSelect" type Props = { dropedFile: File | undefined @@ -186,6 +187,7 @@ export const SidepanelForm = ({ dropedFile }: Props) => { {...form.getInputProps("message")} />
+
- +

@@ -98,7 +103,7 @@ export const SettingOther = () => {

-
+
{t("generalSettings.system.deleteChatHistory.label")} @@ -122,6 +127,37 @@ export const SettingOther = () => { {t("generalSettings.system.deleteChatHistory.button")}
+
+ + {t("generalSettings.system.export.label")} + + +
+
+ + {t("generalSettings.system.import.label")} + + + { + if (e.target.files) { + importPageAssistData(e.target.files[0]) + } + }} + /> +
) diff --git a/src/db/index.ts b/src/db/index.ts index bc8b2f8..6ca341f 100644 --- a/src/db/index.ts +++ b/src/db/index.ts @@ -444,3 +444,15 @@ export const importChatHistory = async ( } } } + +export const exportPrompts = async () => { + const db = new PageAssitDatabase() + return await db.getAllPrompts() +} + +export const importPrompts = async (prompts: Prompts) => { + const db = new PageAssitDatabase() + for (const prompt of prompts) { + await db.addPrompt(prompt) + } +} diff --git a/src/db/vector.ts b/src/db/vector.ts index 2c2e281..09624fc 100644 --- a/src/db/vector.ts +++ b/src/db/vector.ts @@ -112,6 +112,22 @@ export class PageAssistVectorDb { }) }) } + + saveImportedData = async (data: VectorData[]): Promise => { + return new Promise((resolve, reject) => { + const obj: Record = {} + data.forEach((d) => { + obj[d.id] = d + }) + this.db.set(obj, () => { + if (chrome.runtime.lastError) { + reject(chrome.runtime.lastError) + } else { + resolve() + } + }) + }) + } } export const insertVector = async ( @@ -148,7 +164,5 @@ export const exportVectors = async () => { export const importVectors = async (data: VectorData[]) => { const db = new PageAssistVectorDb() - for (const d of data) { - await db.insertVector(d.id, d.vectors) - } + return db.saveImportedData(data) } diff --git a/src/libs/export-import.ts b/src/libs/export-import.ts new file mode 100644 index 0000000..50bda8b --- /dev/null +++ b/src/libs/export-import.ts @@ -0,0 +1,66 @@ +import { + exportChatHistory, + exportPrompts, + importChatHistory, + importPrompts +} from "@/db" +import { exportKnowledge, importKnowledge } from "@/db/knowledge" +import { exportVectors, importVectors } from "@/db/vector" +import { message } from "antd" + +export const exportPageAssistData = async () => { + const knowledge = await exportKnowledge() + const chat = await exportChatHistory() + const vector = await exportVectors() + const prompts = await exportPrompts() + + const data = { + knowledge, + chat, + vector, + prompts + } + + const dataStr = JSON.stringify(data) + + const blob = new Blob([dataStr], { type: "application/json" }) + const url = URL.createObjectURL(blob) + + const a = document.createElement("a") + a.href = url + a.download = `page-assist-${new Date().toISOString()}.json` + a.click() + URL.revokeObjectURL(url) +} + +export const importPageAssistData = async (file: File) => { + const reader = new FileReader() + reader.onload = async () => { + try { + const data = JSON.parse(reader.result as string) + + if (data?.knowledge) { + await importKnowledge(data.knowledge) + } + + if (data?.chat) { + await importChatHistory(data.chat) + } + + if (data?.vector) { + await importVectors(data.vector) + } + + if (data?.prompts) { + await importPrompts(data.prompts) + } + + message.success("Data imported successfully") + } catch (e) { + console.error(e) + message.error("Failed to import data") + } + } + + reader.readAsText(file) +}