@twick/video-editor 0.15.13 → 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 +2539 -859
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2543 -863
- package/dist/index.mjs.map +1 -1
- package/dist/video-editor.css +180 -31
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -6841,6 +6841,9 @@ class bh extends Va {
|
|
|
6841
6841
|
}
|
|
6842
6842
|
}
|
|
6843
6843
|
t(bh, "type", "Vibrance"), t(bh, "defaults", { vibrance: 0 }), t(bh, "uniformLocations", ["uVibrance"]), tt.setClass(bh);
|
|
6844
|
+
var __defProp2 = Object.defineProperty;
|
|
6845
|
+
var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
6846
|
+
var __publicField2 = (obj, key, value) => __defNormalProp2(obj, key + "", value);
|
|
6844
6847
|
const DEFAULT_TEXT_PROPS = {
|
|
6845
6848
|
/** Font family for text elements */
|
|
6846
6849
|
family: "Poppins",
|
|
@@ -6879,7 +6882,13 @@ const CANVAS_OPERATIONS = {
|
|
|
6879
6882
|
/** An item has been updated/modified on the canvas */
|
|
6880
6883
|
ITEM_UPDATED: "ITEM_UPDATED",
|
|
6881
6884
|
/** Caption properties have been updated */
|
|
6882
|
-
CAPTION_PROPS_UPDATED: "CAPTION_PROPS_UPDATED"
|
|
6885
|
+
CAPTION_PROPS_UPDATED: "CAPTION_PROPS_UPDATED",
|
|
6886
|
+
/** Watermark has been updated */
|
|
6887
|
+
WATERMARK_UPDATED: "WATERMARK_UPDATED",
|
|
6888
|
+
/** A new element was added via drop on canvas; payload is { element } */
|
|
6889
|
+
ADDED_NEW_ELEMENT: "ADDED_NEW_ELEMENT",
|
|
6890
|
+
/** Z-order changed (bring to front / send to back). Payload is { elementId, direction }. Timeline should reorder tracks. */
|
|
6891
|
+
Z_ORDER_CHANGED: "Z_ORDER_CHANGED"
|
|
6883
6892
|
};
|
|
6884
6893
|
const ELEMENT_TYPES = {
|
|
6885
6894
|
/** Text element type */
|
|
@@ -6955,6 +6964,26 @@ const createCanvas = ({
|
|
|
6955
6964
|
canvasMetadata
|
|
6956
6965
|
};
|
|
6957
6966
|
};
|
|
6967
|
+
function measureTextWidth(text, options) {
|
|
6968
|
+
if (typeof document === "undefined" || !text) return 0;
|
|
6969
|
+
const canvas = document.createElement("canvas");
|
|
6970
|
+
const ctx = canvas.getContext("2d");
|
|
6971
|
+
if (!ctx) return 0;
|
|
6972
|
+
const {
|
|
6973
|
+
fontSize,
|
|
6974
|
+
fontFamily,
|
|
6975
|
+
fontStyle = "normal",
|
|
6976
|
+
fontWeight = "normal"
|
|
6977
|
+
} = options;
|
|
6978
|
+
ctx.font = `${fontStyle} ${String(fontWeight)} ${fontSize}px ${fontFamily}`;
|
|
6979
|
+
const lines = text.split("\n");
|
|
6980
|
+
let maxWidth = 0;
|
|
6981
|
+
for (const line of lines) {
|
|
6982
|
+
const { width } = ctx.measureText(line);
|
|
6983
|
+
if (width > maxWidth) maxWidth = width;
|
|
6984
|
+
}
|
|
6985
|
+
return Math.ceil(maxWidth);
|
|
6986
|
+
}
|
|
6958
6987
|
const reorderElementsByZIndex = (canvas) => {
|
|
6959
6988
|
if (!canvas) return;
|
|
6960
6989
|
const backgroundColor = canvas.backgroundColor;
|
|
@@ -6965,6 +6994,49 @@ const reorderElementsByZIndex = (canvas) => {
|
|
|
6965
6994
|
objects.forEach((obj) => canvas.add(obj));
|
|
6966
6995
|
canvas.renderAll();
|
|
6967
6996
|
};
|
|
6997
|
+
const changeZOrder = (canvas, elementId, direction) => {
|
|
6998
|
+
var _a, _b;
|
|
6999
|
+
if (!canvas) return null;
|
|
7000
|
+
const objects = canvas.getObjects();
|
|
7001
|
+
const sorted = [...objects].sort((a2, b2) => (a2.zIndex || 0) - (b2.zIndex || 0));
|
|
7002
|
+
const idx = sorted.findIndex((obj2) => {
|
|
7003
|
+
var _a2;
|
|
7004
|
+
return ((_a2 = obj2.get) == null ? void 0 : _a2.call(obj2, "id")) === elementId;
|
|
7005
|
+
});
|
|
7006
|
+
if (idx < 0) return null;
|
|
7007
|
+
const minZ = ((_a = sorted[0]) == null ? void 0 : _a.zIndex) ?? 0;
|
|
7008
|
+
const maxZ = ((_b = sorted[sorted.length - 1]) == null ? void 0 : _b.zIndex) ?? 0;
|
|
7009
|
+
const obj = sorted[idx];
|
|
7010
|
+
if (direction === "front") {
|
|
7011
|
+
obj.set("zIndex", maxZ + 1);
|
|
7012
|
+
reorderElementsByZIndex(canvas);
|
|
7013
|
+
return maxZ + 1;
|
|
7014
|
+
}
|
|
7015
|
+
if (direction === "back") {
|
|
7016
|
+
obj.set("zIndex", minZ - 1);
|
|
7017
|
+
reorderElementsByZIndex(canvas);
|
|
7018
|
+
return minZ - 1;
|
|
7019
|
+
}
|
|
7020
|
+
if (direction === "forward" && idx < sorted.length - 1) {
|
|
7021
|
+
const next = sorted[idx + 1];
|
|
7022
|
+
const myZ = obj.zIndex ?? idx;
|
|
7023
|
+
const nextZ = next.zIndex ?? idx + 1;
|
|
7024
|
+
obj.set("zIndex", nextZ);
|
|
7025
|
+
next.set("zIndex", myZ);
|
|
7026
|
+
reorderElementsByZIndex(canvas);
|
|
7027
|
+
return nextZ;
|
|
7028
|
+
}
|
|
7029
|
+
if (direction === "backward" && idx > 0) {
|
|
7030
|
+
const prev = sorted[idx - 1];
|
|
7031
|
+
const myZ = obj.zIndex ?? idx;
|
|
7032
|
+
const prevZ = prev.zIndex ?? idx - 1;
|
|
7033
|
+
obj.set("zIndex", prevZ);
|
|
7034
|
+
prev.set("zIndex", myZ);
|
|
7035
|
+
reorderElementsByZIndex(canvas);
|
|
7036
|
+
return prevZ;
|
|
7037
|
+
}
|
|
7038
|
+
return obj.zIndex ?? idx;
|
|
7039
|
+
};
|
|
6968
7040
|
const getCanvasContext = (canvas) => {
|
|
6969
7041
|
var _a, _b, _c, _d;
|
|
6970
7042
|
if (!canvas || !((_b = (_a = canvas.elements) == null ? void 0 : _a.lower) == null ? void 0 : _b.ctx)) return;
|
|
@@ -6985,12 +7057,31 @@ const convertToCanvasPosition = (x2, y2, canvasMetadata) => {
|
|
|
6985
7057
|
y: y2 * canvasMetadata.scaleY + canvasMetadata.height / 2
|
|
6986
7058
|
};
|
|
6987
7059
|
};
|
|
7060
|
+
const getObjectCanvasCenter = (obj) => {
|
|
7061
|
+
if (obj.getCenterPoint) {
|
|
7062
|
+
const p2 = obj.getCenterPoint();
|
|
7063
|
+
return { x: p2.x, y: p2.y };
|
|
7064
|
+
}
|
|
7065
|
+
return { x: obj.left ?? 0, y: obj.top ?? 0 };
|
|
7066
|
+
};
|
|
7067
|
+
const getObjectCanvasAngle = (obj) => {
|
|
7068
|
+
if (typeof obj.getTotalAngle === "function") {
|
|
7069
|
+
return obj.getTotalAngle();
|
|
7070
|
+
}
|
|
7071
|
+
return obj.angle ?? 0;
|
|
7072
|
+
};
|
|
6988
7073
|
const convertToVideoPosition = (x2, y2, canvasMetadata, videoSize) => {
|
|
6989
7074
|
return {
|
|
6990
7075
|
x: Number((x2 / canvasMetadata.scaleX - videoSize.width / 2).toFixed(2)),
|
|
6991
7076
|
y: Number((y2 / canvasMetadata.scaleY - videoSize.height / 2).toFixed(2))
|
|
6992
7077
|
};
|
|
6993
7078
|
};
|
|
7079
|
+
const convertToVideoDimensions = (widthCanvas, heightCanvas, canvasMetadata) => {
|
|
7080
|
+
return {
|
|
7081
|
+
width: Number((widthCanvas / canvasMetadata.scaleX).toFixed(2)),
|
|
7082
|
+
height: Number((heightCanvas / canvasMetadata.scaleY).toFixed(2))
|
|
7083
|
+
};
|
|
7084
|
+
};
|
|
6994
7085
|
const getCurrentFrameEffect = (item, seekTime) => {
|
|
6995
7086
|
var _a;
|
|
6996
7087
|
let currentFrameEffect;
|
|
@@ -7002,6 +7093,17 @@ const getCurrentFrameEffect = (item, seekTime) => {
|
|
|
7002
7093
|
}
|
|
7003
7094
|
return currentFrameEffect;
|
|
7004
7095
|
};
|
|
7096
|
+
const hexToRgba = (hex2, alpha2) => {
|
|
7097
|
+
const color2 = hex2.replace(/^#/, "");
|
|
7098
|
+
const fullHex = color2.length === 3 ? color2.split("").map((c2) => c2 + c2).join("") : color2;
|
|
7099
|
+
if (fullHex.length !== 6) {
|
|
7100
|
+
return hex2;
|
|
7101
|
+
}
|
|
7102
|
+
const r2 = parseInt(fullHex.slice(0, 2), 16);
|
|
7103
|
+
const g2 = parseInt(fullHex.slice(2, 4), 16);
|
|
7104
|
+
const b2 = parseInt(fullHex.slice(4, 6), 16);
|
|
7105
|
+
return `rgba(${r2}, ${g2}, ${b2}, ${alpha2})`;
|
|
7106
|
+
};
|
|
7005
7107
|
const disabledControl = new ai({
|
|
7006
7108
|
/** X position offset */
|
|
7007
7109
|
x: 0,
|
|
@@ -7433,40 +7535,66 @@ const addTextElement = ({
|
|
|
7433
7535
|
canvas,
|
|
7434
7536
|
canvasMetadata
|
|
7435
7537
|
}) => {
|
|
7436
|
-
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;
|
|
7538
|
+
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;
|
|
7437
7539
|
const { x: x2, y: y2 } = convertToCanvasPosition(
|
|
7438
7540
|
((_a = element.props) == null ? void 0 : _a.x) || 0,
|
|
7439
7541
|
((_b = element.props) == null ? void 0 : _b.y) || 0,
|
|
7440
7542
|
canvasMetadata
|
|
7441
7543
|
);
|
|
7442
|
-
|
|
7443
|
-
|
|
7444
|
-
|
|
7445
|
-
|
|
7446
|
-
const
|
|
7544
|
+
const fontSize = Math.floor(
|
|
7545
|
+
(((_c = element.props) == null ? void 0 : _c.fontSize) || DEFAULT_TEXT_PROPS.size) * canvasMetadata.scaleX
|
|
7546
|
+
);
|
|
7547
|
+
const fontFamily = ((_d = element.props) == null ? void 0 : _d.fontFamily) || DEFAULT_TEXT_PROPS.family;
|
|
7548
|
+
const fontStyle = ((_e2 = element.props) == null ? void 0 : _e2.fontStyle) || "normal";
|
|
7549
|
+
const fontWeight = ((_f = element.props) == null ? void 0 : _f.fontWeight) || "normal";
|
|
7550
|
+
let width;
|
|
7551
|
+
if (((_g = element.props) == null ? void 0 : _g.width) != null && element.props.width > 0) {
|
|
7552
|
+
width = element.props.width * canvasMetadata.scaleX;
|
|
7553
|
+
if ((_h2 = element.props) == null ? void 0 : _h2.maxWidth) {
|
|
7554
|
+
width = Math.min(width, element.props.maxWidth * canvasMetadata.scaleX);
|
|
7555
|
+
}
|
|
7556
|
+
} else {
|
|
7557
|
+
const textContent = ((_i2 = element.props) == null ? void 0 : _i2.text) ?? element.t ?? "";
|
|
7558
|
+
width = measureTextWidth(textContent, {
|
|
7559
|
+
fontSize,
|
|
7560
|
+
fontFamily,
|
|
7561
|
+
fontStyle,
|
|
7562
|
+
fontWeight
|
|
7563
|
+
});
|
|
7564
|
+
const padding = 4;
|
|
7565
|
+
width = width + padding * 2;
|
|
7566
|
+
if ((_j = element.props) == null ? void 0 : _j.maxWidth) {
|
|
7567
|
+
width = Math.min(width, element.props.maxWidth * canvasMetadata.scaleX);
|
|
7568
|
+
}
|
|
7569
|
+
if (width <= 0) width = 100;
|
|
7570
|
+
}
|
|
7571
|
+
const backgroundColor = ((_k = element.props) == null ? void 0 : _k.backgroundColor) ? hexToRgba(
|
|
7572
|
+
element.props.backgroundColor,
|
|
7573
|
+
((_l = element.props) == null ? void 0 : _l.backgroundOpacity) ?? 1
|
|
7574
|
+
) : void 0;
|
|
7575
|
+
const text = new Uo(((_m = element.props) == null ? void 0 : _m.text) || element.t || "", {
|
|
7447
7576
|
left: x2,
|
|
7448
7577
|
top: y2,
|
|
7449
7578
|
originX: "center",
|
|
7450
7579
|
originY: "center",
|
|
7451
|
-
angle: ((
|
|
7452
|
-
fontSize
|
|
7453
|
-
|
|
7454
|
-
|
|
7455
|
-
|
|
7456
|
-
|
|
7457
|
-
|
|
7458
|
-
fill: ((_k = element.props) == null ? void 0 : _k.fill) || DEFAULT_TEXT_PROPS.fill,
|
|
7459
|
-
opacity: ((_l = element.props) == null ? void 0 : _l.opacity) ?? 1,
|
|
7580
|
+
angle: ((_n2 = element.props) == null ? void 0 : _n2.rotation) || 0,
|
|
7581
|
+
fontSize,
|
|
7582
|
+
fontFamily,
|
|
7583
|
+
fontStyle,
|
|
7584
|
+
fontWeight,
|
|
7585
|
+
fill: ((_o2 = element.props) == null ? void 0 : _o2.fill) || DEFAULT_TEXT_PROPS.fill,
|
|
7586
|
+
opacity: ((_p = element.props) == null ? void 0 : _p.opacity) ?? 1,
|
|
7460
7587
|
width,
|
|
7461
7588
|
splitByGrapheme: false,
|
|
7462
|
-
textAlign: ((
|
|
7463
|
-
stroke: ((
|
|
7464
|
-
strokeWidth: ((
|
|
7465
|
-
|
|
7466
|
-
|
|
7467
|
-
|
|
7468
|
-
|
|
7469
|
-
|
|
7589
|
+
textAlign: ((_q = element.props) == null ? void 0 : _q.textAlign) || "center",
|
|
7590
|
+
stroke: ((_r2 = element.props) == null ? void 0 : _r2.stroke) || DEFAULT_TEXT_PROPS.stroke,
|
|
7591
|
+
strokeWidth: ((_s2 = element.props) == null ? void 0 : _s2.lineWidth) || DEFAULT_TEXT_PROPS.lineWidth,
|
|
7592
|
+
...backgroundColor && { backgroundColor },
|
|
7593
|
+
shadow: ((_t2 = element.props) == null ? void 0 : _t2.shadowColor) ? new Ds({
|
|
7594
|
+
offsetX: ((_v = (_u = element.props) == null ? void 0 : _u.shadowOffset) == null ? void 0 : _v.length) && ((_x = (_w = element.props) == null ? void 0 : _w.shadowOffset) == null ? void 0 : _x.length) > 1 ? element.props.shadowOffset[0] / 2 : 1,
|
|
7595
|
+
offsetY: ((_z = (_y = element.props) == null ? void 0 : _y.shadowOffset) == null ? void 0 : _z.length) && ((_A = element.props) == null ? void 0 : _A.shadowOffset.length) > 1 ? element.props.shadowOffset[1] / 2 : 1,
|
|
7596
|
+
blur: (((_B = element.props) == null ? void 0 : _B.shadowBlur) || 2) / 2,
|
|
7597
|
+
color: (_C = element.props) == null ? void 0 : _C.shadowColor
|
|
7470
7598
|
}) : void 0
|
|
7471
7599
|
});
|
|
7472
7600
|
text.set("id", element.id);
|
|
@@ -7487,7 +7615,8 @@ const setImageProps = ({
|
|
|
7487
7615
|
img,
|
|
7488
7616
|
element,
|
|
7489
7617
|
index,
|
|
7490
|
-
canvasMetadata
|
|
7618
|
+
canvasMetadata,
|
|
7619
|
+
lockAspectRatio = true
|
|
7491
7620
|
}) => {
|
|
7492
7621
|
var _a, _b, _c, _d, _e2;
|
|
7493
7622
|
const width = (((_a = element.props) == null ? void 0 : _a.width) || 0) * canvasMetadata.scaleX || canvasMetadata.width;
|
|
@@ -7507,13 +7636,15 @@ const setImageProps = ({
|
|
|
7507
7636
|
img.set("selectable", true);
|
|
7508
7637
|
img.set("hasControls", true);
|
|
7509
7638
|
img.set("touchAction", "all");
|
|
7639
|
+
img.set("lockUniScaling", lockAspectRatio);
|
|
7510
7640
|
};
|
|
7511
7641
|
const addCaptionElement = ({
|
|
7512
7642
|
element,
|
|
7513
7643
|
index,
|
|
7514
7644
|
canvas,
|
|
7515
7645
|
captionProps,
|
|
7516
|
-
canvasMetadata
|
|
7646
|
+
canvasMetadata,
|
|
7647
|
+
lockAspectRatio = false
|
|
7517
7648
|
}) => {
|
|
7518
7649
|
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;
|
|
7519
7650
|
const { x: x2, y: y2 } = convertToCanvasPosition(
|
|
@@ -7552,6 +7683,7 @@ const addCaptionElement = ({
|
|
|
7552
7683
|
});
|
|
7553
7684
|
caption.set("id", element.id);
|
|
7554
7685
|
caption.set("zIndex", index);
|
|
7686
|
+
caption.set("lockUniScaling", lockAspectRatio);
|
|
7555
7687
|
caption.controls.mt = disabledControl;
|
|
7556
7688
|
caption.controls.mb = disabledControl;
|
|
7557
7689
|
caption.controls.ml = disabledControl;
|
|
@@ -7599,7 +7731,8 @@ const addImageElement = async ({
|
|
|
7599
7731
|
index,
|
|
7600
7732
|
canvas,
|
|
7601
7733
|
canvasMetadata,
|
|
7602
|
-
currentFrameEffect
|
|
7734
|
+
currentFrameEffect,
|
|
7735
|
+
lockAspectRatio = true
|
|
7603
7736
|
}) => {
|
|
7604
7737
|
try {
|
|
7605
7738
|
const img = await oa.fromURL(imageUrl || element.props.src || "");
|
|
@@ -7608,7 +7741,7 @@ const addImageElement = async ({
|
|
|
7608
7741
|
originY: "center",
|
|
7609
7742
|
lockMovementX: false,
|
|
7610
7743
|
lockMovementY: false,
|
|
7611
|
-
lockUniScaling:
|
|
7744
|
+
lockUniScaling: lockAspectRatio,
|
|
7612
7745
|
hasControls: false,
|
|
7613
7746
|
selectable: false
|
|
7614
7747
|
});
|
|
@@ -7619,10 +7752,11 @@ const addImageElement = async ({
|
|
|
7619
7752
|
index,
|
|
7620
7753
|
canvas,
|
|
7621
7754
|
canvasMetadata,
|
|
7622
|
-
currentFrameEffect
|
|
7755
|
+
currentFrameEffect,
|
|
7756
|
+
lockAspectRatio
|
|
7623
7757
|
});
|
|
7624
7758
|
} else {
|
|
7625
|
-
setImageProps({ img, element, index, canvasMetadata });
|
|
7759
|
+
setImageProps({ img, element, index, canvasMetadata, lockAspectRatio });
|
|
7626
7760
|
canvas.add(img);
|
|
7627
7761
|
return img;
|
|
7628
7762
|
}
|
|
@@ -7635,7 +7769,8 @@ const addMediaGroup = ({
|
|
|
7635
7769
|
index,
|
|
7636
7770
|
canvas,
|
|
7637
7771
|
canvasMetadata,
|
|
7638
|
-
currentFrameEffect
|
|
7772
|
+
currentFrameEffect,
|
|
7773
|
+
lockAspectRatio = true
|
|
7639
7774
|
}) => {
|
|
7640
7775
|
var _a, _b, _c, _d, _e2, _f, _g, _h2, _i2, _j, _k, _l, _m, _n2;
|
|
7641
7776
|
let frameSize;
|
|
@@ -7724,6 +7859,7 @@ const addMediaGroup = ({
|
|
|
7724
7859
|
group.controls.mtr = rotateControl;
|
|
7725
7860
|
group.set("id", element.id);
|
|
7726
7861
|
group.set("zIndex", index);
|
|
7862
|
+
group.set("lockUniScaling", lockAspectRatio);
|
|
7727
7863
|
canvas.add(group);
|
|
7728
7864
|
return group;
|
|
7729
7865
|
};
|
|
@@ -7731,7 +7867,8 @@ const addRectElement = ({
|
|
|
7731
7867
|
element,
|
|
7732
7868
|
index,
|
|
7733
7869
|
canvas,
|
|
7734
|
-
canvasMetadata
|
|
7870
|
+
canvasMetadata,
|
|
7871
|
+
lockAspectRatio = false
|
|
7735
7872
|
}) => {
|
|
7736
7873
|
var _a, _b, _c, _d, _e2, _f, _g, _h2, _i2, _j, _k;
|
|
7737
7874
|
const { x: x2, y: y2 } = convertToCanvasPosition(
|
|
@@ -7769,6 +7906,7 @@ const addRectElement = ({
|
|
|
7769
7906
|
});
|
|
7770
7907
|
rect.set("id", element.id);
|
|
7771
7908
|
rect.set("zIndex", index);
|
|
7909
|
+
rect.set("lockUniScaling", lockAspectRatio);
|
|
7772
7910
|
rect.controls.mtr = rotateControl;
|
|
7773
7911
|
canvas.add(rect);
|
|
7774
7912
|
return rect;
|
|
@@ -7777,9 +7915,10 @@ const addCircleElement = ({
|
|
|
7777
7915
|
element,
|
|
7778
7916
|
index,
|
|
7779
7917
|
canvas,
|
|
7780
|
-
canvasMetadata
|
|
7918
|
+
canvasMetadata,
|
|
7919
|
+
lockAspectRatio = true
|
|
7781
7920
|
}) => {
|
|
7782
|
-
var _a, _b, _c, _d, _e2, _f;
|
|
7921
|
+
var _a, _b, _c, _d, _e2, _f, _g;
|
|
7783
7922
|
const { x: x2, y: y2 } = convertToCanvasPosition(
|
|
7784
7923
|
((_a = element.props) == null ? void 0 : _a.x) || 0,
|
|
7785
7924
|
((_b = element.props) == null ? void 0 : _b.y) || 0,
|
|
@@ -7795,7 +7934,9 @@ const addCircleElement = ({
|
|
|
7795
7934
|
stroke: ((_e2 = element.props) == null ? void 0 : _e2.stroke) || "#000000",
|
|
7796
7935
|
strokeWidth: (((_f = element.props) == null ? void 0 : _f.lineWidth) || 0) * canvasMetadata.scaleX,
|
|
7797
7936
|
originX: "center",
|
|
7798
|
-
originY: "center"
|
|
7937
|
+
originY: "center",
|
|
7938
|
+
// Respect element opacity (0–1). Defaults to fully opaque.
|
|
7939
|
+
opacity: ((_g = element.props) == null ? void 0 : _g.opacity) ?? 1
|
|
7799
7940
|
});
|
|
7800
7941
|
circle.controls.mt = disabledControl;
|
|
7801
7942
|
circle.controls.mb = disabledControl;
|
|
@@ -7804,6 +7945,7 @@ const addCircleElement = ({
|
|
|
7804
7945
|
circle.controls.mtr = disabledControl;
|
|
7805
7946
|
circle.set("id", element.id);
|
|
7806
7947
|
circle.set("zIndex", index);
|
|
7948
|
+
circle.set("lockUniScaling", lockAspectRatio);
|
|
7807
7949
|
canvas.add(circle);
|
|
7808
7950
|
return circle;
|
|
7809
7951
|
};
|
|
@@ -7838,17 +7980,462 @@ const addBackgroundColor = ({
|
|
|
7838
7980
|
canvas.add(bgRect);
|
|
7839
7981
|
return bgRect;
|
|
7840
7982
|
};
|
|
7983
|
+
const VideoElement = {
|
|
7984
|
+
name: ELEMENT_TYPES.VIDEO,
|
|
7985
|
+
async add(params) {
|
|
7986
|
+
var _a, _b;
|
|
7987
|
+
const {
|
|
7988
|
+
element,
|
|
7989
|
+
index,
|
|
7990
|
+
canvas,
|
|
7991
|
+
canvasMetadata,
|
|
7992
|
+
seekTime = 0,
|
|
7993
|
+
elementFrameMapRef,
|
|
7994
|
+
getCurrentFrameEffect: getFrameEffect
|
|
7995
|
+
} = params;
|
|
7996
|
+
if (!getFrameEffect || !elementFrameMapRef) return;
|
|
7997
|
+
const currentFrameEffect = getFrameEffect(element, seekTime);
|
|
7998
|
+
elementFrameMapRef.current[element.id] = currentFrameEffect;
|
|
7999
|
+
const snapTime = (seekTime - ((element == null ? void 0 : element.s) ?? 0)) * (((_a = element == null ? void 0 : element.props) == null ? void 0 : _a.playbackRate) ?? 1) + (((_b = element == null ? void 0 : element.props) == null ? void 0 : _b.time) ?? 0);
|
|
8000
|
+
await addVideoElement({
|
|
8001
|
+
element,
|
|
8002
|
+
index,
|
|
8003
|
+
canvas,
|
|
8004
|
+
canvasMetadata,
|
|
8005
|
+
currentFrameEffect,
|
|
8006
|
+
snapTime
|
|
8007
|
+
});
|
|
8008
|
+
if (element.timelineType === "scene") {
|
|
8009
|
+
await addBackgroundColor({
|
|
8010
|
+
element,
|
|
8011
|
+
index,
|
|
8012
|
+
canvas,
|
|
8013
|
+
canvasMetadata
|
|
8014
|
+
});
|
|
8015
|
+
}
|
|
8016
|
+
},
|
|
8017
|
+
updateFromFabricObject(object, element, context) {
|
|
8018
|
+
const canvasCenter = getObjectCanvasCenter(object);
|
|
8019
|
+
const { x: x2, y: y2 } = convertToVideoPosition(
|
|
8020
|
+
canvasCenter.x,
|
|
8021
|
+
canvasCenter.y,
|
|
8022
|
+
context.canvasMetadata,
|
|
8023
|
+
context.videoSize
|
|
8024
|
+
);
|
|
8025
|
+
const scaledW = (object.width ?? 0) * (object.scaleX ?? 1);
|
|
8026
|
+
const scaledH = (object.height ?? 0) * (object.scaleY ?? 1);
|
|
8027
|
+
const { width: fw, height: fh2 } = convertToVideoDimensions(
|
|
8028
|
+
scaledW,
|
|
8029
|
+
scaledH,
|
|
8030
|
+
context.canvasMetadata
|
|
8031
|
+
);
|
|
8032
|
+
const updatedFrameSize = [fw, fh2];
|
|
8033
|
+
const currentFrameEffect = context.elementFrameMapRef.current[element.id];
|
|
8034
|
+
if (currentFrameEffect) {
|
|
8035
|
+
context.elementFrameMapRef.current[element.id] = {
|
|
8036
|
+
...currentFrameEffect,
|
|
8037
|
+
props: {
|
|
8038
|
+
...currentFrameEffect.props,
|
|
8039
|
+
framePosition: { x: x2, y: y2 },
|
|
8040
|
+
frameSize: updatedFrameSize
|
|
8041
|
+
}
|
|
8042
|
+
};
|
|
8043
|
+
return {
|
|
8044
|
+
element: {
|
|
8045
|
+
...element,
|
|
8046
|
+
frameEffects: (element.frameEffects || []).map(
|
|
8047
|
+
(fe2) => fe2.id === (currentFrameEffect == null ? void 0 : currentFrameEffect.id) ? {
|
|
8048
|
+
...fe2,
|
|
8049
|
+
props: {
|
|
8050
|
+
...fe2.props,
|
|
8051
|
+
framePosition: { x: x2, y: y2 },
|
|
8052
|
+
frameSize: updatedFrameSize
|
|
8053
|
+
}
|
|
8054
|
+
} : fe2
|
|
8055
|
+
)
|
|
8056
|
+
}
|
|
8057
|
+
};
|
|
8058
|
+
}
|
|
8059
|
+
const frame2 = element.frame;
|
|
8060
|
+
return {
|
|
8061
|
+
element: {
|
|
8062
|
+
...element,
|
|
8063
|
+
frame: {
|
|
8064
|
+
...frame2,
|
|
8065
|
+
rotation: getObjectCanvasAngle(object),
|
|
8066
|
+
size: updatedFrameSize,
|
|
8067
|
+
x: x2,
|
|
8068
|
+
y: y2
|
|
8069
|
+
}
|
|
8070
|
+
}
|
|
8071
|
+
};
|
|
8072
|
+
}
|
|
8073
|
+
};
|
|
8074
|
+
const ImageElement = {
|
|
8075
|
+
name: ELEMENT_TYPES.IMAGE,
|
|
8076
|
+
async add(params) {
|
|
8077
|
+
var _a;
|
|
8078
|
+
const { element, index, canvas, canvasMetadata, lockAspectRatio } = params;
|
|
8079
|
+
await addImageElement({
|
|
8080
|
+
element,
|
|
8081
|
+
index,
|
|
8082
|
+
canvas,
|
|
8083
|
+
canvasMetadata,
|
|
8084
|
+
lockAspectRatio: lockAspectRatio ?? ((_a = element.props) == null ? void 0 : _a.lockAspectRatio)
|
|
8085
|
+
});
|
|
8086
|
+
if (element.timelineType === "scene") {
|
|
8087
|
+
await addBackgroundColor({
|
|
8088
|
+
element,
|
|
8089
|
+
index,
|
|
8090
|
+
canvas,
|
|
8091
|
+
canvasMetadata
|
|
8092
|
+
});
|
|
8093
|
+
}
|
|
8094
|
+
},
|
|
8095
|
+
updateFromFabricObject(object, element, context) {
|
|
8096
|
+
const canvasCenter = getObjectCanvasCenter(object);
|
|
8097
|
+
const { x: x2, y: y2 } = convertToVideoPosition(
|
|
8098
|
+
canvasCenter.x,
|
|
8099
|
+
canvasCenter.y,
|
|
8100
|
+
context.canvasMetadata,
|
|
8101
|
+
context.videoSize
|
|
8102
|
+
);
|
|
8103
|
+
const currentFrameEffect = context.elementFrameMapRef.current[element.id];
|
|
8104
|
+
if (object.type === "group") {
|
|
8105
|
+
const scaledW2 = (object.width ?? 0) * (object.scaleX ?? 1);
|
|
8106
|
+
const scaledH2 = (object.height ?? 0) * (object.scaleY ?? 1);
|
|
8107
|
+
const { width: fw, height: fh2 } = convertToVideoDimensions(
|
|
8108
|
+
scaledW2,
|
|
8109
|
+
scaledH2,
|
|
8110
|
+
context.canvasMetadata
|
|
8111
|
+
);
|
|
8112
|
+
const updatedFrameSize = [fw, fh2];
|
|
8113
|
+
if (currentFrameEffect) {
|
|
8114
|
+
context.elementFrameMapRef.current[element.id] = {
|
|
8115
|
+
...currentFrameEffect,
|
|
8116
|
+
props: {
|
|
8117
|
+
...currentFrameEffect.props,
|
|
8118
|
+
framePosition: { x: x2, y: y2 },
|
|
8119
|
+
frameSize: updatedFrameSize
|
|
8120
|
+
}
|
|
8121
|
+
};
|
|
8122
|
+
return {
|
|
8123
|
+
element: {
|
|
8124
|
+
...element,
|
|
8125
|
+
// Keep the base frame in sync with the active frame effect
|
|
8126
|
+
// so visualizer `Rect {...element.frame}` reflects the same size/position.
|
|
8127
|
+
frame: element.frame ? {
|
|
8128
|
+
...element.frame,
|
|
8129
|
+
rotation: getObjectCanvasAngle(object),
|
|
8130
|
+
size: updatedFrameSize,
|
|
8131
|
+
x: x2,
|
|
8132
|
+
y: y2
|
|
8133
|
+
} : element.frame,
|
|
8134
|
+
frameEffects: (element.frameEffects || []).map(
|
|
8135
|
+
(fe2) => fe2.id === (currentFrameEffect == null ? void 0 : currentFrameEffect.id) ? {
|
|
8136
|
+
...fe2,
|
|
8137
|
+
props: {
|
|
8138
|
+
...fe2.props,
|
|
8139
|
+
framePosition: { x: x2, y: y2 },
|
|
8140
|
+
frameSize: updatedFrameSize
|
|
8141
|
+
}
|
|
8142
|
+
} : fe2
|
|
8143
|
+
)
|
|
8144
|
+
}
|
|
8145
|
+
};
|
|
8146
|
+
}
|
|
8147
|
+
const frame2 = element.frame;
|
|
8148
|
+
return {
|
|
8149
|
+
element: {
|
|
8150
|
+
...element,
|
|
8151
|
+
frame: {
|
|
8152
|
+
...frame2,
|
|
8153
|
+
rotation: getObjectCanvasAngle(object),
|
|
8154
|
+
size: updatedFrameSize,
|
|
8155
|
+
x: x2,
|
|
8156
|
+
y: y2
|
|
8157
|
+
}
|
|
8158
|
+
}
|
|
8159
|
+
};
|
|
8160
|
+
}
|
|
8161
|
+
const scaledW = (object.width ?? 0) * (object.scaleX ?? 1);
|
|
8162
|
+
const scaledH = (object.height ?? 0) * (object.scaleY ?? 1);
|
|
8163
|
+
const { width, height } = convertToVideoDimensions(
|
|
8164
|
+
scaledW,
|
|
8165
|
+
scaledH,
|
|
8166
|
+
context.canvasMetadata
|
|
8167
|
+
);
|
|
8168
|
+
return {
|
|
8169
|
+
element: {
|
|
8170
|
+
...element,
|
|
8171
|
+
props: {
|
|
8172
|
+
...element.props,
|
|
8173
|
+
rotation: getObjectCanvasAngle(object),
|
|
8174
|
+
width,
|
|
8175
|
+
height,
|
|
8176
|
+
x: x2,
|
|
8177
|
+
y: y2
|
|
8178
|
+
}
|
|
8179
|
+
}
|
|
8180
|
+
};
|
|
8181
|
+
}
|
|
8182
|
+
};
|
|
8183
|
+
const RectElement = {
|
|
8184
|
+
name: ELEMENT_TYPES.RECT,
|
|
8185
|
+
async add(params) {
|
|
8186
|
+
var _a;
|
|
8187
|
+
const { element, index, canvas, canvasMetadata, lockAspectRatio } = params;
|
|
8188
|
+
await addRectElement({
|
|
8189
|
+
element,
|
|
8190
|
+
index,
|
|
8191
|
+
canvas,
|
|
8192
|
+
canvasMetadata,
|
|
8193
|
+
lockAspectRatio: lockAspectRatio ?? ((_a = element.props) == null ? void 0 : _a.lockAspectRatio)
|
|
8194
|
+
});
|
|
8195
|
+
},
|
|
8196
|
+
updateFromFabricObject(object, element, context) {
|
|
8197
|
+
var _a, _b;
|
|
8198
|
+
const canvasCenter = getObjectCanvasCenter(object);
|
|
8199
|
+
const { x: x2, y: y2 } = convertToVideoPosition(
|
|
8200
|
+
canvasCenter.x,
|
|
8201
|
+
canvasCenter.y,
|
|
8202
|
+
context.canvasMetadata,
|
|
8203
|
+
context.videoSize
|
|
8204
|
+
);
|
|
8205
|
+
return {
|
|
8206
|
+
element: {
|
|
8207
|
+
...element,
|
|
8208
|
+
props: {
|
|
8209
|
+
...element.props,
|
|
8210
|
+
rotation: getObjectCanvasAngle(object),
|
|
8211
|
+
width: (((_a = element.props) == null ? void 0 : _a.width) ?? 0) * object.scaleX,
|
|
8212
|
+
height: (((_b = element.props) == null ? void 0 : _b.height) ?? 0) * object.scaleY,
|
|
8213
|
+
x: x2,
|
|
8214
|
+
y: y2
|
|
8215
|
+
}
|
|
8216
|
+
}
|
|
8217
|
+
};
|
|
8218
|
+
}
|
|
8219
|
+
};
|
|
8220
|
+
const CircleElement = {
|
|
8221
|
+
name: ELEMENT_TYPES.CIRCLE,
|
|
8222
|
+
async add(params) {
|
|
8223
|
+
var _a;
|
|
8224
|
+
const { element, index, canvas, canvasMetadata, lockAspectRatio } = params;
|
|
8225
|
+
await addCircleElement({
|
|
8226
|
+
element,
|
|
8227
|
+
index,
|
|
8228
|
+
canvas,
|
|
8229
|
+
canvasMetadata,
|
|
8230
|
+
lockAspectRatio: lockAspectRatio ?? ((_a = element.props) == null ? void 0 : _a.lockAspectRatio)
|
|
8231
|
+
});
|
|
8232
|
+
},
|
|
8233
|
+
updateFromFabricObject(object, element, context) {
|
|
8234
|
+
var _a, _b;
|
|
8235
|
+
const canvasCenter = getObjectCanvasCenter(object);
|
|
8236
|
+
const { x: x2, y: y2 } = convertToVideoPosition(
|
|
8237
|
+
canvasCenter.x,
|
|
8238
|
+
canvasCenter.y,
|
|
8239
|
+
context.canvasMetadata,
|
|
8240
|
+
context.videoSize
|
|
8241
|
+
);
|
|
8242
|
+
const radius = Number(
|
|
8243
|
+
((((_a = element.props) == null ? void 0 : _a.radius) ?? 0) * object.scaleX).toFixed(2)
|
|
8244
|
+
);
|
|
8245
|
+
const opacity = object.opacity != null ? object.opacity : (_b = element.props) == null ? void 0 : _b.opacity;
|
|
8246
|
+
return {
|
|
8247
|
+
element: {
|
|
8248
|
+
...element,
|
|
8249
|
+
props: {
|
|
8250
|
+
...element.props,
|
|
8251
|
+
rotation: getObjectCanvasAngle(object),
|
|
8252
|
+
radius,
|
|
8253
|
+
height: radius * 2,
|
|
8254
|
+
width: radius * 2,
|
|
8255
|
+
x: x2,
|
|
8256
|
+
y: y2,
|
|
8257
|
+
...opacity != null && { opacity }
|
|
8258
|
+
}
|
|
8259
|
+
}
|
|
8260
|
+
};
|
|
8261
|
+
}
|
|
8262
|
+
};
|
|
8263
|
+
const TextElement = {
|
|
8264
|
+
name: ELEMENT_TYPES.TEXT,
|
|
8265
|
+
async add(params) {
|
|
8266
|
+
const { element, index, canvas, canvasMetadata } = params;
|
|
8267
|
+
await addTextElement({
|
|
8268
|
+
element,
|
|
8269
|
+
index,
|
|
8270
|
+
canvas,
|
|
8271
|
+
canvasMetadata
|
|
8272
|
+
});
|
|
8273
|
+
},
|
|
8274
|
+
updateFromFabricObject(object, element, context) {
|
|
8275
|
+
const canvasCenter = getObjectCanvasCenter(object);
|
|
8276
|
+
const { x: x2, y: y2 } = convertToVideoPosition(
|
|
8277
|
+
canvasCenter.x,
|
|
8278
|
+
canvasCenter.y,
|
|
8279
|
+
context.canvasMetadata,
|
|
8280
|
+
context.videoSize
|
|
8281
|
+
);
|
|
8282
|
+
return {
|
|
8283
|
+
element: {
|
|
8284
|
+
...element,
|
|
8285
|
+
props: {
|
|
8286
|
+
...element.props,
|
|
8287
|
+
rotation: getObjectCanvasAngle(object),
|
|
8288
|
+
x: x2,
|
|
8289
|
+
y: y2
|
|
8290
|
+
}
|
|
8291
|
+
}
|
|
8292
|
+
};
|
|
8293
|
+
}
|
|
8294
|
+
};
|
|
8295
|
+
const CaptionElement = {
|
|
8296
|
+
name: ELEMENT_TYPES.CAPTION,
|
|
8297
|
+
async add(params) {
|
|
8298
|
+
var _a;
|
|
8299
|
+
const { element, index, canvas, captionProps, canvasMetadata, lockAspectRatio } = params;
|
|
8300
|
+
await addCaptionElement({
|
|
8301
|
+
element,
|
|
8302
|
+
index,
|
|
8303
|
+
canvas,
|
|
8304
|
+
captionProps: captionProps ?? {},
|
|
8305
|
+
canvasMetadata,
|
|
8306
|
+
lockAspectRatio: lockAspectRatio ?? ((_a = element.props) == null ? void 0 : _a.lockAspectRatio)
|
|
8307
|
+
});
|
|
8308
|
+
},
|
|
8309
|
+
updateFromFabricObject(object, element, context) {
|
|
8310
|
+
var _a;
|
|
8311
|
+
const canvasCenter = getObjectCanvasCenter(object);
|
|
8312
|
+
const { x: x2, y: y2 } = convertToVideoPosition(
|
|
8313
|
+
canvasCenter.x,
|
|
8314
|
+
canvasCenter.y,
|
|
8315
|
+
context.canvasMetadata,
|
|
8316
|
+
context.videoSize
|
|
8317
|
+
);
|
|
8318
|
+
if ((_a = context.captionPropsRef.current) == null ? void 0 : _a.applyToAll) {
|
|
8319
|
+
return {
|
|
8320
|
+
element,
|
|
8321
|
+
operation: CANVAS_OPERATIONS.CAPTION_PROPS_UPDATED,
|
|
8322
|
+
payload: {
|
|
8323
|
+
element,
|
|
8324
|
+
props: {
|
|
8325
|
+
...context.captionPropsRef.current,
|
|
8326
|
+
x: x2,
|
|
8327
|
+
y: y2
|
|
8328
|
+
}
|
|
8329
|
+
}
|
|
8330
|
+
};
|
|
8331
|
+
}
|
|
8332
|
+
return {
|
|
8333
|
+
element: {
|
|
8334
|
+
...element,
|
|
8335
|
+
props: {
|
|
8336
|
+
...element.props,
|
|
8337
|
+
x: x2,
|
|
8338
|
+
y: y2
|
|
8339
|
+
}
|
|
8340
|
+
}
|
|
8341
|
+
};
|
|
8342
|
+
}
|
|
8343
|
+
};
|
|
8344
|
+
const WatermarkElement = {
|
|
8345
|
+
name: "watermark",
|
|
8346
|
+
async add(params) {
|
|
8347
|
+
const { element, index, canvas, canvasMetadata, watermarkPropsRef } = params;
|
|
8348
|
+
if (element.type === ELEMENT_TYPES.TEXT) {
|
|
8349
|
+
if (watermarkPropsRef) watermarkPropsRef.current = element.props;
|
|
8350
|
+
await addTextElement({
|
|
8351
|
+
element,
|
|
8352
|
+
index,
|
|
8353
|
+
canvas,
|
|
8354
|
+
canvasMetadata
|
|
8355
|
+
});
|
|
8356
|
+
} else if (element.type === ELEMENT_TYPES.IMAGE) {
|
|
8357
|
+
await addImageElement({
|
|
8358
|
+
element,
|
|
8359
|
+
index,
|
|
8360
|
+
canvas,
|
|
8361
|
+
canvasMetadata
|
|
8362
|
+
});
|
|
8363
|
+
}
|
|
8364
|
+
},
|
|
8365
|
+
updateFromFabricObject(object, element, context) {
|
|
8366
|
+
const { x: x2, y: y2 } = convertToVideoPosition(
|
|
8367
|
+
object.left,
|
|
8368
|
+
object.top,
|
|
8369
|
+
context.canvasMetadata,
|
|
8370
|
+
context.videoSize
|
|
8371
|
+
);
|
|
8372
|
+
const rotation = object.angle != null ? object.angle : void 0;
|
|
8373
|
+
const opacity = object.opacity != null ? object.opacity : void 0;
|
|
8374
|
+
const baseProps = element.type === ELEMENT_TYPES.TEXT ? context.watermarkPropsRef.current ?? element.props ?? {} : { ...element.props };
|
|
8375
|
+
const props = element.type === ELEMENT_TYPES.IMAGE && (object.scaleX != null || object.scaleY != null) ? {
|
|
8376
|
+
...baseProps,
|
|
8377
|
+
width: baseProps.width != null && object.scaleX != null ? baseProps.width * object.scaleX : baseProps.width,
|
|
8378
|
+
height: baseProps.height != null && object.scaleY != null ? baseProps.height * object.scaleY : baseProps.height
|
|
8379
|
+
} : baseProps;
|
|
8380
|
+
const payload = {
|
|
8381
|
+
position: { x: x2, y: y2 },
|
|
8382
|
+
...rotation != null && { rotation },
|
|
8383
|
+
...opacity != null && { opacity },
|
|
8384
|
+
...Object.keys(props).length > 0 && { props }
|
|
8385
|
+
};
|
|
8386
|
+
return {
|
|
8387
|
+
element: { ...element, props: { ...element.props, x: x2, y: y2, rotation, opacity, ...props } },
|
|
8388
|
+
operation: CANVAS_OPERATIONS.WATERMARK_UPDATED,
|
|
8389
|
+
payload
|
|
8390
|
+
};
|
|
8391
|
+
}
|
|
8392
|
+
};
|
|
8393
|
+
class ElementController {
|
|
8394
|
+
constructor() {
|
|
8395
|
+
__publicField2(this, "elements", /* @__PURE__ */ new Map());
|
|
8396
|
+
}
|
|
8397
|
+
register(handler) {
|
|
8398
|
+
this.elements.set(handler.name, handler);
|
|
8399
|
+
}
|
|
8400
|
+
get(name) {
|
|
8401
|
+
return this.elements.get(name);
|
|
8402
|
+
}
|
|
8403
|
+
list() {
|
|
8404
|
+
return Array.from(this.elements.keys());
|
|
8405
|
+
}
|
|
8406
|
+
}
|
|
8407
|
+
const elementController = new ElementController();
|
|
8408
|
+
function registerElements() {
|
|
8409
|
+
elementController.register(VideoElement);
|
|
8410
|
+
elementController.register(ImageElement);
|
|
8411
|
+
elementController.register(RectElement);
|
|
8412
|
+
elementController.register(CircleElement);
|
|
8413
|
+
elementController.register(TextElement);
|
|
8414
|
+
elementController.register(CaptionElement);
|
|
8415
|
+
elementController.register(WatermarkElement);
|
|
8416
|
+
}
|
|
8417
|
+
registerElements();
|
|
7841
8418
|
const useTwickCanvas = ({
|
|
7842
8419
|
onCanvasReady,
|
|
7843
|
-
onCanvasOperation
|
|
8420
|
+
onCanvasOperation,
|
|
8421
|
+
/**
|
|
8422
|
+
* When true, holding Shift while dragging an object will lock movement to
|
|
8423
|
+
* the dominant axis (horizontal or vertical). This mirrors behavior in
|
|
8424
|
+
* professional editors and improves precise alignment.
|
|
8425
|
+
*
|
|
8426
|
+
* Default: false (opt‑in to avoid surprising existing consumers).
|
|
8427
|
+
*/
|
|
8428
|
+
enableShiftAxisLock = false
|
|
7844
8429
|
}) => {
|
|
7845
8430
|
const [twickCanvas, setTwickCanvas] = React.useState(null);
|
|
7846
8431
|
const elementMap = React.useRef({});
|
|
8432
|
+
const watermarkPropsRef = React.useRef(null);
|
|
7847
8433
|
const elementFrameMap = React.useRef({});
|
|
7848
8434
|
const twickCanvasRef = React.useRef(null);
|
|
7849
8435
|
const videoSizeRef = React.useRef({ width: 1, height: 1 });
|
|
7850
8436
|
const canvasResolutionRef = React.useRef({ width: 1, height: 1 });
|
|
7851
8437
|
const captionPropsRef = React.useRef(null);
|
|
8438
|
+
const axisLockStateRef = React.useRef(null);
|
|
7852
8439
|
const canvasMetadataRef = React.useRef({
|
|
7853
8440
|
width: 0,
|
|
7854
8441
|
height: 0,
|
|
@@ -7863,6 +8450,57 @@ const useTwickCanvas = ({
|
|
|
7863
8450
|
canvasMetadataRef.current.scaleY = canvasMetadataRef.current.height / videoSize.height;
|
|
7864
8451
|
}
|
|
7865
8452
|
};
|
|
8453
|
+
const handleObjectMoving = (event) => {
|
|
8454
|
+
var _a;
|
|
8455
|
+
if (!enableShiftAxisLock) return;
|
|
8456
|
+
const target = event == null ? void 0 : event.target;
|
|
8457
|
+
const transform = event == null ? void 0 : event.transform;
|
|
8458
|
+
const pointerEvent = event == null ? void 0 : event.e;
|
|
8459
|
+
if (!target || !transform || !pointerEvent) {
|
|
8460
|
+
axisLockStateRef.current = null;
|
|
8461
|
+
return;
|
|
8462
|
+
}
|
|
8463
|
+
if (!pointerEvent.shiftKey) {
|
|
8464
|
+
axisLockStateRef.current = null;
|
|
8465
|
+
return;
|
|
8466
|
+
}
|
|
8467
|
+
const original = transform.original;
|
|
8468
|
+
if (!original || typeof target.left !== "number" || typeof target.top !== "number") {
|
|
8469
|
+
axisLockStateRef.current = null;
|
|
8470
|
+
return;
|
|
8471
|
+
}
|
|
8472
|
+
if (!axisLockStateRef.current) {
|
|
8473
|
+
const dx = Math.abs(target.left - original.left);
|
|
8474
|
+
const dy = Math.abs(target.top - original.top);
|
|
8475
|
+
axisLockStateRef.current = {
|
|
8476
|
+
axis: dx >= dy ? "x" : "y"
|
|
8477
|
+
};
|
|
8478
|
+
}
|
|
8479
|
+
if (axisLockStateRef.current.axis === "x") {
|
|
8480
|
+
target.top = original.top;
|
|
8481
|
+
} else {
|
|
8482
|
+
target.left = original.left;
|
|
8483
|
+
}
|
|
8484
|
+
(_a = target.canvas) == null ? void 0 : _a.requestRenderAll();
|
|
8485
|
+
};
|
|
8486
|
+
const applyMarqueeSelectionControls = () => {
|
|
8487
|
+
const canvasInstance = twickCanvasRef.current;
|
|
8488
|
+
if (!canvasInstance) return;
|
|
8489
|
+
const activeObject = canvasInstance.getActiveObject();
|
|
8490
|
+
if (!activeObject) return;
|
|
8491
|
+
if (activeObject instanceof Qo) {
|
|
8492
|
+
activeObject.controls.mt = disabledControl;
|
|
8493
|
+
activeObject.controls.mb = disabledControl;
|
|
8494
|
+
activeObject.controls.ml = disabledControl;
|
|
8495
|
+
activeObject.controls.mr = disabledControl;
|
|
8496
|
+
activeObject.controls.bl = disabledControl;
|
|
8497
|
+
activeObject.controls.br = disabledControl;
|
|
8498
|
+
activeObject.controls.tl = disabledControl;
|
|
8499
|
+
activeObject.controls.tr = disabledControl;
|
|
8500
|
+
activeObject.controls.mtr = rotateControl;
|
|
8501
|
+
canvasInstance.requestRenderAll();
|
|
8502
|
+
}
|
|
8503
|
+
};
|
|
7866
8504
|
const buildCanvas = ({
|
|
7867
8505
|
videoSize,
|
|
7868
8506
|
canvasSize,
|
|
@@ -7882,6 +8520,9 @@ const useTwickCanvas = ({
|
|
|
7882
8520
|
if (twickCanvasRef.current) {
|
|
7883
8521
|
twickCanvasRef.current.off("mouse:up", handleMouseUp);
|
|
7884
8522
|
twickCanvasRef.current.off("text:editing:exited", onTextEdit);
|
|
8523
|
+
twickCanvasRef.current.off("object:moving", handleObjectMoving);
|
|
8524
|
+
twickCanvasRef.current.off("selection:created", applyMarqueeSelectionControls);
|
|
8525
|
+
twickCanvasRef.current.off("selection:updated", applyMarqueeSelectionControls);
|
|
7885
8526
|
twickCanvasRef.current.dispose();
|
|
7886
8527
|
}
|
|
7887
8528
|
const { canvas, canvasMetadata } = createCanvas({
|
|
@@ -7899,6 +8540,9 @@ const useTwickCanvas = ({
|
|
|
7899
8540
|
videoSizeRef.current = videoSize;
|
|
7900
8541
|
canvas == null ? void 0 : canvas.on("mouse:up", handleMouseUp);
|
|
7901
8542
|
canvas == null ? void 0 : canvas.on("text:editing:exited", onTextEdit);
|
|
8543
|
+
canvas == null ? void 0 : canvas.on("object:moving", handleObjectMoving);
|
|
8544
|
+
canvas == null ? void 0 : canvas.on("selection:created", applyMarqueeSelectionControls);
|
|
8545
|
+
canvas == null ? void 0 : canvas.on("selection:updated", applyMarqueeSelectionControls);
|
|
7902
8546
|
canvasResolutionRef.current = canvasSize;
|
|
7903
8547
|
setTwickCanvas(canvas);
|
|
7904
8548
|
twickCanvasRef.current = canvas;
|
|
@@ -7925,7 +8569,8 @@ const useTwickCanvas = ({
|
|
|
7925
8569
|
if (event.target) {
|
|
7926
8570
|
const object = event.target;
|
|
7927
8571
|
const elementId = object.get("id");
|
|
7928
|
-
|
|
8572
|
+
const action = (_a = event.transform) == null ? void 0 : _a.action;
|
|
8573
|
+
if (action === "drag") {
|
|
7929
8574
|
const original = event.transform.original;
|
|
7930
8575
|
if (object.left === original.left && object.top === original.top) {
|
|
7931
8576
|
onCanvasOperation == null ? void 0 : onCanvasOperation(
|
|
@@ -7935,149 +8580,67 @@ const useTwickCanvas = ({
|
|
|
7935
8580
|
return;
|
|
7936
8581
|
}
|
|
7937
8582
|
}
|
|
7938
|
-
|
|
8583
|
+
const context = {
|
|
8584
|
+
canvasMetadata: canvasMetadataRef.current,
|
|
8585
|
+
videoSize: videoSizeRef.current,
|
|
8586
|
+
elementFrameMapRef: elementFrameMap,
|
|
8587
|
+
captionPropsRef,
|
|
8588
|
+
watermarkPropsRef
|
|
8589
|
+
};
|
|
8590
|
+
if (object instanceof Qo && (action === "drag" || action === "rotate")) {
|
|
8591
|
+
const objects = object.getObjects();
|
|
8592
|
+
for (const fabricObj of objects) {
|
|
8593
|
+
const id2 = fabricObj.get("id");
|
|
8594
|
+
if (!id2 || id2 === "e-watermark") continue;
|
|
8595
|
+
const currentElement = elementMap.current[id2];
|
|
8596
|
+
if (!currentElement) continue;
|
|
8597
|
+
const handler = elementController.get(currentElement.type);
|
|
8598
|
+
const result = (_b = handler == null ? void 0 : handler.updateFromFabricObject) == null ? void 0 : _b.call(
|
|
8599
|
+
handler,
|
|
8600
|
+
fabricObj,
|
|
8601
|
+
currentElement,
|
|
8602
|
+
context
|
|
8603
|
+
);
|
|
8604
|
+
if (result) {
|
|
8605
|
+
elementMap.current[id2] = result.element;
|
|
8606
|
+
onCanvasOperation == null ? void 0 : onCanvasOperation(
|
|
8607
|
+
result.operation ?? CANVAS_OPERATIONS.ITEM_UPDATED,
|
|
8608
|
+
result.payload ?? result.element
|
|
8609
|
+
);
|
|
8610
|
+
}
|
|
8611
|
+
}
|
|
8612
|
+
return;
|
|
8613
|
+
}
|
|
8614
|
+
switch (action) {
|
|
7939
8615
|
case "drag":
|
|
7940
8616
|
case "scale":
|
|
7941
8617
|
case "scaleX":
|
|
7942
8618
|
case "scaleY":
|
|
7943
|
-
case "rotate":
|
|
7944
|
-
const
|
|
7945
|
-
|
|
7946
|
-
|
|
7947
|
-
canvasMetadataRef.current,
|
|
7948
|
-
videoSizeRef.current
|
|
8619
|
+
case "rotate": {
|
|
8620
|
+
const currentElement = elementMap.current[elementId];
|
|
8621
|
+
const handler = elementController.get(
|
|
8622
|
+
elementId === "e-watermark" ? "watermark" : currentElement == null ? void 0 : currentElement.type
|
|
7949
8623
|
);
|
|
7950
|
-
|
|
7951
|
-
|
|
7952
|
-
|
|
7953
|
-
element: elementMap.current[elementId],
|
|
7954
|
-
props: {
|
|
7955
|
-
...captionPropsRef.current,
|
|
7956
|
-
x: x2,
|
|
7957
|
-
y: y2
|
|
7958
|
-
}
|
|
7959
|
-
});
|
|
7960
|
-
} else {
|
|
7961
|
-
elementMap.current[elementId] = {
|
|
7962
|
-
...elementMap.current[elementId],
|
|
7963
|
-
props: {
|
|
7964
|
-
...elementMap.current[elementId].props,
|
|
7965
|
-
x: x2,
|
|
7966
|
-
y: y2
|
|
7967
|
-
}
|
|
7968
|
-
};
|
|
7969
|
-
onCanvasOperation == null ? void 0 : onCanvasOperation(
|
|
7970
|
-
CANVAS_OPERATIONS.ITEM_UPDATED,
|
|
7971
|
-
elementMap.current[elementId]
|
|
7972
|
-
);
|
|
7973
|
-
}
|
|
7974
|
-
} else {
|
|
7975
|
-
if ((object == null ? void 0 : object.type) === "group") {
|
|
7976
|
-
const currentFrameEffect = elementFrameMap.current[elementId];
|
|
7977
|
-
let updatedFrameSize;
|
|
7978
|
-
if (currentFrameEffect) {
|
|
7979
|
-
updatedFrameSize = [
|
|
7980
|
-
currentFrameEffect.props.frameSize[0] * object.scaleX,
|
|
7981
|
-
currentFrameEffect.props.frameSize[1] * object.scaleY
|
|
7982
|
-
];
|
|
7983
|
-
} else {
|
|
7984
|
-
updatedFrameSize = [
|
|
7985
|
-
elementMap.current[elementId].frame.size[0] * object.scaleX,
|
|
7986
|
-
elementMap.current[elementId].frame.size[1] * object.scaleY
|
|
7987
|
-
];
|
|
7988
|
-
}
|
|
7989
|
-
if (currentFrameEffect) {
|
|
7990
|
-
elementMap.current[elementId] = {
|
|
7991
|
-
...elementMap.current[elementId],
|
|
7992
|
-
frameEffects: (elementMap.current[elementId].frameEffects || []).map(
|
|
7993
|
-
(frameEffect) => frameEffect.id === (currentFrameEffect == null ? void 0 : currentFrameEffect.id) ? {
|
|
7994
|
-
...frameEffect,
|
|
7995
|
-
props: {
|
|
7996
|
-
...frameEffect.props,
|
|
7997
|
-
framePosition: {
|
|
7998
|
-
x: x2,
|
|
7999
|
-
y: y2
|
|
8000
|
-
},
|
|
8001
|
-
frameSize: updatedFrameSize
|
|
8002
|
-
}
|
|
8003
|
-
} : frameEffect
|
|
8004
|
-
)
|
|
8005
|
-
};
|
|
8006
|
-
elementFrameMap.current[elementId] = {
|
|
8007
|
-
...elementFrameMap.current[elementId],
|
|
8008
|
-
framePosition: {
|
|
8009
|
-
x: x2,
|
|
8010
|
-
y: y2
|
|
8011
|
-
},
|
|
8012
|
-
frameSize: updatedFrameSize
|
|
8013
|
-
};
|
|
8014
|
-
} else {
|
|
8015
|
-
elementMap.current[elementId] = {
|
|
8016
|
-
...elementMap.current[elementId],
|
|
8017
|
-
frame: {
|
|
8018
|
-
...elementMap.current[elementId].frame,
|
|
8019
|
-
rotation: object.angle,
|
|
8020
|
-
size: updatedFrameSize,
|
|
8021
|
-
x: x2,
|
|
8022
|
-
y: y2
|
|
8023
|
-
}
|
|
8024
|
-
};
|
|
8025
|
-
}
|
|
8026
|
-
} else {
|
|
8027
|
-
if ((object == null ? void 0 : object.type) === "text") {
|
|
8028
|
-
elementMap.current[elementId] = {
|
|
8029
|
-
...elementMap.current[elementId],
|
|
8030
|
-
props: {
|
|
8031
|
-
...elementMap.current[elementId].props,
|
|
8032
|
-
rotation: object.angle,
|
|
8033
|
-
x: x2,
|
|
8034
|
-
y: y2
|
|
8035
|
-
}
|
|
8036
|
-
};
|
|
8037
|
-
} else if ((object == null ? void 0 : object.type) === "circle") {
|
|
8038
|
-
const radius = Number(
|
|
8039
|
-
(elementMap.current[elementId].props.radius * object.scaleX).toFixed(2)
|
|
8040
|
-
);
|
|
8041
|
-
elementMap.current[elementId] = {
|
|
8042
|
-
...elementMap.current[elementId],
|
|
8043
|
-
props: {
|
|
8044
|
-
...elementMap.current[elementId].props,
|
|
8045
|
-
rotation: object.angle,
|
|
8046
|
-
radius,
|
|
8047
|
-
height: radius * 2,
|
|
8048
|
-
width: radius * 2,
|
|
8049
|
-
x: x2,
|
|
8050
|
-
y: y2
|
|
8051
|
-
}
|
|
8052
|
-
};
|
|
8053
|
-
} else {
|
|
8054
|
-
elementMap.current[elementId] = {
|
|
8055
|
-
...elementMap.current[elementId],
|
|
8056
|
-
props: {
|
|
8057
|
-
...elementMap.current[elementId].props,
|
|
8058
|
-
rotation: object.angle,
|
|
8059
|
-
width: elementMap.current[elementId].props.width * object.scaleX,
|
|
8060
|
-
height: elementMap.current[elementId].props.height * object.scaleY,
|
|
8061
|
-
x: x2,
|
|
8062
|
-
y: y2
|
|
8063
|
-
}
|
|
8064
|
-
};
|
|
8065
|
-
}
|
|
8066
|
-
}
|
|
8624
|
+
const result = (_c = handler == null ? void 0 : handler.updateFromFabricObject) == null ? void 0 : _c.call(handler, object, currentElement ?? { id: elementId, type: "text", props: {} }, context);
|
|
8625
|
+
if (result) {
|
|
8626
|
+
elementMap.current[elementId] = result.element;
|
|
8067
8627
|
onCanvasOperation == null ? void 0 : onCanvasOperation(
|
|
8068
|
-
CANVAS_OPERATIONS.ITEM_UPDATED,
|
|
8069
|
-
|
|
8628
|
+
result.operation ?? CANVAS_OPERATIONS.ITEM_UPDATED,
|
|
8629
|
+
result.payload ?? result.element
|
|
8070
8630
|
);
|
|
8071
8631
|
}
|
|
8072
8632
|
break;
|
|
8633
|
+
}
|
|
8073
8634
|
}
|
|
8074
8635
|
}
|
|
8075
8636
|
};
|
|
8076
8637
|
const setCanvasElements = async ({
|
|
8077
8638
|
elements,
|
|
8639
|
+
watermark,
|
|
8078
8640
|
seekTime = 0,
|
|
8079
8641
|
captionProps,
|
|
8080
|
-
cleanAndAdd = false
|
|
8642
|
+
cleanAndAdd = false,
|
|
8643
|
+
lockAspectRatio
|
|
8081
8644
|
}) => {
|
|
8082
8645
|
if (!twickCanvas || !getCanvasContext(twickCanvas)) return;
|
|
8083
8646
|
try {
|
|
@@ -8090,21 +8653,36 @@ const useTwickCanvas = ({
|
|
|
8090
8653
|
}
|
|
8091
8654
|
}
|
|
8092
8655
|
captionPropsRef.current = captionProps;
|
|
8656
|
+
const uniqueElements = [];
|
|
8657
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
8658
|
+
for (const el of elements) {
|
|
8659
|
+
if (!el || !el.id) continue;
|
|
8660
|
+
if (seenIds.has(el.id)) continue;
|
|
8661
|
+
seenIds.add(el.id);
|
|
8662
|
+
uniqueElements.push(el);
|
|
8663
|
+
}
|
|
8093
8664
|
await Promise.all(
|
|
8094
|
-
|
|
8665
|
+
uniqueElements.map(async (element, index) => {
|
|
8095
8666
|
try {
|
|
8096
8667
|
if (!element) return;
|
|
8668
|
+
const zOrder = element.zIndex ?? index;
|
|
8097
8669
|
await addElementToCanvas({
|
|
8098
8670
|
element,
|
|
8099
|
-
index,
|
|
8671
|
+
index: zOrder,
|
|
8100
8672
|
reorder: false,
|
|
8101
8673
|
seekTime,
|
|
8102
|
-
captionProps
|
|
8674
|
+
captionProps,
|
|
8675
|
+
lockAspectRatio
|
|
8103
8676
|
});
|
|
8104
8677
|
} catch {
|
|
8105
8678
|
}
|
|
8106
8679
|
})
|
|
8107
8680
|
);
|
|
8681
|
+
if (watermark) {
|
|
8682
|
+
addWatermarkToCanvas({
|
|
8683
|
+
element: watermark
|
|
8684
|
+
});
|
|
8685
|
+
}
|
|
8108
8686
|
reorderElementsByZIndex(twickCanvas);
|
|
8109
8687
|
} catch {
|
|
8110
8688
|
}
|
|
@@ -8114,316 +8692,1033 @@ const useTwickCanvas = ({
|
|
|
8114
8692
|
index,
|
|
8115
8693
|
reorder = true,
|
|
8116
8694
|
seekTime,
|
|
8117
|
-
captionProps
|
|
8695
|
+
captionProps,
|
|
8696
|
+
lockAspectRatio
|
|
8118
8697
|
}) => {
|
|
8119
|
-
var _a
|
|
8698
|
+
var _a;
|
|
8120
8699
|
if (!twickCanvas) return;
|
|
8121
|
-
|
|
8122
|
-
|
|
8123
|
-
|
|
8124
|
-
|
|
8125
|
-
|
|
8126
|
-
|
|
8127
|
-
|
|
8128
|
-
|
|
8129
|
-
|
|
8130
|
-
|
|
8131
|
-
|
|
8132
|
-
|
|
8133
|
-
|
|
8134
|
-
currentFrameEffect,
|
|
8135
|
-
snapTime
|
|
8136
|
-
});
|
|
8137
|
-
if (element.timelineType === "scene") {
|
|
8138
|
-
await addBackgroundColor({
|
|
8139
|
-
element,
|
|
8140
|
-
index,
|
|
8141
|
-
canvas: twickCanvas,
|
|
8142
|
-
canvasMetadata: canvasMetadataRef.current
|
|
8143
|
-
});
|
|
8144
|
-
}
|
|
8145
|
-
break;
|
|
8146
|
-
case ELEMENT_TYPES.IMAGE:
|
|
8147
|
-
await addImageElement({
|
|
8148
|
-
element,
|
|
8149
|
-
index,
|
|
8150
|
-
canvas: twickCanvas,
|
|
8151
|
-
canvasMetadata: canvasMetadataRef.current
|
|
8152
|
-
});
|
|
8153
|
-
if (element.timelineType === "scene") {
|
|
8154
|
-
await addBackgroundColor({
|
|
8155
|
-
element,
|
|
8156
|
-
index,
|
|
8157
|
-
canvas: twickCanvas,
|
|
8158
|
-
canvasMetadata: canvasMetadataRef.current
|
|
8159
|
-
});
|
|
8160
|
-
}
|
|
8161
|
-
break;
|
|
8162
|
-
case ELEMENT_TYPES.RECT:
|
|
8163
|
-
await addRectElement({
|
|
8164
|
-
element,
|
|
8165
|
-
index,
|
|
8166
|
-
canvas: twickCanvas,
|
|
8167
|
-
canvasMetadata: canvasMetadataRef.current
|
|
8168
|
-
});
|
|
8169
|
-
break;
|
|
8170
|
-
case ELEMENT_TYPES.CIRCLE:
|
|
8171
|
-
await addCircleElement({
|
|
8172
|
-
element,
|
|
8173
|
-
index,
|
|
8174
|
-
canvas: twickCanvas,
|
|
8175
|
-
canvasMetadata: canvasMetadataRef.current
|
|
8176
|
-
});
|
|
8177
|
-
break;
|
|
8178
|
-
case ELEMENT_TYPES.TEXT:
|
|
8179
|
-
await addTextElement({
|
|
8180
|
-
element,
|
|
8181
|
-
index,
|
|
8182
|
-
canvas: twickCanvas,
|
|
8183
|
-
canvasMetadata: canvasMetadataRef.current
|
|
8184
|
-
});
|
|
8185
|
-
break;
|
|
8186
|
-
case ELEMENT_TYPES.CAPTION:
|
|
8187
|
-
await addCaptionElement({
|
|
8188
|
-
element,
|
|
8189
|
-
index,
|
|
8190
|
-
canvas: twickCanvas,
|
|
8191
|
-
captionProps,
|
|
8192
|
-
canvasMetadata: canvasMetadataRef.current
|
|
8193
|
-
});
|
|
8194
|
-
break;
|
|
8700
|
+
const handler = elementController.get(element.type);
|
|
8701
|
+
if (handler) {
|
|
8702
|
+
await handler.add({
|
|
8703
|
+
element,
|
|
8704
|
+
index,
|
|
8705
|
+
canvas: twickCanvas,
|
|
8706
|
+
canvasMetadata: canvasMetadataRef.current,
|
|
8707
|
+
seekTime,
|
|
8708
|
+
captionProps: captionProps ?? null,
|
|
8709
|
+
elementFrameMapRef: elementFrameMap,
|
|
8710
|
+
getCurrentFrameEffect,
|
|
8711
|
+
lockAspectRatio: lockAspectRatio ?? ((_a = element.props) == null ? void 0 : _a.lockAspectRatio)
|
|
8712
|
+
});
|
|
8195
8713
|
}
|
|
8196
|
-
elementMap.current[element.id] = element;
|
|
8714
|
+
elementMap.current[element.id] = { ...element, zIndex: element.zIndex ?? index };
|
|
8197
8715
|
if (reorder) {
|
|
8198
8716
|
reorderElementsByZIndex(twickCanvas);
|
|
8199
8717
|
}
|
|
8200
8718
|
};
|
|
8201
|
-
|
|
8202
|
-
|
|
8203
|
-
|
|
8204
|
-
|
|
8205
|
-
|
|
8206
|
-
|
|
8207
|
-
|
|
8208
|
-
|
|
8209
|
-
|
|
8210
|
-
|
|
8211
|
-
|
|
8212
|
-
|
|
8213
|
-
|
|
8214
|
-
|
|
8215
|
-
setTimelineAction,
|
|
8216
|
-
setSelectedItem,
|
|
8217
|
-
editor,
|
|
8218
|
-
changeLog
|
|
8219
|
-
} = timeline.useTimelineContext();
|
|
8220
|
-
const currentChangeLog = React.useRef(changeLog);
|
|
8221
|
-
const prevSeekTime = React.useRef(0);
|
|
8222
|
-
const [playerUpdating, setPlayerUpdating] = React.useState(false);
|
|
8223
|
-
const handleCanvasReady = (_canvas) => {
|
|
8224
|
-
};
|
|
8225
|
-
const handleCanvasOperation = (operation, data) => {
|
|
8226
|
-
if (operation === CANVAS_OPERATIONS.CAPTION_PROPS_UPDATED) {
|
|
8227
|
-
const subtitlesTrack = editor.getSubtiltesTrack();
|
|
8228
|
-
subtitlesTrack == null ? void 0 : subtitlesTrack.setProps(data.props);
|
|
8229
|
-
setSelectedItem(data.element);
|
|
8230
|
-
editor.refresh();
|
|
8231
|
-
} else {
|
|
8232
|
-
const element = timeline.ElementDeserializer.fromJSON(data);
|
|
8233
|
-
switch (operation) {
|
|
8234
|
-
case CANVAS_OPERATIONS.ITEM_SELECTED:
|
|
8235
|
-
setSelectedItem(element);
|
|
8236
|
-
break;
|
|
8237
|
-
case CANVAS_OPERATIONS.ITEM_UPDATED:
|
|
8238
|
-
if (element) {
|
|
8239
|
-
const updatedElement = editor.updateElement(element);
|
|
8240
|
-
currentChangeLog.current = currentChangeLog.current + 1;
|
|
8241
|
-
setSelectedItem(updatedElement);
|
|
8242
|
-
}
|
|
8243
|
-
break;
|
|
8244
|
-
}
|
|
8245
|
-
}
|
|
8246
|
-
};
|
|
8247
|
-
const { twickCanvas, buildCanvas, setCanvasElements } = useTwickCanvas({
|
|
8248
|
-
onCanvasReady: handleCanvasReady,
|
|
8249
|
-
onCanvasOperation: handleCanvasOperation
|
|
8250
|
-
});
|
|
8251
|
-
const updateCanvas = (seekTime) => {
|
|
8252
|
-
var _a;
|
|
8253
|
-
if (changeLog === currentChangeLog.current && seekTime === prevSeekTime.current) {
|
|
8254
|
-
return;
|
|
8719
|
+
const addWatermarkToCanvas = ({
|
|
8720
|
+
element
|
|
8721
|
+
}) => {
|
|
8722
|
+
if (!twickCanvas) return;
|
|
8723
|
+
const handler = elementController.get("watermark");
|
|
8724
|
+
if (handler) {
|
|
8725
|
+
handler.add({
|
|
8726
|
+
element,
|
|
8727
|
+
index: Object.keys(elementMap.current).length,
|
|
8728
|
+
canvas: twickCanvas,
|
|
8729
|
+
canvasMetadata: canvasMetadataRef.current,
|
|
8730
|
+
watermarkPropsRef
|
|
8731
|
+
});
|
|
8732
|
+
elementMap.current[element.id] = element;
|
|
8255
8733
|
}
|
|
8256
|
-
prevSeekTime.current = seekTime;
|
|
8257
|
-
const elements = timeline.getCurrentElements(
|
|
8258
|
-
seekTime,
|
|
8259
|
-
((_a = editor.getTimelineData()) == null ? void 0 : _a.tracks) ?? []
|
|
8260
|
-
);
|
|
8261
|
-
let captionProps = {};
|
|
8262
|
-
(elements || []).forEach((element) => {
|
|
8263
|
-
if (element instanceof timeline.CaptionElement) {
|
|
8264
|
-
const track = editor.getTrackById(element.getTrackId());
|
|
8265
|
-
captionProps = (track == null ? void 0 : track.getProps()) ?? {};
|
|
8266
|
-
}
|
|
8267
|
-
});
|
|
8268
|
-
setCanvasElements({
|
|
8269
|
-
elements,
|
|
8270
|
-
seekTime,
|
|
8271
|
-
captionProps,
|
|
8272
|
-
cleanAndAdd: true
|
|
8273
|
-
});
|
|
8274
|
-
currentChangeLog.current = changeLog;
|
|
8275
8734
|
};
|
|
8276
|
-
const
|
|
8277
|
-
|
|
8278
|
-
|
|
8279
|
-
|
|
8280
|
-
|
|
8281
|
-
}
|
|
8735
|
+
const applyZOrder = (elementId, direction) => {
|
|
8736
|
+
if (!twickCanvas) return false;
|
|
8737
|
+
const newZIndex = changeZOrder(twickCanvas, elementId, direction);
|
|
8738
|
+
if (newZIndex == null) return false;
|
|
8739
|
+
const element = elementMap.current[elementId];
|
|
8740
|
+
if (element) elementMap.current[elementId] = { ...element, zIndex: newZIndex };
|
|
8741
|
+
onCanvasOperation == null ? void 0 : onCanvasOperation(CANVAS_OPERATIONS.Z_ORDER_CHANGED, { elementId, direction });
|
|
8742
|
+
return true;
|
|
8282
8743
|
};
|
|
8283
|
-
|
|
8284
|
-
|
|
8285
|
-
|
|
8286
|
-
|
|
8287
|
-
if (videoProps) {
|
|
8288
|
-
if (((_a = timelineAction.payload) == null ? void 0 : _a.forceUpdate) || editor.getLatestVersion() !== ((_b = projectData == null ? void 0 : projectData.input) == null ? void 0 : _b.version)) {
|
|
8289
|
-
setPlayerUpdating(true);
|
|
8290
|
-
const _latestProjectData = {
|
|
8291
|
-
input: {
|
|
8292
|
-
properties: videoProps,
|
|
8293
|
-
tracks: ((_c = timelineAction.payload) == null ? void 0 : _c.tracks) ?? [],
|
|
8294
|
-
version: ((_d = timelineAction.payload) == null ? void 0 : _d.version) ?? 0
|
|
8295
|
-
}
|
|
8296
|
-
};
|
|
8297
|
-
setProjectData(_latestProjectData);
|
|
8298
|
-
if (((_e2 = timelineAction.payload) == null ? void 0 : _e2.version) === 1) {
|
|
8299
|
-
setTimeout(() => {
|
|
8300
|
-
setPlayerUpdating(false);
|
|
8301
|
-
});
|
|
8302
|
-
}
|
|
8303
|
-
} else {
|
|
8304
|
-
setTimelineAction(timeline.TIMELINE_ACTION.ON_PLAYER_UPDATED, null);
|
|
8305
|
-
}
|
|
8306
|
-
}
|
|
8307
|
-
break;
|
|
8308
|
-
}
|
|
8309
|
-
}, [timelineAction]);
|
|
8744
|
+
const bringToFront = (elementId) => applyZOrder(elementId, "front");
|
|
8745
|
+
const sendToBack = (elementId) => applyZOrder(elementId, "back");
|
|
8746
|
+
const bringForward = (elementId) => applyZOrder(elementId, "forward");
|
|
8747
|
+
const sendBackward = (elementId) => applyZOrder(elementId, "backward");
|
|
8310
8748
|
return {
|
|
8311
8749
|
twickCanvas,
|
|
8312
|
-
projectData,
|
|
8313
|
-
updateCanvas,
|
|
8314
8750
|
buildCanvas,
|
|
8315
|
-
|
|
8316
|
-
|
|
8751
|
+
onVideoSizeChange,
|
|
8752
|
+
addWatermarkToCanvas,
|
|
8753
|
+
addElementToCanvas,
|
|
8754
|
+
setCanvasElements,
|
|
8755
|
+
bringToFront,
|
|
8756
|
+
sendToBack,
|
|
8757
|
+
bringForward,
|
|
8758
|
+
sendBackward
|
|
8317
8759
|
};
|
|
8318
8760
|
};
|
|
8319
|
-
const
|
|
8320
|
-
|
|
8321
|
-
|
|
8322
|
-
|
|
8323
|
-
|
|
8324
|
-
|
|
8325
|
-
|
|
8326
|
-
|
|
8327
|
-
|
|
8328
|
-
|
|
8329
|
-
|
|
8330
|
-
|
|
8331
|
-
|
|
8332
|
-
|
|
8333
|
-
|
|
8334
|
-
|
|
8335
|
-
|
|
8336
|
-
|
|
8337
|
-
|
|
8338
|
-
|
|
8339
|
-
|
|
8340
|
-
|
|
8341
|
-
|
|
8342
|
-
|
|
8343
|
-
|
|
8344
|
-
|
|
8345
|
-
|
|
8346
|
-
|
|
8347
|
-
|
|
8348
|
-
|
|
8349
|
-
|
|
8350
|
-
|
|
8351
|
-
|
|
8352
|
-
|
|
8353
|
-
|
|
8354
|
-
|
|
8355
|
-
|
|
8356
|
-
|
|
8357
|
-
|
|
8358
|
-
|
|
8359
|
-
|
|
8360
|
-
|
|
8361
|
-
|
|
8362
|
-
|
|
8363
|
-
|
|
8364
|
-
|
|
8365
|
-
|
|
8366
|
-
|
|
8761
|
+
const VIDEO_TYPES = [
|
|
8762
|
+
"video/mp4",
|
|
8763
|
+
"video/webm",
|
|
8764
|
+
"video/ogg",
|
|
8765
|
+
"video/quicktime",
|
|
8766
|
+
"video/x-msvideo",
|
|
8767
|
+
"video/x-matroska"
|
|
8768
|
+
];
|
|
8769
|
+
const AUDIO_TYPES = [
|
|
8770
|
+
"audio/mpeg",
|
|
8771
|
+
"audio/mp3",
|
|
8772
|
+
"audio/wav",
|
|
8773
|
+
"audio/ogg",
|
|
8774
|
+
"audio/webm",
|
|
8775
|
+
"audio/aac",
|
|
8776
|
+
"audio/mp4",
|
|
8777
|
+
"audio/x-wav"
|
|
8778
|
+
];
|
|
8779
|
+
const IMAGE_TYPES = [
|
|
8780
|
+
"image/jpeg",
|
|
8781
|
+
"image/jpg",
|
|
8782
|
+
"image/png",
|
|
8783
|
+
"image/gif",
|
|
8784
|
+
"image/webp",
|
|
8785
|
+
"image/svg+xml",
|
|
8786
|
+
"image/bmp"
|
|
8787
|
+
];
|
|
8788
|
+
const EXT_TO_TYPE = {
|
|
8789
|
+
mp4: "video",
|
|
8790
|
+
webm: "video",
|
|
8791
|
+
mov: "video",
|
|
8792
|
+
avi: "video",
|
|
8793
|
+
mkv: "video",
|
|
8794
|
+
mp3: "audio",
|
|
8795
|
+
wav: "audio",
|
|
8796
|
+
ogg: "audio",
|
|
8797
|
+
m4a: "audio",
|
|
8798
|
+
jpg: "image",
|
|
8799
|
+
jpeg: "image",
|
|
8800
|
+
png: "image",
|
|
8801
|
+
gif: "image",
|
|
8802
|
+
webp: "image",
|
|
8803
|
+
svg: "image",
|
|
8804
|
+
bmp: "image"
|
|
8805
|
+
};
|
|
8806
|
+
function getAssetTypeFromFile(file) {
|
|
8807
|
+
var _a;
|
|
8808
|
+
const mime = (file.type || "").toLowerCase();
|
|
8809
|
+
const ext = (((_a = file.name) == null ? void 0 : _a.split(".").pop()) || "").toLowerCase();
|
|
8810
|
+
if (VIDEO_TYPES.some((t2) => mime.includes(t2))) return "video";
|
|
8811
|
+
if (AUDIO_TYPES.some((t2) => mime.includes(t2))) return "audio";
|
|
8812
|
+
if (IMAGE_TYPES.some((t2) => mime.includes(t2))) return "image";
|
|
8813
|
+
if (ext && EXT_TO_TYPE[ext]) return EXT_TO_TYPE[ext];
|
|
8814
|
+
return null;
|
|
8815
|
+
}
|
|
8816
|
+
const INITIAL_TIMELINE_DATA = {
|
|
8817
|
+
tracks: [
|
|
8367
8818
|
{
|
|
8368
|
-
|
|
8369
|
-
|
|
8370
|
-
|
|
8371
|
-
|
|
8372
|
-
|
|
8373
|
-
|
|
8374
|
-
"
|
|
8375
|
-
|
|
8376
|
-
|
|
8377
|
-
|
|
8378
|
-
|
|
8379
|
-
|
|
8380
|
-
|
|
8381
|
-
|
|
8382
|
-
),
|
|
8383
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8384
|
-
livePlayer.LivePlayer,
|
|
8385
|
-
{
|
|
8386
|
-
seekTime,
|
|
8387
|
-
projectData,
|
|
8388
|
-
quality: (playerProps == null ? void 0 : playerProps.quality) || 1,
|
|
8389
|
-
videoSize: {
|
|
8390
|
-
width: videoProps.width,
|
|
8391
|
-
height: videoProps.height
|
|
8392
|
-
},
|
|
8393
|
-
onPlayerUpdate,
|
|
8394
|
-
containerStyle: {
|
|
8395
|
-
opacity: canvasMode ? playerState === livePlayer.PLAYER_STATE.PAUSED ? 0 : 1 : 1
|
|
8396
|
-
},
|
|
8397
|
-
onTimeUpdate: handleTimeUpdate,
|
|
8398
|
-
volume: playerVolume,
|
|
8399
|
-
onDurationChange: (duration) => {
|
|
8400
|
-
durationRef.current = duration;
|
|
8401
|
-
},
|
|
8402
|
-
playing: playerState === livePlayer.PLAYER_STATE.PLAYING
|
|
8403
|
-
}
|
|
8404
|
-
),
|
|
8405
|
-
canvasMode && /* @__PURE__ */ jsxRuntime.jsx(
|
|
8406
|
-
"div",
|
|
8407
|
-
{
|
|
8408
|
-
ref: containerRef,
|
|
8409
|
-
className: "twick-editor-canvas-container",
|
|
8410
|
-
style: {
|
|
8411
|
-
opacity: playerState === livePlayer.PLAYER_STATE.PAUSED ? 1 : 0
|
|
8412
|
-
},
|
|
8413
|
-
children: /* @__PURE__ */ jsxRuntime.jsx("canvas", { ref: canvasRef, className: "twick-editor-canvas" })
|
|
8819
|
+
type: "element",
|
|
8820
|
+
id: "t-sample",
|
|
8821
|
+
name: "sample",
|
|
8822
|
+
elements: [
|
|
8823
|
+
{
|
|
8824
|
+
id: "e-sample",
|
|
8825
|
+
trackId: "t-sample",
|
|
8826
|
+
name: "sample",
|
|
8827
|
+
type: "text",
|
|
8828
|
+
s: 0,
|
|
8829
|
+
e: 5,
|
|
8830
|
+
props: {
|
|
8831
|
+
text: "Twick Video Editor",
|
|
8832
|
+
fill: "#FFFFFF"
|
|
8414
8833
|
}
|
|
8415
|
-
|
|
8834
|
+
}
|
|
8416
8835
|
]
|
|
8417
8836
|
}
|
|
8418
|
-
|
|
8837
|
+
],
|
|
8838
|
+
version: 1
|
|
8419
8839
|
};
|
|
8420
|
-
|
|
8421
|
-
|
|
8422
|
-
|
|
8423
|
-
|
|
8424
|
-
|
|
8425
|
-
|
|
8426
|
-
|
|
8840
|
+
const MIN_DURATION = 0.1;
|
|
8841
|
+
const TIMELINE_DROP_MEDIA_TYPE = "application/x-twick-media";
|
|
8842
|
+
const DRAG_TYPE = {
|
|
8843
|
+
/** Drag operation is starting */
|
|
8844
|
+
START: "start",
|
|
8845
|
+
/** Drag operation is in progress */
|
|
8846
|
+
MOVE: "move",
|
|
8847
|
+
/** Drag operation has ended */
|
|
8848
|
+
END: "end"
|
|
8849
|
+
};
|
|
8850
|
+
const DEFAULT_TIMELINE_ZOOM = 1.5;
|
|
8851
|
+
const DEFAULT_FPS = 30;
|
|
8852
|
+
const SNAP_THRESHOLD_PX = 10;
|
|
8853
|
+
const DEFAULT_TIMELINE_ZOOM_CONFIG = {
|
|
8854
|
+
/** Minimum zoom level (10%) */
|
|
8855
|
+
min: 0.1,
|
|
8856
|
+
/** Maximum zoom level (300%) */
|
|
8857
|
+
max: 3,
|
|
8858
|
+
/** Zoom step increment/decrement (10%) */
|
|
8859
|
+
step: 0.1,
|
|
8860
|
+
/** Default zoom level (150%) */
|
|
8861
|
+
default: 1.5
|
|
8862
|
+
};
|
|
8863
|
+
const DEFAULT_TIMELINE_TICK_CONFIGS = [
|
|
8864
|
+
{
|
|
8865
|
+
durationThreshold: 10,
|
|
8866
|
+
// < 10 seconds
|
|
8867
|
+
majorInterval: 1,
|
|
8868
|
+
// 1s major ticks
|
|
8869
|
+
minorTicks: 10
|
|
8870
|
+
// 0.1s minor ticks (10 minors between majors)
|
|
8871
|
+
},
|
|
8872
|
+
{
|
|
8873
|
+
durationThreshold: 30,
|
|
8874
|
+
// < 30 seconds
|
|
8875
|
+
majorInterval: 5,
|
|
8876
|
+
// 5s major ticks
|
|
8877
|
+
minorTicks: 5
|
|
8878
|
+
// 1s minor ticks (5 minors between majors)
|
|
8879
|
+
},
|
|
8880
|
+
{
|
|
8881
|
+
durationThreshold: 120,
|
|
8882
|
+
// < 2 minutes
|
|
8883
|
+
majorInterval: 10,
|
|
8884
|
+
// 10s major ticks
|
|
8885
|
+
minorTicks: 5
|
|
8886
|
+
// 2s minor ticks (5 minors between majors)
|
|
8887
|
+
},
|
|
8888
|
+
{
|
|
8889
|
+
durationThreshold: 300,
|
|
8890
|
+
// < 5 minutes
|
|
8891
|
+
majorInterval: 30,
|
|
8892
|
+
// 30s major ticks
|
|
8893
|
+
minorTicks: 6
|
|
8894
|
+
// 5s minor ticks (6 minors between majors)
|
|
8895
|
+
},
|
|
8896
|
+
{
|
|
8897
|
+
durationThreshold: 900,
|
|
8898
|
+
// < 15 minutes
|
|
8899
|
+
majorInterval: 60,
|
|
8900
|
+
// 1m major ticks
|
|
8901
|
+
minorTicks: 6
|
|
8902
|
+
// 10s minor ticks (6 minors between majors)
|
|
8903
|
+
},
|
|
8904
|
+
{
|
|
8905
|
+
durationThreshold: 1800,
|
|
8906
|
+
// < 30 minutes
|
|
8907
|
+
majorInterval: 120,
|
|
8908
|
+
// 2m major ticks
|
|
8909
|
+
minorTicks: 4
|
|
8910
|
+
// 30s minor ticks (4 minors between majors)
|
|
8911
|
+
},
|
|
8912
|
+
{
|
|
8913
|
+
durationThreshold: 3600,
|
|
8914
|
+
// < 1 hour
|
|
8915
|
+
majorInterval: 300,
|
|
8916
|
+
// 5m major ticks
|
|
8917
|
+
minorTicks: 5
|
|
8918
|
+
// 1m minor ticks (5 minors between majors)
|
|
8919
|
+
},
|
|
8920
|
+
{
|
|
8921
|
+
durationThreshold: 7200,
|
|
8922
|
+
// < 2 hours
|
|
8923
|
+
majorInterval: 600,
|
|
8924
|
+
// 10m major ticks
|
|
8925
|
+
minorTicks: 10
|
|
8926
|
+
// 1m minor ticks (10 minors between majors)
|
|
8927
|
+
},
|
|
8928
|
+
{
|
|
8929
|
+
durationThreshold: Infinity,
|
|
8930
|
+
// >= 2 hours
|
|
8931
|
+
majorInterval: 1800,
|
|
8932
|
+
// 30m major ticks
|
|
8933
|
+
minorTicks: 6
|
|
8934
|
+
// 5m minor ticks (6 minors between majors)
|
|
8935
|
+
}
|
|
8936
|
+
];
|
|
8937
|
+
const DEFAULT_ELEMENT_COLORS = {
|
|
8938
|
+
/** Fragment element color - deep charcoal matching UI background */
|
|
8939
|
+
fragment: "#1A1A1A",
|
|
8940
|
+
/** Video element color - vibrant royal purple */
|
|
8941
|
+
video: "#8B5FBF",
|
|
8942
|
+
/** Caption element color - soft wisteria purple */
|
|
8943
|
+
caption: "#9B8ACE",
|
|
8944
|
+
/** Image element color - warm copper accent */
|
|
8945
|
+
image: "#D4956C",
|
|
8946
|
+
/** Audio element color - deep teal */
|
|
8947
|
+
audio: "#3D8B8B",
|
|
8948
|
+
/** Text element color - medium lavender */
|
|
8949
|
+
text: "#8D74C4",
|
|
8950
|
+
/** Generic element color - muted amethyst */
|
|
8951
|
+
element: "#7B68B8",
|
|
8952
|
+
/** Rectangle element color - deep indigo */
|
|
8953
|
+
rect: "#5B4B99",
|
|
8954
|
+
/** Frame effect color - rich magenta */
|
|
8955
|
+
frameEffect: "#B55B9C",
|
|
8956
|
+
/** Filters color - periwinkle blue */
|
|
8957
|
+
filters: "#7A89D4",
|
|
8958
|
+
/** Transition color - burnished bronze */
|
|
8959
|
+
transition: "#BE8157",
|
|
8960
|
+
/** Animation color - muted emerald */
|
|
8961
|
+
animation: "#4B9B78",
|
|
8962
|
+
/** Icon element color - bright orchid */
|
|
8963
|
+
icon: "#A76CD4",
|
|
8964
|
+
/** Circle element color - deep byzantium */
|
|
8965
|
+
circle: "#703D8B"
|
|
8966
|
+
};
|
|
8967
|
+
const AVAILABLE_TEXT_FONTS = {
|
|
8968
|
+
// Google Fonts
|
|
8969
|
+
/** Modern sans-serif font */
|
|
8970
|
+
RUBIK: "Rubik",
|
|
8971
|
+
/** Clean and readable font */
|
|
8972
|
+
MULISH: "Mulish",
|
|
8973
|
+
/** Bold display font */
|
|
8974
|
+
LUCKIEST_GUY: "Luckiest Guy",
|
|
8975
|
+
/** Elegant serif font */
|
|
8976
|
+
PLAYFAIR_DISPLAY: "Playfair Display",
|
|
8977
|
+
/** Classic sans-serif font */
|
|
8978
|
+
ROBOTO: "Roboto",
|
|
8979
|
+
/** Modern geometric font */
|
|
8980
|
+
POPPINS: "Poppins",
|
|
8981
|
+
// Display and Decorative Fonts
|
|
8982
|
+
/** Comic-style display font */
|
|
8983
|
+
BANGERS: "Bangers",
|
|
8984
|
+
/** Handwritten-style font */
|
|
8985
|
+
BIRTHSTONE: "Birthstone",
|
|
8986
|
+
/** Elegant script font */
|
|
8987
|
+
CORINTHIA: "Corinthia",
|
|
8988
|
+
/** Formal script font */
|
|
8989
|
+
IMPERIAL_SCRIPT: "Imperial Script",
|
|
8990
|
+
/** Bold outline font */
|
|
8991
|
+
KUMAR_ONE_OUTLINE: "Kumar One Outline",
|
|
8992
|
+
/** Light outline font */
|
|
8993
|
+
LONDRI_OUTLINE: "Londrina Outline",
|
|
8994
|
+
/** Casual script font */
|
|
8995
|
+
MARCK_SCRIPT: "Marck Script",
|
|
8996
|
+
/** Modern sans-serif font */
|
|
8997
|
+
MONTSERRAT: "Montserrat",
|
|
8998
|
+
/** Stylish display font */
|
|
8999
|
+
PATTAYA: "Pattaya",
|
|
9000
|
+
// CDN Fonts
|
|
9001
|
+
/** Unique display font */
|
|
9002
|
+
PERALTA: "Peralta",
|
|
9003
|
+
/** Bold impact font */
|
|
9004
|
+
IMPACT: "Impact",
|
|
9005
|
+
/** Handwritten-style font */
|
|
9006
|
+
LUMANOSIMO: "Lumanosimo",
|
|
9007
|
+
/** Custom display font */
|
|
9008
|
+
KAPAKANA: "Kapakana",
|
|
9009
|
+
/** Handwritten font */
|
|
9010
|
+
HANDYRUSH: "HandyRush",
|
|
9011
|
+
/** Decorative font */
|
|
9012
|
+
DASHER: "Dasher",
|
|
9013
|
+
/** Signature-style font */
|
|
9014
|
+
BRITTANY_SIGNATURE: "Brittany Signature"
|
|
9015
|
+
};
|
|
9016
|
+
const DEFAULT_DROP_DURATION = 5;
|
|
9017
|
+
function useTimelineDrop({
|
|
9018
|
+
containerRef,
|
|
9019
|
+
scrollContainerRef,
|
|
9020
|
+
tracks,
|
|
9021
|
+
duration,
|
|
9022
|
+
zoomLevel,
|
|
9023
|
+
labelWidth,
|
|
9024
|
+
trackHeight,
|
|
9025
|
+
/** Width of the track content area (timeline minus labels). Used for accurate time mapping. */
|
|
9026
|
+
trackContentWidth,
|
|
9027
|
+
onDrop,
|
|
9028
|
+
enabled = true
|
|
9029
|
+
}) {
|
|
9030
|
+
const [preview, setPreview] = React.useState(null);
|
|
9031
|
+
const [isDraggingOver, setIsDraggingOver] = React.useState(false);
|
|
9032
|
+
const computePosition = React.useCallback(
|
|
9033
|
+
(clientX, clientY) => {
|
|
9034
|
+
var _a, _b;
|
|
9035
|
+
if (!containerRef.current) return null;
|
|
9036
|
+
const rect = containerRef.current.getBoundingClientRect();
|
|
9037
|
+
const scrollEl = (scrollContainerRef == null ? void 0 : scrollContainerRef.current) ?? containerRef.current;
|
|
9038
|
+
const scrollLeft = (scrollEl == null ? void 0 : scrollEl.scrollLeft) ?? 0;
|
|
9039
|
+
const viewportLeft = ((_b = (_a = scrollEl == null ? void 0 : scrollEl.getBoundingClientRect) == null ? void 0 : _a.call(scrollEl)) == null ? void 0 : _b.left) ?? rect.left;
|
|
9040
|
+
const contentX = clientX - viewportLeft + scrollLeft - labelWidth;
|
|
9041
|
+
const relY = clientY - rect.top;
|
|
9042
|
+
const rawTrackIndex = Math.floor(relY / trackHeight);
|
|
9043
|
+
const trackIndex = tracks.length === 0 ? 0 : Math.max(0, Math.min(tracks.length - 1, rawTrackIndex));
|
|
9044
|
+
const pixelsPerSecond = trackContentWidth != null && trackContentWidth > 0 ? trackContentWidth / duration : 100 * zoomLevel;
|
|
9045
|
+
const timeSec = Math.max(
|
|
9046
|
+
0,
|
|
9047
|
+
Math.min(duration, contentX / pixelsPerSecond)
|
|
9048
|
+
);
|
|
9049
|
+
return { trackIndex, timeSec };
|
|
9050
|
+
},
|
|
9051
|
+
[
|
|
9052
|
+
containerRef,
|
|
9053
|
+
scrollContainerRef,
|
|
9054
|
+
tracks.length,
|
|
9055
|
+
labelWidth,
|
|
9056
|
+
trackHeight,
|
|
9057
|
+
zoomLevel,
|
|
9058
|
+
duration,
|
|
9059
|
+
trackContentWidth
|
|
9060
|
+
]
|
|
9061
|
+
);
|
|
9062
|
+
const handleDragOver = React.useCallback(
|
|
9063
|
+
(e3) => {
|
|
9064
|
+
if (!enabled) return;
|
|
9065
|
+
e3.preventDefault();
|
|
9066
|
+
e3.stopPropagation();
|
|
9067
|
+
const hasFiles = e3.dataTransfer.types.includes("Files");
|
|
9068
|
+
const hasPanelMedia = e3.dataTransfer.types.includes(TIMELINE_DROP_MEDIA_TYPE);
|
|
9069
|
+
if (!hasFiles && !hasPanelMedia) return;
|
|
9070
|
+
e3.dataTransfer.dropEffect = "copy";
|
|
9071
|
+
setIsDraggingOver(true);
|
|
9072
|
+
const pos = computePosition(e3.clientX, e3.clientY);
|
|
9073
|
+
if (pos) {
|
|
9074
|
+
const track = tracks[pos.trackIndex] ?? null;
|
|
9075
|
+
if (track || tracks.length === 0) {
|
|
9076
|
+
setPreview({
|
|
9077
|
+
trackIndex: pos.trackIndex,
|
|
9078
|
+
timeSec: pos.timeSec,
|
|
9079
|
+
widthPct: Math.min(
|
|
9080
|
+
100,
|
|
9081
|
+
DEFAULT_DROP_DURATION / duration * 100
|
|
9082
|
+
)
|
|
9083
|
+
});
|
|
9084
|
+
}
|
|
9085
|
+
}
|
|
9086
|
+
},
|
|
9087
|
+
[enabled, computePosition, tracks, duration]
|
|
9088
|
+
);
|
|
9089
|
+
const handleDragLeave = React.useCallback((e3) => {
|
|
9090
|
+
if (!e3.currentTarget.contains(e3.relatedTarget)) {
|
|
9091
|
+
setIsDraggingOver(false);
|
|
9092
|
+
setPreview(null);
|
|
9093
|
+
}
|
|
9094
|
+
}, []);
|
|
9095
|
+
const handleDrop = React.useCallback(
|
|
9096
|
+
async (e3) => {
|
|
9097
|
+
if (!enabled) return;
|
|
9098
|
+
e3.preventDefault();
|
|
9099
|
+
e3.stopPropagation();
|
|
9100
|
+
setIsDraggingOver(false);
|
|
9101
|
+
setPreview(null);
|
|
9102
|
+
const pos = computePosition(e3.clientX, e3.clientY);
|
|
9103
|
+
if (!pos || pos.trackIndex < 0) return;
|
|
9104
|
+
const track = tracks[pos.trackIndex] ?? null;
|
|
9105
|
+
if (e3.dataTransfer.types.includes(TIMELINE_DROP_MEDIA_TYPE)) {
|
|
9106
|
+
try {
|
|
9107
|
+
const data = JSON.parse(
|
|
9108
|
+
e3.dataTransfer.getData(TIMELINE_DROP_MEDIA_TYPE) || "{}"
|
|
9109
|
+
);
|
|
9110
|
+
if (data.type && data.url) {
|
|
9111
|
+
await onDrop({
|
|
9112
|
+
track,
|
|
9113
|
+
timeSec: pos.timeSec,
|
|
9114
|
+
type: data.type,
|
|
9115
|
+
url: data.url
|
|
9116
|
+
});
|
|
9117
|
+
}
|
|
9118
|
+
} catch {
|
|
9119
|
+
}
|
|
9120
|
+
return;
|
|
9121
|
+
}
|
|
9122
|
+
const files = Array.from(e3.dataTransfer.files || []);
|
|
9123
|
+
for (const file of files) {
|
|
9124
|
+
const type = getAssetTypeFromFile(file);
|
|
9125
|
+
if (!type) continue;
|
|
9126
|
+
const blobUrl = URL.createObjectURL(file);
|
|
9127
|
+
try {
|
|
9128
|
+
await onDrop({
|
|
9129
|
+
track,
|
|
9130
|
+
timeSec: pos.timeSec,
|
|
9131
|
+
type,
|
|
9132
|
+
url: blobUrl
|
|
9133
|
+
});
|
|
9134
|
+
} finally {
|
|
9135
|
+
URL.revokeObjectURL(blobUrl);
|
|
9136
|
+
}
|
|
9137
|
+
break;
|
|
9138
|
+
}
|
|
9139
|
+
},
|
|
9140
|
+
[enabled, computePosition, tracks, onDrop]
|
|
9141
|
+
);
|
|
9142
|
+
return { preview, isDraggingOver, handleDragOver, handleDragLeave, handleDrop };
|
|
9143
|
+
}
|
|
9144
|
+
function createElementFromDrop(type, blobUrl, parentSize) {
|
|
9145
|
+
switch (type) {
|
|
9146
|
+
case "video":
|
|
9147
|
+
return new timeline.VideoElement(blobUrl, parentSize);
|
|
9148
|
+
case "audio":
|
|
9149
|
+
return new timeline.AudioElement(blobUrl);
|
|
9150
|
+
case "image":
|
|
9151
|
+
return new timeline.ImageElement(blobUrl, parentSize);
|
|
9152
|
+
default:
|
|
9153
|
+
throw new Error(`Unknown asset type: ${type}`);
|
|
9154
|
+
}
|
|
9155
|
+
}
|
|
9156
|
+
const usePlayerManager = ({
|
|
9157
|
+
videoProps,
|
|
9158
|
+
canvasConfig
|
|
9159
|
+
}) => {
|
|
9160
|
+
const [projectData, setProjectData] = React.useState(null);
|
|
9161
|
+
const {
|
|
9162
|
+
timelineAction,
|
|
9163
|
+
setTimelineAction,
|
|
9164
|
+
setSelectedItem,
|
|
9165
|
+
editor,
|
|
9166
|
+
changeLog,
|
|
9167
|
+
videoResolution
|
|
9168
|
+
} = timeline.useTimelineContext();
|
|
9169
|
+
const { getCurrentTime } = livePlayer.useLivePlayerContext();
|
|
9170
|
+
const currentChangeLog = React.useRef(changeLog);
|
|
9171
|
+
const prevSeekTime = React.useRef(0);
|
|
9172
|
+
const [playerUpdating, setPlayerUpdating] = React.useState(false);
|
|
9173
|
+
const handleCanvasReady = (_canvas) => {
|
|
9174
|
+
};
|
|
9175
|
+
const handleCanvasOperation = (operation, data) => {
|
|
9176
|
+
var _a;
|
|
9177
|
+
if (operation === CANVAS_OPERATIONS.ADDED_NEW_ELEMENT) {
|
|
9178
|
+
if (data == null ? void 0 : data.element) {
|
|
9179
|
+
setSelectedItem(data.element);
|
|
9180
|
+
}
|
|
9181
|
+
return;
|
|
9182
|
+
}
|
|
9183
|
+
if (operation === CANVAS_OPERATIONS.Z_ORDER_CHANGED) {
|
|
9184
|
+
const { elementId, direction } = data ?? {};
|
|
9185
|
+
if (!elementId || !direction) return;
|
|
9186
|
+
const tracks = ((_a = editor.getTimelineData()) == null ? void 0 : _a.tracks) ?? [];
|
|
9187
|
+
const trackIndex = tracks.findIndex(
|
|
9188
|
+
(t2) => t2.getElements().some((el) => el.getId() === elementId)
|
|
9189
|
+
);
|
|
9190
|
+
if (trackIndex < 0) return;
|
|
9191
|
+
const track = tracks[trackIndex];
|
|
9192
|
+
const reordered = [...tracks];
|
|
9193
|
+
if (direction === "front") {
|
|
9194
|
+
reordered.splice(trackIndex, 1);
|
|
9195
|
+
reordered.push(track);
|
|
9196
|
+
} else if (direction === "back") {
|
|
9197
|
+
reordered.splice(trackIndex, 1);
|
|
9198
|
+
reordered.unshift(track);
|
|
9199
|
+
} else if (direction === "forward" && trackIndex < reordered.length - 1) {
|
|
9200
|
+
[reordered[trackIndex], reordered[trackIndex + 1]] = [reordered[trackIndex + 1], reordered[trackIndex]];
|
|
9201
|
+
} else if (direction === "backward" && trackIndex > 0) {
|
|
9202
|
+
[reordered[trackIndex - 1], reordered[trackIndex]] = [reordered[trackIndex], reordered[trackIndex - 1]];
|
|
9203
|
+
} else {
|
|
9204
|
+
return;
|
|
9205
|
+
}
|
|
9206
|
+
editor.reorderTracks(reordered);
|
|
9207
|
+
currentChangeLog.current = currentChangeLog.current + 1;
|
|
9208
|
+
return;
|
|
9209
|
+
}
|
|
9210
|
+
if (operation === CANVAS_OPERATIONS.CAPTION_PROPS_UPDATED) {
|
|
9211
|
+
const captionsTrack = editor.getCaptionsTrack();
|
|
9212
|
+
captionsTrack == null ? void 0 : captionsTrack.setProps(data.props);
|
|
9213
|
+
setSelectedItem(data.element);
|
|
9214
|
+
editor.refresh();
|
|
9215
|
+
} else if (operation === CANVAS_OPERATIONS.WATERMARK_UPDATED) {
|
|
9216
|
+
const w2 = editor.getWatermark();
|
|
9217
|
+
if (w2 && data) {
|
|
9218
|
+
if (data.position) w2.setPosition(data.position);
|
|
9219
|
+
if (data.rotation != null) w2.setRotation(data.rotation);
|
|
9220
|
+
if (data.opacity != null) w2.setOpacity(data.opacity);
|
|
9221
|
+
if (data.props) w2.setProps(data.props);
|
|
9222
|
+
editor.setWatermark(w2);
|
|
9223
|
+
currentChangeLog.current = currentChangeLog.current + 1;
|
|
9224
|
+
}
|
|
9225
|
+
} else {
|
|
9226
|
+
const element = timeline.ElementDeserializer.fromJSON(data);
|
|
9227
|
+
switch (operation) {
|
|
9228
|
+
case CANVAS_OPERATIONS.ITEM_SELECTED:
|
|
9229
|
+
setSelectedItem(element);
|
|
9230
|
+
break;
|
|
9231
|
+
case CANVAS_OPERATIONS.ITEM_UPDATED:
|
|
9232
|
+
if (element) {
|
|
9233
|
+
const updatedElement = editor.updateElement(element);
|
|
9234
|
+
currentChangeLog.current = currentChangeLog.current + 1;
|
|
9235
|
+
setSelectedItem(updatedElement);
|
|
9236
|
+
}
|
|
9237
|
+
break;
|
|
9238
|
+
}
|
|
9239
|
+
}
|
|
9240
|
+
};
|
|
9241
|
+
const handleDropOnCanvas = React.useCallback(
|
|
9242
|
+
async (payload) => {
|
|
9243
|
+
const { type, url, canvasX, canvasY } = payload;
|
|
9244
|
+
const element = createElementFromDrop(type, url, videoResolution);
|
|
9245
|
+
const currentTime = getCurrentTime();
|
|
9246
|
+
element.setStart(currentTime);
|
|
9247
|
+
const newTrack = editor.addTrack(`Track_${Date.now()}`);
|
|
9248
|
+
const result = await editor.addElementToTrack(newTrack, element);
|
|
9249
|
+
if (result) {
|
|
9250
|
+
setSelectedItem(element);
|
|
9251
|
+
currentChangeLog.current = currentChangeLog.current + 1;
|
|
9252
|
+
editor.refresh();
|
|
9253
|
+
handleCanvasOperation(CANVAS_OPERATIONS.ADDED_NEW_ELEMENT, {
|
|
9254
|
+
element,
|
|
9255
|
+
canvasPosition: canvasX != null && canvasY != null ? { x: canvasX, y: canvasY } : void 0
|
|
9256
|
+
});
|
|
9257
|
+
}
|
|
9258
|
+
},
|
|
9259
|
+
[editor, videoResolution, getCurrentTime, setSelectedItem]
|
|
9260
|
+
);
|
|
9261
|
+
const {
|
|
9262
|
+
twickCanvas,
|
|
9263
|
+
buildCanvas,
|
|
9264
|
+
setCanvasElements,
|
|
9265
|
+
bringToFront,
|
|
9266
|
+
sendToBack,
|
|
9267
|
+
bringForward,
|
|
9268
|
+
sendBackward
|
|
9269
|
+
} = useTwickCanvas({
|
|
9270
|
+
onCanvasReady: handleCanvasReady,
|
|
9271
|
+
onCanvasOperation: handleCanvasOperation,
|
|
9272
|
+
enableShiftAxisLock: (canvasConfig == null ? void 0 : canvasConfig.enableShiftAxisLock) ?? false
|
|
9273
|
+
});
|
|
9274
|
+
const updateCanvas = (seekTime) => {
|
|
9275
|
+
var _a;
|
|
9276
|
+
if (changeLog === currentChangeLog.current && seekTime === prevSeekTime.current) {
|
|
9277
|
+
return;
|
|
9278
|
+
}
|
|
9279
|
+
prevSeekTime.current = seekTime;
|
|
9280
|
+
const elements = timeline.getCurrentElements(
|
|
9281
|
+
seekTime,
|
|
9282
|
+
((_a = editor.getTimelineData()) == null ? void 0 : _a.tracks) ?? []
|
|
9283
|
+
);
|
|
9284
|
+
let captionProps = {};
|
|
9285
|
+
(elements || []).forEach((element) => {
|
|
9286
|
+
if (element instanceof timeline.CaptionElement) {
|
|
9287
|
+
const track = editor.getTrackById(element.getTrackId());
|
|
9288
|
+
captionProps = (track == null ? void 0 : track.getProps()) ?? {};
|
|
9289
|
+
}
|
|
9290
|
+
});
|
|
9291
|
+
const watermark = editor.getWatermark();
|
|
9292
|
+
let watermarkElement;
|
|
9293
|
+
if (watermark) {
|
|
9294
|
+
const position = watermark.getPosition();
|
|
9295
|
+
watermarkElement = {
|
|
9296
|
+
id: watermark.getId(),
|
|
9297
|
+
type: watermark.getType(),
|
|
9298
|
+
props: {
|
|
9299
|
+
...watermark.getProps() || {},
|
|
9300
|
+
x: (position == null ? void 0 : position.x) ?? 0,
|
|
9301
|
+
y: (position == null ? void 0 : position.y) ?? 0,
|
|
9302
|
+
rotation: watermark.getRotation() ?? 0,
|
|
9303
|
+
opacity: watermark.getOpacity() ?? 1
|
|
9304
|
+
}
|
|
9305
|
+
};
|
|
9306
|
+
}
|
|
9307
|
+
setCanvasElements({
|
|
9308
|
+
elements,
|
|
9309
|
+
watermark: watermarkElement,
|
|
9310
|
+
seekTime,
|
|
9311
|
+
captionProps,
|
|
9312
|
+
cleanAndAdd: true,
|
|
9313
|
+
lockAspectRatio: canvasConfig == null ? void 0 : canvasConfig.lockAspectRatio
|
|
9314
|
+
});
|
|
9315
|
+
currentChangeLog.current = changeLog;
|
|
9316
|
+
};
|
|
9317
|
+
const onPlayerUpdate = (event) => {
|
|
9318
|
+
var _a;
|
|
9319
|
+
if (((_a = event == null ? void 0 : event.detail) == null ? void 0 : _a.status) === "ready") {
|
|
9320
|
+
setPlayerUpdating(false);
|
|
9321
|
+
setTimelineAction(timeline.TIMELINE_ACTION.ON_PLAYER_UPDATED, null);
|
|
9322
|
+
}
|
|
9323
|
+
};
|
|
9324
|
+
const deleteElement = (elementId) => {
|
|
9325
|
+
var _a;
|
|
9326
|
+
const tracks = ((_a = editor.getTimelineData()) == null ? void 0 : _a.tracks) ?? [];
|
|
9327
|
+
for (const track of tracks) {
|
|
9328
|
+
const element = track.getElementById(elementId);
|
|
9329
|
+
if (element) {
|
|
9330
|
+
editor.removeElement(element);
|
|
9331
|
+
currentChangeLog.current = currentChangeLog.current + 1;
|
|
9332
|
+
setSelectedItem(null);
|
|
9333
|
+
updateCanvas(getCurrentTime());
|
|
9334
|
+
return;
|
|
9335
|
+
}
|
|
9336
|
+
}
|
|
9337
|
+
};
|
|
9338
|
+
React.useEffect(() => {
|
|
9339
|
+
var _a, _b, _c, _d, _e2;
|
|
9340
|
+
switch (timelineAction.type) {
|
|
9341
|
+
case timeline.TIMELINE_ACTION.UPDATE_PLAYER_DATA:
|
|
9342
|
+
if (videoProps) {
|
|
9343
|
+
if (((_a = timelineAction.payload) == null ? void 0 : _a.forceUpdate) || editor.getLatestVersion() !== ((_b = projectData == null ? void 0 : projectData.input) == null ? void 0 : _b.version)) {
|
|
9344
|
+
setPlayerUpdating(true);
|
|
9345
|
+
const _latestProjectData = {
|
|
9346
|
+
input: {
|
|
9347
|
+
properties: videoProps,
|
|
9348
|
+
tracks: ((_c = timelineAction.payload) == null ? void 0 : _c.tracks) ?? [],
|
|
9349
|
+
version: ((_d = timelineAction.payload) == null ? void 0 : _d.version) ?? 0
|
|
9350
|
+
}
|
|
9351
|
+
};
|
|
9352
|
+
setProjectData(_latestProjectData);
|
|
9353
|
+
if (((_e2 = timelineAction.payload) == null ? void 0 : _e2.version) === 1) {
|
|
9354
|
+
setTimeout(() => {
|
|
9355
|
+
setPlayerUpdating(false);
|
|
9356
|
+
});
|
|
9357
|
+
}
|
|
9358
|
+
} else {
|
|
9359
|
+
setTimelineAction(timeline.TIMELINE_ACTION.ON_PLAYER_UPDATED, null);
|
|
9360
|
+
}
|
|
9361
|
+
}
|
|
9362
|
+
break;
|
|
9363
|
+
}
|
|
9364
|
+
}, [timelineAction]);
|
|
9365
|
+
return {
|
|
9366
|
+
twickCanvas,
|
|
9367
|
+
projectData,
|
|
9368
|
+
updateCanvas,
|
|
9369
|
+
buildCanvas,
|
|
9370
|
+
onPlayerUpdate,
|
|
9371
|
+
playerUpdating,
|
|
9372
|
+
handleDropOnCanvas,
|
|
9373
|
+
bringToFront,
|
|
9374
|
+
sendToBack,
|
|
9375
|
+
bringForward,
|
|
9376
|
+
sendBackward,
|
|
9377
|
+
deleteElement
|
|
9378
|
+
};
|
|
9379
|
+
};
|
|
9380
|
+
function useCanvasDrop({
|
|
9381
|
+
containerRef,
|
|
9382
|
+
videoSize,
|
|
9383
|
+
onDrop,
|
|
9384
|
+
enabled = true
|
|
9385
|
+
}) {
|
|
9386
|
+
const handleDragOver = React.useCallback(
|
|
9387
|
+
(e3) => {
|
|
9388
|
+
if (!enabled) return;
|
|
9389
|
+
e3.preventDefault();
|
|
9390
|
+
e3.stopPropagation();
|
|
9391
|
+
const hasFiles = e3.dataTransfer.types.includes("Files");
|
|
9392
|
+
const hasPanelMedia = e3.dataTransfer.types.includes(TIMELINE_DROP_MEDIA_TYPE);
|
|
9393
|
+
if (!hasFiles && !hasPanelMedia) return;
|
|
9394
|
+
e3.dataTransfer.dropEffect = "copy";
|
|
9395
|
+
},
|
|
9396
|
+
[enabled]
|
|
9397
|
+
);
|
|
9398
|
+
const handleDrop = React.useCallback(
|
|
9399
|
+
async (e3) => {
|
|
9400
|
+
if (!enabled || !containerRef.current) return;
|
|
9401
|
+
e3.preventDefault();
|
|
9402
|
+
e3.stopPropagation();
|
|
9403
|
+
let type = null;
|
|
9404
|
+
let url = null;
|
|
9405
|
+
if (e3.dataTransfer.types.includes(TIMELINE_DROP_MEDIA_TYPE)) {
|
|
9406
|
+
try {
|
|
9407
|
+
const data = JSON.parse(
|
|
9408
|
+
e3.dataTransfer.getData(TIMELINE_DROP_MEDIA_TYPE) || "{}"
|
|
9409
|
+
);
|
|
9410
|
+
if (data.type && data.url) {
|
|
9411
|
+
type = data.type;
|
|
9412
|
+
url = data.url;
|
|
9413
|
+
}
|
|
9414
|
+
} catch {
|
|
9415
|
+
}
|
|
9416
|
+
}
|
|
9417
|
+
if (!type || !url) {
|
|
9418
|
+
const files = Array.from(e3.dataTransfer.files || []);
|
|
9419
|
+
for (const file of files) {
|
|
9420
|
+
const detectedType = getAssetTypeFromFile(file);
|
|
9421
|
+
if (detectedType) {
|
|
9422
|
+
type = detectedType;
|
|
9423
|
+
url = URL.createObjectURL(file);
|
|
9424
|
+
try {
|
|
9425
|
+
await onDrop({
|
|
9426
|
+
type,
|
|
9427
|
+
url,
|
|
9428
|
+
canvasX: getCanvasX(e3, containerRef.current, videoSize),
|
|
9429
|
+
canvasY: getCanvasY(e3, containerRef.current, videoSize)
|
|
9430
|
+
});
|
|
9431
|
+
} finally {
|
|
9432
|
+
URL.revokeObjectURL(url);
|
|
9433
|
+
}
|
|
9434
|
+
return;
|
|
9435
|
+
}
|
|
9436
|
+
}
|
|
9437
|
+
return;
|
|
9438
|
+
}
|
|
9439
|
+
await onDrop({
|
|
9440
|
+
type,
|
|
9441
|
+
url,
|
|
9442
|
+
canvasX: getCanvasX(e3, containerRef.current, videoSize),
|
|
9443
|
+
canvasY: getCanvasY(e3, containerRef.current, videoSize)
|
|
9444
|
+
});
|
|
9445
|
+
},
|
|
9446
|
+
[enabled, containerRef, videoSize, onDrop]
|
|
9447
|
+
);
|
|
9448
|
+
const handleDragLeave = React.useCallback((e3) => {
|
|
9449
|
+
if (!e3.currentTarget.contains(e3.relatedTarget)) ;
|
|
9450
|
+
}, []);
|
|
9451
|
+
return { handleDragOver, handleDragLeave, handleDrop };
|
|
9452
|
+
}
|
|
9453
|
+
function getCanvasX(e3, container, videoSize) {
|
|
9454
|
+
const rect = container.getBoundingClientRect();
|
|
9455
|
+
const relX = (e3.clientX - rect.left) / rect.width;
|
|
9456
|
+
return Math.max(0, Math.min(videoSize.width, relX * videoSize.width));
|
|
9457
|
+
}
|
|
9458
|
+
function getCanvasY(e3, container, videoSize) {
|
|
9459
|
+
const rect = container.getBoundingClientRect();
|
|
9460
|
+
const relY = (e3.clientY - rect.top) / rect.height;
|
|
9461
|
+
return Math.max(0, Math.min(videoSize.height, relY * videoSize.height));
|
|
9462
|
+
}
|
|
9463
|
+
const CanvasContextMenu = ({
|
|
9464
|
+
x: x2,
|
|
9465
|
+
y: y2,
|
|
9466
|
+
elementId,
|
|
9467
|
+
onBringToFront,
|
|
9468
|
+
onSendToBack,
|
|
9469
|
+
onBringForward,
|
|
9470
|
+
onSendBackward,
|
|
9471
|
+
onDelete,
|
|
9472
|
+
onClose
|
|
9473
|
+
}) => {
|
|
9474
|
+
const menuRef = React.useRef(null);
|
|
9475
|
+
React.useEffect(() => {
|
|
9476
|
+
const handleClickOutside = (e3) => {
|
|
9477
|
+
if (menuRef.current && !menuRef.current.contains(e3.target)) {
|
|
9478
|
+
onClose();
|
|
9479
|
+
}
|
|
9480
|
+
};
|
|
9481
|
+
const handleEscape = (e3) => {
|
|
9482
|
+
if (e3.key === "Escape") onClose();
|
|
9483
|
+
};
|
|
9484
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
9485
|
+
document.addEventListener("keydown", handleEscape);
|
|
9486
|
+
return () => {
|
|
9487
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
9488
|
+
document.removeEventListener("keydown", handleEscape);
|
|
9489
|
+
};
|
|
9490
|
+
}, [onClose]);
|
|
9491
|
+
const handleAction = (fn2) => {
|
|
9492
|
+
fn2(elementId);
|
|
9493
|
+
onClose();
|
|
9494
|
+
};
|
|
9495
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9496
|
+
"div",
|
|
9497
|
+
{
|
|
9498
|
+
ref: menuRef,
|
|
9499
|
+
className: "twick-canvas-context-menu",
|
|
9500
|
+
style: { left: x2, top: y2 },
|
|
9501
|
+
role: "menu",
|
|
9502
|
+
children: [
|
|
9503
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9504
|
+
"button",
|
|
9505
|
+
{
|
|
9506
|
+
type: "button",
|
|
9507
|
+
className: "twick-canvas-context-menu-item",
|
|
9508
|
+
onClick: () => handleAction(onBringToFront),
|
|
9509
|
+
role: "menuitem",
|
|
9510
|
+
children: "Bring to Front"
|
|
9511
|
+
}
|
|
9512
|
+
),
|
|
9513
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9514
|
+
"button",
|
|
9515
|
+
{
|
|
9516
|
+
type: "button",
|
|
9517
|
+
className: "twick-canvas-context-menu-item",
|
|
9518
|
+
onClick: () => handleAction(onBringForward),
|
|
9519
|
+
role: "menuitem",
|
|
9520
|
+
children: "Bring Forward"
|
|
9521
|
+
}
|
|
9522
|
+
),
|
|
9523
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9524
|
+
"button",
|
|
9525
|
+
{
|
|
9526
|
+
type: "button",
|
|
9527
|
+
className: "twick-canvas-context-menu-item",
|
|
9528
|
+
onClick: () => handleAction(onSendBackward),
|
|
9529
|
+
role: "menuitem",
|
|
9530
|
+
children: "Send Backward"
|
|
9531
|
+
}
|
|
9532
|
+
),
|
|
9533
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9534
|
+
"button",
|
|
9535
|
+
{
|
|
9536
|
+
type: "button",
|
|
9537
|
+
className: "twick-canvas-context-menu-item",
|
|
9538
|
+
onClick: () => handleAction(onSendToBack),
|
|
9539
|
+
role: "menuitem",
|
|
9540
|
+
children: "Send to Back"
|
|
9541
|
+
}
|
|
9542
|
+
),
|
|
9543
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "twick-canvas-context-menu-separator", role: "separator" }),
|
|
9544
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9545
|
+
"button",
|
|
9546
|
+
{
|
|
9547
|
+
type: "button",
|
|
9548
|
+
className: "twick-canvas-context-menu-item twick-canvas-context-menu-item-danger",
|
|
9549
|
+
onClick: () => handleAction(onDelete),
|
|
9550
|
+
role: "menuitem",
|
|
9551
|
+
children: "Delete"
|
|
9552
|
+
}
|
|
9553
|
+
)
|
|
9554
|
+
]
|
|
9555
|
+
}
|
|
9556
|
+
);
|
|
9557
|
+
};
|
|
9558
|
+
const PlayerManager = ({
|
|
9559
|
+
videoProps,
|
|
9560
|
+
playerProps,
|
|
9561
|
+
canvasMode,
|
|
9562
|
+
canvasConfig
|
|
9563
|
+
}) => {
|
|
9564
|
+
const containerRef = React.useRef(null);
|
|
9565
|
+
const canvasRef = React.useRef(null);
|
|
9566
|
+
const durationRef = React.useRef(0);
|
|
9567
|
+
const { changeLog } = timeline.useTimelineContext();
|
|
9568
|
+
const {
|
|
9569
|
+
playerState,
|
|
9570
|
+
playerVolume,
|
|
9571
|
+
seekTime,
|
|
9572
|
+
setPlayerState,
|
|
9573
|
+
setCurrentTime
|
|
9574
|
+
} = livePlayer.useLivePlayerContext();
|
|
9575
|
+
const {
|
|
9576
|
+
twickCanvas,
|
|
9577
|
+
projectData,
|
|
9578
|
+
updateCanvas,
|
|
9579
|
+
playerUpdating,
|
|
9580
|
+
onPlayerUpdate,
|
|
9581
|
+
buildCanvas,
|
|
9582
|
+
handleDropOnCanvas,
|
|
9583
|
+
bringToFront,
|
|
9584
|
+
sendToBack,
|
|
9585
|
+
bringForward,
|
|
9586
|
+
sendBackward,
|
|
9587
|
+
deleteElement
|
|
9588
|
+
} = usePlayerManager({ videoProps, canvasConfig });
|
|
9589
|
+
const [contextMenu, setContextMenu] = React.useState(null);
|
|
9590
|
+
const closeContextMenu = React.useCallback(() => setContextMenu(null), []);
|
|
9591
|
+
const { handleDragOver, handleDragLeave, handleDrop } = useCanvasDrop({
|
|
9592
|
+
containerRef,
|
|
9593
|
+
videoSize: { width: videoProps.width, height: videoProps.height },
|
|
9594
|
+
onDrop: handleDropOnCanvas,
|
|
9595
|
+
enabled: !!handleDropOnCanvas && canvasMode
|
|
9596
|
+
});
|
|
9597
|
+
React.useEffect(() => {
|
|
9598
|
+
const container = containerRef.current;
|
|
9599
|
+
const canvasSize = {
|
|
9600
|
+
width: container == null ? void 0 : container.clientWidth,
|
|
9601
|
+
height: container == null ? void 0 : container.clientHeight
|
|
9602
|
+
};
|
|
9603
|
+
buildCanvas({
|
|
9604
|
+
backgroundColor: videoProps.backgroundColor,
|
|
9605
|
+
videoSize: {
|
|
9606
|
+
width: videoProps.width,
|
|
9607
|
+
height: videoProps.height
|
|
9608
|
+
},
|
|
9609
|
+
canvasSize,
|
|
9610
|
+
canvasRef: canvasRef.current
|
|
9611
|
+
});
|
|
9612
|
+
}, [videoProps]);
|
|
9613
|
+
React.useEffect(() => {
|
|
9614
|
+
if (twickCanvas && playerState === livePlayer.PLAYER_STATE.PAUSED) {
|
|
9615
|
+
updateCanvas(seekTime);
|
|
9616
|
+
}
|
|
9617
|
+
}, [twickCanvas, playerState, seekTime, changeLog]);
|
|
9618
|
+
React.useEffect(() => {
|
|
9619
|
+
if (!twickCanvas || !canvasMode) return;
|
|
9620
|
+
const onSelectionCreated = (e3) => {
|
|
9621
|
+
var _a, _b;
|
|
9622
|
+
const ev = e3 == null ? void 0 : e3.e;
|
|
9623
|
+
if (!ev) return;
|
|
9624
|
+
const id2 = (_b = (_a = e3.target) == null ? void 0 : _a.get) == null ? void 0 : _b.call(_a, "id");
|
|
9625
|
+
if (id2) {
|
|
9626
|
+
setContextMenu({ x: ev.clientX, y: ev.clientY, elementId: id2 });
|
|
9627
|
+
}
|
|
9628
|
+
};
|
|
9629
|
+
twickCanvas.on("contextmenu", onSelectionCreated);
|
|
9630
|
+
return () => {
|
|
9631
|
+
twickCanvas.off("contextmenu", onSelectionCreated);
|
|
9632
|
+
};
|
|
9633
|
+
}, [twickCanvas, canvasMode]);
|
|
9634
|
+
const handleTimeUpdate = (time2) => {
|
|
9635
|
+
if (durationRef.current && time2 >= durationRef.current) {
|
|
9636
|
+
setCurrentTime(0);
|
|
9637
|
+
setPlayerState(livePlayer.PLAYER_STATE.PAUSED);
|
|
9638
|
+
} else {
|
|
9639
|
+
setCurrentTime(time2);
|
|
9640
|
+
}
|
|
9641
|
+
};
|
|
9642
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9643
|
+
"div",
|
|
9644
|
+
{
|
|
9645
|
+
className: "twick-editor-container",
|
|
9646
|
+
style: {
|
|
9647
|
+
aspectRatio: `${videoProps.width}/${videoProps.height}`
|
|
9648
|
+
},
|
|
9649
|
+
children: [
|
|
9650
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9651
|
+
"div",
|
|
9652
|
+
{
|
|
9653
|
+
className: "twick-editor-loading-overlay",
|
|
9654
|
+
style: {
|
|
9655
|
+
display: playerUpdating ? "flex" : "none"
|
|
9656
|
+
},
|
|
9657
|
+
children: playerUpdating ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twick-editor-loading-spinner" }) : null
|
|
9658
|
+
}
|
|
9659
|
+
),
|
|
9660
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9661
|
+
livePlayer.LivePlayer,
|
|
9662
|
+
{
|
|
9663
|
+
seekTime,
|
|
9664
|
+
projectData,
|
|
9665
|
+
quality: (playerProps == null ? void 0 : playerProps.quality) || 1,
|
|
9666
|
+
videoSize: {
|
|
9667
|
+
width: videoProps.width,
|
|
9668
|
+
height: videoProps.height
|
|
9669
|
+
},
|
|
9670
|
+
onPlayerUpdate,
|
|
9671
|
+
containerStyle: {
|
|
9672
|
+
opacity: canvasMode ? playerState === livePlayer.PLAYER_STATE.PAUSED ? 0 : 1 : 1
|
|
9673
|
+
},
|
|
9674
|
+
onTimeUpdate: handleTimeUpdate,
|
|
9675
|
+
volume: playerVolume,
|
|
9676
|
+
onDurationChange: (duration) => {
|
|
9677
|
+
durationRef.current = duration;
|
|
9678
|
+
},
|
|
9679
|
+
playing: playerState === livePlayer.PLAYER_STATE.PLAYING
|
|
9680
|
+
}
|
|
9681
|
+
),
|
|
9682
|
+
canvasMode && /* @__PURE__ */ jsxRuntime.jsx(
|
|
9683
|
+
"div",
|
|
9684
|
+
{
|
|
9685
|
+
ref: containerRef,
|
|
9686
|
+
className: "twick-editor-canvas-container",
|
|
9687
|
+
style: {
|
|
9688
|
+
opacity: playerState === livePlayer.PLAYER_STATE.PAUSED ? 1 : 0
|
|
9689
|
+
},
|
|
9690
|
+
onDragOver: handleDragOver,
|
|
9691
|
+
onDragLeave: handleDragLeave,
|
|
9692
|
+
onDrop: handleDrop,
|
|
9693
|
+
onContextMenu: (e3) => e3.preventDefault(),
|
|
9694
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("canvas", { ref: canvasRef, className: "twick-editor-canvas" })
|
|
9695
|
+
}
|
|
9696
|
+
),
|
|
9697
|
+
contextMenu && /* @__PURE__ */ jsxRuntime.jsx(
|
|
9698
|
+
CanvasContextMenu,
|
|
9699
|
+
{
|
|
9700
|
+
x: contextMenu.x,
|
|
9701
|
+
y: contextMenu.y,
|
|
9702
|
+
elementId: contextMenu.elementId,
|
|
9703
|
+
onBringToFront: bringToFront,
|
|
9704
|
+
onSendToBack: sendToBack,
|
|
9705
|
+
onBringForward: bringForward,
|
|
9706
|
+
onSendBackward: sendBackward,
|
|
9707
|
+
onDelete: deleteElement,
|
|
9708
|
+
onClose: closeContextMenu
|
|
9709
|
+
}
|
|
9710
|
+
)
|
|
9711
|
+
]
|
|
9712
|
+
}
|
|
9713
|
+
);
|
|
9714
|
+
};
|
|
9715
|
+
function clamp$1(v2, min, max) {
|
|
9716
|
+
return Math.max(min, Math.min(v2, max));
|
|
9717
|
+
}
|
|
9718
|
+
const V = {
|
|
9719
|
+
toVector(v2, fallback) {
|
|
9720
|
+
if (v2 === void 0) v2 = fallback;
|
|
9721
|
+
return Array.isArray(v2) ? v2 : [v2, v2];
|
|
8427
9722
|
},
|
|
8428
9723
|
add(v1, v2) {
|
|
8429
9724
|
return [v1[0] + v2[0], v1[1] + v2[1]];
|
|
@@ -9753,7 +11048,8 @@ function SeekTrack({
|
|
|
9753
11048
|
zoom = 1,
|
|
9754
11049
|
onSeek,
|
|
9755
11050
|
timelineCount = 0,
|
|
9756
|
-
timelineTickConfigs
|
|
11051
|
+
timelineTickConfigs,
|
|
11052
|
+
onPlayheadUpdate
|
|
9757
11053
|
}) {
|
|
9758
11054
|
const containerRef = React.useRef(null);
|
|
9759
11055
|
const [isDragging2, setIsDragging] = React.useState(false);
|
|
@@ -9765,6 +11061,12 @@ function SeekTrack({
|
|
|
9765
11061
|
const position = isDragging2 && dragPosition !== null ? dragPosition : currentTime * pixelsPerSecond;
|
|
9766
11062
|
return Math.max(0, position);
|
|
9767
11063
|
}, [isDragging2, dragPosition, currentTime, pixelsPerSecond]);
|
|
11064
|
+
React.useEffect(() => {
|
|
11065
|
+
onPlayheadUpdate == null ? void 0 : onPlayheadUpdate({
|
|
11066
|
+
positionPx: seekPosition,
|
|
11067
|
+
isDragging: isDragging2
|
|
11068
|
+
});
|
|
11069
|
+
}, [seekPosition, isDragging2, onPlayheadUpdate]);
|
|
9768
11070
|
const { majorIntervalSec, minorIntervalSec } = React.useMemo(() => {
|
|
9769
11071
|
if (timelineTickConfigs && timelineTickConfigs.length > 0) {
|
|
9770
11072
|
const sortedConfigs = [...timelineTickConfigs].sort((a2, b2) => a2.durationThreshold - b2.durationThreshold);
|
|
@@ -9933,7 +11235,7 @@ function SeekTrack({
|
|
|
9933
11235
|
transform: `translateX(${seekPosition}px)`,
|
|
9934
11236
|
top: 0,
|
|
9935
11237
|
touchAction: "none",
|
|
9936
|
-
transition: isDragging2 ? "none" : "transform 0.
|
|
11238
|
+
transition: isDragging2 ? "none" : "transform 150ms cubic-bezier(0.4, 0, 0.2, 1)",
|
|
9937
11239
|
willChange: isDragging2 ? "transform" : "auto"
|
|
9938
11240
|
},
|
|
9939
11241
|
children: [
|
|
@@ -9957,7 +11259,8 @@ const SeekControl = ({
|
|
|
9957
11259
|
zoom,
|
|
9958
11260
|
timelineCount,
|
|
9959
11261
|
onSeek,
|
|
9960
|
-
timelineTickConfigs
|
|
11262
|
+
timelineTickConfigs,
|
|
11263
|
+
onPlayheadUpdate
|
|
9961
11264
|
}) => {
|
|
9962
11265
|
const { currentTime } = livePlayer.useLivePlayerContext();
|
|
9963
11266
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -9968,7 +11271,8 @@ const SeekControl = ({
|
|
|
9968
11271
|
zoom,
|
|
9969
11272
|
onSeek,
|
|
9970
11273
|
timelineCount,
|
|
9971
|
-
timelineTickConfigs
|
|
11274
|
+
timelineTickConfigs,
|
|
11275
|
+
onPlayheadUpdate
|
|
9972
11276
|
}
|
|
9973
11277
|
);
|
|
9974
11278
|
};
|
|
@@ -10077,7 +11381,21 @@ const createLucideIcon = (iconName, iconNode) => {
|
|
|
10077
11381
|
* This source code is licensed under the ISC license.
|
|
10078
11382
|
* See the LICENSE file in the root directory of this source tree.
|
|
10079
11383
|
*/
|
|
10080
|
-
const __iconNode$
|
|
11384
|
+
const __iconNode$e = [
|
|
11385
|
+
["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
|
|
11386
|
+
["line", { x1: "22", x2: "18", y1: "12", y2: "12", key: "l9bcsi" }],
|
|
11387
|
+
["line", { x1: "6", x2: "2", y1: "12", y2: "12", key: "13hhkx" }],
|
|
11388
|
+
["line", { x1: "12", x2: "12", y1: "6", y2: "2", key: "10w3f3" }],
|
|
11389
|
+
["line", { x1: "12", x2: "12", y1: "22", y2: "18", key: "15g9kq" }]
|
|
11390
|
+
];
|
|
11391
|
+
const Crosshair = createLucideIcon("crosshair", __iconNode$e);
|
|
11392
|
+
/**
|
|
11393
|
+
* @license lucide-react v0.511.0 - ISC
|
|
11394
|
+
*
|
|
11395
|
+
* This source code is licensed under the ISC license.
|
|
11396
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
11397
|
+
*/
|
|
11398
|
+
const __iconNode$d = [
|
|
10081
11399
|
["circle", { cx: "9", cy: "12", r: "1", key: "1vctgf" }],
|
|
10082
11400
|
["circle", { cx: "9", cy: "5", r: "1", key: "hp0tcf" }],
|
|
10083
11401
|
["circle", { cx: "9", cy: "19", r: "1", key: "fkjjf6" }],
|
|
@@ -10085,81 +11403,103 @@ const __iconNode$b = [
|
|
|
10085
11403
|
["circle", { cx: "15", cy: "5", r: "1", key: "19l28e" }],
|
|
10086
11404
|
["circle", { cx: "15", cy: "19", r: "1", key: "f4zoj3" }]
|
|
10087
11405
|
];
|
|
10088
|
-
const GripVertical = createLucideIcon("grip-vertical", __iconNode$
|
|
11406
|
+
const GripVertical = createLucideIcon("grip-vertical", __iconNode$d);
|
|
10089
11407
|
/**
|
|
10090
11408
|
* @license lucide-react v0.511.0 - ISC
|
|
10091
11409
|
*
|
|
10092
11410
|
* This source code is licensed under the ISC license.
|
|
10093
11411
|
* See the LICENSE file in the root directory of this source tree.
|
|
10094
11412
|
*/
|
|
10095
|
-
const __iconNode$
|
|
10096
|
-
const LoaderCircle = createLucideIcon("loader-circle", __iconNode$
|
|
11413
|
+
const __iconNode$c = [["path", { d: "M21 12a9 9 0 1 1-6.219-8.56", key: "13zald" }]];
|
|
11414
|
+
const LoaderCircle = createLucideIcon("loader-circle", __iconNode$c);
|
|
10097
11415
|
/**
|
|
10098
11416
|
* @license lucide-react v0.511.0 - ISC
|
|
10099
11417
|
*
|
|
10100
11418
|
* This source code is licensed under the ISC license.
|
|
10101
11419
|
* See the LICENSE file in the root directory of this source tree.
|
|
10102
11420
|
*/
|
|
10103
|
-
const __iconNode$
|
|
11421
|
+
const __iconNode$b = [
|
|
10104
11422
|
["rect", { width: "18", height: "11", x: "3", y: "11", rx: "2", ry: "2", key: "1w4ew1" }],
|
|
10105
11423
|
["path", { d: "M7 11V7a5 5 0 0 1 10 0v4", key: "fwvmzm" }]
|
|
10106
11424
|
];
|
|
10107
|
-
const Lock = createLucideIcon("lock", __iconNode$
|
|
11425
|
+
const Lock = createLucideIcon("lock", __iconNode$b);
|
|
10108
11426
|
/**
|
|
10109
11427
|
* @license lucide-react v0.511.0 - ISC
|
|
10110
11428
|
*
|
|
10111
11429
|
* This source code is licensed under the ISC license.
|
|
10112
11430
|
* See the LICENSE file in the root directory of this source tree.
|
|
10113
11431
|
*/
|
|
10114
|
-
const __iconNode$
|
|
11432
|
+
const __iconNode$a = [
|
|
10115
11433
|
["rect", { x: "14", y: "4", width: "4", height: "16", rx: "1", key: "zuxfzm" }],
|
|
10116
11434
|
["rect", { x: "6", y: "4", width: "4", height: "16", rx: "1", key: "1okwgv" }]
|
|
10117
11435
|
];
|
|
10118
|
-
const Pause = createLucideIcon("pause", __iconNode$
|
|
11436
|
+
const Pause = createLucideIcon("pause", __iconNode$a);
|
|
10119
11437
|
/**
|
|
10120
11438
|
* @license lucide-react v0.511.0 - ISC
|
|
10121
11439
|
*
|
|
10122
11440
|
* This source code is licensed under the ISC license.
|
|
10123
11441
|
* See the LICENSE file in the root directory of this source tree.
|
|
10124
11442
|
*/
|
|
10125
|
-
const __iconNode$
|
|
10126
|
-
const Play = createLucideIcon("play", __iconNode$
|
|
11443
|
+
const __iconNode$9 = [["polygon", { points: "6 3 20 12 6 21 6 3", key: "1oa8hb" }]];
|
|
11444
|
+
const Play = createLucideIcon("play", __iconNode$9);
|
|
10127
11445
|
/**
|
|
10128
11446
|
* @license lucide-react v0.511.0 - ISC
|
|
10129
11447
|
*
|
|
10130
11448
|
* This source code is licensed under the ISC license.
|
|
10131
11449
|
* See the LICENSE file in the root directory of this source tree.
|
|
10132
11450
|
*/
|
|
10133
|
-
const __iconNode$
|
|
11451
|
+
const __iconNode$8 = [
|
|
10134
11452
|
["path", { d: "M5 12h14", key: "1ays0h" }],
|
|
10135
11453
|
["path", { d: "M12 5v14", key: "s699le" }]
|
|
10136
11454
|
];
|
|
10137
|
-
const Plus = createLucideIcon("plus", __iconNode$
|
|
11455
|
+
const Plus = createLucideIcon("plus", __iconNode$8);
|
|
10138
11456
|
/**
|
|
10139
11457
|
* @license lucide-react v0.511.0 - ISC
|
|
10140
11458
|
*
|
|
10141
11459
|
* This source code is licensed under the ISC license.
|
|
10142
11460
|
* See the LICENSE file in the root directory of this source tree.
|
|
10143
11461
|
*/
|
|
10144
|
-
const __iconNode$
|
|
11462
|
+
const __iconNode$7 = [
|
|
10145
11463
|
["path", { d: "m15 14 5-5-5-5", key: "12vg1m" }],
|
|
10146
11464
|
["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" }]
|
|
10147
11465
|
];
|
|
10148
|
-
const Redo2 = createLucideIcon("redo-2", __iconNode$
|
|
11466
|
+
const Redo2 = createLucideIcon("redo-2", __iconNode$7);
|
|
10149
11467
|
/**
|
|
10150
11468
|
* @license lucide-react v0.511.0 - ISC
|
|
10151
11469
|
*
|
|
10152
11470
|
* This source code is licensed under the ISC license.
|
|
10153
11471
|
* See the LICENSE file in the root directory of this source tree.
|
|
10154
11472
|
*/
|
|
10155
|
-
const __iconNode$
|
|
11473
|
+
const __iconNode$6 = [
|
|
10156
11474
|
["circle", { cx: "6", cy: "6", r: "3", key: "1lh9wr" }],
|
|
10157
11475
|
["path", { d: "M8.12 8.12 12 12", key: "1alkpv" }],
|
|
10158
11476
|
["path", { d: "M20 4 8.12 15.88", key: "xgtan2" }],
|
|
10159
11477
|
["circle", { cx: "6", cy: "18", r: "3", key: "fqmcym" }],
|
|
10160
11478
|
["path", { d: "M14.8 14.8 20 20", key: "ptml3r" }]
|
|
10161
11479
|
];
|
|
10162
|
-
const Scissors = createLucideIcon("scissors", __iconNode$
|
|
11480
|
+
const Scissors = createLucideIcon("scissors", __iconNode$6);
|
|
11481
|
+
/**
|
|
11482
|
+
* @license lucide-react v0.511.0 - ISC
|
|
11483
|
+
*
|
|
11484
|
+
* This source code is licensed under the ISC license.
|
|
11485
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
11486
|
+
*/
|
|
11487
|
+
const __iconNode$5 = [
|
|
11488
|
+
["polygon", { points: "19 20 9 12 19 4 19 20", key: "o2sva" }],
|
|
11489
|
+
["line", { x1: "5", x2: "5", y1: "19", y2: "5", key: "1ocqjk" }]
|
|
11490
|
+
];
|
|
11491
|
+
const SkipBack = createLucideIcon("skip-back", __iconNode$5);
|
|
11492
|
+
/**
|
|
11493
|
+
* @license lucide-react v0.511.0 - ISC
|
|
11494
|
+
*
|
|
11495
|
+
* This source code is licensed under the ISC license.
|
|
11496
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
11497
|
+
*/
|
|
11498
|
+
const __iconNode$4 = [
|
|
11499
|
+
["polygon", { points: "5 4 15 12 5 20 5 4", key: "16p6eg" }],
|
|
11500
|
+
["line", { x1: "19", x2: "19", y1: "5", y2: "19", key: "futhcm" }]
|
|
11501
|
+
];
|
|
11502
|
+
const SkipForward = createLucideIcon("skip-forward", __iconNode$4);
|
|
10163
11503
|
/**
|
|
10164
11504
|
* @license lucide-react v0.511.0 - ISC
|
|
10165
11505
|
*
|
|
@@ -10212,7 +11552,7 @@ const __iconNode = [
|
|
|
10212
11552
|
const ZoomOut = createLucideIcon("zoom-out", __iconNode);
|
|
10213
11553
|
const TrackHeader = ({
|
|
10214
11554
|
track,
|
|
10215
|
-
|
|
11555
|
+
selectedIds,
|
|
10216
11556
|
onDragStart,
|
|
10217
11557
|
onDragOver,
|
|
10218
11558
|
onDrop,
|
|
@@ -10221,9 +11561,9 @@ const TrackHeader = ({
|
|
|
10221
11561
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
10222
11562
|
"div",
|
|
10223
11563
|
{
|
|
10224
|
-
className: `twick-track-header ${
|
|
11564
|
+
className: `twick-track-header ${selectedIds.has(track.getId()) ? "twick-track-header-selected" : "twick-track-header-default"}`,
|
|
10225
11565
|
draggable: true,
|
|
10226
|
-
onClick: () => onSelect(track),
|
|
11566
|
+
onClick: (e3) => onSelect(track, e3),
|
|
10227
11567
|
onDragStart: (e3) => onDragStart(e3, track),
|
|
10228
11568
|
onDragOver,
|
|
10229
11569
|
onDrop: (e3) => onDrop(e3, track),
|
|
@@ -17260,321 +18600,124 @@ class VisualElement {
|
|
|
17260
18600
|
}
|
|
17261
18601
|
const target = this.getBaseTargetFromProps(this.props, key);
|
|
17262
18602
|
if (target !== void 0 && !isMotionValue(target))
|
|
17263
|
-
return target;
|
|
17264
|
-
return this.initialValues[key] !== void 0 && valueFromInitial === void 0 ? void 0 : this.baseTarget[key];
|
|
17265
|
-
}
|
|
17266
|
-
on(eventName, callback) {
|
|
17267
|
-
if (!this.events[eventName]) {
|
|
17268
|
-
this.events[eventName] = new SubscriptionManager();
|
|
17269
|
-
}
|
|
17270
|
-
return this.events[eventName].add(callback);
|
|
17271
|
-
}
|
|
17272
|
-
notify(eventName, ...args) {
|
|
17273
|
-
if (this.events[eventName]) {
|
|
17274
|
-
this.events[eventName].notify(...args);
|
|
17275
|
-
}
|
|
17276
|
-
}
|
|
17277
|
-
}
|
|
17278
|
-
class DOMVisualElement extends VisualElement {
|
|
17279
|
-
constructor() {
|
|
17280
|
-
super(...arguments);
|
|
17281
|
-
this.KeyframeResolver = DOMKeyframesResolver;
|
|
17282
|
-
}
|
|
17283
|
-
sortInstanceNodePosition(a2, b2) {
|
|
17284
|
-
return a2.compareDocumentPosition(b2) & 2 ? 1 : -1;
|
|
17285
|
-
}
|
|
17286
|
-
getBaseTargetFromProps(props, key) {
|
|
17287
|
-
return props.style ? props.style[key] : void 0;
|
|
17288
|
-
}
|
|
17289
|
-
removeValueFromRenderState(key, { vars, style }) {
|
|
17290
|
-
delete vars[key];
|
|
17291
|
-
delete style[key];
|
|
17292
|
-
}
|
|
17293
|
-
handleChildMotionValue() {
|
|
17294
|
-
if (this.childSubscription) {
|
|
17295
|
-
this.childSubscription();
|
|
17296
|
-
delete this.childSubscription;
|
|
17297
|
-
}
|
|
17298
|
-
const { children } = this.props;
|
|
17299
|
-
if (isMotionValue(children)) {
|
|
17300
|
-
this.childSubscription = children.on("change", (latest) => {
|
|
17301
|
-
if (this.current) {
|
|
17302
|
-
this.current.textContent = `${latest}`;
|
|
17303
|
-
}
|
|
17304
|
-
});
|
|
17305
|
-
}
|
|
17306
|
-
}
|
|
17307
|
-
}
|
|
17308
|
-
function getComputedStyle(element) {
|
|
17309
|
-
return window.getComputedStyle(element);
|
|
17310
|
-
}
|
|
17311
|
-
class HTMLVisualElement extends DOMVisualElement {
|
|
17312
|
-
constructor() {
|
|
17313
|
-
super(...arguments);
|
|
17314
|
-
this.type = "html";
|
|
17315
|
-
this.renderInstance = renderHTML;
|
|
17316
|
-
}
|
|
17317
|
-
readValueFromInstance(instance, key) {
|
|
17318
|
-
if (transformProps.has(key)) {
|
|
17319
|
-
const defaultType = getDefaultValueType(key);
|
|
17320
|
-
return defaultType ? defaultType.default || 0 : 0;
|
|
17321
|
-
} else {
|
|
17322
|
-
const computedStyle = getComputedStyle(instance);
|
|
17323
|
-
const value = (isCSSVariableName(key) ? computedStyle.getPropertyValue(key) : computedStyle[key]) || 0;
|
|
17324
|
-
return typeof value === "string" ? value.trim() : value;
|
|
17325
|
-
}
|
|
17326
|
-
}
|
|
17327
|
-
measureInstanceViewportBox(instance, { transformPagePoint }) {
|
|
17328
|
-
return measureViewportBox(instance, transformPagePoint);
|
|
17329
|
-
}
|
|
17330
|
-
build(renderState, latestValues, props) {
|
|
17331
|
-
buildHTMLStyles(renderState, latestValues, props.transformTemplate);
|
|
17332
|
-
}
|
|
17333
|
-
scrapeMotionValuesFromProps(props, prevProps, visualElement) {
|
|
17334
|
-
return scrapeMotionValuesFromProps$1(props, prevProps, visualElement);
|
|
17335
|
-
}
|
|
17336
|
-
}
|
|
17337
|
-
class SVGVisualElement extends DOMVisualElement {
|
|
17338
|
-
constructor() {
|
|
17339
|
-
super(...arguments);
|
|
17340
|
-
this.type = "svg";
|
|
17341
|
-
this.isSVGTag = false;
|
|
17342
|
-
this.measureInstanceViewportBox = createBox;
|
|
18603
|
+
return target;
|
|
18604
|
+
return this.initialValues[key] !== void 0 && valueFromInitial === void 0 ? void 0 : this.baseTarget[key];
|
|
17343
18605
|
}
|
|
17344
|
-
|
|
17345
|
-
|
|
18606
|
+
on(eventName, callback) {
|
|
18607
|
+
if (!this.events[eventName]) {
|
|
18608
|
+
this.events[eventName] = new SubscriptionManager();
|
|
18609
|
+
}
|
|
18610
|
+
return this.events[eventName].add(callback);
|
|
17346
18611
|
}
|
|
17347
|
-
|
|
17348
|
-
if (
|
|
17349
|
-
|
|
17350
|
-
return defaultType ? defaultType.default || 0 : 0;
|
|
18612
|
+
notify(eventName, ...args) {
|
|
18613
|
+
if (this.events[eventName]) {
|
|
18614
|
+
this.events[eventName].notify(...args);
|
|
17351
18615
|
}
|
|
17352
|
-
key = !camelCaseAttributes.has(key) ? camelToDash(key) : key;
|
|
17353
|
-
return instance.getAttribute(key);
|
|
17354
18616
|
}
|
|
17355
|
-
|
|
17356
|
-
|
|
18617
|
+
}
|
|
18618
|
+
class DOMVisualElement extends VisualElement {
|
|
18619
|
+
constructor() {
|
|
18620
|
+
super(...arguments);
|
|
18621
|
+
this.KeyframeResolver = DOMKeyframesResolver;
|
|
17357
18622
|
}
|
|
17358
|
-
|
|
17359
|
-
|
|
18623
|
+
sortInstanceNodePosition(a2, b2) {
|
|
18624
|
+
return a2.compareDocumentPosition(b2) & 2 ? 1 : -1;
|
|
17360
18625
|
}
|
|
17361
|
-
|
|
17362
|
-
|
|
18626
|
+
getBaseTargetFromProps(props, key) {
|
|
18627
|
+
return props.style ? props.style[key] : void 0;
|
|
17363
18628
|
}
|
|
17364
|
-
|
|
17365
|
-
|
|
17366
|
-
|
|
18629
|
+
removeValueFromRenderState(key, { vars, style }) {
|
|
18630
|
+
delete vars[key];
|
|
18631
|
+
delete style[key];
|
|
17367
18632
|
}
|
|
17368
|
-
|
|
17369
|
-
|
|
17370
|
-
|
|
17371
|
-
|
|
17372
|
-
});
|
|
17373
|
-
};
|
|
17374
|
-
const createMotionComponent = /* @__PURE__ */ createMotionComponentFactory({
|
|
17375
|
-
...animations,
|
|
17376
|
-
...gestureAnimations,
|
|
17377
|
-
...drag,
|
|
17378
|
-
...layout
|
|
17379
|
-
}, createDomVisualElement);
|
|
17380
|
-
const motion = /* @__PURE__ */ createDOMMotionComponentProxy(createMotionComponent);
|
|
17381
|
-
const INITIAL_TIMELINE_DATA = {
|
|
17382
|
-
tracks: [
|
|
17383
|
-
{
|
|
17384
|
-
type: "element",
|
|
17385
|
-
id: "t-sample",
|
|
17386
|
-
name: "sample",
|
|
17387
|
-
elements: [
|
|
17388
|
-
{
|
|
17389
|
-
id: "e-sample",
|
|
17390
|
-
trackId: "t-sample",
|
|
17391
|
-
name: "sample",
|
|
17392
|
-
type: "text",
|
|
17393
|
-
s: 0,
|
|
17394
|
-
e: 5,
|
|
17395
|
-
props: {
|
|
17396
|
-
text: "Twick Video Editor",
|
|
17397
|
-
fill: "#FFFFFF"
|
|
17398
|
-
}
|
|
17399
|
-
}
|
|
17400
|
-
]
|
|
18633
|
+
handleChildMotionValue() {
|
|
18634
|
+
if (this.childSubscription) {
|
|
18635
|
+
this.childSubscription();
|
|
18636
|
+
delete this.childSubscription;
|
|
17401
18637
|
}
|
|
17402
|
-
|
|
17403
|
-
|
|
17404
|
-
|
|
17405
|
-
|
|
17406
|
-
|
|
17407
|
-
|
|
17408
|
-
|
|
17409
|
-
|
|
17410
|
-
|
|
17411
|
-
|
|
17412
|
-
|
|
17413
|
-
|
|
17414
|
-
|
|
17415
|
-
|
|
17416
|
-
|
|
17417
|
-
|
|
17418
|
-
|
|
17419
|
-
|
|
17420
|
-
|
|
17421
|
-
|
|
17422
|
-
|
|
17423
|
-
|
|
17424
|
-
|
|
17425
|
-
|
|
17426
|
-
|
|
17427
|
-
|
|
17428
|
-
|
|
17429
|
-
|
|
17430
|
-
|
|
17431
|
-
|
|
17432
|
-
|
|
17433
|
-
}
|
|
17434
|
-
{
|
|
17435
|
-
|
|
17436
|
-
|
|
17437
|
-
|
|
17438
|
-
|
|
17439
|
-
|
|
17440
|
-
|
|
17441
|
-
|
|
17442
|
-
{
|
|
17443
|
-
|
|
17444
|
-
|
|
17445
|
-
|
|
17446
|
-
|
|
17447
|
-
|
|
17448
|
-
|
|
17449
|
-
|
|
17450
|
-
|
|
17451
|
-
|
|
17452
|
-
|
|
17453
|
-
|
|
17454
|
-
|
|
17455
|
-
|
|
17456
|
-
|
|
17457
|
-
|
|
17458
|
-
|
|
17459
|
-
|
|
17460
|
-
|
|
17461
|
-
|
|
17462
|
-
|
|
17463
|
-
|
|
17464
|
-
|
|
17465
|
-
|
|
17466
|
-
|
|
17467
|
-
|
|
17468
|
-
|
|
17469
|
-
|
|
17470
|
-
|
|
17471
|
-
|
|
17472
|
-
|
|
17473
|
-
|
|
17474
|
-
{
|
|
17475
|
-
|
|
17476
|
-
|
|
17477
|
-
majorInterval: 300,
|
|
17478
|
-
// 5m major ticks
|
|
17479
|
-
minorTicks: 5
|
|
17480
|
-
// 1m minor ticks (5 minors between majors)
|
|
17481
|
-
},
|
|
17482
|
-
{
|
|
17483
|
-
durationThreshold: 7200,
|
|
17484
|
-
// < 2 hours
|
|
17485
|
-
majorInterval: 600,
|
|
17486
|
-
// 10m major ticks
|
|
17487
|
-
minorTicks: 10
|
|
17488
|
-
// 1m minor ticks (10 minors between majors)
|
|
17489
|
-
},
|
|
17490
|
-
{
|
|
17491
|
-
durationThreshold: Infinity,
|
|
17492
|
-
// >= 2 hours
|
|
17493
|
-
majorInterval: 1800,
|
|
17494
|
-
// 30m major ticks
|
|
17495
|
-
minorTicks: 6
|
|
17496
|
-
// 5m minor ticks (6 minors between majors)
|
|
17497
|
-
}
|
|
17498
|
-
];
|
|
17499
|
-
const DEFAULT_ELEMENT_COLORS = {
|
|
17500
|
-
/** Fragment element color - deep charcoal matching UI background */
|
|
17501
|
-
fragment: "#1A1A1A",
|
|
17502
|
-
/** Video element color - vibrant royal purple */
|
|
17503
|
-
video: "#8B5FBF",
|
|
17504
|
-
/** Caption element color - soft wisteria purple */
|
|
17505
|
-
caption: "#9B8ACE",
|
|
17506
|
-
/** Image element color - warm copper accent */
|
|
17507
|
-
image: "#D4956C",
|
|
17508
|
-
/** Audio element color - deep teal */
|
|
17509
|
-
audio: "#3D8B8B",
|
|
17510
|
-
/** Text element color - medium lavender */
|
|
17511
|
-
text: "#8D74C4",
|
|
17512
|
-
/** Generic element color - muted amethyst */
|
|
17513
|
-
element: "#7B68B8",
|
|
17514
|
-
/** Rectangle element color - deep indigo */
|
|
17515
|
-
rect: "#5B4B99",
|
|
17516
|
-
/** Frame effect color - rich magenta */
|
|
17517
|
-
frameEffect: "#B55B9C",
|
|
17518
|
-
/** Filters color - periwinkle blue */
|
|
17519
|
-
filters: "#7A89D4",
|
|
17520
|
-
/** Transition color - burnished bronze */
|
|
17521
|
-
transition: "#BE8157",
|
|
17522
|
-
/** Animation color - muted emerald */
|
|
17523
|
-
animation: "#4B9B78",
|
|
17524
|
-
/** Icon element color - bright orchid */
|
|
17525
|
-
icon: "#A76CD4",
|
|
17526
|
-
/** Circle element color - deep byzantium */
|
|
17527
|
-
circle: "#703D8B"
|
|
17528
|
-
};
|
|
17529
|
-
const AVAILABLE_TEXT_FONTS = {
|
|
17530
|
-
// Google Fonts
|
|
17531
|
-
/** Modern sans-serif font */
|
|
17532
|
-
RUBIK: "Rubik",
|
|
17533
|
-
/** Clean and readable font */
|
|
17534
|
-
MULISH: "Mulish",
|
|
17535
|
-
/** Bold display font */
|
|
17536
|
-
LUCKIEST_GUY: "Luckiest Guy",
|
|
17537
|
-
/** Elegant serif font */
|
|
17538
|
-
PLAYFAIR_DISPLAY: "Playfair Display",
|
|
17539
|
-
/** Classic sans-serif font */
|
|
17540
|
-
ROBOTO: "Roboto",
|
|
17541
|
-
/** Modern geometric font */
|
|
17542
|
-
POPPINS: "Poppins",
|
|
17543
|
-
// Display and Decorative Fonts
|
|
17544
|
-
/** Comic-style display font */
|
|
17545
|
-
BANGERS: "Bangers",
|
|
17546
|
-
/** Handwritten-style font */
|
|
17547
|
-
BIRTHSTONE: "Birthstone",
|
|
17548
|
-
/** Elegant script font */
|
|
17549
|
-
CORINTHIA: "Corinthia",
|
|
17550
|
-
/** Formal script font */
|
|
17551
|
-
IMPERIAL_SCRIPT: "Imperial Script",
|
|
17552
|
-
/** Bold outline font */
|
|
17553
|
-
KUMAR_ONE_OUTLINE: "Kumar One Outline",
|
|
17554
|
-
/** Light outline font */
|
|
17555
|
-
LONDRI_OUTLINE: "Londrina Outline",
|
|
17556
|
-
/** Casual script font */
|
|
17557
|
-
MARCK_SCRIPT: "Marck Script",
|
|
17558
|
-
/** Modern sans-serif font */
|
|
17559
|
-
MONTSERRAT: "Montserrat",
|
|
17560
|
-
/** Stylish display font */
|
|
17561
|
-
PATTAYA: "Pattaya",
|
|
17562
|
-
// CDN Fonts
|
|
17563
|
-
/** Unique display font */
|
|
17564
|
-
PERALTA: "Peralta",
|
|
17565
|
-
/** Bold impact font */
|
|
17566
|
-
IMPACT: "Impact",
|
|
17567
|
-
/** Handwritten-style font */
|
|
17568
|
-
LUMANOSIMO: "Lumanosimo",
|
|
17569
|
-
/** Custom display font */
|
|
17570
|
-
KAPAKANA: "Kapakana",
|
|
17571
|
-
/** Handwritten font */
|
|
17572
|
-
HANDYRUSH: "HandyRush",
|
|
17573
|
-
/** Decorative font */
|
|
17574
|
-
DASHER: "Dasher",
|
|
17575
|
-
/** Signature-style font */
|
|
17576
|
-
BRITTANY_SIGNATURE: "Brittany Signature"
|
|
18638
|
+
const { children } = this.props;
|
|
18639
|
+
if (isMotionValue(children)) {
|
|
18640
|
+
this.childSubscription = children.on("change", (latest) => {
|
|
18641
|
+
if (this.current) {
|
|
18642
|
+
this.current.textContent = `${latest}`;
|
|
18643
|
+
}
|
|
18644
|
+
});
|
|
18645
|
+
}
|
|
18646
|
+
}
|
|
18647
|
+
}
|
|
18648
|
+
function getComputedStyle(element) {
|
|
18649
|
+
return window.getComputedStyle(element);
|
|
18650
|
+
}
|
|
18651
|
+
class HTMLVisualElement extends DOMVisualElement {
|
|
18652
|
+
constructor() {
|
|
18653
|
+
super(...arguments);
|
|
18654
|
+
this.type = "html";
|
|
18655
|
+
this.renderInstance = renderHTML;
|
|
18656
|
+
}
|
|
18657
|
+
readValueFromInstance(instance, key) {
|
|
18658
|
+
if (transformProps.has(key)) {
|
|
18659
|
+
const defaultType = getDefaultValueType(key);
|
|
18660
|
+
return defaultType ? defaultType.default || 0 : 0;
|
|
18661
|
+
} else {
|
|
18662
|
+
const computedStyle = getComputedStyle(instance);
|
|
18663
|
+
const value = (isCSSVariableName(key) ? computedStyle.getPropertyValue(key) : computedStyle[key]) || 0;
|
|
18664
|
+
return typeof value === "string" ? value.trim() : value;
|
|
18665
|
+
}
|
|
18666
|
+
}
|
|
18667
|
+
measureInstanceViewportBox(instance, { transformPagePoint }) {
|
|
18668
|
+
return measureViewportBox(instance, transformPagePoint);
|
|
18669
|
+
}
|
|
18670
|
+
build(renderState, latestValues, props) {
|
|
18671
|
+
buildHTMLStyles(renderState, latestValues, props.transformTemplate);
|
|
18672
|
+
}
|
|
18673
|
+
scrapeMotionValuesFromProps(props, prevProps, visualElement) {
|
|
18674
|
+
return scrapeMotionValuesFromProps$1(props, prevProps, visualElement);
|
|
18675
|
+
}
|
|
18676
|
+
}
|
|
18677
|
+
class SVGVisualElement extends DOMVisualElement {
|
|
18678
|
+
constructor() {
|
|
18679
|
+
super(...arguments);
|
|
18680
|
+
this.type = "svg";
|
|
18681
|
+
this.isSVGTag = false;
|
|
18682
|
+
this.measureInstanceViewportBox = createBox;
|
|
18683
|
+
}
|
|
18684
|
+
getBaseTargetFromProps(props, key) {
|
|
18685
|
+
return props[key];
|
|
18686
|
+
}
|
|
18687
|
+
readValueFromInstance(instance, key) {
|
|
18688
|
+
if (transformProps.has(key)) {
|
|
18689
|
+
const defaultType = getDefaultValueType(key);
|
|
18690
|
+
return defaultType ? defaultType.default || 0 : 0;
|
|
18691
|
+
}
|
|
18692
|
+
key = !camelCaseAttributes.has(key) ? camelToDash(key) : key;
|
|
18693
|
+
return instance.getAttribute(key);
|
|
18694
|
+
}
|
|
18695
|
+
scrapeMotionValuesFromProps(props, prevProps, visualElement) {
|
|
18696
|
+
return scrapeMotionValuesFromProps(props, prevProps, visualElement);
|
|
18697
|
+
}
|
|
18698
|
+
build(renderState, latestValues, props) {
|
|
18699
|
+
buildSVGAttrs(renderState, latestValues, this.isSVGTag, props.transformTemplate);
|
|
18700
|
+
}
|
|
18701
|
+
renderInstance(instance, renderState, styleProp, projection) {
|
|
18702
|
+
renderSVG(instance, renderState, styleProp, projection);
|
|
18703
|
+
}
|
|
18704
|
+
mount(instance) {
|
|
18705
|
+
this.isSVGTag = isSVGTag(instance.tagName);
|
|
18706
|
+
super.mount(instance);
|
|
18707
|
+
}
|
|
18708
|
+
}
|
|
18709
|
+
const createDomVisualElement = (Component, options) => {
|
|
18710
|
+
return isSVGComponent(Component) ? new SVGVisualElement(options) : new HTMLVisualElement(options, {
|
|
18711
|
+
allowProjection: Component !== React.Fragment
|
|
18712
|
+
});
|
|
17577
18713
|
};
|
|
18714
|
+
const createMotionComponent = /* @__PURE__ */ createMotionComponentFactory({
|
|
18715
|
+
...animations,
|
|
18716
|
+
...gestureAnimations,
|
|
18717
|
+
...drag,
|
|
18718
|
+
...layout
|
|
18719
|
+
}, createDomVisualElement);
|
|
18720
|
+
const motion = /* @__PURE__ */ createDOMMotionComponentProxy(createMotionComponent);
|
|
17578
18721
|
let ELEMENT_COLORS = { ...DEFAULT_ELEMENT_COLORS };
|
|
17579
18722
|
const setElementColors = (colors) => {
|
|
17580
18723
|
ELEMENT_COLORS = {
|
|
@@ -17589,6 +18732,7 @@ const TrackElementView = ({
|
|
|
17589
18732
|
nextStart,
|
|
17590
18733
|
prevEnd,
|
|
17591
18734
|
selectedItem,
|
|
18735
|
+
selectedIds,
|
|
17592
18736
|
onSelection,
|
|
17593
18737
|
onDrag,
|
|
17594
18738
|
allowOverlap = false,
|
|
@@ -17615,8 +18759,8 @@ const TrackElementView = ({
|
|
|
17615
18759
|
dragType.current = DRAG_TYPE.MOVE;
|
|
17616
18760
|
setPosition((prev) => {
|
|
17617
18761
|
const span = prev.end - prev.start;
|
|
17618
|
-
let newStart =
|
|
17619
|
-
newStart = Math.min(newStart, prev.end - MIN_DURATION);
|
|
18762
|
+
let newStart = prev.start + dx / parentWidth * duration;
|
|
18763
|
+
newStart = Math.max(0, Math.min(newStart, prev.end - MIN_DURATION));
|
|
17620
18764
|
if (!allowOverlap) {
|
|
17621
18765
|
if (prevEnd !== null && newStart < prevEnd) {
|
|
17622
18766
|
newStart = prevEnd;
|
|
@@ -17640,8 +18784,8 @@ const TrackElementView = ({
|
|
|
17640
18784
|
}
|
|
17641
18785
|
dragType.current = DRAG_TYPE.START;
|
|
17642
18786
|
setPosition((prev) => {
|
|
17643
|
-
let newStart =
|
|
17644
|
-
newStart = Math.min(newStart, prev.end - MIN_DURATION);
|
|
18787
|
+
let newStart = prev.start + dx / parentWidth * duration;
|
|
18788
|
+
newStart = Math.max(0, Math.min(newStart, prev.end - MIN_DURATION));
|
|
17645
18789
|
if (prevEnd !== null && !allowOverlap && newStart < prevEnd) {
|
|
17646
18790
|
newStart = prevEnd;
|
|
17647
18791
|
}
|
|
@@ -17699,8 +18843,9 @@ const TrackElementView = ({
|
|
|
17699
18843
|
return ELEMENT_COLORS.element;
|
|
17700
18844
|
};
|
|
17701
18845
|
const isSelected = React.useMemo(() => {
|
|
17702
|
-
return
|
|
17703
|
-
}, [
|
|
18846
|
+
return selectedIds.has(element.getId());
|
|
18847
|
+
}, [selectedIds, element]);
|
|
18848
|
+
const hasHandles = (selectedItem == null ? void 0 : selectedItem.getId()) === element.getId();
|
|
17704
18849
|
const motionProps = {
|
|
17705
18850
|
ref,
|
|
17706
18851
|
className: `twick-track-element ${isSelected ? "twick-track-element-selected" : "twick-track-element-default"} ${isDragging2 ? "twick-track-element-dragging" : ""}`,
|
|
@@ -17716,9 +18861,9 @@ const TrackElementView = ({
|
|
|
17716
18861
|
},
|
|
17717
18862
|
onMouseUp: sendUpdate,
|
|
17718
18863
|
onTouchEnd: sendUpdate,
|
|
17719
|
-
onClick: () => {
|
|
18864
|
+
onClick: (e3) => {
|
|
17720
18865
|
if (onSelection) {
|
|
17721
|
-
onSelection(element);
|
|
18866
|
+
onSelection(element, e3);
|
|
17722
18867
|
}
|
|
17723
18868
|
},
|
|
17724
18869
|
style: {
|
|
@@ -17729,7 +18874,7 @@ const TrackElementView = ({
|
|
|
17729
18874
|
}
|
|
17730
18875
|
};
|
|
17731
18876
|
return /* @__PURE__ */ jsxRuntime.jsx(motion.div, { ...motionProps, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { touchAction: "none", height: "100%" }, ...bind(), children: [
|
|
17732
|
-
|
|
18877
|
+
hasHandles ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
17733
18878
|
"div",
|
|
17734
18879
|
{
|
|
17735
18880
|
style: { touchAction: "none", zIndex: isSelected ? 100 : 1 },
|
|
@@ -17738,7 +18883,7 @@ const TrackElementView = ({
|
|
|
17738
18883
|
}
|
|
17739
18884
|
) : null,
|
|
17740
18885
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "twick-track-element-content", children: element.getText ? element.getText() : element.getName() || element.getType() }),
|
|
17741
|
-
|
|
18886
|
+
hasHandles ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
17742
18887
|
"div",
|
|
17743
18888
|
{
|
|
17744
18889
|
style: { touchAction: "none", zIndex: isSelected ? 100 : 1 },
|
|
@@ -17768,6 +18913,7 @@ const TrackBase = ({
|
|
|
17768
18913
|
track,
|
|
17769
18914
|
trackWidth,
|
|
17770
18915
|
selectedItem,
|
|
18916
|
+
selectedIds,
|
|
17771
18917
|
onItemSelection,
|
|
17772
18918
|
onDrag,
|
|
17773
18919
|
allowOverlap = false,
|
|
@@ -17792,6 +18938,7 @@ const TrackBase = ({
|
|
|
17792
18938
|
allowOverlap,
|
|
17793
18939
|
parentWidth: trackWidth,
|
|
17794
18940
|
selectedItem,
|
|
18941
|
+
selectedIds,
|
|
17795
18942
|
onSelection: onItemSelection,
|
|
17796
18943
|
onDrag,
|
|
17797
18944
|
elementColors,
|
|
@@ -17803,6 +18950,199 @@ const TrackBase = ({
|
|
|
17803
18950
|
}
|
|
17804
18951
|
);
|
|
17805
18952
|
};
|
|
18953
|
+
const DEFAULT_MARGIN = 80;
|
|
18954
|
+
function usePlayheadScroll(scrollContainerRef, playheadPositionPx, isActive, config) {
|
|
18955
|
+
const margin = (config == null ? void 0 : config.margin) ?? DEFAULT_MARGIN;
|
|
18956
|
+
const labelWidth = config == null ? void 0 : config.labelWidth;
|
|
18957
|
+
const rafRef = React.useRef(null);
|
|
18958
|
+
React.useEffect(() => {
|
|
18959
|
+
if (!isActive || !scrollContainerRef.current) return;
|
|
18960
|
+
const container = scrollContainerRef.current;
|
|
18961
|
+
const contentX = labelWidth + playheadPositionPx;
|
|
18962
|
+
const scrollToKeepPlayheadVisible = () => {
|
|
18963
|
+
const { scrollLeft, clientWidth } = container;
|
|
18964
|
+
const minVisible = scrollLeft + margin;
|
|
18965
|
+
const maxVisible = scrollLeft + clientWidth - margin;
|
|
18966
|
+
let newScrollLeft = null;
|
|
18967
|
+
if (contentX < minVisible) {
|
|
18968
|
+
newScrollLeft = Math.max(0, contentX - margin);
|
|
18969
|
+
} else if (contentX > maxVisible) {
|
|
18970
|
+
newScrollLeft = contentX - clientWidth + margin;
|
|
18971
|
+
}
|
|
18972
|
+
if (newScrollLeft !== null) {
|
|
18973
|
+
container.scrollLeft = newScrollLeft;
|
|
18974
|
+
}
|
|
18975
|
+
};
|
|
18976
|
+
const scheduleScroll = () => {
|
|
18977
|
+
if (rafRef.current !== null) cancelAnimationFrame(rafRef.current);
|
|
18978
|
+
rafRef.current = requestAnimationFrame(() => {
|
|
18979
|
+
rafRef.current = null;
|
|
18980
|
+
scrollToKeepPlayheadVisible();
|
|
18981
|
+
});
|
|
18982
|
+
};
|
|
18983
|
+
scheduleScroll();
|
|
18984
|
+
return () => {
|
|
18985
|
+
if (rafRef.current !== null) {
|
|
18986
|
+
cancelAnimationFrame(rafRef.current);
|
|
18987
|
+
}
|
|
18988
|
+
};
|
|
18989
|
+
}, [
|
|
18990
|
+
isActive,
|
|
18991
|
+
playheadPositionPx,
|
|
18992
|
+
scrollContainerRef,
|
|
18993
|
+
margin,
|
|
18994
|
+
labelWidth
|
|
18995
|
+
]);
|
|
18996
|
+
}
|
|
18997
|
+
const MARQUEE_THRESHOLD = 4;
|
|
18998
|
+
function useMarqueeSelection({
|
|
18999
|
+
duration,
|
|
19000
|
+
zoomLevel,
|
|
19001
|
+
labelWidth,
|
|
19002
|
+
trackCount,
|
|
19003
|
+
trackHeight,
|
|
19004
|
+
tracks,
|
|
19005
|
+
containerRef,
|
|
19006
|
+
onMarqueeSelect,
|
|
19007
|
+
onEmptyClick
|
|
19008
|
+
}) {
|
|
19009
|
+
const [marquee, setMarquee] = React.useState(null);
|
|
19010
|
+
const startPosRef = React.useRef(null);
|
|
19011
|
+
const hasDraggedRef = React.useRef(false);
|
|
19012
|
+
const marqueeRef = React.useRef(marquee);
|
|
19013
|
+
marqueeRef.current = marquee;
|
|
19014
|
+
const pixelsPerSecond = 100 * zoomLevel;
|
|
19015
|
+
const getCoords = React.useCallback(
|
|
19016
|
+
(e3) => {
|
|
19017
|
+
var _a, _b;
|
|
19018
|
+
const rect = (_a = containerRef.current) == null ? void 0 : _a.getBoundingClientRect();
|
|
19019
|
+
if (!rect) return { x: 0, y: 0 };
|
|
19020
|
+
return {
|
|
19021
|
+
x: e3.clientX - rect.left + (((_b = containerRef.current) == null ? void 0 : _b.scrollLeft) ?? 0),
|
|
19022
|
+
y: e3.clientY - rect.top
|
|
19023
|
+
};
|
|
19024
|
+
},
|
|
19025
|
+
[containerRef]
|
|
19026
|
+
);
|
|
19027
|
+
const handleGlobalMouseMove = React.useCallback(
|
|
19028
|
+
(e3) => {
|
|
19029
|
+
if (!startPosRef.current) return;
|
|
19030
|
+
const { x: x2, y: y2 } = getCoords(e3);
|
|
19031
|
+
const dx = Math.abs(x2 - startPosRef.current.x);
|
|
19032
|
+
const dy = Math.abs(y2 - startPosRef.current.y);
|
|
19033
|
+
if (dx > MARQUEE_THRESHOLD || dy > MARQUEE_THRESHOLD) {
|
|
19034
|
+
hasDraggedRef.current = true;
|
|
19035
|
+
}
|
|
19036
|
+
setMarquee((prev) => prev ? { ...prev, endX: x2, endY: y2 } : null);
|
|
19037
|
+
},
|
|
19038
|
+
[getCoords]
|
|
19039
|
+
);
|
|
19040
|
+
const handleGlobalMouseUp = React.useCallback(() => {
|
|
19041
|
+
if (!startPosRef.current) return;
|
|
19042
|
+
const currentMarquee = marqueeRef.current;
|
|
19043
|
+
if (!hasDraggedRef.current || !currentMarquee) {
|
|
19044
|
+
setMarquee(null);
|
|
19045
|
+
startPosRef.current = null;
|
|
19046
|
+
onEmptyClick();
|
|
19047
|
+
window.removeEventListener("mousemove", handleGlobalMouseMove);
|
|
19048
|
+
window.removeEventListener("mouseup", handleGlobalMouseUp);
|
|
19049
|
+
return;
|
|
19050
|
+
}
|
|
19051
|
+
const left = Math.min(currentMarquee.startX, currentMarquee.endX);
|
|
19052
|
+
const right = Math.max(currentMarquee.startX, currentMarquee.endX);
|
|
19053
|
+
const top = Math.min(currentMarquee.startY, currentMarquee.endY);
|
|
19054
|
+
const bottom = Math.max(currentMarquee.startY, currentMarquee.endY);
|
|
19055
|
+
const startTime = Math.max(0, (left - labelWidth) / pixelsPerSecond);
|
|
19056
|
+
const endTime = Math.min(duration, (right - labelWidth) / pixelsPerSecond);
|
|
19057
|
+
const rowHeight = trackHeight + 2;
|
|
19058
|
+
const startTrackIdx = Math.max(0, Math.floor(top / rowHeight));
|
|
19059
|
+
const endTrackIdx = Math.min(
|
|
19060
|
+
trackCount - 1,
|
|
19061
|
+
Math.floor(bottom / rowHeight)
|
|
19062
|
+
);
|
|
19063
|
+
const selectedIds = /* @__PURE__ */ new Set();
|
|
19064
|
+
for (let tIdx = startTrackIdx; tIdx <= endTrackIdx; tIdx++) {
|
|
19065
|
+
const track = tracks[tIdx];
|
|
19066
|
+
if (!track) continue;
|
|
19067
|
+
for (const el of track.getElements()) {
|
|
19068
|
+
const elStart = el.getStart();
|
|
19069
|
+
const elEnd = el.getEnd();
|
|
19070
|
+
if (elStart < endTime && elEnd > startTime) {
|
|
19071
|
+
selectedIds.add(el.getId());
|
|
19072
|
+
}
|
|
19073
|
+
}
|
|
19074
|
+
}
|
|
19075
|
+
onMarqueeSelect(selectedIds);
|
|
19076
|
+
setMarquee(null);
|
|
19077
|
+
startPosRef.current = null;
|
|
19078
|
+
window.removeEventListener("mousemove", handleGlobalMouseMove);
|
|
19079
|
+
window.removeEventListener("mouseup", handleGlobalMouseUp);
|
|
19080
|
+
}, [
|
|
19081
|
+
duration,
|
|
19082
|
+
pixelsPerSecond,
|
|
19083
|
+
labelWidth,
|
|
19084
|
+
trackCount,
|
|
19085
|
+
trackHeight,
|
|
19086
|
+
tracks,
|
|
19087
|
+
onMarqueeSelect,
|
|
19088
|
+
onEmptyClick,
|
|
19089
|
+
handleGlobalMouseMove
|
|
19090
|
+
]);
|
|
19091
|
+
React.useEffect(() => {
|
|
19092
|
+
if (!marquee) return;
|
|
19093
|
+
window.addEventListener("mousemove", handleGlobalMouseMove);
|
|
19094
|
+
window.addEventListener("mouseup", handleGlobalMouseUp);
|
|
19095
|
+
return () => {
|
|
19096
|
+
window.removeEventListener("mousemove", handleGlobalMouseMove);
|
|
19097
|
+
window.removeEventListener("mouseup", handleGlobalMouseUp);
|
|
19098
|
+
};
|
|
19099
|
+
}, [marquee, handleGlobalMouseMove, handleGlobalMouseUp]);
|
|
19100
|
+
const handleMouseDown = React.useCallback(
|
|
19101
|
+
(e3) => {
|
|
19102
|
+
if (e3.target.closest(".twick-track-element") || e3.target.closest(".twick-track-header")) {
|
|
19103
|
+
return;
|
|
19104
|
+
}
|
|
19105
|
+
const { x: x2, y: y2 } = getCoords(e3.nativeEvent);
|
|
19106
|
+
startPosRef.current = { x: x2, y: y2 };
|
|
19107
|
+
hasDraggedRef.current = false;
|
|
19108
|
+
setMarquee({ startX: x2, startY: y2, endX: x2, endY: y2 });
|
|
19109
|
+
},
|
|
19110
|
+
[getCoords]
|
|
19111
|
+
);
|
|
19112
|
+
return { marquee, handleMouseDown };
|
|
19113
|
+
}
|
|
19114
|
+
function MarqueeOverlay({ marquee }) {
|
|
19115
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
19116
|
+
"div",
|
|
19117
|
+
{
|
|
19118
|
+
className: "twick-marquee-overlay",
|
|
19119
|
+
style: {
|
|
19120
|
+
position: "absolute",
|
|
19121
|
+
inset: 0,
|
|
19122
|
+
zIndex: 25,
|
|
19123
|
+
pointerEvents: "none"
|
|
19124
|
+
},
|
|
19125
|
+
children: marquee && /* @__PURE__ */ jsxRuntime.jsx(
|
|
19126
|
+
"div",
|
|
19127
|
+
{
|
|
19128
|
+
className: "twick-marquee-rect",
|
|
19129
|
+
style: {
|
|
19130
|
+
position: "absolute",
|
|
19131
|
+
left: Math.min(marquee.startX, marquee.endX),
|
|
19132
|
+
top: Math.min(marquee.startY, marquee.endY),
|
|
19133
|
+
width: Math.abs(marquee.endX - marquee.startX),
|
|
19134
|
+
height: Math.abs(marquee.endY - marquee.startY),
|
|
19135
|
+
border: "1px solid rgba(255, 255, 255, 0.7)",
|
|
19136
|
+
backgroundColor: "rgba(255, 255, 255, 0.1)",
|
|
19137
|
+
pointerEvents: "none"
|
|
19138
|
+
}
|
|
19139
|
+
}
|
|
19140
|
+
)
|
|
19141
|
+
}
|
|
19142
|
+
);
|
|
19143
|
+
}
|
|
19144
|
+
const LABEL_WIDTH = 40;
|
|
19145
|
+
const TRACK_HEIGHT = 44;
|
|
17806
19146
|
function TimelineView({
|
|
17807
19147
|
zoomLevel,
|
|
17808
19148
|
selectedItem,
|
|
@@ -17811,20 +19151,28 @@ function TimelineView({
|
|
|
17811
19151
|
seekTrack,
|
|
17812
19152
|
onAddTrack,
|
|
17813
19153
|
onReorder,
|
|
17814
|
-
|
|
19154
|
+
onItemSelect,
|
|
19155
|
+
onEmptyClick,
|
|
19156
|
+
onMarqueeSelect,
|
|
17815
19157
|
onElementDrag,
|
|
17816
|
-
elementColors
|
|
19158
|
+
elementColors,
|
|
19159
|
+
selectedIds,
|
|
19160
|
+
playheadPositionPx = 0,
|
|
19161
|
+
isPlayheadActive = false,
|
|
19162
|
+
onDropOnTimeline,
|
|
19163
|
+
videoResolution,
|
|
19164
|
+
enableDropOnTimeline = true
|
|
17817
19165
|
}) {
|
|
17818
19166
|
const containerRef = React.useRef(null);
|
|
17819
19167
|
const seekContainerRef = React.useRef(null);
|
|
17820
19168
|
const timelineContentRef = React.useRef(null);
|
|
17821
19169
|
const [, setScrollLeft] = React.useState(0);
|
|
17822
19170
|
const [draggedTimeline, setDraggedTimeline] = React.useState(null);
|
|
17823
|
-
const {
|
|
19171
|
+
const { selectedTrackElement } = React.useMemo(() => {
|
|
17824
19172
|
if (selectedItem && "elements" in selectedItem) {
|
|
17825
|
-
return {
|
|
19173
|
+
return { selectedTrackElement: null };
|
|
17826
19174
|
}
|
|
17827
|
-
return {
|
|
19175
|
+
return { selectedTrackElement: selectedItem };
|
|
17828
19176
|
}, [selectedItem]);
|
|
17829
19177
|
const timelineWidth = Math.max(100, duration * zoomLevel * 100);
|
|
17830
19178
|
const timelineWidthPx = `${timelineWidth}px`;
|
|
@@ -17854,7 +19202,33 @@ function TimelineView({
|
|
|
17854
19202
|
window.removeEventListener("resize", updateWidth);
|
|
17855
19203
|
};
|
|
17856
19204
|
}, [duration, zoomLevel]);
|
|
17857
|
-
|
|
19205
|
+
usePlayheadScroll(containerRef, playheadPositionPx, isPlayheadActive, {
|
|
19206
|
+
labelWidth: LABEL_WIDTH
|
|
19207
|
+
});
|
|
19208
|
+
const { marquee, handleMouseDown: handleMarqueeMouseDown } = useMarqueeSelection({
|
|
19209
|
+
duration,
|
|
19210
|
+
zoomLevel,
|
|
19211
|
+
labelWidth: LABEL_WIDTH,
|
|
19212
|
+
trackCount: (tracks == null ? void 0 : tracks.length) ?? 0,
|
|
19213
|
+
trackHeight: TRACK_HEIGHT,
|
|
19214
|
+
tracks: tracks ?? [],
|
|
19215
|
+
containerRef: timelineContentRef,
|
|
19216
|
+
onMarqueeSelect,
|
|
19217
|
+
onEmptyClick
|
|
19218
|
+
});
|
|
19219
|
+
const { preview, handleDragOver, handleDragLeave, handleDrop } = useTimelineDrop({
|
|
19220
|
+
containerRef: timelineContentRef,
|
|
19221
|
+
scrollContainerRef: containerRef,
|
|
19222
|
+
tracks: tracks ?? [],
|
|
19223
|
+
duration,
|
|
19224
|
+
zoomLevel,
|
|
19225
|
+
labelWidth: LABEL_WIDTH,
|
|
19226
|
+
trackHeight: TRACK_HEIGHT,
|
|
19227
|
+
trackContentWidth: timelineWidth - LABEL_WIDTH,
|
|
19228
|
+
onDrop: onDropOnTimeline ?? (async () => {
|
|
19229
|
+
}),
|
|
19230
|
+
enabled: enableDropOnTimeline && !!onDropOnTimeline && !!videoResolution
|
|
19231
|
+
});
|
|
17858
19232
|
const handleTrackDragStart = (e3, track) => {
|
|
17859
19233
|
setDraggedTimeline(track);
|
|
17860
19234
|
e3.dataTransfer.setData("application/json", JSON.stringify(track));
|
|
@@ -17886,10 +19260,8 @@ function TimelineView({
|
|
|
17886
19260
|
}
|
|
17887
19261
|
setDraggedTimeline(null);
|
|
17888
19262
|
};
|
|
17889
|
-
const handleItemSelection = (
|
|
17890
|
-
|
|
17891
|
-
onSelectionChange(element);
|
|
17892
|
-
}
|
|
19263
|
+
const handleItemSelection = (item, event) => {
|
|
19264
|
+
onItemSelect(item, event);
|
|
17893
19265
|
};
|
|
17894
19266
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
17895
19267
|
"div",
|
|
@@ -17902,44 +19274,106 @@ function TimelineView({
|
|
|
17902
19274
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "twick-seek-track-empty-space", onClick: onAddTrack, children: /* @__PURE__ */ jsxRuntime.jsx(Plus, { color: "white", size: 20 }) }),
|
|
17903
19275
|
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { flexGrow: 1 }, children: seekTrack })
|
|
17904
19276
|
] }) : null }),
|
|
17905
|
-
/* @__PURE__ */ jsxRuntime.
|
|
17906
|
-
|
|
17907
|
-
|
|
17908
|
-
|
|
17909
|
-
|
|
17910
|
-
|
|
17911
|
-
|
|
17912
|
-
|
|
17913
|
-
|
|
17914
|
-
|
|
17915
|
-
|
|
17916
|
-
|
|
17917
|
-
|
|
17918
|
-
|
|
17919
|
-
|
|
17920
|
-
|
|
17921
|
-
|
|
17922
|
-
|
|
17923
|
-
|
|
17924
|
-
|
|
17925
|
-
|
|
17926
|
-
|
|
17927
|
-
|
|
17928
|
-
|
|
17929
|
-
|
|
17930
|
-
|
|
17931
|
-
|
|
19277
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
19278
|
+
"div",
|
|
19279
|
+
{
|
|
19280
|
+
ref: timelineContentRef,
|
|
19281
|
+
style: { width: timelineWidthPx, position: "relative" },
|
|
19282
|
+
onMouseDown: handleMarqueeMouseDown,
|
|
19283
|
+
onDragOver: handleDragOver,
|
|
19284
|
+
onDragLeave: handleDragLeave,
|
|
19285
|
+
onDrop: handleDrop,
|
|
19286
|
+
children: [
|
|
19287
|
+
/* @__PURE__ */ jsxRuntime.jsx(MarqueeOverlay, { marquee }),
|
|
19288
|
+
preview && /* @__PURE__ */ jsxRuntime.jsx(
|
|
19289
|
+
"div",
|
|
19290
|
+
{
|
|
19291
|
+
className: "twick-drop-preview",
|
|
19292
|
+
style: {
|
|
19293
|
+
position: "absolute",
|
|
19294
|
+
left: LABEL_WIDTH + preview.timeSec / duration * (timelineWidth - LABEL_WIDTH),
|
|
19295
|
+
top: preview.trackIndex * TRACK_HEIGHT + 2,
|
|
19296
|
+
width: preview.widthPct / 100 * (timelineWidth - LABEL_WIDTH),
|
|
19297
|
+
height: TRACK_HEIGHT - 4
|
|
19298
|
+
}
|
|
19299
|
+
}
|
|
19300
|
+
),
|
|
19301
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "relative", zIndex: 10 }, children: (tracks || []).map((track) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twick-timeline-container", children: [
|
|
19302
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "twick-timeline-header-container", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
19303
|
+
TrackHeader,
|
|
19304
|
+
{
|
|
19305
|
+
track,
|
|
19306
|
+
selectedIds,
|
|
19307
|
+
onSelect: handleItemSelection,
|
|
19308
|
+
onDragStart: handleTrackDragStart,
|
|
19309
|
+
onDragOver: handleTrackDragOver,
|
|
19310
|
+
onDrop: handleTrackDrop
|
|
19311
|
+
}
|
|
19312
|
+
) }),
|
|
19313
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
19314
|
+
TrackBase,
|
|
19315
|
+
{
|
|
19316
|
+
track,
|
|
19317
|
+
duration,
|
|
19318
|
+
selectedItem: selectedTrackElement,
|
|
19319
|
+
selectedIds,
|
|
19320
|
+
zoom: zoomLevel,
|
|
19321
|
+
allowOverlap: false,
|
|
19322
|
+
trackWidth: timelineWidth - LABEL_WIDTH,
|
|
19323
|
+
onItemSelection: handleItemSelection,
|
|
19324
|
+
onDrag: onElementDrag,
|
|
19325
|
+
elementColors
|
|
19326
|
+
}
|
|
19327
|
+
)
|
|
19328
|
+
] }, track.getId())) })
|
|
19329
|
+
]
|
|
19330
|
+
}
|
|
19331
|
+
)
|
|
17932
19332
|
]
|
|
17933
19333
|
}
|
|
17934
19334
|
);
|
|
17935
19335
|
}
|
|
17936
19336
|
const useTimelineManager = () => {
|
|
17937
|
-
const { selectedItem, changeLog, setSelectedItem, totalDuration, editor } = timeline.useTimelineContext();
|
|
19337
|
+
const { selectedItem, changeLog, setSelectedItem, totalDuration, editor, selectedIds } = timeline.useTimelineContext();
|
|
17938
19338
|
const onElementDrag = ({
|
|
17939
19339
|
element,
|
|
17940
19340
|
dragType,
|
|
17941
19341
|
updates
|
|
17942
19342
|
}) => {
|
|
19343
|
+
var _a;
|
|
19344
|
+
const tracks = ((_a = editor.getTimelineData()) == null ? void 0 : _a.tracks) ?? [];
|
|
19345
|
+
const duration = totalDuration;
|
|
19346
|
+
if (dragType === DRAG_TYPE.MOVE && selectedIds.has(element.getId()) && selectedIds.size > 1) {
|
|
19347
|
+
const resolved = timeline.resolveIds(selectedIds, tracks);
|
|
19348
|
+
const elements = resolved.filter((item) => item instanceof timeline.TrackElement);
|
|
19349
|
+
if (elements.length > 1) {
|
|
19350
|
+
const minStart = Math.min(...elements.map((el) => el.getStart()));
|
|
19351
|
+
const maxEnd = Math.max(...elements.map((el) => el.getEnd()));
|
|
19352
|
+
const delta = updates.start - element.getStart();
|
|
19353
|
+
const deltaMin = -minStart;
|
|
19354
|
+
const deltaMax = duration - maxEnd;
|
|
19355
|
+
const clampedDelta = Math.max(deltaMin, Math.min(deltaMax, delta));
|
|
19356
|
+
for (const el of elements) {
|
|
19357
|
+
const newStart = el.getStart() + clampedDelta;
|
|
19358
|
+
const newEnd = el.getEnd() + clampedDelta;
|
|
19359
|
+
if (el instanceof timeline.VideoElement || el instanceof timeline.AudioElement) {
|
|
19360
|
+
const elementProps = el.getProps();
|
|
19361
|
+
const startDelta = newStart - el.getStart() * ((elementProps == null ? void 0 : elementProps.playbackRate) || 1);
|
|
19362
|
+
if (el instanceof timeline.AudioElement) {
|
|
19363
|
+
el.setStartAt(el.getStartAt() + startDelta);
|
|
19364
|
+
} else {
|
|
19365
|
+
el.setStartAt(el.getStartAt() + startDelta);
|
|
19366
|
+
}
|
|
19367
|
+
}
|
|
19368
|
+
el.setStart(newStart);
|
|
19369
|
+
el.setEnd(newEnd);
|
|
19370
|
+
editor.updateElement(el);
|
|
19371
|
+
}
|
|
19372
|
+
setSelectedItem(element);
|
|
19373
|
+
editor.refresh();
|
|
19374
|
+
return;
|
|
19375
|
+
}
|
|
19376
|
+
}
|
|
17943
19377
|
if (dragType === DRAG_TYPE.START) {
|
|
17944
19378
|
if (element instanceof timeline.VideoElement || element instanceof timeline.AudioElement) {
|
|
17945
19379
|
const elementProps = element.getProps();
|
|
@@ -17986,13 +19420,135 @@ const useTimelineManager = () => {
|
|
|
17986
19420
|
totalDuration
|
|
17987
19421
|
};
|
|
17988
19422
|
};
|
|
19423
|
+
function useTimelineSelection() {
|
|
19424
|
+
const { editor, selectedIds, setSelection, setSelectedItem } = timeline.useTimelineContext();
|
|
19425
|
+
const handleItemSelect = React.useCallback(
|
|
19426
|
+
(item, event) => {
|
|
19427
|
+
var _a;
|
|
19428
|
+
const id2 = item.getId();
|
|
19429
|
+
const isMulti = event.metaKey || event.ctrlKey;
|
|
19430
|
+
const isRange = event.shiftKey;
|
|
19431
|
+
const tracks = ((_a = editor.getTimelineData()) == null ? void 0 : _a.tracks) ?? [];
|
|
19432
|
+
if (isMulti) {
|
|
19433
|
+
setSelection((prev) => {
|
|
19434
|
+
const next = new Set(prev);
|
|
19435
|
+
if (next.has(id2)) {
|
|
19436
|
+
next.delete(id2);
|
|
19437
|
+
} else {
|
|
19438
|
+
next.add(id2);
|
|
19439
|
+
}
|
|
19440
|
+
return next;
|
|
19441
|
+
});
|
|
19442
|
+
return;
|
|
19443
|
+
}
|
|
19444
|
+
if (isRange) {
|
|
19445
|
+
const primaryId = selectedIds.size > 0 ? [...selectedIds][0] : null;
|
|
19446
|
+
if (!primaryId) {
|
|
19447
|
+
setSelectedItem(item);
|
|
19448
|
+
return;
|
|
19449
|
+
}
|
|
19450
|
+
const primary = timeline.resolveId(primaryId, tracks);
|
|
19451
|
+
if (!primary) {
|
|
19452
|
+
setSelectedItem(item);
|
|
19453
|
+
return;
|
|
19454
|
+
}
|
|
19455
|
+
if (item instanceof timeline.Track && primary instanceof timeline.Track) {
|
|
19456
|
+
const trackIds = tracks.map((t2) => t2.getId());
|
|
19457
|
+
const fromIdx = trackIds.indexOf(primary.getId());
|
|
19458
|
+
const toIdx = trackIds.indexOf(item.getId());
|
|
19459
|
+
if (fromIdx !== -1 && toIdx !== -1) {
|
|
19460
|
+
const [start, end] = fromIdx <= toIdx ? [fromIdx, toIdx] : [toIdx, fromIdx];
|
|
19461
|
+
const rangeIds = new Set(
|
|
19462
|
+
trackIds.slice(start, end + 1)
|
|
19463
|
+
);
|
|
19464
|
+
setSelection((prev) => /* @__PURE__ */ new Set([...prev, ...rangeIds]));
|
|
19465
|
+
return;
|
|
19466
|
+
}
|
|
19467
|
+
}
|
|
19468
|
+
if (item instanceof timeline.TrackElement && primary instanceof timeline.TrackElement) {
|
|
19469
|
+
const track = editor.getTrackById(item.getTrackId());
|
|
19470
|
+
const primaryTrack = editor.getTrackById(primary.getTrackId());
|
|
19471
|
+
if (track && primaryTrack && track.getId() === primaryTrack.getId()) {
|
|
19472
|
+
const rangeIds = timeline.getElementIdsInRange(
|
|
19473
|
+
track,
|
|
19474
|
+
primary.getId(),
|
|
19475
|
+
item.getId()
|
|
19476
|
+
);
|
|
19477
|
+
setSelection((prev) => /* @__PURE__ */ new Set([...prev, ...rangeIds]));
|
|
19478
|
+
return;
|
|
19479
|
+
}
|
|
19480
|
+
}
|
|
19481
|
+
}
|
|
19482
|
+
setSelectedItem(item);
|
|
19483
|
+
},
|
|
19484
|
+
[editor, selectedIds, setSelection, setSelectedItem]
|
|
19485
|
+
);
|
|
19486
|
+
const handleEmptyClick = React.useCallback(() => {
|
|
19487
|
+
setSelectedItem(null);
|
|
19488
|
+
}, [setSelectedItem]);
|
|
19489
|
+
const handleMarqueeSelect = React.useCallback(
|
|
19490
|
+
(ids) => {
|
|
19491
|
+
setSelection(ids);
|
|
19492
|
+
},
|
|
19493
|
+
[setSelection]
|
|
19494
|
+
);
|
|
19495
|
+
return { handleItemSelect, handleEmptyClick, handleMarqueeSelect };
|
|
19496
|
+
}
|
|
17989
19497
|
const TimelineManager = ({
|
|
17990
19498
|
trackZoom,
|
|
17991
19499
|
timelineTickConfigs,
|
|
17992
19500
|
elementColors
|
|
17993
19501
|
}) => {
|
|
17994
19502
|
var _a;
|
|
17995
|
-
const {
|
|
19503
|
+
const { playerState } = livePlayer.useLivePlayerContext();
|
|
19504
|
+
const { followPlayheadEnabled, editor, videoResolution, setSelectedItem } = timeline.useTimelineContext();
|
|
19505
|
+
const {
|
|
19506
|
+
timelineData,
|
|
19507
|
+
totalDuration,
|
|
19508
|
+
selectedItem,
|
|
19509
|
+
onAddTrack,
|
|
19510
|
+
onReorder,
|
|
19511
|
+
onElementDrag,
|
|
19512
|
+
onSeek
|
|
19513
|
+
} = useTimelineManager();
|
|
19514
|
+
const { selectedIds } = timeline.useTimelineContext();
|
|
19515
|
+
const { handleItemSelect, handleEmptyClick, handleMarqueeSelect } = useTimelineSelection();
|
|
19516
|
+
const [playheadState, setPlayheadState] = React.useState({
|
|
19517
|
+
positionPx: 0,
|
|
19518
|
+
isDragging: false
|
|
19519
|
+
});
|
|
19520
|
+
const handlePlayheadUpdate = React.useCallback((state) => {
|
|
19521
|
+
setPlayheadState(state);
|
|
19522
|
+
}, []);
|
|
19523
|
+
const isPlayheadActive = followPlayheadEnabled && playerState === livePlayer.PLAYER_STATE.PLAYING || playheadState.isDragging;
|
|
19524
|
+
const handleDropOnTimeline = React.useCallback(
|
|
19525
|
+
async (params) => {
|
|
19526
|
+
const { track, timeSec, type, url } = params;
|
|
19527
|
+
const element = createElementFromDrop(type, url, videoResolution);
|
|
19528
|
+
element.setStart(timeSec);
|
|
19529
|
+
const targetTrack = track ?? editor.addTrack(`Track_${Date.now()}`);
|
|
19530
|
+
const tryAdd = async (t2) => {
|
|
19531
|
+
var _a2;
|
|
19532
|
+
try {
|
|
19533
|
+
const result = await editor.addElementToTrack(t2, element);
|
|
19534
|
+
if (result) {
|
|
19535
|
+
setSelectedItem(element);
|
|
19536
|
+
return true;
|
|
19537
|
+
}
|
|
19538
|
+
} catch (err) {
|
|
19539
|
+
if (err instanceof timeline.ValidationError && ((_a2 = err.errors) == null ? void 0 : _a2.includes(timeline.VALIDATION_ERROR_CODE.COLLISION_ERROR))) {
|
|
19540
|
+
const newTrack = editor.addTrack(`Track_${Date.now()}`);
|
|
19541
|
+
return tryAdd(newTrack);
|
|
19542
|
+
}
|
|
19543
|
+
throw err;
|
|
19544
|
+
}
|
|
19545
|
+
return false;
|
|
19546
|
+
};
|
|
19547
|
+
await tryAdd(targetTrack);
|
|
19548
|
+
editor.refresh();
|
|
19549
|
+
},
|
|
19550
|
+
[editor, videoResolution, setSelectedItem]
|
|
19551
|
+
);
|
|
17996
19552
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
17997
19553
|
TimelineView,
|
|
17998
19554
|
{
|
|
@@ -18000,14 +19556,22 @@ const TimelineManager = ({
|
|
|
18000
19556
|
zoomLevel: trackZoom,
|
|
18001
19557
|
duration: totalDuration,
|
|
18002
19558
|
selectedItem,
|
|
19559
|
+
selectedIds,
|
|
18003
19560
|
onDeletion: () => {
|
|
18004
19561
|
},
|
|
18005
19562
|
onAddTrack,
|
|
18006
19563
|
onReorder,
|
|
18007
19564
|
onElementDrag,
|
|
18008
19565
|
onSeek,
|
|
18009
|
-
|
|
19566
|
+
onItemSelect: handleItemSelect,
|
|
19567
|
+
onEmptyClick: handleEmptyClick,
|
|
19568
|
+
onMarqueeSelect: handleMarqueeSelect,
|
|
18010
19569
|
elementColors,
|
|
19570
|
+
playheadPositionPx: playheadState.positionPx,
|
|
19571
|
+
isPlayheadActive,
|
|
19572
|
+
onDropOnTimeline: handleDropOnTimeline,
|
|
19573
|
+
videoResolution,
|
|
19574
|
+
enableDropOnTimeline: true,
|
|
18011
19575
|
seekTrack: /* @__PURE__ */ jsxRuntime.jsx(
|
|
18012
19576
|
SeekControl,
|
|
18013
19577
|
{
|
|
@@ -18015,7 +19579,8 @@ const TimelineManager = ({
|
|
|
18015
19579
|
zoom: trackZoom,
|
|
18016
19580
|
onSeek,
|
|
18017
19581
|
timelineCount: ((_a = timelineData == null ? void 0 : timelineData.tracks) == null ? void 0 : _a.length) ?? 0,
|
|
18018
|
-
timelineTickConfigs
|
|
19582
|
+
timelineTickConfigs,
|
|
19583
|
+
onPlayheadUpdate: handlePlayheadUpdate
|
|
18019
19584
|
}
|
|
18020
19585
|
)
|
|
18021
19586
|
}
|
|
@@ -18045,6 +19610,7 @@ const UndoRedoControls = ({ canUndo, canRedo, onUndo, onRedo }) => {
|
|
|
18045
19610
|
};
|
|
18046
19611
|
const PlayerControls = ({
|
|
18047
19612
|
selectedItem,
|
|
19613
|
+
selectedIds = /* @__PURE__ */ new Set(),
|
|
18048
19614
|
duration,
|
|
18049
19615
|
currentTime,
|
|
18050
19616
|
playerState,
|
|
@@ -18058,21 +19624,31 @@ const PlayerControls = ({
|
|
|
18058
19624
|
zoomLevel = 1,
|
|
18059
19625
|
setZoomLevel,
|
|
18060
19626
|
className = "",
|
|
18061
|
-
zoomConfig = DEFAULT_TIMELINE_ZOOM_CONFIG
|
|
19627
|
+
zoomConfig = DEFAULT_TIMELINE_ZOOM_CONFIG,
|
|
19628
|
+
fps = DEFAULT_FPS,
|
|
19629
|
+
onSeek,
|
|
19630
|
+
followPlayheadEnabled = true,
|
|
19631
|
+
onFollowPlayheadToggle
|
|
18062
19632
|
}) => {
|
|
18063
19633
|
const MAX_ZOOM = zoomConfig.max;
|
|
18064
19634
|
const MIN_ZOOM = zoomConfig.min;
|
|
18065
19635
|
const ZOOM_STEP = zoomConfig.step;
|
|
18066
|
-
const formatTime = React.useCallback(
|
|
18067
|
-
|
|
18068
|
-
|
|
18069
|
-
|
|
18070
|
-
|
|
19636
|
+
const formatTime = React.useCallback(
|
|
19637
|
+
(time2) => timeline.formatTimeWithFrames(time2, fps),
|
|
19638
|
+
[fps]
|
|
19639
|
+
);
|
|
19640
|
+
const handleSeekToStart = React.useCallback(() => {
|
|
19641
|
+
onSeek == null ? void 0 : onSeek(0);
|
|
19642
|
+
}, [onSeek]);
|
|
19643
|
+
const handleSeekToEnd = React.useCallback(() => {
|
|
19644
|
+
onSeek == null ? void 0 : onSeek(duration);
|
|
19645
|
+
}, [onSeek, duration]);
|
|
18071
19646
|
const handleDelete = React.useCallback(() => {
|
|
18072
|
-
if (
|
|
18073
|
-
onDelete(
|
|
19647
|
+
if (selectedIds.size > 0 && onDelete) {
|
|
19648
|
+
onDelete();
|
|
18074
19649
|
}
|
|
18075
|
-
}, [
|
|
19650
|
+
}, [selectedIds.size, onDelete]);
|
|
19651
|
+
const hasSelection = selectedIds.size > 0;
|
|
18076
19652
|
const handleSplit = React.useCallback(() => {
|
|
18077
19653
|
if (selectedItem instanceof timeline.TrackElement && onSplit) {
|
|
18078
19654
|
onSplit(selectedItem, currentTime);
|
|
@@ -18094,9 +19670,9 @@ const PlayerControls = ({
|
|
|
18094
19670
|
"button",
|
|
18095
19671
|
{
|
|
18096
19672
|
onClick: handleDelete,
|
|
18097
|
-
disabled: !
|
|
19673
|
+
disabled: !hasSelection,
|
|
18098
19674
|
title: "Delete",
|
|
18099
|
-
className: `control-btn delete-btn ${!
|
|
19675
|
+
className: `control-btn delete-btn ${!hasSelection ? "btn-disabled" : ""}`,
|
|
18100
19676
|
children: /* @__PURE__ */ jsxRuntime.jsx(Trash2, { className: "icon-md" })
|
|
18101
19677
|
}
|
|
18102
19678
|
),
|
|
@@ -18121,6 +19697,25 @@ const PlayerControls = ({
|
|
|
18121
19697
|
)
|
|
18122
19698
|
] }),
|
|
18123
19699
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "playback-controls", children: [
|
|
19700
|
+
onFollowPlayheadToggle && /* @__PURE__ */ jsxRuntime.jsx(
|
|
19701
|
+
"button",
|
|
19702
|
+
{
|
|
19703
|
+
onClick: onFollowPlayheadToggle,
|
|
19704
|
+
title: followPlayheadEnabled ? "Follow playhead on (click to disable)" : "Follow playhead off (click to enable)",
|
|
19705
|
+
className: `control-btn ${followPlayheadEnabled ? "follow-btn-active" : ""}`,
|
|
19706
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Crosshair, { className: "icon-md" })
|
|
19707
|
+
}
|
|
19708
|
+
),
|
|
19709
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
19710
|
+
"button",
|
|
19711
|
+
{
|
|
19712
|
+
onClick: handleSeekToStart,
|
|
19713
|
+
disabled: playerState === livePlayer.PLAYER_STATE.REFRESH,
|
|
19714
|
+
title: "Jump to start",
|
|
19715
|
+
className: "control-btn",
|
|
19716
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(SkipBack, { className: "icon-md" })
|
|
19717
|
+
}
|
|
19718
|
+
),
|
|
18124
19719
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
18125
19720
|
"button",
|
|
18126
19721
|
{
|
|
@@ -18131,6 +19726,16 @@ const PlayerControls = ({
|
|
|
18131
19726
|
children: playerState === livePlayer.PLAYER_STATE.PLAYING ? /* @__PURE__ */ jsxRuntime.jsx(Pause, { className: "icon-lg" }) : playerState === livePlayer.PLAYER_STATE.REFRESH ? /* @__PURE__ */ jsxRuntime.jsx(LoaderCircle, { className: "icon-lg animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(Play, { className: "icon-lg" })
|
|
18132
19727
|
}
|
|
18133
19728
|
),
|
|
19729
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
19730
|
+
"button",
|
|
19731
|
+
{
|
|
19732
|
+
onClick: handleSeekToEnd,
|
|
19733
|
+
disabled: playerState === livePlayer.PLAYER_STATE.REFRESH,
|
|
19734
|
+
title: "Jump to end",
|
|
19735
|
+
className: "control-btn",
|
|
19736
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(SkipForward, { className: "icon-md" })
|
|
19737
|
+
}
|
|
19738
|
+
),
|
|
18134
19739
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "time-display", children: [
|
|
18135
19740
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "current-time", children: formatTime(currentTime) }),
|
|
18136
19741
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "time-separator", children: "/" }),
|
|
@@ -18192,12 +19797,17 @@ const usePlayerControl = () => {
|
|
|
18192
19797
|
};
|
|
18193
19798
|
};
|
|
18194
19799
|
const useTimelineControl = () => {
|
|
18195
|
-
const { editor, setSelectedItem } = timeline.useTimelineContext();
|
|
19800
|
+
const { editor, setSelectedItem, selectedIds } = timeline.useTimelineContext();
|
|
18196
19801
|
const deleteItem = (item) => {
|
|
18197
|
-
|
|
18198
|
-
|
|
18199
|
-
|
|
18200
|
-
|
|
19802
|
+
var _a;
|
|
19803
|
+
const tracks = ((_a = editor.getTimelineData()) == null ? void 0 : _a.tracks) ?? [];
|
|
19804
|
+
const toDelete = item !== void 0 ? [item] : timeline.resolveIds(selectedIds, tracks);
|
|
19805
|
+
for (const el of toDelete) {
|
|
19806
|
+
if (el instanceof timeline.Track) {
|
|
19807
|
+
editor.removeTrack(el);
|
|
19808
|
+
} else if (el instanceof timeline.TrackElement) {
|
|
19809
|
+
editor.removeElement(el);
|
|
19810
|
+
}
|
|
18201
19811
|
}
|
|
18202
19812
|
setSelectedItem(null);
|
|
18203
19813
|
};
|
|
@@ -18217,32 +19827,97 @@ const useTimelineControl = () => {
|
|
|
18217
19827
|
handleRedo
|
|
18218
19828
|
};
|
|
18219
19829
|
};
|
|
19830
|
+
function shouldIgnoreKeydown() {
|
|
19831
|
+
const active = document.activeElement;
|
|
19832
|
+
if (!active) return false;
|
|
19833
|
+
const tag = active.tagName.toLowerCase();
|
|
19834
|
+
if (tag === "input" || tag === "textarea") return true;
|
|
19835
|
+
if (active.isContentEditable) return true;
|
|
19836
|
+
return false;
|
|
19837
|
+
}
|
|
19838
|
+
function useCanvasKeyboard({
|
|
19839
|
+
onDelete,
|
|
19840
|
+
onUndo,
|
|
19841
|
+
onRedo,
|
|
19842
|
+
enabled = true
|
|
19843
|
+
}) {
|
|
19844
|
+
React.useEffect(() => {
|
|
19845
|
+
if (!enabled) return;
|
|
19846
|
+
const handleKeyDown = (e3) => {
|
|
19847
|
+
if (shouldIgnoreKeydown()) return;
|
|
19848
|
+
const key = e3.key.toLowerCase();
|
|
19849
|
+
const hasPrimaryModifier = e3.metaKey || e3.ctrlKey;
|
|
19850
|
+
if (hasPrimaryModifier) {
|
|
19851
|
+
if (key === "z" && !e3.shiftKey) {
|
|
19852
|
+
e3.preventDefault();
|
|
19853
|
+
onUndo == null ? void 0 : onUndo();
|
|
19854
|
+
return;
|
|
19855
|
+
}
|
|
19856
|
+
if (key === "y" || key === "z" && e3.shiftKey) {
|
|
19857
|
+
e3.preventDefault();
|
|
19858
|
+
onRedo == null ? void 0 : onRedo();
|
|
19859
|
+
return;
|
|
19860
|
+
}
|
|
19861
|
+
}
|
|
19862
|
+
if (!hasPrimaryModifier && (key === "delete" || key === "backspace")) {
|
|
19863
|
+
e3.preventDefault();
|
|
19864
|
+
onDelete == null ? void 0 : onDelete();
|
|
19865
|
+
}
|
|
19866
|
+
};
|
|
19867
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
19868
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
19869
|
+
}, [enabled, onDelete]);
|
|
19870
|
+
}
|
|
18220
19871
|
const ControlManager = ({
|
|
18221
19872
|
trackZoom,
|
|
18222
19873
|
setTrackZoom,
|
|
18223
|
-
zoomConfig
|
|
19874
|
+
zoomConfig,
|
|
19875
|
+
fps
|
|
18224
19876
|
}) => {
|
|
18225
|
-
const { currentTime, playerState } = livePlayer.useLivePlayerContext();
|
|
19877
|
+
const { currentTime, playerState, setSeekTime, setCurrentTime } = livePlayer.useLivePlayerContext();
|
|
18226
19878
|
const { togglePlayback } = usePlayerControl();
|
|
18227
|
-
const {
|
|
19879
|
+
const {
|
|
19880
|
+
canRedo,
|
|
19881
|
+
canUndo,
|
|
19882
|
+
totalDuration,
|
|
19883
|
+
selectedItem,
|
|
19884
|
+
selectedIds,
|
|
19885
|
+
followPlayheadEnabled,
|
|
19886
|
+
setFollowPlayheadEnabled
|
|
19887
|
+
} = timeline.useTimelineContext();
|
|
18228
19888
|
const { deleteItem, splitElement, handleUndo, handleRedo } = useTimelineControl();
|
|
19889
|
+
useCanvasKeyboard({
|
|
19890
|
+
onDelete: () => deleteItem(),
|
|
19891
|
+
onUndo: () => handleUndo(),
|
|
19892
|
+
onRedo: () => handleRedo()
|
|
19893
|
+
});
|
|
19894
|
+
const handleSeek = (time2) => {
|
|
19895
|
+
const clamped = Math.max(0, Math.min(totalDuration, time2));
|
|
19896
|
+
setCurrentTime(clamped);
|
|
19897
|
+
setSeekTime(clamped);
|
|
19898
|
+
};
|
|
18229
19899
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twick-editor-timeline-controls", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
18230
19900
|
PlayerControls,
|
|
18231
19901
|
{
|
|
18232
19902
|
selectedItem,
|
|
19903
|
+
selectedIds,
|
|
18233
19904
|
duration: totalDuration,
|
|
18234
19905
|
currentTime,
|
|
18235
19906
|
playerState,
|
|
18236
19907
|
togglePlayback,
|
|
18237
19908
|
canUndo,
|
|
18238
19909
|
canRedo,
|
|
18239
|
-
onDelete: deleteItem,
|
|
19910
|
+
onDelete: () => deleteItem(),
|
|
18240
19911
|
onSplit: splitElement,
|
|
18241
19912
|
onUndo: handleUndo,
|
|
18242
19913
|
onRedo: handleRedo,
|
|
18243
19914
|
zoomLevel: trackZoom,
|
|
18244
19915
|
setZoomLevel: setTrackZoom,
|
|
18245
|
-
zoomConfig
|
|
19916
|
+
zoomConfig,
|
|
19917
|
+
fps: fps ?? DEFAULT_FPS,
|
|
19918
|
+
onSeek: handleSeek,
|
|
19919
|
+
followPlayheadEnabled,
|
|
19920
|
+
onFollowPlayheadToggle: () => setFollowPlayheadEnabled(!followPlayheadEnabled)
|
|
18246
19921
|
}
|
|
18247
19922
|
) });
|
|
18248
19923
|
};
|
|
@@ -18269,7 +19944,8 @@ const VideoEditor = ({
|
|
|
18269
19944
|
{
|
|
18270
19945
|
videoProps: editorConfig.videoProps,
|
|
18271
19946
|
playerProps: editorConfig.playerProps,
|
|
18272
|
-
canvasMode: editorConfig.canvasMode ?? true
|
|
19947
|
+
canvasMode: editorConfig.canvasMode ?? true,
|
|
19948
|
+
canvasConfig: editorConfig.canvasConfig
|
|
18273
19949
|
}
|
|
18274
19950
|
),
|
|
18275
19951
|
[editorConfig]
|
|
@@ -18287,7 +19963,8 @@ const VideoEditor = ({
|
|
|
18287
19963
|
{
|
|
18288
19964
|
trackZoom,
|
|
18289
19965
|
setTrackZoom,
|
|
18290
|
-
zoomConfig
|
|
19966
|
+
zoomConfig,
|
|
19967
|
+
fps: editorConfig.fps
|
|
18291
19968
|
}
|
|
18292
19969
|
) : null,
|
|
18293
19970
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -18691,6 +20368,7 @@ exports.AVAILABLE_TEXT_FONTS = AVAILABLE_TEXT_FONTS;
|
|
|
18691
20368
|
exports.BaseMediaManager = BaseMediaManager;
|
|
18692
20369
|
exports.BrowserMediaManager = BrowserMediaManager;
|
|
18693
20370
|
exports.DEFAULT_ELEMENT_COLORS = DEFAULT_ELEMENT_COLORS;
|
|
20371
|
+
exports.DEFAULT_FPS = DEFAULT_FPS;
|
|
18694
20372
|
exports.DEFAULT_TIMELINE_TICK_CONFIGS = DEFAULT_TIMELINE_TICK_CONFIGS;
|
|
18695
20373
|
exports.DEFAULT_TIMELINE_ZOOM = DEFAULT_TIMELINE_ZOOM;
|
|
18696
20374
|
exports.DEFAULT_TIMELINE_ZOOM_CONFIG = DEFAULT_TIMELINE_ZOOM_CONFIG;
|
|
@@ -18698,7 +20376,9 @@ exports.DRAG_TYPE = DRAG_TYPE;
|
|
|
18698
20376
|
exports.INITIAL_TIMELINE_DATA = INITIAL_TIMELINE_DATA;
|
|
18699
20377
|
exports.MIN_DURATION = MIN_DURATION;
|
|
18700
20378
|
exports.PlayerControls = PlayerControls;
|
|
20379
|
+
exports.SNAP_THRESHOLD_PX = SNAP_THRESHOLD_PX;
|
|
18701
20380
|
exports.TEXT_EFFECTS = TEXT_EFFECTS;
|
|
20381
|
+
exports.TIMELINE_DROP_MEDIA_TYPE = TIMELINE_DROP_MEDIA_TYPE;
|
|
18702
20382
|
exports.TimelineManager = TimelineManager;
|
|
18703
20383
|
exports.animationGifs = animationGifs;
|
|
18704
20384
|
exports.default = VideoEditor;
|