feat(chat): colle les fichiers du presse-papiers via Ctrl+V dans le composer (#181)

This commit is contained in:
2026-08-11 15:57:30 +02:00
parent ce2640c9af
commit 419987d890
4 changed files with 296 additions and 3 deletions

View File

@ -57,6 +57,7 @@ interface AttachmentDraft {
label: string;
input: ChatAttachmentInput;
previewUrl?: string;
dedupeKey?: string;
}
interface ProfileCommandDialogState {
@ -351,6 +352,7 @@ async function clipboardFileToAttachment(file: File): Promise<AttachmentDraft> {
previewUrl: mime.startsWith("image/")
? `data:${mime};base64,${contentBase64}`
: undefined,
dedupeKey: fileMetadataKey(file),
input: {
filename,
contentBase64,
@ -360,6 +362,10 @@ async function clipboardFileToAttachment(file: File): Promise<AttachmentDraft> {
};
}
function fileMetadataKey(file: File): string {
return `${file.name}\u0000${file.type}\u0000${file.size}\u0000${file.lastModified}`;
}
function clipboardFiles(event: ClipboardEvent<HTMLTextAreaElement>): File[] {
const clipboardItems = event.clipboardData.items
? Array.from(event.clipboardData.items)
@ -373,7 +379,7 @@ function clipboardFiles(event: ClipboardEvent<HTMLTextAreaElement>): File[] {
.filter((file): file is File => Boolean(file));
const seen = new Set<string>();
return [...fromItems, ...clipboardFiles].filter((file) => {
const key = `${file.name}\u0000${file.type}\u0000${file.size}\u0000${file.lastModified}`;
const key = fileMetadataKey(file);
if (seen.has(key)) return false;
seen.add(key);
return true;
@ -958,7 +964,20 @@ export function CustomAgentChatView({
const nextAttachments = await Promise.all(
files.map(clipboardFileToAttachment),
);
setAttachments((prev) => [...prev, ...nextAttachments]);
setAttachments((prev) => {
const known = new Set(
prev
.map((attachment) => attachment.dedupeKey)
.filter((key): key is string => Boolean(key)),
);
const unique = nextAttachments.filter((attachment) => {
if (!attachment.dedupeKey) return true;
if (known.has(attachment.dedupeKey)) return false;
known.add(attachment.dedupeKey);
return true;
});
return unique.length > 0 ? [...prev, ...unique] : prev;
});
if (pastedText) {
setDraft((prev) =>
prev.slice(0, selectionStart) +