@pixerate/editor-react 0.57.0 → 0.58.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.
@@ -518,6 +518,7 @@ function VideoTimeline({ controller, pxPerSecond, onPxPerSecondChange, editable
518
518
  }
519
519
 
520
520
  // src/video-editor/ClipInspector.tsx
521
+ var import_react5 = require("react");
521
522
  var import_video_editor6 = require("@pixerate/editor/video-editor");
522
523
  var import_jsx_runtime5 = require("react/jsx-runtime");
523
524
  var btn2 = "rounded border border-border px-1.5 py-0.5 text-[11px] disabled:opacity-40";
@@ -547,6 +548,59 @@ function Commit({
547
548
  );
548
549
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("input", { className: field, ...rest, defaultValue: value, onKeyDown: (e) => e.key === "Enter" && e.currentTarget.blur(), onBlur: (e) => commit(e.currentTarget.value) }, value);
549
550
  }
551
+ function LutControls({ controller, clip, disabled }) {
552
+ const [message, setMessage] = (0, import_react5.useState)(null);
553
+ const lut = clip.grade?.lut;
554
+ const apply = (setting, label) => {
555
+ const r = controller.apply((0, import_video_editor6.videoLutEdits)(controller.getProject(), [clip.id], setting), label);
556
+ setMessage(r.ok ? null : r.error);
557
+ };
558
+ const choose = async (src) => {
559
+ if (!src) return lut && apply(null, "Remove LUT");
560
+ try {
561
+ const table = await controller.luts.load(src);
562
+ apply({ src, ...lut?.strength !== void 0 ? { strength: lut.strength } : {}, ...table.title ? { name: table.title } : {} }, "Apply LUT");
563
+ } catch (e) {
564
+ setMessage(e instanceof Error ? e.message : String(e));
565
+ }
566
+ };
567
+ const readFile = async (file) => {
568
+ if (!file) return;
569
+ try {
570
+ const table = controller.luts.set(file.name, await file.text());
571
+ apply({ src: file.name, ...lut?.strength !== void 0 ? { strength: lut.strength } : {}, ...table.title ? { name: table.title } : {} }, "Apply LUT");
572
+ } catch (e) {
573
+ setMessage(`${file.name}: ${e instanceof Error ? e.message : String(e)}`);
574
+ }
575
+ };
576
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
577
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Row, { label: "LUT", name: "grade.lut", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex min-w-0 gap-1", children: [
578
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Commit, { value: lut?.src ?? "", placeholder: "look.cube", "aria-label": "LUT file", disabled, onCommit: (raw) => void choose(raw.trim()) }),
579
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: `${btn2} cursor-pointer whitespace-nowrap`, title: "Load a .cube file", children: [
580
+ "Load",
581
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("input", { type: "file", accept: ".cube", className: "sr-only", disabled, "aria-label": "Load a .cube LUT", onChange: (e) => void readFile(e.currentTarget.files?.[0]) })
582
+ ] }),
583
+ lut && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: btn2, disabled, "aria-label": "Remove LUT", onClick: () => apply(null, "Remove LUT"), children: "\u2715" })
584
+ ] }) }),
585
+ lut && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Row, { label: "LUT strength", name: "grade.lut.strength", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
586
+ Commit,
587
+ {
588
+ type: "number",
589
+ min: 0,
590
+ max: 1,
591
+ step: 0.05,
592
+ value: String(lut.strength ?? 1),
593
+ "aria-label": "LUT strength",
594
+ disabled,
595
+ onCommit: (raw) => {
596
+ const n = Number(raw);
597
+ if (raw.trim() && Number.isFinite(n)) apply(n > 0 ? { ...lut, strength: Math.min(1, n) } : null, n > 0 ? "Change LUT strength" : "Remove LUT");
598
+ }
599
+ }
600
+ ) }),
601
+ message && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-xs text-red-500", role: "alert", "data-lut-error": "", children: message })
602
+ ] });
603
+ }
550
604
  function Row({ label, name, children }) {
551
605
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "grid grid-cols-[5.5rem_minmax(0,1fr)] items-center gap-2 text-xs", "data-field": name, children: [
552
606
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-muted-foreground", children: label }),
@@ -639,7 +693,7 @@ function ClipInspector({ controller, className = "" }) {
639
693
  disabled,
640
694
  "aria-label": "Apply a look",
641
695
  onChange: (e) => {
642
- const grade = (0, import_video_editor6.videoGradePreset)(e.target.value);
696
+ const grade = (0, import_video_editor6.videoGradeLook)(clip.grade, e.target.value);
643
697
  if (grade) controller.apply({ op: "update_clip", clip: clip.id, set: { grade } }, `Apply ${e.target.value} look`);
644
698
  },
645
699
  children: [
@@ -666,7 +720,8 @@ function ClipInspector({ controller, className = "" }) {
666
720
  set("grade", grade, `Change ${c.label.toLowerCase()}`);
667
721
  }
668
722
  }
669
- ) }, c.key))
723
+ ) }, c.key)),
724
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(LutControls, { controller, clip, disabled })
670
725
  ] }),
671
726
  at.track.kind === "video" && ["animateIn", "animateOut"].map((key) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Row, { label: key === "animateIn" ? "Animate in" : "Animate out", name: key, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
672
727
  "select",
@@ -797,18 +852,18 @@ function ClipInspector({ controller, className = "" }) {
797
852
  }
798
853
 
799
854
  // src/video-editor/MediaBin.tsx
800
- var import_react5 = require("react");
855
+ var import_react6 = require("react");
801
856
  var import_video_editor7 = require("@pixerate/editor/video-editor");
802
857
  var import_jsx_runtime6 = require("react/jsx-runtime");
803
858
  var btn3 = "rounded border border-border px-2 py-0.5 text-xs disabled:opacity-40";
804
859
  function MediaBin({ controller, editable = true, fileToUrl, probe = import_video_editor7.probeVideoMedia, className = "" }) {
805
860
  const { project, readOnly } = useVideoEditorState(controller);
806
861
  const canEdit = editable && !readOnly;
807
- const [url, setUrl] = (0, import_react5.useState)("");
808
- const [urlKind, setUrlKind] = (0, import_react5.useState)("");
809
- const [busy, setBusy] = (0, import_react5.useState)(0);
810
- const [problem, setProblem] = (0, import_react5.useState)(null);
811
- const [over, setOver] = (0, import_react5.useState)(false);
862
+ const [url, setUrl] = (0, import_react6.useState)("");
863
+ const [urlKind, setUrlKind] = (0, import_react6.useState)("");
864
+ const [busy, setBusy] = (0, import_react6.useState)(0);
865
+ const [problem, setProblem] = (0, import_react6.useState)(null);
866
+ const [over, setOver] = (0, import_react6.useState)(false);
812
867
  const addSource = async (src, kind, name) => {
813
868
  setBusy((n) => n + 1);
814
869
  try {
@@ -943,7 +998,7 @@ function MediaBin({ controller, editable = true, fileToUrl, probe = import_video
943
998
  }
944
999
 
945
1000
  // src/video-editor/TranscriptPanel.tsx
946
- var import_react6 = __toESM(require("react"), 1);
1001
+ var import_react7 = __toESM(require("react"), 1);
947
1002
  var import_video_editor8 = require("@pixerate/editor/video-editor");
948
1003
  var import_jsx_runtime7 = require("react/jsx-runtime");
949
1004
  var btn4 = "rounded border border-border px-1.5 py-0.5 text-[11px] disabled:opacity-40";
@@ -951,10 +1006,10 @@ function TranscriptPanel({ controller, track, editable = true, className = "" })
951
1006
  const state = useVideoEditorState(controller);
952
1007
  const { project, time, readOnly } = state;
953
1008
  const canEdit = editable && !readOnly;
954
- const [anchor, setAnchor] = (0, import_react6.useState)(null);
955
- const [focus, setFocus] = (0, import_react6.useState)(null);
956
- const [message, setMessage] = (0, import_react6.useState)(null);
957
- const words = (0, import_react6.useMemo)(() => {
1009
+ const [anchor, setAnchor] = (0, import_react7.useState)(null);
1010
+ const [focus, setFocus] = (0, import_react7.useState)(null);
1011
+ const [message, setMessage] = (0, import_react7.useState)(null);
1012
+ const words = (0, import_react7.useMemo)(() => {
958
1013
  try {
959
1014
  return (0, import_video_editor8.videoTimelineTranscript)(project, { track });
960
1015
  } catch {
@@ -998,7 +1053,7 @@ function TranscriptPanel({ controller, track, editable = true, className = "" })
998
1053
  },
999
1054
  children: words.map((w, i) => {
1000
1055
  const inSelection = selected !== null && i >= selected[0] && i <= selected[1];
1001
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react6.default.Fragment, { children: [
1056
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react7.default.Fragment, { children: [
1002
1057
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1003
1058
  "span",
1004
1059
  {
@@ -1047,11 +1102,11 @@ function TranscriptPanel({ controller, track, editable = true, className = "" })
1047
1102
  }
1048
1103
 
1049
1104
  // src/video-editor/VideoEditor.tsx
1050
- var import_react7 = require("react");
1105
+ var import_react8 = require("react");
1051
1106
  var import_jsx_runtime8 = require("react/jsx-runtime");
1052
1107
  function VideoEditor({ controller: given, options, editable = true, pxPerSecond, onPxPerSecondChange, resolution, showIssues = true, showTranscript = true, showScopes = false, className = "" }) {
1053
1108
  const { controller, state } = useVideoEditor(given ?? options ?? {});
1054
- (0, import_react7.useEffect)(() => {
1109
+ (0, import_react8.useEffect)(() => {
1055
1110
  if (editable) return;
1056
1111
  const was = controller.getState().readOnly;
1057
1112
  controller.setReadOnly(true);