* feat(webui): refine output timeline and composer queue * feat(webui): add provider model picker * fix(webui): polish model settings and heartbeat checks * chore: keep heartbeat changes out of webui pr * refactor(webui): isolate settings routes * fix(providers): align minimax anthropic test * fix(providers): keep minimax anthropic base sdk-compatible * fix(providers): normalize anthropic base urls
65 lines
1.4 KiB
TypeScript
65 lines
1.4 KiB
TypeScript
import type { UIMediaAttachment, UIMediaKind } from "@/lib/types";
|
|
|
|
const IMAGE_EXTENSIONS = new Set([
|
|
".png",
|
|
".jpg",
|
|
".jpeg",
|
|
".gif",
|
|
".webp",
|
|
".bmp",
|
|
".ico",
|
|
".svg",
|
|
".tif",
|
|
".tiff",
|
|
]);
|
|
|
|
const VIDEO_EXTENSIONS = new Set([
|
|
".mp4",
|
|
".webm",
|
|
".mov",
|
|
".m4v",
|
|
".avi",
|
|
".mkv",
|
|
".3gp",
|
|
]);
|
|
|
|
function cleanPath(value: string): string {
|
|
return value.split(/[?#]/, 1)[0]?.toLowerCase() ?? "";
|
|
}
|
|
|
|
function extensionOf(value?: string): string {
|
|
if (!value) return "";
|
|
const path = cleanPath(value);
|
|
const dot = path.lastIndexOf(".");
|
|
if (dot < 0) return "";
|
|
return path.slice(dot);
|
|
}
|
|
|
|
function explicitMediaKind(media: { url?: string; name?: string }): UIMediaKind | null {
|
|
const url = media.url ?? "";
|
|
if (url.startsWith("data:image/")) return "image";
|
|
if (url.startsWith("data:video/")) return "video";
|
|
|
|
const ext = extensionOf(media.name) || extensionOf(url);
|
|
if (!ext) return null;
|
|
if (IMAGE_EXTENSIONS.has(ext)) return "image";
|
|
if (VIDEO_EXTENSIONS.has(ext)) return "video";
|
|
return "file";
|
|
}
|
|
|
|
export function inferMediaKind(media: { url?: string; name?: string }): UIMediaKind {
|
|
return explicitMediaKind(media) ?? "file";
|
|
}
|
|
|
|
export function toMediaAttachment(media: {
|
|
url?: string;
|
|
name?: string;
|
|
kind?: UIMediaKind;
|
|
}): UIMediaAttachment {
|
|
return {
|
|
kind: explicitMediaKind(media) ?? media.kind ?? "file",
|
|
url: media.url,
|
|
name: media.name,
|
|
};
|
|
}
|