@pixerate/editor-react 0.77.0 → 0.78.0
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/{chunk-QARGR5E5.js → chunk-2EW42UXC.js} +617 -464
- package/dist/chunk-2EW42UXC.js.map +1 -0
- package/dist/chunk-FX3EREF5.js +206 -0
- package/dist/chunk-FX3EREF5.js.map +1 -0
- package/dist/chunk-ZQZAKUUM.js +416 -0
- package/dist/chunk-ZQZAKUUM.js.map +1 -0
- package/dist/effects/index.cjs +606 -0
- package/dist/effects/index.cjs.map +1 -0
- package/dist/effects/index.d.cts +80 -0
- package/dist/effects/index.d.ts +80 -0
- package/dist/effects/index.js +16 -0
- package/dist/effects/index.js.map +1 -0
- package/dist/image-editor/index.cjs +1282 -574
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.d.cts +29 -2
- package/dist/image-editor/index.d.ts +29 -2
- package/dist/image-editor/index.js +7 -1
- package/dist/index.cjs +1695 -987
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/parameters/index.js +55 -439
- package/dist/parameters/index.js.map +1 -1
- package/dist/video-editor/index.cjs +732 -17
- package/dist/video-editor/index.cjs.map +1 -1
- package/dist/video-editor/index.d.cts +31 -3
- package/dist/video-editor/index.d.ts +31 -3
- package/dist/video-editor/index.js +184 -17
- package/dist/video-editor/index.js.map +1 -1
- package/package.json +8 -3
- package/dist/chunk-QARGR5E5.js.map +0 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { VideoEditorController, VideoEditorState, VideoEditResult, VideoEditorOptions, VideoEditorToolProviderOptions, VideoPreviewOptions, VideoScopeKind, VideoWaveformMode, probeVideoMedia } from '@pixerate/editor/video-editor';
|
|
1
|
+
import { VideoEditorController, VideoEditorState, VideoEditResult, VideoEditorOptions, VideoEditorToolProviderOptions, VideoEffectsToolProviderOptions, VideoPreviewOptions, VideoScopeKind, VideoWaveformMode, probeVideoMedia } from '@pixerate/editor/video-editor';
|
|
2
2
|
import { ToolRegistry } from '@pixerate/editor/tools';
|
|
3
3
|
import React__default from 'react';
|
|
4
|
+
import { EffectProvider } from '@pixerate/editor/effects';
|
|
4
5
|
|
|
5
6
|
/** A `VideoEditorController` created once for the component's lifetime (destroyed on unmount). */
|
|
6
7
|
declare function useVideoEditorController(options?: VideoEditorOptions): VideoEditorController;
|
|
@@ -35,6 +36,13 @@ declare function useVideoEditor(controllerOrOptions?: VideoEditorController | Vi
|
|
|
35
36
|
* `trim_video_clip`, …) in `registry` while mounted.
|
|
36
37
|
*/
|
|
37
38
|
declare function useVideoEditorTools(registry: ToolRegistry | null | undefined, controller: VideoEditorController, options?: VideoEditorToolProviderOptions): void;
|
|
39
|
+
/**
|
|
40
|
+
* Registers the video editor's effect tools (`search_effects`, `describe_effect`, `apply_effect`,
|
|
41
|
+
* `add_video_effect_clip`, `set_video_effect_transition`, …) in `registry` while mounted. They
|
|
42
|
+
* work once an effects provider is registered (see `useEffects`); before that the read tools say
|
|
43
|
+
* so and the write tools refuse.
|
|
44
|
+
*/
|
|
45
|
+
declare function useVideoEffectsTools(registry: ToolRegistry | null | undefined, controller: VideoEditorController, options?: VideoEffectsToolProviderOptions): void;
|
|
38
46
|
|
|
39
47
|
interface VideoPreviewProps {
|
|
40
48
|
controller: VideoEditorController;
|
|
@@ -163,6 +171,14 @@ interface VideoEditorProps {
|
|
|
163
171
|
showTranscript?: boolean;
|
|
164
172
|
/** Show colour scopes (histogram, luma waveform, vectorscope) of the preview under it (default false). */
|
|
165
173
|
showScopes?: boolean;
|
|
174
|
+
/**
|
|
175
|
+
* An effects provider (see `@pixerate/editor/effects`), registered with the controller while
|
|
176
|
+
* mounted: clip effects, effect transitions and effect clips, with the effects panel. Optional:
|
|
177
|
+
* without one the editor is as before (effects in a project show as placeholders).
|
|
178
|
+
*/
|
|
179
|
+
effects?: EffectProvider | null;
|
|
180
|
+
/** Show the effects panel (default: when a provider is given or the project has effects). */
|
|
181
|
+
showEffects?: boolean;
|
|
166
182
|
className?: string;
|
|
167
183
|
}
|
|
168
184
|
/**
|
|
@@ -170,6 +186,18 @@ interface VideoEditorProps {
|
|
|
170
186
|
* transport, the timeline and the clip inspector, with the project's issues listed under the
|
|
171
187
|
* timeline. Every edit is one undo step; the controller's agent tools edit the same project.
|
|
172
188
|
*/
|
|
173
|
-
declare function VideoEditor({ controller: given, options, editable, pxPerSecond, onPxPerSecondChange, resolution, showIssues, showTranscript, showScopes, className }: VideoEditorProps): React__default.JSX.Element;
|
|
189
|
+
declare function VideoEditor({ controller: given, options, editable, pxPerSecond, onPxPerSecondChange, resolution, showIssues, showTranscript, showScopes, effects, showEffects, className }: VideoEditorProps): React__default.JSX.Element;
|
|
190
|
+
|
|
191
|
+
interface VideoEffectsPanelProps {
|
|
192
|
+
controller: VideoEditorController;
|
|
193
|
+
className?: string;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Provider effects for the selected clip: its effect stack (add, turn on and off, reorder, change
|
|
197
|
+
* controls, remove), its effect transition, and an effect clip's own controls; plus adding effect
|
|
198
|
+
* clips at the playhead. Every change is one undo step (a slider drag is one). Without a provider it
|
|
199
|
+
* lists what the project holds and says why nothing can be added.
|
|
200
|
+
*/
|
|
201
|
+
declare function VideoEffectsPanel({ controller, className }: VideoEffectsPanelProps): React__default.JSX.Element;
|
|
174
202
|
|
|
175
|
-
export { ClipInspector, type ClipInspectorProps, MediaBin, type MediaBinProps, TranscriptPanel, type TranscriptPanelProps, type UseVideoEditorResult, VideoEditor, type VideoEditorProps, VideoPreview, type VideoPreviewProps, VideoScopes, type VideoScopesProps, VideoTimeline, type VideoTimelineProps, VideoTransport, type VideoTransportProps, useVideoEditor, useVideoEditorController, useVideoEditorState, useVideoEditorTools };
|
|
203
|
+
export { ClipInspector, type ClipInspectorProps, MediaBin, type MediaBinProps, TranscriptPanel, type TranscriptPanelProps, type UseVideoEditorResult, VideoEditor, type VideoEditorProps, VideoEffectsPanel, type VideoEffectsPanelProps, VideoPreview, type VideoPreviewProps, VideoScopes, type VideoScopesProps, VideoTimeline, type VideoTimelineProps, VideoTransport, type VideoTransportProps, useVideoEditor, useVideoEditorController, useVideoEditorState, useVideoEditorTools, useVideoEffectsTools };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { VideoEditorController, VideoEditorState, VideoEditResult, VideoEditorOptions, VideoEditorToolProviderOptions, VideoPreviewOptions, VideoScopeKind, VideoWaveformMode, probeVideoMedia } from '@pixerate/editor/video-editor';
|
|
1
|
+
import { VideoEditorController, VideoEditorState, VideoEditResult, VideoEditorOptions, VideoEditorToolProviderOptions, VideoEffectsToolProviderOptions, VideoPreviewOptions, VideoScopeKind, VideoWaveformMode, probeVideoMedia } from '@pixerate/editor/video-editor';
|
|
2
2
|
import { ToolRegistry } from '@pixerate/editor/tools';
|
|
3
3
|
import React__default from 'react';
|
|
4
|
+
import { EffectProvider } from '@pixerate/editor/effects';
|
|
4
5
|
|
|
5
6
|
/** A `VideoEditorController` created once for the component's lifetime (destroyed on unmount). */
|
|
6
7
|
declare function useVideoEditorController(options?: VideoEditorOptions): VideoEditorController;
|
|
@@ -35,6 +36,13 @@ declare function useVideoEditor(controllerOrOptions?: VideoEditorController | Vi
|
|
|
35
36
|
* `trim_video_clip`, …) in `registry` while mounted.
|
|
36
37
|
*/
|
|
37
38
|
declare function useVideoEditorTools(registry: ToolRegistry | null | undefined, controller: VideoEditorController, options?: VideoEditorToolProviderOptions): void;
|
|
39
|
+
/**
|
|
40
|
+
* Registers the video editor's effect tools (`search_effects`, `describe_effect`, `apply_effect`,
|
|
41
|
+
* `add_video_effect_clip`, `set_video_effect_transition`, …) in `registry` while mounted. They
|
|
42
|
+
* work once an effects provider is registered (see `useEffects`); before that the read tools say
|
|
43
|
+
* so and the write tools refuse.
|
|
44
|
+
*/
|
|
45
|
+
declare function useVideoEffectsTools(registry: ToolRegistry | null | undefined, controller: VideoEditorController, options?: VideoEffectsToolProviderOptions): void;
|
|
38
46
|
|
|
39
47
|
interface VideoPreviewProps {
|
|
40
48
|
controller: VideoEditorController;
|
|
@@ -163,6 +171,14 @@ interface VideoEditorProps {
|
|
|
163
171
|
showTranscript?: boolean;
|
|
164
172
|
/** Show colour scopes (histogram, luma waveform, vectorscope) of the preview under it (default false). */
|
|
165
173
|
showScopes?: boolean;
|
|
174
|
+
/**
|
|
175
|
+
* An effects provider (see `@pixerate/editor/effects`), registered with the controller while
|
|
176
|
+
* mounted: clip effects, effect transitions and effect clips, with the effects panel. Optional:
|
|
177
|
+
* without one the editor is as before (effects in a project show as placeholders).
|
|
178
|
+
*/
|
|
179
|
+
effects?: EffectProvider | null;
|
|
180
|
+
/** Show the effects panel (default: when a provider is given or the project has effects). */
|
|
181
|
+
showEffects?: boolean;
|
|
166
182
|
className?: string;
|
|
167
183
|
}
|
|
168
184
|
/**
|
|
@@ -170,6 +186,18 @@ interface VideoEditorProps {
|
|
|
170
186
|
* transport, the timeline and the clip inspector, with the project's issues listed under the
|
|
171
187
|
* timeline. Every edit is one undo step; the controller's agent tools edit the same project.
|
|
172
188
|
*/
|
|
173
|
-
declare function VideoEditor({ controller: given, options, editable, pxPerSecond, onPxPerSecondChange, resolution, showIssues, showTranscript, showScopes, className }: VideoEditorProps): React__default.JSX.Element;
|
|
189
|
+
declare function VideoEditor({ controller: given, options, editable, pxPerSecond, onPxPerSecondChange, resolution, showIssues, showTranscript, showScopes, effects, showEffects, className }: VideoEditorProps): React__default.JSX.Element;
|
|
190
|
+
|
|
191
|
+
interface VideoEffectsPanelProps {
|
|
192
|
+
controller: VideoEditorController;
|
|
193
|
+
className?: string;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Provider effects for the selected clip: its effect stack (add, turn on and off, reorder, change
|
|
197
|
+
* controls, remove), its effect transition, and an effect clip's own controls; plus adding effect
|
|
198
|
+
* clips at the playhead. Every change is one undo step (a slider drag is one). Without a provider it
|
|
199
|
+
* lists what the project holds and says why nothing can be added.
|
|
200
|
+
*/
|
|
201
|
+
declare function VideoEffectsPanel({ controller, className }: VideoEffectsPanelProps): React__default.JSX.Element;
|
|
174
202
|
|
|
175
|
-
export { ClipInspector, type ClipInspectorProps, MediaBin, type MediaBinProps, TranscriptPanel, type TranscriptPanelProps, type UseVideoEditorResult, VideoEditor, type VideoEditorProps, VideoPreview, type VideoPreviewProps, VideoScopes, type VideoScopesProps, VideoTimeline, type VideoTimelineProps, VideoTransport, type VideoTransportProps, useVideoEditor, useVideoEditorController, useVideoEditorState, useVideoEditorTools };
|
|
203
|
+
export { ClipInspector, type ClipInspectorProps, MediaBin, type MediaBinProps, TranscriptPanel, type TranscriptPanelProps, type UseVideoEditorResult, VideoEditor, type VideoEditorProps, VideoEffectsPanel, type VideoEffectsPanelProps, VideoPreview, type VideoPreviewProps, VideoScopes, type VideoScopesProps, VideoTimeline, type VideoTimelineProps, VideoTransport, type VideoTransportProps, useVideoEditor, useVideoEditorController, useVideoEditorState, useVideoEditorTools, useVideoEffectsTools };
|
|
@@ -1,8 +1,17 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EffectParameters,
|
|
3
|
+
EffectsBrowser,
|
|
4
|
+
useEffectDescription,
|
|
5
|
+
useEffects
|
|
6
|
+
} from "../chunk-FX3EREF5.js";
|
|
7
|
+
import "../chunk-ZQZAKUUM.js";
|
|
8
|
+
|
|
1
9
|
// src/video-editor/useVideoEditor.ts
|
|
2
10
|
import { useEffect, useMemo, useRef, useSyncExternalStore } from "react";
|
|
3
11
|
import {
|
|
4
12
|
VideoEditorController,
|
|
5
|
-
createVideoEditorToolProvider
|
|
13
|
+
createVideoEditorToolProvider,
|
|
14
|
+
createVideoEffectsToolProvider
|
|
6
15
|
} from "@pixerate/editor/video-editor";
|
|
7
16
|
function useVideoEditorController(options = {}) {
|
|
8
17
|
const ref = useRef(null);
|
|
@@ -47,6 +56,13 @@ function useVideoEditorTools(registry, controller, options = {}) {
|
|
|
47
56
|
return registry.register(createVideoEditorToolProvider(controller, { id, namespace, readOnly, rasterize, framePixels, framePixelsTimeout }));
|
|
48
57
|
}, [registry, controller, id, namespace, readOnly, rasterize, framePixels, framePixelsTimeout]);
|
|
49
58
|
}
|
|
59
|
+
function useVideoEffectsTools(registry, controller, options = {}) {
|
|
60
|
+
const { id, namespace, readOnly } = options;
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
if (!registry) return;
|
|
63
|
+
return registry.register(createVideoEffectsToolProvider(controller, { id, namespace, readOnly }));
|
|
64
|
+
}, [registry, controller, id, namespace, readOnly]);
|
|
65
|
+
}
|
|
50
66
|
|
|
51
67
|
// src/video-editor/VideoPreview.tsx
|
|
52
68
|
import { useEffect as useEffect2, useRef as useRef2 } from "react";
|
|
@@ -823,6 +839,7 @@ function ClipInspector({ controller, className = "" }) {
|
|
|
823
839
|
] }) }),
|
|
824
840
|
/* @__PURE__ */ jsx5(Row, { label: "Muted", name: "muted", children: /* @__PURE__ */ jsx5("input", { type: "checkbox", className: "justify-self-start", checked: !!clip.muted, disabled, "aria-label": "Muted", onChange: (e) => set("muted", e.target.checked || void 0, e.target.checked ? "Mute clip" : "Unmute clip") }) })
|
|
825
841
|
] }),
|
|
842
|
+
clip.type === "effect" && /* @__PURE__ */ jsx5(Row, { label: "Effect", name: "effect", children: /* @__PURE__ */ jsx5("span", { className: "truncate text-xs", title: "Change its controls in the Effects panel", children: clip.name ?? clip.effect }) }),
|
|
826
843
|
clip.type === "color" && /* @__PURE__ */ jsx5(Row, { label: "Colour", name: "color", children: /* @__PURE__ */ jsx5(Commit, { value: clip.color, "aria-label": "Colour", disabled, onCommit: (raw) => raw.trim() && set("color", raw.trim(), "Change colour") }) }),
|
|
827
844
|
/* @__PURE__ */ jsx5(Row, { label: "Transition in", name: "transition", children: /* @__PURE__ */ jsxs4(
|
|
828
845
|
"select",
|
|
@@ -833,14 +850,15 @@ function ClipInspector({ controller, className = "" }) {
|
|
|
833
850
|
"aria-label": "Transition",
|
|
834
851
|
onChange: (e) => {
|
|
835
852
|
const type = e.target.value;
|
|
853
|
+
const { effect: _effect, ...rest } = transition ?? { duration: DEFAULT_TRANSITION };
|
|
836
854
|
controller.apply(
|
|
837
|
-
{ op: "set_transition", clip: clip.id, transition: type ? { ...
|
|
855
|
+
{ op: "set_transition", clip: clip.id, transition: type ? { ...rest, type, duration: transition?.duration ?? DEFAULT_TRANSITION } : null },
|
|
838
856
|
type ? "Set transition" : "Remove transition"
|
|
839
857
|
);
|
|
840
858
|
},
|
|
841
859
|
children: [
|
|
842
860
|
/* @__PURE__ */ jsx5("option", { value: "", children: "None" }),
|
|
843
|
-
VIDEO_TRANSITION_TYPES.map((t) => /* @__PURE__ */ jsx5("option", { value: t, children: VIDEO_TRANSITION_LABELS[t] }, t))
|
|
861
|
+
VIDEO_TRANSITION_TYPES.filter((t) => t !== "effect" || transition?.type === "effect").map((t) => /* @__PURE__ */ jsx5("option", { value: t, children: t === "effect" ? `Effect: ${transition?.effect?.name ?? transition?.effect?.effect}` : VIDEO_TRANSITION_LABELS[t] }, t))
|
|
844
862
|
]
|
|
845
863
|
}
|
|
846
864
|
) }),
|
|
@@ -1155,9 +1173,153 @@ function TranscriptPanel({ controller, track, editable = true, className = "" })
|
|
|
1155
1173
|
|
|
1156
1174
|
// src/video-editor/VideoEditor.tsx
|
|
1157
1175
|
import { useEffect as useEffect5 } from "react";
|
|
1158
|
-
|
|
1159
|
-
|
|
1176
|
+
|
|
1177
|
+
// src/video-editor/VideoEffectsPanel.tsx
|
|
1178
|
+
import { useState as useState6 } from "react";
|
|
1179
|
+
import {
|
|
1180
|
+
findVideoClip as findVideoClip2,
|
|
1181
|
+
isVideoKeyframes
|
|
1182
|
+
} from "@pixerate/editor/video-editor";
|
|
1183
|
+
import { Fragment as Fragment6, jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1184
|
+
var btn5 = "rounded border border-border px-1.5 py-0.5 text-[11px] disabled:opacity-40";
|
|
1185
|
+
var keyedOf = (values) => new Set(Object.entries(values ?? {}).filter(([, v]) => isVideoKeyframes(v)).map(([k]) => k));
|
|
1186
|
+
function EffectControls({
|
|
1187
|
+
provider,
|
|
1188
|
+
effect,
|
|
1189
|
+
name,
|
|
1190
|
+
values,
|
|
1191
|
+
disabled,
|
|
1192
|
+
onChange,
|
|
1193
|
+
onPreset
|
|
1194
|
+
}) {
|
|
1195
|
+
const { description, error } = useEffectDescription(provider, effect);
|
|
1196
|
+
if (!provider) return /* @__PURE__ */ jsxs7("p", { className: "text-[11px] text-muted-foreground", children: [
|
|
1197
|
+
'No effects provider: "',
|
|
1198
|
+
name ?? effect,
|
|
1199
|
+
'" is kept and shown as a placeholder.'
|
|
1200
|
+
] });
|
|
1201
|
+
if (error) return /* @__PURE__ */ jsx8("p", { className: "text-[11px] text-amber-500", children: error });
|
|
1202
|
+
if (!description) return /* @__PURE__ */ jsx8("p", { className: "text-[11px] text-muted-foreground", children: "Loading\u2026" });
|
|
1203
|
+
return /* @__PURE__ */ jsx8(EffectParameters, { description, values: values ?? {}, keyframed: keyedOf(values), disabled, onChange, onReset: (k) => onChange(k, description.defaults[k], null), onPreset });
|
|
1204
|
+
}
|
|
1205
|
+
function VideoEffectsPanel({ controller, className = "" }) {
|
|
1206
|
+
const state = useVideoEditorState(controller);
|
|
1207
|
+
const provider = state.effectsProvider;
|
|
1208
|
+
const [browse, setBrowse] = useState6(null);
|
|
1209
|
+
const [message, setMessage] = useState6(null);
|
|
1210
|
+
const [open, setOpen] = useState6(null);
|
|
1211
|
+
const selected = state.selection[0];
|
|
1212
|
+
const at = selected ? findVideoClip2(state.project, selected) : null;
|
|
1213
|
+
const clip = at?.clip ?? null;
|
|
1214
|
+
const disabled = state.readOnly;
|
|
1215
|
+
const report = (r) => void Promise.resolve(r).then((x) => setMessage(x.ok ? null : x.error));
|
|
1216
|
+
const addEffectClip = (effect) => {
|
|
1217
|
+
const track = at && at.track.kind === "video" ? at.track.id : state.project.tracks.find((t) => t.kind === "video")?.id;
|
|
1218
|
+
if (!track) return setMessage("Add a video track first.");
|
|
1219
|
+
report(controller.addEffectClip(track, effect, { start: Math.round(state.time), mode: "overwrite" }).then((r) => (r.ok && r.clip && controller.select([r.clip]), r)));
|
|
1220
|
+
};
|
|
1221
|
+
return /* @__PURE__ */ jsxs7("section", { className: `flex flex-col gap-2 rounded-md border border-border p-2 text-xs ${className}`, "aria-label": "Effects", "data-video-effects": "", children: [
|
|
1222
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-1", children: [
|
|
1223
|
+
/* @__PURE__ */ jsx8("h3", { className: "flex-1 font-semibold", children: "Effects" }),
|
|
1224
|
+
/* @__PURE__ */ jsx8("button", { type: "button", className: btn5, disabled: disabled || !provider, onClick: () => setBrowse(browse === "generator" ? null : "generator"), "aria-pressed": browse === "generator", children: "Add effect clip" })
|
|
1225
|
+
] }),
|
|
1226
|
+
!provider && /* @__PURE__ */ jsx8("p", { className: "text-[11px] text-muted-foreground", children: "No effects provider is registered. Effects in the project are kept and drawn as placeholders." }),
|
|
1227
|
+
browse === "generator" && /* @__PURE__ */ jsx8(EffectsBrowser, { provider, uses: ["generator", "title", "overlay"], title: "Generators and titles", onPick: (e) => (setBrowse(null), addEffectClip(e.id)) }),
|
|
1228
|
+
!clip && /* @__PURE__ */ jsx8("p", { className: "text-[11px] text-muted-foreground", children: "Select a clip to see its effects." }),
|
|
1229
|
+
clip && /* @__PURE__ */ jsxs7(Fragment6, { children: [
|
|
1230
|
+
clip.type === "effect" && /* @__PURE__ */ jsxs7("div", { className: "space-y-1", "data-effect-part": "generator", children: [
|
|
1231
|
+
/* @__PURE__ */ jsx8("div", { className: "font-medium", children: clip.name ?? clip.effect }),
|
|
1232
|
+
/* @__PURE__ */ jsx8(
|
|
1233
|
+
EffectControls,
|
|
1234
|
+
{
|
|
1235
|
+
provider,
|
|
1236
|
+
effect: clip.effect,
|
|
1237
|
+
name: clip.name,
|
|
1238
|
+
values: clip.values,
|
|
1239
|
+
disabled,
|
|
1240
|
+
onChange: (k, v, gesture) => report(controller.updateEffectValues(clip.id, "generator", { [k]: v }, { merge: gesture ?? void 0 })),
|
|
1241
|
+
onPreset: (values) => report(controller.updateEffectValues(clip.id, "generator", values))
|
|
1242
|
+
}
|
|
1243
|
+
)
|
|
1244
|
+
] }),
|
|
1245
|
+
/* @__PURE__ */ jsxs7("div", { className: "space-y-1", "data-effect-part": "transition", children: [
|
|
1246
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-1", children: [
|
|
1247
|
+
/* @__PURE__ */ jsx8("span", { className: "flex-1 font-medium", children: "Transition in" }),
|
|
1248
|
+
clip.transition?.type === "effect" ? /* @__PURE__ */ jsx8("button", { type: "button", className: btn5, disabled, onClick: () => report(controller.apply({ op: "set_transition", clip: clip.id, transition: null }, "Remove transition")), children: "Remove" }) : /* @__PURE__ */ jsx8("button", { type: "button", className: btn5, disabled: disabled || !provider, "aria-pressed": browse === "transition", onClick: () => setBrowse(browse === "transition" ? null : "transition"), children: "Effect transition\u2026" })
|
|
1249
|
+
] }),
|
|
1250
|
+
browse === "transition" && /* @__PURE__ */ jsx8(EffectsBrowser, { provider, uses: ["transition"], title: "Transitions", onPick: (e) => (setBrowse(null), report(controller.setEffectTransition(clip.id, e.id))) }),
|
|
1251
|
+
clip.transition?.type === "effect" && clip.transition.effect && /* @__PURE__ */ jsx8(
|
|
1252
|
+
EffectControls,
|
|
1253
|
+
{
|
|
1254
|
+
provider,
|
|
1255
|
+
effect: clip.transition.effect.effect,
|
|
1256
|
+
name: clip.transition.effect.name,
|
|
1257
|
+
values: clip.transition.effect.values,
|
|
1258
|
+
disabled,
|
|
1259
|
+
onChange: (k, v, gesture) => report(controller.updateEffectValues(clip.id, "transition", { [k]: v }, { merge: gesture ?? void 0 })),
|
|
1260
|
+
onPreset: (values) => report(controller.updateEffectValues(clip.id, "transition", values))
|
|
1261
|
+
}
|
|
1262
|
+
)
|
|
1263
|
+
] }),
|
|
1264
|
+
/* @__PURE__ */ jsxs7("div", { className: "space-y-1", "data-effect-part": "stack", children: [
|
|
1265
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-1", children: [
|
|
1266
|
+
/* @__PURE__ */ jsx8("span", { className: "flex-1 font-medium", children: "On this clip" }),
|
|
1267
|
+
/* @__PURE__ */ jsx8("button", { type: "button", className: btn5, disabled: disabled || !provider, "aria-pressed": browse === "clip", onClick: () => setBrowse(browse === "clip" ? null : "clip"), children: "Add effect\u2026" })
|
|
1268
|
+
] }),
|
|
1269
|
+
browse === "clip" && /* @__PURE__ */ jsx8(EffectsBrowser, { provider, uses: ["filter", "overlay"], title: "Filters and overlays", onPick: (e) => (setBrowse(null), report(controller.applyClipEffect(clip.id, e.id))) }),
|
|
1270
|
+
/* @__PURE__ */ jsx8("ol", { className: "space-y-1", children: (clip.effects ?? []).map((e, i, all) => /* @__PURE__ */ jsxs7("li", { className: "rounded border border-border p-1", "data-clip-effect": e.id, children: [
|
|
1271
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-1", children: [
|
|
1272
|
+
/* @__PURE__ */ jsx8("input", { type: "checkbox", checked: e.enabled !== false, disabled, "aria-label": `${e.name ?? e.effect} on`, onChange: (ev) => report(controller.updateClipEffect(clip.id, e.id, { enabled: ev.currentTarget.checked })) }),
|
|
1273
|
+
/* @__PURE__ */ jsx8("button", { type: "button", className: "min-w-0 flex-1 truncate text-left", "aria-expanded": open === e.id, onClick: () => setOpen(open === e.id ? null : e.id), children: e.name ?? e.effect }),
|
|
1274
|
+
/* @__PURE__ */ jsx8("button", { type: "button", className: btn5, disabled: disabled || i === 0, "aria-label": "Move up", onClick: () => report(controller.updateClipEffect(clip.id, e.id, { index: i - 1 })), children: "\u2191" }),
|
|
1275
|
+
/* @__PURE__ */ jsx8("button", { type: "button", className: btn5, disabled: disabled || i === all.length - 1, "aria-label": "Move down", onClick: () => report(controller.updateClipEffect(clip.id, e.id, { index: i + 1 })), children: "\u2193" }),
|
|
1276
|
+
/* @__PURE__ */ jsx8("button", { type: "button", className: btn5, disabled, "aria-label": `Remove ${e.name ?? e.effect}`, onClick: () => report(controller.removeClipEffect(clip.id, e.id)), children: "\u2715" })
|
|
1277
|
+
] }),
|
|
1278
|
+
open === e.id && /* @__PURE__ */ jsxs7("div", { className: "mt-1 space-y-2", children: [
|
|
1279
|
+
/* @__PURE__ */ jsxs7("label", { className: "flex items-center gap-1", children: [
|
|
1280
|
+
/* @__PURE__ */ jsx8("span", { className: "w-14 text-[11px]", children: "Opacity" }),
|
|
1281
|
+
/* @__PURE__ */ jsx8(
|
|
1282
|
+
"input",
|
|
1283
|
+
{
|
|
1284
|
+
type: "range",
|
|
1285
|
+
min: 0,
|
|
1286
|
+
max: 1,
|
|
1287
|
+
step: 0.01,
|
|
1288
|
+
className: "flex-1",
|
|
1289
|
+
disabled: disabled || isVideoKeyframes(e.opacity),
|
|
1290
|
+
value: typeof e.opacity === "number" ? e.opacity : 1,
|
|
1291
|
+
"aria-label": `${e.name ?? e.effect} opacity`,
|
|
1292
|
+
onChange: (ev) => report(controller.updateClipEffect(clip.id, e.id, { opacity: Number(ev.currentTarget.value) }, { merge: `opacity:${clip.id}:${e.id}` }))
|
|
1293
|
+
}
|
|
1294
|
+
)
|
|
1295
|
+
] }),
|
|
1296
|
+
/* @__PURE__ */ jsx8(
|
|
1297
|
+
EffectControls,
|
|
1298
|
+
{
|
|
1299
|
+
provider,
|
|
1300
|
+
effect: e.effect,
|
|
1301
|
+
name: e.name,
|
|
1302
|
+
values: e.values,
|
|
1303
|
+
disabled,
|
|
1304
|
+
onChange: (k, v, gesture) => report(controller.updateClipEffect(clip.id, e.id, { values: { [k]: v } }, { merge: gesture ?? void 0 })),
|
|
1305
|
+
onPreset: (values) => report(controller.updateClipEffect(clip.id, e.id, { values }))
|
|
1306
|
+
}
|
|
1307
|
+
)
|
|
1308
|
+
] })
|
|
1309
|
+
] }, e.id)) }),
|
|
1310
|
+
!clip.effects?.length && /* @__PURE__ */ jsx8("p", { className: "text-[11px] text-muted-foreground", children: "No effects on this clip." })
|
|
1311
|
+
] })
|
|
1312
|
+
] }),
|
|
1313
|
+
message && /* @__PURE__ */ jsx8("p", { role: "alert", className: "text-[11px] text-destructive", children: message })
|
|
1314
|
+
] });
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
// src/video-editor/VideoEditor.tsx
|
|
1318
|
+
import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1319
|
+
function VideoEditor({ controller: given, options, editable = true, pxPerSecond, onPxPerSecondChange, resolution, showIssues = true, showTranscript = true, showScopes = false, effects, showEffects, className = "" }) {
|
|
1160
1320
|
const { controller, state } = useVideoEditor(given ?? options ?? {});
|
|
1321
|
+
useEffects(controller, effects);
|
|
1322
|
+
const effectsShown = showEffects ?? !!(state.effectsProvider || state.project.tracks.some((t) => t.clips.some((c) => c.type === "effect" || c.effects?.length || c.transition?.type === "effect")));
|
|
1161
1323
|
useEffect5(() => {
|
|
1162
1324
|
if (editable) return;
|
|
1163
1325
|
const was = controller.getState().readOnly;
|
|
@@ -1165,19 +1327,22 @@ function VideoEditor({ controller: given, options, editable = true, pxPerSecond,
|
|
|
1165
1327
|
return () => controller.setReadOnly(was);
|
|
1166
1328
|
}, [controller, editable]);
|
|
1167
1329
|
const { issues } = state;
|
|
1168
|
-
const issueList = showIssues && issues.length > 0 && /* @__PURE__ */
|
|
1169
|
-
return /* @__PURE__ */
|
|
1170
|
-
/* @__PURE__ */
|
|
1171
|
-
editable && /* @__PURE__ */
|
|
1172
|
-
/* @__PURE__ */
|
|
1173
|
-
/* @__PURE__ */
|
|
1174
|
-
/* @__PURE__ */
|
|
1175
|
-
showScopes && /* @__PURE__ */
|
|
1330
|
+
const issueList = showIssues && issues.length > 0 && /* @__PURE__ */ jsx9("ul", { className: "space-y-0.5 text-xs", "aria-label": "Video issues", "data-video-issues": "", children: issues.map((issue, i) => /* @__PURE__ */ jsx9("li", { className: issue.severity === "error" ? "text-red-500" : issue.severity === "warning" ? "text-amber-500" : "text-muted-foreground", "data-issue": issue.code, children: issue.message }, i)) });
|
|
1331
|
+
return /* @__PURE__ */ jsxs8("div", { className: `flex flex-col gap-3 ${className}`, "data-video-editor": "", children: [
|
|
1332
|
+
/* @__PURE__ */ jsxs8("div", { className: `grid gap-3 ${editable ? "lg:grid-cols-[16rem_minmax(0,1fr)_18rem]" : ""}`, children: [
|
|
1333
|
+
editable && /* @__PURE__ */ jsx9(MediaBin, { controller, className: "min-w-0" }),
|
|
1334
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex min-w-0 flex-col gap-2", children: [
|
|
1335
|
+
/* @__PURE__ */ jsx9(VideoPreview, { controller, resolution, className: "rounded-md border border-border" }),
|
|
1336
|
+
/* @__PURE__ */ jsx9(VideoTransport, { controller, editable }),
|
|
1337
|
+
showScopes && /* @__PURE__ */ jsx9(VideoScopes, { controller })
|
|
1176
1338
|
] }),
|
|
1177
|
-
editable && /* @__PURE__ */
|
|
1339
|
+
editable && /* @__PURE__ */ jsxs8("div", { className: "flex min-w-0 flex-col gap-3", children: [
|
|
1340
|
+
/* @__PURE__ */ jsx9(ClipInspector, { controller, className: "min-w-0" }),
|
|
1341
|
+
effectsShown && /* @__PURE__ */ jsx9(VideoEffectsPanel, { controller, className: "min-w-0" })
|
|
1342
|
+
] })
|
|
1178
1343
|
] }),
|
|
1179
|
-
/* @__PURE__ */
|
|
1180
|
-
showTranscript && /* @__PURE__ */
|
|
1344
|
+
/* @__PURE__ */ jsx9(VideoTimeline, { controller, editable, pxPerSecond, onPxPerSecondChange }),
|
|
1345
|
+
showTranscript && /* @__PURE__ */ jsx9(TranscriptPanel, { controller, editable }),
|
|
1181
1346
|
issueList
|
|
1182
1347
|
] });
|
|
1183
1348
|
}
|
|
@@ -1186,6 +1351,7 @@ export {
|
|
|
1186
1351
|
MediaBin,
|
|
1187
1352
|
TranscriptPanel,
|
|
1188
1353
|
VideoEditor,
|
|
1354
|
+
VideoEffectsPanel,
|
|
1189
1355
|
VideoPreview,
|
|
1190
1356
|
VideoScopes,
|
|
1191
1357
|
VideoTimeline,
|
|
@@ -1193,6 +1359,7 @@ export {
|
|
|
1193
1359
|
useVideoEditor,
|
|
1194
1360
|
useVideoEditorController,
|
|
1195
1361
|
useVideoEditorState,
|
|
1196
|
-
useVideoEditorTools
|
|
1362
|
+
useVideoEditorTools,
|
|
1363
|
+
useVideoEffectsTools
|
|
1197
1364
|
};
|
|
1198
1365
|
//# sourceMappingURL=index.js.map
|