Skip to content

Commit 8c87be2

Browse files
feat: optimize demo page
1 parent 12887db commit 8c87be2

File tree

14 files changed

+1322
-80
lines changed

14 files changed

+1322
-80
lines changed

apps/demo/package.json

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,20 @@
55
"type": "module",
66
"scripts": {
77
"dev": "vite",
8-
"build": "vite build",
9-
"typecheck": "tsc -p tsconfig.json --noEmit",
10-
"lint": "echo 'No lint configured yet'",
11-
"test": "echo 'No tests yet'"
8+
"build": "tsc -b && vite build",
9+
"preview": "vite preview"
1210
},
1311
"dependencies": {
14-
"react": "^18.3.1",
15-
"react-dom": "^18.3.1",
12+
"react": "^19.0.0",
13+
"react-dom": "^19.0.0",
14+
"react-router-dom": "^7.0.0",
1615
"react-virtualized-diff": "workspace:*"
1716
},
1817
"devDependencies": {
19-
"@types/react": "^18.3.12",
20-
"@types/react-dom": "^18.3.1",
21-
"@vitejs/plugin-react": "^4.3.4",
22-
"typescript": "^5.8.2",
23-
"vite": "^6.2.2"
18+
"@types/react": "^19.0.0",
19+
"@types/react-dom": "^19.0.0",
20+
"@vitejs/plugin-react": "^4.3.0",
21+
"typescript": "^5.6.0",
22+
"vite": "^5.4.0"
2423
}
2524
}

apps/demo/src/App.tsx

Lines changed: 8 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,12 @@
1-
import React from 'react';
2-
import { DiffViewer } from 'react-virtualized-diff';
1+
import { Routes, Route } from 'react-router-dom';
2+
import HomePage from './pages/HomePage';
3+
import DemoPage from './pages/DemoPage';
34

4-
const original = `import React from 'react';
5-
6-
export function hello() {
7-
return 'hello';
8-
}
9-
10-
export function sum(a: number, b: number) {
11-
return a + b;
12-
}
13-
14-
// unchanged line 1
15-
// unchanged line 2
16-
// unchanged line 3
17-
// unchanged line 4
18-
// unchanged line 5
19-
`;
20-
21-
const modified = `import React from 'react';
22-
23-
export function hello() {
24-
return 'hello world';
25-
}
26-
27-
export function sum(a: number, b: number) {
28-
return a + b + 1;
29-
}
30-
31-
// unchanged line 1
32-
// unchanged line 2
33-
// unchanged line 3
34-
// unchanged line 4
35-
// unchanged line 5
36-
`;
37-
38-
export default function App(): React.JSX.Element {
5+
export default function App() {
396
return (
40-
<div
41-
style={{
42-
maxWidth: 1200,
43-
margin: '40px auto',
44-
padding: '0 16px',
45-
}}
46-
>
47-
<h1>virtualized-diff-viewer demo</h1>
48-
<p>A high-performance React diff viewer for large files.</p>
49-
50-
<DiffViewer
51-
original={original}
52-
modified={modified}
53-
contextLines={2}
54-
height={500}
55-
/>
56-
</div>
7+
<Routes>
8+
<Route path="/" element={<HomePage />} />
9+
<Route path="/demo" element={<DemoPage />} />
10+
</Routes>
5711
);
5812
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
const FEATURES = [
2+
{
3+
title: 'Virtualized rendering',
4+
description:
5+
'Keeps rendering smooth by only mounting the visible diff rows instead of the entire file.',
6+
},
7+
{
8+
title: 'Large file friendly',
9+
description:
10+
'Built for scenarios where traditional diff viewers start to struggle with line count and memory pressure.',
11+
},
12+
{
13+
title: 'Developer focused API',
14+
description:
15+
'Designed for React integration, so it is easy to plug into internal tools, editors, or review workflows.',
16+
},
17+
{
18+
title: 'Interactive playground',
19+
description:
20+
'Try different file sizes, viewport heights, and context settings from the demo page.',
21+
},
22+
];
23+
24+
export default function FeatureGrid() {
25+
return (
26+
<section className="feature-section">
27+
<div className="page-shell">
28+
<div className="section-heading">
29+
<h2>Why this exists</h2>
30+
<p>
31+
Standard diff experiences get rough when the input becomes large. This project focuses
32+
on keeping the UI responsive while still showing meaningful context.
33+
</p>
34+
</div>
35+
36+
<div className="feature-grid">
37+
{FEATURES.map((feature) => (
38+
<article key={feature.title} className="feature-card">
39+
<h3>{feature.title}</h3>
40+
<p>{feature.description}</p>
41+
</article>
42+
))}
43+
</div>
44+
</div>
45+
</section>
46+
);
47+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { Link } from 'react-router-dom';
2+
3+
export default function HeroSection() {
4+
return (
5+
<section className="hero">
6+
<div className="page-shell hero__inner">
7+
<div className="hero__badge">Virtualized diff viewer for large text files</div>
8+
9+
<h1 className="hero__title">Render large diffs without making the browser cry</h1>
10+
11+
<p className="hero__description">
12+
A high-performance React diff viewer designed for large text comparison. It focuses on
13+
virtualization, responsive rendering, and an experience that still feels usable when the
14+
file size gets serious.
15+
</p>
16+
17+
<div className="hero__actions">
18+
<Link to="/demo" className="button button--primary">
19+
Open live demo
20+
</Link>
21+
<a
22+
className="button button--secondary"
23+
href="https://github.com/Zhang-JiahangH/react-virtualized-diff"
24+
target="_blank"
25+
rel="noreferrer"
26+
>
27+
View on GitHub
28+
</a>
29+
</div>
30+
</div>
31+
</section>
32+
);
33+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
type MetricsBarProps = {
2+
lineCount: number;
3+
contextLines: number;
4+
height: number;
5+
prepareTime: number | null;
6+
commitTime: number | null;
7+
totalTime: number | null;
8+
};
9+
10+
function formatDuration(value: number | null): string {
11+
if (value === null) {
12+
return '--';
13+
}
14+
15+
if (value >= 1000) {
16+
return `${(value / 1000).toFixed(2)} s`;
17+
}
18+
19+
return `${value.toFixed(1)} ms`;
20+
}
21+
22+
export default function MetricsBar(props: MetricsBarProps) {
23+
const { lineCount, contextLines, height, prepareTime, commitTime, totalTime } = props;
24+
25+
return (
26+
<section className="metrics-bar">
27+
<div className="metric-card">
28+
<span className="metric-card__label">Lines</span>
29+
<strong className="metric-card__value">{lineCount.toLocaleString()}</strong>
30+
</div>
31+
32+
<div className="metric-card">
33+
<span className="metric-card__label">Context</span>
34+
<strong className="metric-card__value">{contextLines}</strong>
35+
</div>
36+
37+
<div className="metric-card">
38+
<span className="metric-card__label">Height</span>
39+
<strong className="metric-card__value">{height}px</strong>
40+
</div>
41+
42+
<div className="metric-card">
43+
<span className="metric-card__label">Prepare</span>
44+
<strong className="metric-card__value">{formatDuration(prepareTime)}</strong>
45+
</div>
46+
47+
<div className="metric-card">
48+
<span className="metric-card__label">Commit</span>
49+
<strong className="metric-card__value">{formatDuration(commitTime)}</strong>
50+
</div>
51+
52+
<div className="metric-card">
53+
<span className="metric-card__label">Total</span>
54+
<strong className="metric-card__value">{formatDuration(totalTime)}</strong>
55+
</div>
56+
</section>
57+
);
58+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { DATASET_OPTIONS, type DatasetKey } from '../data/presets';
2+
3+
export type DemoState = {
4+
dataset: DatasetKey;
5+
contextLines: number;
6+
height: number;
7+
};
8+
9+
type PlaygroundControlsProps = {
10+
value: DemoState;
11+
onChange: (nextValue: DemoState) => void;
12+
onRunStressTest: () => void;
13+
};
14+
15+
export default function PlaygroundControls(props: PlaygroundControlsProps) {
16+
const { value, onChange, onRunStressTest } = props;
17+
18+
return (
19+
<aside className="control-panel">
20+
<div className="control-panel__section">
21+
<label className="field-label" htmlFor="dataset">
22+
Dataset
23+
</label>
24+
<select
25+
id="dataset"
26+
className="field-input"
27+
value={value.dataset}
28+
onChange={(event) =>
29+
onChange({
30+
...value,
31+
dataset: event.target.value as DatasetKey,
32+
})
33+
}
34+
>
35+
{DATASET_OPTIONS.map((option) => (
36+
<option key={option.key} value={option.key}>
37+
{option.label}
38+
</option>
39+
))}
40+
</select>
41+
</div>
42+
43+
<div className="control-panel__section">
44+
<label className="field-label" htmlFor="contextLines">
45+
Context lines: {value.contextLines}
46+
</label>
47+
<input
48+
id="contextLines"
49+
className="field-range"
50+
type="range"
51+
min={0}
52+
max={20}
53+
step={1}
54+
value={value.contextLines}
55+
onChange={(event) =>
56+
onChange({
57+
...value,
58+
contextLines: Number(event.target.value),
59+
})
60+
}
61+
/>
62+
</div>
63+
64+
<div className="control-panel__section">
65+
<label className="field-label" htmlFor="height">
66+
Viewer height: {value.height}px
67+
</label>
68+
<input
69+
id="height"
70+
className="field-range"
71+
type="range"
72+
min={240}
73+
max={960}
74+
step={40}
75+
value={value.height}
76+
onChange={(event) =>
77+
onChange({
78+
...value,
79+
height: Number(event.target.value),
80+
})
81+
}
82+
/>
83+
</div>
84+
85+
<button type="button" className="button button--primary button--block" onClick={onRunStressTest}>
86+
Run 100k stress test
87+
</button>
88+
</aside>
89+
);
90+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { Link, NavLink } from 'react-router-dom';
2+
3+
export default function SiteHeader() {
4+
return (
5+
<header className="site-header">
6+
<div className="page-shell site-header__inner">
7+
<Link to="/" className="site-header__brand">
8+
react-virtualized-diff
9+
</Link>
10+
11+
<nav className="site-header__nav">
12+
<NavLink
13+
to="/"
14+
end
15+
className={({ isActive }) =>
16+
isActive ? 'site-header__link site-header__link--active' : 'site-header__link'
17+
}
18+
>
19+
Home
20+
</NavLink>
21+
<NavLink
22+
to="/demo"
23+
className={({ isActive }) =>
24+
isActive ? 'site-header__link site-header__link--active' : 'site-header__link'
25+
}
26+
>
27+
Demo
28+
</NavLink>
29+
<a
30+
className="site-header__link"
31+
href="https://github.com/Zhang-JiahangH/react-virtualized-diff"
32+
target="_blank"
33+
rel="noreferrer"
34+
>
35+
GitHub
36+
</a>
37+
</nav>
38+
</div>
39+
</header>
40+
);
41+
}

0 commit comments

Comments
 (0)