How Pi Web's File Viewer Handles Images, PDFs, and DOCX Files

Pi Web's FileViewer component in components/FileViewer.tsx routes files to specialized viewers—ImageViewer, DocumentViewer, or TextFileViewer—based on type detection via helper functions from lib/file-types.ts.

Pi Web implements a unified file viewing system that automatically detects file types and renders them with appropriate viewers. The file type handling logic lives in two core modules: the dispatching component and a shared type-detection library. This architecture keeps rendering logic separated while ensuring consistent URL generation and preview behavior across all file formats.

File Type Detection Architecture

All file type detection predicates reside in lib/file-types.ts. These pure functions examine file extensions and map them to MIME-based categories:

  • isImagePath() — validates against known image extensions【/cache/repos/github.com/agegr/pi-web/main/lib/file-types.ts#L63-L66】
  • isAudioPath() — handles audio file detection【/cache/repos/github.com/agegr/pi-web/main/lib/file-types.ts#L67-L69】
  • isDocumentPreviewPath() — identifies PDF and DOCX files via documentPreviewKind()【/cache/repos/github.com/agegr/pi-web/main/lib/file-types.ts#L71-L73】

These helpers are designed for reuse across the UI. The file explorer and other components can import the same functions without duplicating extension-to-MIME mapping logic.

Dispatching to Specialized Viewers

The FileViewer component in components/FileViewer.tsx implements a simple cascade to select the correct renderer:

if (isImagePath(filePath)) {
  return <ImageViewer … />;
}
if (isAudioPath(filePath)) {
  return <AudioViewer … />;
}
if (isDocumentPreviewPath(filePath)) {
  return <DocumentViewer … />;
}
return <TextFileViewer … />;

This branching occurs at lines 35–44【/cache/repos/github.com/agegr/pi-web/main/components/FileViewer.tsx#L35-L44】. Each viewer receives the same props—filePath, cwd, sourceSessionId, and watchEnabled—ensuring consistent behavior regardless of file type.

How Pi Web's File Viewer Handles Images

The ImageViewer renders images through the /api/files endpoint using URL construction from getFileApiUrl(..., "read")【/cache/repos/github.com/agegr/pi-web/main/components/FileViewer.tsx#L6-L10】.

Key implementation details:

  • Live synchronization: The component optionally opens an EventSource to watch for file changes via the watch endpoint—useful for dynamically updating images or log visualizations【/cache/repos/github.com/agegr/pi-web/main/components/FileViewer.tsx#L67-L82】
  • Aspect ratio preservation: Images render with objectFit: "contain" to prevent distortion
  • Status indication: A badge displays whether the image is actively syncing or static【/cache/repos/github.com/agegr/pi-web/main/components/FileViewer.tsx#L31-L46】
<ImageViewer
  filePath="/home/user/screenshots/dashboard.png"
  cwd="/home/user"
  sourceSessionId={sessionId}
  watchEnabled={true}  // Enables live reload on file changes
/>

How Pi Web's File Viewer Handles PDFs and DOCX Files

The DocumentViewer handles both formats through the same component but uses different API endpoints depending on the file extension【/cache/repos/github.com/agegr/pi-web/main/components/FileViewer.tsx#L59-L64】.

PDF Rendering

PDFs stream directly from the read endpoint. The raw binary renders natively in the browser via an <iframe>【/cache/repos/github.com/agegr/pi-web/main/components/FileViewer.tsx#L9-L15】.

DOCX Rendering

DOCX files require server-side processing:

  • The preview endpoint returns rendered HTML rather than the raw binary
  • The iframe receives this HTML in a sandboxed environment for security isolation
  • A 10 MiB size limit (DOCX_PREVIEW_MAX_BYTES) prevents server overload—exceeding this triggers an error message instead of rendering【/cache/repos/github.com/agegr/pi-web/main/components/FileViewer.tsx#L80-L84】
<FileViewer
  filePath="/home/user/reports/annual-review.docx"
  cwd="/home/user/reports"
  sourceSessionId={sessionId}
  watchEnabled={false}  // DOCX previews don't support live sync
/>

Shared URL Generation Infrastructure

All viewers rely on getFileApiUrl() defined in the same file【/cache/repos/github.com/agegr/pi-web/main/components/FileViewer.tsx#L7-L19】. This helper supports four endpoint types:

Endpoint Type Purpose
read Returns raw file content (images, PDFs, text files)
preview Returns server-rendered HTML (DOCX files)
meta File metadata for headers and properties
watch EventSource for live change notifications

Manual URL construction example:

import { getFileApiUrl } from '@/components/FileViewer';

const previewUrl = getFileApiUrl(
  '/home/user/documents/requirements.docx',
  'preview',
  sessionId,
  { v: 1 }  // Cache-busting version parameter
);
// Result: /api/files?path=...&op=preview&session=...&v=1

File Path Utilities

The lib/file-paths.ts module encodes paths for API safety and resolves relative paths for display. This separation keeps the viewer components focused on rendering rather than path manipulation.

Summary

  • Type detection occurs in lib/file-types.ts via extension-based predicates (isImagePath, isDocumentPreviewPath)【/cache/repos/github.com/agegr/pi-web/main/lib/file-types.ts#L63-L73】
  • Component dispatch in components/FileViewer.tsx routes files to ImageViewer, DocumentViewer, AudioViewer, or TextFileViewer【/cache/repos/github.com/agegr/pi-web/main/components/FileViewer.tsx#L35-L44】
  • Images use the read endpoint with optional EventSource live sync【/cache/repos/github.com/agegr/pi-web/main/components/FileViewer.tsx#L67-L82】
  • PDFs render natively via the read endpoint in a sandboxed iframe
  • DOCX files convert to HTML through the preview endpoint with a 10 MiB size limit【/cache/repos/github.com/agegr/pi-web/main/components/FileViewer.tsx#L80-L84】
  • Unified URL builder getFileApiUrl() supports read, preview, meta, and watch operations【/cache/repos/github.com/agegr/pi-web/main/components/FileViewer.tsx#L7-L19】

Frequently Asked Questions

How does Pi Web determine which viewer to use for a file?

Pi Web checks file extensions in sequence using helper functions from lib/file-types.ts. isImagePath() tests against known image MIME mappings, isAudioPath() handles audio, and isDocumentPreviewPath() identifies PDF and DOCX files. The first matching predicate determines which specialized viewer component renders the file.

Can Pi Web preview DOCX files without Microsoft Office installed?

Yes. The server-side preview endpoint converts DOCX to HTML before streaming it to the browser. The DocumentViewer receives this HTML in a sandboxed iframe. No client-side Office software is required, though DOCX files exceeding 10 MiB will not preview and instead display an error.

Does Pi Web support live reloading for images?

Yes. When watchEnabled={true} is passed to FileViewer, the ImageViewer subscribes to file changes via an EventSource connection to the watch endpoint. This automatically refreshes the image without manual page reloads—particularly useful for dashboards, log visualizations, or screenshots from running processes.

What happens if a file type doesn't match any known category?

Files that fail isImagePath(), isAudioPath(), and isDocumentPreviewPath() fall through to TextFileViewer, which renders the raw content as plain text【/cache/repos/github.com/agegr/pi-web/main/components/FileViewer.tsx#L35-L44】. This provides a safe fallback for source code, configuration files, and other text formats without requiring explicit type registration.

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 →