@hyperframes/studio 0.7.71 → 0.7.73
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{hyperframes-player-C1X90psg.js → hyperframes-player-DGlzhf_s.js} +1 -1
- package/dist/assets/index-B37rWXo4.css +1 -0
- package/dist/assets/{index-DzxDHPR1.js → index-CbVTOJ-E.js} +1 -1
- package/dist/assets/{index-nY4lLBqM.js → index-CcA5WVJv.js} +1 -1
- package/dist/assets/index-DXcLI2wu.js +428 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.html +2 -2
- package/dist/index.js +7762 -4261
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/src/App.tsx +4 -4
- package/src/components/StudioLeftSidebar.tsx +2 -3
- package/src/components/StudioRightPanel.tsx +10 -4
- package/src/components/editor/AnimationCard.test.tsx +100 -0
- package/src/components/editor/AnimationCard.tsx +5 -2
- package/src/components/editor/EaseCurveSection.test.tsx +348 -0
- package/src/components/editor/EaseCurveSection.tsx +444 -237
- package/src/components/editor/EaseParamFields.test.tsx +188 -0
- package/src/components/editor/EaseParamFields.tsx +246 -0
- package/src/components/editor/PropertyPanel.test.tsx +9 -7
- package/src/components/editor/PropertyPanelFlat.tsx +75 -88
- package/src/components/editor/colorGradingFrameAnalysis.test.ts +101 -0
- package/src/components/editor/colorGradingFrameAnalysis.ts +203 -0
- package/src/components/editor/colorGradingScopePatch.test.ts +3 -3
- package/src/components/editor/easeCurveSvg.tsx +65 -0
- package/src/components/editor/easePresetLibrary.test.ts +114 -0
- package/src/components/editor/easePresetLibrary.ts +49 -0
- package/src/components/editor/gsapAnimationConstants.ts +15 -13
- package/src/components/editor/propertyPanelColorCurveGraph.tsx +549 -0
- package/src/components/editor/propertyPanelColorCurves.test.tsx +158 -0
- package/src/components/editor/propertyPanelColorCurves.tsx +185 -0
- package/src/components/editor/propertyPanelColorGradingControls.tsx +115 -77
- package/src/components/editor/propertyPanelColorScopes.tsx +258 -0
- package/src/components/editor/propertyPanelColorSecondary.test.tsx +181 -0
- package/src/components/editor/propertyPanelColorSecondary.tsx +448 -0
- package/src/components/editor/propertyPanelColorWheels.test.tsx +119 -0
- package/src/components/editor/propertyPanelColorWheels.tsx +334 -0
- package/src/components/editor/propertyPanelFlatColorGradingAccessory.tsx +109 -0
- package/src/components/editor/propertyPanelFlatColorGradingSection.test.tsx +239 -41
- package/src/components/editor/propertyPanelFlatColorGradingSection.tsx +260 -300
- package/src/components/editor/propertyPanelFlatEffectControl.tsx +96 -0
- package/src/components/editor/propertyPanelFlatEffectSpecs.ts +305 -0
- package/src/components/editor/propertyPanelFlatEffectsSection.test.tsx +331 -0
- package/src/components/editor/propertyPanelFlatEffectsSection.tsx +503 -0
- package/src/components/editor/propertyPanelFlatOverlaysSection.test.tsx +113 -0
- package/src/components/editor/propertyPanelFlatOverlaysSection.tsx +108 -0
- package/src/components/editor/propertyPanelFlatPrimitives.tsx +2 -0
- package/src/components/editor/propertyPanelGradingNumberField.test.tsx +79 -0
- package/src/components/editor/propertyPanelGradingNumberField.tsx +116 -0
- package/src/components/editor/propertyPanelPresetPreview.ts +36 -0
- package/src/components/editor/propertyPanelTypes.ts +13 -0
- package/src/components/editor/useColorGradingController.test.ts +331 -10
- package/src/components/editor/useColorGradingController.ts +57 -90
- package/src/components/editor/useColorGradingPreviews.ts +350 -0
- package/src/components/editor/useColorGradingScopes.test.tsx +143 -0
- package/src/components/editor/useColorGradingScopes.ts +62 -0
- package/src/components/editor/useInspectorGestureTransaction.ts +30 -1
- package/src/components/nle/NLEContext.tsx +1 -1
- package/src/contexts/PanelLayoutContext.tsx +3 -6
- package/src/hooks/useBlockCatalog.ts +31 -13
- package/src/hooks/useBlockHandlers.ts +22 -0
- package/src/hooks/useGsapScriptCommits.test.tsx +15 -0
- package/src/hooks/useGsapScriptCommits.ts +14 -1
- package/src/hooks/usePanelLayout.test.ts +68 -1
- package/src/hooks/usePanelLayout.ts +72 -34
- package/src/icons/SystemIcons.tsx +4 -0
- package/src/player/components/PlayerControls.tsx +68 -229
- package/src/player/components/ShortcutsPanel.tsx +2 -4
- package/src/player/components/SpeedMenu.tsx +1 -2
- package/src/telemetry/events.test.ts +9 -0
- package/src/telemetry/events.ts +5 -0
- package/src/utils/blockInstaller.test.ts +67 -0
- package/src/utils/blockInstaller.ts +162 -109
- package/src/utils/studioUiPreferences.test.ts +5 -0
- package/src/utils/studioUiPreferences.ts +8 -0
- package/dist/assets/index-CSCh2Vrp.js +0 -428
- package/dist/assets/index-Ceyz8Qt2.css +0 -1
- package/src/player/components/PlayerControls.test.ts +0 -20
- package/src/player/components/useSeekBarDrag.ts +0 -169
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
// @vitest-environment happy-dom
|
|
2
|
+
|
|
3
|
+
import React, { act } from "react";
|
|
4
|
+
import { createRoot } from "react-dom/client";
|
|
5
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
6
|
+
import {
|
|
7
|
+
HF_COLOR_GRADING_ACTIVE_EFFECT_KEYS,
|
|
8
|
+
HF_COLOR_GRADING_EFFECT_APPLY_DEFAULTS,
|
|
9
|
+
HF_COLOR_GRADING_PALETTES,
|
|
10
|
+
getHfColorGradingCapabilities,
|
|
11
|
+
normalizeHfColorGrading,
|
|
12
|
+
} from "@hyperframes/core/color-grading";
|
|
13
|
+
import {
|
|
14
|
+
activeColorGradingEffectCount,
|
|
15
|
+
FlatEffectsAccessory,
|
|
16
|
+
FlatEffectsSection,
|
|
17
|
+
} from "./propertyPanelFlatEffectsSection";
|
|
18
|
+
import { EFFECT_SPECS } from "./propertyPanelFlatEffectSpecs";
|
|
19
|
+
|
|
20
|
+
(globalThis as unknown as { IS_REACT_ACT_ENVIRONMENT: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
|
21
|
+
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
document.body.innerHTML = "";
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
function neutralGrading() {
|
|
27
|
+
const grading = normalizeHfColorGrading("neutral");
|
|
28
|
+
if (!grading) throw new Error("expected neutral grading");
|
|
29
|
+
return grading;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function renderInto(node: React.ReactElement) {
|
|
33
|
+
const host = document.body.appendChild(document.createElement("div"));
|
|
34
|
+
const root = createRoot(host);
|
|
35
|
+
act(() => root.render(node));
|
|
36
|
+
return { host, root };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function sectionProps(
|
|
40
|
+
grading: ReturnType<typeof neutralGrading>,
|
|
41
|
+
onCommitColorGrading: ReturnType<typeof vi.fn> = vi.fn(),
|
|
42
|
+
) {
|
|
43
|
+
return {
|
|
44
|
+
grading,
|
|
45
|
+
previews: {
|
|
46
|
+
status: "ready" as const,
|
|
47
|
+
images: { pixelate: "data:image/png;base64,pixelate" },
|
|
48
|
+
width: 160,
|
|
49
|
+
height: 90,
|
|
50
|
+
},
|
|
51
|
+
presetPreviews: {
|
|
52
|
+
status: "ready" as const,
|
|
53
|
+
images: { "vhs-playback": "data:image/png;base64,vhs" },
|
|
54
|
+
width: 160,
|
|
55
|
+
height: 90,
|
|
56
|
+
},
|
|
57
|
+
onCommitColorGrading,
|
|
58
|
+
onPreviewColorGrading: vi.fn(),
|
|
59
|
+
onRequestEffectPreviews: vi.fn(),
|
|
60
|
+
onRequestPresetPreviews: vi.fn(),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
describe("FlatEffectsSection", () => {
|
|
65
|
+
it("places missing composite treatments in their effect family without a Styles section", () => {
|
|
66
|
+
const onCommit = vi.fn();
|
|
67
|
+
const props = sectionProps(neutralGrading(), onCommit);
|
|
68
|
+
const { host, root } = renderInto(<FlatEffectsSection {...props} />);
|
|
69
|
+
expect(host.textContent).not.toContain("Styles");
|
|
70
|
+
expect(props.onRequestPresetPreviews).not.toHaveBeenCalled();
|
|
71
|
+
const retro = host.querySelector<HTMLButtonElement>(
|
|
72
|
+
'[data-flat-effect-group="Retro & Glitch"]',
|
|
73
|
+
);
|
|
74
|
+
act(() => retro?.dispatchEvent(new MouseEvent("click", { bubbles: true })));
|
|
75
|
+
expect(props.onRequestPresetPreviews).toHaveBeenCalledTimes(1);
|
|
76
|
+
expect(host.querySelector('[data-flat-effect-preset-preview="vhs-playback"]')).not.toBeNull();
|
|
77
|
+
const vhs = host.querySelector<HTMLButtonElement>('[data-flat-effect-preset="vhs-playback"]');
|
|
78
|
+
act(() => vhs?.dispatchEvent(new MouseEvent("pointerover", { bubbles: true })));
|
|
79
|
+
expect(props.onPreviewColorGrading.mock.calls.at(-1)?.[0]).toMatchObject({
|
|
80
|
+
preset: "vhs-playback",
|
|
81
|
+
effects: { tapeDamage: 0.82, scanlineCount: 0.17 },
|
|
82
|
+
});
|
|
83
|
+
expect(onCommit).not.toHaveBeenCalled();
|
|
84
|
+
act(() => vhs?.dispatchEvent(new MouseEvent("click", { bubbles: true })));
|
|
85
|
+
expect(onCommit.mock.calls[0][0].preset).toBe("vhs-playback");
|
|
86
|
+
|
|
87
|
+
const print = host.querySelector<HTMLButtonElement>('[data-flat-effect-group="Print"]');
|
|
88
|
+
act(() => print?.dispatchEvent(new MouseEvent("click", { bubbles: true })));
|
|
89
|
+
expect(host.querySelector('[data-flat-effect-preset="editorial-halftone"]')).not.toBeNull();
|
|
90
|
+
expect(host.querySelector('[data-flat-effect-preset="two-ink-print"]')).not.toBeNull();
|
|
91
|
+
act(() => root.unmount());
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("offers every shader master once in four intentional families", () => {
|
|
95
|
+
const { host, root } = renderInto(<FlatEffectsSection {...sectionProps(neutralGrading())} />);
|
|
96
|
+
const renderedKeys: Array<string | null> = [];
|
|
97
|
+
for (const label of ["Essentials", "Retro & Glitch", "Print", "Art"]) {
|
|
98
|
+
const tab = host.querySelector<HTMLButtonElement>(`[data-flat-effect-group="${label}"]`);
|
|
99
|
+
expect(tab).not.toBeNull();
|
|
100
|
+
act(() => tab?.dispatchEvent(new MouseEvent("click", { bubbles: true })));
|
|
101
|
+
renderedKeys.push(
|
|
102
|
+
...Array.from(host.querySelectorAll("[data-flat-effect-option]"), (element) =>
|
|
103
|
+
element.getAttribute("data-flat-effect-option"),
|
|
104
|
+
),
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
expect(renderedKeys.sort()).toEqual([...HF_COLOR_GRADING_ACTIVE_EFFECT_KEYS].sort());
|
|
108
|
+
const capabilities = new Map(
|
|
109
|
+
getHfColorGradingCapabilities().effects.map((effect) => [effect.key, effect]),
|
|
110
|
+
);
|
|
111
|
+
for (const effect of EFFECT_SPECS) {
|
|
112
|
+
expect(Boolean(effect.palette)).toBe(capabilities.get(effect.key)?.supportsPalette);
|
|
113
|
+
}
|
|
114
|
+
expect(host.querySelectorAll('[data-flat-slider-track="true"]')).toHaveLength(0);
|
|
115
|
+
act(() => root.unmount());
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("requests exact cards and auditions a chosen default without persisting", () => {
|
|
119
|
+
const props = sectionProps(neutralGrading());
|
|
120
|
+
const { host, root } = renderInto(<FlatEffectsSection {...props} />);
|
|
121
|
+
expect(props.onRequestEffectPreviews).toHaveBeenCalledTimes(1);
|
|
122
|
+
expect(props.onRequestEffectPreviews).toHaveBeenCalledWith(["blur", "pixelate", "bloom"]);
|
|
123
|
+
expect(host.querySelector('[data-flat-effect-preview="pixelate"]')).not.toBeNull();
|
|
124
|
+
const pixelate = host.querySelector<HTMLButtonElement>('[data-flat-effect-option="pixelate"]');
|
|
125
|
+
act(() => pixelate?.dispatchEvent(new MouseEvent("pointerover", { bubbles: true })));
|
|
126
|
+
expect(props.onPreviewColorGrading.mock.calls.at(-1)?.[0].effects.pixelate).toBe(
|
|
127
|
+
HF_COLOR_GRADING_EFFECT_APPLY_DEFAULTS.pixelate.pixelate,
|
|
128
|
+
);
|
|
129
|
+
expect(props.onPreviewColorGrading.mock.calls.at(-1)?.[1]).toEqual({
|
|
130
|
+
animatedPreview: { kind: "effects", id: "pixelate" },
|
|
131
|
+
});
|
|
132
|
+
expect(props.onCommitColorGrading).not.toHaveBeenCalled();
|
|
133
|
+
act(() => pixelate?.dispatchEvent(new MouseEvent("pointerout", { bubbles: true })));
|
|
134
|
+
expect(props.onPreviewColorGrading).toHaveBeenLastCalledWith(null);
|
|
135
|
+
act(() => root.unmount());
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it("applies a useful canonical default when an effect is chosen", () => {
|
|
139
|
+
const onCommit = vi.fn();
|
|
140
|
+
const grading = { ...neutralGrading(), intensity: 0 };
|
|
141
|
+
const { host, root } = renderInto(<FlatEffectsSection {...sectionProps(grading, onCommit)} />);
|
|
142
|
+
const pixelate = host.querySelector<HTMLButtonElement>('[data-flat-effect-option="pixelate"]');
|
|
143
|
+
act(() => pixelate?.dispatchEvent(new MouseEvent("click", { bubbles: true })));
|
|
144
|
+
expect(onCommit).toHaveBeenCalledTimes(1);
|
|
145
|
+
expect(onCommit.mock.calls[0][0].effects.pixelate).toBe(
|
|
146
|
+
HF_COLOR_GRADING_EFFECT_APPLY_DEFAULTS.pixelate.pixelate,
|
|
147
|
+
);
|
|
148
|
+
expect(onCommit.mock.calls[0][0].intensity).toBe(0);
|
|
149
|
+
act(() => root.unmount());
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it("uses a complete treatment card as a fresh starting point while preserving the LUT", () => {
|
|
153
|
+
const onCommit = vi.fn();
|
|
154
|
+
const base = neutralGrading();
|
|
155
|
+
const grading = {
|
|
156
|
+
...base,
|
|
157
|
+
adjust: { ...base.adjust, exposure: 0.4 },
|
|
158
|
+
effects: { ...base.effects, blur: 0.6 },
|
|
159
|
+
palette: ["#112233", "#ffffff"],
|
|
160
|
+
lut: { src: "assets/luts/custom.cube", intensity: 0.5 },
|
|
161
|
+
};
|
|
162
|
+
const { host, root } = renderInto(<FlatEffectsSection {...sectionProps(grading, onCommit)} />);
|
|
163
|
+
const toggle = host.querySelector<HTMLButtonElement>('[data-flat-effects-add-toggle="true"]');
|
|
164
|
+
act(() => toggle?.dispatchEvent(new MouseEvent("click", { bubbles: true })));
|
|
165
|
+
const retro = host.querySelector<HTMLButtonElement>(
|
|
166
|
+
'[data-flat-effect-group="Retro & Glitch"]',
|
|
167
|
+
);
|
|
168
|
+
act(() => retro?.dispatchEvent(new MouseEvent("click", { bubbles: true })));
|
|
169
|
+
const vhs = host.querySelector<HTMLButtonElement>('[data-flat-effect-preset="vhs-playback"]');
|
|
170
|
+
act(() => vhs?.dispatchEvent(new MouseEvent("click", { bubbles: true })));
|
|
171
|
+
const committed = onCommit.mock.calls[0][0];
|
|
172
|
+
expect(committed).toMatchObject({
|
|
173
|
+
preset: "vhs-playback",
|
|
174
|
+
lut: { src: "assets/luts/custom.cube", intensity: 0.5 },
|
|
175
|
+
});
|
|
176
|
+
expect(committed.effects.tapeDamage).toBeGreaterThan(0);
|
|
177
|
+
expect(committed.effects.blur).toBe(0);
|
|
178
|
+
expect(committed.palette).toBeNull();
|
|
179
|
+
expect(committed.adjust.exposure).not.toBe(0.4);
|
|
180
|
+
act(() => root.unmount());
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it("shows only the selected active effect controls and converts degrees to shader units", () => {
|
|
184
|
+
const onCommit = vi.fn();
|
|
185
|
+
const grading = normalizeHfColorGrading({
|
|
186
|
+
effects: HF_COLOR_GRADING_EFFECT_APPLY_DEFAULTS.chromaticAberration,
|
|
187
|
+
});
|
|
188
|
+
if (!grading) throw new Error("expected chromatic grading");
|
|
189
|
+
const { host, root } = renderInto(<FlatEffectsSection {...sectionProps(grading, onCommit)} />);
|
|
190
|
+
expect(host.textContent).toContain("Angle");
|
|
191
|
+
const effect = host.querySelector('[data-flat-effect-editor="chromaticAberration"]');
|
|
192
|
+
if (!effect) throw new Error("expected chromatic effect");
|
|
193
|
+
const rows = effect.querySelectorAll('[data-flat-slider-track="true"]');
|
|
194
|
+
const angleTrack = rows[1] as HTMLElement | undefined;
|
|
195
|
+
if (!angleTrack) throw new Error("expected angle slider");
|
|
196
|
+
Object.defineProperty(angleTrack, "getBoundingClientRect", {
|
|
197
|
+
value: () => ({ left: 0, width: 100, top: 0, height: 2, right: 100, bottom: 2 }),
|
|
198
|
+
});
|
|
199
|
+
act(() => {
|
|
200
|
+
angleTrack.dispatchEvent(new MouseEvent("pointerdown", { bubbles: true, clientX: 50 }));
|
|
201
|
+
angleTrack.dispatchEvent(new MouseEvent("pointerup", { bubbles: true, clientX: 50 }));
|
|
202
|
+
});
|
|
203
|
+
expect(onCommit.mock.calls.at(-1)?.[0].effects.chromaticAngle).toBe(0.5);
|
|
204
|
+
act(() => root.unmount());
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it("offers native ASCII styles, binary controls, and a bounded custom palette", () => {
|
|
208
|
+
const onCommit = vi.fn();
|
|
209
|
+
const grading = normalizeHfColorGrading({
|
|
210
|
+
effects: HF_COLOR_GRADING_EFFECT_APPLY_DEFAULTS.ascii,
|
|
211
|
+
});
|
|
212
|
+
if (!grading) throw new Error("expected ASCII grading");
|
|
213
|
+
const { host, root } = renderInto(<FlatEffectsSection {...sectionProps(grading, onCommit)} />);
|
|
214
|
+
expect(
|
|
215
|
+
host.querySelector<HTMLSelectElement>('select[aria-label="Style"]')?.options,
|
|
216
|
+
).toHaveLength(8);
|
|
217
|
+
expect(host.querySelectorAll('[data-flat-toggle="true"]')).toHaveLength(2);
|
|
218
|
+
expect(host.querySelector('[data-flat-effect-editor="ascii"]')?.textContent).not.toContain(
|
|
219
|
+
"Mix",
|
|
220
|
+
);
|
|
221
|
+
expect(host.querySelectorAll("[data-flat-effects-palette-preset]")).toHaveLength(
|
|
222
|
+
HF_COLOR_GRADING_PALETTES.length,
|
|
223
|
+
);
|
|
224
|
+
const synthwave = host.querySelector<HTMLButtonElement>(
|
|
225
|
+
'[data-flat-effects-palette-preset="synthwave"]',
|
|
226
|
+
);
|
|
227
|
+
act(() => synthwave?.dispatchEvent(new MouseEvent("click", { bubbles: true })));
|
|
228
|
+
expect(onCommit.mock.calls.at(-1)?.[0].palette).toEqual(
|
|
229
|
+
HF_COLOR_GRADING_PALETTES.find(({ id }) => id === "synthwave")?.colors,
|
|
230
|
+
);
|
|
231
|
+
const addPalette = host.querySelector<HTMLButtonElement>(
|
|
232
|
+
'[data-flat-effects-add-palette="true"]',
|
|
233
|
+
);
|
|
234
|
+
act(() => addPalette?.dispatchEvent(new MouseEvent("click", { bubbles: true })));
|
|
235
|
+
expect(onCommit.mock.calls.at(-1)?.[0].palette).toEqual(["#000000", "#ffffff"]);
|
|
236
|
+
act(() => root.unmount());
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it("resets the selected effect to its authored defaults", () => {
|
|
240
|
+
const onCommit = vi.fn();
|
|
241
|
+
const grading = normalizeHfColorGrading({
|
|
242
|
+
effects: { ...HF_COLOR_GRADING_EFFECT_APPLY_DEFAULTS.digitalGlitch, digitalGlitch: 0.9 },
|
|
243
|
+
});
|
|
244
|
+
if (!grading) throw new Error("expected digital glitch grading");
|
|
245
|
+
const { host, root } = renderInto(<FlatEffectsSection {...sectionProps(grading, onCommit)} />);
|
|
246
|
+
expect(
|
|
247
|
+
host.querySelector('[data-flat-effect-editor="digitalGlitch"]')?.textContent,
|
|
248
|
+
).not.toContain("Mix");
|
|
249
|
+
const reset = host.querySelector<HTMLButtonElement>('[title="Reset Digital Glitch"]');
|
|
250
|
+
act(() => reset?.dispatchEvent(new MouseEvent("click", { bubbles: true })));
|
|
251
|
+
expect(onCommit.mock.calls.at(-1)?.[0].effects).toMatchObject(
|
|
252
|
+
HF_COLOR_GRADING_EFFECT_APPLY_DEFAULTS.digitalGlitch,
|
|
253
|
+
);
|
|
254
|
+
act(() => root.unmount());
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it("removes only the selected effect and preserves another active effect", () => {
|
|
258
|
+
const onCommit = vi.fn();
|
|
259
|
+
const grading = normalizeHfColorGrading({
|
|
260
|
+
effects: {
|
|
261
|
+
...HF_COLOR_GRADING_EFFECT_APPLY_DEFAULTS.blur,
|
|
262
|
+
...HF_COLOR_GRADING_EFFECT_APPLY_DEFAULTS.pixelate,
|
|
263
|
+
},
|
|
264
|
+
});
|
|
265
|
+
if (!grading) throw new Error("expected multi-effect grading");
|
|
266
|
+
const { host, root } = renderInto(<FlatEffectsSection {...sectionProps(grading, onCommit)} />);
|
|
267
|
+
const remove = host.querySelector<HTMLButtonElement>('[title="Remove Blur"]');
|
|
268
|
+
act(() => remove?.dispatchEvent(new MouseEvent("click", { bubbles: true })));
|
|
269
|
+
expect(onCommit.mock.calls.at(-1)?.[0].effects.blur).toBe(0);
|
|
270
|
+
expect(onCommit.mock.calls.at(-1)?.[0].effects.pixelate).toBe(
|
|
271
|
+
HF_COLOR_GRADING_EFFECT_APPLY_DEFAULTS.pixelate.pixelate,
|
|
272
|
+
);
|
|
273
|
+
act(() => root.unmount());
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
it("closes the catalog when an already active effect is selected", () => {
|
|
277
|
+
const grading = normalizeHfColorGrading({
|
|
278
|
+
effects: HF_COLOR_GRADING_EFFECT_APPLY_DEFAULTS.blur,
|
|
279
|
+
});
|
|
280
|
+
if (!grading) throw new Error("expected blur grading");
|
|
281
|
+
const props = sectionProps(grading);
|
|
282
|
+
const { host, root } = renderInto(<FlatEffectsSection {...props} />);
|
|
283
|
+
expect(props.onRequestEffectPreviews).not.toHaveBeenCalled();
|
|
284
|
+
const toggle = host.querySelector<HTMLButtonElement>('[data-flat-effects-add-toggle="true"]');
|
|
285
|
+
act(() => toggle?.dispatchEvent(new MouseEvent("click", { bubbles: true })));
|
|
286
|
+
expect(toggle?.getAttribute("aria-expanded")).toBe("true");
|
|
287
|
+
expect(props.onRequestEffectPreviews).toHaveBeenCalledWith(["blur", "pixelate", "bloom"]);
|
|
288
|
+
const blur = host.querySelector<HTMLButtonElement>('[data-flat-effect-option="blur"]');
|
|
289
|
+
act(() => blur?.dispatchEvent(new MouseEvent("click", { bubbles: true })));
|
|
290
|
+
expect(toggle?.getAttribute("aria-expanded")).toBe("false");
|
|
291
|
+
act(() => root.unmount());
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it("requests only the visible family when the catalog tab changes", () => {
|
|
295
|
+
const props = sectionProps(neutralGrading());
|
|
296
|
+
const { host, root } = renderInto(<FlatEffectsSection {...props} />);
|
|
297
|
+
const art = host.querySelector<HTMLButtonElement>('[data-flat-effect-group="Art"]');
|
|
298
|
+
act(() => art?.dispatchEvent(new MouseEvent("click", { bubbles: true })));
|
|
299
|
+
expect(props.onRequestEffectPreviews).toHaveBeenLastCalledWith([
|
|
300
|
+
"ascii",
|
|
301
|
+
"engraving",
|
|
302
|
+
"crosshatch",
|
|
303
|
+
"kuwahara",
|
|
304
|
+
]);
|
|
305
|
+
act(() => root.unmount());
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
describe("FlatEffectsAccessory", () => {
|
|
310
|
+
it("counts active masters and resets only effects and palette", () => {
|
|
311
|
+
const base = neutralGrading();
|
|
312
|
+
const grading = {
|
|
313
|
+
...base,
|
|
314
|
+
adjust: { ...base.adjust, contrast: 0.2 },
|
|
315
|
+
effects: { ...base.effects, blur: 0.4, ascii: 1 },
|
|
316
|
+
palette: ["#112233", "#ffffff"],
|
|
317
|
+
};
|
|
318
|
+
expect(activeColorGradingEffectCount(grading)).toBe(2);
|
|
319
|
+
const onCommit = vi.fn();
|
|
320
|
+
const { host, root } = renderInto(
|
|
321
|
+
<FlatEffectsAccessory grading={grading} onCommitColorGrading={onCommit} />,
|
|
322
|
+
);
|
|
323
|
+
const reset = host.querySelector<HTMLButtonElement>('[data-flat-effects-reset="true"]');
|
|
324
|
+
act(() => reset?.dispatchEvent(new MouseEvent("click", { bubbles: true })));
|
|
325
|
+
expect(onCommit.mock.calls[0][0].adjust.contrast).toBe(0.2);
|
|
326
|
+
expect(onCommit.mock.calls[0][0].effects.blur).toBe(0);
|
|
327
|
+
expect(onCommit.mock.calls[0][0].effects.ascii).toBe(0);
|
|
328
|
+
expect(onCommit.mock.calls[0][0].palette).toBeNull();
|
|
329
|
+
act(() => root.unmount());
|
|
330
|
+
});
|
|
331
|
+
});
|