# How AionUi AI File Preview Works: A Technical Deep Dive

> Discover how the AionUi AI file preview system works. Explore its modular design, context-driven rendering for markdown, code, PDFs, Office docs, and images, and IPC bridges for live updates.

- Repository: [OfficeAI/AionUi](https://github.com/iofficeai/aionui)
- Tags: deep-dive
- Published: 2026-02-15

---

**The AionUi AI file preview system is a modular, context-driven subsystem that renders markdown, code, PDFs, Office documents, and images in a dedicated panel using React context, content-type detection, and IPC bridges for live agent updates.**

The AionUi AI file preview feature provides a seamless way to inspect generated or uploaded files without leaving the conversation interface. Built as a self-contained module within the Electron renderer process, it supports everything from live-streamed markdown to binary Office documents, while maintaining full edit-and-save capabilities and cross-session persistence.

## Architecture Overview

The AionUi preview system is organized into four logical layers that handle state, detection, rendering, and communication.

### Context and State Management

At the core lies the `PreviewContext`, defined in [`src/renderer/pages/conversation/preview/context/PreviewContext.tsx`](https://github.com/iOfficeAI/AionUi/blob/main/src/renderer/pages/conversation/preview/context/PreviewContext.tsx). This React context holds the entire preview panel state—including open/close status, tab list, active tab ID, dirty flags, and metadata—and persists it to `localStorage` under the key `aionui_preview_state`.

The context exposes critical helpers such as `openPreview`, `closeTab`, `updateContent`, and `saveContent`. These methods manage tab deduplication (via `findPreviewTabInList`), activation, and the merging of live stream updates without flickering the UI.

### Content-Type Detection

Before rendering, the system must map file paths to viewer types. The utility module [`src/renderer/pages/conversation/preview/utils/fileUtils.ts`](https://github.com/iOfficeAI/AionUi/blob/main/src/renderer/pages/conversation/preview/utils/fileUtils.ts) maintains a `FILE_EXTENSION_MAP` that associates extensions with `PreviewContentType` values (`markdown`, `html`, `code`, `pdf`, `image`, `url`, etc.).

Key functions include:
- `getContentTypeByExtension(fileName: string)` – Returns the enumerated type.
- `isImageFile(fileName: string)` – Checks against known image extensions.
- `isTextFile(fileName: string)` – Determines if content is editable text.
- `isOfficeFile(fileName: string)` – Identifies Word, Excel, or PowerPoint documents.

### Rendering Engine

The `PreviewPanel` component in [`src/renderer/pages/conversation/preview/components/PreviewPanel/PreviewPanel.tsx`](https://github.com/iOfficeAI/AionUi/blob/main/src/renderer/pages/conversation/preview/components/PreviewPanel/PreviewPanel.tsx) acts as the rendering dispatcher. It reads the active tab’s `contentType` and executes a large `renderContent` switch statement to mount the appropriate viewer:

- **Markdown** → `MarkdownViewer` or `MarkdownEditor` (with optional split-screen)
- **HTML** → `HTMLRenderer` or `HTMLEditor`
- **Code** → `CodePreview` (read-only) or `TextEditor` (edit mode)
- **PDF/Office** → `PDFViewer`, `PPTViewer`, `WordViewer`, `ExcelViewer`
- **Image** → `ImageViewer` (handles Base64 or file-system paths)
- **URL** → `URLViewer` (iframe-based web preview)

The panel also integrates `PreviewToolbar` for actions like download, open-in-system, split-toggle, and history navigation, plus `PreviewTabs` for multi-file management.

### IPC and Event Bridge

To allow the main process, background agents, or other UI parts to trigger previews without direct context access, the system uses two channels:

1. **Internal Emitter** – [`src/renderer/utils/emitter.ts`](https://github.com/iOfficeAI/AionUi/blob/main/src/renderer/utils/emitter.ts) broadcasts `preview.open` events within the renderer.
2. **IPC Bridge** – `ipcBridge.preview.open` handles cross-process requests.

`PreviewContext` registers listeners for both channels (lines 492–505 in [`PreviewContext.tsx`](https://github.com/iOfficeAI/AionUi/blob/main/PreviewContext.tsx)) and forwards payloads to the internal `openPreview` method, ensuring a unified entry point regardless of the request origin.

## End-to-End Lifecycle

Understanding the AionUi AI file preview flow requires tracing a file from trigger to render:

1. **Trigger** – A UI component (e.g., file list) calls `usePreviewContext().openPreview(content, type, metadata)`, or an AI agent emits `ipcBridge.preview.open` from the main process.

2. **Tab Management** – `openPreview` checks for existing tabs via `findPreviewTabInList`. If found, it activates that tab; otherwise, it creates a new tab with a unique ID, title derived from `fileName`, and the supplied content.

3. **State Persistence** – The new tab is added to the `tabs` array, `activeTabId` is updated, and `isOpen` is set to `true`. The entire state snapshot is saved to `localStorage` under `aionui_preview_state`.

4. **Rendering** – `PreviewPanel` detects the active tab’s `contentType` and mounts the corresponding viewer component (e.g., `MarkdownViewer` for `.md` files).

5. **Toolbar Actions** – Users can download files (via `Blob` construction or `ipcBridge.fs.getImageBase64`), open files in the system default app (`ipcBridge.shell.openFile`), or navigate history (`usePreviewHistory`).

6. **Live Updates** – The context subscribes to `ipcBridge.fileStream.contentUpdate`. Incoming writes are debounced (500ms) per file path to prevent UI flicker during LLM streaming. Delete operations immediately close the associated tab.

7. **Editing & Saving** – When users edit content, `saveContent` writes back to the workspace via `ipcBridge.fs.writeFile`. A guard (`savingFilesRef`) prevents the debounced stream listener from overwriting user edits mid-save.

8. **Cleanup** – Closing a tab removes it from the `tabs` array. If the last tab closes, the panel collapses (`setIsOpen(false)`). Layout state (split ratios) persists via `useResizableSplit` in `localStorage`.

## Code Examples

### Opening a Preview from Any UI Component

Components interact with the preview system through the React context hook:

```tsx
import { usePreviewContext } from '@/renderer/pages/conversation/preview';
import { getContentTypeByExtension } from '@/renderer/pages/conversation/preview/utils/fileUtils';

function FileItem({ filePath, fileName }: { filePath: string; fileName: string }) {
  const { openPreview } = usePreviewContext();

  const handleClick = async () => {
    // Read file via IPC bridge
    const { data } = await ipcBridge.fs.readFile.invoke({ path: filePath });
    const type = getContentTypeByExtension(fileName);
    
    openPreview(data, type, { 
      filePath, 
      fileName, 
      editable: true 
    });
  };

  return <div onClick={handleClick}>{fileName}</div>;
}

```

*Source:* `openPreview` implementation in [`PreviewContext.tsx`](https://github.com/iOfficeAI/AionUi/blob/main/PreviewContext.tsx) (lines 52–66).

### Receiving a Preview Request from the Backend

AI agents running in the main process can trigger previews without direct React context access:

```ts
// Main process or agent worker
ipcBridge.preview.open.send({
  content: generatedMarkdown,
  contentType: 'markdown',
  metadata: { 
    fileName: 'agent-output.md', 
    editable: false 
  }
});

```

The `PreviewContext` registers listeners for both internal emitter events and IPC bridge messages (lines 492–505), forwarding all requests to the unified `openPreview` handler.

### Determining Content Type from File Name

The utility layer maps extensions to viewer types:

```ts
import { getContentTypeByExtension } from '@/renderer/pages/conversation/preview/utils/fileUtils';

const type = getContentTypeByExtension('report.pdf'); // → 'pdf'
const imageType = getContentTypeByExtension('chart.png'); // → 'image'

```

*Source:* `getContentTypeByExtension` in [`fileUtils.ts`](https://github.com/iOfficeAI/AionUi/blob/main/fileUtils.ts) (lines 70–83).

### Adding a Custom Viewer

Extending the preview system to support new formats requires three steps:

1. **Map the extension** in `FILE_EXTENSION_MAP` inside [`fileUtils.ts`](https://github.com/iOfficeAI/AionUi/blob/main/fileUtils.ts).
2. **Create the viewer component** (e.g., [`CSVViewer.tsx`](https://github.com/iOfficeAI/AionUi/blob/main/CSVViewer.tsx)).
3. **Extend the render switch** in [`PreviewPanel.tsx`](https://github.com/iOfficeAI/AionUi/blob/main/PreviewPanel.tsx):

```tsx
// Inside PreviewPanel.tsx renderContent method
else if (contentType === 'csv') {
  return (
    <CSVViewer 
      content={content} 
      filePath={metadata?.filePath} 
    />
  );
}

```

*Source:* `renderContent` switch in [`PreviewPanel.tsx`](https://github.com/iOfficeAI/AionUi/blob/main/PreviewPanel.tsx) (lines 66–80).

## Key Implementation Files

| File | Role | Link |
|------|------|------|
| [`src/renderer/pages/conversation/preview/context/PreviewContext.tsx`](https://github.com/iOfficeAI/AionUi/blob/main/src/renderer/pages/conversation/preview/context/PreviewContext.tsx) | Central React context; manages tabs, persistence, IPC listeners, and state mutations | [PreviewContext.tsx](https://github.com/iOfficeAI/AionUi/blob/main/src/renderer/pages/conversation/preview/context/PreviewContext.tsx) |
| [`src/renderer/pages/conversation/preview/utils/fileUtils.ts`](https://github.com/iOfficeAI/AionUi/blob/main/src/renderer/pages/conversation/preview/utils/fileUtils.ts) | Extension mapping, content-type detection, file classification helpers | [fileUtils.ts](https://github.com/iOfficeAI/AionUi/blob/main/src/renderer/pages/conversation/preview/utils/fileUtils.ts) |
| [`src/renderer/pages/conversation/preview/components/PreviewPanel/PreviewPanel.tsx`](https://github.com/iOfficeAI/AionUi/blob/main/src/renderer/pages/conversation/preview/components/PreviewPanel/PreviewPanel.tsx) | Main container; handles viewer selection, toolbar integration, and debounced updates | [PreviewPanel.tsx](https://github.com/iOfficeAI/AionUi/blob/main/src/renderer/pages/conversation/preview/components/PreviewPanel/PreviewPanel.tsx) |
| `src/renderer/pages/conversation/preview/components/viewers/*` | Specialized viewers for markdown, HTML, code, PDF, Office formats, images, and URLs | [viewers folder](https://github.com/iOfficeAI/AionUi/tree/main/src/renderer/pages/conversation/preview/components/viewers) |
| [`src/renderer/pages/conversation/preview/hooks/usePreviewHistory.ts`](https://github.com/iOfficeAI/AionUi/blob/main/src/renderer/pages/conversation/preview/hooks/usePreviewHistory.ts) | Snapshot history management via main-process IPC | [usePreviewHistory.ts](https://github.com/iOfficeAI/AionUi/blob/main/src/renderer/pages/conversation/preview/hooks/usePreviewHistory.ts) |
| [`src/common/types/preview.ts`](https://github.com/iOfficeAI/AionUi/blob/main/src/common/types/preview.ts) | Shared TypeScript definitions for content types and metadata | [preview.ts](https://github.com/iOfficeAI/AionUi/blob/main/src/common/types/preview.ts) |
| [`src/renderer/utils/emitter.ts`](https://github.com/iOfficeAI/AionUi/blob/main/src/renderer/utils/emitter.ts) | Internal event emitter for renderer-side preview triggers | [emitter.ts](https://github.com/iOfficeAI/AionUi/blob/main/src/renderer/utils/emitter.ts) |
| [`src/renderer/hooks/useResizableSplit.ts`](https://github.com/iOfficeAI/AionUi/blob/main/src/renderer/hooks/useResizableSplit.ts) | Layout persistence for split-screen editing modes | [useResizableSplit.ts](https://github.com/iOfficeAI/AionUi/blob/main/src/renderer/hooks/useResizableSplit.ts) |

## Summary

- **AionUi AI file preview** is implemented as a self-contained React module using a centralized `PreviewContext` for state management and persistence.
- The system supports **multiple content types** (markdown, HTML, code, PDF, Office docs, images, URLs) through a utility-based extension mapping in [`fileUtils.ts`](https://github.com/iOfficeAI/AionUi/blob/main/fileUtils.ts).
- **Dual entry points** allow previews to be triggered from UI components via React hooks or from AI agents via IPC bridges and internal event emitters.
- **Live streaming** from background agents is handled through debounced updates (500ms) to prevent UI flicker, with guards to prevent overwriting user edits during save operations.
- **Full persistence** across sessions is achieved via `localStorage` for tab state and split-screen ratios, while `usePreviewHistory` enables snapshot versioning through the main process.

## Frequently Asked Questions

### What file types does AionUi AI file preview support?

The preview system supports markdown, HTML, plain text and code files, PDFs, Microsoft Office documents (Word, Excel, PowerPoint), common image formats (PNG, JPG, SVG), and external URLs. The `getContentTypeByExtension` function in [`fileUtils.ts`](https://github.com/iOfficeAI/AionUi/blob/main/fileUtils.ts) maps file extensions to these `PreviewContentType` values, allowing the `PreviewPanel` to select the appropriate viewer component.

### How does AionUi handle live updates from AI agents?

When an AI agent writes or modifies a file, the main process emits `ipcBridge.fileStream.contentUpdate` events to the renderer. The `PreviewContext` subscribes to these events and applies debouncing (500ms per file path) to batch rapid changes and prevent UI flicker. A `savingFilesRef` guard ensures that incoming stream updates do not overwrite content while the user is actively saving edits via `ipcBridge.fs.writeFile`.

### Can users edit files directly in the preview panel?

Yes, the preview panel supports inline editing for text-based formats including markdown, HTML, and code files. When `openPreview` is called with `editable: true`, the `PreviewPanel` renders the editor variant of the viewer (e.g., `MarkdownEditor` instead of `MarkdownViewer`). Changes are persisted to disk via the `saveContent` method in `PreviewContext`, which invokes `ipcBridge.fs.writeFile` and temporarily blocks stream updates during the save operation.

### How is the preview state persisted across sessions?

The `PreviewContext` automatically serializes the entire tab state—including open tabs, active tab ID, and metadata—to `localStorage` under the key `aionui_preview_state` whenever mutations occur. While the panel defaults to closed on application restart, the tab list is restored immediately. Additionally, `useResizableSplit` persists split-screen ratios and toolbar configurations to `localStorage`, ensuring the layout remains consistent between sessions.