From 43b4f076e9402fa92244e749090f7240774a8c1d Mon Sep 17 00:00:00 2001 From: n4ze3m Date: Sat, 14 Dec 2024 18:30:27 +0530 Subject: [PATCH] feat: Add more options for chat messages to copy, download chat entirely --- bun.lockb | Bin 440402 -> 440402 bytes src/assets/locale/da/option.json | 17 +- src/assets/locale/de/option.json | 17 +- src/assets/locale/en/option.json | 17 +- src/assets/locale/es/option.json | 19 +- src/assets/locale/fa/option.json | 19 +- src/assets/locale/fr/option.json | 17 +- src/assets/locale/it/option.json | 17 +- src/assets/locale/ja-JP/option.json | 39 +++-- src/assets/locale/ko/option.json | 19 +- src/assets/locale/ml/option.json | 17 +- src/assets/locale/no/option.json | 19 +- src/assets/locale/pt-BR/option.json | 17 +- src/assets/locale/ru/option.json | 18 +- src/assets/locale/sv/option.json | 17 +- src/assets/locale/uk/option.json | 19 +- src/assets/locale/zh/option.json | 17 +- .../Common/{ShareBtn.tsx => ShareModal.tsx} | 124 ++++++------- src/components/Layouts/Header.tsx | 28 ++- src/components/Layouts/MoreOptions.tsx | 163 ++++++++++++++++++ 20 files changed, 509 insertions(+), 111 deletions(-) rename src/components/Common/{ShareBtn.tsx => ShareModal.tsx} (64%) create mode 100644 src/components/Layouts/MoreOptions.tsx diff --git a/bun.lockb b/bun.lockb index 8d4fe90e294d729224c4e4397fe940cc2bf9fa6d..a2dd2afd2e73621f84936a13cae325a70697b803 100644 GIT binary patch delta 41 vcmccgLF&>6sfHHD7N!>FEiBfpoQ!b>dIow%76$Dutt>#yy4|Ifjn5bWLEsH> delta 41 pcmccgLF&>6sfHHD7N!>FEiBfpoJrG_U0T`ri~%9-3%&pV diff --git a/src/assets/locale/da/option.json b/src/assets/locale/da/option.json index 5fc0281..17ad319 100644 --- a/src/assets/locale/da/option.json +++ b/src/assets/locale/da/option.json @@ -9,5 +9,20 @@ "validationSelectModel": "Venligst vælg en model for at forsæætte", "deleteHistoryConfirmation": "Er du sikker på at du vil slette denne historik?", "editHistoryTitle": "Indtast en ny titel", - "temporaryChat": "Midlertidig Chat" + "temporaryChat": "Midlertidig Chat", + "more": { + "copy": { + "group": "Kopier", + "asText": "Kopier som tekst", + "asMarkdown": "Kopier som Markdown", + "success": "Kopieret til udklipsholder!" + }, + "download": { + "group": "Download", + "text": "Tekstfil (.txt)", + "markdown": "Markdown (.md)", + "json": "JSON-fil (.json)" + }, + "share": "Del" + } } \ No newline at end of file diff --git a/src/assets/locale/de/option.json b/src/assets/locale/de/option.json index 4303931..708231f 100644 --- a/src/assets/locale/de/option.json +++ b/src/assets/locale/de/option.json @@ -9,5 +9,20 @@ "validationSelectModel": "Bitte wähle ein Modell aus, um fortzufahren", "deleteHistoryConfirmation": "Bist du sicher, dass du diesen Verlauf löschen möchtest?", "editHistoryTitle": "Gib einen neuen Titel ein", - "temporaryChat": "Temporärer Chat" + "temporaryChat": "Temporärer Chat", + "more": { + "copy": { + "group": "Kopieren", + "asText": "Als Text kopieren", + "asMarkdown": "Als Markdown kopieren", + "success": "In die Zwischenablage kopiert!" + }, + "download": { + "group": "Herunterladen", + "text": "Textdatei (.txt)", + "markdown": "Markdown (.md)", + "json": "JSON-Datei (.json)" + }, + "share": "Teilen" + } } \ No newline at end of file diff --git a/src/assets/locale/en/option.json b/src/assets/locale/en/option.json index 7d11938..debb1be 100644 --- a/src/assets/locale/en/option.json +++ b/src/assets/locale/en/option.json @@ -9,5 +9,20 @@ "validationSelectModel": "Please select a model to continue", "deleteHistoryConfirmation": "Are you sure you want to delete this history?", "editHistoryTitle": "Enter a new title", - "temporaryChat": "Temporary Chat" + "temporaryChat": "Temporary Chat", + "more": { + "copy": { + "group": "Copy", + "asText": "Copy as Text", + "asMarkdown": "Copy as Markdown", + "success": "Copied to clipboard!" + }, + "download": { + "group": "Download", + "text": "Text File (.txt)", + "markdown": "Markdown (.md)", + "json": "JSON File (.json)" + }, + "share": "Share" + } } \ No newline at end of file diff --git a/src/assets/locale/es/option.json b/src/assets/locale/es/option.json index d9ab8cf..3a2a395 100644 --- a/src/assets/locale/es/option.json +++ b/src/assets/locale/es/option.json @@ -9,5 +9,20 @@ "validationSelectModel": "Selecione un modelo para continuar", "deleteHistoryConfirmation": "¿Esta seguro que quiere borrar éste histórico?", "editHistoryTitle": "Ingrese un nuevo título", - "temporaryChat": "Chat Temporal" -} + "temporaryChat": "Chat Temporal", + "more": { + "copy": { + "group": "Copiar", + "asText": "Copiar como Texto", + "asMarkdown": "Copiar como Markdown", + "success": "¡Copiado al portapapeles!" + }, + "download": { + "group": "Descargar", + "text": "Archivo de Texto (.txt)", + "markdown": "Markdown (.md)", + "json": "Archivo JSON (.json)" + }, + "share": "Compartir" + } +} \ No newline at end of file diff --git a/src/assets/locale/fa/option.json b/src/assets/locale/fa/option.json index 5278cd0..7d1ee0e 100644 --- a/src/assets/locale/fa/option.json +++ b/src/assets/locale/fa/option.json @@ -9,5 +9,20 @@ "validationSelectModel": "لطفا یک مدل را برای ادامه انتخاب کنید", "deleteHistoryConfirmation": "آیا مطمئن هستید که می خواهید این تاریخچه را حذف کنید؟", "editHistoryTitle": "یک عنوان جدید وارد کنید", - "temporaryChat": "گپ موقت" -} + "temporaryChat": "گپ موقت", + "more": { + "copy": { + "group": "کپی", + "asText": "کپی به صورت متن", + "asMarkdown": "کپی به صورت مارک‌داون", + "success": "در کلیپ‌بورد کپی شد!" + }, + "download": { + "group": "دانلود", + "text": "فایل متنی (.txt)", + "markdown": "مارک‌داون (.md)", + "json": "فایل JSON (.json)" + }, + "share": "اشتراک‌گذاری" + } +} \ No newline at end of file diff --git a/src/assets/locale/fr/option.json b/src/assets/locale/fr/option.json index ad374aa..a2b1dd0 100644 --- a/src/assets/locale/fr/option.json +++ b/src/assets/locale/fr/option.json @@ -9,5 +9,20 @@ "validationSelectModel": "Veuillez sélectionner un modèle pour continuer", "deleteHistoryConfirmation": "Êtes-vous sûr de vouloir supprimer cette historique ?", "editHistoryTitle": "Entrez un nouveau titre", - "temporaryChat": "Chat temporaire" + "temporaryChat": "Chat temporaire", + "more": { + "copy": { + "group": "Copier", + "asText": "Copier en texte", + "asMarkdown": "Copier en Markdown", + "success": "Copié dans le presse-papiers !" + }, + "download": { + "group": "Télécharger", + "text": "Fichier texte (.txt)", + "markdown": "Markdown (.md)", + "json": "Fichier JSON (.json)" + }, + "share": "Partager" + } } \ No newline at end of file diff --git a/src/assets/locale/it/option.json b/src/assets/locale/it/option.json index fb222c3..f95cf73 100644 --- a/src/assets/locale/it/option.json +++ b/src/assets/locale/it/option.json @@ -9,5 +9,20 @@ "validationSelectModel": "Scegliere un modello per continuare", "deleteHistoryConfirmation": "Sei sicuro che vuoi eliminare la cronologia?", "editHistoryTitle": "Inserisci un nuovo titolo", - "temporaryChat": "Chat Temporanea" + "temporaryChat": "Chat Temporanea", + "more": { + "copy": { + "group": "Copia", + "asText": "Copia come Testo", + "asMarkdown": "Copia come Markdown", + "success": "Copiato negli appunti!" + }, + "download": { + "group": "Scarica", + "text": "File di Testo (.txt)", + "markdown": "Markdown (.md)", + "json": "File JSON (.json)" + }, + "share": "Condividi" + } } \ No newline at end of file diff --git a/src/assets/locale/ja-JP/option.json b/src/assets/locale/ja-JP/option.json index 76dc343..6b7e7bb 100644 --- a/src/assets/locale/ja-JP/option.json +++ b/src/assets/locale/ja-JP/option.json @@ -1,13 +1,28 @@ { - "newChat": "新しいチャット", - "selectAPrompt": "プロンプトを選択", - "githubRepository": "GitHubリポジトリ", - "settings": "設定", - "sidebarTitle": "チャット履歴", - "error": "エラー", - "somethingWentWrong": "何かが間違っています", - "validationSelectModel": "続行するにはモデルを選択してください", - "deleteHistoryConfirmation": "この履歴を削除しますか?", - "editHistoryTitle": "新しいタイトルを入力", - "temporaryChat": "一時的なチャット" - } \ No newline at end of file + "newChat": "新しいチャット", + "selectAPrompt": "プロンプトを選択", + "githubRepository": "GitHubリポジトリ", + "settings": "設定", + "sidebarTitle": "チャット履歴", + "error": "エラー", + "somethingWentWrong": "何かが間違っています", + "validationSelectModel": "続行するにはモデルを選択してください", + "deleteHistoryConfirmation": "この履歴を削除しますか?", + "editHistoryTitle": "新しいタイトルを入力", + "temporaryChat": "一時的なチャット", + "more": { + "copy": { + "group": "コピー", + "asText": "テキストとしてコピー", + "asMarkdown": "Markdownとしてコピー", + "success": "クリップボードにコピーしました!" + }, + "download": { + "group": "ダウンロード", + "text": "テキストファイル (.txt)", + "markdown": "Markdownファイル (.md)", + "json": "JSONファイル (.json)" + }, + "share": "共有" + } +} \ No newline at end of file diff --git a/src/assets/locale/ko/option.json b/src/assets/locale/ko/option.json index 2db30cc..d93fcc3 100644 --- a/src/assets/locale/ko/option.json +++ b/src/assets/locale/ko/option.json @@ -9,5 +9,20 @@ "validationSelectModel": "계속하려면 모델을 선택하세요", "deleteHistoryConfirmation": "이 기록을 삭제하시겠습니까?", "editHistoryTitle": "새 제목 입력", - "temporaryChat": "임시 채팅" -} + "temporaryChat": "임시 채팅", + "more": { + "copy": { + "group": "복사", + "asText": "텍스트로 복사", + "asMarkdown": "마크다운으로 복사", + "success": "클립보드에 복사되었습니다!" + }, + "download": { + "group": "다운로드", + "text": "텍스트 파일 (.txt)", + "markdown": "마크다운 (.md)", + "json": "JSON 파일 (.json)" + }, + "share": "공유" + } +} \ No newline at end of file diff --git a/src/assets/locale/ml/option.json b/src/assets/locale/ml/option.json index 1004b13..53d8b4d 100644 --- a/src/assets/locale/ml/option.json +++ b/src/assets/locale/ml/option.json @@ -9,5 +9,20 @@ "deleteHistoryConfirmation": "നിങ്ങളുടെ ചാറ്റ് ചരിത്രം ഇല്ലാതാക്കണമെന്ന് തീർച്ചയാണോ?", "editHistoryTitle": "ചാറ്റ് title എഡിറ്റുചെയ്യുക", "validationSelectModel": "തുടരുന്നതിന് ഒരു മോഡല്‍ തിരഞ്ഞെടുക്കുക", - "temporaryChat": "താൽക്കാലിക ചാറ്റ്" + "temporaryChat": "താൽക്കാലിക ചാറ്റ്", + "more": { + "copy": { + "group": "പകർത്തുക", + "asText": "ടെക്സ്റ്റായി പകർത്തുക", + "asMarkdown": "മാർക്ക്ഡൗൺ ആയി പകർത്തുക", + "success": "ക്ലിപ്പ്ബോർഡിലേക്ക് പകർത്തി!" + }, + "download": { + "group": "ഡൗൺലോഡ്", + "text": "ടെക്സ്റ്റ് ഫയൽ (.txt)", + "markdown": "മാർക്ക്ഡൗൺ (.md)", + "json": "JSON ഫയൽ (.json)" + }, + "share": "പങ്കുവയ്ക്കുക" + } } \ No newline at end of file diff --git a/src/assets/locale/no/option.json b/src/assets/locale/no/option.json index 64adb7b..fad0201 100644 --- a/src/assets/locale/no/option.json +++ b/src/assets/locale/no/option.json @@ -9,5 +9,20 @@ "validationSelectModel": "Vennligst velg en modell for å fortsette", "deleteHistoryConfirmation": "Er du sikker på at du vil slette denne historikken?", "editHistoryTitle": "Skriv inn en ny tittel", - "temporaryChat": "Midlertidig Chat" -} + "temporaryChat": "Midlertidig Chat", + "more": { + "copy": { + "group": "Kopier", + "asText": "Kopier som tekst", + "asMarkdown": "Kopier som Markdown", + "success": "Kopiert til utklippstavlen!" + }, + "download": { + "group": "Last ned", + "text": "Tekstfil (.txt)", + "markdown": "Markdown (.md)", + "json": "JSON-fil (.json)" + }, + "share": "Del" + } +} \ No newline at end of file diff --git a/src/assets/locale/pt-BR/option.json b/src/assets/locale/pt-BR/option.json index eeffe1c..8702733 100644 --- a/src/assets/locale/pt-BR/option.json +++ b/src/assets/locale/pt-BR/option.json @@ -9,5 +9,20 @@ "validationSelectModel": "Por favor, selecione um modelo para continuar", "deleteHistoryConfirmation": "Tem certeza de que deseja excluir este histórico?", "editHistoryTitle": "Digite um novo título", - "temporaryChat": "Chat Temporário" + "temporaryChat": "Chat Temporário", + "more": { + "copy": { + "group": "Copiar", + "asText": "Copiar como Texto", + "asMarkdown": "Copiar como Markdown", + "success": "Copiado para área de transferência!" + }, + "download": { + "group": "Baixar", + "text": "Arquivo de Texto (.txt)", + "markdown": "Markdown (.md)", + "json": "Arquivo JSON (.json)" + }, + "share": "Compartilhar" + } } \ No newline at end of file diff --git a/src/assets/locale/ru/option.json b/src/assets/locale/ru/option.json index bd8f987..a14a9d4 100644 --- a/src/assets/locale/ru/option.json +++ b/src/assets/locale/ru/option.json @@ -9,5 +9,19 @@ "validationSelectModel": "Пожалуйста, выберите модель, чтобы продолжить", "deleteHistoryConfirmation": "Вы уверены, что хотите удалить эту историю?", "editHistoryTitle": "Введите новое название", - "temporaryChat": "Временный чат" -} + "temporaryChat": "Временный чат", + "more": { + "copy": { + "group": "Копировать", + "asText": "Копировать как текст", + "asMarkdown": "Копировать как Markdown", + "success": "Скопировано в буфер обмена!" + }, + "download": { + "group": "Скачать", + "text": "Текстовый файл (.txt)", + "markdown": "Markdown (.md)", + "json": "JSON файл (.json)" + }, + "share": "Поделиться" + }} diff --git a/src/assets/locale/sv/option.json b/src/assets/locale/sv/option.json index 98e1012..96d873a 100644 --- a/src/assets/locale/sv/option.json +++ b/src/assets/locale/sv/option.json @@ -9,5 +9,20 @@ "validationSelectModel": "Vänligen välj en modell för att fortsätta", "deleteHistoryConfirmation": "Är du säker på att du vill radera denna historik?", "editHistoryTitle": "Ange en ny titel", - "temporaryChat": "Tillfällig chatt" + "temporaryChat": "Tillfällig chatt", + "more": { + "copy": { + "group": "Kopiera", + "asText": "Kopiera som text", + "asMarkdown": "Kopiera som Markdown", + "success": "Kopierat till urklipp!" + }, + "download": { + "group": "Ladda ner", + "text": "Textfil (.txt)", + "markdown": "Markdown (.md)", + "json": "JSON-fil (.json)" + }, + "share": "Dela" + } } diff --git a/src/assets/locale/uk/option.json b/src/assets/locale/uk/option.json index 22a76d4..0823889 100644 --- a/src/assets/locale/uk/option.json +++ b/src/assets/locale/uk/option.json @@ -9,5 +9,20 @@ "validationSelectModel": "Будь ласка, виберіть модель для продовження", "deleteHistoryConfirmation": "Ви впевнені, що хочете видалити цю історію?", "editHistoryTitle": "Введіть нову назву", - "temporaryChat": "Тимчасовий чат" -} + "temporaryChat": "Тимчасовий чат", + "more": { + "copy": { + "group": "Копіювати", + "asText": "Копіювати як текст", + "asMarkdown": "Копіювати як Markdown", + "success": "Скопійовано в буфер обміну!" + }, + "download": { + "group": "Завантажити", + "text": "Текстовий файл (.txt)", + "markdown": "Markdown (.md)", + "json": "JSON файл (.json)" + }, + "share": "Поділитися" + } +} \ No newline at end of file diff --git a/src/assets/locale/zh/option.json b/src/assets/locale/zh/option.json index cba6731..d4ed2ee 100644 --- a/src/assets/locale/zh/option.json +++ b/src/assets/locale/zh/option.json @@ -9,5 +9,20 @@ "validationSelectModel": "请选择一个模型以继续", "deleteHistoryConfirmation": "你确定要删除这个历史记录吗?", "editHistoryTitle": "输入一个新的标题", - "temporaryChat": "临时聊天" + "temporaryChat": "临时聊天", + "more": { + "copy": { + "group": "复制", + "asText": "复制为文本", + "asMarkdown": "复制为 Markdown", + "success": "已复制到剪贴板!" + }, + "download": { + "group": "下载", + "text": "文本文件 (.txt)", + "markdown": "Markdown 文件 (.md)", + "json": "JSON 文件 (.json)" + }, + "share": "分享" + } } \ No newline at end of file diff --git a/src/components/Common/ShareBtn.tsx b/src/components/Common/ShareModal.tsx similarity index 64% rename from src/components/Common/ShareBtn.tsx rename to src/components/Common/ShareModal.tsx index e1146a0..3808d3b 100644 --- a/src/components/Common/ShareBtn.tsx +++ b/src/components/Common/ShareModal.tsx @@ -14,6 +14,8 @@ import fetcher from "@/libs/fetcher" type Props = { messages: Message[] historyId: string + open: boolean + setOpen: (state: boolean) => void } const reformatMessages = (messages: Message[], username: string) => { @@ -77,9 +79,13 @@ export const PlaygroundMessage = ( ) } -export const ShareBtn: React.FC = ({ messages, historyId }) => { +export const ShareModal: React.FC = ({ + messages, + historyId, + open, + setOpen +}) => { const { t } = useTranslation("common") - const [open, setOpen] = useState(false) const [form] = Form.useForm() const name = Form.useWatch("name", form) @@ -142,75 +148,55 @@ export const ShareBtn: React.FC = ({ messages, historyId }) => { }) return ( - <> - - - + setOpen(false)}> +
+ + + + + + - setOpen(false)}> - - - - - - - - - -
-
- {messages.map((message, index) => ( - - ))} -
+ +
+
+ {messages.map((message, index) => ( + + ))}
- +
+
- -
- -
-
- - - + +
+ +
+
+ + ) } diff --git a/src/components/Layouts/Header.tsx b/src/components/Layouts/Header.tsx index 72f31da..0001ab7 100644 --- a/src/components/Layouts/Header.tsx +++ b/src/components/Layouts/Header.tsx @@ -19,10 +19,10 @@ import { fetchChatModels } from "~/services/ollama" import { useMessageOption } from "~/hooks/useMessageOption" import { Select, Tooltip } from "antd" import { getAllPrompts } from "@/db" -import { ShareBtn } from "~/components/Common/ShareBtn" import { ProviderIcons } from "../Common/ProviderIcon" import { NewChat } from "./NewChat" import { PageAssistSelect } from "../Select" +import { MoreOptions } from "./MoreOptions" type Props = { setSidebarOpen: (open: boolean) => void setOpenModelSettings: (open: boolean) => void @@ -50,7 +50,11 @@ export const Header: React.FC = ({ historyId, temporaryChat } = useMessageOption() - const { data: models, isLoading: isModelsLoading, refetch } = useQuery({ + const { + data: models, + isLoading: isModelsLoading, + refetch + } = useQuery({ queryKey: ["fetchModel"], queryFn: () => fetchChatModels({ returnEmpty: true }), refetchIntervalInBackground: false, @@ -134,7 +138,6 @@ export const Header: React.FC = ({ ), value: model.model }))} - onRefresh={() => { refetch() }} @@ -189,6 +192,19 @@ export const Header: React.FC = ({
+ {/* {pathname === "/" && + messages.length > 0 && + !streaming && + shareModeEnabled && ( + + )} */} + {messages.length > 0 && !streaming && ( + + )} {!hideCurrentChatModelSettings && ( )} - {pathname === "/" && - messages.length > 0 && - !streaming && - shareModeEnabled && ( - - )} { + return messages + .map((msg) => { + const text = `${msg.isBot ? msg.name : "You"}: ${msg.message}` + return text + }) + .join("\n\n") +} + +const formatAsMarkdown = (messages: Message[]) => { + return messages + .map((msg) => { + let content = `**${msg.isBot ? msg.name : "You"}**:\n${msg.message}` + + if (msg.images && msg.images.length > 0) { + const imageMarkdown = msg.images + .filter((img) => img.length > 0) + .map((img) => `\n\n![Image](${img})`) + .join("\n") + content += imageMarkdown + } + + return content + }) + .join("\n\n") +} + +const downloadFile = (content: string, filename: string) => { + const blob = new Blob([content], { type: "text/plain;charset=utf-8" }) + const url = URL.createObjectURL(blob) + const link = document.createElement("a") + link.href = url + link.download = filename + document.body.appendChild(link) + link.click() + document.body.removeChild(link) + URL.revokeObjectURL(url) +} + +export const MoreOptions = ({ + shareModeEnabled = false, + historyId, + messages +}: MoreOptionsProps) => { + const { t } = useTranslation("option") + const [onShareOpen, setOnShareOpen] = useState(false) + const baseItems: MenuProps["items"] = [ + { + type: "group", + label: t("more.copy.group"), + children: [ + { + key: "copy-text", + label: t("more.copy.asText"), + icon: , + onClick: () => { + navigator.clipboard.writeText(formatAsText(messages)) + message.success(t("more.copy.success")) + } + }, + { + key: "copy-markdown", + label: t("more.copy.asMarkdown"), + icon: , + onClick: () => { + navigator.clipboard.writeText(formatAsMarkdown(messages)) + message.success(t("more.copy.success")) + } + } + ] + }, + { + type: "divider" + }, + { + type: "group", + label: t("more.download.group"), + children: [ + { + key: "download-txt", + label: t("more.download.text"), + icon: , + onClick: () => { + downloadFile(formatAsText(messages), "chat.txt") + } + }, + { + key: "download-md", + label: t("more.download.markdown"), + icon: , + onClick: () => { + downloadFile(formatAsMarkdown(messages), "chat.md") + } + }, + { + key: "download-json", + label: t("more.download.json"), + icon: , + onClick: () => { + const jsonContent = JSON.stringify(messages, null, 2) + downloadFile(jsonContent, "chat.json") + } + } + ] + } + ] + + const shareItem = { + type: "divider" + } as const + + const shareOption = { + key: "share", + label: t("more.share"), + icon: , + onClick: () => { + setOnShareOpen(true) + } + } + + const items = shareModeEnabled + ? [...baseItems, shareItem, shareOption] + : baseItems + + return ( + <> + + + + + + ) +}