diff --git a/web/src/lib/locales/en.ts b/web/src/lib/locales/en.ts index f33b394f..3630d350 100644 --- a/web/src/lib/locales/en.ts +++ b/web/src/lib/locales/en.ts @@ -310,6 +310,7 @@ export default { 'file.page.unknownPath': 'Unknown path', 'file.page.copyPath': 'Copy path', 'file.page.copyContent': 'Copy file content', + 'file.page.download': 'Download file', 'file.page.tab.diff': 'Diff', 'file.page.tab.file': 'File', 'file.page.missingPath': 'No file path provided.', diff --git a/web/src/lib/locales/zh-CN.ts b/web/src/lib/locales/zh-CN.ts index b5e6c07b..ef48bc81 100644 --- a/web/src/lib/locales/zh-CN.ts +++ b/web/src/lib/locales/zh-CN.ts @@ -314,6 +314,7 @@ export default { 'file.page.unknownPath': '未知路径', 'file.page.copyPath': '复制路径', 'file.page.copyContent': '复制文件内容', + 'file.page.download': '下载文件', 'file.page.tab.diff': 'Diff', 'file.page.tab.file': '文件', 'file.page.missingPath': '未提供文件路径。', diff --git a/web/src/routes/sessions/file.tsx b/web/src/routes/sessions/file.tsx index a2327404..ab2b89d9 100644 --- a/web/src/routes/sessions/file.tsx +++ b/web/src/routes/sessions/file.tsx @@ -36,6 +36,44 @@ function decodePath(value: string): string { return decoded.ok ? decoded.text : value } +function DownloadIcon(props: { className?: string }) { + return ( + + + + + + ) +} + +function triggerDownload(fileName: string, base64Content: string, mimeType: string | null) { + const byteChars = atob(base64Content) + const byteArray = new Uint8Array(byteChars.length) + for (let i = 0; i < byteChars.length; i++) { + byteArray[i] = byteChars.charCodeAt(i) + } + const blob = new Blob([byteArray], { type: mimeType ?? 'application/octet-stream' }) + const url = URL.createObjectURL(blob) + const a = document.createElement('a') + a.href = url + a.download = fileName + document.body.appendChild(a) + a.click() + document.body.removeChild(a) + URL.revokeObjectURL(url) +} + function BackIcon(props: { className?: string }) { return ( 0 && contentSizeBytes <= MAX_COPYABLE_FILE_BYTES + const canDownload = fileContentResult?.success === true && Boolean(fileContentResult.content) + const [displayMode, setDisplayMode] = useState<'diff' | 'file'>('diff') useEffect(() => { @@ -260,6 +300,16 @@ export default function FilePage() { > {pathCopied ? : } + {canDownload ? ( + + ) : null}