How AionUi AI File Preview Works: A Technical Deep Dive
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. 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 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 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 →
MarkdownViewerorMarkdownEditor(with optional split-screen) - HTML →
HTMLRendererorHTMLEditor - Code →
CodePreview(read-only) orTextEditor(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:
- Internal Emitter –
src/renderer/utils/emitter.tsbroadcastspreview.openevents within the renderer. - IPC Bridge –
ipcBridge.preview.openhandles cross-process requests.
PreviewContext registers listeners for both channels (lines 492–505 in 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:
-
Trigger – A UI component (e.g., file list) calls
usePreviewContext().openPreview(content, type, metadata), or an AI agent emitsipcBridge.preview.openfrom the main process. -
Tab Management –
openPreviewchecks for existing tabs viafindPreviewTabInList. If found, it activates that tab; otherwise, it creates a new tab with a unique ID, title derived fromfileName, and the supplied content. -
State Persistence – The new tab is added to the
tabsarray,activeTabIdis updated, andisOpenis set totrue. The entire state snapshot is saved tolocalStorageunderaionui_preview_state. -
Rendering –
PreviewPaneldetects the active tab’scontentTypeand mounts the corresponding viewer component (e.g.,MarkdownViewerfor.mdfiles). -
Toolbar Actions – Users can download files (via
Blobconstruction oripcBridge.fs.getImageBase64), open files in the system default app (ipcBridge.shell.openFile), or navigate history (usePreviewHistory). -
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. -
Editing & Saving – When users edit content,
saveContentwrites back to the workspace viaipcBridge.fs.writeFile. A guard (savingFilesRef) prevents the debounced stream listener from overwriting user edits mid-save. -
Cleanup – Closing a tab removes it from the
tabsarray. If the last tab closes, the panel collapses (setIsOpen(false)). Layout state (split ratios) persists viauseResizableSplitinlocalStorage.
Code Examples
Opening a Preview from Any UI Component
Components interact with the preview system through the React context hook:
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 (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:
// 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:
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 (lines 70–83).
Adding a Custom Viewer
Extending the preview system to support new formats requires three steps:
- Map the extension in
FILE_EXTENSION_MAPinsidefileUtils.ts. - Create the viewer component (e.g.,
CSVViewer.tsx). - Extend the render switch in
PreviewPanel.tsx:
// Inside PreviewPanel.tsx renderContent method
else if (contentType === 'csv') {
return (
<CSVViewer
content={content}
filePath={metadata?.filePath}
/>
);
}
Source: renderContent switch in PreviewPanel.tsx (lines 66–80).
Key Implementation Files
| File | Role | Link |
|---|---|---|
src/renderer/pages/conversation/preview/context/PreviewContext.tsx |
Central React context; manages tabs, persistence, IPC listeners, and state mutations | PreviewContext.tsx |
src/renderer/pages/conversation/preview/utils/fileUtils.ts |
Extension mapping, content-type detection, file classification helpers | fileUtils.ts |
src/renderer/pages/conversation/preview/components/PreviewPanel/PreviewPanel.tsx |
Main container; handles viewer selection, toolbar integration, and debounced updates | PreviewPanel.tsx |
src/renderer/pages/conversation/preview/components/viewers/* |
Specialized viewers for markdown, HTML, code, PDF, Office formats, images, and URLs | viewers folder |
src/renderer/pages/conversation/preview/hooks/usePreviewHistory.ts |
Snapshot history management via main-process IPC | usePreviewHistory.ts |
src/common/types/preview.ts |
Shared TypeScript definitions for content types and metadata | preview.ts |
src/renderer/utils/emitter.ts |
Internal event emitter for renderer-side preview triggers | emitter.ts |
src/renderer/hooks/useResizableSplit.ts |
Layout persistence for split-screen editing modes | useResizableSplit.ts |
Summary
- AionUi AI file preview is implemented as a self-contained React module using a centralized
PreviewContextfor 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. - 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
localStoragefor tab state and split-screen ratios, whileusePreviewHistoryenables 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 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.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →