From cc07c84a31b78cee1a825d29c41065526837bfee Mon Sep 17 00:00:00 2001 From: Vance Ingalls Date: Thu, 9 Jul 2026 15:55:16 -0700 Subject: [PATCH] feat(studio): add HDR warning banner and Apply-to-scope row, completing FlatColorGradingSection Also adds a not-busy click assertion for the Apply button so onApplyToScope is exercised end-to-end, not just its disabled state. Co-Authored-By: Claude Sonnet 5 --- ...pertyPanelFlatColorGradingSection.test.tsx | 56 +++++++++++++++++ .../propertyPanelFlatColorGradingSection.tsx | 60 +++++++++++++++++-- 2 files changed, 110 insertions(+), 6 deletions(-) diff --git a/packages/studio/src/components/editor/propertyPanelFlatColorGradingSection.test.tsx b/packages/studio/src/components/editor/propertyPanelFlatColorGradingSection.test.tsx index 6a0ead5b0..a1ee58b82 100644 --- a/packages/studio/src/components/editor/propertyPanelFlatColorGradingSection.test.tsx +++ b/packages/studio/src/components/editor/propertyPanelFlatColorGradingSection.test.tsx @@ -370,3 +370,59 @@ describe("FlatColorGradingSection — Effects", () => { act(() => root.unmount()); }); }); + +describe("FlatColorGradingSection — HDR banner and Apply scope", () => { + it("shows the HDR banner only when mediaMetadata reports an HDR source", () => { + const { host, root } = renderInto( + , + ); + expect(host.textContent).toContain("SDR preview"); + act(() => root.unmount()); + }); + + it("omits the HDR banner for SDR media", () => { + const { host, root } = renderInto( + , + ); + expect(host.textContent).not.toContain("SDR preview"); + act(() => root.unmount()); + }); + + it("fires onApplyToScope from the Apply button, respecting applyBusy", () => { + const onApplyToScope = vi.fn(); + const { host, root } = renderInto( + , + ); + const applyButton = host.querySelector('[data-flat-grade-apply="true"]'); + expect(applyButton?.disabled).toBe(true); + act(() => root.unmount()); + }); + + it("fires onApplyToScope exactly once when the Apply button is clicked while not busy", () => { + const onApplyToScope = vi.fn(); + const { host, root } = renderInto( + , + ); + const applyButton = host.querySelector('[data-flat-grade-apply="true"]'); + expect(applyButton?.disabled).toBe(false); + act(() => applyButton?.dispatchEvent(new MouseEvent("click", { bubbles: true }))); + expect(onApplyToScope).toHaveBeenCalledTimes(1); + act(() => root.unmount()); + }); +}); diff --git a/packages/studio/src/components/editor/propertyPanelFlatColorGradingSection.tsx b/packages/studio/src/components/editor/propertyPanelFlatColorGradingSection.tsx index b325ae590..be8e21fca 100644 --- a/packages/studio/src/components/editor/propertyPanelFlatColorGradingSection.tsx +++ b/packages/studio/src/components/editor/propertyPanelFlatColorGradingSection.tsx @@ -138,18 +138,39 @@ const EFFECT_SLIDERS: Array<{ key: HfColorGradingEffectKey; label: string }> = [ { key: "pixelate", label: "Pixelate" }, ]; +function HdrBanner({ metadata }: { metadata: MediaMetadata | null }) { + if (metadata?.color.dynamicRange !== "hdr") return null; + return ( +
+
+ {metadata.color.label} source + + SDR preview + +
+

+ These controls use the current SDR shader preview path. Render may stay HDR-tagged, but this + is not true HDR color grading yet. +

+
+ ); +} + // fallow-ignore-next-line complexity export function FlatColorGradingSection({ grading, assets, onImportAssets, onCommitColorGrading, - applyScope: _applyScope, - applyBusy: _applyBusy, - onSetApplyScope: _onSetApplyScope, - onApplyToScope: _onApplyToScope, - onApplyScopeAvailable: _onApplyScopeAvailable, - mediaMetadata: _mediaMetadata, + applyScope, + applyBusy, + onSetApplyScope, + onApplyToScope, + onApplyScopeAvailable, + mediaMetadata, }: { grading: NormalizedHfColorGrading; assets: string[]; @@ -218,6 +239,7 @@ export function FlatColorGradingSection({ return (
+
Preset + + {onApplyScopeAvailable && ( +
+ + Copy grade to + + + +
+ )}
); }