Skip to content

Commit 6e213e3

Browse files
authored
Merge pull request #47 from FreeOps-Tools/dev
Dev
2 parents 01eb8c9 + 46b49b1 commit 6e213e3

10 files changed

Lines changed: 902 additions & 2516 deletions

File tree

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM node:18-bullseye
1+
FROM
22

33
WORKDIR /app
44

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import { useEffect, useMemo, useState } from "react";
2+
3+
const FRAME_COUNT = 6;
4+
const FRAME_INTERVAL = 1200;
5+
6+
function getStatusLabel(state) {
7+
switch (state) {
8+
case "loading":
9+
return "Loading…";
10+
case "ready":
11+
return "Rendered";
12+
case "error":
13+
return "Blocked";
14+
default:
15+
return "Queued";
16+
}
17+
}
18+
19+
function PreviewFrame({ url, index, delayMs }) {
20+
const [source, setSource] = useState("");
21+
const [state, setState] = useState("queued");
22+
const [startedAt, setStartedAt] = useState(null);
23+
const [loadedAt, setLoadedAt] = useState(null);
24+
25+
useEffect(() => {
26+
if (!url) {
27+
setSource("");
28+
setState("idle");
29+
setStartedAt(null);
30+
setLoadedAt(null);
31+
return;
32+
}
33+
34+
setState("queued");
35+
const timer = setTimeout(() => {
36+
setSource(url);
37+
setStartedAt(performance.now());
38+
setState("loading");
39+
}, delayMs);
40+
41+
return () => {
42+
clearTimeout(timer);
43+
setSource("");
44+
setState("queued");
45+
setStartedAt(null);
46+
setLoadedAt(null);
47+
};
48+
}, [url, delayMs]);
49+
50+
const handleLoad = () => {
51+
setState("ready");
52+
setLoadedAt(performance.now());
53+
};
54+
55+
const handleError = () => {
56+
setState("error");
57+
};
58+
59+
const deltaMs =
60+
startedAt && loadedAt ? Math.max(0, loadedAt - startedAt).toFixed(0) : null;
61+
62+
return (
63+
<div className={`preview-frame ${state}`}>
64+
{source ? (
65+
<iframe
66+
src={source}
67+
title={`preview-${index}`}
68+
onLoad={handleLoad}
69+
onError={handleError}
70+
sandbox="allow-same-origin allow-scripts allow-forms"
71+
loading="lazy"
72+
referrerPolicy="no-referrer"
73+
/>
74+
) : (
75+
<div className="preview-placeholder">Awaiting snapshot…</div>
76+
)}
77+
<footer>
78+
<div>
79+
<span>Frame {index + 1}</span>
80+
<small>{getStatusLabel(state)}</small>
81+
</div>
82+
<div className="latency-badge">
83+
{deltaMs ? `${deltaMs} ms` : "—"}
84+
</div>
85+
</footer>
86+
</div>
87+
);
88+
}
89+
90+
export default function PreviewGrid({ url }) {
91+
const frames = useMemo(
92+
() => Array.from({ length: FRAME_COUNT }, (_, idx) => idx),
93+
[]
94+
);
95+
96+
if (!url) {
97+
return null;
98+
}
99+
100+
return (
101+
<section className="preview-grid">
102+
<header>
103+
<div>
104+
<p className="eyebrow">Live stability preview</p>
105+
<h2>How does the page settle?</h2>
106+
</div>
107+
<p>
108+
We reload the site multiple times in quick succession to surface
109+
layout shifts, blocking assets, and other instability indicators.
110+
</p>
111+
</header>
112+
<div className="preview-grid__frames">
113+
{frames.map((idx) => (
114+
<PreviewFrame
115+
key={`${url}-${idx}`}
116+
url={url}
117+
index={idx}
118+
delayMs={idx * FRAME_INTERVAL}
119+
/>
120+
))}
121+
</div>
122+
</section>
123+
);
124+
}
125+

netlify.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[build]
2+
command = "npm run build"
3+
publish = "dist"
4+
5+
[build.environment]
6+
NODE_VERSION = "22"
7+
NPM_FLAGS = "--legacy-peer-deps"
8+

package-lock.json

Lines changed: 84 additions & 60 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,15 @@
1616
"axios": "^1.7.9",
1717
"react": "^19.0.0",
1818
"react-dom": "^19.0.0",
19-
"react-fetch-hook": "^1.9.5",
20-
"react-icons": "^5.4.0",
21-
"use-http": "^1.0.28"
19+
"react-icons": "^5.4.0"
2220
},
2321
"devDependencies": {
2422
"@types/react": "^19.0.7",
2523
"@types/react-dom": "^19.0.3",
2624
"@vitejs/plugin-react": "^4.3.4",
25+
"autoprefixer": "^10.4.21",
26+
"postcss": "^8.5.6",
27+
"tailwindcss": "^4.1.11",
2728
"vite": "^6.0.7"
28-
},
29-
"packageManager": "yarn@4.6.0+sha512.5383cc12567a95f1d668fbe762dfe0075c595b4bfff433be478dbbe24e05251a8e8c3eb992a986667c1d53b6c3a9c85b8398c35a960587fbd9fa3a0915406728"
29+
}
3030
}

0 commit comments

Comments
 (0)