diff --git a/src/components/Chat/ChatContent/DownloadChat.tsx b/src/components/Chat/ChatContent/DownloadChat.tsx index 98243462c..61bad422e 100644 --- a/src/components/Chat/ChatContent/DownloadChat.tsx +++ b/src/components/Chat/ChatContent/DownloadChat.tsx @@ -20,6 +20,32 @@ const DownloadChat = React.memo( ({ saveRef }: { saveRef: React.RefObject }) => { const { t } = useTranslation(); const [isModalOpen, setIsModalOpen] = useState(false); + const [isGenerating, setIsGenerating] = useState(false); + + const handleImageDownload = () => { + const currentRef = saveRef?.current; + if (currentRef) { + setIsGenerating(true); + requestAnimationFrame(async () => { + try { + currentRef.offsetHeight; + const imgData = await htmlToImg(currentRef); + await downloadImg( + imgData, + `${ + useStore + .getState() + .chats?.[useStore.getState().currentChatIndex].title.trim() ?? + 'download' + }.png` + ); + } finally { + setIsGenerating(false); + } + }); + } + }; + return ( <> - {/* */} - + {/* - + }} + > + + PDF + */} + + + )} diff --git a/src/constants/chat.ts b/src/constants/chat.ts index f806ef1b6..a9ac60dd5 100644 --- a/src/constants/chat.ts +++ b/src/constants/chat.ts @@ -14,7 +14,7 @@ const dateString = export const _defaultSystemMessage = import.meta.env.VITE_DEFAULT_SYSTEM_MESSAGE ?? `You are ChatGPT, a large language model trained by OpenAI. -Carefully heed the user's instructions. +Carefully heed the user's instructions. Respond using Markdown.`; export const modelOptions: ModelOptions[] = [ @@ -28,14 +28,23 @@ export const modelOptions: ModelOptions[] = [ 'gpt-4-0125-preview', 'gpt-4-turbo', 'gpt-4-turbo-2024-04-09', + 'gpt-4o-mini', + 'gpt-4o-mini-2024-07-18', 'gpt-4o', 'gpt-4o-2024-05-13', + 'gpt-4o-2024-08-06', + 'gpt-4o-2024-11-20', + 'chatgpt-4o-latest', + 'o1-mini', + 'o1-mini-2024-09-12', + 'o1-preview', + 'o1-preview-2024-09-12', // 'gpt-3.5-turbo-0301', // 'gpt-4-0314', // 'gpt-4-32k-0314', ]; -export const defaultModel = 'gpt-3.5-turbo'; +export const defaultModel = 'gpt-4o-mini'; export const modelMaxToken = { 'gpt-3.5-turbo': 4096, @@ -55,8 +64,17 @@ export const modelMaxToken = { 'gpt-4-0125-preview': 128000, 'gpt-4-turbo': 128000, 'gpt-4-turbo-2024-04-09': 128000, + 'gpt-4o-mini': 128000, + 'gpt-4o-mini-2024-07-18': 128000, 'gpt-4o': 128000, 'gpt-4o-2024-05-13': 128000, + 'gpt-4o-2024-08-06': 128000, + 'gpt-4o-2024-11-20': 128000, + 'chatgpt-4o-latest': 128000, + 'o1-mini': 128000, + 'o1-mini-2024-09-12': 128000, + 'o1-preview': 128000, + 'o1-preview-2024-09-12': 128000, }; export const modelCost = { @@ -128,6 +146,14 @@ export const modelCost = { prompt: { price: 0.01, unit: 1000 }, completion: { price: 0.03, unit: 1000 }, }, + 'gpt-4o-mini': { + prompt: { price: 0.00015, unit: 1000 }, + completion: { price: 0.0006, unit: 1000 }, + }, + 'gpt-4o-mini-2024-07-18': { + prompt: { price: 0.00015, unit: 1000 }, + completion: { price: 0.0006, unit: 1000 }, + }, 'gpt-4o': { prompt: { price: 0.005, unit: 1000 }, completion: { price: 0.015, unit: 1000 }, @@ -136,6 +162,34 @@ export const modelCost = { prompt: { price: 0.005, unit: 1000 }, completion: { price: 0.015, unit: 1000 }, }, + 'gpt-4o-2024-08-06': { + prompt: { price: 0.0025, unit: 1000 }, + completion: { price: 0.01, unit: 1000 }, + }, + 'gpt-4o-2024-11-20': { + prompt: { price: 0.0025, unit: 1000 }, + completion: { price: 0.01, unit: 1000 }, + }, + 'chatgpt-4o-latest': { + prompt: { price: 0.0025, unit: 1000 }, + completion: { price: 0.01, unit: 1000 }, + }, + 'o1-mini': { + prompt: { price: 0.003, unit: 1000 }, + completion: { price: 0.012, unit: 1000 }, + }, + 'o1-mini-2024-09-12': { + prompt: { price: 0.003, unit: 1000 }, + completion: { price: 0.012, unit: 1000 }, + }, + 'o1-preview': { + prompt: { price: 0.015, unit: 1000 }, + completion: { price: 0.06, unit: 1000 }, + }, + 'o1-preview-2024-09-12': { + prompt: { price: 0.015, unit: 1000 }, + completion: { price: 0.06, unit: 1000 }, + }, }; export const defaultUserMaxToken = 4000; diff --git a/src/types/chat.ts b/src/types/chat.ts index 5b706952a..886570f10 100644 --- a/src/types/chat.ts +++ b/src/types/chat.ts @@ -50,8 +50,17 @@ export interface Folder { } export type ModelOptions = + | 'gpt-4o-mini' + | 'gpt-4o-mini-2024-07-18' | 'gpt-4o' | 'gpt-4o-2024-05-13' + | 'gpt-4o-2024-08-06' + | 'gpt-4o-2024-11-20' + | 'chatgpt-4o-latest' + | 'o1-mini' + | 'o1-mini-2024-09-12' + | 'o1-preview' + | 'o1-preview-2024-09-12' | 'gpt-4' | 'gpt-4-32k' | 'gpt-4-1106-preview' diff --git a/src/utils/chat.ts b/src/utils/chat.ts index 8c0672643..a585fd1a3 100644 --- a/src/utils/chat.ts +++ b/src/utils/chat.ts @@ -8,19 +8,63 @@ export const htmlToImg = async (html: HTMLDivElement) => { if (needResize) { html.style.width = '1023px'; } - const canvas = await html2canvas(html); + await Promise.all( + Array.from(html.querySelectorAll('img')) + .filter(img => !img.complete) + .map(img => new Promise(resolve => { img.onload = img.onerror = resolve; })) + ); + const canvas = await html2canvas(html, { + useCORS: true, + }); if (needResize) html.style.width = initialWidth; - const dataURL = canvas.toDataURL('image/png'); + const croppedCanvas = document.createElement('canvas'); + const ctx = croppedCanvas.getContext('2d'); + if (ctx) { + const cropHeight = 3; + const cropWidth = 3; + croppedCanvas.width = canvas.width - cropWidth; + croppedCanvas.height = canvas.height - cropHeight; + ctx.drawImage( + canvas, + 0, 0, + canvas.width - cropWidth, canvas.height - cropHeight, + 0, 0, + canvas.width - cropWidth, canvas.height - cropHeight + ); + const dataURL = croppedCanvas.toDataURL('image/png'); return dataURL; + } else { + const dataURL = canvas.toDataURL('image/png'); + return dataURL; + } }; // Function to download the image as a file export const downloadImg = (imgData: string, fileName: string) => { + const byteString = atob(imgData.split(',')[1]); + const mimeString = 'image/png'; + const ab = new ArrayBuffer(byteString.length); + const ia = new Uint8Array(ab); + + for (let i = 0; i < byteString.length; i++) { + ia[i] = byteString.charCodeAt(i); + } + + const blob = new Blob([ab], { type: mimeString }); + + const url = URL.createObjectURL(blob); const link = document.createElement('a'); - link.href = imgData; + link.href = url; + + fileName = fileName.endsWith('.png') ? fileName : `${fileName}.png`; link.download = fileName; - link.click(); - link.remove(); + + document.body.appendChild(link); + const event = new MouseEvent('click'); + link.dispatchEvent(event); + document.body.removeChild(link); + + URL.revokeObjectURL(url); }; // Function to convert a chat object to markdown format @@ -34,10 +78,14 @@ export const chatToMarkdown = (chat: ChatInterface) => { // Function to download the markdown content as a file export const downloadMarkdown = (markdown: string, fileName: string) => { + const markdownFile = new Blob([markdown], { type: 'text/markdown;charset=utf-8' }); + const link = document.createElement('a'); - const markdownFile = new Blob([markdown], { type: 'text/markdown' }); link.href = URL.createObjectURL(markdownFile); + fileName = fileName.endsWith('.md') ? fileName : `${fileName}.md`; link.download = fileName; - link.click(); - link.remove(); + document.body.appendChild(link); + const event = new MouseEvent('click'); + link.dispatchEvent(event); + document.body.removeChild(link); };