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