@twick/video-editor 0.15.14 → 0.15.15
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/components/controls/control-manager.d.ts +2 -1
- package/dist/components/controls/player-controls.d.ts +13 -3
- package/dist/components/controls/seek-control.d.ts +3 -1
- package/dist/components/player/canvas-context-menu.d.ts +18 -0
- package/dist/components/player/player-manager.d.ts +5 -1
- package/dist/components/timeline/marquee-overlay.d.ts +12 -0
- package/dist/components/timeline/timeline-view.d.ts +21 -3
- package/dist/components/track/seek-track.d.ts +7 -1
- package/dist/components/track/track-base.d.ts +3 -2
- package/dist/components/track/track-element.d.ts +2 -1
- package/dist/components/track/track-header.d.ts +3 -3
- package/dist/components/video-editor.d.ts +6 -1
- package/dist/helpers/asset-type.d.ts +6 -0
- package/dist/helpers/constants.d.ts +12 -0
- package/dist/helpers/snap-targets.d.ts +7 -0
- package/dist/helpers/types.d.ts +10 -0
- package/dist/hooks/use-canvas-drop.d.ts +25 -0
- package/dist/hooks/use-canvas-keyboard.d.ts +13 -0
- package/dist/hooks/use-marquee-selection.d.ts +24 -0
- package/dist/hooks/use-player-manager.d.ts +14 -2
- package/dist/hooks/use-playhead-scroll.d.ts +19 -0
- package/dist/hooks/use-timeline-control.d.ts +1 -1
- package/dist/hooks/use-timeline-drop.d.ts +40 -0
- package/dist/hooks/use-timeline-selection.d.ts +13 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1961 -628
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1963 -630
- package/dist/index.mjs.map +1 -1
- package/dist/video-editor.css +180 -31
- package/package.json +5 -5
package/dist/index.mjs
CHANGED
|
@@ -3,8 +3,8 @@ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { en
|
|
|
3
3
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
4
|
import { jsxs, jsx } from "react/jsx-runtime";
|
|
5
5
|
import { useLivePlayerContext, PLAYER_STATE, LivePlayer } from "@twick/live-player";
|
|
6
|
-
import {
|
|
7
|
-
import React, { useState, useRef, useEffect, useMemo, forwardRef, createElement, createContext, useContext, useId,
|
|
6
|
+
import { ImageElement as ImageElement$1, AudioElement, VideoElement as VideoElement$1, useTimelineContext, TIMELINE_ACTION, ElementDeserializer, getCurrentElements, CaptionElement as CaptionElement$1, getDecimalNumber, resolveIds, TrackElement, resolveId, Track, getElementIdsInRange, ValidationError, VALIDATION_ERROR_CODE, formatTimeWithFrames } from "@twick/timeline";
|
|
7
|
+
import React, { useState, useRef, useCallback, useEffect, useMemo, forwardRef, createElement, createContext, useContext, useId, useLayoutEffect, useInsertionEffect, Fragment, Component } from "react";
|
|
8
8
|
function t(t2, e3, s2) {
|
|
9
9
|
return (e3 = function(t3) {
|
|
10
10
|
var e4 = function(t4, e5) {
|
|
@@ -6882,7 +6882,11 @@ const CANVAS_OPERATIONS = {
|
|
|
6882
6882
|
/** Caption properties have been updated */
|
|
6883
6883
|
CAPTION_PROPS_UPDATED: "CAPTION_PROPS_UPDATED",
|
|
6884
6884
|
/** Watermark has been updated */
|
|
6885
|
-
WATERMARK_UPDATED: "WATERMARK_UPDATED"
|
|
6885
|
+
WATERMARK_UPDATED: "WATERMARK_UPDATED",
|
|
6886
|
+
/** A new element was added via drop on canvas; payload is { element } */
|
|
6887
|
+
ADDED_NEW_ELEMENT: "ADDED_NEW_ELEMENT",
|
|
6888
|
+
/** Z-order changed (bring to front / send to back). Payload is { elementId, direction }. Timeline should reorder tracks. */
|
|
6889
|
+
Z_ORDER_CHANGED: "Z_ORDER_CHANGED"
|
|
6886
6890
|
};
|
|
6887
6891
|
const ELEMENT_TYPES = {
|
|
6888
6892
|
/** Text element type */
|
|
@@ -6988,6 +6992,49 @@ const reorderElementsByZIndex = (canvas) => {
|
|
|
6988
6992
|
objects.forEach((obj) => canvas.add(obj));
|
|
6989
6993
|
canvas.renderAll();
|
|
6990
6994
|
};
|
|
6995
|
+
const changeZOrder = (canvas, elementId, direction) => {
|
|
6996
|
+
var _a, _b;
|
|
6997
|
+
if (!canvas) return null;
|
|
6998
|
+
const objects = canvas.getObjects();
|
|
6999
|
+
const sorted = [...objects].sort((a2, b2) => (a2.zIndex || 0) - (b2.zIndex || 0));
|
|
7000
|
+
const idx = sorted.findIndex((obj2) => {
|
|
7001
|
+
var _a2;
|
|
7002
|
+
return ((_a2 = obj2.get) == null ? void 0 : _a2.call(obj2, "id")) === elementId;
|
|
7003
|
+
});
|
|
7004
|
+
if (idx < 0) return null;
|
|
7005
|
+
const minZ = ((_a = sorted[0]) == null ? void 0 : _a.zIndex) ?? 0;
|
|
7006
|
+
const maxZ = ((_b = sorted[sorted.length - 1]) == null ? void 0 : _b.zIndex) ?? 0;
|
|
7007
|
+
const obj = sorted[idx];
|
|
7008
|
+
if (direction === "front") {
|
|
7009
|
+
obj.set("zIndex", maxZ + 1);
|
|
7010
|
+
reorderElementsByZIndex(canvas);
|
|
7011
|
+
return maxZ + 1;
|
|
7012
|
+
}
|
|
7013
|
+
if (direction === "back") {
|
|
7014
|
+
obj.set("zIndex", minZ - 1);
|
|
7015
|
+
reorderElementsByZIndex(canvas);
|
|
7016
|
+
return minZ - 1;
|
|
7017
|
+
}
|
|
7018
|
+
if (direction === "forward" && idx < sorted.length - 1) {
|
|
7019
|
+
const next = sorted[idx + 1];
|
|
7020
|
+
const myZ = obj.zIndex ?? idx;
|
|
7021
|
+
const nextZ = next.zIndex ?? idx + 1;
|
|
7022
|
+
obj.set("zIndex", nextZ);
|
|
7023
|
+
next.set("zIndex", myZ);
|
|
7024
|
+
reorderElementsByZIndex(canvas);
|
|
7025
|
+
return nextZ;
|
|
7026
|
+
}
|
|
7027
|
+
if (direction === "backward" && idx > 0) {
|
|
7028
|
+
const prev = sorted[idx - 1];
|
|
7029
|
+
const myZ = obj.zIndex ?? idx;
|
|
7030
|
+
const prevZ = prev.zIndex ?? idx - 1;
|
|
7031
|
+
obj.set("zIndex", prevZ);
|
|
7032
|
+
prev.set("zIndex", myZ);
|
|
7033
|
+
reorderElementsByZIndex(canvas);
|
|
7034
|
+
return prevZ;
|
|
7035
|
+
}
|
|
7036
|
+
return obj.zIndex ?? idx;
|
|
7037
|
+
};
|
|
6991
7038
|
const getCanvasContext = (canvas) => {
|
|
6992
7039
|
var _a, _b, _c, _d;
|
|
6993
7040
|
if (!canvas || !((_b = (_a = canvas.elements) == null ? void 0 : _a.lower) == null ? void 0 : _b.ctx)) return;
|
|
@@ -7008,12 +7055,31 @@ const convertToCanvasPosition = (x2, y2, canvasMetadata) => {
|
|
|
7008
7055
|
y: y2 * canvasMetadata.scaleY + canvasMetadata.height / 2
|
|
7009
7056
|
};
|
|
7010
7057
|
};
|
|
7058
|
+
const getObjectCanvasCenter = (obj) => {
|
|
7059
|
+
if (obj.getCenterPoint) {
|
|
7060
|
+
const p2 = obj.getCenterPoint();
|
|
7061
|
+
return { x: p2.x, y: p2.y };
|
|
7062
|
+
}
|
|
7063
|
+
return { x: obj.left ?? 0, y: obj.top ?? 0 };
|
|
7064
|
+
};
|
|
7065
|
+
const getObjectCanvasAngle = (obj) => {
|
|
7066
|
+
if (typeof obj.getTotalAngle === "function") {
|
|
7067
|
+
return obj.getTotalAngle();
|
|
7068
|
+
}
|
|
7069
|
+
return obj.angle ?? 0;
|
|
7070
|
+
};
|
|
7011
7071
|
const convertToVideoPosition = (x2, y2, canvasMetadata, videoSize) => {
|
|
7012
7072
|
return {
|
|
7013
7073
|
x: Number((x2 / canvasMetadata.scaleX - videoSize.width / 2).toFixed(2)),
|
|
7014
7074
|
y: Number((y2 / canvasMetadata.scaleY - videoSize.height / 2).toFixed(2))
|
|
7015
7075
|
};
|
|
7016
7076
|
};
|
|
7077
|
+
const convertToVideoDimensions = (widthCanvas, heightCanvas, canvasMetadata) => {
|
|
7078
|
+
return {
|
|
7079
|
+
width: Number((widthCanvas / canvasMetadata.scaleX).toFixed(2)),
|
|
7080
|
+
height: Number((heightCanvas / canvasMetadata.scaleY).toFixed(2))
|
|
7081
|
+
};
|
|
7082
|
+
};
|
|
7017
7083
|
const getCurrentFrameEffect = (item, seekTime) => {
|
|
7018
7084
|
var _a;
|
|
7019
7085
|
let currentFrameEffect;
|
|
@@ -7547,7 +7613,8 @@ const setImageProps = ({
|
|
|
7547
7613
|
img,
|
|
7548
7614
|
element,
|
|
7549
7615
|
index,
|
|
7550
|
-
canvasMetadata
|
|
7616
|
+
canvasMetadata,
|
|
7617
|
+
lockAspectRatio = true
|
|
7551
7618
|
}) => {
|
|
7552
7619
|
var _a, _b, _c, _d, _e2;
|
|
7553
7620
|
const width = (((_a = element.props) == null ? void 0 : _a.width) || 0) * canvasMetadata.scaleX || canvasMetadata.width;
|
|
@@ -7567,13 +7634,15 @@ const setImageProps = ({
|
|
|
7567
7634
|
img.set("selectable", true);
|
|
7568
7635
|
img.set("hasControls", true);
|
|
7569
7636
|
img.set("touchAction", "all");
|
|
7637
|
+
img.set("lockUniScaling", lockAspectRatio);
|
|
7570
7638
|
};
|
|
7571
7639
|
const addCaptionElement = ({
|
|
7572
7640
|
element,
|
|
7573
7641
|
index,
|
|
7574
7642
|
canvas,
|
|
7575
7643
|
captionProps,
|
|
7576
|
-
canvasMetadata
|
|
7644
|
+
canvasMetadata,
|
|
7645
|
+
lockAspectRatio = false
|
|
7577
7646
|
}) => {
|
|
7578
7647
|
var _a, _b, _c, _d, _e2, _f, _g, _h2, _i2, _j, _k, _l, _m, _n2, _o2, _p, _q, _r2, _s2, _t2, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J;
|
|
7579
7648
|
const { x: x2, y: y2 } = convertToCanvasPosition(
|
|
@@ -7612,6 +7681,7 @@ const addCaptionElement = ({
|
|
|
7612
7681
|
});
|
|
7613
7682
|
caption.set("id", element.id);
|
|
7614
7683
|
caption.set("zIndex", index);
|
|
7684
|
+
caption.set("lockUniScaling", lockAspectRatio);
|
|
7615
7685
|
caption.controls.mt = disabledControl;
|
|
7616
7686
|
caption.controls.mb = disabledControl;
|
|
7617
7687
|
caption.controls.ml = disabledControl;
|
|
@@ -7659,7 +7729,8 @@ const addImageElement = async ({
|
|
|
7659
7729
|
index,
|
|
7660
7730
|
canvas,
|
|
7661
7731
|
canvasMetadata,
|
|
7662
|
-
currentFrameEffect
|
|
7732
|
+
currentFrameEffect,
|
|
7733
|
+
lockAspectRatio = true
|
|
7663
7734
|
}) => {
|
|
7664
7735
|
try {
|
|
7665
7736
|
const img = await oa.fromURL(imageUrl || element.props.src || "");
|
|
@@ -7668,7 +7739,7 @@ const addImageElement = async ({
|
|
|
7668
7739
|
originY: "center",
|
|
7669
7740
|
lockMovementX: false,
|
|
7670
7741
|
lockMovementY: false,
|
|
7671
|
-
lockUniScaling:
|
|
7742
|
+
lockUniScaling: lockAspectRatio,
|
|
7672
7743
|
hasControls: false,
|
|
7673
7744
|
selectable: false
|
|
7674
7745
|
});
|
|
@@ -7679,10 +7750,11 @@ const addImageElement = async ({
|
|
|
7679
7750
|
index,
|
|
7680
7751
|
canvas,
|
|
7681
7752
|
canvasMetadata,
|
|
7682
|
-
currentFrameEffect
|
|
7753
|
+
currentFrameEffect,
|
|
7754
|
+
lockAspectRatio
|
|
7683
7755
|
});
|
|
7684
7756
|
} else {
|
|
7685
|
-
setImageProps({ img, element, index, canvasMetadata });
|
|
7757
|
+
setImageProps({ img, element, index, canvasMetadata, lockAspectRatio });
|
|
7686
7758
|
canvas.add(img);
|
|
7687
7759
|
return img;
|
|
7688
7760
|
}
|
|
@@ -7695,7 +7767,8 @@ const addMediaGroup = ({
|
|
|
7695
7767
|
index,
|
|
7696
7768
|
canvas,
|
|
7697
7769
|
canvasMetadata,
|
|
7698
|
-
currentFrameEffect
|
|
7770
|
+
currentFrameEffect,
|
|
7771
|
+
lockAspectRatio = true
|
|
7699
7772
|
}) => {
|
|
7700
7773
|
var _a, _b, _c, _d, _e2, _f, _g, _h2, _i2, _j, _k, _l, _m, _n2;
|
|
7701
7774
|
let frameSize;
|
|
@@ -7784,6 +7857,7 @@ const addMediaGroup = ({
|
|
|
7784
7857
|
group.controls.mtr = rotateControl;
|
|
7785
7858
|
group.set("id", element.id);
|
|
7786
7859
|
group.set("zIndex", index);
|
|
7860
|
+
group.set("lockUniScaling", lockAspectRatio);
|
|
7787
7861
|
canvas.add(group);
|
|
7788
7862
|
return group;
|
|
7789
7863
|
};
|
|
@@ -7791,7 +7865,8 @@ const addRectElement = ({
|
|
|
7791
7865
|
element,
|
|
7792
7866
|
index,
|
|
7793
7867
|
canvas,
|
|
7794
|
-
canvasMetadata
|
|
7868
|
+
canvasMetadata,
|
|
7869
|
+
lockAspectRatio = false
|
|
7795
7870
|
}) => {
|
|
7796
7871
|
var _a, _b, _c, _d, _e2, _f, _g, _h2, _i2, _j, _k;
|
|
7797
7872
|
const { x: x2, y: y2 } = convertToCanvasPosition(
|
|
@@ -7829,6 +7904,7 @@ const addRectElement = ({
|
|
|
7829
7904
|
});
|
|
7830
7905
|
rect.set("id", element.id);
|
|
7831
7906
|
rect.set("zIndex", index);
|
|
7907
|
+
rect.set("lockUniScaling", lockAspectRatio);
|
|
7832
7908
|
rect.controls.mtr = rotateControl;
|
|
7833
7909
|
canvas.add(rect);
|
|
7834
7910
|
return rect;
|
|
@@ -7837,9 +7913,10 @@ const addCircleElement = ({
|
|
|
7837
7913
|
element,
|
|
7838
7914
|
index,
|
|
7839
7915
|
canvas,
|
|
7840
|
-
canvasMetadata
|
|
7916
|
+
canvasMetadata,
|
|
7917
|
+
lockAspectRatio = true
|
|
7841
7918
|
}) => {
|
|
7842
|
-
var _a, _b, _c, _d, _e2, _f;
|
|
7919
|
+
var _a, _b, _c, _d, _e2, _f, _g;
|
|
7843
7920
|
const { x: x2, y: y2 } = convertToCanvasPosition(
|
|
7844
7921
|
((_a = element.props) == null ? void 0 : _a.x) || 0,
|
|
7845
7922
|
((_b = element.props) == null ? void 0 : _b.y) || 0,
|
|
@@ -7855,7 +7932,9 @@ const addCircleElement = ({
|
|
|
7855
7932
|
stroke: ((_e2 = element.props) == null ? void 0 : _e2.stroke) || "#000000",
|
|
7856
7933
|
strokeWidth: (((_f = element.props) == null ? void 0 : _f.lineWidth) || 0) * canvasMetadata.scaleX,
|
|
7857
7934
|
originX: "center",
|
|
7858
|
-
originY: "center"
|
|
7935
|
+
originY: "center",
|
|
7936
|
+
// Respect element opacity (0–1). Defaults to fully opaque.
|
|
7937
|
+
opacity: ((_g = element.props) == null ? void 0 : _g.opacity) ?? 1
|
|
7859
7938
|
});
|
|
7860
7939
|
circle.controls.mt = disabledControl;
|
|
7861
7940
|
circle.controls.mb = disabledControl;
|
|
@@ -7864,6 +7943,7 @@ const addCircleElement = ({
|
|
|
7864
7943
|
circle.controls.mtr = disabledControl;
|
|
7865
7944
|
circle.set("id", element.id);
|
|
7866
7945
|
circle.set("zIndex", index);
|
|
7946
|
+
circle.set("lockUniScaling", lockAspectRatio);
|
|
7867
7947
|
canvas.add(circle);
|
|
7868
7948
|
return circle;
|
|
7869
7949
|
};
|
|
@@ -7933,19 +8013,23 @@ const VideoElement = {
|
|
|
7933
8013
|
}
|
|
7934
8014
|
},
|
|
7935
8015
|
updateFromFabricObject(object, element, context) {
|
|
8016
|
+
const canvasCenter = getObjectCanvasCenter(object);
|
|
7936
8017
|
const { x: x2, y: y2 } = convertToVideoPosition(
|
|
7937
|
-
|
|
7938
|
-
|
|
8018
|
+
canvasCenter.x,
|
|
8019
|
+
canvasCenter.y,
|
|
7939
8020
|
context.canvasMetadata,
|
|
7940
8021
|
context.videoSize
|
|
7941
8022
|
);
|
|
8023
|
+
const scaledW = (object.width ?? 0) * (object.scaleX ?? 1);
|
|
8024
|
+
const scaledH = (object.height ?? 0) * (object.scaleY ?? 1);
|
|
8025
|
+
const { width: fw, height: fh2 } = convertToVideoDimensions(
|
|
8026
|
+
scaledW,
|
|
8027
|
+
scaledH,
|
|
8028
|
+
context.canvasMetadata
|
|
8029
|
+
);
|
|
8030
|
+
const updatedFrameSize = [fw, fh2];
|
|
7942
8031
|
const currentFrameEffect = context.elementFrameMapRef.current[element.id];
|
|
7943
|
-
let updatedFrameSize;
|
|
7944
8032
|
if (currentFrameEffect) {
|
|
7945
|
-
updatedFrameSize = [
|
|
7946
|
-
currentFrameEffect.props.frameSize[0] * object.scaleX,
|
|
7947
|
-
currentFrameEffect.props.frameSize[1] * object.scaleY
|
|
7948
|
-
];
|
|
7949
8033
|
context.elementFrameMapRef.current[element.id] = {
|
|
7950
8034
|
...currentFrameEffect,
|
|
7951
8035
|
props: {
|
|
@@ -7971,16 +8055,12 @@ const VideoElement = {
|
|
|
7971
8055
|
};
|
|
7972
8056
|
}
|
|
7973
8057
|
const frame2 = element.frame;
|
|
7974
|
-
updatedFrameSize = [
|
|
7975
|
-
(frame2.size[0] ?? 0) * object.scaleX,
|
|
7976
|
-
(frame2.size[1] ?? 0) * object.scaleY
|
|
7977
|
-
];
|
|
7978
8058
|
return {
|
|
7979
8059
|
element: {
|
|
7980
8060
|
...element,
|
|
7981
8061
|
frame: {
|
|
7982
8062
|
...frame2,
|
|
7983
|
-
rotation: object
|
|
8063
|
+
rotation: getObjectCanvasAngle(object),
|
|
7984
8064
|
size: updatedFrameSize,
|
|
7985
8065
|
x: x2,
|
|
7986
8066
|
y: y2
|
|
@@ -7992,12 +8072,14 @@ const VideoElement = {
|
|
|
7992
8072
|
const ImageElement = {
|
|
7993
8073
|
name: ELEMENT_TYPES.IMAGE,
|
|
7994
8074
|
async add(params) {
|
|
7995
|
-
|
|
8075
|
+
var _a;
|
|
8076
|
+
const { element, index, canvas, canvasMetadata, lockAspectRatio } = params;
|
|
7996
8077
|
await addImageElement({
|
|
7997
8078
|
element,
|
|
7998
8079
|
index,
|
|
7999
8080
|
canvas,
|
|
8000
|
-
canvasMetadata
|
|
8081
|
+
canvasMetadata,
|
|
8082
|
+
lockAspectRatio: lockAspectRatio ?? ((_a = element.props) == null ? void 0 : _a.lockAspectRatio)
|
|
8001
8083
|
});
|
|
8002
8084
|
if (element.timelineType === "scene") {
|
|
8003
8085
|
await addBackgroundColor({
|
|
@@ -8009,21 +8091,24 @@ const ImageElement = {
|
|
|
8009
8091
|
}
|
|
8010
8092
|
},
|
|
8011
8093
|
updateFromFabricObject(object, element, context) {
|
|
8012
|
-
|
|
8094
|
+
const canvasCenter = getObjectCanvasCenter(object);
|
|
8013
8095
|
const { x: x2, y: y2 } = convertToVideoPosition(
|
|
8014
|
-
|
|
8015
|
-
|
|
8096
|
+
canvasCenter.x,
|
|
8097
|
+
canvasCenter.y,
|
|
8016
8098
|
context.canvasMetadata,
|
|
8017
8099
|
context.videoSize
|
|
8018
8100
|
);
|
|
8019
8101
|
const currentFrameEffect = context.elementFrameMapRef.current[element.id];
|
|
8020
8102
|
if (object.type === "group") {
|
|
8021
|
-
|
|
8103
|
+
const scaledW2 = (object.width ?? 0) * (object.scaleX ?? 1);
|
|
8104
|
+
const scaledH2 = (object.height ?? 0) * (object.scaleY ?? 1);
|
|
8105
|
+
const { width: fw, height: fh2 } = convertToVideoDimensions(
|
|
8106
|
+
scaledW2,
|
|
8107
|
+
scaledH2,
|
|
8108
|
+
context.canvasMetadata
|
|
8109
|
+
);
|
|
8110
|
+
const updatedFrameSize = [fw, fh2];
|
|
8022
8111
|
if (currentFrameEffect) {
|
|
8023
|
-
updatedFrameSize = [
|
|
8024
|
-
currentFrameEffect.props.frameSize[0] * object.scaleX,
|
|
8025
|
-
currentFrameEffect.props.frameSize[1] * object.scaleY
|
|
8026
|
-
];
|
|
8027
8112
|
context.elementFrameMapRef.current[element.id] = {
|
|
8028
8113
|
...currentFrameEffect,
|
|
8029
8114
|
props: {
|
|
@@ -8035,6 +8120,15 @@ const ImageElement = {
|
|
|
8035
8120
|
return {
|
|
8036
8121
|
element: {
|
|
8037
8122
|
...element,
|
|
8123
|
+
// Keep the base frame in sync with the active frame effect
|
|
8124
|
+
// so visualizer `Rect {...element.frame}` reflects the same size/position.
|
|
8125
|
+
frame: element.frame ? {
|
|
8126
|
+
...element.frame,
|
|
8127
|
+
rotation: getObjectCanvasAngle(object),
|
|
8128
|
+
size: updatedFrameSize,
|
|
8129
|
+
x: x2,
|
|
8130
|
+
y: y2
|
|
8131
|
+
} : element.frame,
|
|
8038
8132
|
frameEffects: (element.frameEffects || []).map(
|
|
8039
8133
|
(fe2) => fe2.id === (currentFrameEffect == null ? void 0 : currentFrameEffect.id) ? {
|
|
8040
8134
|
...fe2,
|
|
@@ -8049,16 +8143,12 @@ const ImageElement = {
|
|
|
8049
8143
|
};
|
|
8050
8144
|
}
|
|
8051
8145
|
const frame2 = element.frame;
|
|
8052
|
-
updatedFrameSize = [
|
|
8053
|
-
(frame2.size[0] ?? 0) * object.scaleX,
|
|
8054
|
-
(frame2.size[1] ?? 0) * object.scaleY
|
|
8055
|
-
];
|
|
8056
8146
|
return {
|
|
8057
8147
|
element: {
|
|
8058
8148
|
...element,
|
|
8059
8149
|
frame: {
|
|
8060
8150
|
...frame2,
|
|
8061
|
-
rotation: object
|
|
8151
|
+
rotation: getObjectCanvasAngle(object),
|
|
8062
8152
|
size: updatedFrameSize,
|
|
8063
8153
|
x: x2,
|
|
8064
8154
|
y: y2
|
|
@@ -8066,14 +8156,21 @@ const ImageElement = {
|
|
|
8066
8156
|
}
|
|
8067
8157
|
};
|
|
8068
8158
|
}
|
|
8159
|
+
const scaledW = (object.width ?? 0) * (object.scaleX ?? 1);
|
|
8160
|
+
const scaledH = (object.height ?? 0) * (object.scaleY ?? 1);
|
|
8161
|
+
const { width, height } = convertToVideoDimensions(
|
|
8162
|
+
scaledW,
|
|
8163
|
+
scaledH,
|
|
8164
|
+
context.canvasMetadata
|
|
8165
|
+
);
|
|
8069
8166
|
return {
|
|
8070
8167
|
element: {
|
|
8071
8168
|
...element,
|
|
8072
8169
|
props: {
|
|
8073
8170
|
...element.props,
|
|
8074
|
-
rotation: object
|
|
8075
|
-
width
|
|
8076
|
-
height
|
|
8171
|
+
rotation: getObjectCanvasAngle(object),
|
|
8172
|
+
width,
|
|
8173
|
+
height,
|
|
8077
8174
|
x: x2,
|
|
8078
8175
|
y: y2
|
|
8079
8176
|
}
|
|
@@ -8084,19 +8181,22 @@ const ImageElement = {
|
|
|
8084
8181
|
const RectElement = {
|
|
8085
8182
|
name: ELEMENT_TYPES.RECT,
|
|
8086
8183
|
async add(params) {
|
|
8087
|
-
|
|
8184
|
+
var _a;
|
|
8185
|
+
const { element, index, canvas, canvasMetadata, lockAspectRatio } = params;
|
|
8088
8186
|
await addRectElement({
|
|
8089
8187
|
element,
|
|
8090
8188
|
index,
|
|
8091
8189
|
canvas,
|
|
8092
|
-
canvasMetadata
|
|
8190
|
+
canvasMetadata,
|
|
8191
|
+
lockAspectRatio: lockAspectRatio ?? ((_a = element.props) == null ? void 0 : _a.lockAspectRatio)
|
|
8093
8192
|
});
|
|
8094
8193
|
},
|
|
8095
8194
|
updateFromFabricObject(object, element, context) {
|
|
8096
8195
|
var _a, _b;
|
|
8196
|
+
const canvasCenter = getObjectCanvasCenter(object);
|
|
8097
8197
|
const { x: x2, y: y2 } = convertToVideoPosition(
|
|
8098
|
-
|
|
8099
|
-
|
|
8198
|
+
canvasCenter.x,
|
|
8199
|
+
canvasCenter.y,
|
|
8100
8200
|
context.canvasMetadata,
|
|
8101
8201
|
context.videoSize
|
|
8102
8202
|
);
|
|
@@ -8105,7 +8205,7 @@ const RectElement = {
|
|
|
8105
8205
|
...element,
|
|
8106
8206
|
props: {
|
|
8107
8207
|
...element.props,
|
|
8108
|
-
rotation: object
|
|
8208
|
+
rotation: getObjectCanvasAngle(object),
|
|
8109
8209
|
width: (((_a = element.props) == null ? void 0 : _a.width) ?? 0) * object.scaleX,
|
|
8110
8210
|
height: (((_b = element.props) == null ? void 0 : _b.height) ?? 0) * object.scaleY,
|
|
8111
8211
|
x: x2,
|
|
@@ -8118,36 +8218,41 @@ const RectElement = {
|
|
|
8118
8218
|
const CircleElement = {
|
|
8119
8219
|
name: ELEMENT_TYPES.CIRCLE,
|
|
8120
8220
|
async add(params) {
|
|
8121
|
-
|
|
8221
|
+
var _a;
|
|
8222
|
+
const { element, index, canvas, canvasMetadata, lockAspectRatio } = params;
|
|
8122
8223
|
await addCircleElement({
|
|
8123
8224
|
element,
|
|
8124
8225
|
index,
|
|
8125
8226
|
canvas,
|
|
8126
|
-
canvasMetadata
|
|
8227
|
+
canvasMetadata,
|
|
8228
|
+
lockAspectRatio: lockAspectRatio ?? ((_a = element.props) == null ? void 0 : _a.lockAspectRatio)
|
|
8127
8229
|
});
|
|
8128
8230
|
},
|
|
8129
8231
|
updateFromFabricObject(object, element, context) {
|
|
8130
|
-
var _a;
|
|
8232
|
+
var _a, _b;
|
|
8233
|
+
const canvasCenter = getObjectCanvasCenter(object);
|
|
8131
8234
|
const { x: x2, y: y2 } = convertToVideoPosition(
|
|
8132
|
-
|
|
8133
|
-
|
|
8235
|
+
canvasCenter.x,
|
|
8236
|
+
canvasCenter.y,
|
|
8134
8237
|
context.canvasMetadata,
|
|
8135
8238
|
context.videoSize
|
|
8136
8239
|
);
|
|
8137
8240
|
const radius = Number(
|
|
8138
8241
|
((((_a = element.props) == null ? void 0 : _a.radius) ?? 0) * object.scaleX).toFixed(2)
|
|
8139
8242
|
);
|
|
8243
|
+
const opacity = object.opacity != null ? object.opacity : (_b = element.props) == null ? void 0 : _b.opacity;
|
|
8140
8244
|
return {
|
|
8141
8245
|
element: {
|
|
8142
8246
|
...element,
|
|
8143
8247
|
props: {
|
|
8144
8248
|
...element.props,
|
|
8145
|
-
rotation: object
|
|
8249
|
+
rotation: getObjectCanvasAngle(object),
|
|
8146
8250
|
radius,
|
|
8147
8251
|
height: radius * 2,
|
|
8148
8252
|
width: radius * 2,
|
|
8149
8253
|
x: x2,
|
|
8150
|
-
y: y2
|
|
8254
|
+
y: y2,
|
|
8255
|
+
...opacity != null && { opacity }
|
|
8151
8256
|
}
|
|
8152
8257
|
}
|
|
8153
8258
|
};
|
|
@@ -8165,9 +8270,10 @@ const TextElement = {
|
|
|
8165
8270
|
});
|
|
8166
8271
|
},
|
|
8167
8272
|
updateFromFabricObject(object, element, context) {
|
|
8273
|
+
const canvasCenter = getObjectCanvasCenter(object);
|
|
8168
8274
|
const { x: x2, y: y2 } = convertToVideoPosition(
|
|
8169
|
-
|
|
8170
|
-
|
|
8275
|
+
canvasCenter.x,
|
|
8276
|
+
canvasCenter.y,
|
|
8171
8277
|
context.canvasMetadata,
|
|
8172
8278
|
context.videoSize
|
|
8173
8279
|
);
|
|
@@ -8176,7 +8282,7 @@ const TextElement = {
|
|
|
8176
8282
|
...element,
|
|
8177
8283
|
props: {
|
|
8178
8284
|
...element.props,
|
|
8179
|
-
rotation: object
|
|
8285
|
+
rotation: getObjectCanvasAngle(object),
|
|
8180
8286
|
x: x2,
|
|
8181
8287
|
y: y2
|
|
8182
8288
|
}
|
|
@@ -8187,20 +8293,23 @@ const TextElement = {
|
|
|
8187
8293
|
const CaptionElement = {
|
|
8188
8294
|
name: ELEMENT_TYPES.CAPTION,
|
|
8189
8295
|
async add(params) {
|
|
8190
|
-
|
|
8296
|
+
var _a;
|
|
8297
|
+
const { element, index, canvas, captionProps, canvasMetadata, lockAspectRatio } = params;
|
|
8191
8298
|
await addCaptionElement({
|
|
8192
8299
|
element,
|
|
8193
8300
|
index,
|
|
8194
8301
|
canvas,
|
|
8195
8302
|
captionProps: captionProps ?? {},
|
|
8196
|
-
canvasMetadata
|
|
8303
|
+
canvasMetadata,
|
|
8304
|
+
lockAspectRatio: lockAspectRatio ?? ((_a = element.props) == null ? void 0 : _a.lockAspectRatio)
|
|
8197
8305
|
});
|
|
8198
8306
|
},
|
|
8199
8307
|
updateFromFabricObject(object, element, context) {
|
|
8200
8308
|
var _a;
|
|
8309
|
+
const canvasCenter = getObjectCanvasCenter(object);
|
|
8201
8310
|
const { x: x2, y: y2 } = convertToVideoPosition(
|
|
8202
|
-
|
|
8203
|
-
|
|
8311
|
+
canvasCenter.x,
|
|
8312
|
+
canvasCenter.y,
|
|
8204
8313
|
context.canvasMetadata,
|
|
8205
8314
|
context.videoSize
|
|
8206
8315
|
);
|
|
@@ -8306,7 +8415,15 @@ function registerElements() {
|
|
|
8306
8415
|
registerElements();
|
|
8307
8416
|
const useTwickCanvas = ({
|
|
8308
8417
|
onCanvasReady,
|
|
8309
|
-
onCanvasOperation
|
|
8418
|
+
onCanvasOperation,
|
|
8419
|
+
/**
|
|
8420
|
+
* When true, holding Shift while dragging an object will lock movement to
|
|
8421
|
+
* the dominant axis (horizontal or vertical). This mirrors behavior in
|
|
8422
|
+
* professional editors and improves precise alignment.
|
|
8423
|
+
*
|
|
8424
|
+
* Default: false (opt‑in to avoid surprising existing consumers).
|
|
8425
|
+
*/
|
|
8426
|
+
enableShiftAxisLock = false
|
|
8310
8427
|
}) => {
|
|
8311
8428
|
const [twickCanvas, setTwickCanvas] = useState(null);
|
|
8312
8429
|
const elementMap = useRef({});
|
|
@@ -8316,6 +8433,7 @@ const useTwickCanvas = ({
|
|
|
8316
8433
|
const videoSizeRef = useRef({ width: 1, height: 1 });
|
|
8317
8434
|
const canvasResolutionRef = useRef({ width: 1, height: 1 });
|
|
8318
8435
|
const captionPropsRef = useRef(null);
|
|
8436
|
+
const axisLockStateRef = useRef(null);
|
|
8319
8437
|
const canvasMetadataRef = useRef({
|
|
8320
8438
|
width: 0,
|
|
8321
8439
|
height: 0,
|
|
@@ -8330,6 +8448,57 @@ const useTwickCanvas = ({
|
|
|
8330
8448
|
canvasMetadataRef.current.scaleY = canvasMetadataRef.current.height / videoSize.height;
|
|
8331
8449
|
}
|
|
8332
8450
|
};
|
|
8451
|
+
const handleObjectMoving = (event) => {
|
|
8452
|
+
var _a;
|
|
8453
|
+
if (!enableShiftAxisLock) return;
|
|
8454
|
+
const target = event == null ? void 0 : event.target;
|
|
8455
|
+
const transform = event == null ? void 0 : event.transform;
|
|
8456
|
+
const pointerEvent = event == null ? void 0 : event.e;
|
|
8457
|
+
if (!target || !transform || !pointerEvent) {
|
|
8458
|
+
axisLockStateRef.current = null;
|
|
8459
|
+
return;
|
|
8460
|
+
}
|
|
8461
|
+
if (!pointerEvent.shiftKey) {
|
|
8462
|
+
axisLockStateRef.current = null;
|
|
8463
|
+
return;
|
|
8464
|
+
}
|
|
8465
|
+
const original = transform.original;
|
|
8466
|
+
if (!original || typeof target.left !== "number" || typeof target.top !== "number") {
|
|
8467
|
+
axisLockStateRef.current = null;
|
|
8468
|
+
return;
|
|
8469
|
+
}
|
|
8470
|
+
if (!axisLockStateRef.current) {
|
|
8471
|
+
const dx = Math.abs(target.left - original.left);
|
|
8472
|
+
const dy = Math.abs(target.top - original.top);
|
|
8473
|
+
axisLockStateRef.current = {
|
|
8474
|
+
axis: dx >= dy ? "x" : "y"
|
|
8475
|
+
};
|
|
8476
|
+
}
|
|
8477
|
+
if (axisLockStateRef.current.axis === "x") {
|
|
8478
|
+
target.top = original.top;
|
|
8479
|
+
} else {
|
|
8480
|
+
target.left = original.left;
|
|
8481
|
+
}
|
|
8482
|
+
(_a = target.canvas) == null ? void 0 : _a.requestRenderAll();
|
|
8483
|
+
};
|
|
8484
|
+
const applyMarqueeSelectionControls = () => {
|
|
8485
|
+
const canvasInstance = twickCanvasRef.current;
|
|
8486
|
+
if (!canvasInstance) return;
|
|
8487
|
+
const activeObject = canvasInstance.getActiveObject();
|
|
8488
|
+
if (!activeObject) return;
|
|
8489
|
+
if (activeObject instanceof Qo) {
|
|
8490
|
+
activeObject.controls.mt = disabledControl;
|
|
8491
|
+
activeObject.controls.mb = disabledControl;
|
|
8492
|
+
activeObject.controls.ml = disabledControl;
|
|
8493
|
+
activeObject.controls.mr = disabledControl;
|
|
8494
|
+
activeObject.controls.bl = disabledControl;
|
|
8495
|
+
activeObject.controls.br = disabledControl;
|
|
8496
|
+
activeObject.controls.tl = disabledControl;
|
|
8497
|
+
activeObject.controls.tr = disabledControl;
|
|
8498
|
+
activeObject.controls.mtr = rotateControl;
|
|
8499
|
+
canvasInstance.requestRenderAll();
|
|
8500
|
+
}
|
|
8501
|
+
};
|
|
8333
8502
|
const buildCanvas = ({
|
|
8334
8503
|
videoSize,
|
|
8335
8504
|
canvasSize,
|
|
@@ -8349,6 +8518,9 @@ const useTwickCanvas = ({
|
|
|
8349
8518
|
if (twickCanvasRef.current) {
|
|
8350
8519
|
twickCanvasRef.current.off("mouse:up", handleMouseUp);
|
|
8351
8520
|
twickCanvasRef.current.off("text:editing:exited", onTextEdit);
|
|
8521
|
+
twickCanvasRef.current.off("object:moving", handleObjectMoving);
|
|
8522
|
+
twickCanvasRef.current.off("selection:created", applyMarqueeSelectionControls);
|
|
8523
|
+
twickCanvasRef.current.off("selection:updated", applyMarqueeSelectionControls);
|
|
8352
8524
|
twickCanvasRef.current.dispose();
|
|
8353
8525
|
}
|
|
8354
8526
|
const { canvas, canvasMetadata } = createCanvas({
|
|
@@ -8366,6 +8538,9 @@ const useTwickCanvas = ({
|
|
|
8366
8538
|
videoSizeRef.current = videoSize;
|
|
8367
8539
|
canvas == null ? void 0 : canvas.on("mouse:up", handleMouseUp);
|
|
8368
8540
|
canvas == null ? void 0 : canvas.on("text:editing:exited", onTextEdit);
|
|
8541
|
+
canvas == null ? void 0 : canvas.on("object:moving", handleObjectMoving);
|
|
8542
|
+
canvas == null ? void 0 : canvas.on("selection:created", applyMarqueeSelectionControls);
|
|
8543
|
+
canvas == null ? void 0 : canvas.on("selection:updated", applyMarqueeSelectionControls);
|
|
8369
8544
|
canvasResolutionRef.current = canvasSize;
|
|
8370
8545
|
setTwickCanvas(canvas);
|
|
8371
8546
|
twickCanvasRef.current = canvas;
|
|
@@ -8392,7 +8567,8 @@ const useTwickCanvas = ({
|
|
|
8392
8567
|
if (event.target) {
|
|
8393
8568
|
const object = event.target;
|
|
8394
8569
|
const elementId = object.get("id");
|
|
8395
|
-
|
|
8570
|
+
const action = (_a = event.transform) == null ? void 0 : _a.action;
|
|
8571
|
+
if (action === "drag") {
|
|
8396
8572
|
const original = event.transform.original;
|
|
8397
8573
|
if (object.left === original.left && object.top === original.top) {
|
|
8398
8574
|
onCanvasOperation == null ? void 0 : onCanvasOperation(
|
|
@@ -8402,7 +8578,38 @@ const useTwickCanvas = ({
|
|
|
8402
8578
|
return;
|
|
8403
8579
|
}
|
|
8404
8580
|
}
|
|
8405
|
-
|
|
8581
|
+
const context = {
|
|
8582
|
+
canvasMetadata: canvasMetadataRef.current,
|
|
8583
|
+
videoSize: videoSizeRef.current,
|
|
8584
|
+
elementFrameMapRef: elementFrameMap,
|
|
8585
|
+
captionPropsRef,
|
|
8586
|
+
watermarkPropsRef
|
|
8587
|
+
};
|
|
8588
|
+
if (object instanceof Qo && (action === "drag" || action === "rotate")) {
|
|
8589
|
+
const objects = object.getObjects();
|
|
8590
|
+
for (const fabricObj of objects) {
|
|
8591
|
+
const id2 = fabricObj.get("id");
|
|
8592
|
+
if (!id2 || id2 === "e-watermark") continue;
|
|
8593
|
+
const currentElement = elementMap.current[id2];
|
|
8594
|
+
if (!currentElement) continue;
|
|
8595
|
+
const handler = elementController.get(currentElement.type);
|
|
8596
|
+
const result = (_b = handler == null ? void 0 : handler.updateFromFabricObject) == null ? void 0 : _b.call(
|
|
8597
|
+
handler,
|
|
8598
|
+
fabricObj,
|
|
8599
|
+
currentElement,
|
|
8600
|
+
context
|
|
8601
|
+
);
|
|
8602
|
+
if (result) {
|
|
8603
|
+
elementMap.current[id2] = result.element;
|
|
8604
|
+
onCanvasOperation == null ? void 0 : onCanvasOperation(
|
|
8605
|
+
result.operation ?? CANVAS_OPERATIONS.ITEM_UPDATED,
|
|
8606
|
+
result.payload ?? result.element
|
|
8607
|
+
);
|
|
8608
|
+
}
|
|
8609
|
+
}
|
|
8610
|
+
return;
|
|
8611
|
+
}
|
|
8612
|
+
switch (action) {
|
|
8406
8613
|
case "drag":
|
|
8407
8614
|
case "scale":
|
|
8408
8615
|
case "scaleX":
|
|
@@ -8412,13 +8619,7 @@ const useTwickCanvas = ({
|
|
|
8412
8619
|
const handler = elementController.get(
|
|
8413
8620
|
elementId === "e-watermark" ? "watermark" : currentElement == null ? void 0 : currentElement.type
|
|
8414
8621
|
);
|
|
8415
|
-
const result = (_c = handler == null ? void 0 : handler.updateFromFabricObject) == null ? void 0 : _c.call(handler, object, currentElement ?? { id: elementId, type: "text", props: {} },
|
|
8416
|
-
canvasMetadata: canvasMetadataRef.current,
|
|
8417
|
-
videoSize: videoSizeRef.current,
|
|
8418
|
-
elementFrameMapRef: elementFrameMap,
|
|
8419
|
-
captionPropsRef,
|
|
8420
|
-
watermarkPropsRef
|
|
8421
|
-
});
|
|
8622
|
+
const result = (_c = handler == null ? void 0 : handler.updateFromFabricObject) == null ? void 0 : _c.call(handler, object, currentElement ?? { id: elementId, type: "text", props: {} }, context);
|
|
8422
8623
|
if (result) {
|
|
8423
8624
|
elementMap.current[elementId] = result.element;
|
|
8424
8625
|
onCanvasOperation == null ? void 0 : onCanvasOperation(
|
|
@@ -8436,7 +8637,8 @@ const useTwickCanvas = ({
|
|
|
8436
8637
|
watermark,
|
|
8437
8638
|
seekTime = 0,
|
|
8438
8639
|
captionProps,
|
|
8439
|
-
cleanAndAdd = false
|
|
8640
|
+
cleanAndAdd = false,
|
|
8641
|
+
lockAspectRatio
|
|
8440
8642
|
}) => {
|
|
8441
8643
|
if (!twickCanvas || !getCanvasContext(twickCanvas)) return;
|
|
8442
8644
|
try {
|
|
@@ -8449,16 +8651,26 @@ const useTwickCanvas = ({
|
|
|
8449
8651
|
}
|
|
8450
8652
|
}
|
|
8451
8653
|
captionPropsRef.current = captionProps;
|
|
8654
|
+
const uniqueElements = [];
|
|
8655
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
8656
|
+
for (const el of elements) {
|
|
8657
|
+
if (!el || !el.id) continue;
|
|
8658
|
+
if (seenIds.has(el.id)) continue;
|
|
8659
|
+
seenIds.add(el.id);
|
|
8660
|
+
uniqueElements.push(el);
|
|
8661
|
+
}
|
|
8452
8662
|
await Promise.all(
|
|
8453
|
-
|
|
8663
|
+
uniqueElements.map(async (element, index) => {
|
|
8454
8664
|
try {
|
|
8455
8665
|
if (!element) return;
|
|
8666
|
+
const zOrder = element.zIndex ?? index;
|
|
8456
8667
|
await addElementToCanvas({
|
|
8457
8668
|
element,
|
|
8458
|
-
index,
|
|
8669
|
+
index: zOrder,
|
|
8459
8670
|
reorder: false,
|
|
8460
8671
|
seekTime,
|
|
8461
|
-
captionProps
|
|
8672
|
+
captionProps,
|
|
8673
|
+
lockAspectRatio
|
|
8462
8674
|
});
|
|
8463
8675
|
} catch {
|
|
8464
8676
|
}
|
|
@@ -8478,8 +8690,10 @@ const useTwickCanvas = ({
|
|
|
8478
8690
|
index,
|
|
8479
8691
|
reorder = true,
|
|
8480
8692
|
seekTime,
|
|
8481
|
-
captionProps
|
|
8693
|
+
captionProps,
|
|
8694
|
+
lockAspectRatio
|
|
8482
8695
|
}) => {
|
|
8696
|
+
var _a;
|
|
8483
8697
|
if (!twickCanvas) return;
|
|
8484
8698
|
const handler = elementController.get(element.type);
|
|
8485
8699
|
if (handler) {
|
|
@@ -8491,10 +8705,11 @@ const useTwickCanvas = ({
|
|
|
8491
8705
|
seekTime,
|
|
8492
8706
|
captionProps: captionProps ?? null,
|
|
8493
8707
|
elementFrameMapRef: elementFrameMap,
|
|
8494
|
-
getCurrentFrameEffect
|
|
8708
|
+
getCurrentFrameEffect,
|
|
8709
|
+
lockAspectRatio: lockAspectRatio ?? ((_a = element.props) == null ? void 0 : _a.lockAspectRatio)
|
|
8495
8710
|
});
|
|
8496
8711
|
}
|
|
8497
|
-
elementMap.current[element.id] = element;
|
|
8712
|
+
elementMap.current[element.id] = { ...element, zIndex: element.zIndex ?? index };
|
|
8498
8713
|
if (reorder) {
|
|
8499
8714
|
reorderElementsByZIndex(twickCanvas);
|
|
8500
8715
|
}
|
|
@@ -8515,169 +8730,868 @@ const useTwickCanvas = ({
|
|
|
8515
8730
|
elementMap.current[element.id] = element;
|
|
8516
8731
|
}
|
|
8517
8732
|
};
|
|
8733
|
+
const applyZOrder = (elementId, direction) => {
|
|
8734
|
+
if (!twickCanvas) return false;
|
|
8735
|
+
const newZIndex = changeZOrder(twickCanvas, elementId, direction);
|
|
8736
|
+
if (newZIndex == null) return false;
|
|
8737
|
+
const element = elementMap.current[elementId];
|
|
8738
|
+
if (element) elementMap.current[elementId] = { ...element, zIndex: newZIndex };
|
|
8739
|
+
onCanvasOperation == null ? void 0 : onCanvasOperation(CANVAS_OPERATIONS.Z_ORDER_CHANGED, { elementId, direction });
|
|
8740
|
+
return true;
|
|
8741
|
+
};
|
|
8742
|
+
const bringToFront = (elementId) => applyZOrder(elementId, "front");
|
|
8743
|
+
const sendToBack = (elementId) => applyZOrder(elementId, "back");
|
|
8744
|
+
const bringForward = (elementId) => applyZOrder(elementId, "forward");
|
|
8745
|
+
const sendBackward = (elementId) => applyZOrder(elementId, "backward");
|
|
8518
8746
|
return {
|
|
8519
8747
|
twickCanvas,
|
|
8520
8748
|
buildCanvas,
|
|
8521
8749
|
onVideoSizeChange,
|
|
8522
8750
|
addWatermarkToCanvas,
|
|
8523
8751
|
addElementToCanvas,
|
|
8524
|
-
setCanvasElements
|
|
8752
|
+
setCanvasElements,
|
|
8753
|
+
bringToFront,
|
|
8754
|
+
sendToBack,
|
|
8755
|
+
bringForward,
|
|
8756
|
+
sendBackward
|
|
8525
8757
|
};
|
|
8526
8758
|
};
|
|
8527
|
-
const
|
|
8528
|
-
|
|
8529
|
-
|
|
8530
|
-
|
|
8531
|
-
|
|
8532
|
-
|
|
8533
|
-
|
|
8534
|
-
|
|
8535
|
-
|
|
8536
|
-
|
|
8537
|
-
|
|
8538
|
-
|
|
8539
|
-
|
|
8540
|
-
|
|
8541
|
-
|
|
8542
|
-
|
|
8543
|
-
|
|
8544
|
-
|
|
8545
|
-
|
|
8546
|
-
|
|
8547
|
-
|
|
8548
|
-
|
|
8549
|
-
|
|
8550
|
-
|
|
8551
|
-
|
|
8552
|
-
|
|
8553
|
-
|
|
8554
|
-
|
|
8555
|
-
|
|
8556
|
-
|
|
8557
|
-
|
|
8558
|
-
|
|
8559
|
-
|
|
8560
|
-
|
|
8561
|
-
|
|
8562
|
-
|
|
8563
|
-
|
|
8564
|
-
|
|
8565
|
-
|
|
8566
|
-
|
|
8567
|
-
|
|
8568
|
-
|
|
8569
|
-
|
|
8570
|
-
|
|
8571
|
-
|
|
8572
|
-
|
|
8573
|
-
|
|
8574
|
-
|
|
8575
|
-
const
|
|
8576
|
-
|
|
8577
|
-
|
|
8578
|
-
|
|
8579
|
-
|
|
8580
|
-
|
|
8581
|
-
|
|
8582
|
-
|
|
8583
|
-
|
|
8584
|
-
|
|
8585
|
-
|
|
8586
|
-
|
|
8587
|
-
|
|
8588
|
-
|
|
8589
|
-
|
|
8590
|
-
|
|
8591
|
-
|
|
8592
|
-
|
|
8593
|
-
|
|
8594
|
-
|
|
8595
|
-
|
|
8596
|
-
|
|
8597
|
-
|
|
8598
|
-
|
|
8599
|
-
const position = watermark.getPosition();
|
|
8600
|
-
watermarkElement = {
|
|
8601
|
-
id: watermark.getId(),
|
|
8602
|
-
type: watermark.getType(),
|
|
8603
|
-
props: {
|
|
8604
|
-
...watermark.getProps() || {},
|
|
8605
|
-
x: (position == null ? void 0 : position.x) ?? 0,
|
|
8606
|
-
y: (position == null ? void 0 : position.y) ?? 0,
|
|
8607
|
-
rotation: watermark.getRotation() ?? 0,
|
|
8608
|
-
opacity: watermark.getOpacity() ?? 1
|
|
8609
|
-
}
|
|
8610
|
-
};
|
|
8611
|
-
}
|
|
8612
|
-
setCanvasElements({
|
|
8613
|
-
elements,
|
|
8614
|
-
watermark: watermarkElement,
|
|
8615
|
-
seekTime,
|
|
8616
|
-
captionProps,
|
|
8617
|
-
cleanAndAdd: true
|
|
8618
|
-
});
|
|
8619
|
-
currentChangeLog.current = changeLog;
|
|
8620
|
-
};
|
|
8621
|
-
const onPlayerUpdate = (event) => {
|
|
8622
|
-
var _a;
|
|
8623
|
-
if (((_a = event == null ? void 0 : event.detail) == null ? void 0 : _a.status) === "ready") {
|
|
8624
|
-
setPlayerUpdating(false);
|
|
8625
|
-
setTimelineAction(TIMELINE_ACTION.ON_PLAYER_UPDATED, null);
|
|
8626
|
-
}
|
|
8627
|
-
};
|
|
8628
|
-
useEffect(() => {
|
|
8629
|
-
var _a, _b, _c, _d, _e2;
|
|
8630
|
-
switch (timelineAction.type) {
|
|
8631
|
-
case TIMELINE_ACTION.UPDATE_PLAYER_DATA:
|
|
8632
|
-
if (videoProps) {
|
|
8633
|
-
if (((_a = timelineAction.payload) == null ? void 0 : _a.forceUpdate) || editor.getLatestVersion() !== ((_b = projectData == null ? void 0 : projectData.input) == null ? void 0 : _b.version)) {
|
|
8634
|
-
setPlayerUpdating(true);
|
|
8635
|
-
const _latestProjectData = {
|
|
8636
|
-
input: {
|
|
8637
|
-
properties: videoProps,
|
|
8638
|
-
tracks: ((_c = timelineAction.payload) == null ? void 0 : _c.tracks) ?? [],
|
|
8639
|
-
version: ((_d = timelineAction.payload) == null ? void 0 : _d.version) ?? 0
|
|
8640
|
-
}
|
|
8641
|
-
};
|
|
8642
|
-
setProjectData(_latestProjectData);
|
|
8643
|
-
if (((_e2 = timelineAction.payload) == null ? void 0 : _e2.version) === 1) {
|
|
8644
|
-
setTimeout(() => {
|
|
8645
|
-
setPlayerUpdating(false);
|
|
8646
|
-
});
|
|
8647
|
-
}
|
|
8648
|
-
} else {
|
|
8649
|
-
setTimelineAction(TIMELINE_ACTION.ON_PLAYER_UPDATED, null);
|
|
8759
|
+
const VIDEO_TYPES = [
|
|
8760
|
+
"video/mp4",
|
|
8761
|
+
"video/webm",
|
|
8762
|
+
"video/ogg",
|
|
8763
|
+
"video/quicktime",
|
|
8764
|
+
"video/x-msvideo",
|
|
8765
|
+
"video/x-matroska"
|
|
8766
|
+
];
|
|
8767
|
+
const AUDIO_TYPES = [
|
|
8768
|
+
"audio/mpeg",
|
|
8769
|
+
"audio/mp3",
|
|
8770
|
+
"audio/wav",
|
|
8771
|
+
"audio/ogg",
|
|
8772
|
+
"audio/webm",
|
|
8773
|
+
"audio/aac",
|
|
8774
|
+
"audio/mp4",
|
|
8775
|
+
"audio/x-wav"
|
|
8776
|
+
];
|
|
8777
|
+
const IMAGE_TYPES = [
|
|
8778
|
+
"image/jpeg",
|
|
8779
|
+
"image/jpg",
|
|
8780
|
+
"image/png",
|
|
8781
|
+
"image/gif",
|
|
8782
|
+
"image/webp",
|
|
8783
|
+
"image/svg+xml",
|
|
8784
|
+
"image/bmp"
|
|
8785
|
+
];
|
|
8786
|
+
const EXT_TO_TYPE = {
|
|
8787
|
+
mp4: "video",
|
|
8788
|
+
webm: "video",
|
|
8789
|
+
mov: "video",
|
|
8790
|
+
avi: "video",
|
|
8791
|
+
mkv: "video",
|
|
8792
|
+
mp3: "audio",
|
|
8793
|
+
wav: "audio",
|
|
8794
|
+
ogg: "audio",
|
|
8795
|
+
m4a: "audio",
|
|
8796
|
+
jpg: "image",
|
|
8797
|
+
jpeg: "image",
|
|
8798
|
+
png: "image",
|
|
8799
|
+
gif: "image",
|
|
8800
|
+
webp: "image",
|
|
8801
|
+
svg: "image",
|
|
8802
|
+
bmp: "image"
|
|
8803
|
+
};
|
|
8804
|
+
function getAssetTypeFromFile(file) {
|
|
8805
|
+
var _a;
|
|
8806
|
+
const mime = (file.type || "").toLowerCase();
|
|
8807
|
+
const ext = (((_a = file.name) == null ? void 0 : _a.split(".").pop()) || "").toLowerCase();
|
|
8808
|
+
if (VIDEO_TYPES.some((t2) => mime.includes(t2))) return "video";
|
|
8809
|
+
if (AUDIO_TYPES.some((t2) => mime.includes(t2))) return "audio";
|
|
8810
|
+
if (IMAGE_TYPES.some((t2) => mime.includes(t2))) return "image";
|
|
8811
|
+
if (ext && EXT_TO_TYPE[ext]) return EXT_TO_TYPE[ext];
|
|
8812
|
+
return null;
|
|
8813
|
+
}
|
|
8814
|
+
const INITIAL_TIMELINE_DATA = {
|
|
8815
|
+
tracks: [
|
|
8816
|
+
{
|
|
8817
|
+
type: "element",
|
|
8818
|
+
id: "t-sample",
|
|
8819
|
+
name: "sample",
|
|
8820
|
+
elements: [
|
|
8821
|
+
{
|
|
8822
|
+
id: "e-sample",
|
|
8823
|
+
trackId: "t-sample",
|
|
8824
|
+
name: "sample",
|
|
8825
|
+
type: "text",
|
|
8826
|
+
s: 0,
|
|
8827
|
+
e: 5,
|
|
8828
|
+
props: {
|
|
8829
|
+
text: "Twick Video Editor",
|
|
8830
|
+
fill: "#FFFFFF"
|
|
8650
8831
|
}
|
|
8651
8832
|
}
|
|
8652
|
-
|
|
8833
|
+
]
|
|
8653
8834
|
}
|
|
8654
|
-
|
|
8655
|
-
|
|
8656
|
-
twickCanvas,
|
|
8657
|
-
projectData,
|
|
8658
|
-
updateCanvas,
|
|
8659
|
-
buildCanvas,
|
|
8660
|
-
onPlayerUpdate,
|
|
8661
|
-
playerUpdating
|
|
8662
|
-
};
|
|
8835
|
+
],
|
|
8836
|
+
version: 1
|
|
8663
8837
|
};
|
|
8664
|
-
const
|
|
8665
|
-
|
|
8666
|
-
|
|
8667
|
-
|
|
8668
|
-
|
|
8669
|
-
|
|
8670
|
-
|
|
8671
|
-
|
|
8672
|
-
|
|
8673
|
-
|
|
8674
|
-
|
|
8675
|
-
|
|
8838
|
+
const MIN_DURATION = 0.1;
|
|
8839
|
+
const TIMELINE_DROP_MEDIA_TYPE = "application/x-twick-media";
|
|
8840
|
+
const DRAG_TYPE = {
|
|
8841
|
+
/** Drag operation is starting */
|
|
8842
|
+
START: "start",
|
|
8843
|
+
/** Drag operation is in progress */
|
|
8844
|
+
MOVE: "move",
|
|
8845
|
+
/** Drag operation has ended */
|
|
8846
|
+
END: "end"
|
|
8847
|
+
};
|
|
8848
|
+
const DEFAULT_TIMELINE_ZOOM = 1.5;
|
|
8849
|
+
const DEFAULT_FPS = 30;
|
|
8850
|
+
const SNAP_THRESHOLD_PX = 10;
|
|
8851
|
+
const DEFAULT_TIMELINE_ZOOM_CONFIG = {
|
|
8852
|
+
/** Minimum zoom level (10%) */
|
|
8853
|
+
min: 0.1,
|
|
8854
|
+
/** Maximum zoom level (300%) */
|
|
8855
|
+
max: 3,
|
|
8856
|
+
/** Zoom step increment/decrement (10%) */
|
|
8857
|
+
step: 0.1,
|
|
8858
|
+
/** Default zoom level (150%) */
|
|
8859
|
+
default: 1.5
|
|
8860
|
+
};
|
|
8861
|
+
const DEFAULT_TIMELINE_TICK_CONFIGS = [
|
|
8862
|
+
{
|
|
8863
|
+
durationThreshold: 10,
|
|
8864
|
+
// < 10 seconds
|
|
8865
|
+
majorInterval: 1,
|
|
8866
|
+
// 1s major ticks
|
|
8867
|
+
minorTicks: 10
|
|
8868
|
+
// 0.1s minor ticks (10 minors between majors)
|
|
8869
|
+
},
|
|
8870
|
+
{
|
|
8871
|
+
durationThreshold: 30,
|
|
8872
|
+
// < 30 seconds
|
|
8873
|
+
majorInterval: 5,
|
|
8874
|
+
// 5s major ticks
|
|
8875
|
+
minorTicks: 5
|
|
8876
|
+
// 1s minor ticks (5 minors between majors)
|
|
8877
|
+
},
|
|
8878
|
+
{
|
|
8879
|
+
durationThreshold: 120,
|
|
8880
|
+
// < 2 minutes
|
|
8881
|
+
majorInterval: 10,
|
|
8882
|
+
// 10s major ticks
|
|
8883
|
+
minorTicks: 5
|
|
8884
|
+
// 2s minor ticks (5 minors between majors)
|
|
8885
|
+
},
|
|
8886
|
+
{
|
|
8887
|
+
durationThreshold: 300,
|
|
8888
|
+
// < 5 minutes
|
|
8889
|
+
majorInterval: 30,
|
|
8890
|
+
// 30s major ticks
|
|
8891
|
+
minorTicks: 6
|
|
8892
|
+
// 5s minor ticks (6 minors between majors)
|
|
8893
|
+
},
|
|
8894
|
+
{
|
|
8895
|
+
durationThreshold: 900,
|
|
8896
|
+
// < 15 minutes
|
|
8897
|
+
majorInterval: 60,
|
|
8898
|
+
// 1m major ticks
|
|
8899
|
+
minorTicks: 6
|
|
8900
|
+
// 10s minor ticks (6 minors between majors)
|
|
8901
|
+
},
|
|
8902
|
+
{
|
|
8903
|
+
durationThreshold: 1800,
|
|
8904
|
+
// < 30 minutes
|
|
8905
|
+
majorInterval: 120,
|
|
8906
|
+
// 2m major ticks
|
|
8907
|
+
minorTicks: 4
|
|
8908
|
+
// 30s minor ticks (4 minors between majors)
|
|
8909
|
+
},
|
|
8910
|
+
{
|
|
8911
|
+
durationThreshold: 3600,
|
|
8912
|
+
// < 1 hour
|
|
8913
|
+
majorInterval: 300,
|
|
8914
|
+
// 5m major ticks
|
|
8915
|
+
minorTicks: 5
|
|
8916
|
+
// 1m minor ticks (5 minors between majors)
|
|
8917
|
+
},
|
|
8918
|
+
{
|
|
8919
|
+
durationThreshold: 7200,
|
|
8920
|
+
// < 2 hours
|
|
8921
|
+
majorInterval: 600,
|
|
8922
|
+
// 10m major ticks
|
|
8923
|
+
minorTicks: 10
|
|
8924
|
+
// 1m minor ticks (10 minors between majors)
|
|
8925
|
+
},
|
|
8926
|
+
{
|
|
8927
|
+
durationThreshold: Infinity,
|
|
8928
|
+
// >= 2 hours
|
|
8929
|
+
majorInterval: 1800,
|
|
8930
|
+
// 30m major ticks
|
|
8931
|
+
minorTicks: 6
|
|
8932
|
+
// 5m minor ticks (6 minors between majors)
|
|
8933
|
+
}
|
|
8934
|
+
];
|
|
8935
|
+
const DEFAULT_ELEMENT_COLORS = {
|
|
8936
|
+
/** Fragment element color - deep charcoal matching UI background */
|
|
8937
|
+
fragment: "#1A1A1A",
|
|
8938
|
+
/** Video element color - vibrant royal purple */
|
|
8939
|
+
video: "#8B5FBF",
|
|
8940
|
+
/** Caption element color - soft wisteria purple */
|
|
8941
|
+
caption: "#9B8ACE",
|
|
8942
|
+
/** Image element color - warm copper accent */
|
|
8943
|
+
image: "#D4956C",
|
|
8944
|
+
/** Audio element color - deep teal */
|
|
8945
|
+
audio: "#3D8B8B",
|
|
8946
|
+
/** Text element color - medium lavender */
|
|
8947
|
+
text: "#8D74C4",
|
|
8948
|
+
/** Generic element color - muted amethyst */
|
|
8949
|
+
element: "#7B68B8",
|
|
8950
|
+
/** Rectangle element color - deep indigo */
|
|
8951
|
+
rect: "#5B4B99",
|
|
8952
|
+
/** Frame effect color - rich magenta */
|
|
8953
|
+
frameEffect: "#B55B9C",
|
|
8954
|
+
/** Filters color - periwinkle blue */
|
|
8955
|
+
filters: "#7A89D4",
|
|
8956
|
+
/** Transition color - burnished bronze */
|
|
8957
|
+
transition: "#BE8157",
|
|
8958
|
+
/** Animation color - muted emerald */
|
|
8959
|
+
animation: "#4B9B78",
|
|
8960
|
+
/** Icon element color - bright orchid */
|
|
8961
|
+
icon: "#A76CD4",
|
|
8962
|
+
/** Circle element color - deep byzantium */
|
|
8963
|
+
circle: "#703D8B"
|
|
8964
|
+
};
|
|
8965
|
+
const AVAILABLE_TEXT_FONTS = {
|
|
8966
|
+
// Google Fonts
|
|
8967
|
+
/** Modern sans-serif font */
|
|
8968
|
+
RUBIK: "Rubik",
|
|
8969
|
+
/** Clean and readable font */
|
|
8970
|
+
MULISH: "Mulish",
|
|
8971
|
+
/** Bold display font */
|
|
8972
|
+
LUCKIEST_GUY: "Luckiest Guy",
|
|
8973
|
+
/** Elegant serif font */
|
|
8974
|
+
PLAYFAIR_DISPLAY: "Playfair Display",
|
|
8975
|
+
/** Classic sans-serif font */
|
|
8976
|
+
ROBOTO: "Roboto",
|
|
8977
|
+
/** Modern geometric font */
|
|
8978
|
+
POPPINS: "Poppins",
|
|
8979
|
+
// Display and Decorative Fonts
|
|
8980
|
+
/** Comic-style display font */
|
|
8981
|
+
BANGERS: "Bangers",
|
|
8982
|
+
/** Handwritten-style font */
|
|
8983
|
+
BIRTHSTONE: "Birthstone",
|
|
8984
|
+
/** Elegant script font */
|
|
8985
|
+
CORINTHIA: "Corinthia",
|
|
8986
|
+
/** Formal script font */
|
|
8987
|
+
IMPERIAL_SCRIPT: "Imperial Script",
|
|
8988
|
+
/** Bold outline font */
|
|
8989
|
+
KUMAR_ONE_OUTLINE: "Kumar One Outline",
|
|
8990
|
+
/** Light outline font */
|
|
8991
|
+
LONDRI_OUTLINE: "Londrina Outline",
|
|
8992
|
+
/** Casual script font */
|
|
8993
|
+
MARCK_SCRIPT: "Marck Script",
|
|
8994
|
+
/** Modern sans-serif font */
|
|
8995
|
+
MONTSERRAT: "Montserrat",
|
|
8996
|
+
/** Stylish display font */
|
|
8997
|
+
PATTAYA: "Pattaya",
|
|
8998
|
+
// CDN Fonts
|
|
8999
|
+
/** Unique display font */
|
|
9000
|
+
PERALTA: "Peralta",
|
|
9001
|
+
/** Bold impact font */
|
|
9002
|
+
IMPACT: "Impact",
|
|
9003
|
+
/** Handwritten-style font */
|
|
9004
|
+
LUMANOSIMO: "Lumanosimo",
|
|
9005
|
+
/** Custom display font */
|
|
9006
|
+
KAPAKANA: "Kapakana",
|
|
9007
|
+
/** Handwritten font */
|
|
9008
|
+
HANDYRUSH: "HandyRush",
|
|
9009
|
+
/** Decorative font */
|
|
9010
|
+
DASHER: "Dasher",
|
|
9011
|
+
/** Signature-style font */
|
|
9012
|
+
BRITTANY_SIGNATURE: "Brittany Signature"
|
|
9013
|
+
};
|
|
9014
|
+
const DEFAULT_DROP_DURATION = 5;
|
|
9015
|
+
function useTimelineDrop({
|
|
9016
|
+
containerRef,
|
|
9017
|
+
scrollContainerRef,
|
|
9018
|
+
tracks,
|
|
9019
|
+
duration,
|
|
9020
|
+
zoomLevel,
|
|
9021
|
+
labelWidth,
|
|
9022
|
+
trackHeight,
|
|
9023
|
+
/** Width of the track content area (timeline minus labels). Used for accurate time mapping. */
|
|
9024
|
+
trackContentWidth,
|
|
9025
|
+
onDrop,
|
|
9026
|
+
enabled = true
|
|
9027
|
+
}) {
|
|
9028
|
+
const [preview, setPreview] = useState(null);
|
|
9029
|
+
const [isDraggingOver, setIsDraggingOver] = useState(false);
|
|
9030
|
+
const computePosition = useCallback(
|
|
9031
|
+
(clientX, clientY) => {
|
|
9032
|
+
var _a, _b;
|
|
9033
|
+
if (!containerRef.current) return null;
|
|
9034
|
+
const rect = containerRef.current.getBoundingClientRect();
|
|
9035
|
+
const scrollEl = (scrollContainerRef == null ? void 0 : scrollContainerRef.current) ?? containerRef.current;
|
|
9036
|
+
const scrollLeft = (scrollEl == null ? void 0 : scrollEl.scrollLeft) ?? 0;
|
|
9037
|
+
const viewportLeft = ((_b = (_a = scrollEl == null ? void 0 : scrollEl.getBoundingClientRect) == null ? void 0 : _a.call(scrollEl)) == null ? void 0 : _b.left) ?? rect.left;
|
|
9038
|
+
const contentX = clientX - viewportLeft + scrollLeft - labelWidth;
|
|
9039
|
+
const relY = clientY - rect.top;
|
|
9040
|
+
const rawTrackIndex = Math.floor(relY / trackHeight);
|
|
9041
|
+
const trackIndex = tracks.length === 0 ? 0 : Math.max(0, Math.min(tracks.length - 1, rawTrackIndex));
|
|
9042
|
+
const pixelsPerSecond = trackContentWidth != null && trackContentWidth > 0 ? trackContentWidth / duration : 100 * zoomLevel;
|
|
9043
|
+
const timeSec = Math.max(
|
|
9044
|
+
0,
|
|
9045
|
+
Math.min(duration, contentX / pixelsPerSecond)
|
|
9046
|
+
);
|
|
9047
|
+
return { trackIndex, timeSec };
|
|
9048
|
+
},
|
|
9049
|
+
[
|
|
9050
|
+
containerRef,
|
|
9051
|
+
scrollContainerRef,
|
|
9052
|
+
tracks.length,
|
|
9053
|
+
labelWidth,
|
|
9054
|
+
trackHeight,
|
|
9055
|
+
zoomLevel,
|
|
9056
|
+
duration,
|
|
9057
|
+
trackContentWidth
|
|
9058
|
+
]
|
|
9059
|
+
);
|
|
9060
|
+
const handleDragOver = useCallback(
|
|
9061
|
+
(e3) => {
|
|
9062
|
+
if (!enabled) return;
|
|
9063
|
+
e3.preventDefault();
|
|
9064
|
+
e3.stopPropagation();
|
|
9065
|
+
const hasFiles = e3.dataTransfer.types.includes("Files");
|
|
9066
|
+
const hasPanelMedia = e3.dataTransfer.types.includes(TIMELINE_DROP_MEDIA_TYPE);
|
|
9067
|
+
if (!hasFiles && !hasPanelMedia) return;
|
|
9068
|
+
e3.dataTransfer.dropEffect = "copy";
|
|
9069
|
+
setIsDraggingOver(true);
|
|
9070
|
+
const pos = computePosition(e3.clientX, e3.clientY);
|
|
9071
|
+
if (pos) {
|
|
9072
|
+
const track = tracks[pos.trackIndex] ?? null;
|
|
9073
|
+
if (track || tracks.length === 0) {
|
|
9074
|
+
setPreview({
|
|
9075
|
+
trackIndex: pos.trackIndex,
|
|
9076
|
+
timeSec: pos.timeSec,
|
|
9077
|
+
widthPct: Math.min(
|
|
9078
|
+
100,
|
|
9079
|
+
DEFAULT_DROP_DURATION / duration * 100
|
|
9080
|
+
)
|
|
9081
|
+
});
|
|
9082
|
+
}
|
|
9083
|
+
}
|
|
9084
|
+
},
|
|
9085
|
+
[enabled, computePosition, tracks, duration]
|
|
9086
|
+
);
|
|
9087
|
+
const handleDragLeave = useCallback((e3) => {
|
|
9088
|
+
if (!e3.currentTarget.contains(e3.relatedTarget)) {
|
|
9089
|
+
setIsDraggingOver(false);
|
|
9090
|
+
setPreview(null);
|
|
9091
|
+
}
|
|
9092
|
+
}, []);
|
|
9093
|
+
const handleDrop = useCallback(
|
|
9094
|
+
async (e3) => {
|
|
9095
|
+
if (!enabled) return;
|
|
9096
|
+
e3.preventDefault();
|
|
9097
|
+
e3.stopPropagation();
|
|
9098
|
+
setIsDraggingOver(false);
|
|
9099
|
+
setPreview(null);
|
|
9100
|
+
const pos = computePosition(e3.clientX, e3.clientY);
|
|
9101
|
+
if (!pos || pos.trackIndex < 0) return;
|
|
9102
|
+
const track = tracks[pos.trackIndex] ?? null;
|
|
9103
|
+
if (e3.dataTransfer.types.includes(TIMELINE_DROP_MEDIA_TYPE)) {
|
|
9104
|
+
try {
|
|
9105
|
+
const data = JSON.parse(
|
|
9106
|
+
e3.dataTransfer.getData(TIMELINE_DROP_MEDIA_TYPE) || "{}"
|
|
9107
|
+
);
|
|
9108
|
+
if (data.type && data.url) {
|
|
9109
|
+
await onDrop({
|
|
9110
|
+
track,
|
|
9111
|
+
timeSec: pos.timeSec,
|
|
9112
|
+
type: data.type,
|
|
9113
|
+
url: data.url
|
|
9114
|
+
});
|
|
9115
|
+
}
|
|
9116
|
+
} catch {
|
|
9117
|
+
}
|
|
9118
|
+
return;
|
|
9119
|
+
}
|
|
9120
|
+
const files = Array.from(e3.dataTransfer.files || []);
|
|
9121
|
+
for (const file of files) {
|
|
9122
|
+
const type = getAssetTypeFromFile(file);
|
|
9123
|
+
if (!type) continue;
|
|
9124
|
+
const blobUrl = URL.createObjectURL(file);
|
|
9125
|
+
try {
|
|
9126
|
+
await onDrop({
|
|
9127
|
+
track,
|
|
9128
|
+
timeSec: pos.timeSec,
|
|
9129
|
+
type,
|
|
9130
|
+
url: blobUrl
|
|
9131
|
+
});
|
|
9132
|
+
} finally {
|
|
9133
|
+
URL.revokeObjectURL(blobUrl);
|
|
9134
|
+
}
|
|
9135
|
+
break;
|
|
9136
|
+
}
|
|
9137
|
+
},
|
|
9138
|
+
[enabled, computePosition, tracks, onDrop]
|
|
9139
|
+
);
|
|
9140
|
+
return { preview, isDraggingOver, handleDragOver, handleDragLeave, handleDrop };
|
|
9141
|
+
}
|
|
9142
|
+
function createElementFromDrop(type, blobUrl, parentSize) {
|
|
9143
|
+
switch (type) {
|
|
9144
|
+
case "video":
|
|
9145
|
+
return new VideoElement$1(blobUrl, parentSize);
|
|
9146
|
+
case "audio":
|
|
9147
|
+
return new AudioElement(blobUrl);
|
|
9148
|
+
case "image":
|
|
9149
|
+
return new ImageElement$1(blobUrl, parentSize);
|
|
9150
|
+
default:
|
|
9151
|
+
throw new Error(`Unknown asset type: ${type}`);
|
|
9152
|
+
}
|
|
9153
|
+
}
|
|
9154
|
+
const usePlayerManager = ({
|
|
9155
|
+
videoProps,
|
|
9156
|
+
canvasConfig
|
|
9157
|
+
}) => {
|
|
9158
|
+
const [projectData, setProjectData] = useState(null);
|
|
9159
|
+
const {
|
|
9160
|
+
timelineAction,
|
|
9161
|
+
setTimelineAction,
|
|
9162
|
+
setSelectedItem,
|
|
9163
|
+
editor,
|
|
9164
|
+
changeLog,
|
|
9165
|
+
videoResolution
|
|
9166
|
+
} = useTimelineContext();
|
|
9167
|
+
const { getCurrentTime } = useLivePlayerContext();
|
|
9168
|
+
const currentChangeLog = useRef(changeLog);
|
|
9169
|
+
const prevSeekTime = useRef(0);
|
|
9170
|
+
const [playerUpdating, setPlayerUpdating] = useState(false);
|
|
9171
|
+
const handleCanvasReady = (_canvas) => {
|
|
9172
|
+
};
|
|
9173
|
+
const handleCanvasOperation = (operation, data) => {
|
|
9174
|
+
var _a;
|
|
9175
|
+
if (operation === CANVAS_OPERATIONS.ADDED_NEW_ELEMENT) {
|
|
9176
|
+
if (data == null ? void 0 : data.element) {
|
|
9177
|
+
setSelectedItem(data.element);
|
|
9178
|
+
}
|
|
9179
|
+
return;
|
|
9180
|
+
}
|
|
9181
|
+
if (operation === CANVAS_OPERATIONS.Z_ORDER_CHANGED) {
|
|
9182
|
+
const { elementId, direction } = data ?? {};
|
|
9183
|
+
if (!elementId || !direction) return;
|
|
9184
|
+
const tracks = ((_a = editor.getTimelineData()) == null ? void 0 : _a.tracks) ?? [];
|
|
9185
|
+
const trackIndex = tracks.findIndex(
|
|
9186
|
+
(t2) => t2.getElements().some((el) => el.getId() === elementId)
|
|
9187
|
+
);
|
|
9188
|
+
if (trackIndex < 0) return;
|
|
9189
|
+
const track = tracks[trackIndex];
|
|
9190
|
+
const reordered = [...tracks];
|
|
9191
|
+
if (direction === "front") {
|
|
9192
|
+
reordered.splice(trackIndex, 1);
|
|
9193
|
+
reordered.push(track);
|
|
9194
|
+
} else if (direction === "back") {
|
|
9195
|
+
reordered.splice(trackIndex, 1);
|
|
9196
|
+
reordered.unshift(track);
|
|
9197
|
+
} else if (direction === "forward" && trackIndex < reordered.length - 1) {
|
|
9198
|
+
[reordered[trackIndex], reordered[trackIndex + 1]] = [reordered[trackIndex + 1], reordered[trackIndex]];
|
|
9199
|
+
} else if (direction === "backward" && trackIndex > 0) {
|
|
9200
|
+
[reordered[trackIndex - 1], reordered[trackIndex]] = [reordered[trackIndex], reordered[trackIndex - 1]];
|
|
9201
|
+
} else {
|
|
9202
|
+
return;
|
|
9203
|
+
}
|
|
9204
|
+
editor.reorderTracks(reordered);
|
|
9205
|
+
currentChangeLog.current = currentChangeLog.current + 1;
|
|
9206
|
+
return;
|
|
9207
|
+
}
|
|
9208
|
+
if (operation === CANVAS_OPERATIONS.CAPTION_PROPS_UPDATED) {
|
|
9209
|
+
const captionsTrack = editor.getCaptionsTrack();
|
|
9210
|
+
captionsTrack == null ? void 0 : captionsTrack.setProps(data.props);
|
|
9211
|
+
setSelectedItem(data.element);
|
|
9212
|
+
editor.refresh();
|
|
9213
|
+
} else if (operation === CANVAS_OPERATIONS.WATERMARK_UPDATED) {
|
|
9214
|
+
const w2 = editor.getWatermark();
|
|
9215
|
+
if (w2 && data) {
|
|
9216
|
+
if (data.position) w2.setPosition(data.position);
|
|
9217
|
+
if (data.rotation != null) w2.setRotation(data.rotation);
|
|
9218
|
+
if (data.opacity != null) w2.setOpacity(data.opacity);
|
|
9219
|
+
if (data.props) w2.setProps(data.props);
|
|
9220
|
+
editor.setWatermark(w2);
|
|
9221
|
+
currentChangeLog.current = currentChangeLog.current + 1;
|
|
9222
|
+
}
|
|
9223
|
+
} else {
|
|
9224
|
+
const element = ElementDeserializer.fromJSON(data);
|
|
9225
|
+
switch (operation) {
|
|
9226
|
+
case CANVAS_OPERATIONS.ITEM_SELECTED:
|
|
9227
|
+
setSelectedItem(element);
|
|
9228
|
+
break;
|
|
9229
|
+
case CANVAS_OPERATIONS.ITEM_UPDATED:
|
|
9230
|
+
if (element) {
|
|
9231
|
+
const updatedElement = editor.updateElement(element);
|
|
9232
|
+
currentChangeLog.current = currentChangeLog.current + 1;
|
|
9233
|
+
setSelectedItem(updatedElement);
|
|
9234
|
+
}
|
|
9235
|
+
break;
|
|
9236
|
+
}
|
|
9237
|
+
}
|
|
9238
|
+
};
|
|
9239
|
+
const handleDropOnCanvas = useCallback(
|
|
9240
|
+
async (payload) => {
|
|
9241
|
+
const { type, url, canvasX, canvasY } = payload;
|
|
9242
|
+
const element = createElementFromDrop(type, url, videoResolution);
|
|
9243
|
+
const currentTime = getCurrentTime();
|
|
9244
|
+
element.setStart(currentTime);
|
|
9245
|
+
const newTrack = editor.addTrack(`Track_${Date.now()}`);
|
|
9246
|
+
const result = await editor.addElementToTrack(newTrack, element);
|
|
9247
|
+
if (result) {
|
|
9248
|
+
setSelectedItem(element);
|
|
9249
|
+
currentChangeLog.current = currentChangeLog.current + 1;
|
|
9250
|
+
editor.refresh();
|
|
9251
|
+
handleCanvasOperation(CANVAS_OPERATIONS.ADDED_NEW_ELEMENT, {
|
|
9252
|
+
element,
|
|
9253
|
+
canvasPosition: canvasX != null && canvasY != null ? { x: canvasX, y: canvasY } : void 0
|
|
9254
|
+
});
|
|
9255
|
+
}
|
|
9256
|
+
},
|
|
9257
|
+
[editor, videoResolution, getCurrentTime, setSelectedItem]
|
|
9258
|
+
);
|
|
9259
|
+
const {
|
|
9260
|
+
twickCanvas,
|
|
9261
|
+
buildCanvas,
|
|
9262
|
+
setCanvasElements,
|
|
9263
|
+
bringToFront,
|
|
9264
|
+
sendToBack,
|
|
9265
|
+
bringForward,
|
|
9266
|
+
sendBackward
|
|
9267
|
+
} = useTwickCanvas({
|
|
9268
|
+
onCanvasReady: handleCanvasReady,
|
|
9269
|
+
onCanvasOperation: handleCanvasOperation,
|
|
9270
|
+
enableShiftAxisLock: (canvasConfig == null ? void 0 : canvasConfig.enableShiftAxisLock) ?? false
|
|
9271
|
+
});
|
|
9272
|
+
const updateCanvas = (seekTime) => {
|
|
9273
|
+
var _a;
|
|
9274
|
+
if (changeLog === currentChangeLog.current && seekTime === prevSeekTime.current) {
|
|
9275
|
+
return;
|
|
9276
|
+
}
|
|
9277
|
+
prevSeekTime.current = seekTime;
|
|
9278
|
+
const elements = getCurrentElements(
|
|
9279
|
+
seekTime,
|
|
9280
|
+
((_a = editor.getTimelineData()) == null ? void 0 : _a.tracks) ?? []
|
|
9281
|
+
);
|
|
9282
|
+
let captionProps = {};
|
|
9283
|
+
(elements || []).forEach((element) => {
|
|
9284
|
+
if (element instanceof CaptionElement$1) {
|
|
9285
|
+
const track = editor.getTrackById(element.getTrackId());
|
|
9286
|
+
captionProps = (track == null ? void 0 : track.getProps()) ?? {};
|
|
9287
|
+
}
|
|
9288
|
+
});
|
|
9289
|
+
const watermark = editor.getWatermark();
|
|
9290
|
+
let watermarkElement;
|
|
9291
|
+
if (watermark) {
|
|
9292
|
+
const position = watermark.getPosition();
|
|
9293
|
+
watermarkElement = {
|
|
9294
|
+
id: watermark.getId(),
|
|
9295
|
+
type: watermark.getType(),
|
|
9296
|
+
props: {
|
|
9297
|
+
...watermark.getProps() || {},
|
|
9298
|
+
x: (position == null ? void 0 : position.x) ?? 0,
|
|
9299
|
+
y: (position == null ? void 0 : position.y) ?? 0,
|
|
9300
|
+
rotation: watermark.getRotation() ?? 0,
|
|
9301
|
+
opacity: watermark.getOpacity() ?? 1
|
|
9302
|
+
}
|
|
9303
|
+
};
|
|
9304
|
+
}
|
|
9305
|
+
setCanvasElements({
|
|
9306
|
+
elements,
|
|
9307
|
+
watermark: watermarkElement,
|
|
9308
|
+
seekTime,
|
|
9309
|
+
captionProps,
|
|
9310
|
+
cleanAndAdd: true,
|
|
9311
|
+
lockAspectRatio: canvasConfig == null ? void 0 : canvasConfig.lockAspectRatio
|
|
9312
|
+
});
|
|
9313
|
+
currentChangeLog.current = changeLog;
|
|
9314
|
+
};
|
|
9315
|
+
const onPlayerUpdate = (event) => {
|
|
9316
|
+
var _a;
|
|
9317
|
+
if (((_a = event == null ? void 0 : event.detail) == null ? void 0 : _a.status) === "ready") {
|
|
9318
|
+
setPlayerUpdating(false);
|
|
9319
|
+
setTimelineAction(TIMELINE_ACTION.ON_PLAYER_UPDATED, null);
|
|
9320
|
+
}
|
|
9321
|
+
};
|
|
9322
|
+
const deleteElement = (elementId) => {
|
|
9323
|
+
var _a;
|
|
9324
|
+
const tracks = ((_a = editor.getTimelineData()) == null ? void 0 : _a.tracks) ?? [];
|
|
9325
|
+
for (const track of tracks) {
|
|
9326
|
+
const element = track.getElementById(elementId);
|
|
9327
|
+
if (element) {
|
|
9328
|
+
editor.removeElement(element);
|
|
9329
|
+
currentChangeLog.current = currentChangeLog.current + 1;
|
|
9330
|
+
setSelectedItem(null);
|
|
9331
|
+
updateCanvas(getCurrentTime());
|
|
9332
|
+
return;
|
|
9333
|
+
}
|
|
9334
|
+
}
|
|
9335
|
+
};
|
|
9336
|
+
useEffect(() => {
|
|
9337
|
+
var _a, _b, _c, _d, _e2;
|
|
9338
|
+
switch (timelineAction.type) {
|
|
9339
|
+
case TIMELINE_ACTION.UPDATE_PLAYER_DATA:
|
|
9340
|
+
if (videoProps) {
|
|
9341
|
+
if (((_a = timelineAction.payload) == null ? void 0 : _a.forceUpdate) || editor.getLatestVersion() !== ((_b = projectData == null ? void 0 : projectData.input) == null ? void 0 : _b.version)) {
|
|
9342
|
+
setPlayerUpdating(true);
|
|
9343
|
+
const _latestProjectData = {
|
|
9344
|
+
input: {
|
|
9345
|
+
properties: videoProps,
|
|
9346
|
+
tracks: ((_c = timelineAction.payload) == null ? void 0 : _c.tracks) ?? [],
|
|
9347
|
+
version: ((_d = timelineAction.payload) == null ? void 0 : _d.version) ?? 0
|
|
9348
|
+
}
|
|
9349
|
+
};
|
|
9350
|
+
setProjectData(_latestProjectData);
|
|
9351
|
+
if (((_e2 = timelineAction.payload) == null ? void 0 : _e2.version) === 1) {
|
|
9352
|
+
setTimeout(() => {
|
|
9353
|
+
setPlayerUpdating(false);
|
|
9354
|
+
});
|
|
9355
|
+
}
|
|
9356
|
+
} else {
|
|
9357
|
+
setTimelineAction(TIMELINE_ACTION.ON_PLAYER_UPDATED, null);
|
|
9358
|
+
}
|
|
9359
|
+
}
|
|
9360
|
+
break;
|
|
9361
|
+
}
|
|
9362
|
+
}, [timelineAction]);
|
|
9363
|
+
return {
|
|
9364
|
+
twickCanvas,
|
|
9365
|
+
projectData,
|
|
9366
|
+
updateCanvas,
|
|
9367
|
+
buildCanvas,
|
|
9368
|
+
onPlayerUpdate,
|
|
9369
|
+
playerUpdating,
|
|
9370
|
+
handleDropOnCanvas,
|
|
9371
|
+
bringToFront,
|
|
9372
|
+
sendToBack,
|
|
9373
|
+
bringForward,
|
|
9374
|
+
sendBackward,
|
|
9375
|
+
deleteElement
|
|
9376
|
+
};
|
|
9377
|
+
};
|
|
9378
|
+
function useCanvasDrop({
|
|
9379
|
+
containerRef,
|
|
9380
|
+
videoSize,
|
|
9381
|
+
onDrop,
|
|
9382
|
+
enabled = true
|
|
9383
|
+
}) {
|
|
9384
|
+
const handleDragOver = useCallback(
|
|
9385
|
+
(e3) => {
|
|
9386
|
+
if (!enabled) return;
|
|
9387
|
+
e3.preventDefault();
|
|
9388
|
+
e3.stopPropagation();
|
|
9389
|
+
const hasFiles = e3.dataTransfer.types.includes("Files");
|
|
9390
|
+
const hasPanelMedia = e3.dataTransfer.types.includes(TIMELINE_DROP_MEDIA_TYPE);
|
|
9391
|
+
if (!hasFiles && !hasPanelMedia) return;
|
|
9392
|
+
e3.dataTransfer.dropEffect = "copy";
|
|
9393
|
+
},
|
|
9394
|
+
[enabled]
|
|
9395
|
+
);
|
|
9396
|
+
const handleDrop = useCallback(
|
|
9397
|
+
async (e3) => {
|
|
9398
|
+
if (!enabled || !containerRef.current) return;
|
|
9399
|
+
e3.preventDefault();
|
|
9400
|
+
e3.stopPropagation();
|
|
9401
|
+
let type = null;
|
|
9402
|
+
let url = null;
|
|
9403
|
+
if (e3.dataTransfer.types.includes(TIMELINE_DROP_MEDIA_TYPE)) {
|
|
9404
|
+
try {
|
|
9405
|
+
const data = JSON.parse(
|
|
9406
|
+
e3.dataTransfer.getData(TIMELINE_DROP_MEDIA_TYPE) || "{}"
|
|
9407
|
+
);
|
|
9408
|
+
if (data.type && data.url) {
|
|
9409
|
+
type = data.type;
|
|
9410
|
+
url = data.url;
|
|
9411
|
+
}
|
|
9412
|
+
} catch {
|
|
9413
|
+
}
|
|
9414
|
+
}
|
|
9415
|
+
if (!type || !url) {
|
|
9416
|
+
const files = Array.from(e3.dataTransfer.files || []);
|
|
9417
|
+
for (const file of files) {
|
|
9418
|
+
const detectedType = getAssetTypeFromFile(file);
|
|
9419
|
+
if (detectedType) {
|
|
9420
|
+
type = detectedType;
|
|
9421
|
+
url = URL.createObjectURL(file);
|
|
9422
|
+
try {
|
|
9423
|
+
await onDrop({
|
|
9424
|
+
type,
|
|
9425
|
+
url,
|
|
9426
|
+
canvasX: getCanvasX(e3, containerRef.current, videoSize),
|
|
9427
|
+
canvasY: getCanvasY(e3, containerRef.current, videoSize)
|
|
9428
|
+
});
|
|
9429
|
+
} finally {
|
|
9430
|
+
URL.revokeObjectURL(url);
|
|
9431
|
+
}
|
|
9432
|
+
return;
|
|
9433
|
+
}
|
|
9434
|
+
}
|
|
9435
|
+
return;
|
|
9436
|
+
}
|
|
9437
|
+
await onDrop({
|
|
9438
|
+
type,
|
|
9439
|
+
url,
|
|
9440
|
+
canvasX: getCanvasX(e3, containerRef.current, videoSize),
|
|
9441
|
+
canvasY: getCanvasY(e3, containerRef.current, videoSize)
|
|
9442
|
+
});
|
|
9443
|
+
},
|
|
9444
|
+
[enabled, containerRef, videoSize, onDrop]
|
|
9445
|
+
);
|
|
9446
|
+
const handleDragLeave = useCallback((e3) => {
|
|
9447
|
+
if (!e3.currentTarget.contains(e3.relatedTarget)) ;
|
|
9448
|
+
}, []);
|
|
9449
|
+
return { handleDragOver, handleDragLeave, handleDrop };
|
|
9450
|
+
}
|
|
9451
|
+
function getCanvasX(e3, container, videoSize) {
|
|
9452
|
+
const rect = container.getBoundingClientRect();
|
|
9453
|
+
const relX = (e3.clientX - rect.left) / rect.width;
|
|
9454
|
+
return Math.max(0, Math.min(videoSize.width, relX * videoSize.width));
|
|
9455
|
+
}
|
|
9456
|
+
function getCanvasY(e3, container, videoSize) {
|
|
9457
|
+
const rect = container.getBoundingClientRect();
|
|
9458
|
+
const relY = (e3.clientY - rect.top) / rect.height;
|
|
9459
|
+
return Math.max(0, Math.min(videoSize.height, relY * videoSize.height));
|
|
9460
|
+
}
|
|
9461
|
+
const CanvasContextMenu = ({
|
|
9462
|
+
x: x2,
|
|
9463
|
+
y: y2,
|
|
9464
|
+
elementId,
|
|
9465
|
+
onBringToFront,
|
|
9466
|
+
onSendToBack,
|
|
9467
|
+
onBringForward,
|
|
9468
|
+
onSendBackward,
|
|
9469
|
+
onDelete,
|
|
9470
|
+
onClose
|
|
9471
|
+
}) => {
|
|
9472
|
+
const menuRef = useRef(null);
|
|
9473
|
+
useEffect(() => {
|
|
9474
|
+
const handleClickOutside = (e3) => {
|
|
9475
|
+
if (menuRef.current && !menuRef.current.contains(e3.target)) {
|
|
9476
|
+
onClose();
|
|
9477
|
+
}
|
|
9478
|
+
};
|
|
9479
|
+
const handleEscape = (e3) => {
|
|
9480
|
+
if (e3.key === "Escape") onClose();
|
|
9481
|
+
};
|
|
9482
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
9483
|
+
document.addEventListener("keydown", handleEscape);
|
|
9484
|
+
return () => {
|
|
9485
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
9486
|
+
document.removeEventListener("keydown", handleEscape);
|
|
9487
|
+
};
|
|
9488
|
+
}, [onClose]);
|
|
9489
|
+
const handleAction = (fn2) => {
|
|
9490
|
+
fn2(elementId);
|
|
9491
|
+
onClose();
|
|
9492
|
+
};
|
|
9493
|
+
return /* @__PURE__ */ jsxs(
|
|
9494
|
+
"div",
|
|
9495
|
+
{
|
|
9496
|
+
ref: menuRef,
|
|
9497
|
+
className: "twick-canvas-context-menu",
|
|
9498
|
+
style: { left: x2, top: y2 },
|
|
9499
|
+
role: "menu",
|
|
9500
|
+
children: [
|
|
9501
|
+
/* @__PURE__ */ jsx(
|
|
9502
|
+
"button",
|
|
9503
|
+
{
|
|
9504
|
+
type: "button",
|
|
9505
|
+
className: "twick-canvas-context-menu-item",
|
|
9506
|
+
onClick: () => handleAction(onBringToFront),
|
|
9507
|
+
role: "menuitem",
|
|
9508
|
+
children: "Bring to Front"
|
|
9509
|
+
}
|
|
9510
|
+
),
|
|
9511
|
+
/* @__PURE__ */ jsx(
|
|
9512
|
+
"button",
|
|
9513
|
+
{
|
|
9514
|
+
type: "button",
|
|
9515
|
+
className: "twick-canvas-context-menu-item",
|
|
9516
|
+
onClick: () => handleAction(onBringForward),
|
|
9517
|
+
role: "menuitem",
|
|
9518
|
+
children: "Bring Forward"
|
|
9519
|
+
}
|
|
9520
|
+
),
|
|
9521
|
+
/* @__PURE__ */ jsx(
|
|
9522
|
+
"button",
|
|
9523
|
+
{
|
|
9524
|
+
type: "button",
|
|
9525
|
+
className: "twick-canvas-context-menu-item",
|
|
9526
|
+
onClick: () => handleAction(onSendBackward),
|
|
9527
|
+
role: "menuitem",
|
|
9528
|
+
children: "Send Backward"
|
|
9529
|
+
}
|
|
9530
|
+
),
|
|
9531
|
+
/* @__PURE__ */ jsx(
|
|
9532
|
+
"button",
|
|
9533
|
+
{
|
|
9534
|
+
type: "button",
|
|
9535
|
+
className: "twick-canvas-context-menu-item",
|
|
9536
|
+
onClick: () => handleAction(onSendToBack),
|
|
9537
|
+
role: "menuitem",
|
|
9538
|
+
children: "Send to Back"
|
|
9539
|
+
}
|
|
9540
|
+
),
|
|
9541
|
+
/* @__PURE__ */ jsx("div", { className: "twick-canvas-context-menu-separator", role: "separator" }),
|
|
9542
|
+
/* @__PURE__ */ jsx(
|
|
9543
|
+
"button",
|
|
9544
|
+
{
|
|
9545
|
+
type: "button",
|
|
9546
|
+
className: "twick-canvas-context-menu-item twick-canvas-context-menu-item-danger",
|
|
9547
|
+
onClick: () => handleAction(onDelete),
|
|
9548
|
+
role: "menuitem",
|
|
9549
|
+
children: "Delete"
|
|
9550
|
+
}
|
|
9551
|
+
)
|
|
9552
|
+
]
|
|
9553
|
+
}
|
|
9554
|
+
);
|
|
9555
|
+
};
|
|
9556
|
+
const PlayerManager = ({
|
|
9557
|
+
videoProps,
|
|
9558
|
+
playerProps,
|
|
9559
|
+
canvasMode,
|
|
9560
|
+
canvasConfig
|
|
9561
|
+
}) => {
|
|
9562
|
+
const containerRef = useRef(null);
|
|
9563
|
+
const canvasRef = useRef(null);
|
|
9564
|
+
const durationRef = useRef(0);
|
|
9565
|
+
const { changeLog } = useTimelineContext();
|
|
9566
|
+
const {
|
|
9567
|
+
playerState,
|
|
9568
|
+
playerVolume,
|
|
9569
|
+
seekTime,
|
|
8676
9570
|
setPlayerState,
|
|
8677
9571
|
setCurrentTime
|
|
8678
9572
|
} = useLivePlayerContext();
|
|
8679
|
-
const
|
|
8680
|
-
|
|
9573
|
+
const {
|
|
9574
|
+
twickCanvas,
|
|
9575
|
+
projectData,
|
|
9576
|
+
updateCanvas,
|
|
9577
|
+
playerUpdating,
|
|
9578
|
+
onPlayerUpdate,
|
|
9579
|
+
buildCanvas,
|
|
9580
|
+
handleDropOnCanvas,
|
|
9581
|
+
bringToFront,
|
|
9582
|
+
sendToBack,
|
|
9583
|
+
bringForward,
|
|
9584
|
+
sendBackward,
|
|
9585
|
+
deleteElement
|
|
9586
|
+
} = usePlayerManager({ videoProps, canvasConfig });
|
|
9587
|
+
const [contextMenu, setContextMenu] = useState(null);
|
|
9588
|
+
const closeContextMenu = useCallback(() => setContextMenu(null), []);
|
|
9589
|
+
const { handleDragOver, handleDragLeave, handleDrop } = useCanvasDrop({
|
|
9590
|
+
containerRef,
|
|
9591
|
+
videoSize: { width: videoProps.width, height: videoProps.height },
|
|
9592
|
+
onDrop: handleDropOnCanvas,
|
|
9593
|
+
enabled: !!handleDropOnCanvas && canvasMode
|
|
9594
|
+
});
|
|
8681
9595
|
useEffect(() => {
|
|
8682
9596
|
const container = containerRef.current;
|
|
8683
9597
|
const canvasSize = {
|
|
@@ -8699,6 +9613,22 @@ const PlayerManager = ({
|
|
|
8699
9613
|
updateCanvas(seekTime);
|
|
8700
9614
|
}
|
|
8701
9615
|
}, [twickCanvas, playerState, seekTime, changeLog]);
|
|
9616
|
+
useEffect(() => {
|
|
9617
|
+
if (!twickCanvas || !canvasMode) return;
|
|
9618
|
+
const onSelectionCreated = (e3) => {
|
|
9619
|
+
var _a, _b;
|
|
9620
|
+
const ev = e3 == null ? void 0 : e3.e;
|
|
9621
|
+
if (!ev) return;
|
|
9622
|
+
const id2 = (_b = (_a = e3.target) == null ? void 0 : _a.get) == null ? void 0 : _b.call(_a, "id");
|
|
9623
|
+
if (id2) {
|
|
9624
|
+
setContextMenu({ x: ev.clientX, y: ev.clientY, elementId: id2 });
|
|
9625
|
+
}
|
|
9626
|
+
};
|
|
9627
|
+
twickCanvas.on("contextmenu", onSelectionCreated);
|
|
9628
|
+
return () => {
|
|
9629
|
+
twickCanvas.off("contextmenu", onSelectionCreated);
|
|
9630
|
+
};
|
|
9631
|
+
}, [twickCanvas, canvasMode]);
|
|
8702
9632
|
const handleTimeUpdate = (time2) => {
|
|
8703
9633
|
if (durationRef.current && time2 >= durationRef.current) {
|
|
8704
9634
|
setCurrentTime(0);
|
|
@@ -8755,8 +9685,26 @@ const PlayerManager = ({
|
|
|
8755
9685
|
style: {
|
|
8756
9686
|
opacity: playerState === PLAYER_STATE.PAUSED ? 1 : 0
|
|
8757
9687
|
},
|
|
9688
|
+
onDragOver: handleDragOver,
|
|
9689
|
+
onDragLeave: handleDragLeave,
|
|
9690
|
+
onDrop: handleDrop,
|
|
9691
|
+
onContextMenu: (e3) => e3.preventDefault(),
|
|
8758
9692
|
children: /* @__PURE__ */ jsx("canvas", { ref: canvasRef, className: "twick-editor-canvas" })
|
|
8759
9693
|
}
|
|
9694
|
+
),
|
|
9695
|
+
contextMenu && /* @__PURE__ */ jsx(
|
|
9696
|
+
CanvasContextMenu,
|
|
9697
|
+
{
|
|
9698
|
+
x: contextMenu.x,
|
|
9699
|
+
y: contextMenu.y,
|
|
9700
|
+
elementId: contextMenu.elementId,
|
|
9701
|
+
onBringToFront: bringToFront,
|
|
9702
|
+
onSendToBack: sendToBack,
|
|
9703
|
+
onBringForward: bringForward,
|
|
9704
|
+
onSendBackward: sendBackward,
|
|
9705
|
+
onDelete: deleteElement,
|
|
9706
|
+
onClose: closeContextMenu
|
|
9707
|
+
}
|
|
8760
9708
|
)
|
|
8761
9709
|
]
|
|
8762
9710
|
}
|
|
@@ -10098,7 +11046,8 @@ function SeekTrack({
|
|
|
10098
11046
|
zoom = 1,
|
|
10099
11047
|
onSeek,
|
|
10100
11048
|
timelineCount = 0,
|
|
10101
|
-
timelineTickConfigs
|
|
11049
|
+
timelineTickConfigs,
|
|
11050
|
+
onPlayheadUpdate
|
|
10102
11051
|
}) {
|
|
10103
11052
|
const containerRef = useRef(null);
|
|
10104
11053
|
const [isDragging2, setIsDragging] = useState(false);
|
|
@@ -10110,6 +11059,12 @@ function SeekTrack({
|
|
|
10110
11059
|
const position = isDragging2 && dragPosition !== null ? dragPosition : currentTime * pixelsPerSecond;
|
|
10111
11060
|
return Math.max(0, position);
|
|
10112
11061
|
}, [isDragging2, dragPosition, currentTime, pixelsPerSecond]);
|
|
11062
|
+
React.useEffect(() => {
|
|
11063
|
+
onPlayheadUpdate == null ? void 0 : onPlayheadUpdate({
|
|
11064
|
+
positionPx: seekPosition,
|
|
11065
|
+
isDragging: isDragging2
|
|
11066
|
+
});
|
|
11067
|
+
}, [seekPosition, isDragging2, onPlayheadUpdate]);
|
|
10113
11068
|
const { majorIntervalSec, minorIntervalSec } = useMemo(() => {
|
|
10114
11069
|
if (timelineTickConfigs && timelineTickConfigs.length > 0) {
|
|
10115
11070
|
const sortedConfigs = [...timelineTickConfigs].sort((a2, b2) => a2.durationThreshold - b2.durationThreshold);
|
|
@@ -10278,7 +11233,7 @@ function SeekTrack({
|
|
|
10278
11233
|
transform: `translateX(${seekPosition}px)`,
|
|
10279
11234
|
top: 0,
|
|
10280
11235
|
touchAction: "none",
|
|
10281
|
-
transition: isDragging2 ? "none" : "transform 0.
|
|
11236
|
+
transition: isDragging2 ? "none" : "transform 150ms cubic-bezier(0.4, 0, 0.2, 1)",
|
|
10282
11237
|
willChange: isDragging2 ? "transform" : "auto"
|
|
10283
11238
|
},
|
|
10284
11239
|
children: [
|
|
@@ -10302,7 +11257,8 @@ const SeekControl = ({
|
|
|
10302
11257
|
zoom,
|
|
10303
11258
|
timelineCount,
|
|
10304
11259
|
onSeek,
|
|
10305
|
-
timelineTickConfigs
|
|
11260
|
+
timelineTickConfigs,
|
|
11261
|
+
onPlayheadUpdate
|
|
10306
11262
|
}) => {
|
|
10307
11263
|
const { currentTime } = useLivePlayerContext();
|
|
10308
11264
|
return /* @__PURE__ */ jsx(
|
|
@@ -10313,7 +11269,8 @@ const SeekControl = ({
|
|
|
10313
11269
|
zoom,
|
|
10314
11270
|
onSeek,
|
|
10315
11271
|
timelineCount,
|
|
10316
|
-
timelineTickConfigs
|
|
11272
|
+
timelineTickConfigs,
|
|
11273
|
+
onPlayheadUpdate
|
|
10317
11274
|
}
|
|
10318
11275
|
);
|
|
10319
11276
|
};
|
|
@@ -10422,7 +11379,21 @@ const createLucideIcon = (iconName, iconNode) => {
|
|
|
10422
11379
|
* This source code is licensed under the ISC license.
|
|
10423
11380
|
* See the LICENSE file in the root directory of this source tree.
|
|
10424
11381
|
*/
|
|
10425
|
-
const __iconNode$
|
|
11382
|
+
const __iconNode$e = [
|
|
11383
|
+
["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
|
|
11384
|
+
["line", { x1: "22", x2: "18", y1: "12", y2: "12", key: "l9bcsi" }],
|
|
11385
|
+
["line", { x1: "6", x2: "2", y1: "12", y2: "12", key: "13hhkx" }],
|
|
11386
|
+
["line", { x1: "12", x2: "12", y1: "6", y2: "2", key: "10w3f3" }],
|
|
11387
|
+
["line", { x1: "12", x2: "12", y1: "22", y2: "18", key: "15g9kq" }]
|
|
11388
|
+
];
|
|
11389
|
+
const Crosshair = createLucideIcon("crosshair", __iconNode$e);
|
|
11390
|
+
/**
|
|
11391
|
+
* @license lucide-react v0.511.0 - ISC
|
|
11392
|
+
*
|
|
11393
|
+
* This source code is licensed under the ISC license.
|
|
11394
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
11395
|
+
*/
|
|
11396
|
+
const __iconNode$d = [
|
|
10426
11397
|
["circle", { cx: "9", cy: "12", r: "1", key: "1vctgf" }],
|
|
10427
11398
|
["circle", { cx: "9", cy: "5", r: "1", key: "hp0tcf" }],
|
|
10428
11399
|
["circle", { cx: "9", cy: "19", r: "1", key: "fkjjf6" }],
|
|
@@ -10430,81 +11401,103 @@ const __iconNode$b = [
|
|
|
10430
11401
|
["circle", { cx: "15", cy: "5", r: "1", key: "19l28e" }],
|
|
10431
11402
|
["circle", { cx: "15", cy: "19", r: "1", key: "f4zoj3" }]
|
|
10432
11403
|
];
|
|
10433
|
-
const GripVertical = createLucideIcon("grip-vertical", __iconNode$
|
|
11404
|
+
const GripVertical = createLucideIcon("grip-vertical", __iconNode$d);
|
|
10434
11405
|
/**
|
|
10435
11406
|
* @license lucide-react v0.511.0 - ISC
|
|
10436
11407
|
*
|
|
10437
11408
|
* This source code is licensed under the ISC license.
|
|
10438
11409
|
* See the LICENSE file in the root directory of this source tree.
|
|
10439
11410
|
*/
|
|
10440
|
-
const __iconNode$
|
|
10441
|
-
const LoaderCircle = createLucideIcon("loader-circle", __iconNode$
|
|
11411
|
+
const __iconNode$c = [["path", { d: "M21 12a9 9 0 1 1-6.219-8.56", key: "13zald" }]];
|
|
11412
|
+
const LoaderCircle = createLucideIcon("loader-circle", __iconNode$c);
|
|
10442
11413
|
/**
|
|
10443
11414
|
* @license lucide-react v0.511.0 - ISC
|
|
10444
11415
|
*
|
|
10445
11416
|
* This source code is licensed under the ISC license.
|
|
10446
11417
|
* See the LICENSE file in the root directory of this source tree.
|
|
10447
11418
|
*/
|
|
10448
|
-
const __iconNode$
|
|
11419
|
+
const __iconNode$b = [
|
|
10449
11420
|
["rect", { width: "18", height: "11", x: "3", y: "11", rx: "2", ry: "2", key: "1w4ew1" }],
|
|
10450
11421
|
["path", { d: "M7 11V7a5 5 0 0 1 10 0v4", key: "fwvmzm" }]
|
|
10451
11422
|
];
|
|
10452
|
-
const Lock = createLucideIcon("lock", __iconNode$
|
|
11423
|
+
const Lock = createLucideIcon("lock", __iconNode$b);
|
|
10453
11424
|
/**
|
|
10454
11425
|
* @license lucide-react v0.511.0 - ISC
|
|
10455
11426
|
*
|
|
10456
11427
|
* This source code is licensed under the ISC license.
|
|
10457
11428
|
* See the LICENSE file in the root directory of this source tree.
|
|
10458
11429
|
*/
|
|
10459
|
-
const __iconNode$
|
|
11430
|
+
const __iconNode$a = [
|
|
10460
11431
|
["rect", { x: "14", y: "4", width: "4", height: "16", rx: "1", key: "zuxfzm" }],
|
|
10461
11432
|
["rect", { x: "6", y: "4", width: "4", height: "16", rx: "1", key: "1okwgv" }]
|
|
10462
11433
|
];
|
|
10463
|
-
const Pause = createLucideIcon("pause", __iconNode$
|
|
11434
|
+
const Pause = createLucideIcon("pause", __iconNode$a);
|
|
10464
11435
|
/**
|
|
10465
11436
|
* @license lucide-react v0.511.0 - ISC
|
|
10466
11437
|
*
|
|
10467
11438
|
* This source code is licensed under the ISC license.
|
|
10468
11439
|
* See the LICENSE file in the root directory of this source tree.
|
|
10469
11440
|
*/
|
|
10470
|
-
const __iconNode$
|
|
10471
|
-
const Play = createLucideIcon("play", __iconNode$
|
|
11441
|
+
const __iconNode$9 = [["polygon", { points: "6 3 20 12 6 21 6 3", key: "1oa8hb" }]];
|
|
11442
|
+
const Play = createLucideIcon("play", __iconNode$9);
|
|
10472
11443
|
/**
|
|
10473
11444
|
* @license lucide-react v0.511.0 - ISC
|
|
10474
11445
|
*
|
|
10475
11446
|
* This source code is licensed under the ISC license.
|
|
10476
11447
|
* See the LICENSE file in the root directory of this source tree.
|
|
10477
11448
|
*/
|
|
10478
|
-
const __iconNode$
|
|
11449
|
+
const __iconNode$8 = [
|
|
10479
11450
|
["path", { d: "M5 12h14", key: "1ays0h" }],
|
|
10480
11451
|
["path", { d: "M12 5v14", key: "s699le" }]
|
|
10481
11452
|
];
|
|
10482
|
-
const Plus = createLucideIcon("plus", __iconNode$
|
|
11453
|
+
const Plus = createLucideIcon("plus", __iconNode$8);
|
|
10483
11454
|
/**
|
|
10484
11455
|
* @license lucide-react v0.511.0 - ISC
|
|
10485
11456
|
*
|
|
10486
11457
|
* This source code is licensed under the ISC license.
|
|
10487
11458
|
* See the LICENSE file in the root directory of this source tree.
|
|
10488
11459
|
*/
|
|
10489
|
-
const __iconNode$
|
|
11460
|
+
const __iconNode$7 = [
|
|
10490
11461
|
["path", { d: "m15 14 5-5-5-5", key: "12vg1m" }],
|
|
10491
11462
|
["path", { d: "M20 9H9.5A5.5 5.5 0 0 0 4 14.5A5.5 5.5 0 0 0 9.5 20H13", key: "6uklza" }]
|
|
10492
11463
|
];
|
|
10493
|
-
const Redo2 = createLucideIcon("redo-2", __iconNode$
|
|
11464
|
+
const Redo2 = createLucideIcon("redo-2", __iconNode$7);
|
|
10494
11465
|
/**
|
|
10495
11466
|
* @license lucide-react v0.511.0 - ISC
|
|
10496
11467
|
*
|
|
10497
11468
|
* This source code is licensed under the ISC license.
|
|
10498
11469
|
* See the LICENSE file in the root directory of this source tree.
|
|
10499
11470
|
*/
|
|
10500
|
-
const __iconNode$
|
|
11471
|
+
const __iconNode$6 = [
|
|
10501
11472
|
["circle", { cx: "6", cy: "6", r: "3", key: "1lh9wr" }],
|
|
10502
11473
|
["path", { d: "M8.12 8.12 12 12", key: "1alkpv" }],
|
|
10503
11474
|
["path", { d: "M20 4 8.12 15.88", key: "xgtan2" }],
|
|
10504
11475
|
["circle", { cx: "6", cy: "18", r: "3", key: "fqmcym" }],
|
|
10505
11476
|
["path", { d: "M14.8 14.8 20 20", key: "ptml3r" }]
|
|
10506
11477
|
];
|
|
10507
|
-
const Scissors = createLucideIcon("scissors", __iconNode$
|
|
11478
|
+
const Scissors = createLucideIcon("scissors", __iconNode$6);
|
|
11479
|
+
/**
|
|
11480
|
+
* @license lucide-react v0.511.0 - ISC
|
|
11481
|
+
*
|
|
11482
|
+
* This source code is licensed under the ISC license.
|
|
11483
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
11484
|
+
*/
|
|
11485
|
+
const __iconNode$5 = [
|
|
11486
|
+
["polygon", { points: "19 20 9 12 19 4 19 20", key: "o2sva" }],
|
|
11487
|
+
["line", { x1: "5", x2: "5", y1: "19", y2: "5", key: "1ocqjk" }]
|
|
11488
|
+
];
|
|
11489
|
+
const SkipBack = createLucideIcon("skip-back", __iconNode$5);
|
|
11490
|
+
/**
|
|
11491
|
+
* @license lucide-react v0.511.0 - ISC
|
|
11492
|
+
*
|
|
11493
|
+
* This source code is licensed under the ISC license.
|
|
11494
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
11495
|
+
*/
|
|
11496
|
+
const __iconNode$4 = [
|
|
11497
|
+
["polygon", { points: "5 4 15 12 5 20 5 4", key: "16p6eg" }],
|
|
11498
|
+
["line", { x1: "19", x2: "19", y1: "5", y2: "19", key: "futhcm" }]
|
|
11499
|
+
];
|
|
11500
|
+
const SkipForward = createLucideIcon("skip-forward", __iconNode$4);
|
|
10508
11501
|
/**
|
|
10509
11502
|
* @license lucide-react v0.511.0 - ISC
|
|
10510
11503
|
*
|
|
@@ -10557,7 +11550,7 @@ const __iconNode = [
|
|
|
10557
11550
|
const ZoomOut = createLucideIcon("zoom-out", __iconNode);
|
|
10558
11551
|
const TrackHeader = ({
|
|
10559
11552
|
track,
|
|
10560
|
-
|
|
11553
|
+
selectedIds,
|
|
10561
11554
|
onDragStart,
|
|
10562
11555
|
onDragOver,
|
|
10563
11556
|
onDrop,
|
|
@@ -10566,9 +11559,9 @@ const TrackHeader = ({
|
|
|
10566
11559
|
return /* @__PURE__ */ jsxs(
|
|
10567
11560
|
"div",
|
|
10568
11561
|
{
|
|
10569
|
-
className: `twick-track-header ${
|
|
11562
|
+
className: `twick-track-header ${selectedIds.has(track.getId()) ? "twick-track-header-selected" : "twick-track-header-default"}`,
|
|
10570
11563
|
draggable: true,
|
|
10571
|
-
onClick: () => onSelect(track),
|
|
11564
|
+
onClick: (e3) => onSelect(track, e3),
|
|
10572
11565
|
onDragStart: (e3) => onDragStart(e3, track),
|
|
10573
11566
|
onDragOver,
|
|
10574
11567
|
onDrop: (e3) => onDrop(e3, track),
|
|
@@ -17605,321 +18598,124 @@ class VisualElement {
|
|
|
17605
18598
|
}
|
|
17606
18599
|
const target = this.getBaseTargetFromProps(this.props, key);
|
|
17607
18600
|
if (target !== void 0 && !isMotionValue(target))
|
|
17608
|
-
return target;
|
|
17609
|
-
return this.initialValues[key] !== void 0 && valueFromInitial === void 0 ? void 0 : this.baseTarget[key];
|
|
17610
|
-
}
|
|
17611
|
-
on(eventName, callback) {
|
|
17612
|
-
if (!this.events[eventName]) {
|
|
17613
|
-
this.events[eventName] = new SubscriptionManager();
|
|
17614
|
-
}
|
|
17615
|
-
return this.events[eventName].add(callback);
|
|
17616
|
-
}
|
|
17617
|
-
notify(eventName, ...args) {
|
|
17618
|
-
if (this.events[eventName]) {
|
|
17619
|
-
this.events[eventName].notify(...args);
|
|
17620
|
-
}
|
|
17621
|
-
}
|
|
17622
|
-
}
|
|
17623
|
-
class DOMVisualElement extends VisualElement {
|
|
17624
|
-
constructor() {
|
|
17625
|
-
super(...arguments);
|
|
17626
|
-
this.KeyframeResolver = DOMKeyframesResolver;
|
|
17627
|
-
}
|
|
17628
|
-
sortInstanceNodePosition(a2, b2) {
|
|
17629
|
-
return a2.compareDocumentPosition(b2) & 2 ? 1 : -1;
|
|
17630
|
-
}
|
|
17631
|
-
getBaseTargetFromProps(props, key) {
|
|
17632
|
-
return props.style ? props.style[key] : void 0;
|
|
17633
|
-
}
|
|
17634
|
-
removeValueFromRenderState(key, { vars, style }) {
|
|
17635
|
-
delete vars[key];
|
|
17636
|
-
delete style[key];
|
|
17637
|
-
}
|
|
17638
|
-
handleChildMotionValue() {
|
|
17639
|
-
if (this.childSubscription) {
|
|
17640
|
-
this.childSubscription();
|
|
17641
|
-
delete this.childSubscription;
|
|
17642
|
-
}
|
|
17643
|
-
const { children } = this.props;
|
|
17644
|
-
if (isMotionValue(children)) {
|
|
17645
|
-
this.childSubscription = children.on("change", (latest) => {
|
|
17646
|
-
if (this.current) {
|
|
17647
|
-
this.current.textContent = `${latest}`;
|
|
17648
|
-
}
|
|
17649
|
-
});
|
|
17650
|
-
}
|
|
17651
|
-
}
|
|
17652
|
-
}
|
|
17653
|
-
function getComputedStyle(element) {
|
|
17654
|
-
return window.getComputedStyle(element);
|
|
17655
|
-
}
|
|
17656
|
-
class HTMLVisualElement extends DOMVisualElement {
|
|
17657
|
-
constructor() {
|
|
17658
|
-
super(...arguments);
|
|
17659
|
-
this.type = "html";
|
|
17660
|
-
this.renderInstance = renderHTML;
|
|
17661
|
-
}
|
|
17662
|
-
readValueFromInstance(instance, key) {
|
|
17663
|
-
if (transformProps.has(key)) {
|
|
17664
|
-
const defaultType = getDefaultValueType(key);
|
|
17665
|
-
return defaultType ? defaultType.default || 0 : 0;
|
|
17666
|
-
} else {
|
|
17667
|
-
const computedStyle = getComputedStyle(instance);
|
|
17668
|
-
const value = (isCSSVariableName(key) ? computedStyle.getPropertyValue(key) : computedStyle[key]) || 0;
|
|
17669
|
-
return typeof value === "string" ? value.trim() : value;
|
|
17670
|
-
}
|
|
17671
|
-
}
|
|
17672
|
-
measureInstanceViewportBox(instance, { transformPagePoint }) {
|
|
17673
|
-
return measureViewportBox(instance, transformPagePoint);
|
|
17674
|
-
}
|
|
17675
|
-
build(renderState, latestValues, props) {
|
|
17676
|
-
buildHTMLStyles(renderState, latestValues, props.transformTemplate);
|
|
17677
|
-
}
|
|
17678
|
-
scrapeMotionValuesFromProps(props, prevProps, visualElement) {
|
|
17679
|
-
return scrapeMotionValuesFromProps$1(props, prevProps, visualElement);
|
|
17680
|
-
}
|
|
17681
|
-
}
|
|
17682
|
-
class SVGVisualElement extends DOMVisualElement {
|
|
17683
|
-
constructor() {
|
|
17684
|
-
super(...arguments);
|
|
17685
|
-
this.type = "svg";
|
|
17686
|
-
this.isSVGTag = false;
|
|
17687
|
-
this.measureInstanceViewportBox = createBox;
|
|
18601
|
+
return target;
|
|
18602
|
+
return this.initialValues[key] !== void 0 && valueFromInitial === void 0 ? void 0 : this.baseTarget[key];
|
|
17688
18603
|
}
|
|
17689
|
-
|
|
17690
|
-
|
|
18604
|
+
on(eventName, callback) {
|
|
18605
|
+
if (!this.events[eventName]) {
|
|
18606
|
+
this.events[eventName] = new SubscriptionManager();
|
|
18607
|
+
}
|
|
18608
|
+
return this.events[eventName].add(callback);
|
|
17691
18609
|
}
|
|
17692
|
-
|
|
17693
|
-
if (
|
|
17694
|
-
|
|
17695
|
-
return defaultType ? defaultType.default || 0 : 0;
|
|
18610
|
+
notify(eventName, ...args) {
|
|
18611
|
+
if (this.events[eventName]) {
|
|
18612
|
+
this.events[eventName].notify(...args);
|
|
17696
18613
|
}
|
|
17697
|
-
key = !camelCaseAttributes.has(key) ? camelToDash(key) : key;
|
|
17698
|
-
return instance.getAttribute(key);
|
|
17699
18614
|
}
|
|
17700
|
-
|
|
17701
|
-
|
|
18615
|
+
}
|
|
18616
|
+
class DOMVisualElement extends VisualElement {
|
|
18617
|
+
constructor() {
|
|
18618
|
+
super(...arguments);
|
|
18619
|
+
this.KeyframeResolver = DOMKeyframesResolver;
|
|
17702
18620
|
}
|
|
17703
|
-
|
|
17704
|
-
|
|
18621
|
+
sortInstanceNodePosition(a2, b2) {
|
|
18622
|
+
return a2.compareDocumentPosition(b2) & 2 ? 1 : -1;
|
|
17705
18623
|
}
|
|
17706
|
-
|
|
17707
|
-
|
|
18624
|
+
getBaseTargetFromProps(props, key) {
|
|
18625
|
+
return props.style ? props.style[key] : void 0;
|
|
17708
18626
|
}
|
|
17709
|
-
|
|
17710
|
-
|
|
17711
|
-
|
|
18627
|
+
removeValueFromRenderState(key, { vars, style }) {
|
|
18628
|
+
delete vars[key];
|
|
18629
|
+
delete style[key];
|
|
17712
18630
|
}
|
|
17713
|
-
|
|
17714
|
-
|
|
17715
|
-
|
|
17716
|
-
|
|
17717
|
-
});
|
|
17718
|
-
};
|
|
17719
|
-
const createMotionComponent = /* @__PURE__ */ createMotionComponentFactory({
|
|
17720
|
-
...animations,
|
|
17721
|
-
...gestureAnimations,
|
|
17722
|
-
...drag,
|
|
17723
|
-
...layout
|
|
17724
|
-
}, createDomVisualElement);
|
|
17725
|
-
const motion = /* @__PURE__ */ createDOMMotionComponentProxy(createMotionComponent);
|
|
17726
|
-
const INITIAL_TIMELINE_DATA = {
|
|
17727
|
-
tracks: [
|
|
17728
|
-
{
|
|
17729
|
-
type: "element",
|
|
17730
|
-
id: "t-sample",
|
|
17731
|
-
name: "sample",
|
|
17732
|
-
elements: [
|
|
17733
|
-
{
|
|
17734
|
-
id: "e-sample",
|
|
17735
|
-
trackId: "t-sample",
|
|
17736
|
-
name: "sample",
|
|
17737
|
-
type: "text",
|
|
17738
|
-
s: 0,
|
|
17739
|
-
e: 5,
|
|
17740
|
-
props: {
|
|
17741
|
-
text: "Twick Video Editor",
|
|
17742
|
-
fill: "#FFFFFF"
|
|
17743
|
-
}
|
|
17744
|
-
}
|
|
17745
|
-
]
|
|
18631
|
+
handleChildMotionValue() {
|
|
18632
|
+
if (this.childSubscription) {
|
|
18633
|
+
this.childSubscription();
|
|
18634
|
+
delete this.childSubscription;
|
|
17746
18635
|
}
|
|
17747
|
-
|
|
17748
|
-
|
|
17749
|
-
|
|
17750
|
-
|
|
17751
|
-
|
|
17752
|
-
|
|
17753
|
-
|
|
17754
|
-
|
|
17755
|
-
|
|
17756
|
-
|
|
17757
|
-
|
|
17758
|
-
|
|
17759
|
-
|
|
17760
|
-
|
|
17761
|
-
|
|
17762
|
-
|
|
17763
|
-
|
|
17764
|
-
|
|
17765
|
-
|
|
17766
|
-
|
|
17767
|
-
|
|
17768
|
-
|
|
17769
|
-
|
|
17770
|
-
|
|
17771
|
-
|
|
17772
|
-
|
|
17773
|
-
|
|
17774
|
-
|
|
17775
|
-
|
|
17776
|
-
|
|
17777
|
-
|
|
17778
|
-
}
|
|
17779
|
-
{
|
|
17780
|
-
|
|
17781
|
-
|
|
17782
|
-
|
|
17783
|
-
|
|
17784
|
-
|
|
17785
|
-
|
|
17786
|
-
|
|
17787
|
-
{
|
|
17788
|
-
|
|
17789
|
-
|
|
17790
|
-
|
|
17791
|
-
|
|
17792
|
-
|
|
17793
|
-
|
|
17794
|
-
|
|
17795
|
-
|
|
17796
|
-
|
|
17797
|
-
|
|
17798
|
-
|
|
17799
|
-
|
|
17800
|
-
|
|
17801
|
-
|
|
17802
|
-
|
|
17803
|
-
|
|
17804
|
-
|
|
17805
|
-
|
|
17806
|
-
|
|
17807
|
-
|
|
17808
|
-
|
|
17809
|
-
|
|
17810
|
-
|
|
17811
|
-
|
|
17812
|
-
|
|
17813
|
-
|
|
17814
|
-
|
|
17815
|
-
|
|
17816
|
-
|
|
17817
|
-
|
|
17818
|
-
|
|
17819
|
-
{
|
|
17820
|
-
|
|
17821
|
-
|
|
17822
|
-
majorInterval: 300,
|
|
17823
|
-
// 5m major ticks
|
|
17824
|
-
minorTicks: 5
|
|
17825
|
-
// 1m minor ticks (5 minors between majors)
|
|
17826
|
-
},
|
|
17827
|
-
{
|
|
17828
|
-
durationThreshold: 7200,
|
|
17829
|
-
// < 2 hours
|
|
17830
|
-
majorInterval: 600,
|
|
17831
|
-
// 10m major ticks
|
|
17832
|
-
minorTicks: 10
|
|
17833
|
-
// 1m minor ticks (10 minors between majors)
|
|
17834
|
-
},
|
|
17835
|
-
{
|
|
17836
|
-
durationThreshold: Infinity,
|
|
17837
|
-
// >= 2 hours
|
|
17838
|
-
majorInterval: 1800,
|
|
17839
|
-
// 30m major ticks
|
|
17840
|
-
minorTicks: 6
|
|
17841
|
-
// 5m minor ticks (6 minors between majors)
|
|
17842
|
-
}
|
|
17843
|
-
];
|
|
17844
|
-
const DEFAULT_ELEMENT_COLORS = {
|
|
17845
|
-
/** Fragment element color - deep charcoal matching UI background */
|
|
17846
|
-
fragment: "#1A1A1A",
|
|
17847
|
-
/** Video element color - vibrant royal purple */
|
|
17848
|
-
video: "#8B5FBF",
|
|
17849
|
-
/** Caption element color - soft wisteria purple */
|
|
17850
|
-
caption: "#9B8ACE",
|
|
17851
|
-
/** Image element color - warm copper accent */
|
|
17852
|
-
image: "#D4956C",
|
|
17853
|
-
/** Audio element color - deep teal */
|
|
17854
|
-
audio: "#3D8B8B",
|
|
17855
|
-
/** Text element color - medium lavender */
|
|
17856
|
-
text: "#8D74C4",
|
|
17857
|
-
/** Generic element color - muted amethyst */
|
|
17858
|
-
element: "#7B68B8",
|
|
17859
|
-
/** Rectangle element color - deep indigo */
|
|
17860
|
-
rect: "#5B4B99",
|
|
17861
|
-
/** Frame effect color - rich magenta */
|
|
17862
|
-
frameEffect: "#B55B9C",
|
|
17863
|
-
/** Filters color - periwinkle blue */
|
|
17864
|
-
filters: "#7A89D4",
|
|
17865
|
-
/** Transition color - burnished bronze */
|
|
17866
|
-
transition: "#BE8157",
|
|
17867
|
-
/** Animation color - muted emerald */
|
|
17868
|
-
animation: "#4B9B78",
|
|
17869
|
-
/** Icon element color - bright orchid */
|
|
17870
|
-
icon: "#A76CD4",
|
|
17871
|
-
/** Circle element color - deep byzantium */
|
|
17872
|
-
circle: "#703D8B"
|
|
17873
|
-
};
|
|
17874
|
-
const AVAILABLE_TEXT_FONTS = {
|
|
17875
|
-
// Google Fonts
|
|
17876
|
-
/** Modern sans-serif font */
|
|
17877
|
-
RUBIK: "Rubik",
|
|
17878
|
-
/** Clean and readable font */
|
|
17879
|
-
MULISH: "Mulish",
|
|
17880
|
-
/** Bold display font */
|
|
17881
|
-
LUCKIEST_GUY: "Luckiest Guy",
|
|
17882
|
-
/** Elegant serif font */
|
|
17883
|
-
PLAYFAIR_DISPLAY: "Playfair Display",
|
|
17884
|
-
/** Classic sans-serif font */
|
|
17885
|
-
ROBOTO: "Roboto",
|
|
17886
|
-
/** Modern geometric font */
|
|
17887
|
-
POPPINS: "Poppins",
|
|
17888
|
-
// Display and Decorative Fonts
|
|
17889
|
-
/** Comic-style display font */
|
|
17890
|
-
BANGERS: "Bangers",
|
|
17891
|
-
/** Handwritten-style font */
|
|
17892
|
-
BIRTHSTONE: "Birthstone",
|
|
17893
|
-
/** Elegant script font */
|
|
17894
|
-
CORINTHIA: "Corinthia",
|
|
17895
|
-
/** Formal script font */
|
|
17896
|
-
IMPERIAL_SCRIPT: "Imperial Script",
|
|
17897
|
-
/** Bold outline font */
|
|
17898
|
-
KUMAR_ONE_OUTLINE: "Kumar One Outline",
|
|
17899
|
-
/** Light outline font */
|
|
17900
|
-
LONDRI_OUTLINE: "Londrina Outline",
|
|
17901
|
-
/** Casual script font */
|
|
17902
|
-
MARCK_SCRIPT: "Marck Script",
|
|
17903
|
-
/** Modern sans-serif font */
|
|
17904
|
-
MONTSERRAT: "Montserrat",
|
|
17905
|
-
/** Stylish display font */
|
|
17906
|
-
PATTAYA: "Pattaya",
|
|
17907
|
-
// CDN Fonts
|
|
17908
|
-
/** Unique display font */
|
|
17909
|
-
PERALTA: "Peralta",
|
|
17910
|
-
/** Bold impact font */
|
|
17911
|
-
IMPACT: "Impact",
|
|
17912
|
-
/** Handwritten-style font */
|
|
17913
|
-
LUMANOSIMO: "Lumanosimo",
|
|
17914
|
-
/** Custom display font */
|
|
17915
|
-
KAPAKANA: "Kapakana",
|
|
17916
|
-
/** Handwritten font */
|
|
17917
|
-
HANDYRUSH: "HandyRush",
|
|
17918
|
-
/** Decorative font */
|
|
17919
|
-
DASHER: "Dasher",
|
|
17920
|
-
/** Signature-style font */
|
|
17921
|
-
BRITTANY_SIGNATURE: "Brittany Signature"
|
|
18636
|
+
const { children } = this.props;
|
|
18637
|
+
if (isMotionValue(children)) {
|
|
18638
|
+
this.childSubscription = children.on("change", (latest) => {
|
|
18639
|
+
if (this.current) {
|
|
18640
|
+
this.current.textContent = `${latest}`;
|
|
18641
|
+
}
|
|
18642
|
+
});
|
|
18643
|
+
}
|
|
18644
|
+
}
|
|
18645
|
+
}
|
|
18646
|
+
function getComputedStyle(element) {
|
|
18647
|
+
return window.getComputedStyle(element);
|
|
18648
|
+
}
|
|
18649
|
+
class HTMLVisualElement extends DOMVisualElement {
|
|
18650
|
+
constructor() {
|
|
18651
|
+
super(...arguments);
|
|
18652
|
+
this.type = "html";
|
|
18653
|
+
this.renderInstance = renderHTML;
|
|
18654
|
+
}
|
|
18655
|
+
readValueFromInstance(instance, key) {
|
|
18656
|
+
if (transformProps.has(key)) {
|
|
18657
|
+
const defaultType = getDefaultValueType(key);
|
|
18658
|
+
return defaultType ? defaultType.default || 0 : 0;
|
|
18659
|
+
} else {
|
|
18660
|
+
const computedStyle = getComputedStyle(instance);
|
|
18661
|
+
const value = (isCSSVariableName(key) ? computedStyle.getPropertyValue(key) : computedStyle[key]) || 0;
|
|
18662
|
+
return typeof value === "string" ? value.trim() : value;
|
|
18663
|
+
}
|
|
18664
|
+
}
|
|
18665
|
+
measureInstanceViewportBox(instance, { transformPagePoint }) {
|
|
18666
|
+
return measureViewportBox(instance, transformPagePoint);
|
|
18667
|
+
}
|
|
18668
|
+
build(renderState, latestValues, props) {
|
|
18669
|
+
buildHTMLStyles(renderState, latestValues, props.transformTemplate);
|
|
18670
|
+
}
|
|
18671
|
+
scrapeMotionValuesFromProps(props, prevProps, visualElement) {
|
|
18672
|
+
return scrapeMotionValuesFromProps$1(props, prevProps, visualElement);
|
|
18673
|
+
}
|
|
18674
|
+
}
|
|
18675
|
+
class SVGVisualElement extends DOMVisualElement {
|
|
18676
|
+
constructor() {
|
|
18677
|
+
super(...arguments);
|
|
18678
|
+
this.type = "svg";
|
|
18679
|
+
this.isSVGTag = false;
|
|
18680
|
+
this.measureInstanceViewportBox = createBox;
|
|
18681
|
+
}
|
|
18682
|
+
getBaseTargetFromProps(props, key) {
|
|
18683
|
+
return props[key];
|
|
18684
|
+
}
|
|
18685
|
+
readValueFromInstance(instance, key) {
|
|
18686
|
+
if (transformProps.has(key)) {
|
|
18687
|
+
const defaultType = getDefaultValueType(key);
|
|
18688
|
+
return defaultType ? defaultType.default || 0 : 0;
|
|
18689
|
+
}
|
|
18690
|
+
key = !camelCaseAttributes.has(key) ? camelToDash(key) : key;
|
|
18691
|
+
return instance.getAttribute(key);
|
|
18692
|
+
}
|
|
18693
|
+
scrapeMotionValuesFromProps(props, prevProps, visualElement) {
|
|
18694
|
+
return scrapeMotionValuesFromProps(props, prevProps, visualElement);
|
|
18695
|
+
}
|
|
18696
|
+
build(renderState, latestValues, props) {
|
|
18697
|
+
buildSVGAttrs(renderState, latestValues, this.isSVGTag, props.transformTemplate);
|
|
18698
|
+
}
|
|
18699
|
+
renderInstance(instance, renderState, styleProp, projection) {
|
|
18700
|
+
renderSVG(instance, renderState, styleProp, projection);
|
|
18701
|
+
}
|
|
18702
|
+
mount(instance) {
|
|
18703
|
+
this.isSVGTag = isSVGTag(instance.tagName);
|
|
18704
|
+
super.mount(instance);
|
|
18705
|
+
}
|
|
18706
|
+
}
|
|
18707
|
+
const createDomVisualElement = (Component2, options) => {
|
|
18708
|
+
return isSVGComponent(Component2) ? new SVGVisualElement(options) : new HTMLVisualElement(options, {
|
|
18709
|
+
allowProjection: Component2 !== Fragment
|
|
18710
|
+
});
|
|
17922
18711
|
};
|
|
18712
|
+
const createMotionComponent = /* @__PURE__ */ createMotionComponentFactory({
|
|
18713
|
+
...animations,
|
|
18714
|
+
...gestureAnimations,
|
|
18715
|
+
...drag,
|
|
18716
|
+
...layout
|
|
18717
|
+
}, createDomVisualElement);
|
|
18718
|
+
const motion = /* @__PURE__ */ createDOMMotionComponentProxy(createMotionComponent);
|
|
17923
18719
|
let ELEMENT_COLORS = { ...DEFAULT_ELEMENT_COLORS };
|
|
17924
18720
|
const setElementColors = (colors) => {
|
|
17925
18721
|
ELEMENT_COLORS = {
|
|
@@ -17934,6 +18730,7 @@ const TrackElementView = ({
|
|
|
17934
18730
|
nextStart,
|
|
17935
18731
|
prevEnd,
|
|
17936
18732
|
selectedItem,
|
|
18733
|
+
selectedIds,
|
|
17937
18734
|
onSelection,
|
|
17938
18735
|
onDrag,
|
|
17939
18736
|
allowOverlap = false,
|
|
@@ -17960,8 +18757,8 @@ const TrackElementView = ({
|
|
|
17960
18757
|
dragType.current = DRAG_TYPE.MOVE;
|
|
17961
18758
|
setPosition((prev) => {
|
|
17962
18759
|
const span = prev.end - prev.start;
|
|
17963
|
-
let newStart =
|
|
17964
|
-
newStart = Math.min(newStart, prev.end - MIN_DURATION);
|
|
18760
|
+
let newStart = prev.start + dx / parentWidth * duration;
|
|
18761
|
+
newStart = Math.max(0, Math.min(newStart, prev.end - MIN_DURATION));
|
|
17965
18762
|
if (!allowOverlap) {
|
|
17966
18763
|
if (prevEnd !== null && newStart < prevEnd) {
|
|
17967
18764
|
newStart = prevEnd;
|
|
@@ -17985,8 +18782,8 @@ const TrackElementView = ({
|
|
|
17985
18782
|
}
|
|
17986
18783
|
dragType.current = DRAG_TYPE.START;
|
|
17987
18784
|
setPosition((prev) => {
|
|
17988
|
-
let newStart =
|
|
17989
|
-
newStart = Math.min(newStart, prev.end - MIN_DURATION);
|
|
18785
|
+
let newStart = prev.start + dx / parentWidth * duration;
|
|
18786
|
+
newStart = Math.max(0, Math.min(newStart, prev.end - MIN_DURATION));
|
|
17990
18787
|
if (prevEnd !== null && !allowOverlap && newStart < prevEnd) {
|
|
17991
18788
|
newStart = prevEnd;
|
|
17992
18789
|
}
|
|
@@ -18044,8 +18841,9 @@ const TrackElementView = ({
|
|
|
18044
18841
|
return ELEMENT_COLORS.element;
|
|
18045
18842
|
};
|
|
18046
18843
|
const isSelected = useMemo(() => {
|
|
18047
|
-
return
|
|
18048
|
-
}, [
|
|
18844
|
+
return selectedIds.has(element.getId());
|
|
18845
|
+
}, [selectedIds, element]);
|
|
18846
|
+
const hasHandles = (selectedItem == null ? void 0 : selectedItem.getId()) === element.getId();
|
|
18049
18847
|
const motionProps = {
|
|
18050
18848
|
ref,
|
|
18051
18849
|
className: `twick-track-element ${isSelected ? "twick-track-element-selected" : "twick-track-element-default"} ${isDragging2 ? "twick-track-element-dragging" : ""}`,
|
|
@@ -18061,9 +18859,9 @@ const TrackElementView = ({
|
|
|
18061
18859
|
},
|
|
18062
18860
|
onMouseUp: sendUpdate,
|
|
18063
18861
|
onTouchEnd: sendUpdate,
|
|
18064
|
-
onClick: () => {
|
|
18862
|
+
onClick: (e3) => {
|
|
18065
18863
|
if (onSelection) {
|
|
18066
|
-
onSelection(element);
|
|
18864
|
+
onSelection(element, e3);
|
|
18067
18865
|
}
|
|
18068
18866
|
},
|
|
18069
18867
|
style: {
|
|
@@ -18074,7 +18872,7 @@ const TrackElementView = ({
|
|
|
18074
18872
|
}
|
|
18075
18873
|
};
|
|
18076
18874
|
return /* @__PURE__ */ jsx(motion.div, { ...motionProps, children: /* @__PURE__ */ jsxs("div", { style: { touchAction: "none", height: "100%" }, ...bind(), children: [
|
|
18077
|
-
|
|
18875
|
+
hasHandles ? /* @__PURE__ */ jsx(
|
|
18078
18876
|
"div",
|
|
18079
18877
|
{
|
|
18080
18878
|
style: { touchAction: "none", zIndex: isSelected ? 100 : 1 },
|
|
@@ -18083,7 +18881,7 @@ const TrackElementView = ({
|
|
|
18083
18881
|
}
|
|
18084
18882
|
) : null,
|
|
18085
18883
|
/* @__PURE__ */ jsx("div", { className: "twick-track-element-content", children: element.getText ? element.getText() : element.getName() || element.getType() }),
|
|
18086
|
-
|
|
18884
|
+
hasHandles ? /* @__PURE__ */ jsx(
|
|
18087
18885
|
"div",
|
|
18088
18886
|
{
|
|
18089
18887
|
style: { touchAction: "none", zIndex: isSelected ? 100 : 1 },
|
|
@@ -18113,6 +18911,7 @@ const TrackBase = ({
|
|
|
18113
18911
|
track,
|
|
18114
18912
|
trackWidth,
|
|
18115
18913
|
selectedItem,
|
|
18914
|
+
selectedIds,
|
|
18116
18915
|
onItemSelection,
|
|
18117
18916
|
onDrag,
|
|
18118
18917
|
allowOverlap = false,
|
|
@@ -18137,6 +18936,7 @@ const TrackBase = ({
|
|
|
18137
18936
|
allowOverlap,
|
|
18138
18937
|
parentWidth: trackWidth,
|
|
18139
18938
|
selectedItem,
|
|
18939
|
+
selectedIds,
|
|
18140
18940
|
onSelection: onItemSelection,
|
|
18141
18941
|
onDrag,
|
|
18142
18942
|
elementColors,
|
|
@@ -18148,6 +18948,199 @@ const TrackBase = ({
|
|
|
18148
18948
|
}
|
|
18149
18949
|
);
|
|
18150
18950
|
};
|
|
18951
|
+
const DEFAULT_MARGIN = 80;
|
|
18952
|
+
function usePlayheadScroll(scrollContainerRef, playheadPositionPx, isActive, config) {
|
|
18953
|
+
const margin = (config == null ? void 0 : config.margin) ?? DEFAULT_MARGIN;
|
|
18954
|
+
const labelWidth = config == null ? void 0 : config.labelWidth;
|
|
18955
|
+
const rafRef = useRef(null);
|
|
18956
|
+
useEffect(() => {
|
|
18957
|
+
if (!isActive || !scrollContainerRef.current) return;
|
|
18958
|
+
const container = scrollContainerRef.current;
|
|
18959
|
+
const contentX = labelWidth + playheadPositionPx;
|
|
18960
|
+
const scrollToKeepPlayheadVisible = () => {
|
|
18961
|
+
const { scrollLeft, clientWidth } = container;
|
|
18962
|
+
const minVisible = scrollLeft + margin;
|
|
18963
|
+
const maxVisible = scrollLeft + clientWidth - margin;
|
|
18964
|
+
let newScrollLeft = null;
|
|
18965
|
+
if (contentX < minVisible) {
|
|
18966
|
+
newScrollLeft = Math.max(0, contentX - margin);
|
|
18967
|
+
} else if (contentX > maxVisible) {
|
|
18968
|
+
newScrollLeft = contentX - clientWidth + margin;
|
|
18969
|
+
}
|
|
18970
|
+
if (newScrollLeft !== null) {
|
|
18971
|
+
container.scrollLeft = newScrollLeft;
|
|
18972
|
+
}
|
|
18973
|
+
};
|
|
18974
|
+
const scheduleScroll = () => {
|
|
18975
|
+
if (rafRef.current !== null) cancelAnimationFrame(rafRef.current);
|
|
18976
|
+
rafRef.current = requestAnimationFrame(() => {
|
|
18977
|
+
rafRef.current = null;
|
|
18978
|
+
scrollToKeepPlayheadVisible();
|
|
18979
|
+
});
|
|
18980
|
+
};
|
|
18981
|
+
scheduleScroll();
|
|
18982
|
+
return () => {
|
|
18983
|
+
if (rafRef.current !== null) {
|
|
18984
|
+
cancelAnimationFrame(rafRef.current);
|
|
18985
|
+
}
|
|
18986
|
+
};
|
|
18987
|
+
}, [
|
|
18988
|
+
isActive,
|
|
18989
|
+
playheadPositionPx,
|
|
18990
|
+
scrollContainerRef,
|
|
18991
|
+
margin,
|
|
18992
|
+
labelWidth
|
|
18993
|
+
]);
|
|
18994
|
+
}
|
|
18995
|
+
const MARQUEE_THRESHOLD = 4;
|
|
18996
|
+
function useMarqueeSelection({
|
|
18997
|
+
duration,
|
|
18998
|
+
zoomLevel,
|
|
18999
|
+
labelWidth,
|
|
19000
|
+
trackCount,
|
|
19001
|
+
trackHeight,
|
|
19002
|
+
tracks,
|
|
19003
|
+
containerRef,
|
|
19004
|
+
onMarqueeSelect,
|
|
19005
|
+
onEmptyClick
|
|
19006
|
+
}) {
|
|
19007
|
+
const [marquee, setMarquee] = useState(null);
|
|
19008
|
+
const startPosRef = useRef(null);
|
|
19009
|
+
const hasDraggedRef = useRef(false);
|
|
19010
|
+
const marqueeRef = useRef(marquee);
|
|
19011
|
+
marqueeRef.current = marquee;
|
|
19012
|
+
const pixelsPerSecond = 100 * zoomLevel;
|
|
19013
|
+
const getCoords = useCallback(
|
|
19014
|
+
(e3) => {
|
|
19015
|
+
var _a, _b;
|
|
19016
|
+
const rect = (_a = containerRef.current) == null ? void 0 : _a.getBoundingClientRect();
|
|
19017
|
+
if (!rect) return { x: 0, y: 0 };
|
|
19018
|
+
return {
|
|
19019
|
+
x: e3.clientX - rect.left + (((_b = containerRef.current) == null ? void 0 : _b.scrollLeft) ?? 0),
|
|
19020
|
+
y: e3.clientY - rect.top
|
|
19021
|
+
};
|
|
19022
|
+
},
|
|
19023
|
+
[containerRef]
|
|
19024
|
+
);
|
|
19025
|
+
const handleGlobalMouseMove = useCallback(
|
|
19026
|
+
(e3) => {
|
|
19027
|
+
if (!startPosRef.current) return;
|
|
19028
|
+
const { x: x2, y: y2 } = getCoords(e3);
|
|
19029
|
+
const dx = Math.abs(x2 - startPosRef.current.x);
|
|
19030
|
+
const dy = Math.abs(y2 - startPosRef.current.y);
|
|
19031
|
+
if (dx > MARQUEE_THRESHOLD || dy > MARQUEE_THRESHOLD) {
|
|
19032
|
+
hasDraggedRef.current = true;
|
|
19033
|
+
}
|
|
19034
|
+
setMarquee((prev) => prev ? { ...prev, endX: x2, endY: y2 } : null);
|
|
19035
|
+
},
|
|
19036
|
+
[getCoords]
|
|
19037
|
+
);
|
|
19038
|
+
const handleGlobalMouseUp = useCallback(() => {
|
|
19039
|
+
if (!startPosRef.current) return;
|
|
19040
|
+
const currentMarquee = marqueeRef.current;
|
|
19041
|
+
if (!hasDraggedRef.current || !currentMarquee) {
|
|
19042
|
+
setMarquee(null);
|
|
19043
|
+
startPosRef.current = null;
|
|
19044
|
+
onEmptyClick();
|
|
19045
|
+
window.removeEventListener("mousemove", handleGlobalMouseMove);
|
|
19046
|
+
window.removeEventListener("mouseup", handleGlobalMouseUp);
|
|
19047
|
+
return;
|
|
19048
|
+
}
|
|
19049
|
+
const left = Math.min(currentMarquee.startX, currentMarquee.endX);
|
|
19050
|
+
const right = Math.max(currentMarquee.startX, currentMarquee.endX);
|
|
19051
|
+
const top = Math.min(currentMarquee.startY, currentMarquee.endY);
|
|
19052
|
+
const bottom = Math.max(currentMarquee.startY, currentMarquee.endY);
|
|
19053
|
+
const startTime = Math.max(0, (left - labelWidth) / pixelsPerSecond);
|
|
19054
|
+
const endTime = Math.min(duration, (right - labelWidth) / pixelsPerSecond);
|
|
19055
|
+
const rowHeight = trackHeight + 2;
|
|
19056
|
+
const startTrackIdx = Math.max(0, Math.floor(top / rowHeight));
|
|
19057
|
+
const endTrackIdx = Math.min(
|
|
19058
|
+
trackCount - 1,
|
|
19059
|
+
Math.floor(bottom / rowHeight)
|
|
19060
|
+
);
|
|
19061
|
+
const selectedIds = /* @__PURE__ */ new Set();
|
|
19062
|
+
for (let tIdx = startTrackIdx; tIdx <= endTrackIdx; tIdx++) {
|
|
19063
|
+
const track = tracks[tIdx];
|
|
19064
|
+
if (!track) continue;
|
|
19065
|
+
for (const el of track.getElements()) {
|
|
19066
|
+
const elStart = el.getStart();
|
|
19067
|
+
const elEnd = el.getEnd();
|
|
19068
|
+
if (elStart < endTime && elEnd > startTime) {
|
|
19069
|
+
selectedIds.add(el.getId());
|
|
19070
|
+
}
|
|
19071
|
+
}
|
|
19072
|
+
}
|
|
19073
|
+
onMarqueeSelect(selectedIds);
|
|
19074
|
+
setMarquee(null);
|
|
19075
|
+
startPosRef.current = null;
|
|
19076
|
+
window.removeEventListener("mousemove", handleGlobalMouseMove);
|
|
19077
|
+
window.removeEventListener("mouseup", handleGlobalMouseUp);
|
|
19078
|
+
}, [
|
|
19079
|
+
duration,
|
|
19080
|
+
pixelsPerSecond,
|
|
19081
|
+
labelWidth,
|
|
19082
|
+
trackCount,
|
|
19083
|
+
trackHeight,
|
|
19084
|
+
tracks,
|
|
19085
|
+
onMarqueeSelect,
|
|
19086
|
+
onEmptyClick,
|
|
19087
|
+
handleGlobalMouseMove
|
|
19088
|
+
]);
|
|
19089
|
+
useEffect(() => {
|
|
19090
|
+
if (!marquee) return;
|
|
19091
|
+
window.addEventListener("mousemove", handleGlobalMouseMove);
|
|
19092
|
+
window.addEventListener("mouseup", handleGlobalMouseUp);
|
|
19093
|
+
return () => {
|
|
19094
|
+
window.removeEventListener("mousemove", handleGlobalMouseMove);
|
|
19095
|
+
window.removeEventListener("mouseup", handleGlobalMouseUp);
|
|
19096
|
+
};
|
|
19097
|
+
}, [marquee, handleGlobalMouseMove, handleGlobalMouseUp]);
|
|
19098
|
+
const handleMouseDown = useCallback(
|
|
19099
|
+
(e3) => {
|
|
19100
|
+
if (e3.target.closest(".twick-track-element") || e3.target.closest(".twick-track-header")) {
|
|
19101
|
+
return;
|
|
19102
|
+
}
|
|
19103
|
+
const { x: x2, y: y2 } = getCoords(e3.nativeEvent);
|
|
19104
|
+
startPosRef.current = { x: x2, y: y2 };
|
|
19105
|
+
hasDraggedRef.current = false;
|
|
19106
|
+
setMarquee({ startX: x2, startY: y2, endX: x2, endY: y2 });
|
|
19107
|
+
},
|
|
19108
|
+
[getCoords]
|
|
19109
|
+
);
|
|
19110
|
+
return { marquee, handleMouseDown };
|
|
19111
|
+
}
|
|
19112
|
+
function MarqueeOverlay({ marquee }) {
|
|
19113
|
+
return /* @__PURE__ */ jsx(
|
|
19114
|
+
"div",
|
|
19115
|
+
{
|
|
19116
|
+
className: "twick-marquee-overlay",
|
|
19117
|
+
style: {
|
|
19118
|
+
position: "absolute",
|
|
19119
|
+
inset: 0,
|
|
19120
|
+
zIndex: 25,
|
|
19121
|
+
pointerEvents: "none"
|
|
19122
|
+
},
|
|
19123
|
+
children: marquee && /* @__PURE__ */ jsx(
|
|
19124
|
+
"div",
|
|
19125
|
+
{
|
|
19126
|
+
className: "twick-marquee-rect",
|
|
19127
|
+
style: {
|
|
19128
|
+
position: "absolute",
|
|
19129
|
+
left: Math.min(marquee.startX, marquee.endX),
|
|
19130
|
+
top: Math.min(marquee.startY, marquee.endY),
|
|
19131
|
+
width: Math.abs(marquee.endX - marquee.startX),
|
|
19132
|
+
height: Math.abs(marquee.endY - marquee.startY),
|
|
19133
|
+
border: "1px solid rgba(255, 255, 255, 0.7)",
|
|
19134
|
+
backgroundColor: "rgba(255, 255, 255, 0.1)",
|
|
19135
|
+
pointerEvents: "none"
|
|
19136
|
+
}
|
|
19137
|
+
}
|
|
19138
|
+
)
|
|
19139
|
+
}
|
|
19140
|
+
);
|
|
19141
|
+
}
|
|
19142
|
+
const LABEL_WIDTH = 40;
|
|
19143
|
+
const TRACK_HEIGHT = 44;
|
|
18151
19144
|
function TimelineView({
|
|
18152
19145
|
zoomLevel,
|
|
18153
19146
|
selectedItem,
|
|
@@ -18156,20 +19149,28 @@ function TimelineView({
|
|
|
18156
19149
|
seekTrack,
|
|
18157
19150
|
onAddTrack,
|
|
18158
19151
|
onReorder,
|
|
18159
|
-
|
|
19152
|
+
onItemSelect,
|
|
19153
|
+
onEmptyClick,
|
|
19154
|
+
onMarqueeSelect,
|
|
18160
19155
|
onElementDrag,
|
|
18161
|
-
elementColors
|
|
19156
|
+
elementColors,
|
|
19157
|
+
selectedIds,
|
|
19158
|
+
playheadPositionPx = 0,
|
|
19159
|
+
isPlayheadActive = false,
|
|
19160
|
+
onDropOnTimeline,
|
|
19161
|
+
videoResolution,
|
|
19162
|
+
enableDropOnTimeline = true
|
|
18162
19163
|
}) {
|
|
18163
19164
|
const containerRef = useRef(null);
|
|
18164
19165
|
const seekContainerRef = useRef(null);
|
|
18165
19166
|
const timelineContentRef = useRef(null);
|
|
18166
19167
|
const [, setScrollLeft] = useState(0);
|
|
18167
19168
|
const [draggedTimeline, setDraggedTimeline] = useState(null);
|
|
18168
|
-
const {
|
|
19169
|
+
const { selectedTrackElement } = useMemo(() => {
|
|
18169
19170
|
if (selectedItem && "elements" in selectedItem) {
|
|
18170
|
-
return {
|
|
19171
|
+
return { selectedTrackElement: null };
|
|
18171
19172
|
}
|
|
18172
|
-
return {
|
|
19173
|
+
return { selectedTrackElement: selectedItem };
|
|
18173
19174
|
}, [selectedItem]);
|
|
18174
19175
|
const timelineWidth = Math.max(100, duration * zoomLevel * 100);
|
|
18175
19176
|
const timelineWidthPx = `${timelineWidth}px`;
|
|
@@ -18199,7 +19200,33 @@ function TimelineView({
|
|
|
18199
19200
|
window.removeEventListener("resize", updateWidth);
|
|
18200
19201
|
};
|
|
18201
19202
|
}, [duration, zoomLevel]);
|
|
18202
|
-
|
|
19203
|
+
usePlayheadScroll(containerRef, playheadPositionPx, isPlayheadActive, {
|
|
19204
|
+
labelWidth: LABEL_WIDTH
|
|
19205
|
+
});
|
|
19206
|
+
const { marquee, handleMouseDown: handleMarqueeMouseDown } = useMarqueeSelection({
|
|
19207
|
+
duration,
|
|
19208
|
+
zoomLevel,
|
|
19209
|
+
labelWidth: LABEL_WIDTH,
|
|
19210
|
+
trackCount: (tracks == null ? void 0 : tracks.length) ?? 0,
|
|
19211
|
+
trackHeight: TRACK_HEIGHT,
|
|
19212
|
+
tracks: tracks ?? [],
|
|
19213
|
+
containerRef: timelineContentRef,
|
|
19214
|
+
onMarqueeSelect,
|
|
19215
|
+
onEmptyClick
|
|
19216
|
+
});
|
|
19217
|
+
const { preview, handleDragOver, handleDragLeave, handleDrop } = useTimelineDrop({
|
|
19218
|
+
containerRef: timelineContentRef,
|
|
19219
|
+
scrollContainerRef: containerRef,
|
|
19220
|
+
tracks: tracks ?? [],
|
|
19221
|
+
duration,
|
|
19222
|
+
zoomLevel,
|
|
19223
|
+
labelWidth: LABEL_WIDTH,
|
|
19224
|
+
trackHeight: TRACK_HEIGHT,
|
|
19225
|
+
trackContentWidth: timelineWidth - LABEL_WIDTH,
|
|
19226
|
+
onDrop: onDropOnTimeline ?? (async () => {
|
|
19227
|
+
}),
|
|
19228
|
+
enabled: enableDropOnTimeline && !!onDropOnTimeline && !!videoResolution
|
|
19229
|
+
});
|
|
18203
19230
|
const handleTrackDragStart = (e3, track) => {
|
|
18204
19231
|
setDraggedTimeline(track);
|
|
18205
19232
|
e3.dataTransfer.setData("application/json", JSON.stringify(track));
|
|
@@ -18231,10 +19258,8 @@ function TimelineView({
|
|
|
18231
19258
|
}
|
|
18232
19259
|
setDraggedTimeline(null);
|
|
18233
19260
|
};
|
|
18234
|
-
const handleItemSelection = (
|
|
18235
|
-
|
|
18236
|
-
onSelectionChange(element);
|
|
18237
|
-
}
|
|
19261
|
+
const handleItemSelection = (item, event) => {
|
|
19262
|
+
onItemSelect(item, event);
|
|
18238
19263
|
};
|
|
18239
19264
|
return /* @__PURE__ */ jsxs(
|
|
18240
19265
|
"div",
|
|
@@ -18247,44 +19272,106 @@ function TimelineView({
|
|
|
18247
19272
|
/* @__PURE__ */ jsx("div", { className: "twick-seek-track-empty-space", onClick: onAddTrack, children: /* @__PURE__ */ jsx(Plus, { color: "white", size: 20 }) }),
|
|
18248
19273
|
/* @__PURE__ */ jsx("div", { style: { flexGrow: 1 }, children: seekTrack })
|
|
18249
19274
|
] }) : null }),
|
|
18250
|
-
/* @__PURE__ */
|
|
18251
|
-
|
|
18252
|
-
|
|
18253
|
-
|
|
18254
|
-
|
|
18255
|
-
|
|
18256
|
-
|
|
18257
|
-
|
|
18258
|
-
|
|
18259
|
-
|
|
18260
|
-
|
|
18261
|
-
|
|
18262
|
-
|
|
18263
|
-
|
|
18264
|
-
|
|
18265
|
-
|
|
18266
|
-
|
|
18267
|
-
|
|
18268
|
-
|
|
18269
|
-
|
|
18270
|
-
|
|
18271
|
-
|
|
18272
|
-
|
|
18273
|
-
|
|
18274
|
-
|
|
18275
|
-
|
|
18276
|
-
|
|
19275
|
+
/* @__PURE__ */ jsxs(
|
|
19276
|
+
"div",
|
|
19277
|
+
{
|
|
19278
|
+
ref: timelineContentRef,
|
|
19279
|
+
style: { width: timelineWidthPx, position: "relative" },
|
|
19280
|
+
onMouseDown: handleMarqueeMouseDown,
|
|
19281
|
+
onDragOver: handleDragOver,
|
|
19282
|
+
onDragLeave: handleDragLeave,
|
|
19283
|
+
onDrop: handleDrop,
|
|
19284
|
+
children: [
|
|
19285
|
+
/* @__PURE__ */ jsx(MarqueeOverlay, { marquee }),
|
|
19286
|
+
preview && /* @__PURE__ */ jsx(
|
|
19287
|
+
"div",
|
|
19288
|
+
{
|
|
19289
|
+
className: "twick-drop-preview",
|
|
19290
|
+
style: {
|
|
19291
|
+
position: "absolute",
|
|
19292
|
+
left: LABEL_WIDTH + preview.timeSec / duration * (timelineWidth - LABEL_WIDTH),
|
|
19293
|
+
top: preview.trackIndex * TRACK_HEIGHT + 2,
|
|
19294
|
+
width: preview.widthPct / 100 * (timelineWidth - LABEL_WIDTH),
|
|
19295
|
+
height: TRACK_HEIGHT - 4
|
|
19296
|
+
}
|
|
19297
|
+
}
|
|
19298
|
+
),
|
|
19299
|
+
/* @__PURE__ */ jsx("div", { style: { position: "relative", zIndex: 10 }, children: (tracks || []).map((track) => /* @__PURE__ */ jsxs("div", { className: "twick-timeline-container", children: [
|
|
19300
|
+
/* @__PURE__ */ jsx("div", { className: "twick-timeline-header-container", children: /* @__PURE__ */ jsx(
|
|
19301
|
+
TrackHeader,
|
|
19302
|
+
{
|
|
19303
|
+
track,
|
|
19304
|
+
selectedIds,
|
|
19305
|
+
onSelect: handleItemSelection,
|
|
19306
|
+
onDragStart: handleTrackDragStart,
|
|
19307
|
+
onDragOver: handleTrackDragOver,
|
|
19308
|
+
onDrop: handleTrackDrop
|
|
19309
|
+
}
|
|
19310
|
+
) }),
|
|
19311
|
+
/* @__PURE__ */ jsx(
|
|
19312
|
+
TrackBase,
|
|
19313
|
+
{
|
|
19314
|
+
track,
|
|
19315
|
+
duration,
|
|
19316
|
+
selectedItem: selectedTrackElement,
|
|
19317
|
+
selectedIds,
|
|
19318
|
+
zoom: zoomLevel,
|
|
19319
|
+
allowOverlap: false,
|
|
19320
|
+
trackWidth: timelineWidth - LABEL_WIDTH,
|
|
19321
|
+
onItemSelection: handleItemSelection,
|
|
19322
|
+
onDrag: onElementDrag,
|
|
19323
|
+
elementColors
|
|
19324
|
+
}
|
|
19325
|
+
)
|
|
19326
|
+
] }, track.getId())) })
|
|
19327
|
+
]
|
|
19328
|
+
}
|
|
19329
|
+
)
|
|
18277
19330
|
]
|
|
18278
19331
|
}
|
|
18279
19332
|
);
|
|
18280
19333
|
}
|
|
18281
19334
|
const useTimelineManager = () => {
|
|
18282
|
-
const { selectedItem, changeLog, setSelectedItem, totalDuration, editor } = useTimelineContext();
|
|
19335
|
+
const { selectedItem, changeLog, setSelectedItem, totalDuration, editor, selectedIds } = useTimelineContext();
|
|
18283
19336
|
const onElementDrag = ({
|
|
18284
19337
|
element,
|
|
18285
19338
|
dragType,
|
|
18286
19339
|
updates
|
|
18287
19340
|
}) => {
|
|
19341
|
+
var _a;
|
|
19342
|
+
const tracks = ((_a = editor.getTimelineData()) == null ? void 0 : _a.tracks) ?? [];
|
|
19343
|
+
const duration = totalDuration;
|
|
19344
|
+
if (dragType === DRAG_TYPE.MOVE && selectedIds.has(element.getId()) && selectedIds.size > 1) {
|
|
19345
|
+
const resolved = resolveIds(selectedIds, tracks);
|
|
19346
|
+
const elements = resolved.filter((item) => item instanceof TrackElement);
|
|
19347
|
+
if (elements.length > 1) {
|
|
19348
|
+
const minStart = Math.min(...elements.map((el) => el.getStart()));
|
|
19349
|
+
const maxEnd = Math.max(...elements.map((el) => el.getEnd()));
|
|
19350
|
+
const delta = updates.start - element.getStart();
|
|
19351
|
+
const deltaMin = -minStart;
|
|
19352
|
+
const deltaMax = duration - maxEnd;
|
|
19353
|
+
const clampedDelta = Math.max(deltaMin, Math.min(deltaMax, delta));
|
|
19354
|
+
for (const el of elements) {
|
|
19355
|
+
const newStart = el.getStart() + clampedDelta;
|
|
19356
|
+
const newEnd = el.getEnd() + clampedDelta;
|
|
19357
|
+
if (el instanceof VideoElement$1 || el instanceof AudioElement) {
|
|
19358
|
+
const elementProps = el.getProps();
|
|
19359
|
+
const startDelta = newStart - el.getStart() * ((elementProps == null ? void 0 : elementProps.playbackRate) || 1);
|
|
19360
|
+
if (el instanceof AudioElement) {
|
|
19361
|
+
el.setStartAt(el.getStartAt() + startDelta);
|
|
19362
|
+
} else {
|
|
19363
|
+
el.setStartAt(el.getStartAt() + startDelta);
|
|
19364
|
+
}
|
|
19365
|
+
}
|
|
19366
|
+
el.setStart(newStart);
|
|
19367
|
+
el.setEnd(newEnd);
|
|
19368
|
+
editor.updateElement(el);
|
|
19369
|
+
}
|
|
19370
|
+
setSelectedItem(element);
|
|
19371
|
+
editor.refresh();
|
|
19372
|
+
return;
|
|
19373
|
+
}
|
|
19374
|
+
}
|
|
18288
19375
|
if (dragType === DRAG_TYPE.START) {
|
|
18289
19376
|
if (element instanceof VideoElement$1 || element instanceof AudioElement) {
|
|
18290
19377
|
const elementProps = element.getProps();
|
|
@@ -18331,13 +19418,135 @@ const useTimelineManager = () => {
|
|
|
18331
19418
|
totalDuration
|
|
18332
19419
|
};
|
|
18333
19420
|
};
|
|
19421
|
+
function useTimelineSelection() {
|
|
19422
|
+
const { editor, selectedIds, setSelection, setSelectedItem } = useTimelineContext();
|
|
19423
|
+
const handleItemSelect = useCallback(
|
|
19424
|
+
(item, event) => {
|
|
19425
|
+
var _a;
|
|
19426
|
+
const id2 = item.getId();
|
|
19427
|
+
const isMulti = event.metaKey || event.ctrlKey;
|
|
19428
|
+
const isRange = event.shiftKey;
|
|
19429
|
+
const tracks = ((_a = editor.getTimelineData()) == null ? void 0 : _a.tracks) ?? [];
|
|
19430
|
+
if (isMulti) {
|
|
19431
|
+
setSelection((prev) => {
|
|
19432
|
+
const next = new Set(prev);
|
|
19433
|
+
if (next.has(id2)) {
|
|
19434
|
+
next.delete(id2);
|
|
19435
|
+
} else {
|
|
19436
|
+
next.add(id2);
|
|
19437
|
+
}
|
|
19438
|
+
return next;
|
|
19439
|
+
});
|
|
19440
|
+
return;
|
|
19441
|
+
}
|
|
19442
|
+
if (isRange) {
|
|
19443
|
+
const primaryId = selectedIds.size > 0 ? [...selectedIds][0] : null;
|
|
19444
|
+
if (!primaryId) {
|
|
19445
|
+
setSelectedItem(item);
|
|
19446
|
+
return;
|
|
19447
|
+
}
|
|
19448
|
+
const primary = resolveId(primaryId, tracks);
|
|
19449
|
+
if (!primary) {
|
|
19450
|
+
setSelectedItem(item);
|
|
19451
|
+
return;
|
|
19452
|
+
}
|
|
19453
|
+
if (item instanceof Track && primary instanceof Track) {
|
|
19454
|
+
const trackIds = tracks.map((t2) => t2.getId());
|
|
19455
|
+
const fromIdx = trackIds.indexOf(primary.getId());
|
|
19456
|
+
const toIdx = trackIds.indexOf(item.getId());
|
|
19457
|
+
if (fromIdx !== -1 && toIdx !== -1) {
|
|
19458
|
+
const [start, end] = fromIdx <= toIdx ? [fromIdx, toIdx] : [toIdx, fromIdx];
|
|
19459
|
+
const rangeIds = new Set(
|
|
19460
|
+
trackIds.slice(start, end + 1)
|
|
19461
|
+
);
|
|
19462
|
+
setSelection((prev) => /* @__PURE__ */ new Set([...prev, ...rangeIds]));
|
|
19463
|
+
return;
|
|
19464
|
+
}
|
|
19465
|
+
}
|
|
19466
|
+
if (item instanceof TrackElement && primary instanceof TrackElement) {
|
|
19467
|
+
const track = editor.getTrackById(item.getTrackId());
|
|
19468
|
+
const primaryTrack = editor.getTrackById(primary.getTrackId());
|
|
19469
|
+
if (track && primaryTrack && track.getId() === primaryTrack.getId()) {
|
|
19470
|
+
const rangeIds = getElementIdsInRange(
|
|
19471
|
+
track,
|
|
19472
|
+
primary.getId(),
|
|
19473
|
+
item.getId()
|
|
19474
|
+
);
|
|
19475
|
+
setSelection((prev) => /* @__PURE__ */ new Set([...prev, ...rangeIds]));
|
|
19476
|
+
return;
|
|
19477
|
+
}
|
|
19478
|
+
}
|
|
19479
|
+
}
|
|
19480
|
+
setSelectedItem(item);
|
|
19481
|
+
},
|
|
19482
|
+
[editor, selectedIds, setSelection, setSelectedItem]
|
|
19483
|
+
);
|
|
19484
|
+
const handleEmptyClick = useCallback(() => {
|
|
19485
|
+
setSelectedItem(null);
|
|
19486
|
+
}, [setSelectedItem]);
|
|
19487
|
+
const handleMarqueeSelect = useCallback(
|
|
19488
|
+
(ids) => {
|
|
19489
|
+
setSelection(ids);
|
|
19490
|
+
},
|
|
19491
|
+
[setSelection]
|
|
19492
|
+
);
|
|
19493
|
+
return { handleItemSelect, handleEmptyClick, handleMarqueeSelect };
|
|
19494
|
+
}
|
|
18334
19495
|
const TimelineManager = ({
|
|
18335
19496
|
trackZoom,
|
|
18336
19497
|
timelineTickConfigs,
|
|
18337
19498
|
elementColors
|
|
18338
19499
|
}) => {
|
|
18339
19500
|
var _a;
|
|
18340
|
-
const {
|
|
19501
|
+
const { playerState } = useLivePlayerContext();
|
|
19502
|
+
const { followPlayheadEnabled, editor, videoResolution, setSelectedItem } = useTimelineContext();
|
|
19503
|
+
const {
|
|
19504
|
+
timelineData,
|
|
19505
|
+
totalDuration,
|
|
19506
|
+
selectedItem,
|
|
19507
|
+
onAddTrack,
|
|
19508
|
+
onReorder,
|
|
19509
|
+
onElementDrag,
|
|
19510
|
+
onSeek
|
|
19511
|
+
} = useTimelineManager();
|
|
19512
|
+
const { selectedIds } = useTimelineContext();
|
|
19513
|
+
const { handleItemSelect, handleEmptyClick, handleMarqueeSelect } = useTimelineSelection();
|
|
19514
|
+
const [playheadState, setPlayheadState] = useState({
|
|
19515
|
+
positionPx: 0,
|
|
19516
|
+
isDragging: false
|
|
19517
|
+
});
|
|
19518
|
+
const handlePlayheadUpdate = useCallback((state) => {
|
|
19519
|
+
setPlayheadState(state);
|
|
19520
|
+
}, []);
|
|
19521
|
+
const isPlayheadActive = followPlayheadEnabled && playerState === PLAYER_STATE.PLAYING || playheadState.isDragging;
|
|
19522
|
+
const handleDropOnTimeline = useCallback(
|
|
19523
|
+
async (params) => {
|
|
19524
|
+
const { track, timeSec, type, url } = params;
|
|
19525
|
+
const element = createElementFromDrop(type, url, videoResolution);
|
|
19526
|
+
element.setStart(timeSec);
|
|
19527
|
+
const targetTrack = track ?? editor.addTrack(`Track_${Date.now()}`);
|
|
19528
|
+
const tryAdd = async (t2) => {
|
|
19529
|
+
var _a2;
|
|
19530
|
+
try {
|
|
19531
|
+
const result = await editor.addElementToTrack(t2, element);
|
|
19532
|
+
if (result) {
|
|
19533
|
+
setSelectedItem(element);
|
|
19534
|
+
return true;
|
|
19535
|
+
}
|
|
19536
|
+
} catch (err) {
|
|
19537
|
+
if (err instanceof ValidationError && ((_a2 = err.errors) == null ? void 0 : _a2.includes(VALIDATION_ERROR_CODE.COLLISION_ERROR))) {
|
|
19538
|
+
const newTrack = editor.addTrack(`Track_${Date.now()}`);
|
|
19539
|
+
return tryAdd(newTrack);
|
|
19540
|
+
}
|
|
19541
|
+
throw err;
|
|
19542
|
+
}
|
|
19543
|
+
return false;
|
|
19544
|
+
};
|
|
19545
|
+
await tryAdd(targetTrack);
|
|
19546
|
+
editor.refresh();
|
|
19547
|
+
},
|
|
19548
|
+
[editor, videoResolution, setSelectedItem]
|
|
19549
|
+
);
|
|
18341
19550
|
return /* @__PURE__ */ jsx(
|
|
18342
19551
|
TimelineView,
|
|
18343
19552
|
{
|
|
@@ -18345,14 +19554,22 @@ const TimelineManager = ({
|
|
|
18345
19554
|
zoomLevel: trackZoom,
|
|
18346
19555
|
duration: totalDuration,
|
|
18347
19556
|
selectedItem,
|
|
19557
|
+
selectedIds,
|
|
18348
19558
|
onDeletion: () => {
|
|
18349
19559
|
},
|
|
18350
19560
|
onAddTrack,
|
|
18351
19561
|
onReorder,
|
|
18352
19562
|
onElementDrag,
|
|
18353
19563
|
onSeek,
|
|
18354
|
-
|
|
19564
|
+
onItemSelect: handleItemSelect,
|
|
19565
|
+
onEmptyClick: handleEmptyClick,
|
|
19566
|
+
onMarqueeSelect: handleMarqueeSelect,
|
|
18355
19567
|
elementColors,
|
|
19568
|
+
playheadPositionPx: playheadState.positionPx,
|
|
19569
|
+
isPlayheadActive,
|
|
19570
|
+
onDropOnTimeline: handleDropOnTimeline,
|
|
19571
|
+
videoResolution,
|
|
19572
|
+
enableDropOnTimeline: true,
|
|
18356
19573
|
seekTrack: /* @__PURE__ */ jsx(
|
|
18357
19574
|
SeekControl,
|
|
18358
19575
|
{
|
|
@@ -18360,7 +19577,8 @@ const TimelineManager = ({
|
|
|
18360
19577
|
zoom: trackZoom,
|
|
18361
19578
|
onSeek,
|
|
18362
19579
|
timelineCount: ((_a = timelineData == null ? void 0 : timelineData.tracks) == null ? void 0 : _a.length) ?? 0,
|
|
18363
|
-
timelineTickConfigs
|
|
19580
|
+
timelineTickConfigs,
|
|
19581
|
+
onPlayheadUpdate: handlePlayheadUpdate
|
|
18364
19582
|
}
|
|
18365
19583
|
)
|
|
18366
19584
|
}
|
|
@@ -18390,6 +19608,7 @@ const UndoRedoControls = ({ canUndo, canRedo, onUndo, onRedo }) => {
|
|
|
18390
19608
|
};
|
|
18391
19609
|
const PlayerControls = ({
|
|
18392
19610
|
selectedItem,
|
|
19611
|
+
selectedIds = /* @__PURE__ */ new Set(),
|
|
18393
19612
|
duration,
|
|
18394
19613
|
currentTime,
|
|
18395
19614
|
playerState,
|
|
@@ -18403,21 +19622,31 @@ const PlayerControls = ({
|
|
|
18403
19622
|
zoomLevel = 1,
|
|
18404
19623
|
setZoomLevel,
|
|
18405
19624
|
className = "",
|
|
18406
|
-
zoomConfig = DEFAULT_TIMELINE_ZOOM_CONFIG
|
|
19625
|
+
zoomConfig = DEFAULT_TIMELINE_ZOOM_CONFIG,
|
|
19626
|
+
fps = DEFAULT_FPS,
|
|
19627
|
+
onSeek,
|
|
19628
|
+
followPlayheadEnabled = true,
|
|
19629
|
+
onFollowPlayheadToggle
|
|
18407
19630
|
}) => {
|
|
18408
19631
|
const MAX_ZOOM = zoomConfig.max;
|
|
18409
19632
|
const MIN_ZOOM = zoomConfig.min;
|
|
18410
19633
|
const ZOOM_STEP = zoomConfig.step;
|
|
18411
|
-
const formatTime = useCallback(
|
|
18412
|
-
|
|
18413
|
-
|
|
18414
|
-
|
|
18415
|
-
|
|
19634
|
+
const formatTime = useCallback(
|
|
19635
|
+
(time2) => formatTimeWithFrames(time2, fps),
|
|
19636
|
+
[fps]
|
|
19637
|
+
);
|
|
19638
|
+
const handleSeekToStart = useCallback(() => {
|
|
19639
|
+
onSeek == null ? void 0 : onSeek(0);
|
|
19640
|
+
}, [onSeek]);
|
|
19641
|
+
const handleSeekToEnd = useCallback(() => {
|
|
19642
|
+
onSeek == null ? void 0 : onSeek(duration);
|
|
19643
|
+
}, [onSeek, duration]);
|
|
18416
19644
|
const handleDelete = useCallback(() => {
|
|
18417
|
-
if (
|
|
18418
|
-
onDelete(
|
|
19645
|
+
if (selectedIds.size > 0 && onDelete) {
|
|
19646
|
+
onDelete();
|
|
18419
19647
|
}
|
|
18420
|
-
}, [
|
|
19648
|
+
}, [selectedIds.size, onDelete]);
|
|
19649
|
+
const hasSelection = selectedIds.size > 0;
|
|
18421
19650
|
const handleSplit = useCallback(() => {
|
|
18422
19651
|
if (selectedItem instanceof TrackElement && onSplit) {
|
|
18423
19652
|
onSplit(selectedItem, currentTime);
|
|
@@ -18439,9 +19668,9 @@ const PlayerControls = ({
|
|
|
18439
19668
|
"button",
|
|
18440
19669
|
{
|
|
18441
19670
|
onClick: handleDelete,
|
|
18442
|
-
disabled: !
|
|
19671
|
+
disabled: !hasSelection,
|
|
18443
19672
|
title: "Delete",
|
|
18444
|
-
className: `control-btn delete-btn ${!
|
|
19673
|
+
className: `control-btn delete-btn ${!hasSelection ? "btn-disabled" : ""}`,
|
|
18445
19674
|
children: /* @__PURE__ */ jsx(Trash2, { className: "icon-md" })
|
|
18446
19675
|
}
|
|
18447
19676
|
),
|
|
@@ -18466,6 +19695,25 @@ const PlayerControls = ({
|
|
|
18466
19695
|
)
|
|
18467
19696
|
] }),
|
|
18468
19697
|
/* @__PURE__ */ jsxs("div", { className: "playback-controls", children: [
|
|
19698
|
+
onFollowPlayheadToggle && /* @__PURE__ */ jsx(
|
|
19699
|
+
"button",
|
|
19700
|
+
{
|
|
19701
|
+
onClick: onFollowPlayheadToggle,
|
|
19702
|
+
title: followPlayheadEnabled ? "Follow playhead on (click to disable)" : "Follow playhead off (click to enable)",
|
|
19703
|
+
className: `control-btn ${followPlayheadEnabled ? "follow-btn-active" : ""}`,
|
|
19704
|
+
children: /* @__PURE__ */ jsx(Crosshair, { className: "icon-md" })
|
|
19705
|
+
}
|
|
19706
|
+
),
|
|
19707
|
+
/* @__PURE__ */ jsx(
|
|
19708
|
+
"button",
|
|
19709
|
+
{
|
|
19710
|
+
onClick: handleSeekToStart,
|
|
19711
|
+
disabled: playerState === PLAYER_STATE.REFRESH,
|
|
19712
|
+
title: "Jump to start",
|
|
19713
|
+
className: "control-btn",
|
|
19714
|
+
children: /* @__PURE__ */ jsx(SkipBack, { className: "icon-md" })
|
|
19715
|
+
}
|
|
19716
|
+
),
|
|
18469
19717
|
/* @__PURE__ */ jsx(
|
|
18470
19718
|
"button",
|
|
18471
19719
|
{
|
|
@@ -18476,6 +19724,16 @@ const PlayerControls = ({
|
|
|
18476
19724
|
children: playerState === PLAYER_STATE.PLAYING ? /* @__PURE__ */ jsx(Pause, { className: "icon-lg" }) : playerState === PLAYER_STATE.REFRESH ? /* @__PURE__ */ jsx(LoaderCircle, { className: "icon-lg animate-spin" }) : /* @__PURE__ */ jsx(Play, { className: "icon-lg" })
|
|
18477
19725
|
}
|
|
18478
19726
|
),
|
|
19727
|
+
/* @__PURE__ */ jsx(
|
|
19728
|
+
"button",
|
|
19729
|
+
{
|
|
19730
|
+
onClick: handleSeekToEnd,
|
|
19731
|
+
disabled: playerState === PLAYER_STATE.REFRESH,
|
|
19732
|
+
title: "Jump to end",
|
|
19733
|
+
className: "control-btn",
|
|
19734
|
+
children: /* @__PURE__ */ jsx(SkipForward, { className: "icon-md" })
|
|
19735
|
+
}
|
|
19736
|
+
),
|
|
18479
19737
|
/* @__PURE__ */ jsxs("div", { className: "time-display", children: [
|
|
18480
19738
|
/* @__PURE__ */ jsx("span", { className: "current-time", children: formatTime(currentTime) }),
|
|
18481
19739
|
/* @__PURE__ */ jsx("span", { className: "time-separator", children: "/" }),
|
|
@@ -18537,12 +19795,17 @@ const usePlayerControl = () => {
|
|
|
18537
19795
|
};
|
|
18538
19796
|
};
|
|
18539
19797
|
const useTimelineControl = () => {
|
|
18540
|
-
const { editor, setSelectedItem } = useTimelineContext();
|
|
19798
|
+
const { editor, setSelectedItem, selectedIds } = useTimelineContext();
|
|
18541
19799
|
const deleteItem = (item) => {
|
|
18542
|
-
|
|
18543
|
-
|
|
18544
|
-
|
|
18545
|
-
|
|
19800
|
+
var _a;
|
|
19801
|
+
const tracks = ((_a = editor.getTimelineData()) == null ? void 0 : _a.tracks) ?? [];
|
|
19802
|
+
const toDelete = item !== void 0 ? [item] : resolveIds(selectedIds, tracks);
|
|
19803
|
+
for (const el of toDelete) {
|
|
19804
|
+
if (el instanceof Track) {
|
|
19805
|
+
editor.removeTrack(el);
|
|
19806
|
+
} else if (el instanceof TrackElement) {
|
|
19807
|
+
editor.removeElement(el);
|
|
19808
|
+
}
|
|
18546
19809
|
}
|
|
18547
19810
|
setSelectedItem(null);
|
|
18548
19811
|
};
|
|
@@ -18562,32 +19825,97 @@ const useTimelineControl = () => {
|
|
|
18562
19825
|
handleRedo
|
|
18563
19826
|
};
|
|
18564
19827
|
};
|
|
19828
|
+
function shouldIgnoreKeydown() {
|
|
19829
|
+
const active = document.activeElement;
|
|
19830
|
+
if (!active) return false;
|
|
19831
|
+
const tag = active.tagName.toLowerCase();
|
|
19832
|
+
if (tag === "input" || tag === "textarea") return true;
|
|
19833
|
+
if (active.isContentEditable) return true;
|
|
19834
|
+
return false;
|
|
19835
|
+
}
|
|
19836
|
+
function useCanvasKeyboard({
|
|
19837
|
+
onDelete,
|
|
19838
|
+
onUndo,
|
|
19839
|
+
onRedo,
|
|
19840
|
+
enabled = true
|
|
19841
|
+
}) {
|
|
19842
|
+
useEffect(() => {
|
|
19843
|
+
if (!enabled) return;
|
|
19844
|
+
const handleKeyDown = (e3) => {
|
|
19845
|
+
if (shouldIgnoreKeydown()) return;
|
|
19846
|
+
const key = e3.key.toLowerCase();
|
|
19847
|
+
const hasPrimaryModifier = e3.metaKey || e3.ctrlKey;
|
|
19848
|
+
if (hasPrimaryModifier) {
|
|
19849
|
+
if (key === "z" && !e3.shiftKey) {
|
|
19850
|
+
e3.preventDefault();
|
|
19851
|
+
onUndo == null ? void 0 : onUndo();
|
|
19852
|
+
return;
|
|
19853
|
+
}
|
|
19854
|
+
if (key === "y" || key === "z" && e3.shiftKey) {
|
|
19855
|
+
e3.preventDefault();
|
|
19856
|
+
onRedo == null ? void 0 : onRedo();
|
|
19857
|
+
return;
|
|
19858
|
+
}
|
|
19859
|
+
}
|
|
19860
|
+
if (!hasPrimaryModifier && (key === "delete" || key === "backspace")) {
|
|
19861
|
+
e3.preventDefault();
|
|
19862
|
+
onDelete == null ? void 0 : onDelete();
|
|
19863
|
+
}
|
|
19864
|
+
};
|
|
19865
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
19866
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
19867
|
+
}, [enabled, onDelete]);
|
|
19868
|
+
}
|
|
18565
19869
|
const ControlManager = ({
|
|
18566
19870
|
trackZoom,
|
|
18567
19871
|
setTrackZoom,
|
|
18568
|
-
zoomConfig
|
|
19872
|
+
zoomConfig,
|
|
19873
|
+
fps
|
|
18569
19874
|
}) => {
|
|
18570
|
-
const { currentTime, playerState } = useLivePlayerContext();
|
|
19875
|
+
const { currentTime, playerState, setSeekTime, setCurrentTime } = useLivePlayerContext();
|
|
18571
19876
|
const { togglePlayback } = usePlayerControl();
|
|
18572
|
-
const {
|
|
19877
|
+
const {
|
|
19878
|
+
canRedo,
|
|
19879
|
+
canUndo,
|
|
19880
|
+
totalDuration,
|
|
19881
|
+
selectedItem,
|
|
19882
|
+
selectedIds,
|
|
19883
|
+
followPlayheadEnabled,
|
|
19884
|
+
setFollowPlayheadEnabled
|
|
19885
|
+
} = useTimelineContext();
|
|
18573
19886
|
const { deleteItem, splitElement, handleUndo, handleRedo } = useTimelineControl();
|
|
19887
|
+
useCanvasKeyboard({
|
|
19888
|
+
onDelete: () => deleteItem(),
|
|
19889
|
+
onUndo: () => handleUndo(),
|
|
19890
|
+
onRedo: () => handleRedo()
|
|
19891
|
+
});
|
|
19892
|
+
const handleSeek = (time2) => {
|
|
19893
|
+
const clamped = Math.max(0, Math.min(totalDuration, time2));
|
|
19894
|
+
setCurrentTime(clamped);
|
|
19895
|
+
setSeekTime(clamped);
|
|
19896
|
+
};
|
|
18574
19897
|
return /* @__PURE__ */ jsx("div", { className: "twick-editor-timeline-controls", children: /* @__PURE__ */ jsx(
|
|
18575
19898
|
PlayerControls,
|
|
18576
19899
|
{
|
|
18577
19900
|
selectedItem,
|
|
19901
|
+
selectedIds,
|
|
18578
19902
|
duration: totalDuration,
|
|
18579
19903
|
currentTime,
|
|
18580
19904
|
playerState,
|
|
18581
19905
|
togglePlayback,
|
|
18582
19906
|
canUndo,
|
|
18583
19907
|
canRedo,
|
|
18584
|
-
onDelete: deleteItem,
|
|
19908
|
+
onDelete: () => deleteItem(),
|
|
18585
19909
|
onSplit: splitElement,
|
|
18586
19910
|
onUndo: handleUndo,
|
|
18587
19911
|
onRedo: handleRedo,
|
|
18588
19912
|
zoomLevel: trackZoom,
|
|
18589
19913
|
setZoomLevel: setTrackZoom,
|
|
18590
|
-
zoomConfig
|
|
19914
|
+
zoomConfig,
|
|
19915
|
+
fps: fps ?? DEFAULT_FPS,
|
|
19916
|
+
onSeek: handleSeek,
|
|
19917
|
+
followPlayheadEnabled,
|
|
19918
|
+
onFollowPlayheadToggle: () => setFollowPlayheadEnabled(!followPlayheadEnabled)
|
|
18591
19919
|
}
|
|
18592
19920
|
) });
|
|
18593
19921
|
};
|
|
@@ -18614,7 +19942,8 @@ const VideoEditor = ({
|
|
|
18614
19942
|
{
|
|
18615
19943
|
videoProps: editorConfig.videoProps,
|
|
18616
19944
|
playerProps: editorConfig.playerProps,
|
|
18617
|
-
canvasMode: editorConfig.canvasMode ?? true
|
|
19945
|
+
canvasMode: editorConfig.canvasMode ?? true,
|
|
19946
|
+
canvasConfig: editorConfig.canvasConfig
|
|
18618
19947
|
}
|
|
18619
19948
|
),
|
|
18620
19949
|
[editorConfig]
|
|
@@ -18632,7 +19961,8 @@ const VideoEditor = ({
|
|
|
18632
19961
|
{
|
|
18633
19962
|
trackZoom,
|
|
18634
19963
|
setTrackZoom,
|
|
18635
|
-
zoomConfig
|
|
19964
|
+
zoomConfig,
|
|
19965
|
+
fps: editorConfig.fps
|
|
18636
19966
|
}
|
|
18637
19967
|
) : null,
|
|
18638
19968
|
/* @__PURE__ */ jsx(
|
|
@@ -19037,6 +20367,7 @@ export {
|
|
|
19037
20367
|
BaseMediaManager,
|
|
19038
20368
|
BrowserMediaManager,
|
|
19039
20369
|
DEFAULT_ELEMENT_COLORS,
|
|
20370
|
+
DEFAULT_FPS,
|
|
19040
20371
|
DEFAULT_TIMELINE_TICK_CONFIGS,
|
|
19041
20372
|
DEFAULT_TIMELINE_ZOOM,
|
|
19042
20373
|
DEFAULT_TIMELINE_ZOOM_CONFIG,
|
|
@@ -19044,7 +20375,9 @@ export {
|
|
|
19044
20375
|
INITIAL_TIMELINE_DATA,
|
|
19045
20376
|
MIN_DURATION,
|
|
19046
20377
|
PlayerControls,
|
|
20378
|
+
SNAP_THRESHOLD_PX,
|
|
19047
20379
|
TEXT_EFFECTS,
|
|
20380
|
+
TIMELINE_DROP_MEDIA_TYPE,
|
|
19048
20381
|
TimelineManager,
|
|
19049
20382
|
animationGifs,
|
|
19050
20383
|
VideoEditor as default,
|