tests pass

This commit is contained in:
Eason Goodale
2025-04-19 18:49:29 -07:00
parent 0d6a98f9af
commit 35148c2ba9
14 changed files with 783 additions and 6 deletions

View File

@@ -0,0 +1,5 @@
// Reexport TypeScript implementation so regular `.js` import paths used by
// the existing testsuite continue to resolve correctly at runtime.
export * from "./image-picker-utils.ts";
export { default } from "./image-picker-utils.ts";

View File

@@ -0,0 +1,51 @@
import fs from "node:fs";
import path from "node:path";
/** Determine if a filename looks like an image. */
export function isImage(filename: string): boolean {
return /\.(png|jpe?g|gif|bmp|webp|svg)$/i.test(filename);
}
export interface PickerItem {
label: string;
value: string;
// When value is "__UP__" this represents the synthetic "../" entry.
}
/**
* Return selectable items for the given directory. Directories appear *after*
* images (so that pressing <enter> immediately selects the first image).
* The synthetic "../" entry is always first unless we are already at
* pickerRoot in which case it is omitted.
*/
export function getDirectoryItems(
cwd: string,
pickerRoot: string,
): Array<PickerItem> {
const files: Array<PickerItem> = [];
const dirs: Array<PickerItem> = [];
try {
for (const entry of fs.readdirSync(cwd, { withFileTypes: true })) {
if (entry.isDirectory()) {
dirs.push({ label: entry.name + "/", value: path.join(cwd, entry.name) });
} else if (entry.isFile() && isImage(entry.name)) {
files.push({ label: entry.name, value: path.join(cwd, entry.name) });
}
}
} catch {
// ignore errors return empty list so UI shows "No images".
}
files.sort((a, b) => a.label.localeCompare(b.label));
dirs.sort((a, b) => a.label.localeCompare(b.label));
const items: Array<PickerItem> = [];
if (path.resolve(cwd) !== path.resolve(pickerRoot)) {
items.push({ label: "../", value: "__UP__" });
}
items.push(...files, ...dirs);
return items;
}

View File

@@ -2,6 +2,11 @@ import type { ResponseInputItem } from "openai/resources/responses/responses";
import { fileTypeFromBuffer } from "file-type";
import fs from "fs/promises";
import path from "node:path";
// Map dataurls → original filenames so TUI can render friendly labels.
// Populated during createInputItem.
export const imageFilenameByDataUrl = new Map<string, string>();
export async function createInputItem(
text: string,
@@ -20,10 +25,18 @@ export async function createInputItem(
/* eslint-enable no-await-in-loop */
const encoded = binary.toString("base64");
const mime = kind?.mime ?? "application/octet-stream";
const dataUrl = `data:${mime};base64,${encoded}`;
// Store pretty label (relative path when possible)
const label = path.isAbsolute(filePath)
? path.relative(process.cwd(), filePath)
: filePath;
imageFilenameByDataUrl.set(dataUrl, label);
inputItem.content.push({
type: "input_image",
detail: "auto",
image_url: `data:${mime};base64,${encoded}`,
image_url: dataUrl,
});
}