How pi-web's FileViewer Previews Images, Audio, PDFs, and Source Code

pi-web's FileViewer component selects specialized rendering engines based on file extensions, using Server-Sent Events (SSE) for live synchronization and dedicated sub-components for images, audio, documents, and source code.

The agegr/pi-web repository provides a browser-based file management interface with a sophisticated preview system. At the heart of this system lies the FileViewer component, which automatically detects file types and renders appropriate previews for images, audio files, PDFs, and source code. Located in components/FileViewer.tsx, this component orchestrates multiple specialized viewers that handle real-time updates and format-specific rendering.

File Type Detection and Routing Logic

The entry point components/FileViewer.tsx determines which viewer to render by evaluating the file path against type-checking utilities defined in lib/file-types.ts. The selection logic uses a cascading conditional structure at the bottom of the component:

if (isImagePath(filePath)) { … }
if (isAudioPath(filePath)) { … }
if (isDocumentPreviewPath(filePath)) { … }

These helper functions—isImagePath, isAudioPath, and isDocumentPreviewPath—map file extensions to MIME types and preview categories. When no specific media type matches, the component defaults to TextFileViewer for source code and plain text files.

Image Preview with Live Synchronization

When isImagePath returns true, the ImageViewer sub-component renders the file. This implementation opens an EventSource connection to /api/files/... with type=watch to receive Server-Sent Events (SSE) for live file synchronization.

The image source URL is constructed using getFileApiUrl(..., "read") and includes a cache-busting query parameter v that increments on every change event to prevent stale renders:

const src = getFileApiUrl(filePath, "read", sourceSessionId, bust ? { v: bust } : undefined);
return <img src={src} alt={filePath} style={{ maxWidth: "100%", maxHeight: "100%" }} />;

The viewer displays a toolbar containing a live-sync indicator, file size information, and a download button. The image itself renders via a standard <img> tag with objectFit: "contain" and lazy loading enabled.

Audio Preview with Metadata Support

For audio files identified by isAudioPath, the AudioViewer component handles playback. Like the image viewer, it maintains an SSE connection to watch for file changes and updates the audio source accordingly.

The component calculates duration by listening to the onLoadedMetadata event on the audio element:

const src = getFileApiUrl(filePath, "read", sourceSessionId, bust ? { v: bust } : undefined);
return <audio controls src={src} onLoadedMetadata={(e) => setDuration(e.currentTarget.duration)} />;

The toolbar displays the file extension, size, computed duration, live-sync status, and download functionality.

Document Preview for PDF and DOCX Files

The DocumentViewer component activates when isDocumentPreviewPath identifies a document file. It distinguishes between PDF and DOCX formats using documentPreviewKind logic.

PDF rendering uses the standard read endpoint via an iframe:

const previewUrl = isPdf
  ? getFileApiUrl(filePath, "read", sourceSessionId, bust ? { v: bust } : undefined)
  : getFileApiUrl(filePath, "preview", sourceSessionId, bust ? { v: bust } : undefined);
return <iframe src={previewUrl} title="PDF preview" style={{ width: "100%", height: "100%" }} />;

DOCX handling routes through the API's preview endpoint, which returns rendered HTML. The component checks file size against DOCX_PREVIEW_MAX_BYTES (10 MiB) via the meta endpoint and refuses preview if the limit is exceeded. Both formats support live synchronization through SSE.

Source Code Rendering and Markdown Preview

All other files route to TextFileViewer, which provides syntax highlighting and text analysis features. The component fetches content via the read endpoint and determines the programming language from the file extension.

Source code renders using react-syntax-highlighter with theme-aware styling:

<SyntaxHighlighter
  language={language === "text" ? "plaintext" : language}
  style={isDark ? vscDarkPlus : vs}
  showLineNumbers
  wrapLongLines={wrapLines}
  renderer={(props) => <SourceCodeRenderer {...props} wrapLines={wrapLines} />}
>
  {content}
</SyntaxHighlighter>

For Markdown files, a toggle switches between raw source and rendered preview using react-markdown, supporting embedded images, links, and Mermaid diagrams. Additional features include line number annotations, wrap-line toggling, and a "mention selected lines" shortcut for collaboration.

Summary

  • Type Detection: lib/file-types.ts provides isImagePath, isAudioPath, and isDocumentPreviewPath to classify files by extension.
  • Live Sync: All viewers implement Server-Sent Events via EventSource to watch files for changes and update content in real-time.
  • Media Handling: ImageViewer and AudioViewer use the read API endpoint with cache-busting query parameters.
  • Document Support: DocumentViewer renders PDFs directly in iframes and converts DOCX files via the preview endpoint with a 10 MiB size limit.
  • Code Display: TextFileViewer leverages react-syntax-highlighter for language-specific formatting and supports Markdown rendering with diagrams.

Frequently Asked Questions

How does pi-web FileViewer handle file changes while previewing?

The component establishes an EventSource connection to the /api/files/... endpoint with the type=watch parameter. When the underlying file changes on disk, the server pushes events to the client, triggering a cache-bust parameter update that refreshes the preview without manual reloading.

What is the maximum file size for DOCX previews in pi-web?

According to the source code in components/FileViewer.tsx, the DocumentViewer checks file size against DOCX_PREVIEW_MAX_BYTES, which is set to 10 MiB (10,485,760 bytes). Files exceeding this limit display a size error rather than rendering the preview.

Which syntax highlighting themes does pi-web FileViewer support?

The TextFileViewer component imports vscDarkPlus and vs themes from react-syntax-highlighter. It dynamically selects between these based on the application's current dark mode state, providing either a dark or light code viewing experience.

Can pi-web FileViewer display Mermaid diagrams in Markdown files?

Yes. When previewing Markdown files, the TextFileViewer uses react-markdown with plugins that support embedded Mermaid diagrams, alongside standard Markdown features like images and links.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →