UNPKG

@kaifronsdal/transcript-viewer

Version:

A web-based viewer for AI conversation transcripts with rollback support

150 lines (128 loc) 4.58 kB
import type { TranscriptDisplay, TableRow } from '$lib/shared/types'; /** * Build a folder tree structure from transcripts only (client-side) * This is more efficient than server-side building since we don't need directory scanning * Uses _filePath to properly handle nested directory structures */ export function buildFolderTreeFromTranscripts( transcripts: TranscriptDisplay[] ): TableRow[] { const rootNode: TableRow = { id: 'root', name: 'root', path: '', type: 'folder' as const, subRows: [], isEmpty: false, transcriptCount: 0 }; // Create a map for quick folder lookup const folderMap = new Map<string, TableRow>(); folderMap.set('', rootNode); // Root folder // Process each transcript and build the nested folder structure for (const transcript of transcripts) { // Extract directory path from _filePath (e.g., "folder1/subfolder2/transcript_123.json" -> "folder1/subfolder2") const filePath = transcript._filePath || ''; const pathParts = filePath.split('/'); // Remove the filename (last part) to get directory path const directoryParts = pathParts.slice(0, -1); const directoryPath = directoryParts.join('/'); // console.log('🏗️ [CLIENT-TREE] Processing transcript:', { // id: transcript.id, // filePath, // directoryParts, // directoryPath // }); // Create nested folder structure let currentPath = ''; let currentFolder = rootNode; for (const folderName of directoryParts) { const parentPath = currentPath; currentPath = currentPath ? `${currentPath}/${folderName}` : folderName; // Create folder if it doesn't exist if (!folderMap.has(currentPath)) { const folder: TableRow = { id: currentPath, name: folderName, path: currentPath, type: 'folder' as const, subRows: [], isEmpty: false, transcriptCount: 0 }; folderMap.set(currentPath, folder); currentFolder.subRows!.push(folder); } currentFolder = folderMap.get(currentPath)!; } // Add transcript to the deepest folder const transcriptNode: TableRow = { id: directoryPath ? `${directoryPath}/${transcript.id}` : transcript.id, name: transcript.summary.substring(0, 50) + (transcript.summary.length > 50 ? '...' : ''), path: directoryPath ? `${directoryPath}/${transcript.id}` : transcript.id, type: 'transcript' as const, // Include all transcript data directly in the unified format model: transcript.model, split: transcript.split, summary: transcript.summary, scores: transcript.scores, concerningScore: transcript.concerningScore, judgeSummary: transcript.judgeSummary, justification: transcript.justification, tags: transcript.tags, originalTranscript: transcript }; currentFolder.subRows!.push(transcriptNode); } // Update transcript counts for all folders updateFolderCounts(rootNode); // Sort folders and transcripts within each folder sortFolderTree(rootNode); return rootNode.subRows || []; } /** * Recursively update transcript counts for all folders */ function updateFolderCounts(node: TableRow): number { if (node.type === 'transcript') { return 1; } if (node.type === 'folder' && node.subRows) { let totalCount = 0; for (const child of node.subRows) { totalCount += updateFolderCounts(child); } node.transcriptCount = totalCount; node.isEmpty = totalCount === 0; return totalCount; } return 0; } /** * Recursively sort folder tree: folders first (alphabetically), then transcripts (by ID) */ function sortFolderTree(node: TableRow) { if (node.type !== 'folder' || !node.subRows) return; // Sort subRows: folders first, then transcripts node.subRows.sort((a, b) => { // Folders come before transcripts if (a.type !== b.type) { return a.type === 'folder' ? -1 : 1; } // Within same type, sort alphabetically if (a.type === 'folder') { return a.name.localeCompare(b.name); } else { // For transcripts, sort by original transcript ID const aId = a.originalTranscript?.id || a.id; const bId = b.originalTranscript?.id || b.id; return aId.localeCompare(bId); } }); // Recursively sort children for (const child of node.subRows) { if (child.type === 'folder') { sortFolderTree(child); } } }