import { CommonModule } from '@angular/common'; import { AfterViewInit, Component, ElementRef, Input, OnChanges, OnDestroy, ViewChild } from '@angular/core'; import type { ChatEntry } from './models'; type JsonViewTree = object; type JsonViewApi = { renderJSON(value: unknown, container: HTMLElement): JsonViewTree; expand?(tree: JsonViewTree): void; destroy?(tree: JsonViewTree): void; }; declare global { interface Window { jsonview?: JsonViewApi; } } @Component({ selector: 'app-json-file-viewer', imports: [CommonModule], template: `
@if (errorMessage) {

{{ errorMessage }}

}
`, styleUrl: './json-file-viewer.component.scss', }) export class JsonFileViewerComponent implements AfterViewInit, OnChanges, OnDestroy { @Input({ required: true }) entry!: ChatEntry; @ViewChild('host') private readonly host?: ElementRef; errorMessage: string | null = null; private renderedTree: JsonViewTree | null = null; private renderVersion = 0; ngAfterViewInit(): void { void this.renderJsonTree(); } ngOnChanges(): void { void this.renderJsonTree(); } ngOnDestroy(): void { this.destroyRenderedTree(); } private async renderJsonTree(): Promise { const host = this.host?.nativeElement; if (!host || !this.entry.downloadUrl) { return; } const renderVersion = ++this.renderVersion; const jsonview = window.jsonview; this.destroyRenderedTree(); host.replaceChildren(); this.errorMessage = null; if (!jsonview) { this.errorMessage = 'JSON viewer unavailable.'; return; } try { const response = await fetch(this.entry.downloadUrl); const content = await response.text(); const parsed = JSON.parse(content); if (renderVersion !== this.renderVersion) { return; } const tree = jsonview.renderJSON(parsed, host); jsonview.expand?.(tree); this.renderedTree = tree; } catch { if (renderVersion !== this.renderVersion) { return; } this.errorMessage = 'Could not render JSON preview.'; } } private destroyRenderedTree(): void { if (!this.renderedTree) { return; } window.jsonview?.destroy?.(this.renderedTree); this.renderedTree = null; } }