function renderQueueItemFileActions(item: QueueItem): string { if (item.status !== 'completed' || !item.outputFiles || item.outputFiles.length === 0) { return ''; } const first = item.outputFiles[0]; if (typeof first !== 'string' || !first) return ''; const safeFirst = escapeHtml(first); const safeFirstAttr = first.replace(/'/g, "\\'").replace(/"/g, '"'); const buttons: string[] = []; // "Open file" only makes sense when there's exactly one output (a clip / // full VOD download). For multi-part downloads "open the first part" is // surprising — the user almost always wants the folder. if (item.outputFiles.length === 1) { buttons.push(``); } buttons.push(``); // Surface a "View chat" button when a sibling chat file exists in the // outputs list. Single click opens the in-app viewer modal. const chatFile = item.outputFiles.find((f) => /\.chat\.json(l)?$/i.test(f)); if (chatFile) { const safeChatAttr = chatFile.replace(/'/g, "\\'").replace(/"/g, '"'); buttons.push(``); } // Same pattern for the .events.jsonl sidecar — title/game change timeline. const eventsFile = item.outputFiles.find((f) => /\.events\.jsonl$/i.test(f)); if (eventsFile) { const safeEventsAttr = eventsFile.replace(/'/g, "\\'").replace(/"/g, '"'); buttons.push(``); } const fileLabel = item.outputFiles.length === 1 ? safeFirst : `${escapeHtml(UI_TEXT.queue.outputFilesLabel.replace('{count}', String(item.outputFiles.length)))}`; return `