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