export const objectCopy = (obj: T): T => { return JSON.parse(JSON.stringify(obj)) as T; }; export const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); export const imgExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.svg', '.bmp']; export const videoExtensions = ['.mp4', '.avi', '.mov', '.mkv', '.wmv', '.m4v', '.flv']; export const audioExtensions = ['.mp3', '.wav', '.flac', '.ogg']; export const isVideo = (filePath: string) => videoExtensions.includes(filePath.toLowerCase().slice(-4)); export const isImage = (filePath: string) => imgExtensions.includes(filePath.toLowerCase().slice(-4)); export const isAudio = (filePath: string) => audioExtensions.includes(filePath.toLowerCase().slice(-4)); export const tagsToObj = (tagStr: string): Record => { const result: Record = {}; const regex = /<([A-Z_][A-Z0-9_]*)>([\s\S]*?)<\/\1>/g; let match; while ((match = regex.exec(tagStr)) !== null) { const value = match[2].trim(); try { result[match[1]] = JSON.parse(value); } catch { result[match[1]] = value; } } return result; }; export const objToTags = (obj: Record): string => { return Object.entries(obj) .map(([key, value]) => { const content = typeof value === 'string' ? value : JSON.stringify(value); return `<${key}>${content}`; }) .join('\n'); }; export const getFilename = (filePath: string) => { const idx = Math.max(filePath.lastIndexOf('/'), filePath.lastIndexOf('\\')); return idx === -1 ? filePath : filePath.slice(idx + 1); }; export const getFoldername = (filePath: string) => { const idx = Math.max(filePath.lastIndexOf('/'), filePath.lastIndexOf('\\')); return idx === -1 ? '' : filePath.slice(0, idx); }; /** * Encode an absolute file path for /api/files/ and /api/img/ URLs as * `/` — two URL segments, so the last one is * the real filename and downloaders (wget, curl -O, browsers) save it under * that name instead of the fully-escaped path. Works for posix and Windows * paths (`C:\foo\bar.safetensors` -> `C%3A%5Cfoo/bar.safetensors`). The * servers accept both this and the legacy single-segment * `encodeURIComponent(fullPath)` form. */ export const encodeFilePathForUrl = (filePath: string) => { const idx = Math.max(filePath.lastIndexOf('/'), filePath.lastIndexOf('\\')); if (idx === -1) return encodeURIComponent(filePath); // keep a root-only folder ("/" or "C:\") as a non-empty segment const folder = idx === 0 ? filePath[0] : filePath.slice(0, idx); return `${encodeURIComponent(folder)}/${encodeURIComponent(filePath.slice(idx + 1))}`; }; export const pathJoin = (...parts: string[]) => { const sep = parts.length > 0 && parts[0].includes('\\') ? '\\' : '/'; const leadingTrailing = sep === '\\' ? /^\\+|\\+$/g : /^\/+|\/+$/g; const trailing = sep === '\\' ? /\\+$/ : /\/+$/; return parts .map((part, index) => { if (index === 0) { return part.replace(trailing, ''); } else { return part.replace(leadingTrailing, ''); } }) .filter(part => part.length > 0) .join(sep); }