This commit is contained in:
@ -111,8 +111,9 @@ describe("CustomAgentChatView", () => {
|
||||
expect.any(Function),
|
||||
),
|
||||
);
|
||||
expect(screen.queryByRole("button", { name: "Cancel" })).toBeNull();
|
||||
|
||||
fireEvent.click(await screen.findByRole("button", { name: "Cancel" }));
|
||||
fireEvent.click(await screen.findByRole("button", { name: "Stop" }));
|
||||
|
||||
await waitFor(() =>
|
||||
expect(agent.cancelAgentChat).toHaveBeenCalledWith("chat-session-1"),
|
||||
@ -1080,7 +1081,7 @@ describe("CustomAgentChatView", () => {
|
||||
expect(screen.getByText("Fichier: paste-image.png")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("accepts pasted images exposed only through clipboardData.files", async () => {
|
||||
it("accepts pasted generic files exposed only through clipboardData.files", async () => {
|
||||
const agent = {
|
||||
launchAgentChat: vi.fn(),
|
||||
reattachAgentChat: vi.fn(async (sessionId: string) => ({
|
||||
@ -1121,7 +1122,7 @@ describe("CustomAgentChatView", () => {
|
||||
),
|
||||
);
|
||||
|
||||
const file = new File(["ignored"], "files-only.png", { type: "image/png" });
|
||||
const file = new File(["ignored"], "files-only.pdf", { type: "application/pdf" });
|
||||
Object.defineProperty(file, "arrayBuffer", {
|
||||
value: vi.fn(async () => new Uint8Array([4, 5, 6]).buffer),
|
||||
});
|
||||
@ -1133,7 +1134,8 @@ describe("CustomAgentChatView", () => {
|
||||
},
|
||||
});
|
||||
|
||||
await screen.findByText("Fichier joint: files-only.png");
|
||||
await screen.findByText("Fichier joint: files-only.pdf");
|
||||
expect(screen.queryByTestId("attachment-preview-files-only.pdf")).toBeNull();
|
||||
fireEvent.click(screen.getByRole("button", { name: "Envoyer" }));
|
||||
|
||||
await waitFor(() => expect(agent.sendAgentChat).toHaveBeenCalledTimes(1));
|
||||
@ -1144,9 +1146,9 @@ describe("CustomAgentChatView", () => {
|
||||
{
|
||||
attachments: [
|
||||
{
|
||||
filename: "files-only.png",
|
||||
filename: "files-only.pdf",
|
||||
contentBase64: "BAUG",
|
||||
mime: "image/png",
|
||||
mime: "application/pdf",
|
||||
sourceKind: "clipboard",
|
||||
},
|
||||
],
|
||||
@ -1194,14 +1196,12 @@ describe("CustomAgentChatView", () => {
|
||||
});
|
||||
const scroll = screen.getByTestId("custom-agent-chat-scroll");
|
||||
const composer = screen.getByTestId("custom-agent-chat-composer");
|
||||
const cancel = await screen.findByRole("button", { name: "Cancel" });
|
||||
|
||||
expect(shell.className).toContain("h-full");
|
||||
expect(shell.className).toContain("min-h-0");
|
||||
expect(shell.className).toContain("overflow-hidden");
|
||||
expect(toolbar.className).toContain("overflow-hidden");
|
||||
expect(cancel.className).toContain("shrink-0");
|
||||
expect(cancel.className).toContain("z-30");
|
||||
expect(screen.queryByRole("button", { name: "Cancel" })).toBeNull();
|
||||
expect(scroll.className).toContain("flex-1");
|
||||
expect(scroll.className).toContain("basis-0");
|
||||
expect(scroll.className).toContain("overflow-y-auto");
|
||||
@ -1520,7 +1520,7 @@ describe("CustomAgentChatView", () => {
|
||||
fireEvent.click(screen.getByRole("button", { name: "Envoyer" }));
|
||||
await waitFor(() => expect(agent.sendAgentChat).toHaveBeenCalled());
|
||||
|
||||
fireEvent.click(await screen.findByRole("button", { name: "Cancel" }));
|
||||
fireEvent.click(await screen.findByRole("button", { name: "Stop" }));
|
||||
|
||||
expect(await screen.findByText("Session déjà fermée.")).toBeTruthy();
|
||||
expect(onSessionId).toHaveBeenCalledWith(null);
|
||||
|
||||
@ -246,11 +246,13 @@ function bytesToBase64(bytes: Uint8Array): string {
|
||||
return btoa(binary);
|
||||
}
|
||||
|
||||
async function clipboardImageToAttachment(file: File): Promise<AttachmentDraft> {
|
||||
async function clipboardFileToAttachment(file: File): Promise<AttachmentDraft> {
|
||||
const mime = file.type || "application/octet-stream";
|
||||
const filename =
|
||||
file.name ||
|
||||
`clipboard-image-${Date.now()}.${fileExtension(mime)}`;
|
||||
(mime.startsWith("image/")
|
||||
? `clipboard-image-${Date.now()}.${fileExtension(mime)}`
|
||||
: `clipboard-file-${Date.now()}`);
|
||||
const contentBase64 = bytesToBase64(new Uint8Array(await file.arrayBuffer()));
|
||||
return {
|
||||
id: `clipboard-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
||||
@ -267,7 +269,7 @@ async function clipboardImageToAttachment(file: File): Promise<AttachmentDraft>
|
||||
};
|
||||
}
|
||||
|
||||
function clipboardImageFiles(event: ClipboardEvent<HTMLTextAreaElement>): File[] {
|
||||
function clipboardFiles(event: ClipboardEvent<HTMLTextAreaElement>): File[] {
|
||||
const clipboardItems = event.clipboardData.items
|
||||
? Array.from(event.clipboardData.items)
|
||||
: [];
|
||||
@ -275,12 +277,11 @@ function clipboardImageFiles(event: ClipboardEvent<HTMLTextAreaElement>): File[]
|
||||
? Array.from(event.clipboardData.files)
|
||||
: [];
|
||||
const fromItems = clipboardItems
|
||||
.filter((item) => item.kind === "file" && item.type.startsWith("image/"))
|
||||
.filter((item) => item.kind === "file")
|
||||
.map((item) => item.getAsFile())
|
||||
.filter((file): file is File => Boolean(file));
|
||||
const fromFiles = clipboardFiles.filter((file) => file.type.startsWith("image/"));
|
||||
const seen = new Set<string>();
|
||||
return [...fromItems, ...fromFiles].filter((file) => {
|
||||
return [...fromItems, ...clipboardFiles].filter((file) => {
|
||||
const key = `${file.name}\u0000${file.type}\u0000${file.size}\u0000${file.lastModified}`;
|
||||
if (seen.has(key)) return false;
|
||||
seen.add(key);
|
||||
@ -849,10 +850,10 @@ export function CustomAgentChatView({
|
||||
}
|
||||
}
|
||||
|
||||
async function pasteClipboardImages(
|
||||
async function pasteClipboardFiles(
|
||||
event: ClipboardEvent<HTMLTextAreaElement>,
|
||||
) {
|
||||
const files = clipboardImageFiles(event);
|
||||
const files = clipboardFiles(event);
|
||||
if (files.length === 0) return;
|
||||
|
||||
event.preventDefault();
|
||||
@ -861,7 +862,7 @@ export function CustomAgentChatView({
|
||||
const selectionEnd = event.currentTarget.selectionEnd;
|
||||
try {
|
||||
const nextAttachments = await Promise.all(
|
||||
files.map(clipboardImageToAttachment),
|
||||
files.map(clipboardFileToAttachment),
|
||||
);
|
||||
setAttachments((prev) => [...prev, ...nextAttachments]);
|
||||
if (pastedText) {
|
||||
@ -976,18 +977,6 @@ export function CustomAgentChatView({
|
||||
CLI custom · {profile.name}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex shrink-0 items-center justify-end gap-2">
|
||||
{(opening || busy) && (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="danger"
|
||||
className="relative z-30 shrink-0 whitespace-nowrap shadow-sm"
|
||||
onClick={() => void cancel()}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!supported && (
|
||||
@ -1206,7 +1195,7 @@ export function CustomAgentChatView({
|
||||
disabled={!supported || opening || busy}
|
||||
placeholder="Message à l'agent…"
|
||||
onChange={(e) => setDraft(e.target.value)}
|
||||
onPaste={(e) => void pasteClipboardImages(e)}
|
||||
onPaste={(e) => void pasteClipboardFiles(e)}
|
||||
onKeyDown={(e) => {
|
||||
if (slashMenuOpen && slashCommands.length > 0) {
|
||||
if (e.key === "ArrowDown") {
|
||||
@ -1250,12 +1239,12 @@ export function CustomAgentChatView({
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant={busy ? "danger" : undefined}
|
||||
className="shrink-0 whitespace-nowrap"
|
||||
disabled={!canSend}
|
||||
loading={busy}
|
||||
onClick={() => void send()}
|
||||
disabled={busy ? !supported || !currentSession : !canSend}
|
||||
onClick={() => (busy ? void cancel() : void send())}
|
||||
>
|
||||
Envoyer
|
||||
{busy ? "Stop" : "Envoyer"}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user