emo-online-api / emo /frontend /src /lib /resolveToolPreview.js
Emo Online
Deploy Emo API
dd87944
Raw
History Blame Contribute Delete
7.44 kB
import { isImagePath, previewTextSnippet } from "./previewHelpers";
import { htmlPreviewUrl, isHtmlPath } from "./filePreview";
import {
base64ToDataUrl,
isUsableImageBase64,
mergeImageFields,
resolveImageUrl,
} from "./imagePreview";
const BROWSER_TOOLS = [
"web_fetch", "browser_visit", "browser_open",
"browser_click", "browser_snapshot", "browser_scroll", "browser_press", "browser_type", "browser_fill",
];
function hasValidScreenshot(data) {
const b64 = data?.screenshot_base64;
if (!b64 || typeof b64 !== "string") return false;
if (b64.includes("truncated") || b64.includes("[screenshot:")) return false;
return b64.length > 500;
}
/** Build a displayable image src from SSE / tool result fields. */
export function buildImagePreviewSrc(input) {
const fields = mergeImageFields(input);
const dataUrl = base64ToDataUrl(fields.image_base64, fields.mime);
if (dataUrl) return dataUrl;
if (input?.src && typeof input.src === "string") {
const fromDirect = resolveImageUrl(input.src);
if (fromDirect) return fromDirect;
}
return resolveImageUrl(fields.image_url);
}
/** Pair display src + alternate (URL ↔ base64) for resilient previews. */
export function buildImagePreviewPair(input) {
if (!input || typeof input !== "object") return { src: null, fallbackSrc: null };
const fields = mergeImageFields(input);
const dataUrl = base64ToDataUrl(fields.image_base64, fields.mime);
const fromUrl = resolveImageUrl(fields.image_url);
const main = dataUrl || fromUrl || buildImagePreviewSrc(input);
if (!main) return { src: null, fallbackSrc: null };
let fallbackSrc = null;
if (main === dataUrl && fromUrl) fallbackSrc = fromUrl;
else if (main === fromUrl && dataUrl) fallbackSrc = dataUrl;
if (fallbackSrc === main) fallbackSrc = null;
return { src: main, fallbackSrc };
}
/** True when event carries enough data to attempt image display. */
export function hasImagePayload(input) {
if (!input || typeof input !== "object") return false;
const fields = mergeImageFields(input);
return Boolean(
isUsableImageBase64(fields.image_base64)
|| resolveImageUrl(fields.image_url)
|| input.has_image,
);
}
/** Détecte si un événement tool peut afficher un aperçu visuel. */
export function hasToolPreview(event) {
return Boolean(resolveToolPreview(event));
}
/** Contenu fichier complet pour copie (priorité SSE > args > result). */
export function resolveFullCopyContent(event) {
const preview = event?.inlinePreview;
const result = event?.result;
const args = event?.args || {};
const tool = event?.tool;
if (preview?.preview) return preview.preview;
if (tool === "write_file" || tool === "edit_file") {
if (args.content) return args.content;
}
if (result?.content) return result.content;
return "";
}
function enrichWithFullContent(data, event) {
if (!data || (data.kind !== "text" && data.kind !== "html")) return data;
const copyContent = resolveFullCopyContent(event);
return copyContent ? { ...data, fullContent: copyContent } : data;
}
/** Résout les données d'aperçu pour fichier, HTML, site, capture. */
export function resolveToolPreview(event) {
const preview = event?.inlinePreview;
const result = event?.result;
const tool = event?.tool;
const args = event?.args || {};
if (preview?.type === "browser") {
const p = preview;
if (p.action === "search" && (p.results || []).length) {
return {
kind: "search",
title: p.query || "Recherche",
results: (p.results || []).slice(0, 4),
};
}
if (p.url || hasValidScreenshot(p)) {
return {
kind: "interactive",
url: p.url || result?.url || "",
title: p.title || p.url || "Navigateur",
previewText: p.preview,
screenshot_base64: hasValidScreenshot(p) ? p.screenshot_base64 : null,
elements: p.elements || [],
session_id: p.session_id || "default",
};
}
}
if (preview?.type === "file") {
return enrichWithFullContent(
resolveFilePreview(preview.path, preview.preview, preview.is_image),
event,
);
}
if (preview?.type === "image" || (tool === "generate_image" && result?.ok !== false)) {
const fields = mergeImageFields(preview, result);
if (hasImagePayload(fields) || hasImagePayload(preview) || hasImagePayload(result)) {
return {
kind: "image",
title: fields.title || args.prompt || "Image générée",
imageFields: fields,
};
}
}
if (!result || result.ok === false) {
const url = args.url;
if (url && BROWSER_TOOLS.includes(tool)) {
return {
kind: "interactive",
url,
title: url,
elements: [],
session_id: "default",
};
}
if (tool === "generate_image" && result?.error) {
return { kind: "text", text: result.error, title: "Génération d'image" };
}
return null;
}
if (tool === "web_search" && (result.results || []).length) {
return {
kind: "search",
title: result.query || args.query || "Recherche",
results: (result.results || []).slice(0, 4),
};
}
if (tool === "write_file" || tool === "read_file" || tool === "edit_file") {
const path = result.path || args.path || preview?.path || "";
const content =
tool === "write_file" || tool === "edit_file"
? (args.content || result.content || preview?.preview || "")
: (result.content || preview?.preview || "");
if (path && content) {
return enrichWithFullContent(resolveFilePreview(path, content), event);
}
}
if (BROWSER_TOOLS.includes(tool)) {
const url = result.url || args.url;
if (url || hasValidScreenshot(result)) {
return {
kind: "interactive",
url: url || "",
title: result.title || url || "Capture",
previewText: result.preview || result.text,
screenshot_base64: hasValidScreenshot(result) ? result.screenshot_base64 : null,
elements: result.elements || [],
session_id: result.session_id || "default",
};
}
}
if (hasValidScreenshot(result)) {
return {
kind: "interactive",
url: result.url || "",
title: result.title || result.url || "Capture",
screenshot_base64: result.screenshot_base64,
elements: result.elements || [],
session_id: result.session_id || "default",
};
}
return null;
}
function resolveFilePreview(path, content, isImage) {
if (!path || !content) return null;
if (isImage && content.startsWith("data:")) {
return { kind: "image", src: content, title: path.split(/[/\\]/).pop(), path };
}
if (isImagePath(path) && content.startsWith("data:")) {
return { kind: "image", src: content, title: path.split(/[/\\]/).pop(), path };
}
if (isImage && !content.startsWith("data:") && content.length > 100) {
return {
kind: "image",
src: `data:image/jpeg;base64,${content}`,
title: path.split(/[/\\]/).pop(),
path,
};
}
if (isHtmlPath(path)) {
return {
kind: "html",
htmlUrl: htmlPreviewUrl(content),
title: path.split(/[/\\]/).pop(),
path,
fullContent: content,
};
}
if (content.length > 20) {
return {
kind: "text",
text: previewTextSnippet(content, 1200),
title: path.split(/[/\\]/).pop(),
path,
fullContent: content,
};
}
return null;
}