How AionUi's Preview Panel Supports 10+ File Formats with Version History and Real-Time Editing
AionUi's preview panel combines file-type-specific renderers with a debounced snapshot system and split-screen editing to support Markdown, HTML, PDF, Office documents, and images with full version history and instant updates.
The AionUi repository implements a sophisticated preview architecture that transforms a simple file viewer into a comprehensive editing environment. The preview panel automatically selects appropriate renderers for over ten file formats while maintaining a complete version history through the IO⁺ service. This article examines the component structure, real-time editing mechanisms, and snapshot persistence that power this feature.
Architecture of the AionUi Preview Panel
The preview panel operates as a dynamic container that instantiates format-specific components based on content type detection.
File Type Detection and Component Selection
The PreviewPanel component in src/renderer/pages/conversation/preview/components/PreviewPanel/PreviewPanel.tsx acts as the central router for file rendering. It inspects the contentType property to determine which specialized viewer to mount:
- MarkdownPreview for
.mdfiles - HTMLRenderer for
.htmlcontent - PDFPreview for PDF documents
- WordPreview, ExcelPreview, PowerPointPreview for Office formats
- ImagePreview for visual assets
- URLViewer for web links
This modular approach allows the AionUi preview panel to extend support for new formats without refactoring the core container logic.
Split-Screen Layout for Real-Time Editing
Real-time editing relies on the useResizableSplit hook to create a draggable divider between editor and preview panes. When isSplitScreenEnabled is active, the panel renders side-by-side components:
// Inside PreviewPanel.tsx
if (isSplitScreenEnabled) {
return (
<div className="flex flex-1 relative overflow-hidden">
{/* Editor side */}
<div className="flex flex-col" style={{ width: `${splitRatio}%` }}>
<MarkdownEditor
value={content}
onChange={updateContent}
containerRef={editorContainerRef}
onScroll={handleEditorScroll}
/>
</div>
{/* Preview side */}
<div className="flex flex-col" style={{ width: `${100 - splitRatio}%` }}>
<MarkdownPreview
content={content}
filePath={metadata?.filePath}
/>
</div>
</div>
);
}
The updateContent function propagates changes immediately to the preview component, creating the real-time feedback loop that characterizes the AionUi editing experience.
Implementing Version History in AionUi
The IO⁺ version history system persists content snapshots through a combination of React hooks and IPC communication with the main process.
The usePreviewHistory Hook
Located in src/renderer/pages/conversation/preview/hooks/usePreviewHistory.ts, this hook encapsulates all history operations. It constructs a historyTarget object from the active tab's metadata to identify the resource being versioned:
const historyTarget = useMemo(() => {
if (!activeTab?.metadata) return null;
return {
id: activeTab.id,
type: activeTab.contentType,
path: activeTab.metadata.filePath,
};
}, [activeTab]);
The hook exposes three primary operations through the ipcBridge.previewHistory namespace:
list.invoke()retrieves all snapshots for the targetsave.invoke()persists current content as a new versiongetContent.invoke()retrieves specific snapshot data for restoration
Debounced Snapshot Mechanism
To prevent storage flooding during rapid editing, AionUi implements a debounce guard using lastSnapshotTimeRef and the SNAPSHOT_DEBOUNCE_TIME constant (default 1000ms):
const handleSaveSnapshot = useCallback(async () => {
if (!historyTarget || !activeTab) return;
if (snapshotSaving) return;
const now = Date.now();
if (now - lastSnapshotTimeRef.current < SNAPSHOT_DEBOUNCE_TIME) {
messageApi.info(t('preview.tooFrequent'));
return;
}
try {
setSnapshotSaving(true);
lastSnapshotTimeRef.current = now;
await ipcBridge.previewHistory.save.invoke({
target: historyTarget,
content: activeTab.content,
});
messageApi.success(t('preview.snapshotSaved'));
await refreshHistory();
} finally {
setSnapshotSaving(false);
}
}, [historyTarget, activeTab, snapshotSaving, messageApi, refreshHistory, t]);
The PreviewToolbar component surfaces this functionality through a "Save Snapshot" button, while the history list renders available versions for selection and restoration.
Real-Time Editing Workflow
The complete editing cycle demonstrates how AionUi synchronizes user input, preview updates, and version persistence:
- Activation: User toggles edit mode via
setIsEditMode(true)inPreviewPanel.tsx(line 73) - Layout: For code-type files, split-screen auto-enables via
setIsSplitScreenEnabled(true) - Input: Editor components (
MarkdownEditor,HTMLEditor, etc.) bindonChange={updateContent} - Propagation:
updateContentmodifies the active tab's content in the preview context, triggering immediate re-render of the preview pane (lines 69-73) - Persistence: The debounced snapshot mechanism ensures version history updates without blocking the UI thread
This architecture allows users to edit Markdown, HTML, and text files while viewing formatted output simultaneously, with every significant change eligible for version capture.
Key Source Files and Components
| File | Role |
|---|---|
src/renderer/pages/conversation/preview/components/PreviewPanel/PreviewPanel.tsx |
Core container managing file-type routing, split-screen layout, and editing state |
src/renderer/pages/conversation/preview/hooks/usePreviewHistory.ts |
Version history logic including debounced saves and snapshot retrieval |
src/renderer/pages/conversation/preview/components/Toolbar/PreviewToolbar.tsx |
Action buttons for edit mode, snapshot creation, and history refresh |
src/common/ipcBridge.ts |
IPC contract defining previewHistory.* channels for main-process communication |
src/renderer/pages/conversation/preview/constants.ts |
Configuration values including SNAPSHOT_DEBOUNCE_TIME |
These files collectively implement the IO⁺ standard for the AionUi preview panel, delivering format-agnostic viewing, real-time collaborative editing capabilities, and robust version persistence.
Summary
- AionUi's preview panel dynamically selects specialized renderers for 10+ file formats including Markdown, HTML, PDF, Office documents, and images through a modular component architecture in
PreviewPanel.tsx. - Real-time editing utilizes a resizable split-screen layout (
useResizableSplit) that synchronizes editor input with live preview updates via theupdateContentcallback. - Version history (IO⁺) persists through
usePreviewHistory, which manages snapshots via IPC bridges to the main process, implementing debounced saves to prevent storage flooding. - Snapshot restoration allows users to retrieve any historical version through
previewHistory.getContent.invokeand inject it back into the active editor session.
Frequently Asked Questions
How does AionUi determine which viewer to use for different file formats?
The PreviewPanel component inspects the contentType property of the active tab to select the appropriate renderer. It maintains a mapping that instantiates MarkdownPreview for Markdown files, PDFPreview for PDFs, WordPreview for DOCX files, and specialized components for Excel, PowerPoint, images, and URLs. This routing logic lives in src/renderer/pages/conversation/preview/components/PreviewPanel/PreviewPanel.tsx.
What prevents the version history from saving too frequently during rapid editing?
AionUi implements a debounce mechanism in the usePreviewHistory hook using a lastSnapshotTimeRef and the SNAPSHOT_DEBOUNCE_TIME constant (set to 1000ms by default). Before saving a snapshot, the system checks if the elapsed time since the last save exceeds this threshold. If the user attempts to save too frequently, the UI displays a toast notification indicating the action is rate-limited.
Can users restore previous versions without leaving the preview panel?
Yes, the version history system allows complete restoration within the panel interface. When a user selects a historical snapshot from the history list, the handleSnapshotSelect function in usePreviewHistory.ts calls ipcBridge.previewHistory.getContent.invoke to retrieve the snapshot data. It then uses the updateContent callback to inject the historical content directly into the active editor, updating both the editor state and live preview simultaneously.
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 →