@pixerate/editor-react 0.54.0 → 0.56.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.
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/video-editor/index.ts
|
|
@@ -22,6 +32,7 @@ var video_editor_exports = {};
|
|
|
22
32
|
__export(video_editor_exports, {
|
|
23
33
|
ClipInspector: () => ClipInspector,
|
|
24
34
|
MediaBin: () => MediaBin,
|
|
35
|
+
TranscriptPanel: () => TranscriptPanel,
|
|
25
36
|
VideoEditor: () => VideoEditor,
|
|
26
37
|
VideoPreview: () => VideoPreview,
|
|
27
38
|
VideoTimeline: () => VideoTimeline,
|
|
@@ -440,6 +451,7 @@ function VideoTimeline({ controller, pxPerSecond, onPxPerSecondChange, editable
|
|
|
440
451
|
// src/video-editor/ClipInspector.tsx
|
|
441
452
|
var import_video_editor5 = require("@pixerate/editor/video-editor");
|
|
442
453
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
454
|
+
var btn2 = "rounded border border-border px-1.5 py-0.5 text-[11px] disabled:opacity-40";
|
|
443
455
|
var field = "w-full rounded border border-border bg-transparent px-1 py-0.5 text-xs disabled:opacity-50";
|
|
444
456
|
var DEFAULT_TRANSITION = 500;
|
|
445
457
|
function Commit({
|
|
@@ -607,7 +619,24 @@ function ClipInspector({ controller, className = "" }) {
|
|
|
607
619
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Row, { label: "Fade in (ms)", name: "fadeIn", children: number("fadeIn", "Fade in", { min: 0, step: 100 }) }),
|
|
608
620
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Row, { label: "Fade out (ms)", name: "fadeOut", children: number("fadeOut", "Fade out", { min: 0, step: 100 }) }),
|
|
609
621
|
clip.type === "media" && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
610
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Row, { label: "Speed", name: "speed", children:
|
|
622
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Row, { label: "Speed", name: "speed", children: clip.timeRemap ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "flex items-center gap-1", "data-ramped": "", children: [
|
|
623
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-muted-foreground", children: "ramped" }),
|
|
624
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: btn2, disabled, onClick: () => controller.apply({ op: "set_time_remap", clip: clip.id, keys: null, ripple: true }, "Remove speed ramp"), children: "Remove ramp" })
|
|
625
|
+
] }) : number("speed", "Speed", { min: 0.1, step: 0.25 }) }),
|
|
626
|
+
project.assets.find((a) => a.id === clip.asset)?.kind !== "image" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Row, { label: "Retime", name: "retime", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "flex flex-wrap gap-1", children: [
|
|
627
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
628
|
+
"button",
|
|
629
|
+
{
|
|
630
|
+
type: "button",
|
|
631
|
+
className: btn2,
|
|
632
|
+
disabled: disabled || time < clip.start || time > clip.start + clip.duration,
|
|
633
|
+
title: "Hold the frame at the playhead for a second, pushing later clips along",
|
|
634
|
+
onClick: () => controller.apply((0, import_video_editor5.videoFreezeFrameEdit)(clip, time - clip.start, 1e3), "Freeze frame"),
|
|
635
|
+
children: "Freeze 1 s here"
|
|
636
|
+
}
|
|
637
|
+
),
|
|
638
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: btn2, disabled, title: "Normal speed, then a glide into 0.3\xD7 slow motion", onClick: () => controller.apply((0, import_video_editor5.videoSpeedRampEdit)(clip, (0, import_video_editor5.videoSlowMotionRamp)(clip.duration)), "Speed ramp"), children: "Slow-mo ramp" })
|
|
639
|
+
] }) }),
|
|
611
640
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Row, { label: "Muted", name: "muted", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("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") }) }),
|
|
612
641
|
["fadeInCurve", "fadeOutCurve"].map(
|
|
613
642
|
(key) => (key === "fadeInCurve" ? clip.fadeIn : clip.fadeOut) ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Row, { label: key === "fadeInCurve" ? "Fade in sound" : "Fade out sound", name: key, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
@@ -702,7 +731,7 @@ function ClipInspector({ controller, className = "" }) {
|
|
|
702
731
|
var import_react4 = require("react");
|
|
703
732
|
var import_video_editor6 = require("@pixerate/editor/video-editor");
|
|
704
733
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
705
|
-
var
|
|
734
|
+
var btn3 = "rounded border border-border px-2 py-0.5 text-xs disabled:opacity-40";
|
|
706
735
|
function MediaBin({ controller, editable = true, fileToUrl, probe = import_video_editor6.probeVideoMedia, className = "" }) {
|
|
707
736
|
const { project, readOnly } = useVideoEditorState(controller);
|
|
708
737
|
const canEdit = editable && !readOnly;
|
|
@@ -781,7 +810,7 @@ function MediaBin({ controller, editable = true, fileToUrl, probe = import_video
|
|
|
781
810
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "rounded bg-muted px-1 text-[10px] uppercase text-muted-foreground", "data-asset-kind": "", children: asset.kind }),
|
|
782
811
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "min-w-0 flex-1 truncate", title: asset.src, children: asset.name ?? asset.id }),
|
|
783
812
|
asset.duration !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "tabular-nums text-muted-foreground", children: (0, import_video_editor6.formatVideoSeconds)(asset.duration) }),
|
|
784
|
-
editable && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className:
|
|
813
|
+
editable && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: btn3, disabled: !canEdit, onClick: () => addToTimeline(asset), "data-add-to-timeline": "", children: "Add to timeline" })
|
|
785
814
|
] }, asset.id)) }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("p", { className: "text-xs text-muted-foreground", children: [
|
|
786
815
|
"No media yet.",
|
|
787
816
|
editable ? " Choose or drop files, or add a URL." : ""
|
|
@@ -832,7 +861,7 @@ function MediaBin({ controller, editable = true, fileToUrl, probe = import_video
|
|
|
832
861
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: "audio", children: "Audio" }),
|
|
833
862
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: "image", children: "Image" })
|
|
834
863
|
] }),
|
|
835
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "submit", className:
|
|
864
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "submit", className: btn3, disabled: !canEdit || !url.trim(), "data-add-url": "", children: "Add" })
|
|
836
865
|
]
|
|
837
866
|
}
|
|
838
867
|
)
|
|
@@ -844,29 +873,134 @@ function MediaBin({ controller, editable = true, fileToUrl, probe = import_video
|
|
|
844
873
|
);
|
|
845
874
|
}
|
|
846
875
|
|
|
847
|
-
// src/video-editor/
|
|
848
|
-
var import_react5 = require("react");
|
|
876
|
+
// src/video-editor/TranscriptPanel.tsx
|
|
877
|
+
var import_react5 = __toESM(require("react"), 1);
|
|
878
|
+
var import_video_editor7 = require("@pixerate/editor/video-editor");
|
|
849
879
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
850
|
-
|
|
880
|
+
var btn4 = "rounded border border-border px-1.5 py-0.5 text-[11px] disabled:opacity-40";
|
|
881
|
+
function TranscriptPanel({ controller, track, editable = true, className = "" }) {
|
|
882
|
+
const state = useVideoEditorState(controller);
|
|
883
|
+
const { project, time, readOnly } = state;
|
|
884
|
+
const canEdit = editable && !readOnly;
|
|
885
|
+
const [anchor, setAnchor] = (0, import_react5.useState)(null);
|
|
886
|
+
const [focus, setFocus] = (0, import_react5.useState)(null);
|
|
887
|
+
const [message, setMessage] = (0, import_react5.useState)(null);
|
|
888
|
+
const words = (0, import_react5.useMemo)(() => {
|
|
889
|
+
try {
|
|
890
|
+
return (0, import_video_editor7.videoTimelineTranscript)(project, { track });
|
|
891
|
+
} catch {
|
|
892
|
+
return [];
|
|
893
|
+
}
|
|
894
|
+
}, [project, track]);
|
|
895
|
+
const current = (0, import_video_editor7.videoWordAt)(words, time);
|
|
896
|
+
const selected = anchor !== null && focus !== null && anchor < words.length && focus < words.length ? [Math.min(anchor, focus), Math.max(anchor, focus)] : null;
|
|
897
|
+
const sound = project.assets.filter((a) => a.kind !== "image");
|
|
898
|
+
const cut = (ranges, label, none) => {
|
|
899
|
+
if (!ranges.length) return setMessage(none);
|
|
900
|
+
const r = controller.apply((0, import_video_editor7.videoRemoveRangesEdits)(ranges), label);
|
|
901
|
+
setMessage(r.ok ? null : r.error);
|
|
902
|
+
setAnchor(null);
|
|
903
|
+
setFocus(null);
|
|
904
|
+
};
|
|
905
|
+
const load = async (assetId, file) => {
|
|
906
|
+
if (!file) return;
|
|
907
|
+
try {
|
|
908
|
+
const r = controller.apply((0, import_video_editor7.videoTranscriptEdit)(assetId, await file.text()), "Attach transcript");
|
|
909
|
+
setMessage(r.ok ? null : r.error);
|
|
910
|
+
} catch (e) {
|
|
911
|
+
setMessage(e instanceof Error ? e.message : String(e));
|
|
912
|
+
}
|
|
913
|
+
};
|
|
914
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: `flex flex-col gap-2 text-xs ${className}`, "data-transcript-panel": "", children: [
|
|
915
|
+
words.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
916
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
917
|
+
"div",
|
|
918
|
+
{
|
|
919
|
+
className: "max-h-48 overflow-y-auto leading-6",
|
|
920
|
+
role: "listbox",
|
|
921
|
+
"aria-label": "Transcript",
|
|
922
|
+
"aria-multiselectable": "true",
|
|
923
|
+
tabIndex: 0,
|
|
924
|
+
onKeyDown: (e) => {
|
|
925
|
+
if (canEdit && selected && (e.key === "Delete" || e.key === "Backspace")) {
|
|
926
|
+
e.preventDefault();
|
|
927
|
+
cut([(0, import_video_editor7.videoWordsRange)(words, selected[0], selected[1])], "Cut words", "Select words to cut.");
|
|
928
|
+
}
|
|
929
|
+
},
|
|
930
|
+
children: words.map((w, i) => {
|
|
931
|
+
const inSelection = selected !== null && i >= selected[0] && i <= selected[1];
|
|
932
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react5.default.Fragment, { children: [
|
|
933
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
934
|
+
"span",
|
|
935
|
+
{
|
|
936
|
+
role: "option",
|
|
937
|
+
"aria-selected": inSelection,
|
|
938
|
+
tabIndex: -1,
|
|
939
|
+
className: `cursor-pointer rounded px-0.5 ${inSelection ? "bg-rose-500/30" : i === current ? "bg-amber-400/40" : "hover:bg-muted"}`,
|
|
940
|
+
"data-word": i,
|
|
941
|
+
"data-current": i === current ? "" : void 0,
|
|
942
|
+
"data-selected": inSelection ? "" : void 0,
|
|
943
|
+
onClick: (e) => {
|
|
944
|
+
if (e.shiftKey && anchor !== null) setFocus(i);
|
|
945
|
+
else {
|
|
946
|
+
setAnchor(i);
|
|
947
|
+
setFocus(i);
|
|
948
|
+
controller.seek(w.start);
|
|
949
|
+
}
|
|
950
|
+
},
|
|
951
|
+
children: w.word
|
|
952
|
+
}
|
|
953
|
+
),
|
|
954
|
+
" "
|
|
955
|
+
] }, `${w.clip}:${w.index}`);
|
|
956
|
+
})
|
|
957
|
+
}
|
|
958
|
+
),
|
|
959
|
+
canEdit && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-wrap gap-1", children: [
|
|
960
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: btn4, disabled: !selected, onClick: () => selected && cut([(0, import_video_editor7.videoWordsRange)(words, selected[0], selected[1])], "Cut words", "Select words to cut."), children: "Delete selected words" }),
|
|
961
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: btn4, onClick: () => cut((0, import_video_editor7.findVideoFillers)(words), "Remove fillers", "No filler words found."), children: "Remove fillers" }),
|
|
962
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: btn4, onClick: () => cut((0, import_video_editor7.findVideoTranscriptPauses)(words, { fps: project.fps }), "Tighten pauses", "No long pauses found."), children: "Tighten pauses" })
|
|
963
|
+
] })
|
|
964
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-muted-foreground", "data-transcript-empty": "", children: "No transcribed media on the timeline. Load a transcript (Whisper JSON, .srt or .vtt) for a video or audio asset to edit by text." }),
|
|
965
|
+
canEdit && sound.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("details", { children: [
|
|
966
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("summary", { className: "cursor-pointer text-muted-foreground", children: "Transcripts" }),
|
|
967
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("ul", { className: "mt-1 space-y-1", children: sound.map((a) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("li", { className: "flex items-center gap-2", children: [
|
|
968
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "min-w-0 flex-1 truncate", children: [
|
|
969
|
+
a.name ?? a.id,
|
|
970
|
+
" ",
|
|
971
|
+
a.transcript ? `\xB7 ${a.transcript.length} words` : ""
|
|
972
|
+
] }),
|
|
973
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "file", accept: ".json,.srt,.vtt,application/json,text/vtt", "aria-label": `Load a transcript for ${a.name ?? a.id}`, className: "w-40 text-[10px]", onChange: (e) => void load(a.id, e.target.files?.[0]) })
|
|
974
|
+
] }, a.id)) })
|
|
975
|
+
] }),
|
|
976
|
+
message && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-red-500", role: "alert", "data-transcript-error": "", children: message })
|
|
977
|
+
] });
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
// src/video-editor/VideoEditor.tsx
|
|
981
|
+
var import_react6 = require("react");
|
|
982
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
983
|
+
function VideoEditor({ controller: given, options, editable = true, pxPerSecond, onPxPerSecondChange, resolution, showIssues = true, showTranscript = true, className = "" }) {
|
|
851
984
|
const { controller, state } = useVideoEditor(given ?? options ?? {});
|
|
852
|
-
(0,
|
|
985
|
+
(0, import_react6.useEffect)(() => {
|
|
853
986
|
if (editable) return;
|
|
854
987
|
const was = controller.getState().readOnly;
|
|
855
988
|
controller.setReadOnly(true);
|
|
856
989
|
return () => controller.setReadOnly(was);
|
|
857
990
|
}, [controller, editable]);
|
|
858
991
|
const { issues } = state;
|
|
859
|
-
const issueList = showIssues && issues.length > 0 && /* @__PURE__ */ (0,
|
|
860
|
-
return /* @__PURE__ */ (0,
|
|
861
|
-
/* @__PURE__ */ (0,
|
|
862
|
-
editable && /* @__PURE__ */ (0,
|
|
863
|
-
/* @__PURE__ */ (0,
|
|
864
|
-
/* @__PURE__ */ (0,
|
|
865
|
-
/* @__PURE__ */ (0,
|
|
992
|
+
const issueList = showIssues && issues.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("ul", { className: "space-y-0.5 text-xs", "aria-label": "Video issues", "data-video-issues": "", children: issues.map((issue, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("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)) });
|
|
993
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: `flex flex-col gap-3 ${className}`, "data-video-editor": "", children: [
|
|
994
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: `grid gap-3 ${editable ? "lg:grid-cols-[16rem_minmax(0,1fr)_18rem]" : ""}`, children: [
|
|
995
|
+
editable && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MediaBin, { controller, className: "min-w-0" }),
|
|
996
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex min-w-0 flex-col gap-2", children: [
|
|
997
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(VideoPreview, { controller, resolution, className: "rounded-md border border-border" }),
|
|
998
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(VideoTransport, { controller, editable })
|
|
866
999
|
] }),
|
|
867
|
-
editable && /* @__PURE__ */ (0,
|
|
1000
|
+
editable && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ClipInspector, { controller, className: "min-w-0" })
|
|
868
1001
|
] }),
|
|
869
|
-
/* @__PURE__ */ (0,
|
|
1002
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(VideoTimeline, { controller, editable, pxPerSecond, onPxPerSecondChange }),
|
|
1003
|
+
showTranscript && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TranscriptPanel, { controller, editable }),
|
|
870
1004
|
issueList
|
|
871
1005
|
] });
|
|
872
1006
|
}
|
|
@@ -874,6 +1008,7 @@ function VideoEditor({ controller: given, options, editable = true, pxPerSecond,
|
|
|
874
1008
|
0 && (module.exports = {
|
|
875
1009
|
ClipInspector,
|
|
876
1010
|
MediaBin,
|
|
1011
|
+
TranscriptPanel,
|
|
877
1012
|
VideoEditor,
|
|
878
1013
|
VideoPreview,
|
|
879
1014
|
VideoTimeline,
|