commit
17dc6a68c0
@ -21,6 +21,9 @@
|
|||||||
"copilotResumeLastChat": {
|
"copilotResumeLastChat": {
|
||||||
"label": "استئناف آخر محادثة عند فتح اللوحة الجانبية (كوبيلوت)"
|
"label": "استئناف آخر محادثة عند فتح اللوحة الجانبية (كوبيلوت)"
|
||||||
},
|
},
|
||||||
|
"turnOnChatWithWebsite": {
|
||||||
|
"label": "تمكين الدردشة مع الموقع بشكل افتراضي (كوبيلوت)"
|
||||||
|
},
|
||||||
"webUIResumeLastChat": {
|
"webUIResumeLastChat": {
|
||||||
"label": "استئناف آخر محادثة عند فتح واجهة المستخدم"
|
"label": "استئناف آخر محادثة عند فتح واجهة المستخدم"
|
||||||
},
|
},
|
||||||
|
@ -21,6 +21,9 @@
|
|||||||
"copilotResumeLastChat": {
|
"copilotResumeLastChat": {
|
||||||
"label": "Letzten Chat beim Öffnen des Seitenpanels fortsetzen (Copilot)"
|
"label": "Letzten Chat beim Öffnen des Seitenpanels fortsetzen (Copilot)"
|
||||||
},
|
},
|
||||||
|
"turnOnChatWithWebsite": {
|
||||||
|
"label": "Chat mit Website standardmäßig aktivieren (Copilot)"
|
||||||
|
},
|
||||||
"webUIResumeLastChat": {
|
"webUIResumeLastChat": {
|
||||||
"label": "Letzten Chat beim Öffnen der Web-UI fortsetzen"
|
"label": "Letzten Chat beim Öffnen der Web-UI fortsetzen"
|
||||||
},
|
},
|
||||||
|
@ -21,7 +21,8 @@
|
|||||||
"group": "Download",
|
"group": "Download",
|
||||||
"text": "Text File (.txt)",
|
"text": "Text File (.txt)",
|
||||||
"markdown": "Markdown (.md)",
|
"markdown": "Markdown (.md)",
|
||||||
"json": "JSON File (.json)"
|
"json": "JSON File (.json)",
|
||||||
|
"image": "Image (.png)"
|
||||||
},
|
},
|
||||||
"share": "Share"
|
"share": "Share"
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,9 @@
|
|||||||
"copilotResumeLastChat": {
|
"copilotResumeLastChat": {
|
||||||
"label": "Resume the last chat when opening the SidePanel (Copilot)"
|
"label": "Resume the last chat when opening the SidePanel (Copilot)"
|
||||||
},
|
},
|
||||||
|
"turnOnChatWithWebsite": {
|
||||||
|
"label": "Enable Chat with Website by default (Copilot)"
|
||||||
|
},
|
||||||
"webUIResumeLastChat": {
|
"webUIResumeLastChat": {
|
||||||
"label": "Resume the last chat when opening the Web UI"
|
"label": "Resume the last chat when opening the Web UI"
|
||||||
},
|
},
|
||||||
|
@ -21,6 +21,9 @@
|
|||||||
"copilotResumeLastChat": {
|
"copilotResumeLastChat": {
|
||||||
"label": "Retomar el último chat al abrir el Panel Lateral (Copilot)"
|
"label": "Retomar el último chat al abrir el Panel Lateral (Copilot)"
|
||||||
},
|
},
|
||||||
|
"turnOnChatWithWebsite": {
|
||||||
|
"label": "Habilitar Chat con Sitio Web por defecto (Copilot)"
|
||||||
|
},
|
||||||
"webUIResumeLastChat": {
|
"webUIResumeLastChat": {
|
||||||
"label": "Retomar el último chat al abrir la Interfaz Web"
|
"label": "Retomar el último chat al abrir la Interfaz Web"
|
||||||
},
|
},
|
||||||
|
@ -21,6 +21,9 @@
|
|||||||
"copilotResumeLastChat": {
|
"copilotResumeLastChat": {
|
||||||
"label": "آخرین گفتگو را هنگام باز کردن SidePanel (Copilot) از سر بگیر"
|
"label": "آخرین گفتگو را هنگام باز کردن SidePanel (Copilot) از سر بگیر"
|
||||||
},
|
},
|
||||||
|
"turnOnChatWithWebsite": {
|
||||||
|
"label": "فعال کردن گپ با وب سایت به صورت پیشفرض (کوپایلوت)"
|
||||||
|
},
|
||||||
"hideCurrentChatModelSettings": {
|
"hideCurrentChatModelSettings": {
|
||||||
"label": "مخفی کردن تنظیمات مدل گپ فعلی را"
|
"label": "مخفی کردن تنظیمات مدل گپ فعلی را"
|
||||||
},
|
},
|
||||||
|
@ -21,6 +21,9 @@
|
|||||||
"copilotResumeLastChat": {
|
"copilotResumeLastChat": {
|
||||||
"label": "Reprendre la dernière conversation lors de l'ouverture du sidepanel (Copilot)"
|
"label": "Reprendre la dernière conversation lors de l'ouverture du sidepanel (Copilot)"
|
||||||
},
|
},
|
||||||
|
"turnOnChatWithWebsite": {
|
||||||
|
"label": "Activer le chat avec le site Web par défaut (Copilot)"
|
||||||
|
},
|
||||||
"webUIResumeLastChat": {
|
"webUIResumeLastChat": {
|
||||||
"label": "Reprendre la dernière conversation lors de l'ouverture de l'interface Web"
|
"label": "Reprendre la dernière conversation lors de l'ouverture de l'interface Web"
|
||||||
},
|
},
|
||||||
|
@ -21,6 +21,9 @@
|
|||||||
"copilotResumeLastChat": {
|
"copilotResumeLastChat": {
|
||||||
"label": "Riprendi l'ultima chat quando apri il Pannello Laterale (Copilot)"
|
"label": "Riprendi l'ultima chat quando apri il Pannello Laterale (Copilot)"
|
||||||
},
|
},
|
||||||
|
"turnOnChatWithWebsite": {
|
||||||
|
"label": "Abilita Chat con il Sito Web per impostazione predefinita (Copilot)"
|
||||||
|
},
|
||||||
"webUIResumeLastChat": {
|
"webUIResumeLastChat": {
|
||||||
"label": "Riprendi l'ultima chat quando apri l'interfaccia Web"
|
"label": "Riprendi l'ultima chat quando apri l'interfaccia Web"
|
||||||
},
|
},
|
||||||
|
@ -24,6 +24,9 @@
|
|||||||
"copilotResumeLastChat": {
|
"copilotResumeLastChat": {
|
||||||
"label": "サイドパネルを開いたときに最後のチャットを再開 (Copilot)"
|
"label": "サイドパネルを開いたときに最後のチャットを再開 (Copilot)"
|
||||||
},
|
},
|
||||||
|
"turnOnChatWithWebsite": {
|
||||||
|
"label": "デフォルトでウェブサイトとのチャットを有効にする (Copilot)"
|
||||||
|
},
|
||||||
"webUIResumeLastChat": {
|
"webUIResumeLastChat": {
|
||||||
"label": "Web UIを開いたときに最後のチャットを再開"
|
"label": "Web UIを開いたときに最後のチャットを再開"
|
||||||
},
|
},
|
||||||
|
@ -24,6 +24,9 @@
|
|||||||
"copilotResumeLastChat": {
|
"copilotResumeLastChat": {
|
||||||
"label": "사이드 패널을 열 때 마지막 채팅 재개 (Copilot)"
|
"label": "사이드 패널을 열 때 마지막 채팅 재개 (Copilot)"
|
||||||
},
|
},
|
||||||
|
"turnOnChatWithWebsite": {
|
||||||
|
"label": "웹사이트와의 채팅 기본 활성화 (Copilot)"
|
||||||
|
},
|
||||||
"webUIResumeLastChat": {
|
"webUIResumeLastChat": {
|
||||||
"label": "웹 UI를 열 때 마지막 채팅 재개"
|
"label": "웹 UI를 열 때 마지막 채팅 재개"
|
||||||
},
|
},
|
||||||
|
@ -24,6 +24,9 @@
|
|||||||
"copilotResumeLastChat": {
|
"copilotResumeLastChat": {
|
||||||
"label": "സൈഡ്പാനൽ തുറക്കുമ്പോൾ അവസാനത്തെ ചാറ്റ് പുനരാരംഭിക്കുക (Copilot)"
|
"label": "സൈഡ്പാനൽ തുറക്കുമ്പോൾ അവസാനത്തെ ചാറ്റ് പുനരാരംഭിക്കുക (Copilot)"
|
||||||
},
|
},
|
||||||
|
"turnOnChatWithWebsite": {
|
||||||
|
"label": "സ്ഥിരസ്ഥിതിയായി വെബ്സൈറ്റുമായുള്ള ചാറ്റ് പ്രവർത്തനക്ഷമമാക്കുക (കോപൈലറ്റ്)"
|
||||||
|
},
|
||||||
"webUIResumeLastChat": {
|
"webUIResumeLastChat": {
|
||||||
"label": "വെബ് UI തുറക്കുമ്പോൾ അവസാനത്തെ ചാറ്റ് പുനരാരംഭിക്കുക"
|
"label": "വെബ് UI തുറക്കുമ്പോൾ അവസാനത്തെ ചാറ്റ് പുനരാരംഭിക്കുക"
|
||||||
},
|
},
|
||||||
|
@ -21,6 +21,9 @@
|
|||||||
"copilotResumeLastChat": {
|
"copilotResumeLastChat": {
|
||||||
"label": "Gjenoppta siste chat ved åpning av SidePanel (copilot)"
|
"label": "Gjenoppta siste chat ved åpning av SidePanel (copilot)"
|
||||||
},
|
},
|
||||||
|
"turnOnChatWithWebsite": {
|
||||||
|
"label": "Aktiver Chat med Nettsted som standard (Copilot)"
|
||||||
|
},
|
||||||
"webUIResumeLastChat": {
|
"webUIResumeLastChat": {
|
||||||
"label": "Gjenoppta siste chat når Web UI åpnes"
|
"label": "Gjenoppta siste chat når Web UI åpnes"
|
||||||
},
|
},
|
||||||
|
@ -21,6 +21,9 @@
|
|||||||
"copilotResumeLastChat": {
|
"copilotResumeLastChat": {
|
||||||
"label": "Retomar o último chat ao abrir o Painel Lateral (Copilot)"
|
"label": "Retomar o último chat ao abrir o Painel Lateral (Copilot)"
|
||||||
},
|
},
|
||||||
|
"turnOnChatWithWebsite": {
|
||||||
|
"label": "Ativar Chat com o Site por padrão (Copilot)"
|
||||||
|
},
|
||||||
"webUIResumeLastChat": {
|
"webUIResumeLastChat": {
|
||||||
"label": "Retomar o último chat ao abrir a Interface Web"
|
"label": "Retomar o último chat ao abrir a Interface Web"
|
||||||
},
|
},
|
||||||
|
@ -21,6 +21,9 @@
|
|||||||
"copilotResumeLastChat": {
|
"copilotResumeLastChat": {
|
||||||
"label": "Возобновить последний чат при открытии боковой панели (Copilot)"
|
"label": "Возобновить последний чат при открытии боковой панели (Copilot)"
|
||||||
},
|
},
|
||||||
|
"turnOnChatWithWebsite": {
|
||||||
|
"label": "Включить чат с веб-сайтом по умолчанию (Copilot)"
|
||||||
|
},
|
||||||
"webUIResumeLastChat": {
|
"webUIResumeLastChat": {
|
||||||
"label": "Возобновить последний чат при открытии веб-интерфейса"
|
"label": "Возобновить последний чат при открытии веб-интерфейса"
|
||||||
},
|
},
|
||||||
|
@ -21,6 +21,9 @@
|
|||||||
"copilotResumeLastChat": {
|
"copilotResumeLastChat": {
|
||||||
"label": "Återuppta den senaste chatten när du öppnar sidopanelen (Copilot)"
|
"label": "Återuppta den senaste chatten när du öppnar sidopanelen (Copilot)"
|
||||||
},
|
},
|
||||||
|
"turnOnChatWithWebsite": {
|
||||||
|
"label": "Aktivera Chatta med Webbplats som standard (Copilot)"
|
||||||
|
},
|
||||||
"webUIResumeLastChat": {
|
"webUIResumeLastChat": {
|
||||||
"label": "Återuppta den senaste chatten när du öppnar webbgränssnittet"
|
"label": "Återuppta den senaste chatten när du öppnar webbgränssnittet"
|
||||||
},
|
},
|
||||||
|
@ -21,6 +21,9 @@
|
|||||||
"copilotResumeLastChat": {
|
"copilotResumeLastChat": {
|
||||||
"label": "Поновити останню розмову при відкритті бічної панелі (Copilot)"
|
"label": "Поновити останню розмову при відкритті бічної панелі (Copilot)"
|
||||||
},
|
},
|
||||||
|
"turnOnChatWithWebsite": {
|
||||||
|
"label": "Увімкнути чат з веб-сайтом за замовчуванням (Copilot)"
|
||||||
|
},
|
||||||
"webUIResumeLastChat": {
|
"webUIResumeLastChat": {
|
||||||
"label": "Поновити останню розмову при відкритті веб-інтерфейсу"
|
"label": "Поновити останню розмову при відкритті веб-інтерфейсу"
|
||||||
},
|
},
|
||||||
|
@ -24,6 +24,9 @@
|
|||||||
"copilotResumeLastChat": {
|
"copilotResumeLastChat": {
|
||||||
"label": "打开侧边栏时恢复上次聊天(Copilot)"
|
"label": "打开侧边栏时恢复上次聊天(Copilot)"
|
||||||
},
|
},
|
||||||
|
"turnOnChatWithWebsite": {
|
||||||
|
"label": "默认启用与网站对话(Copilot)"
|
||||||
|
},
|
||||||
"webUIResumeLastChat": {
|
"webUIResumeLastChat": {
|
||||||
"label": "打开Web UI时恢复上次聊天"
|
"label": "打开Web UI时恢复上次聊天"
|
||||||
},
|
},
|
||||||
|
@ -9,6 +9,7 @@ import { OpenRouterIcon } from "../Icons/OpenRouter"
|
|||||||
import { LLamaFile } from "../Icons/Llamafile"
|
import { LLamaFile } from "../Icons/Llamafile"
|
||||||
import { GeminiIcon } from "../Icons/GeminiIcon"
|
import { GeminiIcon } from "../Icons/GeminiIcon"
|
||||||
import { MistarlIcon } from "../Icons/Mistral"
|
import { MistarlIcon } from "../Icons/Mistral"
|
||||||
|
import { DeepSeekIcon } from "../Icons/DeepSeek"
|
||||||
|
|
||||||
export const ProviderIcons = ({
|
export const ProviderIcons = ({
|
||||||
provider,
|
provider,
|
||||||
@ -40,6 +41,8 @@ export const ProviderIcons = ({
|
|||||||
return <GeminiIcon className={className} />
|
return <GeminiIcon className={className} />
|
||||||
case "mistral":
|
case "mistral":
|
||||||
return <MistarlIcon className={className} />
|
return <MistarlIcon className={className} />
|
||||||
|
case "deepseek":
|
||||||
|
return <DeepSeekIcon className={className} />
|
||||||
default:
|
default:
|
||||||
return <OllamaIcon className={className} />
|
return <OllamaIcon className={className} />
|
||||||
}
|
}
|
||||||
|
19
src/components/Icons/DeepSeek.tsx
Normal file
19
src/components/Icons/DeepSeek.tsx
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import React from "react"
|
||||||
|
|
||||||
|
export const DeepSeekIcon = React.forwardRef<
|
||||||
|
SVGSVGElement,
|
||||||
|
React.SVGProps<SVGSVGElement>
|
||||||
|
>((props, ref) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
fill="currentColor"
|
||||||
|
fillRule="evenodd"
|
||||||
|
ref={ref}
|
||||||
|
style={{ flex: "none", lineHeight: 1 }}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...props}>
|
||||||
|
<path d="M23.748 4.482c-.254-.124-.364.113-.512.234-.051.039-.094.09-.137.136-.372.397-.806.657-1.373.626-.829-.046-1.537.214-2.163.848-.133-.782-.575-1.248-1.247-1.548-.352-.156-.708-.311-.955-.65-.172-.241-.219-.51-.305-.774-.055-.16-.11-.323-.293-.35-.2-.031-.278.136-.356.276-.313.572-.434 1.202-.422 1.84.027 1.436.633 2.58 1.838 3.393.137.093.172.187.129.323-.082.28-.18.552-.266.833-.055.179-.137.217-.329.14a5.526 5.526 0 01-1.736-1.18c-.857-.828-1.631-1.742-2.597-2.458a11.365 11.365 0 00-.689-.471c-.985-.957.13-1.743.388-1.836.27-.098.093-.432-.779-.428-.872.004-1.67.295-2.687.684a3.055 3.055 0 01-.465.137 9.597 9.597 0 00-2.883-.102c-1.885.21-3.39 1.102-4.497 2.623C.082 8.606-.231 10.684.152 12.85c.403 2.284 1.569 4.175 3.36 5.653 1.858 1.533 3.997 2.284 6.438 2.14 1.482-.085 3.133-.284 4.994-1.86.47.234.962.327 1.78.397.63.059 1.236-.03 1.705-.128.735-.156.684-.837.419-.961-2.155-1.004-1.682-.595-2.113-.926 1.096-1.296 2.746-2.642 3.392-7.003.05-.347.007-.565 0-.845-.004-.17.035-.237.23-.256a4.173 4.173 0 001.545-.475c1.396-.763 1.96-2.015 2.093-3.517.02-.23-.004-.467-.247-.588zM11.581 18c-2.089-1.642-3.102-2.183-3.52-2.16-.392.024-.321.471-.235.763.09.288.207.486.371.739.114.167.192.416-.113.603-.673.416-1.842-.14-1.897-.167-1.361-.802-2.5-1.86-3.301-3.307-.774-1.393-1.224-2.887-1.298-4.482-.02-.386.093-.522.477-.592a4.696 4.696 0 011.529-.039c2.132.312 3.946 1.265 5.468 2.774.868.86 1.525 1.887 2.202 2.891.72 1.066 1.494 2.082 2.48 2.914.348.292.625.514.891.677-.802.09-2.14.11-3.054-.614zm1-6.44a.306.306 0 01.415-.287.302.302 0 01.2.288.306.306 0 01-.31.307.303.303 0 01-.304-.308zm3.11 1.596c-.2.081-.399.151-.59.16a1.245 1.245 0 01-.798-.254c-.274-.23-.47-.358-.552-.758a1.73 1.73 0 01.016-.588c.07-.327-.008-.537-.239-.727-.187-.156-.426-.199-.688-.199a.559.559 0 01-.254-.078c-.11-.054-.2-.19-.114-.358.028-.054.16-.186.192-.21.356-.202.767-.136 1.146.016.352.144.618.408 1.001.782.391.451.462.576.685.914.176.265.336.537.445.848.067.195-.019.354-.25.452z" />
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
})
|
@ -3,13 +3,15 @@ import {
|
|||||||
FileText,
|
FileText,
|
||||||
Share2,
|
Share2,
|
||||||
FileJson,
|
FileJson,
|
||||||
FileCode
|
FileCode,
|
||||||
|
ImageIcon
|
||||||
} from "lucide-react"
|
} from "lucide-react"
|
||||||
import { Dropdown, MenuProps, message } from "antd"
|
import { Dropdown, MenuProps, message } from "antd"
|
||||||
import { Message } from "@/types/message"
|
import { Message } from "@/types/message"
|
||||||
import { useState } from "react"
|
import { useState } from "react"
|
||||||
import { ShareModal } from "../Common/ShareModal"
|
import { ShareModal } from "../Common/ShareModal"
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
|
import { removeModelSuffix } from "@/db/models"
|
||||||
|
|
||||||
interface MoreOptionsProps {
|
interface MoreOptionsProps {
|
||||||
messages: Message[]
|
messages: Message[]
|
||||||
@ -24,11 +26,10 @@ const formatAsText = (messages: Message[]) => {
|
|||||||
})
|
})
|
||||||
.join("\n\n")
|
.join("\n\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
const formatAsMarkdown = (messages: Message[]) => {
|
const formatAsMarkdown = (messages: Message[]) => {
|
||||||
return messages
|
return messages
|
||||||
.map((msg) => {
|
.map((msg) => {
|
||||||
let content = `**${msg.isBot ? msg.name : "You"}**:\n${msg.message}`
|
let content = `**${msg.isBot ? removeModelSuffix(msg.name?.replaceAll(/accounts\/[^\/]+\/models\//g, "")) : "You"}**:\n${msg.message}`
|
||||||
|
|
||||||
if (msg.images && msg.images.length > 0) {
|
if (msg.images && msg.images.length > 0) {
|
||||||
const imageMarkdown = msg.images
|
const imageMarkdown = msg.images
|
||||||
@ -55,6 +56,161 @@ const downloadFile = (content: string, filename: string) => {
|
|||||||
URL.revokeObjectURL(url)
|
URL.revokeObjectURL(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const generateChatImage = async (messages: Message[]) => {
|
||||||
|
const canvas = document.createElement("canvas")
|
||||||
|
const ctx = canvas.getContext("2d")!
|
||||||
|
|
||||||
|
canvas.width = 1200
|
||||||
|
const padding = 40
|
||||||
|
let yPosition = padding
|
||||||
|
|
||||||
|
const wrapText = (text: string, maxWidth: number) => {
|
||||||
|
const paragraphs = text.split("\n")
|
||||||
|
const lines = []
|
||||||
|
|
||||||
|
paragraphs.forEach((paragraph) => {
|
||||||
|
if (paragraph.length === 0) {
|
||||||
|
lines.push("")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const words = paragraph.split(" ")
|
||||||
|
let currentLine = words[0]
|
||||||
|
|
||||||
|
for (let i = 1; i < words.length; i++) {
|
||||||
|
const word = words[i]
|
||||||
|
const width = ctx.measureText(currentLine + " " + word).width
|
||||||
|
if (width < maxWidth) {
|
||||||
|
currentLine += " " + word
|
||||||
|
} else {
|
||||||
|
lines.push(currentLine)
|
||||||
|
currentLine = word
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lines.push(currentLine)
|
||||||
|
})
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
let totalHeight = padding
|
||||||
|
messages.forEach((msg) => {
|
||||||
|
totalHeight += 20
|
||||||
|
const maxWidth = canvas.width - padding * 2
|
||||||
|
|
||||||
|
if (msg.message.includes("```")) {
|
||||||
|
const blocks = msg.message.split("```")
|
||||||
|
blocks.forEach((block, index) => {
|
||||||
|
if (index % 2 === 1) {
|
||||||
|
const codeLines = block.split("\n")
|
||||||
|
totalHeight += codeLines.length * 25 + 20
|
||||||
|
} else {
|
||||||
|
const wrappedText = wrapText(block, maxWidth)
|
||||||
|
totalHeight += wrappedText.length * 25
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
const wrappedText = wrapText(msg.message, maxWidth)
|
||||||
|
totalHeight += wrappedText.length * 25
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.images?.length) {
|
||||||
|
totalHeight += msg.images.length * 250
|
||||||
|
}
|
||||||
|
|
||||||
|
totalHeight += 30
|
||||||
|
})
|
||||||
|
|
||||||
|
canvas.height = totalHeight
|
||||||
|
console.log(totalHeight)
|
||||||
|
|
||||||
|
ctx.fillStyle = "#ffffff"
|
||||||
|
ctx.fillRect(0, 0, canvas.width, canvas.height)
|
||||||
|
|
||||||
|
const drawText = async () => {
|
||||||
|
for (const msg of messages) {
|
||||||
|
ctx.font = "bold 18px Inter, Arial"
|
||||||
|
ctx.fillStyle = msg.isBot ? "#1A202C" : "#1E4E8C"
|
||||||
|
ctx.fillText(`${msg.isBot ? removeModelSuffix(msg.name?.replaceAll(/accounts\/[^\/]+\/models\//g, "")) : "You"}:`, padding, yPosition)
|
||||||
|
yPosition += 35
|
||||||
|
|
||||||
|
if (msg.message.includes("```")) {
|
||||||
|
const blocks = msg.message.split("```")
|
||||||
|
blocks.forEach((block, index) => {
|
||||||
|
if (index % 2 === 1) {
|
||||||
|
const codeLines = block.split("\n")
|
||||||
|
const codeHeight = codeLines.length * 25 + 20
|
||||||
|
ctx.fillStyle = "#1a1a1a"
|
||||||
|
ctx.fillRect(
|
||||||
|
padding,
|
||||||
|
yPosition,
|
||||||
|
canvas.width - padding * 2,
|
||||||
|
codeHeight
|
||||||
|
)
|
||||||
|
ctx.font = "15px Consolas, monospace"
|
||||||
|
ctx.fillStyle = "#e6e6e6"
|
||||||
|
codeLines.forEach((line, lineIndex) => {
|
||||||
|
ctx.fillText(line, padding + 15, yPosition + 25 + lineIndex * 25)
|
||||||
|
})
|
||||||
|
yPosition += codeHeight + 20
|
||||||
|
} else {
|
||||||
|
ctx.font = "16px Inter, Arial"
|
||||||
|
ctx.fillStyle = "#1A202C"
|
||||||
|
const wrappedText = wrapText(block, canvas.width - padding * 2)
|
||||||
|
wrappedText.forEach((line) => {
|
||||||
|
ctx.fillText(line, padding, yPosition)
|
||||||
|
yPosition += 30
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
ctx.font = "16px Inter, Arial"
|
||||||
|
ctx.fillStyle = "#1A202C"
|
||||||
|
const wrappedText = wrapText(msg.message, canvas.width - padding * 2)
|
||||||
|
wrappedText.forEach((line) => {
|
||||||
|
ctx.fillText(line, padding, yPosition)
|
||||||
|
yPosition += 30
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.images?.length) {
|
||||||
|
for (const imgUrl of msg.images) {
|
||||||
|
if (imgUrl) {
|
||||||
|
try {
|
||||||
|
const img = new Image()
|
||||||
|
img.crossOrigin = "anonymous"
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
img.onload = resolve
|
||||||
|
img.onerror = reject
|
||||||
|
img.src = imgUrl
|
||||||
|
})
|
||||||
|
|
||||||
|
const maxWidth = canvas.width - padding * 2
|
||||||
|
const maxHeight = 100
|
||||||
|
const scale = Math.min(
|
||||||
|
maxWidth / img.width,
|
||||||
|
maxHeight / img.height,
|
||||||
|
0.5
|
||||||
|
)
|
||||||
|
const drawWidth = img.width * scale
|
||||||
|
const drawHeight = img.height * scale
|
||||||
|
|
||||||
|
ctx.drawImage(img, padding, yPosition + 10, drawWidth, drawHeight)
|
||||||
|
yPosition += drawHeight + 30
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("Failed to load image:", imgUrl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
yPosition += 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await drawText()
|
||||||
|
return canvas.toDataURL("image/png")
|
||||||
|
}
|
||||||
|
|
||||||
export const MoreOptions = ({
|
export const MoreOptions = ({
|
||||||
shareModeEnabled = false,
|
shareModeEnabled = false,
|
||||||
historyId,
|
historyId,
|
||||||
@ -118,6 +274,18 @@ export const MoreOptions = ({
|
|||||||
const jsonContent = JSON.stringify(messages, null, 2)
|
const jsonContent = JSON.stringify(messages, null, 2)
|
||||||
downloadFile(jsonContent, "chat.json")
|
downloadFile(jsonContent, "chat.json")
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "download-image",
|
||||||
|
label: t("more.download.image"),
|
||||||
|
icon: <ImageIcon className="w-4 h-4" />,
|
||||||
|
onClick: async () => {
|
||||||
|
const dataUrl = await generateChatImage(messages)
|
||||||
|
const link = document.createElement("a")
|
||||||
|
link.download = "chat.png"
|
||||||
|
link.href = dataUrl
|
||||||
|
link.click()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,10 @@ export const GeneralSettings = () => {
|
|||||||
"webUIResumeLastChat",
|
"webUIResumeLastChat",
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
|
const [defaultChatWithWebsite, setDefaultChatWithWebsite] = useStorage(
|
||||||
|
"defaultChatWithWebsite",
|
||||||
|
false
|
||||||
|
)
|
||||||
|
|
||||||
const [restoreLastChatModel, setRestoreLastChatModel] = useStorage(
|
const [restoreLastChatModel, setRestoreLastChatModel] = useStorage(
|
||||||
"restoreLastChatModel",
|
"restoreLastChatModel",
|
||||||
@ -118,6 +122,17 @@ export const GeneralSettings = () => {
|
|||||||
onChange={(checked) => setCopilotResumeLastChat(checked)}
|
onChange={(checked) => setCopilotResumeLastChat(checked)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex flex-row justify-between">
|
||||||
|
<div className="inline-flex items-center gap-2">
|
||||||
|
<span className="text-gray-700 dark:text-neutral-50">
|
||||||
|
{t("generalSettings.settings.turnOnChatWithWebsite.label")}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<Switch
|
||||||
|
checked={defaultChatWithWebsite}
|
||||||
|
onChange={(checked) => setDefaultChatWithWebsite(checked)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div className="flex flex-row justify-between">
|
<div className="flex flex-row justify-between">
|
||||||
<div className="inline-flex items-center gap-2">
|
<div className="inline-flex items-center gap-2">
|
||||||
<span className="text-gray-700 dark:text-neutral-50">
|
<span className="text-gray-700 dark:text-neutral-50">
|
||||||
|
@ -134,7 +134,8 @@ export const SidepanelForm = ({ dropedFile }: Props) => {
|
|||||||
speechToTextLanguage,
|
speechToTextLanguage,
|
||||||
useOCR,
|
useOCR,
|
||||||
setUseOCR,
|
setUseOCR,
|
||||||
defaultInternetSearchOn
|
defaultInternetSearchOn,
|
||||||
|
defaultChatWithWebsite
|
||||||
} = useMessage()
|
} = useMessage()
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
@ -200,6 +201,10 @@ export const SidepanelForm = ({ dropedFile }: Props) => {
|
|||||||
setWebSearch(true)
|
setWebSearch(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (defaultChatWithWebsite) {
|
||||||
|
setChatMode("rag")
|
||||||
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
textareaRef.current?.removeEventListener("drop", handleDrop)
|
textareaRef.current?.removeEventListener("drop", handleDrop)
|
||||||
textareaRef.current?.removeEventListener("dragover", handleDragOver)
|
textareaRef.current?.removeEventListener("dragover", handleDragOver)
|
||||||
@ -212,6 +217,12 @@ export const SidepanelForm = ({ dropedFile }: Props) => {
|
|||||||
}
|
}
|
||||||
}, [defaultInternetSearchOn])
|
}, [defaultInternetSearchOn])
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (defaultChatWithWebsite) {
|
||||||
|
setChatMode("rag")
|
||||||
|
}
|
||||||
|
}, [defaultChatWithWebsite])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="px-3 pt-3 md:px-6 md:pt-6 bg-white dark:bg-[#262626] border rounded-t-xl border-gray-300 dark:border-gray-600">
|
<div className="px-3 pt-3 md:px-6 md:pt-6 bg-white dark:bg-[#262626] border rounded-t-xl border-gray-300 dark:border-gray-600">
|
||||||
<div
|
<div
|
||||||
|
@ -59,6 +59,11 @@ export const useMessage = () => {
|
|||||||
"defaultInternetSearchOn",
|
"defaultInternetSearchOn",
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const [
|
||||||
|
defaultChatWithWebsite,
|
||||||
|
] = useStorage("defaultChatWithWebsite", false)
|
||||||
|
|
||||||
const [chatWithWebsiteEmbedding] = useStorage(
|
const [chatWithWebsiteEmbedding] = useStorage(
|
||||||
"chatWithWebsiteEmbedding",
|
"chatWithWebsiteEmbedding",
|
||||||
true
|
true
|
||||||
@ -113,6 +118,9 @@ export const useMessage = () => {
|
|||||||
if(defaultInternetSearchOn) {
|
if(defaultInternetSearchOn) {
|
||||||
setWebSearch(true)
|
setWebSearch(true)
|
||||||
}
|
}
|
||||||
|
if(defaultChatWithWebsite) {
|
||||||
|
setChatMode("rag")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const chatWithWebsiteMode = async (
|
const chatWithWebsiteMode = async (
|
||||||
@ -1721,6 +1729,7 @@ export const useMessage = () => {
|
|||||||
setSpeechToTextLanguage,
|
setSpeechToTextLanguage,
|
||||||
useOCR,
|
useOCR,
|
||||||
setUseOCR,
|
setUseOCR,
|
||||||
defaultInternetSearchOn
|
defaultInternetSearchOn,
|
||||||
|
defaultChatWithWebsite
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,5 +53,10 @@ export const OAI_API_PROVIDERS = [
|
|||||||
label: "Mistral",
|
label: "Mistral",
|
||||||
value: "mistral",
|
value: "mistral",
|
||||||
baseUrl: "https://api.mistral.ai/v1"
|
baseUrl: "https://api.mistral.ai/v1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "DeepSeek",
|
||||||
|
value: "deepseek",
|
||||||
|
baseUrl: "https://api.deepseek.com"
|
||||||
}
|
}
|
||||||
]
|
]
|
@ -51,7 +51,7 @@ export default defineConfig({
|
|||||||
outDir: "build",
|
outDir: "build",
|
||||||
|
|
||||||
manifest: {
|
manifest: {
|
||||||
version: "1.4.2",
|
version: "1.4.3",
|
||||||
name:
|
name:
|
||||||
process.env.TARGET === "firefox"
|
process.env.TARGET === "firefox"
|
||||||
? "Page Assist - A Web UI for Local AI Models"
|
? "Page Assist - A Web UI for Local AI Models"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user