@kaifronsdal/transcript-viewer
Version:
A web-based viewer for AI conversation transcripts with rollback support
151 lines (132 loc) • 4.92 kB
text/typescript
import type { TranscriptMetadata, TranscriptDisplay } from '$lib/shared/types';
export interface TranscriptLoaderState {
metadata: TranscriptMetadata | null;
transcript: TranscriptDisplay | null;
metadataLoading: boolean;
transcriptLoading: boolean;
metadataError: string | null;
transcriptError: string | null;
}
export function createTranscriptLoader(filePath: string) {
let metadata = $state<TranscriptMetadata | null>(null);
let transcript = $state<TranscriptDisplay | null>(null);
let metadataLoading = $state(false);
let transcriptLoading = $state(false);
let metadataError = $state<string | null>(null);
let transcriptError = $state<string | null>(null);
const loadMetadata = async (): Promise<TranscriptMetadata | null> => {
if (metadata) return metadata;
metadataLoading = true;
metadataError = null;
try {
const url = `/api/transcripts?filePath=${encodeURIComponent(filePath)}&metadataOnly=true`;
const response = await fetch(url);
if (!response.ok) {
if (response.status === 404) {
metadataError = `Transcript at "${filePath}" not found`;
} else {
// Try to extract server error details for easier debugging
let details = '';
try {
const errJson = await response.clone().json();
details = errJson?.message || errJson?.error || '';
} catch {
try {
details = await response.text();
} catch {}
}
const extra = details ? ` - ${details}` : '';
throw new Error(`Failed to load metadata: ${response.status} ${response.statusText}${extra}`);
}
return null;
}
const result = await response.json();
if (result.success) {
metadata = result.data;
return metadata;
} else {
throw new Error(result.error || 'Unknown API error');
}
} catch (err) {
metadataError = err instanceof Error ? err.message : 'Unknown error loading metadata';
return null;
} finally {
metadataLoading = false;
}
};
const loadTranscript = async (): Promise<TranscriptDisplay | null> => {
if (transcript) return transcript;
transcriptLoading = true;
transcriptError = null;
try {
const url = `/api/transcripts?filePath=${encodeURIComponent(filePath)}`;
const response = await fetch(url);
if (!response.ok) {
if (response.status === 404) {
transcriptError = `Transcript at "${filePath}" not found`;
} else {
// Try to extract server error details for easier debugging
let details = '';
try {
const errJson = await response.clone().json();
details = errJson?.message || errJson?.error || '';
} catch {
try {
details = await response.text();
} catch {}
}
const extra = details ? ` - ${details}` : '';
throw new Error(`Failed to load transcript: ${response.status} ${response.statusText}${extra}`);
}
return null;
}
const result = await response.json();
if (result.success) {
transcript = result.data;
// If metadata wasn't loaded yet, extract it from the full transcript (when present)
if (!metadata && transcript && 'transcript' in transcript && transcript.transcript?.metadata) {
metadata = transcript.transcript.metadata;
}
return transcript;
} else {
throw new Error(result.error || 'Unknown API error');
}
} catch (err) {
transcriptError = err instanceof Error ? err.message : 'Unknown error loading transcript';
return null;
} finally {
transcriptLoading = false;
}
};
const reset = () => {
metadata = null;
transcript = null;
metadataLoading = false;
transcriptLoading = false;
metadataError = null;
transcriptError = null;
};
return {
// Reactive getters
get metadata() { return metadata; },
get transcript() { return transcript; },
get metadataLoading() { return metadataLoading; },
get transcriptLoading() { return transcriptLoading; },
get metadataError() { return metadataError; },
get transcriptError() { return transcriptError; },
get hasMetadata() { return metadata !== null; },
get hasTranscript() { return transcript !== null; },
get isLoading() { return metadataLoading || transcriptLoading; },
get hasError() { return metadataError !== null || transcriptError !== null; },
// Actions
loadMetadata,
loadTranscript,
reset,
// Convenience method to load both if needed
loadBoth: async () => {
await loadMetadata();
await loadTranscript();
}
};
}
// Removed batch loader utilities for simplicity