mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-19 20:15:02 +00:00
fix(desktop): image rendering for Windows paths and MEDIA: prefix
- toFileUrl: normalize backslashes, detect Windows drive-letter paths - resolveFileUrl: strip MEDIA: prefix from tool results, Windows path support - handleServe: skip prepending "/" for Windows drive-letter URL paths
This commit is contained in:
1 parent
0292440c35
commit
2c94e8070f
3 files changed
+26
-10
No files matched your search
@@ -107,7 +107,13 @@ func (h *FilesHandler) handleServe(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// URL path is the absolute path with leading "/" stripped (e.g. "app/.goclaw/workspace/file.png")
|
||||
absPath := filepath.Clean("/" + urlPath)
|
||||
// Windows drive letter: "C:/Users/..." → use directly without prepending "/"
|
||||
var absPath string
|
||||
if len(urlPath) >= 2 && urlPath[1] == ':' {
|
||||
absPath = filepath.Clean(urlPath)
|
||||
} else {
|
||||
absPath = filepath.Clean("/" + urlPath)
|
||||
}
|
||||
|
||||
// Block access to sensitive system directories
|
||||
for _, prefix := range deniedFilePrefixes {
|
||||
|
||||
@@ -28,13 +28,21 @@ function resolveFileUrl(path: string, baseUrl?: string): string {
|
||||
if (isApiClientReady()) return getApiClient().getBaseUrl() + path
|
||||
return path
|
||||
}
|
||||
// Strip MEDIA: prefix (tool results embed paths as MEDIA:/path/to/file.png)
|
||||
const clean = path.replace(/^MEDIA:/, '')
|
||||
// Normalize backslashes (Windows paths: C:\Users\... → C:/Users/...)
|
||||
const normalized = clean.replace(/\\/g, '/')
|
||||
// Relative path with baseUrl — resolve against the base directory
|
||||
if (baseUrl && !path.startsWith('/')) {
|
||||
if (baseUrl && !normalized.startsWith('/') && !/^[a-zA-Z]:\//.test(normalized)) {
|
||||
const baseDir = baseUrl.substring(0, baseUrl.lastIndexOf('/') + 1)
|
||||
return baseDir + path
|
||||
return baseDir + normalized
|
||||
}
|
||||
// Absolute path — preserve full path for direct file serving
|
||||
const filePath = path.startsWith('/') ? `/v1/files${path}` : `/v1/files/${path.split('/').pop() ?? path}`
|
||||
// Absolute path: Unix /... or Windows C:/...
|
||||
const filePath = normalized.startsWith('/')
|
||||
? `/v1/files${normalized}`
|
||||
: /^[a-zA-Z]:\//.test(normalized)
|
||||
? `/v1/files/${normalized}`
|
||||
: `/v1/files/${normalized.split('/').pop() ?? normalized}`
|
||||
if (isApiClientReady()) return getApiClient().getBaseUrl() + filePath
|
||||
return filePath
|
||||
}
|
||||
|
||||
@@ -10,11 +10,13 @@ import type { AttachedFile } from '../components/chat/InputBar'
|
||||
// can resolve the file directly without findInWorkspace fallback.
|
||||
function toFileUrl(path: string): string {
|
||||
if (!path) return ''
|
||||
if (path.startsWith('/v1/files/') || path.includes('/v1/files/')) return resolveBase(path)
|
||||
// Absolute path — preserve full path for direct file serving
|
||||
if (path.startsWith('/')) return resolveBase(`/v1/files${path}`)
|
||||
const basename = path.split('/').pop() ?? path
|
||||
return resolveBase(`/v1/files/${basename}`)
|
||||
if (path.includes('/v1/files/')) return resolveBase(path)
|
||||
// Normalize backslashes (Windows paths: C:\Users\... → C:/Users/...)
|
||||
const normalized = path.replace(/\\/g, '/')
|
||||
// Absolute: Unix /... or Windows C:/...
|
||||
if (normalized.startsWith('/')) return resolveBase(`/v1/files${normalized}`)
|
||||
if (/^[a-zA-Z]:\//.test(normalized)) return resolveBase(`/v1/files/${normalized}`)
|
||||
return resolveBase(`/v1/files/${normalized.split('/').pop() ?? normalized}`)
|
||||
}
|
||||
|
||||
function resolveBase(path: string): string {
|
||||
|
||||
Reference in new issue
Block a user