fix: restrict file attachments to images only

The OpenClaw gateway only supports image attachments in chat.send;
non-image files were silently dropped, confusing users (fixes #24).

- Filter non-image files in addFiles handler
- Update file input accept to image/* only
- Remove unused fileToBase64 helper
- Update i18n labels from 'Attach file' to 'Attach image' (all 8 langs)

Fixes #24
This commit is contained in:
Nicolas Varrot
2026-03-11 09:04:35 +00:00
parent c4eb7dd844
commit bc3e507a77
2 changed files with 16 additions and 35 deletions

View File

@@ -38,19 +38,6 @@ interface Props {
const MAX_BASE64_CHARS = 300 * 1024; // ~225KB real, well under 512KB WS limit (JSON overhead + base64 bloat)
const MAX_IMAGE_PIXELS = 1280; // Max dimension for resize
function fileToBase64(file: File): Promise<string> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
const dataUrl = reader.result as string;
const base64 = dataUrl.split(',')[1] || '';
resolve(base64);
};
reader.onerror = reject;
reader.readAsDataURL(file);
});
}
function compressImage(file: File, maxBase64Chars: number): Promise<{ base64: string; mimeType: string }> {
return new Promise((resolve, reject) => {
const img = new Image();
@@ -184,24 +171,18 @@ export function ChatInput({ onSend, onNewSession, onAbort, isGenerating, disable
const newFiles: FileAttachment[] = [];
for (const file of Array.from(fileList)) {
if (file.size > 20 * 1024 * 1024) continue; // 20MB max
const isImage = file.type.startsWith('image/');
let base64: string;
let mimeType: string;
if (isImage) {
// Only images are supported — the OpenClaw gateway drops non-image attachments
if (!file.type.startsWith('image/')) continue;
// Compress images to fit WS payload limit
const compressed = await compressImage(file, MAX_BASE64_CHARS);
base64 = compressed.base64;
mimeType = compressed.mimeType;
} else {
base64 = await fileToBase64(file);
mimeType = file.type || 'application/octet-stream';
}
const base64 = compressed.base64;
const mimeType = compressed.mimeType;
newFiles.push({
id: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
file,
base64,
mimeType,
preview: isImage ? `data:${mimeType};base64,${base64}` : undefined,
preview: `data:${mimeType};base64,${base64}`,
});
}
setFiles(prev => [...prev, ...newFiles]);
@@ -418,7 +399,7 @@ export function ChatInput({ onSend, onNewSession, onAbort, isGenerating, disable
multiple
className="hidden"
onChange={(e) => { if (e.target.files) addFiles(e.target.files); e.target.value = ''; }}
accept="image/*,.pdf,.txt,.md,.json,.csv,.log,.py,.js,.ts,.tsx,.jsx,.html,.css,.yaml,.yml,.xml,.sql,.sh,.env,.toml"
accept="image/*"
/>
<textarea

View File

@@ -49,7 +49,7 @@ const en = {
'chat.loadingHistory': 'Loading messages…',
'chat.inputPlaceholder': 'Type a message…',
'chat.inputLabel': 'Message',
'chat.attachFile': 'Attach file',
'chat.attachFile': 'Attach image',
'chat.send': 'Send',
'chat.stop': 'Stop',
'chat.showPreview': 'Preview markdown',
@@ -245,7 +245,7 @@ const fr: Record<keyof typeof en, string> = {
'chat.loadingHistory': 'Chargement des messages…',
'chat.inputPlaceholder': 'Tapez un message…',
'chat.inputLabel': 'Message',
'chat.attachFile': 'Joindre un fichier',
'chat.attachFile': 'Joindre une image',
'chat.send': 'Envoyer',
'chat.stop': 'Arrêter',
'chat.showPreview': 'Aperçu markdown',
@@ -428,7 +428,7 @@ const es: Record<keyof typeof en, string> = {
'chat.loadingHistory': 'Cargando mensajes…',
'chat.inputPlaceholder': 'Escribe un mensaje…',
'chat.inputLabel': 'Mensaje',
'chat.attachFile': 'Adjuntar archivo',
'chat.attachFile': 'Adjuntar imagen',
'chat.send': 'Enviar',
'chat.stop': 'Detener',
'chat.showPreview': 'Vista previa markdown',
@@ -613,7 +613,7 @@ const de: Record<keyof typeof en, string> = {
'chat.loadingHistory': 'Nachrichten werden geladen…',
'chat.inputPlaceholder': 'Nachricht eingeben…',
'chat.inputLabel': 'Nachricht',
'chat.attachFile': 'Datei anhängen',
'chat.attachFile': 'Bild anhängen',
'chat.send': 'Senden',
'chat.stop': 'Stoppen',
'chat.showPreview': 'Markdown-Vorschau',
@@ -796,7 +796,7 @@ const ja: Record<keyof typeof en, string> = {
'chat.loadingHistory': 'メッセージを読み込み中…',
'chat.inputPlaceholder': 'メッセージを入力…',
'chat.inputLabel': 'メッセージ',
'chat.attachFile': 'ファイルを添付',
'chat.attachFile': '画像を添付',
'chat.send': '送信',
'chat.stop': '停止',
'chat.showPreview': 'Markdownプレビュー',
@@ -979,7 +979,7 @@ const pt: Record<keyof typeof en, string> = {
'chat.loadingHistory': 'Carregando mensagens…',
'chat.inputPlaceholder': 'Digite uma mensagem…',
'chat.inputLabel': 'Mensagem',
'chat.attachFile': 'Anexar arquivo',
'chat.attachFile': 'Anexar imagem',
'chat.send': 'Enviar',
'chat.stop': 'Parar',
'chat.showPreview': 'Pré-visualizar markdown',
@@ -1162,7 +1162,7 @@ const zh: Record<keyof typeof en, string> = {
'chat.loadingHistory': '加载消息中…',
'chat.inputPlaceholder': '输入消息…',
'chat.inputLabel': '消息',
'chat.attachFile': '添加附件',
'chat.attachFile': '添加图片',
'chat.send': '发送',
'chat.stop': '停止',
'chat.showPreview': '预览 Markdown',
@@ -1345,7 +1345,7 @@ const it: Record<keyof typeof en, string> = {
'chat.loadingHistory': 'Caricamento messaggi…',
'chat.inputPlaceholder': 'Scrivi un messaggio…',
'chat.inputLabel': 'Messaggio',
'chat.attachFile': 'Allega file',
'chat.attachFile': 'Allega immagine',
'chat.send': 'Invia',
'chat.stop': 'Ferma',
'chat.showPreview': 'Anteprima markdown',