react-reviso
v0.3.2
Published
React component for document restoration QA, review, and correction
Readme
react-reviso
An embeddable React component for reviewing and correcting OCR text on restored document images. Designed for QA workflows — users preview restored documents, validate regions, compare original vs restored, and edit corrections when needed.
Features
- Preview-First UX — default view shows restored documents for QA review, editing is entered explicitly
- Side-by-Side Comparison — original (left) vs restored (right) with independent zoom/pan
- Slider Comparison — drag slider overlay comparing original vs restored, horizontal or vertical orientation
- Region Validation — click checkmarks to mark regions as validated; progress bar tracks review completion
- Inline Editing — click a region to edit text, Tab/Shift+Tab to navigate between regions
- Region Management — create, resize, move, delete text regions; customise font, color, border, background, text position
- Auto Background Detection — restored preview automatically detects and fills region backgrounds from the document image
- Export — JSON (structured data), PDF (text at original positions), PNG (restored page render)
- Undo/Redo — Ctrl+Z / Ctrl+Shift+Z with full snapshot history
- Fit to View — reset zoom across all view modes from toolbar
- Theme Integration — inherits host app's MUI theme, accepts theme overrides
- Feature Toggles — enable/disable editing, region creation, comparison, export via props
- Keyboard Shortcuts — press
?to see all available shortcuts
Workflow
- Preview (default) — review restored documents in side-by-side or slider comparison mode
- Validate — click checkmarks on regions to confirm OCR corrections are accurate
- Edit (on demand) — enter edit mode to fix text, create/delete regions, adjust styles
- Export — download corrected document as JSON, PDF, or PNG
Installation
npm install react-revisoPeer dependencies
npm install react react-dom @mui/material @mui/icons-material @emotion/react @emotion/styled framer-motionUsage
import { Reviso } from 'react-reviso';
import type { RevisoDocument } from 'react-reviso';
const document: RevisoDocument = {
id: 'doc-1',
name: 'My Document',
pages: [
{
id: 'page-1',
pageNumber: 1,
imageSrc: '/path/to/restored-image.png',
originalImageSrc: '/path/to/original-image.png',
width: 1200,
height: 1600,
regions: [
{
id: 'region-1',
x: 100,
y: 200,
width: 300,
height: 40,
text: 'Corrected text',
originalText: 'Original OCR text',
},
],
},
],
};
function App() {
return (
<Reviso
document={document}
onChange={(dirtyPages) => console.log('Dirty pages:', dirtyPages)}
onPageChange={(pageId) => console.log('Page:', pageId)}
onSelectionChange={(regionId) => console.log('Selection:', regionId)}
/>
);
}Theming
Reviso automatically inherits the MUI theme from a parent ThemeProvider. You can also pass a theme prop for component-level overrides — these are deep-merged on top of the inherited theme.
Option 1: Inherit from host app theme
Wrap your app (or a parent component) with MUI's ThemeProvider. Reviso picks up the palette, typography, and other tokens automatically.
import { ThemeProvider, createTheme } from '@mui/material/styles';
const appTheme = createTheme({
palette: {
mode: 'dark',
primary: { main: '#90caf9' },
},
});
function App() {
return (
<ThemeProvider theme={appTheme}>
<Reviso document={document} />
</ThemeProvider>
);
}Option 2: Override via theme prop
Pass MUI ThemeOptions directly to the theme prop. These overrides are deep-merged on top of whatever theme Reviso inherits from the parent.
<Reviso
document={document}
theme={{
palette: {
mode: 'dark',
primary: { main: '#ce93d8' },
background: { default: '#1a1a2e', paper: '#16213e' },
},
typography: {
fontFamily: '"Fira Code", monospace',
},
}}
/>Option 3: Combine both
Use a host theme for global styles and the theme prop for Reviso-specific tweaks.
const appTheme = createTheme({
palette: { mode: 'dark' },
typography: { fontFamily: '"Inter", sans-serif' },
});
<ThemeProvider theme={appTheme}>
<Reviso
document={document}
theme={{
palette: {
primary: { main: '#ff7043' },
},
}}
/>
</ThemeProvider>In this example, Reviso uses the host's dark mode and Inter font, but overrides the primary color to deep orange.
Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| document | RevisoDocument | required | The document to display and review |
| editable | boolean | true | Enable/disable editing (when false, preview and validation only) |
| showSidebar | boolean | true | Show/hide page thumbnail sidebar |
| showToolbar | boolean | true | Show/hide the inline toolbar |
| features | { comparison?, export?, regionCreation? } | all true | Feature toggles |
| defaultRegionStyles | object | — | Default styles for new regions (see below) |
| theme | ThemeOptions | — | MUI theme overrides |
| initialPageId | string | first page | Initial page to display |
| onChange | (dirtyPages: RevisoPage[]) => void | — | Fired on any change, returns only modified pages |
| onRegionChange | (event) => void | — | Granular per-region change event ({ type, pageId, regionId, region? }) |
| onPageChange | (pageId: string) => void | — | Fired on page navigation |
| onSelectionChange | (regionId: string \| null) => void | — | Fired on region select/deselect |
| onExport | (format, data: Blob) => void | — | Intercept export (replaces auto-download) |
defaultRegionStyles
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| fontColor | string | '#1565c0' | Text color (blue) |
| fontFamily | string | 'Inter' | Font family |
| fontWeight | 'normal' \| 'bold' | 'normal' | Font weight |
| fontStyle | 'normal' \| 'italic' | 'normal' | Font style |
| textDecoration | 'none' \| 'line-through' | 'none' | Text decoration |
| borderColor | string | '#4caf50' | Region border color (green) |
| borderVisible | boolean | true | Show/hide region border |
| backgroundColor | string | 'transparent' | Region background fill |
| textPosition | 'inside' \| 'top' \| 'bottom' | 'top' | Where text renders relative to the region box |
RevisoRegion
| Property | Type | Required | Description |
|----------|------|----------|-------------|
| id | string | yes | Unique region identifier |
| x | number | yes | X position (pixels from left) |
| y | number | yes | Y position (pixels from top) |
| width | number | yes | Region width in pixels |
| height | number | yes | Region height in pixels |
| text | string | yes | Current text content |
| originalText | string | no | Original text (for diff tracking) |
| fontColor | string | no | Text color |
| fontFamily | string | no | Font family |
| fontWeight | 'normal' \| 'bold' | no | Font weight |
| fontStyle | 'normal' \| 'italic' | no | Font style |
| textDecoration | 'none' \| 'line-through' | no | Text decoration |
| borderColor | string | no | Border color |
| borderVisible | boolean | no | Show/hide border |
| backgroundColor | string | no | Background fill |
| textPosition | 'inside' \| 'top' \| 'bottom' \| 'left' \| 'right' | no | Text placement relative to region |
| isValidated | boolean | no | Whether the region has been reviewed and confirmed |
When does onChange fire?
onChange fires once per discrete user action — it does not fire continuously during drag operations. It returns only dirty (modified) pages, not the full document. A page is considered dirty if any region was edited, created, or deleted. Dirty flags are reset after each onChange call, so the same changes are not re-emitted.
| Action | When it fires | |--------|---------------| | Edit text | On commit (Enter, Tab, or blur — not on every keystroke) | | Move region | On mouse release (not during drag) | | Resize region | On mouse release (not during drag) | | Create region | When the new region is added | | Delete region | Immediately on delete | | Change style | Immediately on each change (bold, italic, color, text position, etc.) | | Toggle validation | Immediately when region checkmark is clicked | | Undo / Redo | Immediately on restore |
View Modes
Preview Mode (default)
The default landing view for QA review. Two sub-layouts:
- Side-by-Side — original image (left) + restored image (right) with independent zoom/pan. Validation checkmarks overlay the restored pane.
- Slider — single overlay with a draggable comparison slider. Supports horizontal (left/right) and vertical (top/bottom) orientation. No validation checkmarks in this mode.
Edit Mode
Entered via the "Edit" button in the toolbar or Ctrl+E. Full editing capabilities:
- Select, edit text, create/resize/delete regions
- Undo/redo, style controls, text visibility toggle
- Return to preview via the "Preview" button or
Ctrl+E
Keyboard Shortcuts
| Shortcut | Action |
|----------|--------|
| Ctrl+E | Toggle Preview / Edit mode |
| Escape | Exit edit mode (when nothing selected) |
| ← / → | Previous / Next page |
| PageUp / PageDown | Previous / Next page |
| Ctrl+↑ / Ctrl+↓ | Previous / Next document |
| Ctrl+Z | Undo |
| Ctrl+Shift+Z | Redo |
| N | Toggle create mode |
| Delete | Delete selected region |
| Tab / Shift+Tab | Next / Previous region |
| Enter | Confirm edit |
| ? | Show keyboard shortcuts help |
Development
Prerequisites
- Node.js >= 18
- npm >= 8
Setup
git clone https://github.com/zhernrong92/reviso.git
cd reviso
npm install
npm run devThe dev server runs two demo routes:
/— Legacy standalone demo with file upload, multi-document support/reviso— Embeddable component demo with a simulated host app layout
Commands
| Command | Description |
|---------|-------------|
| npm run dev | Start Vite dev server |
| npm run build | Production build (type-check + bundle) |
| npm run build:lib | Build library for publishing |
| npm run preview | Preview production build |
| npm run type-check | TypeScript type checking |
| npm run lint | ESLint check |
Building the Library
The library build is separate from the demo app build. It produces ESM (.mjs) and CommonJS (.cjs) bundles with TypeScript declarations.
# Build the library (outputs to dist/)
npm run build:libThis generates:
dist/index.mjs— ES moduledist/index.cjs— CommonJS moduledist/index.d.ts— TypeScript declarations
Peer dependencies (React, MUI, Emotion, Framer Motion) are not bundled — consumers must install them separately.
Testing Locally
To test the library in another project before publishing:
# 1. Build and pack
npm run build:lib
npm pack
# 2. In the consumer project, install from the tgz
npm install /path/to/react-reviso-x.x.x.tgzPublishing to npm
# 1. Login
npm login
# 2. Bump version
npm version patch --no-git-tag-version
# 3. Build and publish
npm run build:lib
npm publish --access publicTech Stack
| Technology | Purpose | |------------|---------| | React 18 | UI framework | | TypeScript 5 (strict) | Type safety | | Vite 5 | Dev server + library bundler | | MUI 6 | Component library, theming | | Zustand 5 + Immer | State management | | Framer Motion | Page transitions | | react-zoom-pan-pinch | Document viewer zoom/pan | | react-compare-slider | Before/after comparison slider | | pdf-lib | PDF export generation | | nanoid | Unique ID generation |
