tweak: adjust file api to encode images (#3292)

This commit is contained in:
kcrommett
2025-10-20 22:52:39 -07:00
committed by GitHub
parent 96efede846
commit a99bd3aa2c

View File

@@ -1,6 +1,7 @@
import z from "zod/v4" import z from "zod/v4"
import { Bus } from "../bus" import { Bus } from "../bus"
import { $ } from "bun" import { $ } from "bun"
import type { BunFile } from "bun"
import { formatPatch, structuredPatch } from "diff" import { formatPatch, structuredPatch } from "diff"
import path from "path" import path from "path"
import fs from "fs" import fs from "fs"
@@ -41,6 +42,7 @@ export namespace File {
export const Content = z export const Content = z
.object({ .object({
type: z.literal("text"),
content: z.string(), content: z.string(),
diff: z.string().optional(), diff: z.string().optional(),
patch: z patch: z
@@ -61,12 +63,53 @@ export namespace File {
index: z.string().optional(), index: z.string().optional(),
}) })
.optional(), .optional(),
encoding: z.literal("base64").optional(),
mimeType: z.string().optional(),
}) })
.meta({ .meta({
ref: "FileContent", ref: "FileContent",
}) })
export type Content = z.infer<typeof Content> export type Content = z.infer<typeof Content>
async function shouldEncode(file: BunFile): Promise<boolean> {
const type = file.type?.toLowerCase()
if (!type) return false
if (type.startsWith("text/")) return false
if (type.includes("charset=")) return false
const parts = type.split("/", 2)
const top = parts[0]
const rest = parts[1] ?? ""
const sub = rest.split(";", 1)[0]
const tops = ["image", "audio", "video", "font", "model", "multipart"]
if (tops.includes(top)) return true
if (type === "application/octet-stream") return true
const bins = [
"zip",
"gzip",
"bzip",
"compressed",
"binary",
"stream",
"pdf",
"msword",
"powerpoint",
"excel",
"ogg",
"exe",
"dmg",
"iso",
"rar",
]
if (bins.some((mark) => sub.includes(mark))) return true
return false
}
export const Event = { export const Event = {
Edited: Bus.event( Edited: Bus.event(
"file.edited", "file.edited",
@@ -188,14 +231,30 @@ export namespace File {
})) }))
} }
export async function read(file: string) { export async function read(file: string): Promise<Content> {
using _ = log.time("read", { file }) using _ = log.time("read", { file })
const project = Instance.project const project = Instance.project
const full = path.join(Instance.directory, file) const full = path.join(Instance.directory, file)
const content = await Bun.file(full) const bunFile = Bun.file(full)
if (!(await bunFile.exists())) {
return { type: "text", content: "" }
}
const encode = await shouldEncode(bunFile)
if (encode) {
const buffer = await bunFile.arrayBuffer().catch(() => new ArrayBuffer(0))
const content = Buffer.from(buffer).toString("base64")
const mimeType = bunFile.type || "application/octet-stream"
return { type: "text", content, mimeType, encoding: "base64" }
}
const content = await bunFile
.text() .text()
.catch(() => "") .catch(() => "")
.then((x) => x.trim()) .then((x) => x.trim())
if (project.vcs === "git") { if (project.vcs === "git") {
let diff = await $`git diff ${file}`.cwd(Instance.directory).quiet().nothrow().text() let diff = await $`git diff ${file}`.cwd(Instance.directory).quiet().nothrow().text()
if (!diff.trim()) diff = await $`git diff --staged ${file}`.cwd(Instance.directory).quiet().nothrow().text() if (!diff.trim()) diff = await $`git diff --staged ${file}`.cwd(Instance.directory).quiet().nothrow().text()
@@ -206,10 +265,10 @@ export namespace File {
ignoreWhitespace: true, ignoreWhitespace: true,
}) })
const diff = formatPatch(patch) const diff = formatPatch(patch)
return { content, patch, diff } return { type: "text", content, patch, diff }
} }
} }
return { content } return { type: "text", content }
} }
export async function list(dir?: string) { export async function list(dir?: string) {