feat: Add code download functionality
Adds a new "Download Code" button to the code block component, allowing users to download the code displayed for offline use. This feature enhances user convenience and provides a more versatile experience for exploring and utilizing code snippets.
This commit is contained in:
parent
a2f9002b81
commit
9cc309e9fd
@ -101,5 +101,6 @@
|
|||||||
"translate": "Oversæt",
|
"translate": "Oversæt",
|
||||||
"custom": "Brugerdefineret"
|
"custom": "Brugerdefineret"
|
||||||
},
|
},
|
||||||
"citations": "Citater"
|
"citations": "Citater",
|
||||||
|
"downloadCode": "Download Kode"
|
||||||
}
|
}
|
@ -101,5 +101,6 @@
|
|||||||
"translate": "Übersetzen",
|
"translate": "Übersetzen",
|
||||||
"custom": "Benutzerdefiniert"
|
"custom": "Benutzerdefiniert"
|
||||||
},
|
},
|
||||||
"citations": "Zitate"
|
"citations": "Zitate",
|
||||||
|
"downloadCode": "Code herunterladen"
|
||||||
}
|
}
|
@ -105,5 +105,6 @@
|
|||||||
"segmented": {
|
"segmented": {
|
||||||
"ollama": "Ollama Models",
|
"ollama": "Ollama Models",
|
||||||
"custom": "Custom Models"
|
"custom": "Custom Models"
|
||||||
}
|
},
|
||||||
|
"downloadCode": "Download Code"
|
||||||
}
|
}
|
@ -100,5 +100,6 @@
|
|||||||
"rephrase": "Reformular",
|
"rephrase": "Reformular",
|
||||||
"translate": "Traducir"
|
"translate": "Traducir"
|
||||||
},
|
},
|
||||||
"citations": "Citas"
|
"citations": "Citas",
|
||||||
|
"downloadCode": "Descargar Código"
|
||||||
}
|
}
|
@ -94,5 +94,6 @@
|
|||||||
},
|
},
|
||||||
"advanced": "تنظیمات بیشتر مدل"
|
"advanced": "تنظیمات بیشتر مدل"
|
||||||
},
|
},
|
||||||
"citations": "منابع"
|
"citations": "منابع",
|
||||||
|
"downloadCode": "دانلود کد"
|
||||||
}
|
}
|
@ -100,5 +100,6 @@
|
|||||||
"rephrase": "Reformuler",
|
"rephrase": "Reformuler",
|
||||||
"translate": "Traduire"
|
"translate": "Traduire"
|
||||||
},
|
},
|
||||||
"citations": "Citations"
|
"citations": "Citations",
|
||||||
|
"downloadCode": "Télécharger le code"
|
||||||
}
|
}
|
@ -100,5 +100,6 @@
|
|||||||
"rephrase": "Riformulare",
|
"rephrase": "Riformulare",
|
||||||
"translate": "Tradurre"
|
"translate": "Tradurre"
|
||||||
},
|
},
|
||||||
"citations": "Citazioni"
|
"citations": "Citazioni",
|
||||||
|
"downloadCode": "Scarica Codice"
|
||||||
}
|
}
|
@ -100,5 +100,6 @@
|
|||||||
"rephrase": "言い換え",
|
"rephrase": "言い換え",
|
||||||
"translate": "翻訳"
|
"translate": "翻訳"
|
||||||
},
|
},
|
||||||
"citations": "引用"
|
"citations": "引用",
|
||||||
|
"downloadCode": "コードをダウンロード"
|
||||||
}
|
}
|
@ -99,5 +99,6 @@
|
|||||||
"rephrase": "പുനഃരൂപീകരിക്കുക",
|
"rephrase": "പുനഃരൂപീകരിക്കുക",
|
||||||
"translate": "വിവർത്തനം ചെയ്യുക"
|
"translate": "വിവർത്തനം ചെയ്യുക"
|
||||||
},
|
},
|
||||||
"citations": "ഉദ്ധരണികൾ"
|
"citations": "ഉദ്ധരണികൾ",
|
||||||
|
"downloadCode": "കോഡ് ഡൗൺലോഡ് ചെയ്യുക"
|
||||||
}
|
}
|
@ -101,5 +101,6 @@
|
|||||||
"translate": "Oversett",
|
"translate": "Oversett",
|
||||||
"custom": "Egendefinert"
|
"custom": "Egendefinert"
|
||||||
},
|
},
|
||||||
"citations": "Sitater"
|
"citations": "Sitater",
|
||||||
|
"downloadCode": "Last ned kode"
|
||||||
}
|
}
|
@ -100,5 +100,6 @@
|
|||||||
"rephrase": "Reformular",
|
"rephrase": "Reformular",
|
||||||
"translate": "Traduzir"
|
"translate": "Traduzir"
|
||||||
},
|
},
|
||||||
"citations": "Citações"
|
"citations": "Citações",
|
||||||
|
"downloadCode": "Baixar Código"
|
||||||
}
|
}
|
@ -100,5 +100,6 @@
|
|||||||
"rephrase": "Перефразировать",
|
"rephrase": "Перефразировать",
|
||||||
"translate": "Перевести"
|
"translate": "Перевести"
|
||||||
},
|
},
|
||||||
"citations": "Цитаты"
|
"citations": "Цитаты",
|
||||||
|
"downloadCode": "Скачать код"
|
||||||
}
|
}
|
@ -100,5 +100,6 @@
|
|||||||
"rephrase": "重述",
|
"rephrase": "重述",
|
||||||
"translate": "翻译"
|
"translate": "翻译"
|
||||||
},
|
},
|
||||||
"citations": "引用"
|
"citations": "引用",
|
||||||
|
"downloadCode": "下载代码"
|
||||||
}
|
}
|
@ -1,5 +1,6 @@
|
|||||||
|
import { programmingLanguages } from "@/utils/langauge-extension"
|
||||||
import { Tooltip, Modal } from "antd"
|
import { Tooltip, Modal } from "antd"
|
||||||
import { CheckIcon, ClipboardIcon, EyeIcon, Maximize2Icon } from "lucide-react"
|
import { CheckIcon, ClipboardIcon, DownloadIcon } from "lucide-react"
|
||||||
import { FC, useState } from "react"
|
import { FC, useState } from "react"
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"
|
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"
|
||||||
@ -23,30 +24,36 @@ export const CodeBlock: FC<Props> = ({ language, value }) => {
|
|||||||
}, 4000)
|
}, 4000)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handlePreview = () => {
|
|
||||||
setPreviewVisible(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handlePreviewClose = () => {
|
const handlePreviewClose = () => {
|
||||||
setPreviewVisible(false)
|
setPreviewVisible(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleDownload = () => {
|
||||||
|
const blob = new Blob([value], { type: "text/plain" })
|
||||||
|
const url = window.URL.createObjectURL(blob)
|
||||||
|
const a = document.createElement("a")
|
||||||
|
a.href = url
|
||||||
|
a.download = `code_${new Date().toISOString().replace(/[:.]/g, "-")}.${programmingLanguages[language] || language}`
|
||||||
|
document.body.appendChild(a)
|
||||||
|
a.click()
|
||||||
|
document.body.removeChild(a)
|
||||||
|
window.URL.revokeObjectURL(url)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="code relative text-base font-sans codeblock bg-zinc-950 rounded-md overflow-hidden">
|
<div className="code relative text-base font-sans codeblock bg-zinc-950 rounded-md overflow-hidden">
|
||||||
<div className="flex bg-gray-800 items-center justify-between py-1.5 px-4">
|
<div className="flex bg-gray-800 items-center justify-between py-1.5 px-4">
|
||||||
<span className="text-xs lowercase text-gray-200">{language}</span>
|
<span className="text-xs lowercase text-gray-200">{language}</span>
|
||||||
|
|
||||||
<div className="flex items-center">
|
<div className="flex items-center gap-2">
|
||||||
{language.toLowerCase() === "html" && (
|
<Tooltip title={t("downloadCode")}>
|
||||||
<Tooltip title={t("preview")}>
|
|
||||||
<button
|
<button
|
||||||
onClick={handlePreview}
|
onClick={handleDownload}
|
||||||
className="flex gap-1.5 items-center rounded bg-none p-1 text-xs text-gray-200 hover:bg-gray-700 hover:text-gray-100 focus:outline-none">
|
className="flex gap-1.5 items-center rounded bg-none p-1 text-xs text-gray-200 hover:bg-gray-700 hover:text-gray-100 focus:outline-none">
|
||||||
<EyeIcon className="h-4 w-4" />
|
<DownloadIcon className="h-4 w-4" />
|
||||||
</button>
|
</button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
|
||||||
<Tooltip title={t("copyToClipboard")}>
|
<Tooltip title={t("copyToClipboard")}>
|
||||||
<button
|
<button
|
||||||
onClick={handleCopy}
|
onClick={handleCopy}
|
||||||
|
@ -54,7 +54,7 @@ export default function OptionLayout({
|
|||||||
if (confirm) {
|
if (confirm) {
|
||||||
const db = new PageAssitDatabase()
|
const db = new PageAssitDatabase()
|
||||||
await db.deleteAllChatHistory()
|
await db.deleteAllChatHistory()
|
||||||
queryClient.invalidateQueries({
|
await queryClient.invalidateQueries({
|
||||||
queryKey: ["fetchChatHistory"]
|
queryKey: ["fetchChatHistory"]
|
||||||
})
|
})
|
||||||
clearChat()
|
clearChat()
|
||||||
|
@ -61,10 +61,10 @@ export const PlaygroundChat = () => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{!isAtBottom && (
|
{!isAtBottom && (
|
||||||
<div className="fixed md:bottom-40 bottom-36 z-20 left-0 right-0 flex justify-center">
|
<div className="fixed bottom-36 z-20 left-0 right-0 flex justify-center">
|
||||||
<button
|
<button
|
||||||
onClick={scrollToBottom}
|
onClick={scrollToBottom}
|
||||||
className="bg-gray-100 border dark:border-gray-700 dark:bg-gray-800 p-1 rounded-full shadow-md hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors duration-200">
|
className="bg-white border border-gray-100 dark:border-none dark:bg-white/20 p-1.5 rounded-full pointer-events-auto">
|
||||||
<ChevronDown className="size-4 text-gray-600 dark:text-gray-300" />
|
<ChevronDown className="size-4 text-gray-600 dark:text-gray-300" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
systemPromptForNonRagOption
|
systemPromptForNonRagOption
|
||||||
} from "~/services/ollama"
|
} from "~/services/ollama"
|
||||||
import { type ChatHistory, type Message } from "~/store/option"
|
import { type ChatHistory, type Message } from "~/store/option"
|
||||||
import { HumanMessage, SystemMessage } from "@langchain/core/messages"
|
import { SystemMessage } from "@langchain/core/messages"
|
||||||
import { useStoreMessageOption } from "~/store/option"
|
import { useStoreMessageOption } from "~/store/option"
|
||||||
import {
|
import {
|
||||||
deleteChatForEdit,
|
deleteChatForEdit,
|
||||||
|
29
src/utils/langauge-extension.ts
Normal file
29
src/utils/langauge-extension.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
export const programmingLanguages = {
|
||||||
|
html: "html",
|
||||||
|
javascript: "js",
|
||||||
|
typescript: "ts",
|
||||||
|
python: "py",
|
||||||
|
java: "java",
|
||||||
|
cpp: "cpp",
|
||||||
|
c: "c",
|
||||||
|
csharp: "cs",
|
||||||
|
ruby: "rb",
|
||||||
|
php: "php",
|
||||||
|
swift: "swift",
|
||||||
|
go: "go",
|
||||||
|
rust: "rs",
|
||||||
|
kotlin: "kt",
|
||||||
|
sql: "sql",
|
||||||
|
shell: "sh",
|
||||||
|
markdown: "md",
|
||||||
|
json: "json",
|
||||||
|
yaml: "yml",
|
||||||
|
xml: "xml",
|
||||||
|
css: "css",
|
||||||
|
scss: "scss",
|
||||||
|
jsx: "jsx",
|
||||||
|
tsx: "tsx",
|
||||||
|
vue: "vue",
|
||||||
|
dart: "dart",
|
||||||
|
lua: "lua"
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user