@twick/video-editor 0.15.19 → 0.15.21

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/index.js CHANGED
@@ -6857,8 +6857,8 @@ const DEFAULT_TEXT_PROPS = {
6857
6857
  lineWidth: 0
6858
6858
  };
6859
6859
  const DEFAULT_CAPTION_PROPS = {
6860
- /** Font family for caption elements */
6861
- family: "Poppins",
6860
+ /** Font family for caption elements (matches highlight_bg default) */
6861
+ family: "Bangers",
6862
6862
  /** Font size in pixels */
6863
6863
  size: 48,
6864
6864
  /** Text fill color */
@@ -6902,7 +6902,13 @@ const ELEMENT_TYPES = {
6902
6902
  /** Rectangle element type */
6903
6903
  RECT: "rect",
6904
6904
  /** Circle element type */
6905
- CIRCLE: "circle"
6905
+ CIRCLE: "circle",
6906
+ /** Arrow annotation element type */
6907
+ ARROW: "arrow",
6908
+ /** Line annotation / shape element type */
6909
+ LINE: "line",
6910
+ /** Global / adjustment-layer style effect element */
6911
+ EFFECT: "effect"
6906
6912
  };
6907
6913
  const isBrowser$2 = typeof window !== "undefined";
6908
6914
  const isCanvasSupported = isBrowser$2 && !!window.HTMLCanvasElement;
@@ -7646,40 +7652,44 @@ const addCaptionElement = ({
7646
7652
  canvasMetadata,
7647
7653
  lockAspectRatio = false
7648
7654
  }) => {
7649
- var _a, _b, _c, _d, _e2, _f, _g, _h2, _i2, _j, _k, _l, _m, _n2, _o2, _p, _q, _r2, _s2, _t2, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J;
7655
+ 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, _K, _L;
7656
+ const applyToAll = (captionProps == null ? void 0 : captionProps.applyToAll) ?? false;
7657
+ const captionTextColor = ((_a = captionProps == null ? void 0 : captionProps.colors) == null ? void 0 : _a.text) ?? ((_b = captionProps == null ? void 0 : captionProps.color) == null ? void 0 : _b.text);
7650
7658
  const { x: x2, y: y2 } = convertToCanvasPosition(
7651
- ((captionProps == null ? void 0 : captionProps.applyToAll) ? captionProps == null ? void 0 : captionProps.x : ((_a = element.props) == null ? void 0 : _a.x) ?? (captionProps == null ? void 0 : captionProps.x)) ?? 0,
7652
- ((captionProps == null ? void 0 : captionProps.applyToAll) ? captionProps == null ? void 0 : captionProps.y : ((_b = element.props) == null ? void 0 : _b.y) ?? (captionProps == null ? void 0 : captionProps.y)) ?? 0,
7659
+ (applyToAll ? captionProps == null ? void 0 : captionProps.x : ((_c = element.props) == null ? void 0 : _c.x) ?? (captionProps == null ? void 0 : captionProps.x)) ?? 0,
7660
+ (applyToAll ? captionProps == null ? void 0 : captionProps.y : ((_d = element.props) == null ? void 0 : _d.y) ?? (captionProps == null ? void 0 : captionProps.y)) ?? 0,
7653
7661
  canvasMetadata
7654
7662
  );
7655
- let width = ((_c = element.props) == null ? void 0 : _c.width) ? element.props.width * canvasMetadata.scaleX : canvasMetadata.width - 2 * MARGIN;
7656
- if ((_d = element.props) == null ? void 0 : _d.maxWidth) {
7663
+ let width = ((_e2 = element.props) == null ? void 0 : _e2.width) ? element.props.width * canvasMetadata.scaleX : canvasMetadata.width - 2 * MARGIN;
7664
+ if ((_f = element.props) == null ? void 0 : _f.maxWidth) {
7657
7665
  width = Math.min(width, element.props.maxWidth * canvasMetadata.scaleX);
7658
7666
  }
7659
- const caption = new Uo(((_e2 = element.props) == null ? void 0 : _e2.text) || element.t || "", {
7667
+ const elementColors = (_g = element.props) == null ? void 0 : _g.colors;
7668
+ const resolvedFill = (applyToAll ? captionTextColor : ((_h2 = element.props) == null ? void 0 : _h2.fill) ?? (elementColors == null ? void 0 : elementColors.text) ?? captionTextColor) ?? DEFAULT_CAPTION_PROPS.fill;
7669
+ const caption = new Uo(((_i2 = element.props) == null ? void 0 : _i2.text) || element.t || "", {
7660
7670
  left: x2,
7661
7671
  top: y2,
7662
7672
  originX: "center",
7663
7673
  originY: "center",
7664
- angle: ((_f = element.props) == null ? void 0 : _f.rotation) || 0,
7674
+ angle: ((_j = element.props) == null ? void 0 : _j.rotation) || 0,
7665
7675
  fontSize: Math.round(
7666
- (((captionProps == null ? void 0 : captionProps.applyToAll) ? (_g = captionProps == null ? void 0 : captionProps.font) == null ? void 0 : _g.size : ((_i2 = (_h2 = element.props) == null ? void 0 : _h2.font) == null ? void 0 : _i2.size) ?? ((_j = captionProps == null ? void 0 : captionProps.font) == null ? void 0 : _j.size)) ?? DEFAULT_CAPTION_PROPS.size) * canvasMetadata.scaleX
7676
+ ((applyToAll ? (_k = captionProps == null ? void 0 : captionProps.font) == null ? void 0 : _k.size : ((_m = (_l = element.props) == null ? void 0 : _l.font) == null ? void 0 : _m.size) ?? ((_n2 = captionProps == null ? void 0 : captionProps.font) == null ? void 0 : _n2.size)) ?? DEFAULT_CAPTION_PROPS.size) * canvasMetadata.scaleX
7667
7677
  ),
7668
- fontFamily: ((captionProps == null ? void 0 : captionProps.applyToAll) ? (_k = captionProps == null ? void 0 : captionProps.font) == null ? void 0 : _k.family : ((_m = (_l = element.props) == null ? void 0 : _l.font) == null ? void 0 : _m.family) ?? ((_n2 = captionProps == null ? void 0 : captionProps.font) == null ? void 0 : _n2.family)) ?? DEFAULT_CAPTION_PROPS.family,
7669
- fill: ((captionProps == null ? void 0 : captionProps.applyToAll) ? (_o2 = captionProps.color) == null ? void 0 : _o2.text : ((_p = element.props) == null ? void 0 : _p.fill) ?? ((_q = captionProps.color) == null ? void 0 : _q.text)) ?? DEFAULT_CAPTION_PROPS.fill,
7670
- fontWeight: ((captionProps == null ? void 0 : captionProps.applyToAll) ? (_r2 = captionProps == null ? void 0 : captionProps.font) == null ? void 0 : _r2.weight : ((_s2 = element.props) == null ? void 0 : _s2.fontWeight) ?? ((_t2 = captionProps == null ? void 0 : captionProps.font) == null ? void 0 : _t2.weight)) ?? DEFAULT_CAPTION_PROPS.fontWeight,
7671
- stroke: ((captionProps == null ? void 0 : captionProps.applyToAll) ? captionProps == null ? void 0 : captionProps.stroke : ((_u = element.props) == null ? void 0 : _u.stroke) ?? (captionProps == null ? void 0 : captionProps.stroke)) ?? DEFAULT_CAPTION_PROPS.stroke,
7672
- opacity: ((captionProps == null ? void 0 : captionProps.applyToAll) ? captionProps == null ? void 0 : captionProps.opacity : ((_v = element.props) == null ? void 0 : _v.opacity) ?? (captionProps == null ? void 0 : captionProps.opacity)) ?? 1,
7678
+ fontFamily: (applyToAll ? (_o2 = captionProps == null ? void 0 : captionProps.font) == null ? void 0 : _o2.family : ((_q = (_p = element.props) == null ? void 0 : _p.font) == null ? void 0 : _q.family) ?? ((_r2 = captionProps == null ? void 0 : captionProps.font) == null ? void 0 : _r2.family)) ?? DEFAULT_CAPTION_PROPS.family,
7679
+ fill: resolvedFill,
7680
+ fontWeight: (applyToAll ? (_s2 = captionProps == null ? void 0 : captionProps.font) == null ? void 0 : _s2.weight : ((_u = (_t2 = element.props) == null ? void 0 : _t2.font) == null ? void 0 : _u.weight) ?? ((_v = captionProps == null ? void 0 : captionProps.font) == null ? void 0 : _v.weight)) ?? DEFAULT_CAPTION_PROPS.fontWeight,
7681
+ stroke: (applyToAll ? captionProps == null ? void 0 : captionProps.stroke : ((_w = element.props) == null ? void 0 : _w.stroke) ?? (captionProps == null ? void 0 : captionProps.stroke)) ?? DEFAULT_CAPTION_PROPS.stroke,
7682
+ opacity: (applyToAll ? captionProps == null ? void 0 : captionProps.opacity : ((_x = element.props) == null ? void 0 : _x.opacity) ?? (captionProps == null ? void 0 : captionProps.opacity)) ?? 1,
7673
7683
  width,
7674
7684
  splitByGrapheme: false,
7675
- textAlign: ((_w = element.props) == null ? void 0 : _w.textAlign) ?? "center",
7685
+ textAlign: ((_y = element.props) == null ? void 0 : _y.textAlign) ?? "center",
7676
7686
  shadow: new Ds({
7677
- offsetX: ((captionProps == null ? void 0 : captionProps.applyToAll) ? (_x = captionProps == null ? void 0 : captionProps.shadowOffset) == null ? void 0 : _x[0] : ((_z = (_y = element.props) == null ? void 0 : _y.shadowOffset) == null ? void 0 : _z[0]) ?? ((_A = captionProps == null ? void 0 : captionProps.shadowOffset) == null ? void 0 : _A[0])) ?? ((_B = DEFAULT_CAPTION_PROPS.shadowOffset) == null ? void 0 : _B[0]),
7678
- offsetY: ((captionProps == null ? void 0 : captionProps.applyToAll) ? (_C = captionProps == null ? void 0 : captionProps.shadowOffset) == null ? void 0 : _C[1] : ((_E = (_D = element.props) == null ? void 0 : _D.shadowOffset) == null ? void 0 : _E[1]) ?? ((_F = captionProps == null ? void 0 : captionProps.shadowOffset) == null ? void 0 : _F[1])) ?? ((_G = DEFAULT_CAPTION_PROPS.shadowOffset) == null ? void 0 : _G[1]),
7679
- blur: ((captionProps == null ? void 0 : captionProps.applyToAll) ? captionProps == null ? void 0 : captionProps.shadowBlur : ((_H = element.props) == null ? void 0 : _H.shadowBlur) ?? (captionProps == null ? void 0 : captionProps.shadowBlur)) ?? DEFAULT_CAPTION_PROPS.shadowBlur,
7680
- color: ((captionProps == null ? void 0 : captionProps.applyToAll) ? captionProps == null ? void 0 : captionProps.shadowColor : ((_I = element.props) == null ? void 0 : _I.shadowColor) ?? (captionProps == null ? void 0 : captionProps.shadowColor)) ?? DEFAULT_CAPTION_PROPS.shadowColor
7687
+ offsetX: (applyToAll ? (_z = captionProps == null ? void 0 : captionProps.shadowOffset) == null ? void 0 : _z[0] : ((_B = (_A = element.props) == null ? void 0 : _A.shadowOffset) == null ? void 0 : _B[0]) ?? ((_C = captionProps == null ? void 0 : captionProps.shadowOffset) == null ? void 0 : _C[0])) ?? ((_D = DEFAULT_CAPTION_PROPS.shadowOffset) == null ? void 0 : _D[0]),
7688
+ offsetY: (applyToAll ? (_E = captionProps == null ? void 0 : captionProps.shadowOffset) == null ? void 0 : _E[1] : ((_G = (_F = element.props) == null ? void 0 : _F.shadowOffset) == null ? void 0 : _G[1]) ?? ((_H = captionProps == null ? void 0 : captionProps.shadowOffset) == null ? void 0 : _H[1])) ?? ((_I = DEFAULT_CAPTION_PROPS.shadowOffset) == null ? void 0 : _I[1]),
7689
+ blur: (applyToAll ? captionProps == null ? void 0 : captionProps.shadowBlur : ((_J = element.props) == null ? void 0 : _J.shadowBlur) ?? (captionProps == null ? void 0 : captionProps.shadowBlur)) ?? DEFAULT_CAPTION_PROPS.shadowBlur,
7690
+ color: (applyToAll ? captionProps == null ? void 0 : captionProps.shadowColor : ((_K = element.props) == null ? void 0 : _K.shadowColor) ?? (captionProps == null ? void 0 : captionProps.shadowColor)) ?? DEFAULT_CAPTION_PROPS.shadowColor
7681
7691
  }),
7682
- strokeWidth: ((captionProps == null ? void 0 : captionProps.applyToAll) ? captionProps == null ? void 0 : captionProps.lineWidth : ((_J = element.props) == null ? void 0 : _J.lineWidth) ?? (captionProps == null ? void 0 : captionProps.lineWidth)) ?? DEFAULT_CAPTION_PROPS.lineWidth
7692
+ strokeWidth: (applyToAll ? captionProps == null ? void 0 : captionProps.lineWidth : ((_L = element.props) == null ? void 0 : _L.lineWidth) ?? (captionProps == null ? void 0 : captionProps.lineWidth)) ?? DEFAULT_CAPTION_PROPS.lineWidth
7683
7693
  });
7684
7694
  caption.set("id", element.id);
7685
7695
  caption.set("zIndex", index);
@@ -8390,6 +8400,148 @@ const WatermarkElement = {
8390
8400
  };
8391
8401
  }
8392
8402
  };
8403
+ const ArrowElement = {
8404
+ name: ELEMENT_TYPES.ARROW,
8405
+ async add(params) {
8406
+ var _a, _b, _c, _d, _e2, _f, _g, _h2, _i2;
8407
+ const { element, index, canvas, canvasMetadata, lockAspectRatio } = params;
8408
+ const baseWidth = ((_a = element.props) == null ? void 0 : _a.width) ?? 220;
8409
+ const baseHeight = ((_b = element.props) == null ? void 0 : _b.height) ?? 14;
8410
+ const { x: x2, y: y2 } = convertToCanvasPosition(
8411
+ ((_c = element.props) == null ? void 0 : _c.x) ?? 0,
8412
+ ((_d = element.props) == null ? void 0 : _d.y) ?? 0,
8413
+ canvasMetadata
8414
+ );
8415
+ const fill = ((_e2 = element.props) == null ? void 0 : _e2.fill) || "#f59e0b";
8416
+ const radius = (((_f = element.props) == null ? void 0 : _f.radius) ?? 4) * canvasMetadata.scaleX;
8417
+ const barWidth = baseWidth * canvasMetadata.scaleX;
8418
+ const barHeight = baseHeight * canvasMetadata.scaleY;
8419
+ const headSize = barHeight * 1.8;
8420
+ const barLength = barWidth - headSize * 0.5;
8421
+ const bar = new jr({
8422
+ left: -barWidth / 2,
8423
+ top: -barHeight / 2,
8424
+ originX: "left",
8425
+ originY: "top",
8426
+ width: barLength,
8427
+ height: barHeight,
8428
+ rx: radius,
8429
+ ry: radius,
8430
+ fill
8431
+ });
8432
+ const arrowHead = new yo({
8433
+ left: barWidth / 2 - headSize * 0.25,
8434
+ top: 0,
8435
+ originX: "center",
8436
+ originY: "center",
8437
+ width: headSize,
8438
+ height: headSize,
8439
+ fill,
8440
+ angle: 90
8441
+ });
8442
+ const opacity = ((_g = element.props) == null ? void 0 : _g.opacity) ?? 1;
8443
+ const group = new Ur([bar, arrowHead], {
8444
+ left: x2,
8445
+ top: y2,
8446
+ originX: "center",
8447
+ originY: "center",
8448
+ angle: ((_h2 = element.props) == null ? void 0 : _h2.rotation) || 0,
8449
+ opacity,
8450
+ selectable: true,
8451
+ hasControls: true
8452
+ });
8453
+ group.set(
8454
+ "lockUniScaling",
8455
+ lockAspectRatio ?? ((_i2 = element.props) == null ? void 0 : _i2.lockAspectRatio) ?? true
8456
+ );
8457
+ group.set("id", element.id);
8458
+ group.set("zIndex", index);
8459
+ canvas.add(group);
8460
+ },
8461
+ updateFromFabricObject(object, element, context) {
8462
+ var _a, _b, _c;
8463
+ const canvasCenter = getObjectCanvasCenter(object);
8464
+ const { x: x2, y: y2 } = convertToVideoPosition(
8465
+ canvasCenter.x,
8466
+ canvasCenter.y,
8467
+ context.canvasMetadata,
8468
+ context.videoSize
8469
+ );
8470
+ const baseWidth = ((_a = element.props) == null ? void 0 : _a.width) ?? 220;
8471
+ const baseHeight = ((_b = element.props) == null ? void 0 : _b.height) ?? 14;
8472
+ const opacity = object.opacity ?? ((_c = element.props) == null ? void 0 : _c.opacity) ?? 1;
8473
+ return {
8474
+ element: {
8475
+ ...element,
8476
+ props: {
8477
+ ...element.props,
8478
+ rotation: getObjectCanvasAngle(object),
8479
+ width: baseWidth * object.scaleX,
8480
+ height: baseHeight * object.scaleY,
8481
+ x: x2,
8482
+ y: y2,
8483
+ opacity
8484
+ }
8485
+ }
8486
+ };
8487
+ }
8488
+ };
8489
+ const LineElement = {
8490
+ name: ELEMENT_TYPES.LINE,
8491
+ async add(params) {
8492
+ var _a;
8493
+ const { element, index, canvas, canvasMetadata, lockAspectRatio } = params;
8494
+ const lineProps = element.props ?? {};
8495
+ const lineElement = {
8496
+ ...element,
8497
+ props: {
8498
+ ...lineProps,
8499
+ // Use fill as stroke color when a stroke is desired; otherwise rely
8500
+ // on fill-only rendering. Avoid the generic "#000000" fallback.
8501
+ stroke: lineProps.stroke ?? lineProps.fill,
8502
+ // If a specific lineWidth is provided, keep it; otherwise default to 0
8503
+ // so the stroke does not override the visual fill color.
8504
+ lineWidth: lineProps.lineWidth ?? 0
8505
+ }
8506
+ };
8507
+ await addRectElement({
8508
+ element: lineElement,
8509
+ index,
8510
+ canvas,
8511
+ canvasMetadata,
8512
+ lockAspectRatio: lockAspectRatio ?? ((_a = lineElement.props) == null ? void 0 : _a.lockAspectRatio)
8513
+ });
8514
+ },
8515
+ updateFromFabricObject(object, element, context) {
8516
+ var _a, _b;
8517
+ const canvasCenter = getObjectCanvasCenter(object);
8518
+ const { x: x2, y: y2 } = convertToVideoPosition(
8519
+ canvasCenter.x,
8520
+ canvasCenter.y,
8521
+ context.canvasMetadata,
8522
+ context.videoSize
8523
+ );
8524
+ return {
8525
+ element: {
8526
+ ...element,
8527
+ props: {
8528
+ ...element.props,
8529
+ rotation: getObjectCanvasAngle(object),
8530
+ width: (((_a = element.props) == null ? void 0 : _a.width) ?? 0) * object.scaleX,
8531
+ height: (((_b = element.props) == null ? void 0 : _b.height) ?? 0) * object.scaleY,
8532
+ x: x2,
8533
+ y: y2
8534
+ }
8535
+ }
8536
+ };
8537
+ }
8538
+ };
8539
+ const EffectElement = {
8540
+ name: ELEMENT_TYPES.EFFECT,
8541
+ async add() {
8542
+ return;
8543
+ }
8544
+ };
8393
8545
  class ElementController {
8394
8546
  constructor() {
8395
8547
  __publicField2(this, "elements", /* @__PURE__ */ new Map());
@@ -8413,6 +8565,9 @@ function registerElements() {
8413
8565
  elementController.register(TextElement);
8414
8566
  elementController.register(CaptionElement);
8415
8567
  elementController.register(WatermarkElement);
8568
+ elementController.register(ArrowElement);
8569
+ elementController.register(LineElement);
8570
+ elementController.register(EffectElement);
8416
8571
  }
8417
8572
  registerElements();
8418
8573
  const useTwickCanvas = ({
@@ -8767,10 +8922,18 @@ const useTwickCanvas = ({
8767
8922
  const sendToBack = (elementId) => applyZOrder(elementId, "back");
8768
8923
  const bringForward = (elementId) => applyZOrder(elementId, "forward");
8769
8924
  const sendBackward = (elementId) => applyZOrder(elementId, "backward");
8925
+ const setBackgroundColor = React.useCallback((color2) => {
8926
+ const canvas = twickCanvasRef.current;
8927
+ if (canvas) {
8928
+ canvas.backgroundColor = color2;
8929
+ canvas.requestRenderAll();
8930
+ }
8931
+ }, []);
8770
8932
  return {
8771
8933
  twickCanvas,
8772
8934
  buildCanvas,
8773
8935
  resizeCanvas,
8936
+ setBackgroundColor,
8774
8937
  onVideoSizeChange,
8775
8938
  addWatermarkToCanvas,
8776
8939
  addElementToCanvas,
@@ -8985,7 +9148,9 @@ const DEFAULT_ELEMENT_COLORS = {
8985
9148
  /** Icon element color - bright orchid */
8986
9149
  icon: "#A76CD4",
8987
9150
  /** Circle element color - deep byzantium */
8988
- circle: "#703D8B"
9151
+ circle: "#703D8B",
9152
+ /** Effect element color - cyan accent for global effects */
9153
+ effect: "#22C3EE"
8989
9154
  };
8990
9155
  const AVAILABLE_TEXT_FONTS = {
8991
9156
  // Google Fonts
@@ -9292,6 +9457,7 @@ const usePlayerManager = ({
9292
9457
  twickCanvas,
9293
9458
  buildCanvas,
9294
9459
  resizeCanvas,
9460
+ setBackgroundColor,
9295
9461
  setCanvasElements,
9296
9462
  bringToFront,
9297
9463
  sendToBack,
@@ -9368,7 +9534,7 @@ const usePlayerManager = ({
9368
9534
  }
9369
9535
  };
9370
9536
  React.useEffect(() => {
9371
- var _a, _b, _c, _d, _e2;
9537
+ var _a, _b, _c, _d, _e2, _f;
9372
9538
  switch (timelineAction.type) {
9373
9539
  case timeline.TIMELINE_ACTION.UPDATE_PLAYER_DATA:
9374
9540
  if (videoProps) {
@@ -9378,11 +9544,12 @@ const usePlayerManager = ({
9378
9544
  input: {
9379
9545
  properties: videoProps,
9380
9546
  tracks: ((_c = timelineAction.payload) == null ? void 0 : _c.tracks) ?? [],
9381
- version: ((_d = timelineAction.payload) == null ? void 0 : _d.version) ?? 0
9547
+ version: ((_d = timelineAction.payload) == null ? void 0 : _d.version) ?? 0,
9548
+ backgroundColor: ((_e2 = timelineAction.payload) == null ? void 0 : _e2.backgroundColor) ?? "#000000"
9382
9549
  }
9383
9550
  };
9384
9551
  setProjectData(_latestProjectData);
9385
- if (((_e2 = timelineAction.payload) == null ? void 0 : _e2.version) === 1) {
9552
+ if (((_f = timelineAction.payload) == null ? void 0 : _f.version) === 1) {
9386
9553
  setTimeout(() => {
9387
9554
  setPlayerUpdating(false);
9388
9555
  });
@@ -9400,6 +9567,7 @@ const usePlayerManager = ({
9400
9567
  updateCanvas,
9401
9568
  buildCanvas,
9402
9569
  resizeCanvas,
9570
+ setBackgroundColor,
9403
9571
  onPlayerUpdate,
9404
9572
  playerUpdating,
9405
9573
  handleDropOnCanvas,
@@ -9636,6 +9804,7 @@ const PlayerManager = ({
9636
9804
  canvasMode,
9637
9805
  canvasConfig
9638
9806
  }) => {
9807
+ var _a;
9639
9808
  const containerRef = React.useRef(null);
9640
9809
  const canvasRef = React.useRef(null);
9641
9810
  const durationRef = React.useRef(0);
@@ -9653,6 +9822,7 @@ const PlayerManager = ({
9653
9822
  projectData,
9654
9823
  updateCanvas,
9655
9824
  resizeCanvas,
9825
+ setBackgroundColor,
9656
9826
  playerUpdating,
9657
9827
  onPlayerUpdate,
9658
9828
  buildCanvas,
@@ -9672,6 +9842,7 @@ const PlayerManager = ({
9672
9842
  enabled: !!handleDropOnCanvas && canvasMode
9673
9843
  });
9674
9844
  React.useEffect(() => {
9845
+ var _a2;
9675
9846
  const container = containerRef.current;
9676
9847
  const canvasSize = {
9677
9848
  width: (container == null ? void 0 : container.clientWidth) ?? 0,
@@ -9679,7 +9850,7 @@ const PlayerManager = ({
9679
9850
  };
9680
9851
  if (canvasSize.width > 0 && canvasSize.height > 0) {
9681
9852
  buildCanvas({
9682
- backgroundColor: videoProps.backgroundColor,
9853
+ backgroundColor: videoProps.backgroundColor ?? ((_a2 = projectData == null ? void 0 : projectData.input) == null ? void 0 : _a2.backgroundColor) ?? "#000000",
9683
9854
  videoSize: {
9684
9855
  width: videoProps.width,
9685
9856
  height: videoProps.height
@@ -9689,6 +9860,11 @@ const PlayerManager = ({
9689
9860
  });
9690
9861
  }
9691
9862
  }, [videoProps]);
9863
+ React.useEffect(() => {
9864
+ var _a2;
9865
+ const color2 = ((_a2 = projectData == null ? void 0 : projectData.input) == null ? void 0 : _a2.backgroundColor) ?? videoProps.backgroundColor ?? "#000000";
9866
+ setBackgroundColor(color2);
9867
+ }, [(_a = projectData == null ? void 0 : projectData.input) == null ? void 0 : _a.backgroundColor, videoProps.backgroundColor, setBackgroundColor]);
9692
9868
  const handleResize = React.useMemo(
9693
9869
  () => throttle(() => {
9694
9870
  const container = containerRef.current;
@@ -9720,10 +9896,10 @@ const PlayerManager = ({
9720
9896
  React.useEffect(() => {
9721
9897
  if (!twickCanvas || !canvasMode) return;
9722
9898
  const onSelectionCreated = (e3) => {
9723
- var _a, _b;
9899
+ var _a2, _b;
9724
9900
  const ev = e3 == null ? void 0 : e3.e;
9725
9901
  if (!ev) return;
9726
- const id2 = (_b = (_a = e3.target) == null ? void 0 : _a.get) == null ? void 0 : _b.call(_a, "id");
9902
+ const id2 = (_b = (_a2 = e3.target) == null ? void 0 : _a2.get) == null ? void 0 : _b.call(_a2, "id");
9727
9903
  if (id2) {
9728
9904
  setContextMenu({ x: ev.clientX, y: ev.clientY, elementId: id2 });
9729
9905
  }
@@ -11156,13 +11332,25 @@ function SeekTrack({
11156
11332
  const containerRef = React.useRef(null);
11157
11333
  const [isDragging2, setIsDragging] = React.useState(false);
11158
11334
  const [dragPosition, setDragPosition] = React.useState(null);
11335
+ const [pendingSeekTime, setPendingSeekTime] = React.useState(null);
11159
11336
  const pixelsPerSecond = 100 * zoom;
11160
11337
  const totalWidth = duration * pixelsPerSecond;
11161
11338
  const pinHeight = 2 + timelineCount * (2.75 + 0.5);
11339
+ React.useEffect(() => {
11340
+ if (pendingSeekTime === null) return;
11341
+ if (Math.abs(currentTime - pendingSeekTime) < 0.05) {
11342
+ setPendingSeekTime(null);
11343
+ }
11344
+ }, [currentTime, pendingSeekTime]);
11162
11345
  const seekPosition = React.useMemo(() => {
11163
- const position = isDragging2 && dragPosition !== null ? dragPosition : currentTime * pixelsPerSecond;
11164
- return Math.max(0, position);
11165
- }, [isDragging2, dragPosition, currentTime, pixelsPerSecond]);
11346
+ if (isDragging2 && dragPosition !== null) {
11347
+ return Math.max(0, dragPosition);
11348
+ }
11349
+ if (pendingSeekTime !== null) {
11350
+ return Math.max(0, pendingSeekTime * pixelsPerSecond);
11351
+ }
11352
+ return Math.max(0, currentTime * pixelsPerSecond);
11353
+ }, [isDragging2, dragPosition, currentTime, pendingSeekTime, pixelsPerSecond]);
11166
11354
  React.useEffect(() => {
11167
11355
  onPlayheadUpdate == null ? void 0 : onPlayheadUpdate({
11168
11356
  positionPx: seekPosition,
@@ -11251,6 +11439,7 @@ function SeekTrack({
11251
11439
  setDragPosition(xPos);
11252
11440
  } else {
11253
11441
  setDragPosition(null);
11442
+ setPendingSeekTime(newTime);
11254
11443
  seekToTime(newTime);
11255
11444
  }
11256
11445
  });
@@ -18849,8 +19038,10 @@ const TrackElementView = ({
18849
19038
  onSelection,
18850
19039
  onDrag,
18851
19040
  allowOverlap = false,
19041
+ onDragStateChange,
18852
19042
  elementColors
18853
19043
  }) => {
19044
+ var _a, _b;
18854
19045
  const ref = React.useRef(null);
18855
19046
  const dragType = React.useRef(null);
18856
19047
  const lastPosRef = React.useRef(null);
@@ -18868,7 +19059,10 @@ const TrackElementView = ({
18868
19059
  const bind = useDrag(({ delta: [dx] }) => {
18869
19060
  if (!parentWidth) return;
18870
19061
  if (dx == 0) return;
18871
- setIsDragging(true);
19062
+ if (!isDragging2) {
19063
+ setIsDragging(true);
19064
+ onDragStateChange == null ? void 0 : onDragStateChange(true, element);
19065
+ }
18872
19066
  dragType.current = DRAG_TYPE.MOVE;
18873
19067
  setPosition((prev) => {
18874
19068
  const span = prev.end - prev.start;
@@ -18894,6 +19088,7 @@ const TrackElementView = ({
18894
19088
  if (dx === 0) return;
18895
19089
  if (isDragging2) {
18896
19090
  setIsDragging(false);
19091
+ onDragStateChange == null ? void 0 : onDragStateChange(false, element);
18897
19092
  }
18898
19093
  dragType.current = DRAG_TYPE.START;
18899
19094
  setPosition((prev) => {
@@ -18915,6 +19110,7 @@ const TrackElementView = ({
18915
19110
  if (dx === 0) return;
18916
19111
  if (isDragging2) {
18917
19112
  setIsDragging(false);
19113
+ onDragStateChange == null ? void 0 : onDragStateChange(false, element);
18918
19114
  }
18919
19115
  dragType.current = DRAG_TYPE.END;
18920
19116
  setPosition((prev) => {
@@ -18934,24 +19130,37 @@ const TrackElementView = ({
18934
19130
  const setLastPos = () => {
18935
19131
  lastPosRef.current = position;
18936
19132
  };
18937
- const sendUpdate = () => {
18938
- var _a, _b;
19133
+ const sendUpdate = (e3) => {
19134
+ var _a2, _b2, _c;
19135
+ let dropPointer;
19136
+ if (e3) {
19137
+ if ("clientX" in e3) {
19138
+ dropPointer = { clientX: e3.clientX, clientY: e3.clientY };
19139
+ } else if ("changedTouches" in e3 && ((_a2 = e3.changedTouches) == null ? void 0 : _a2[0])) {
19140
+ const t2 = e3.changedTouches[0];
19141
+ dropPointer = { clientX: t2.clientX, clientY: t2.clientY };
19142
+ }
19143
+ }
18939
19144
  setIsDragging(false);
18940
- if (((_a = lastPosRef.current) == null ? void 0 : _a.start) !== position.start || ((_b = lastPosRef.current) == null ? void 0 : _b.end) !== position.end) {
18941
- onDrag({
18942
- element,
18943
- updates: {
18944
- start: timeline.getDecimalNumber(position.start),
18945
- end: timeline.getDecimalNumber(position.end)
18946
- },
18947
- dragType: dragType.current || ""
18948
- });
19145
+ onDragStateChange == null ? void 0 : onDragStateChange(false, element);
19146
+ const payload = {
19147
+ element,
19148
+ updates: {
19149
+ start: timeline.getDecimalNumber(position.start),
19150
+ end: timeline.getDecimalNumber(position.end)
19151
+ },
19152
+ dragType: dragType.current || ""
19153
+ };
19154
+ const didChange = ((_b2 = lastPosRef.current) == null ? void 0 : _b2.start) !== position.start || ((_c = lastPosRef.current) == null ? void 0 : _c.end) !== position.end;
19155
+ if (didChange || dropPointer) {
19156
+ onDrag(payload, dropPointer);
18949
19157
  }
18950
19158
  };
18951
19159
  const getElementColor = (elementType) => {
18952
19160
  const colors = elementColors || ELEMENT_COLORS;
18953
- if (elementType in colors) {
18954
- return colors[elementType];
19161
+ const key = elementType === timeline.TIMELINE_ELEMENT_TYPE.VIDEO ? "video" : elementType === timeline.TIMELINE_ELEMENT_TYPE.AUDIO ? "audio" : elementType === timeline.TIMELINE_ELEMENT_TYPE.IMAGE ? "image" : elementType === timeline.TIMELINE_ELEMENT_TYPE.TEXT ? "text" : elementType === timeline.TIMELINE_ELEMENT_TYPE.CAPTION ? "caption" : elementType === timeline.TIMELINE_ELEMENT_TYPE.RECT ? "rect" : elementType === timeline.TIMELINE_ELEMENT_TYPE.CIRCLE ? "circle" : elementType === timeline.TIMELINE_ELEMENT_TYPE.ICON ? "icon" : elementType === timeline.TIMELINE_ELEMENT_TYPE.EFFECT ? "effect" : "element";
19162
+ if (key in colors) {
19163
+ return colors[key];
18955
19164
  }
18956
19165
  return ELEMENT_COLORS.element;
18957
19166
  };
@@ -18972,8 +19181,8 @@ const TrackElementView = ({
18972
19181
  setLastPos();
18973
19182
  }
18974
19183
  },
18975
- onMouseUp: sendUpdate,
18976
- onTouchEnd: sendUpdate,
19184
+ onMouseUp: (e3) => sendUpdate(e3),
19185
+ onTouchEnd: (e3) => sendUpdate(e3),
18977
19186
  onClick: (e3) => {
18978
19187
  if (onSelection) {
18979
19188
  onSelection(element, e3);
@@ -18995,7 +19204,7 @@ const TrackElementView = ({
18995
19204
  className: "twick-track-element-handle twick-track-element-handle-start"
18996
19205
  }
18997
19206
  ) : null,
18998
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twick-track-element-content", children: element.getText ? element.getText() : element.getName() || element.getType() }),
19207
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twick-track-element-content", children: element.getType() === timeline.TIMELINE_ELEMENT_TYPE.EFFECT ? ((_b = (_a = element.getProps) == null ? void 0 : _a.call(element)) == null ? void 0 : _b.effectKey) ?? "Effect" : element.getText ? element.getText() : element.getName() || element.getType() }),
18999
19208
  hasHandles ? /* @__PURE__ */ jsxRuntime.jsx(
19000
19209
  "div",
19001
19210
  {
@@ -19030,6 +19239,7 @@ const TrackBase = ({
19030
19239
  onItemSelection,
19031
19240
  onDrag,
19032
19241
  allowOverlap = false,
19242
+ onDragStateChange,
19033
19243
  elementColors
19034
19244
  }) => {
19035
19245
  const trackRef = React.useRef(null);
@@ -19054,6 +19264,7 @@ const TrackBase = ({
19054
19264
  selectedIds,
19055
19265
  onSelection: onItemSelection,
19056
19266
  onDrag,
19267
+ onDragStateChange,
19057
19268
  elementColors,
19058
19269
  nextStart: index < elements.length - 1 ? elements[index + 1].getStart() : null,
19059
19270
  prevEnd: index > 0 ? elements[index - 1].getEnd() : 0
@@ -19228,6 +19439,67 @@ function useMarqueeSelection({
19228
19439
  );
19229
19440
  return { marquee, handleMouseDown };
19230
19441
  }
19442
+ function useEdgeAutoScroll({
19443
+ isActive,
19444
+ getMouseClientX,
19445
+ scrollContainerRef,
19446
+ contentWidth,
19447
+ edgeThreshold = 60,
19448
+ maxScrollSpeed = 20
19449
+ }) {
19450
+ const rafRef = React.useRef(null);
19451
+ React.useEffect(() => {
19452
+ if (!isActive) {
19453
+ if (rafRef.current) {
19454
+ cancelAnimationFrame(rafRef.current);
19455
+ rafRef.current = null;
19456
+ }
19457
+ return;
19458
+ }
19459
+ const step = () => {
19460
+ const el = scrollContainerRef.current;
19461
+ if (!el) {
19462
+ rafRef.current = requestAnimationFrame(step);
19463
+ return;
19464
+ }
19465
+ const rect = el.getBoundingClientRect();
19466
+ const mouseX = getMouseClientX();
19467
+ const xRelative = mouseX - rect.left;
19468
+ const viewportWidth = el.clientWidth;
19469
+ const scrollMax = Math.max(0, contentWidth - viewportWidth);
19470
+ let scrollSpeed = 0;
19471
+ if (xRelative < edgeThreshold && el.scrollLeft > 0) {
19472
+ const intensity = 1 - Math.max(0, xRelative) / edgeThreshold;
19473
+ scrollSpeed = -maxScrollSpeed * intensity;
19474
+ } else if (xRelative > viewportWidth - edgeThreshold && el.scrollLeft < scrollMax) {
19475
+ const intensity = 1 - Math.max(0, viewportWidth - edgeThreshold - xRelative) / edgeThreshold;
19476
+ scrollSpeed = maxScrollSpeed * intensity;
19477
+ }
19478
+ if (scrollSpeed !== 0) {
19479
+ const newScrollLeft = Math.max(
19480
+ 0,
19481
+ Math.min(scrollMax, el.scrollLeft + scrollSpeed)
19482
+ );
19483
+ el.scrollLeft = newScrollLeft;
19484
+ }
19485
+ rafRef.current = requestAnimationFrame(step);
19486
+ };
19487
+ rafRef.current = requestAnimationFrame(step);
19488
+ return () => {
19489
+ if (rafRef.current) {
19490
+ cancelAnimationFrame(rafRef.current);
19491
+ rafRef.current = null;
19492
+ }
19493
+ };
19494
+ }, [
19495
+ isActive,
19496
+ getMouseClientX,
19497
+ scrollContainerRef,
19498
+ contentWidth,
19499
+ edgeThreshold,
19500
+ maxScrollSpeed
19501
+ ]);
19502
+ }
19231
19503
  function MarqueeOverlay({ marquee }) {
19232
19504
  return /* @__PURE__ */ jsxRuntime.jsx(
19233
19505
  "div",
@@ -19258,8 +19530,25 @@ function MarqueeOverlay({ marquee }) {
19258
19530
  }
19259
19531
  );
19260
19532
  }
19533
+ const SEPARATOR_HEIGHT$1 = 6;
19534
+ function getTrackOrSeparatorAt(clientY, containerTop, trackHeight) {
19535
+ const y2 = clientY - containerTop;
19536
+ if (y2 < 0) return null;
19537
+ const rowHeight = trackHeight + SEPARATOR_HEIGHT$1;
19538
+ if (y2 < SEPARATOR_HEIGHT$1) {
19539
+ return { type: "separator", separatorIndex: 0 };
19540
+ }
19541
+ const relativeY = y2 - SEPARATOR_HEIGHT$1;
19542
+ const index = Math.floor(relativeY / rowHeight);
19543
+ const remainder = relativeY % rowHeight;
19544
+ if (remainder < trackHeight) {
19545
+ return { type: "track", trackIndex: index };
19546
+ }
19547
+ return { type: "separator", separatorIndex: index + 1 };
19548
+ }
19261
19549
  const LABEL_WIDTH = 40;
19262
19550
  const TRACK_HEIGHT = 44;
19551
+ const SEPARATOR_HEIGHT = 6;
19263
19552
  function TimelineView({
19264
19553
  zoomLevel,
19265
19554
  selectedItem,
@@ -19272,25 +19561,81 @@ function TimelineView({
19272
19561
  onEmptyClick,
19273
19562
  onMarqueeSelect,
19274
19563
  onElementDrag,
19564
+ onElementDrop,
19565
+ onSeek,
19275
19566
  elementColors,
19276
19567
  selectedIds,
19277
19568
  playheadPositionPx = 0,
19278
19569
  isPlayheadActive = false,
19279
19570
  onDropOnTimeline,
19280
19571
  videoResolution,
19281
- enableDropOnTimeline = true
19572
+ enableDropOnTimeline = true,
19573
+ chapters = []
19282
19574
  }) {
19283
19575
  const containerRef = React.useRef(null);
19284
19576
  const seekContainerRef = React.useRef(null);
19285
19577
  const timelineContentRef = React.useRef(null);
19286
19578
  const [, setScrollLeft] = React.useState(0);
19579
+ const pointerRef = React.useRef(null);
19287
19580
  const [draggedTimeline, setDraggedTimeline] = React.useState(null);
19581
+ const [draggingElementId, setDraggingElementId] = React.useState(null);
19582
+ const [activeDropTarget, setActiveDropTarget] = React.useState(null);
19288
19583
  const { selectedTrackElement } = React.useMemo(() => {
19289
19584
  if (selectedItem && "elements" in selectedItem) {
19290
19585
  return { selectedTrackElement: null };
19291
19586
  }
19292
19587
  return { selectedTrackElement: selectedItem };
19293
19588
  }, [selectedItem]);
19589
+ const handleDragWithDrop = React.useCallback(
19590
+ (payload, dropPointer) => {
19591
+ var _a;
19592
+ if (dropPointer && onElementDrop) {
19593
+ const rect = (_a = timelineContentRef.current) == null ? void 0 : _a.getBoundingClientRect();
19594
+ const dropTarget = rect ? getTrackOrSeparatorAt(dropPointer.clientY, rect.top, TRACK_HEIGHT) : null;
19595
+ onElementDrop({ ...payload, dropTarget });
19596
+ } else {
19597
+ onElementDrag(payload);
19598
+ }
19599
+ },
19600
+ [onElementDrag, onElementDrop]
19601
+ );
19602
+ useEdgeAutoScroll({
19603
+ isActive: !!draggingElementId,
19604
+ getMouseClientX: () => {
19605
+ var _a;
19606
+ return ((_a = pointerRef.current) == null ? void 0 : _a.clientX) ?? 0;
19607
+ },
19608
+ scrollContainerRef: containerRef,
19609
+ contentWidth: Math.max(100, duration * zoomLevel * 100)
19610
+ });
19611
+ React.useEffect(() => {
19612
+ if (!draggingElementId) return;
19613
+ const onMove = (e3) => {
19614
+ var _a;
19615
+ const pt2 = "touches" in e3 ? e3.touches[0] : e3;
19616
+ if (pt2) {
19617
+ pointerRef.current = { clientX: pt2.clientX, clientY: pt2.clientY };
19618
+ const rect = (_a = timelineContentRef.current) == null ? void 0 : _a.getBoundingClientRect();
19619
+ if (rect) {
19620
+ setActiveDropTarget(getTrackOrSeparatorAt(pt2.clientY, rect.top, TRACK_HEIGHT));
19621
+ }
19622
+ }
19623
+ };
19624
+ const onUp = () => {
19625
+ pointerRef.current = null;
19626
+ setActiveDropTarget(null);
19627
+ };
19628
+ document.addEventListener("mousemove", onMove);
19629
+ document.addEventListener("touchmove", onMove, { passive: true });
19630
+ document.addEventListener("mouseup", onUp);
19631
+ document.addEventListener("touchend", onUp);
19632
+ return () => {
19633
+ document.removeEventListener("mousemove", onMove);
19634
+ document.removeEventListener("touchmove", onMove);
19635
+ document.removeEventListener("mouseup", onUp);
19636
+ document.removeEventListener("touchend", onUp);
19637
+ };
19638
+ }, [draggingElementId]);
19294
19639
  const timelineWidth = Math.max(100, duration * zoomLevel * 100);
19295
19640
  const timelineWidthPx = `${timelineWidth}px`;
19296
19641
  const handleScroll = (e3) => {
@@ -19387,9 +19732,36 @@ function TimelineView({
19387
19732
  className: "twick-timeline-scroll-container",
19388
19733
  onScroll: handleScroll,
19389
19734
  children: [
19390
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { width: timelineWidthPx }, children: seekTrack ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", position: "relative" }, children: [
19735
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { width: timelineWidthPx }, children: seekTrack ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", position: "relative", minHeight: 34 }, children: [
19391
19736
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twick-seek-track-empty-space", onClick: onAddTrack, children: /* @__PURE__ */ jsxRuntime.jsx(Plus, { color: "white", size: 20 }) }),
19392
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { flexGrow: 1 }, children: seekTrack })
19737
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { flexGrow: 1 }, children: seekTrack }),
19738
+ chapters.map((chapter) => {
19739
+ const left = LABEL_WIDTH + chapter.time / Math.max(duration, 1e-3) * (timelineWidth - LABEL_WIDTH);
19740
+ return /* @__PURE__ */ jsxRuntime.jsx(
19741
+ "button",
19742
+ {
19743
+ className: "btn-ghost",
19744
+ title: chapter.title,
19745
+ onClick: (e3) => {
19746
+ e3.stopPropagation();
19747
+ onSeek(chapter.time);
19748
+ },
19749
+ style: {
19750
+ position: "absolute",
19751
+ left,
19752
+ top: 0,
19753
+ height: "100%",
19754
+ padding: "0 4px",
19755
+ borderRadius: 0,
19756
+ borderLeft: "1px solid rgba(255,255,255,0.4)",
19757
+ borderRight: "none",
19758
+ minWidth: 0
19759
+ },
19760
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 10, opacity: 0.9 }, children: chapter.title })
19761
+ },
19762
+ chapter.id
19763
+ );
19764
+ })
19393
19765
  ] }) : null }),
19394
19766
  /* @__PURE__ */ jsxRuntime.jsxs(
19395
19767
  "div",
@@ -19415,34 +19787,61 @@ function TimelineView({
19415
19787
  }
19416
19788
  }
19417
19789
  ),
19418
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "relative", zIndex: 10 }, children: (tracks || []).map((track) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twick-timeline-container", children: [
19419
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twick-timeline-header-container", children: /* @__PURE__ */ jsxRuntime.jsx(
19420
- TrackHeader,
19421
- {
19422
- track,
19423
- selectedIds,
19424
- onSelect: handleItemSelection,
19425
- onDragStart: handleTrackDragStart,
19426
- onDragOver: handleTrackDragOver,
19427
- onDrop: handleTrackDrop
19428
- }
19429
- ) }),
19790
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative", zIndex: 10 }, children: [
19430
19791
  /* @__PURE__ */ jsxRuntime.jsx(
19431
- TrackBase,
19792
+ "div",
19432
19793
  {
19433
- track,
19434
- duration,
19435
- selectedItem: selectedTrackElement,
19436
- selectedIds,
19437
- zoom: zoomLevel,
19438
- allowOverlap: false,
19439
- trackWidth: timelineWidth - LABEL_WIDTH,
19440
- onItemSelection: handleItemSelection,
19441
- onDrag: onElementDrag,
19442
- elementColors
19794
+ className: "twick-timeline-separator",
19795
+ style: {
19796
+ height: SEPARATOR_HEIGHT,
19797
+ background: (activeDropTarget == null ? void 0 : activeDropTarget.type) === "separator" && activeDropTarget.separatorIndex === 0 ? "rgba(255,255,255,0.2)" : "transparent"
19798
+ }
19443
19799
  }
19444
- )
19445
- ] }, track.getId())) })
19800
+ ),
19801
+ (tracks || []).map((track, index) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
19802
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twick-timeline-container", children: [
19803
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twick-timeline-header-container", children: /* @__PURE__ */ jsxRuntime.jsx(
19804
+ TrackHeader,
19805
+ {
19806
+ track,
19807
+ selectedIds,
19808
+ onSelect: handleItemSelection,
19809
+ onDragStart: handleTrackDragStart,
19810
+ onDragOver: handleTrackDragOver,
19811
+ onDrop: handleTrackDrop
19812
+ }
19813
+ ) }),
19814
+ /* @__PURE__ */ jsxRuntime.jsx(
19815
+ TrackBase,
19816
+ {
19817
+ track,
19818
+ duration,
19819
+ selectedItem: selectedTrackElement,
19820
+ selectedIds,
19821
+ zoom: zoomLevel,
19822
+ allowOverlap: false,
19823
+ trackWidth: timelineWidth - LABEL_WIDTH,
19824
+ onItemSelection: handleItemSelection,
19825
+ onDrag: handleDragWithDrop,
19826
+ onDragStateChange: (isDragging2, el) => {
19827
+ setDraggingElementId(isDragging2 && el ? el.getId() : null);
19828
+ },
19829
+ elementColors
19830
+ }
19831
+ )
19832
+ ] }),
19833
+ /* @__PURE__ */ jsxRuntime.jsx(
19834
+ "div",
19835
+ {
19836
+ className: "twick-timeline-separator",
19837
+ style: {
19838
+ height: SEPARATOR_HEIGHT,
19839
+ background: (activeDropTarget == null ? void 0 : activeDropTarget.type) === "separator" && activeDropTarget.separatorIndex === index + 1 ? "rgba(255,255,255,0.2)" : "transparent"
19840
+ }
19841
+ }
19842
+ )
19843
+ ] }, track.getId()))
19844
+ ] })
19446
19845
  ]
19447
19846
  }
19448
19847
  )
@@ -19499,6 +19898,75 @@ const useTimelineManager = () => {
19499
19898
  setSelectedItem(updatedElement);
19500
19899
  editor.refresh();
19501
19900
  };
19901
+ const isElementTrackType = (track) => track.getType() === timeline.TRACK_TYPES.ELEMENT;
19902
+ const wouldBeElementTrack = (el) => {
19903
+ const elType = el.getType().toLowerCase();
19904
+ return elType !== "video" && elType !== "image" && elType !== "audio";
19905
+ };
19906
+ const onElementDrop = async (params) => {
19907
+ var _a;
19908
+ const { element, dragType, updates, dropTarget } = params;
19909
+ const tracks = ((_a = editor.getTimelineData()) == null ? void 0 : _a.tracks) ?? [];
19910
+ if (!dropTarget) {
19911
+ return;
19912
+ }
19913
+ if (dropTarget.type === "separator") {
19914
+ if (!wouldBeElementTrack(element)) {
19915
+ return;
19916
+ }
19917
+ await editor.moveElementToNewTrackAt(
19918
+ element,
19919
+ dropTarget.separatorIndex,
19920
+ updates.start
19921
+ );
19922
+ setSelectedItem(element);
19923
+ editor.refresh();
19924
+ return;
19925
+ }
19926
+ const targetTrack = tracks[dropTarget.trackIndex];
19927
+ if (!targetTrack) {
19928
+ return;
19929
+ }
19930
+ if (!isElementTrackType(targetTrack)) {
19931
+ return;
19932
+ }
19933
+ const start = updates.start;
19934
+ const end = updates.end;
19935
+ const elementId = element.getId();
19936
+ const currentTrackId = element.getTrackId();
19937
+ let hasOverlap = false;
19938
+ const others = targetTrack.getElements().filter((el) => el.getId() !== elementId);
19939
+ for (const other of others) {
19940
+ const oStart = other.getStart();
19941
+ const oEnd = other.getEnd();
19942
+ if (start < oEnd && end > oStart) {
19943
+ hasOverlap = true;
19944
+ break;
19945
+ }
19946
+ }
19947
+ if (hasOverlap) {
19948
+ await editor.moveElementToNewTrackAt(
19949
+ element,
19950
+ dropTarget.trackIndex + 1,
19951
+ updates.start
19952
+ );
19953
+ setSelectedItem(element);
19954
+ editor.refresh();
19955
+ return;
19956
+ }
19957
+ if (currentTrackId === targetTrack.getId()) {
19958
+ onElementDrag({ element, dragType, updates });
19959
+ return;
19960
+ }
19961
+ editor.removeElement(element);
19962
+ element.setStart(updates.start);
19963
+ element.setEnd(updates.end);
19964
+ const added = await editor.addElementToTrack(targetTrack, element);
19965
+ if (added) {
19966
+ setSelectedItem(element);
19967
+ editor.refresh();
19968
+ }
19969
+ };
19502
19970
  const timelineData = React.useMemo(() => editor.getTimelineData(), [changeLog]);
19503
19971
  const { setSeekTime, setCurrentTime } = livePlayer.useLivePlayerContext();
19504
19972
  const onReorder = (reorderedItems) => {
@@ -19521,6 +19989,7 @@ const useTimelineManager = () => {
19521
19989
  timelineData,
19522
19990
  onAddTrack,
19523
19991
  onElementDrag,
19992
+ onElementDrop,
19524
19993
  onReorder,
19525
19994
  onSeek,
19526
19995
  onSelectionChange,
@@ -19607,7 +20076,7 @@ const TimelineManager = ({
19607
20076
  timelineTickConfigs,
19608
20077
  elementColors
19609
20078
  }) => {
19610
- var _a;
20079
+ var _a, _b;
19611
20080
  const { playerState } = livePlayer.useLivePlayerContext();
19612
20081
  const { followPlayheadEnabled, editor, videoResolution, setSelectedItem } = timeline.useTimelineContext();
19613
20082
  const {
@@ -19617,6 +20086,7 @@ const TimelineManager = ({
19617
20086
  onAddTrack,
19618
20087
  onReorder,
19619
20088
  onElementDrag,
20089
+ onElementDrop,
19620
20090
  onSeek
19621
20091
  } = useTimelineManager();
19622
20092
  const { selectedIds } = timeline.useTimelineContext();
@@ -19670,6 +20140,7 @@ const TimelineManager = ({
19670
20140
  onAddTrack,
19671
20141
  onReorder,
19672
20142
  onElementDrag,
20143
+ onElementDrop,
19673
20144
  onSeek,
19674
20145
  onItemSelect: handleItemSelect,
19675
20146
  onEmptyClick: handleEmptyClick,
@@ -19677,6 +20148,7 @@ const TimelineManager = ({
19677
20148
  elementColors,
19678
20149
  playheadPositionPx: playheadState.positionPx,
19679
20150
  isPlayheadActive,
20151
+ chapters: ((_a = timelineData == null ? void 0 : timelineData.metadata) == null ? void 0 : _a.chapters) ?? [],
19680
20152
  onDropOnTimeline: handleDropOnTimeline,
19681
20153
  videoResolution,
19682
20154
  enableDropOnTimeline: true,
@@ -19686,7 +20158,7 @@ const TimelineManager = ({
19686
20158
  duration: totalDuration,
19687
20159
  zoom: trackZoom,
19688
20160
  onSeek,
19689
- timelineCount: ((_a = timelineData == null ? void 0 : timelineData.tracks) == null ? void 0 : _a.length) ?? 0,
20161
+ timelineCount: ((_b = timelineData == null ? void 0 : timelineData.tracks) == null ? void 0 : _b.length) ?? 0,
19690
20162
  timelineTickConfigs,
19691
20163
  onPlayheadUpdate: handlePlayheadUpdate
19692
20164
  }