import { escapeHTML } from "bun" import { readdirSync } from "fs" import { join, extname } from "path" import type { CommandOutput } from "@/shared/types" import { NOSE_WWW } from "@/config" import { getState } from "@/state" import { appPath } from "@/webapp" import { highlight } from "../lib/highlight" export default async function (path: string) { const state = getState() if (!state) return { error: "no state" } const project = state.project if (!project) return { error: "no project loaded" } const root = appPath(project) if (!root) return { error: "error loading project" } let files: string[] = [] for (const file of readdirSync(root, { withFileTypes: true })) { files.push(file.name) } if (root === NOSE_WWW) { files = files.filter(file => file.endsWith(`${project}.ts`) || file.endsWith(`${project}.tsx`)) } if (!files.includes(path)) return { error: `file not found: ${path}` } return await readFile(join(root, path)) } async function readFile(path: string): Promise { const ext = extname(path).slice(1) const file = Bun.file(path) switch (ext) { case "jpeg": case "jpg": case "png": case "gif": case "webp": const img = await file.arrayBuffer() return { html: `` } case "mp3": case "wav": case "mp4": case "mov": case "avi": case "mkv": case "webm": return "Not implemented" case "js": case "ts": case "tsx": case "json": return { html: `
${highlight(convertIndent(await file.text()))}
` } default: if (await isBinaryContent(file)) throw "Cannot display binary file" return { html: `
${escapeHTML(await file.text())}
` } } } // cut down 4space and 2space indents into 1space, for readability on a small screen function convertIndent(str: string) { const lines = str.split("\n") // find the smallest non-zero indent const minIndent = Math.min( ...lines .map(l => (l.match(/^ +/) || [""])[0].length) .filter(len => len > 0) ) return lines .map(line => line.replace(/^( +)/, (_, spaces) => " ".repeat(spaces.length / minIndent)) ) .join("\n") } async function isBinaryContent(file: Bun.FileBlob): Promise { // Create a stream to read just the beginning const stream = file.stream() const reader = stream.getReader() try { // Read first chunk (typically 16KB, which is more than enough to detect binary) const { value } = await reader.read() if (!value) return false // Check first 512 bytes or less const bytes = new Uint8Array(value) const checkLength = Math.min(bytes.length, 512) for (let i = 0; i < checkLength; i++) { const byte = bytes[i]! if (byte === 0) return true // null byte if (byte < 32 && ![9, 10, 13].includes(byte)) return true // control char } return false } finally { reader.releaseLock() stream.cancel() } }