8d2765313c
Chrome gives a PASTE of a file manager's copy (Ctrl+C in Nautilus, Ctrl+V in the chat) the files and their paths as plain text, but no text/uri-list — only a drag carries that — so localCandidates found no path and every such file was uploaded, and refused past the upload limit. Plain-text paths (or file:// lines) whose names match the pasted files one for one are now candidates too; sameFilesHere still has the server confirm each is the same file (size) on this machine, so server mode and remote browsers upload as before. The prompt box also attaches a file:// text paste with no file by path, as the terminal already did (copiedFilePaths moved to promptAttach).
64 lines
2.6 KiB
TypeScript
64 lines
2.6 KiB
TypeScript
/**
|
|
* @file promptAttach.test.ts — `claudePastes`: how files reach a `claude` pane.
|
|
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
|
*/
|
|
import { describe, expect, it } from "vitest";
|
|
import { claudePastes, copiedFilePaths, localCandidates } from "../promptAttach";
|
|
|
|
describe("claudePastes", () => {
|
|
it("pastes each image as its bare path and the other files as one @ mention paste", () => {
|
|
expect(
|
|
claudePastes(["/d/drops/a.png", "/d/drops/b-report.pdf", "/d/x.JPG", "/d/c.log"])
|
|
).toEqual(["/d/drops/a.png", "/d/x.JPG", "@/d/drops/b-report.pdf @/d/c.log"]);
|
|
});
|
|
|
|
it("keeps a Windows or spaced image path a mention", () => {
|
|
expect(claudePastes(["C:\\u\\drops\\a.png", "/home/u/my shot.png"])).toEqual([
|
|
'@C:/u/drops/a.png @"/home/u/my shot.png"',
|
|
]);
|
|
});
|
|
});
|
|
|
|
/** A clipboard as the browser hands it to a paste handler. */
|
|
const clip = (data: Record<string, string>) =>
|
|
({ getData: (t: string) => data[t] ?? "" }) as unknown as DataTransfer;
|
|
const file = (name: string) => new File(["x"], name);
|
|
|
|
describe("localCandidates (a file manager's copy, pasted)", () => {
|
|
it("takes the plain-text paths when their names match the pasted files", () => {
|
|
expect(window.location.hostname).toBe("localhost");
|
|
const dt = clip({ "text/plain": "/home/u/Videos/big.mp4\n/home/u/b c.pdf\n" });
|
|
expect(localCandidates(dt, [file("b c.pdf"), file("big.mp4")])).toEqual([
|
|
"/home/u/Videos/big.mp4",
|
|
"/home/u/b c.pdf",
|
|
]);
|
|
});
|
|
|
|
it("takes file:// lines too, decoded", () => {
|
|
const dt = clip({ "text/plain": "file:///home/u/a%20b.zip" });
|
|
expect(localCandidates(dt, [file("a b.zip")])).toEqual(["/home/u/a b.zip"]);
|
|
});
|
|
|
|
it("refuses text that does not name exactly the pasted files", () => {
|
|
expect(localCandidates(clip({ "text/plain": "/etc/passwd" }), [file("big.mp4")])).toBeNull();
|
|
expect(localCandidates(clip({ "text/plain": "hello" }), [file("hello")])).toBeNull();
|
|
expect(
|
|
localCandidates(clip({ "text/plain": "/a/x.txt" }), [file("x.txt"), file("y.txt")])
|
|
).toBeNull();
|
|
});
|
|
|
|
it("prefers a uri-list (a drag) as before", () => {
|
|
const dt = clip({ "text/uri-list": "file:///tmp/z.bin", "text/plain": "/other/z.bin" });
|
|
expect(localCandidates(dt, [file("z.bin")])).toEqual(["/tmp/z.bin"]);
|
|
});
|
|
});
|
|
|
|
describe("copiedFilePaths (no file beside the text)", () => {
|
|
it("takes file:// text, never bare absolute paths, which stay text to paste", () => {
|
|
expect(copiedFilePaths(clip({ "text/plain": "file:///home/u/a.txt" }))).toEqual([
|
|
"/home/u/a.txt",
|
|
]);
|
|
expect(copiedFilePaths(clip({ "text/plain": "/home/u/a.txt" }))).toBeNull();
|
|
});
|
|
});
|