fix: prevent double-wrapping of download URLs in MarkdownRenderer (#529)
Co-authored-by: Hango Liang <Hango> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,19 @@ import { getApiKey, getBaseUrlValue } from '../client'
|
|||||||
*/
|
*/
|
||||||
export function getDownloadUrl(filePath: string, fileName?: string): string {
|
export function getDownloadUrl(filePath: string, fileName?: string): string {
|
||||||
const base = getBaseUrlValue()
|
const base = getBaseUrlValue()
|
||||||
|
|
||||||
|
// Guard: if filePath is already a full download URL, extract the real path
|
||||||
|
// to prevent double-wrapping (/api/hermes/download?path=/api/hermes/download?path=...)
|
||||||
|
if (filePath.startsWith('/api/hermes/download?')) {
|
||||||
|
try {
|
||||||
|
const parsed = new URL(filePath, 'http://localhost')
|
||||||
|
const realPath = parsed.searchParams.get('path')
|
||||||
|
if (realPath) filePath = realPath
|
||||||
|
} catch {
|
||||||
|
// fall through with original filePath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Decode the path first in case it's already encoded (e.g., from AI responses)
|
// Decode the path first in case it's already encoded (e.g., from AI responses)
|
||||||
// URLSearchParams will encode it again, so we need to start with decoded text
|
// URLSearchParams will encode it again, so we need to start with decoded text
|
||||||
const decodedPath = decodeURIComponent(filePath)
|
const decodedPath = decodeURIComponent(filePath)
|
||||||
|
|||||||
@@ -305,6 +305,22 @@ async function handleMarkdownClick(event: MouseEvent): Promise<void> {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Full download URL: open directly (already has /api/hermes/download?path=...)
|
||||||
|
if (href.startsWith('/api/hermes/download?')) {
|
||||||
|
event.preventDefault()
|
||||||
|
event.stopPropagation()
|
||||||
|
const linkText = link.textContent || ''
|
||||||
|
const fileName = linkText.startsWith('File: ') ? linkText.slice(6).trim() : linkText.trim()
|
||||||
|
message.info(t('download.downloading'))
|
||||||
|
// Parse the real file path from the existing query param
|
||||||
|
const url = new URL(href, window.location.origin)
|
||||||
|
const realPath = url.searchParams.get('path') || href
|
||||||
|
downloadFile(realPath, fileName || undefined).catch((err: Error) => {
|
||||||
|
message.error(err.message || t('download.downloadFailed'))
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// File path links: intercept and download
|
// File path links: intercept and download
|
||||||
if (href.startsWith('/')) {
|
if (href.startsWith('/')) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
|||||||
Reference in New Issue
Block a user