fix(studio): clear stale color scopes

This commit is contained in:
ukimsanov
2026-07-26 01:29:32 -07:00
parent 20ef48abcb
commit c1fcf7534f
3 changed files with 46 additions and 1 deletions
@@ -189,7 +189,14 @@ export function PropertyPanelColorScopes({
});
useEffect(() => {
if (analysis && canvasRef.current) drawScope(canvasRef.current, mode, analysis);
const canvas = canvasRef.current;
if (!canvas) return;
if (analysis) {
drawScope(canvas, mode, analysis);
return;
}
const context = canvas.getContext("2d");
if (context) drawGrid(context, canvas.width, canvas.height);
}, [analysis, mode]);
return (
@@ -27,6 +27,7 @@ function Harness({
return (
<>
<span data-status>{scopes.status}</span>
<span data-analysis>{scopes.analysis ? "ready" : "empty"}</span>
<button type="button" onClick={scopes.refresh}>
Refresh
</button>
@@ -104,4 +105,39 @@ describe("useColorGradingScopes", () => {
expect(host.querySelector("[data-status]")?.textContent).toBe("unavailable");
act(() => root.unmount());
});
it("clears stale analysis while a refresh is pending and when it is unavailable", async () => {
vi.useFakeTimers();
installPixelDecode();
let resolveRefresh: (frame: ColorGradingCapturedFrame | null) => void = () => {};
const pendingRefresh = new Promise<ColorGradingCapturedFrame | null>((resolve) => {
resolveRefresh = resolve;
});
const captureFrame = vi
.fn()
.mockResolvedValueOnce({
dataUrl: "data:image/png;base64,scope",
width: 1,
height: 1,
})
.mockReturnValueOnce(pendingRefresh);
const host = document.body.appendChild(document.createElement("div"));
const root = createRoot(host);
act(() => root.render(<Harness open refreshKey="a" captureFrame={captureFrame} />));
await act(async () => vi.advanceTimersByTimeAsync(180));
expect(host.querySelector("[data-analysis]")?.textContent).toBe("ready");
act(() => {
host.querySelector("button")?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
});
act(() => vi.advanceTimersByTime(180));
expect(host.querySelector("[data-status]")?.textContent).toBe("loading");
expect(host.querySelector("[data-analysis]")?.textContent).toBe("empty");
await act(async () => resolveRefresh(null));
expect(host.querySelector("[data-status]")?.textContent).toBe("unavailable");
expect(host.querySelector("[data-analysis]")?.textContent).toBe("empty");
act(() => root.unmount());
});
});
@@ -26,11 +26,13 @@ export function useColorGradingScopes({
useEffect(() => {
if (!open) {
setAnalysis(null);
setStatus("idle");
return;
}
let cancelled = false;
const timer = window.setTimeout(async () => {
setAnalysis(null);
setStatus("loading");
try {
const frame = await captureRef.current();