fix(app): query selector with non-latin chars
This commit is contained in:
@@ -57,6 +57,62 @@ function stripQueryAndHash(input: string) {
|
|||||||
return input
|
return input
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function unquoteGitPath(input: string) {
|
||||||
|
if (!input.startsWith('"')) return input
|
||||||
|
if (!input.endsWith('"')) return input
|
||||||
|
const body = input.slice(1, -1)
|
||||||
|
const bytes: number[] = []
|
||||||
|
|
||||||
|
for (let i = 0; i < body.length; i++) {
|
||||||
|
const char = body[i]!
|
||||||
|
if (char !== "\\") {
|
||||||
|
bytes.push(char.charCodeAt(0))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const next = body[i + 1]
|
||||||
|
if (!next) {
|
||||||
|
bytes.push("\\".charCodeAt(0))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (next >= "0" && next <= "7") {
|
||||||
|
const chunk = body.slice(i + 1, i + 4)
|
||||||
|
const match = chunk.match(/^[0-7]{1,3}/)
|
||||||
|
if (!match) {
|
||||||
|
bytes.push(next.charCodeAt(0))
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
bytes.push(parseInt(match[0], 8))
|
||||||
|
i += match[0].length
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const escaped =
|
||||||
|
next === "n"
|
||||||
|
? "\n"
|
||||||
|
: next === "r"
|
||||||
|
? "\r"
|
||||||
|
: next === "t"
|
||||||
|
? "\t"
|
||||||
|
: next === "b"
|
||||||
|
? "\b"
|
||||||
|
: next === "f"
|
||||||
|
? "\f"
|
||||||
|
: next === "v"
|
||||||
|
? "\v"
|
||||||
|
: next === "\\" || next === '"'
|
||||||
|
? next
|
||||||
|
: undefined
|
||||||
|
|
||||||
|
bytes.push((escaped ?? next).charCodeAt(0))
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TextDecoder().decode(new Uint8Array(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
export function selectionFromLines(range: SelectedLineRange): FileSelection {
|
export function selectionFromLines(range: SelectedLineRange): FileSelection {
|
||||||
const startLine = Math.min(range.start, range.end)
|
const startLine = Math.min(range.start, range.end)
|
||||||
const endLine = Math.max(range.start, range.end)
|
const endLine = Math.max(range.start, range.end)
|
||||||
@@ -197,7 +253,7 @@ export const { use: useFile, provider: FileProvider } = createSimpleContext({
|
|||||||
const root = directory()
|
const root = directory()
|
||||||
const prefix = root.endsWith("/") ? root : root + "/"
|
const prefix = root.endsWith("/") ? root : root + "/"
|
||||||
|
|
||||||
let path = stripQueryAndHash(stripFileProtocol(input))
|
let path = unquoteGitPath(stripQueryAndHash(stripFileProtocol(input)))
|
||||||
|
|
||||||
if (path.startsWith(prefix)) {
|
if (path.startsWith(prefix)) {
|
||||||
path = path.slice(prefix.length)
|
path = path.slice(prefix.length)
|
||||||
|
|||||||
@@ -1016,7 +1016,7 @@ export default function Page() {
|
|||||||
|
|
||||||
const activeTab = createMemo(() => {
|
const activeTab = createMemo(() => {
|
||||||
const active = tabs().active()
|
const active = tabs().active()
|
||||||
if (active) return active
|
if (active) return normalizeTab(active)
|
||||||
if (hasReview()) return "review"
|
if (hasReview()) return "review"
|
||||||
|
|
||||||
const first = openedTabs()[0]
|
const first = openedTabs()[0]
|
||||||
|
|||||||
@@ -206,7 +206,11 @@ export namespace File {
|
|||||||
const project = Instance.project
|
const project = Instance.project
|
||||||
if (project.vcs !== "git") return []
|
if (project.vcs !== "git") return []
|
||||||
|
|
||||||
const diffOutput = await $`git diff --numstat HEAD`.cwd(Instance.directory).quiet().nothrow().text()
|
const diffOutput = await $`git -c core.quotepath=false diff --numstat HEAD`
|
||||||
|
.cwd(Instance.directory)
|
||||||
|
.quiet()
|
||||||
|
.nothrow()
|
||||||
|
.text()
|
||||||
|
|
||||||
const changedFiles: Info[] = []
|
const changedFiles: Info[] = []
|
||||||
|
|
||||||
@@ -223,7 +227,7 @@ export namespace File {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const untrackedOutput = await $`git ls-files --others --exclude-standard`
|
const untrackedOutput = await $`git -c core.quotepath=false ls-files --others --exclude-standard`
|
||||||
.cwd(Instance.directory)
|
.cwd(Instance.directory)
|
||||||
.quiet()
|
.quiet()
|
||||||
.nothrow()
|
.nothrow()
|
||||||
@@ -248,7 +252,7 @@ export namespace File {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get deleted files
|
// Get deleted files
|
||||||
const deletedOutput = await $`git diff --name-only --diff-filter=D HEAD`
|
const deletedOutput = await $`git -c core.quotepath=false diff --name-only --diff-filter=D HEAD`
|
||||||
.cwd(Instance.directory)
|
.cwd(Instance.directory)
|
||||||
.quiet()
|
.quiet()
|
||||||
.nothrow()
|
.nothrow()
|
||||||
|
|||||||
@@ -20,6 +20,62 @@ import { Agent } from "@/agent/agent"
|
|||||||
export namespace SessionSummary {
|
export namespace SessionSummary {
|
||||||
const log = Log.create({ service: "session.summary" })
|
const log = Log.create({ service: "session.summary" })
|
||||||
|
|
||||||
|
function unquoteGitPath(input: string) {
|
||||||
|
if (!input.startsWith('"')) return input
|
||||||
|
if (!input.endsWith('"')) return input
|
||||||
|
const body = input.slice(1, -1)
|
||||||
|
const bytes: number[] = []
|
||||||
|
|
||||||
|
for (let i = 0; i < body.length; i++) {
|
||||||
|
const char = body[i]!
|
||||||
|
if (char !== "\\") {
|
||||||
|
bytes.push(char.charCodeAt(0))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const next = body[i + 1]
|
||||||
|
if (!next) {
|
||||||
|
bytes.push("\\".charCodeAt(0))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (next >= "0" && next <= "7") {
|
||||||
|
const chunk = body.slice(i + 1, i + 4)
|
||||||
|
const match = chunk.match(/^[0-7]{1,3}/)
|
||||||
|
if (!match) {
|
||||||
|
bytes.push(next.charCodeAt(0))
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
bytes.push(parseInt(match[0], 8))
|
||||||
|
i += match[0].length
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const escaped =
|
||||||
|
next === "n"
|
||||||
|
? "\n"
|
||||||
|
: next === "r"
|
||||||
|
? "\r"
|
||||||
|
: next === "t"
|
||||||
|
? "\t"
|
||||||
|
: next === "b"
|
||||||
|
? "\b"
|
||||||
|
: next === "f"
|
||||||
|
? "\f"
|
||||||
|
: next === "v"
|
||||||
|
? "\v"
|
||||||
|
: next === "\\" || next === '"'
|
||||||
|
? next
|
||||||
|
: undefined
|
||||||
|
|
||||||
|
bytes.push((escaped ?? next).charCodeAt(0))
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
return Buffer.from(bytes).toString()
|
||||||
|
}
|
||||||
|
|
||||||
export const summarize = fn(
|
export const summarize = fn(
|
||||||
z.object({
|
z.object({
|
||||||
sessionID: z.string(),
|
sessionID: z.string(),
|
||||||
@@ -116,7 +172,18 @@ export namespace SessionSummary {
|
|||||||
messageID: Identifier.schema("message").optional(),
|
messageID: Identifier.schema("message").optional(),
|
||||||
}),
|
}),
|
||||||
async (input) => {
|
async (input) => {
|
||||||
return Storage.read<Snapshot.FileDiff[]>(["session_diff", input.sessionID]).catch(() => [])
|
const diffs = await Storage.read<Snapshot.FileDiff[]>(["session_diff", input.sessionID]).catch(() => [])
|
||||||
|
const next = diffs.map((item) => {
|
||||||
|
const file = unquoteGitPath(item.file)
|
||||||
|
if (file === item.file) return item
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
file,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const changed = next.some((item, i) => item.file !== diffs[i]?.file)
|
||||||
|
if (changed) Storage.write(["session_diff", input.sessionID], next).catch(() => {})
|
||||||
|
return next
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ export namespace Snapshot {
|
|||||||
const git = gitdir()
|
const git = gitdir()
|
||||||
await $`git --git-dir ${git} --work-tree ${Instance.worktree} add .`.quiet().cwd(Instance.directory).nothrow()
|
await $`git --git-dir ${git} --work-tree ${Instance.worktree} add .`.quiet().cwd(Instance.directory).nothrow()
|
||||||
const result =
|
const result =
|
||||||
await $`git -c core.autocrlf=false --git-dir ${git} --work-tree ${Instance.worktree} diff --no-ext-diff ${hash} -- .`
|
await $`git -c core.autocrlf=false -c core.quotepath=false --git-dir ${git} --work-tree ${Instance.worktree} diff --no-ext-diff ${hash} -- .`
|
||||||
.quiet()
|
.quiet()
|
||||||
.cwd(Instance.worktree)
|
.cwd(Instance.worktree)
|
||||||
.nothrow()
|
.nothrow()
|
||||||
@@ -196,7 +196,7 @@ export namespace Snapshot {
|
|||||||
export async function diffFull(from: string, to: string): Promise<FileDiff[]> {
|
export async function diffFull(from: string, to: string): Promise<FileDiff[]> {
|
||||||
const git = gitdir()
|
const git = gitdir()
|
||||||
const result: FileDiff[] = []
|
const result: FileDiff[] = []
|
||||||
for await (const line of $`git -c core.autocrlf=false --git-dir ${git} --work-tree ${Instance.worktree} diff --no-ext-diff --no-renames --numstat ${from} ${to} -- .`
|
for await (const line of $`git -c core.autocrlf=false -c core.quotepath=false --git-dir ${git} --work-tree ${Instance.worktree} diff --no-ext-diff --no-renames --numstat ${from} ${to} -- .`
|
||||||
.quiet()
|
.quiet()
|
||||||
.cwd(Instance.directory)
|
.cwd(Instance.directory)
|
||||||
.nothrow()
|
.nothrow()
|
||||||
|
|||||||
Reference in New Issue
Block a user