fix: properly encode file URLs with special characters (#12424)

This commit is contained in:
Khang Ha (Kelvin)
2026-02-07 05:16:56 +07:00
committed by GitHub
parent e9a3cfc083
commit fde0b39b7c
12 changed files with 114 additions and 22 deletions

View File

@@ -19,6 +19,14 @@ import {
import { Dynamic } from "solid-js/web"
import type { FileNode } from "@opencode-ai/sdk/v2"
function pathToFileUrl(filepath: string): string {
const encodedPath = filepath
.split("/")
.map((segment) => encodeURIComponent(segment))
.join("/")
return `file://${encodedPath}`
}
type Kind = "add" | "del" | "mix"
type Filter = {
@@ -247,7 +255,7 @@ export default function FileTree(props: {
onDragStart={(e: DragEvent) => {
if (!draggable()) return
e.dataTransfer?.setData("text/plain", `file:${local.node.path}`)
e.dataTransfer?.setData("text/uri-list", `file://${local.node.path}`)
e.dataTransfer?.setData("text/uri-list", pathToFileUrl(local.node.path))
if (e.dataTransfer) e.dataTransfer.effectAllowed = "copy"
const dragImage = document.createElement("div")

View File

@@ -30,6 +30,12 @@ type BuildRequestPartsInput = {
const absolute = (directory: string, path: string) =>
path.startsWith("/") ? path : (directory + "/" + path).replace("//", "/")
const encodeFilePath = (filepath: string): string =>
filepath
.split("/")
.map((segment) => encodeURIComponent(segment))
.join("/")
const fileQuery = (selection: FileSelection | undefined) =>
selection ? `?start=${selection.startLine}&end=${selection.endLine}` : ""
@@ -99,7 +105,7 @@ export function buildRequestParts(input: BuildRequestPartsInput) {
id: Identifier.ascending("part"),
type: "file",
mime: "text/plain",
url: `file://${path}${fileQuery(attachment.selection)}`,
url: `file://${encodeFilePath(path)}${fileQuery(attachment.selection)}`,
filename: getFilename(attachment.path),
source: {
type: "file",
@@ -129,7 +135,7 @@ export function buildRequestParts(input: BuildRequestPartsInput) {
const used = new Set(files.map((part) => part.url))
const context = input.context.flatMap((item) => {
const path = absolute(input.sessionDirectory, item.path)
const url = `file://${path}${fileQuery(item.selection)}`
const url = `file://${encodeFilePath(path)}${fileQuery(item.selection)}`
const comment = item.comment?.trim()
if (!comment && used.has(url)) return []
used.add(url)

View File

@@ -72,12 +72,27 @@ export function unquoteGitPath(input: string) {
return new TextDecoder().decode(new Uint8Array(bytes))
}
export function decodeFilePath(input: string) {
try {
return decodeURIComponent(input)
} catch {
return input
}
}
export function encodeFilePath(filepath: string): string {
return filepath
.split("/")
.map((segment) => encodeURIComponent(segment))
.join("/")
}
export function createPathHelpers(scope: () => string) {
const normalize = (input: string) => {
const root = scope()
const prefix = root.endsWith("/") ? root : root + "/"
let path = unquoteGitPath(stripQueryAndHash(stripFileProtocol(input)))
let path = unquoteGitPath(decodeFilePath(stripQueryAndHash(stripFileProtocol(input))))
if (path.startsWith(prefix)) {
path = path.slice(prefix.length)
@@ -100,7 +115,7 @@ export function createPathHelpers(scope: () => string) {
const tab = (input: string) => {
const path = normalize(input)
return `file://${path}`
return `file://${encodeFilePath(path)}`
}
const pathFromTab = (tabValue: string) => {