Skip to content

Commit 677aa6e

Browse files
committed
feat: Add export/import functions for chat history, promt and knowledge
1 parent 11147fd commit 677aa6e

File tree

11 files changed

+159
-22
lines changed

11 files changed

+159
-22
lines changed

src/assets/locale/en/settings.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,12 @@
4141
"confirm": "Are you sure you want to delete your chat history? This action cannot be undone."
4242
},
4343
"export": {
44-
"label": "Export Chat History, Settings, and Prompts",
44+
"label": "Export Chat History, Knowledge Base, and Prompts",
4545
"button": "Export Data",
4646
"success": "Export Success"
4747
},
4848
"import": {
49-
"label": "Import Chat History, Settings, and Prompts",
49+
"label": "Import Chat History, Knowledge Base, and Prompts",
5050
"button": "Import Data",
5151
"success": "Import Success",
5252
"error": "Import Error"

src/assets/locale/ja-JP/settings.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,12 @@
4444
"confirm": "チャット履歴を削除してもよろしいですか?この操作は元に戻せません。"
4545
},
4646
"export": {
47-
"label": "チャット履歴、設定、プロンプトをエクスポートする",
47+
"label": "チャット履歴、知識ベース、プロンプトをエクスポート",
4848
"button": "データをエクスポート",
4949
"success": "エクスポート成功"
5050
},
5151
"import": {
52-
"label": "チャット履歴、設定、プロンプトをインポートする",
52+
"label": "チャット履歴、知識ベース、プロンプトをインポート",
5353
"button": "データをインポート",
5454
"success": "インポート成功",
5555
"error": "インポートエラー"

src/assets/locale/ml/settings.json

+6-6
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,16 @@
4444
"confirm": "നിങ്ങളുടെ ചാറ്റ് ചരിത്രം ഇല്ലാതാക്കണമെന്ന് ഉറപ്പാണോ? ഈ പ്രവർത്തനം പിന്നീട് പിന്വലിക്കാനാവില്ല."
4545
},
4646
"export": {
47-
"label": "ചാറ്റ് ചരിത്രം, ക്രമീകരണങ്ങൾ, പ്രോംപ്റ്റുകൾ എക്സ്പോർട്ട് ചെയ്യുക",
47+
"label": "ചാറ്റ് ചരിത്രം, അറിവ് അടിസ്ഥാനം, പ്രോംപ്റ്റുകൾ എക്സ്പോർട്ട് ചെയ്യുക",
4848
"button": "ഡാറ്റ എക്സ്പോർട്ട് ചെയ്യുക",
4949
"success": "എക്സ്പോർട്ട് വിജയകരമായി"
50-
},
51-
"import": {
52-
"label": "ചാറ്റ് ചരിത്രം, ക്രമീകരണങ്ങൾ, പ്രോംപ്റ്റുകൾ ഇമ്പോർട്ട് ചെയ്യുക",
50+
},
51+
"import": {
52+
"label": "ചാറ്റ് ചരിത്രം, അറിവ് അടിസ്ഥാനം, പ്രോംപ്റ്റുകൾ ഇമ്പോർട്ട് ചെയ്യുക",
5353
"button": "ഡാറ്റ ഇമ്പോർട്ട് ചെയ്യുക",
5454
"success": "ഇമ്പോർട്ട് വിജയകരമായി",
55-
"error": "ഇമ്പോർട്ട് പരാജയപ്പെട്ടു"
56-
}
55+
"error": "ഇമ്പോർട്ട് പിശക്"
56+
}
5757
},
5858
"tts": {
5959
"heading": "ടെക്സ്റ്റ്-ടു-സ്പീച്ച് ക്രമീകരണങ്ങൾ",

src/assets/locale/ru/settings.json

+4-4
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,13 @@
4141
"confirm": "Вы уверены, что хотите удалить историю чата? Это действие нельзя отменить."
4242
},
4343
"export": {
44-
"label": "Экспорт истории чата, настроек и подсказок",
45-
"button": "Экспорт данных",
44+
"label": "Экспорт истории чата, базы знаний и подсказок",
45+
"button": "Экспортировать данные",
4646
"success": "Экспорт успешен"
4747
},
4848
"import": {
49-
"label": "Импорт истории чата, настроек и подсказок",
50-
"button": "Импорт данных",
49+
"label": "Импорт истории чата, базы знаний и подсказок",
50+
"button": "Импортировать данные",
5151
"success": "Импорт успешен",
5252
"error": "Ошибка импорта"
5353
}

src/assets/locale/zh/settings.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,12 @@
4444
"confirm": "您确定要删除聊天记录吗?此操作无法撤销。"
4545
},
4646
"export": {
47-
"label": "导出聊天记录、设置和提示",
47+
"label": "导出聊天记录、知识库和提示",
4848
"button": "导出数据",
4949
"success": "导出成功"
5050
},
5151
"import": {
52-
"label": "导入聊天记录、设置和提示",
52+
"label": "导入聊天记录、知识库和提示",
5353
"button": "导入数据",
5454
"success": "导入成功",
5555
"error": "导入错误"

src/components/Common/Beta.tsx

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { Tag } from "antd"
2+
import { useTranslation } from "react-i18next"
3+
4+
export const BetaTag = () => {
5+
const { t } = useTranslation("common")
6+
7+
return <Tag color="yellow">{t("beta")}</Tag>
8+
}

src/components/Layouts/SettingsOptionLayout.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { useTranslation } from "react-i18next"
1010
import { Link, useLocation } from "react-router-dom"
1111
import { OllamaIcon } from "../Icons/Ollama"
1212
import { Tag } from "antd"
13+
import { BetaTag } from "../Common/Beta"
1314

1415
function classNames(...classes: string[]) {
1516
return classes.filter(Boolean).join(" ")
@@ -81,7 +82,7 @@ export const SettingsLayout = ({ children }: { children: React.ReactNode }) => {
8182
name={
8283
<div className="inline-flex items-center gap-2">
8384
{t("manageKnowledge.title")}
84-
<Tag color="yellow">{t("common:beta")}</Tag>
85+
<BetaTag />
8586
</div>
8687
}
8788
icon={BlocksIcon}

src/components/Option/Settings/other.tsx

+38-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ import { SearchModeSettings } from "./search-mode"
99
import { useTranslation } from "react-i18next"
1010
import { useI18n } from "@/hooks/useI18n"
1111
import { TTSModeSettings } from "./tts-mode"
12+
import {
13+
exportPageAssistData,
14+
importPageAssistData
15+
} from "@/libs/export-import"
16+
import { BetaTag } from "@/components/Common/Beta"
1217

1318
export const SettingOther = () => {
1419
const { clearChat, speechToTextLanguage, setSpeechToTextLanguage } =
@@ -90,15 +95,15 @@ export const SettingOther = () => {
9095
</button>
9196
</div>
9297
<SearchModeSettings />
93-
<TTSModeSettings />
98+
<TTSModeSettings />
9499
<div>
95100
<div className="mb-5">
96101
<h2 className="text-base font-semibold leading-7 text-gray-900 dark:text-white">
97102
{t("generalSettings.system.heading")}
98103
</h2>
99104
<div className="border border-b border-gray-200 dark:border-gray-600 mt-3"></div>
100105
</div>
101-
<div className="flex flex-row justify-between">
106+
<div className="flex flex-row mb-3 justify-between">
102107
<span className="text-gray-500 dark:text-neutral-50 ">
103108
{t("generalSettings.system.deleteChatHistory.label")}
104109
</span>
@@ -122,6 +127,37 @@ export const SettingOther = () => {
122127
{t("generalSettings.system.deleteChatHistory.button")}
123128
</button>
124129
</div>
130+
<div className="flex flex-row mb-3 justify-between">
131+
<span className="text-gray-500 dark:text-neutral-50 ">
132+
<BetaTag /> {t("generalSettings.system.export.label")}
133+
</span>
134+
<button
135+
onClick={exportPageAssistData}
136+
className="bg-gray-800 dark:bg-white text-white dark:text-gray-900 px-4 py-2 rounded-md cursor-pointer">
137+
{t("generalSettings.system.export.button")}
138+
</button>
139+
</div>
140+
<div className="flex flex-row mb-3 justify-between">
141+
<span className="text-gray-500 dark:text-neutral-50 ">
142+
<BetaTag /> {t("generalSettings.system.import.label")}
143+
</span>
144+
<label
145+
htmlFor="import"
146+
className="bg-gray-800 dark:bg-white text-white dark:text-gray-900 px-4 py-2 rounded-md cursor-pointer">
147+
{t("generalSettings.system.import.button")}
148+
</label>
149+
<input
150+
type="file"
151+
accept=".json"
152+
id="import"
153+
className="hidden"
154+
onChange={(e) => {
155+
if (e.target.files) {
156+
importPageAssistData(e.target.files[0])
157+
}
158+
}}
159+
/>
160+
</div>
125161
</div>
126162
</dl>
127163
)

src/db/index.ts

+12
Original file line numberDiff line numberDiff line change
@@ -444,3 +444,15 @@ export const importChatHistory = async (
444444
}
445445
}
446446
}
447+
448+
export const exportPrompts = async () => {
449+
const db = new PageAssitDatabase()
450+
return await db.getAllPrompts()
451+
}
452+
453+
export const importPrompts = async (prompts: Prompts) => {
454+
const db = new PageAssitDatabase()
455+
for (const prompt of prompts) {
456+
await db.addPrompt(prompt)
457+
}
458+
}

src/db/vector.ts

+17-3
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,22 @@ export class PageAssistVectorDb {
112112
})
113113
})
114114
}
115+
116+
saveImportedData = async (data: VectorData[]): Promise<void> => {
117+
return new Promise((resolve, reject) => {
118+
const obj: Record<string, VectorData> = {}
119+
data.forEach((d) => {
120+
obj[d.id] = d
121+
})
122+
this.db.set(obj, () => {
123+
if (chrome.runtime.lastError) {
124+
reject(chrome.runtime.lastError)
125+
} else {
126+
resolve()
127+
}
128+
})
129+
})
130+
}
115131
}
116132

117133
export const insertVector = async (
@@ -148,7 +164,5 @@ export const exportVectors = async () => {
148164

149165
export const importVectors = async (data: VectorData[]) => {
150166
const db = new PageAssistVectorDb()
151-
for (const d of data) {
152-
await db.insertVector(d.id, d.vectors)
153-
}
167+
return db.saveImportedData(data)
154168
}

src/libs/export-import.ts

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import {
2+
exportChatHistory,
3+
exportPrompts,
4+
importChatHistory,
5+
importPrompts
6+
} from "@/db"
7+
import { exportKnowledge, importKnowledge } from "@/db/knowledge"
8+
import { exportVectors, importVectors } from "@/db/vector"
9+
import { message } from "antd"
10+
11+
export const exportPageAssistData = async () => {
12+
const knowledge = await exportKnowledge()
13+
const chat = await exportChatHistory()
14+
const vector = await exportVectors()
15+
const prompts = await exportPrompts()
16+
17+
const data = {
18+
knowledge,
19+
chat,
20+
vector,
21+
prompts
22+
}
23+
24+
const dataStr = JSON.stringify(data)
25+
26+
const blob = new Blob([dataStr], { type: "application/json" })
27+
const url = URL.createObjectURL(blob)
28+
29+
const a = document.createElement("a")
30+
a.href = url
31+
a.download = `page-assist-${new Date().toISOString()}.json`
32+
a.click()
33+
URL.revokeObjectURL(url)
34+
}
35+
36+
export const importPageAssistData = async (file: File) => {
37+
const reader = new FileReader()
38+
reader.onload = async () => {
39+
try {
40+
const data = JSON.parse(reader.result as string)
41+
42+
if (data?.knowledge) {
43+
await importKnowledge(data.knowledge)
44+
}
45+
46+
if (data?.chat) {
47+
await importChatHistory(data.chat)
48+
}
49+
50+
if (data?.vector) {
51+
await importVectors(data.vector)
52+
}
53+
54+
if (data?.prompts) {
55+
await importPrompts(data.prompts)
56+
}
57+
58+
message.success("Data imported successfully")
59+
} catch (e) {
60+
console.error(e)
61+
message.error("Failed to import data")
62+
}
63+
}
64+
65+
reader.readAsText(file)
66+
}

0 commit comments

Comments
 (0)