@twick/video-editor 0.14.5 → 0.14.7

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
@@ -9353,11 +9353,10 @@ This message will only show in development mode. It won't appear in production.
9353
9353
  duration,
9354
9354
  zoom = 1,
9355
9355
  onSeek,
9356
- timelineCount = 0
9356
+ timelineCount = 0,
9357
+ timelineTickConfigs
9357
9358
  }) {
9358
- const canvasRef = React.useRef(null);
9359
9359
  const containerRef = React.useRef(null);
9360
- const [containerWidth, setContainerWidth] = React.useState(0);
9361
9360
  const [isDragging2, setIsDragging] = React.useState(false);
9362
9361
  const [seekPosition, setSeekPosition] = React.useState(0);
9363
9362
  const pixelsPerSecond = 100 * zoom;
@@ -9368,61 +9367,62 @@ This message will only show in development mode. It won't appear in production.
9368
9367
  setSeekPosition(currentTime * pixelsPerSecond);
9369
9368
  }
9370
9369
  }, [currentTime, pixelsPerSecond, isDragging2]);
9371
- const drawTimeline = React.useCallback(() => {
9372
- const canvas = canvasRef.current;
9373
- if (!canvas) return;
9374
- const ctx = canvas.getContext("2d", { alpha: false });
9375
- if (!ctx) return;
9376
- const dpr = window.devicePixelRatio || 1;
9377
- const displayWidth = Math.max(totalWidth, containerWidth);
9378
- canvas.width = displayWidth * dpr;
9379
- canvas.height = 32 * dpr;
9380
- canvas.style.width = `${displayWidth}px`;
9381
- canvas.style.height = "32px";
9382
- ctx.scale(dpr, dpr);
9383
- ctx.fillStyle = "#0f0f0f";
9384
- ctx.fillRect(0, 0, displayWidth, 32);
9385
- for (let i2 = 0; i2 <= Math.ceil(duration * 10); i2++) {
9386
- const time2 = i2 * 0.1;
9387
- const x2 = Math.floor(time2 * pixelsPerSecond) + 0.5;
9388
- const isSecond = i2 % 10 === 0;
9389
- ctx.beginPath();
9390
- ctx.moveTo(x2, 0);
9391
- ctx.lineTo(x2, isSecond ? 24 : 12);
9392
- ctx.strokeStyle = isSecond ? "rgba(255, 255, 255, 0.7)" : "rgba(255, 255, 255, 0.3)";
9393
- ctx.lineWidth = 1;
9394
- ctx.stroke();
9395
- if (isSecond) {
9396
- ctx.font = "bold 10px system-ui, sans-serif";
9397
- ctx.fillStyle = "rgba(255, 255, 255, 0.7)";
9398
- ctx.textAlign = "center";
9399
- ctx.textBaseline = "bottom";
9400
- if (time2 === 0) ;
9401
- else {
9402
- ctx.fillText(`${Math.floor(time2)}s`, x2, 32);
9370
+ const { majorIntervalSec, minorIntervalSec } = React.useMemo(() => {
9371
+ if (timelineTickConfigs && timelineTickConfigs.length > 0) {
9372
+ const sortedConfigs = [...timelineTickConfigs].sort((a2, b2) => a2.durationThreshold - b2.durationThreshold);
9373
+ for (const config of sortedConfigs) {
9374
+ if (duration < config.durationThreshold) {
9375
+ return {
9376
+ majorIntervalSec: config.majorInterval,
9377
+ minorIntervalSec: config.minorTicks > 0 ? config.majorInterval / (config.minorTicks + 1) : config.majorInterval
9378
+ };
9403
9379
  }
9404
9380
  }
9381
+ const lastConfig = sortedConfigs[sortedConfigs.length - 1];
9382
+ return {
9383
+ majorIntervalSec: lastConfig.majorInterval,
9384
+ minorIntervalSec: lastConfig.minorTicks > 0 ? lastConfig.majorInterval / (lastConfig.minorTicks + 1) : lastConfig.majorInterval
9385
+ };
9405
9386
  }
9406
- }, [duration, pixelsPerSecond, totalWidth, containerWidth]);
9407
- React.useEffect(() => {
9408
- if (containerRef.current) {
9409
- setContainerWidth(containerRef.current.clientWidth);
9387
+ let major = 1;
9388
+ let minors = 5;
9389
+ if (duration < 10) {
9390
+ major = 1;
9391
+ minors = 10;
9392
+ } else if (duration < 30) {
9393
+ major = 5;
9394
+ minors = 5;
9395
+ } else if (duration < 120) {
9396
+ major = 10;
9397
+ minors = 5;
9398
+ } else if (duration < 300) {
9399
+ major = 30;
9400
+ minors = 6;
9401
+ } else if (duration < 900) {
9402
+ major = 60;
9403
+ minors = 6;
9404
+ } else if (duration < 1800) {
9405
+ major = 120;
9406
+ minors = 4;
9407
+ } else if (duration < 3600) {
9408
+ major = 300;
9409
+ minors = 5;
9410
+ } else if (duration < 7200) {
9411
+ major = 600;
9412
+ minors = 10;
9413
+ } else {
9414
+ major = 1800;
9415
+ minors = 6;
9410
9416
  }
9411
- const handleResize = () => {
9412
- if (containerRef.current) {
9413
- setContainerWidth(containerRef.current.clientWidth);
9414
- }
9417
+ return {
9418
+ majorIntervalSec: major,
9419
+ minorIntervalSec: minors > 0 ? major / (minors + 1) : major
9415
9420
  };
9416
- window.addEventListener("resize", handleResize);
9417
- return () => window.removeEventListener("resize", handleResize);
9418
- }, []);
9419
- React.useEffect(() => {
9420
- drawTimeline();
9421
- }, [drawTimeline, duration, zoom, containerWidth]);
9421
+ }, [duration, timelineTickConfigs]);
9422
9422
  const handleSeek = (clientX) => {
9423
9423
  if (!containerRef.current) return;
9424
9424
  const rect = containerRef.current.getBoundingClientRect();
9425
- const x2 = clientX - rect.left;
9425
+ const x2 = clientX - rect.left + (containerRef.current.scrollLeft || 0);
9426
9426
  const newTime = Math.max(0, Math.min(duration, x2 / pixelsPerSecond));
9427
9427
  onSeek(newTime);
9428
9428
  };
@@ -9433,7 +9433,7 @@ This message will only show in development mode. It won't appear in production.
9433
9433
  setIsDragging(active);
9434
9434
  if (!containerRef.current) return;
9435
9435
  const rect = containerRef.current.getBoundingClientRect();
9436
- const xPos = x2 - rect.left;
9436
+ const xPos = x2 - rect.left + (containerRef.current.scrollLeft || 0);
9437
9437
  const newTime = Math.max(0, Math.min(duration, xPos / pixelsPerSecond));
9438
9438
  setSeekPosition(xPos);
9439
9439
  onSeek(newTime);
@@ -9442,24 +9442,92 @@ This message will only show in development mode. It won't appear in production.
9442
9442
  "div",
9443
9443
  {
9444
9444
  ref: containerRef,
9445
- className: "twick-seek-track-container",
9445
+ className: "twick-seek-track-container-no-scrollbar",
9446
9446
  onClick: (e2) => handleSeek(e2.clientX),
9447
+ style: {
9448
+ overflowX: "auto",
9449
+ position: "relative",
9450
+ scrollbarWidth: "none",
9451
+ // Firefox
9452
+ msOverflowStyle: "none"
9453
+ // IE/Edge
9454
+ },
9447
9455
  children: [
9448
- /* @__PURE__ */ jsxRuntime.jsx(
9449
- "canvas",
9450
- {
9451
- ref: canvasRef,
9452
- className: "twick-seek-track-canvas",
9453
- style: { minWidth: "100%" }
9456
+ (() => {
9457
+ const ticks = [];
9458
+ const labels = [];
9459
+ const epsilon = 1e-6;
9460
+ const tickPositions = /* @__PURE__ */ new Set();
9461
+ for (let t2 = 0; t2 <= duration + epsilon; t2 += minorIntervalSec) {
9462
+ tickPositions.add(Math.round(t2 * 1e3) / 1e3);
9454
9463
  }
9455
- ),
9464
+ tickPositions.forEach((t2) => {
9465
+ const left = t2 * pixelsPerSecond;
9466
+ const isMajor = Math.abs(t2 / majorIntervalSec - Math.round(t2 / majorIntervalSec)) < 1e-3;
9467
+ ticks.push(
9468
+ /* @__PURE__ */ jsxRuntime.jsx(
9469
+ "div",
9470
+ {
9471
+ style: {
9472
+ position: "absolute",
9473
+ left,
9474
+ top: 0,
9475
+ width: "1px",
9476
+ height: isMajor ? "12px" : "8px",
9477
+ backgroundColor: isMajor ? "rgba(255,255,255,0.5)" : "rgba(255,255,255,0.2)",
9478
+ pointerEvents: "none"
9479
+ }
9480
+ },
9481
+ `tick-${t2}`
9482
+ )
9483
+ );
9484
+ if (isMajor && t2 > epsilon) {
9485
+ labels.push(
9486
+ /* @__PURE__ */ jsxRuntime.jsx(
9487
+ "div",
9488
+ {
9489
+ style: {
9490
+ position: "absolute",
9491
+ left,
9492
+ bottom: "6px",
9493
+ transform: "translateX(-50%)",
9494
+ color: "rgba(255,255,255,0.7)",
9495
+ font: "bold 10px system-ui, sans-serif",
9496
+ pointerEvents: "none",
9497
+ textShadow: "1px 1px 2px rgba(0,0,0,0.8)"
9498
+ },
9499
+ children: `${Math.floor(t2)}s`
9500
+ },
9501
+ `lbl-${t2}`
9502
+ )
9503
+ );
9504
+ }
9505
+ });
9506
+ return /* @__PURE__ */ jsxRuntime.jsxs(
9507
+ "div",
9508
+ {
9509
+ style: {
9510
+ position: "relative",
9511
+ width: `${Math.max(1, Math.round(totalWidth))}px`,
9512
+ height: "32px",
9513
+ backgroundColor: "#0f0f0f"
9514
+ },
9515
+ children: [
9516
+ ticks,
9517
+ labels
9518
+ ]
9519
+ }
9520
+ );
9521
+ })(),
9456
9522
  /* @__PURE__ */ jsxRuntime.jsxs(
9457
9523
  "div",
9458
9524
  {
9459
9525
  ...bind(),
9460
9526
  className: "twick-seek-track-playhead",
9461
9527
  style: {
9528
+ position: "absolute",
9462
9529
  left: seekPosition,
9530
+ top: 0,
9463
9531
  touchAction: "none",
9464
9532
  transition: isDragging2 ? "none" : "left 0.1s linear"
9465
9533
  },
@@ -9483,7 +9551,8 @@ This message will only show in development mode. It won't appear in production.
9483
9551
  duration,
9484
9552
  zoom,
9485
9553
  timelineCount,
9486
- onSeek
9554
+ onSeek,
9555
+ timelineTickConfigs
9487
9556
  }) => {
9488
9557
  const { currentTime } = livePlayer.useLivePlayerContext();
9489
9558
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -9493,7 +9562,8 @@ This message will only show in development mode. It won't appear in production.
9493
9562
  currentTime,
9494
9563
  zoom,
9495
9564
  onSeek,
9496
- timelineCount
9565
+ timelineCount,
9566
+ timelineTickConfigs
9497
9567
  }
9498
9568
  );
9499
9569
  };
@@ -16937,6 +17007,90 @@ This message will only show in development mode. It won't appear in production.
16937
17007
  END: "end"
16938
17008
  };
16939
17009
  const DEFAULT_TIMELINE_ZOOM = 1.5;
17010
+ const DEFAULT_TIMELINE_ZOOM_CONFIG = {
17011
+ /** Minimum zoom level (10%) */
17012
+ min: 0.1,
17013
+ /** Maximum zoom level (300%) */
17014
+ max: 3,
17015
+ /** Zoom step increment/decrement (10%) */
17016
+ step: 0.1,
17017
+ /** Default zoom level (150%) */
17018
+ default: 1.5
17019
+ };
17020
+ const DEFAULT_TIMELINE_TICK_CONFIGS = [
17021
+ {
17022
+ durationThreshold: 10,
17023
+ // < 10 seconds
17024
+ majorInterval: 1,
17025
+ // 1s major ticks
17026
+ minorTicks: 10
17027
+ // 0.1s minor ticks (10 minors between majors)
17028
+ },
17029
+ {
17030
+ durationThreshold: 30,
17031
+ // < 30 seconds
17032
+ majorInterval: 5,
17033
+ // 5s major ticks
17034
+ minorTicks: 5
17035
+ // 1s minor ticks (5 minors between majors)
17036
+ },
17037
+ {
17038
+ durationThreshold: 120,
17039
+ // < 2 minutes
17040
+ majorInterval: 10,
17041
+ // 10s major ticks
17042
+ minorTicks: 5
17043
+ // 2s minor ticks (5 minors between majors)
17044
+ },
17045
+ {
17046
+ durationThreshold: 300,
17047
+ // < 5 minutes
17048
+ majorInterval: 30,
17049
+ // 30s major ticks
17050
+ minorTicks: 6
17051
+ // 5s minor ticks (6 minors between majors)
17052
+ },
17053
+ {
17054
+ durationThreshold: 900,
17055
+ // < 15 minutes
17056
+ majorInterval: 60,
17057
+ // 1m major ticks
17058
+ minorTicks: 6
17059
+ // 10s minor ticks (6 minors between majors)
17060
+ },
17061
+ {
17062
+ durationThreshold: 1800,
17063
+ // < 30 minutes
17064
+ majorInterval: 120,
17065
+ // 2m major ticks
17066
+ minorTicks: 4
17067
+ // 30s minor ticks (4 minors between majors)
17068
+ },
17069
+ {
17070
+ durationThreshold: 3600,
17071
+ // < 1 hour
17072
+ majorInterval: 300,
17073
+ // 5m major ticks
17074
+ minorTicks: 5
17075
+ // 1m minor ticks (5 minors between majors)
17076
+ },
17077
+ {
17078
+ durationThreshold: 7200,
17079
+ // < 2 hours
17080
+ majorInterval: 600,
17081
+ // 10m major ticks
17082
+ minorTicks: 10
17083
+ // 1m minor ticks (10 minors between majors)
17084
+ },
17085
+ {
17086
+ durationThreshold: Infinity,
17087
+ // >= 2 hours
17088
+ majorInterval: 1800,
17089
+ // 30m major ticks
17090
+ minorTicks: 6
17091
+ // 5m minor ticks (6 minors between majors)
17092
+ }
17093
+ ];
16940
17094
  const DEFAULT_ELEMENT_COLORS = {
16941
17095
  /** Fragment element color - deep charcoal matching UI background */
16942
17096
  fragment: "#1A1A1A",
@@ -17032,7 +17186,8 @@ This message will only show in development mode. It won't appear in production.
17032
17186
  selectedItem,
17033
17187
  onSelection,
17034
17188
  onDrag,
17035
- allowOverlap = false
17189
+ allowOverlap = false,
17190
+ elementColors
17036
17191
  }) => {
17037
17192
  const ref = React.useRef(null);
17038
17193
  const dragType = React.useRef(null);
@@ -17132,8 +17287,9 @@ This message will only show in development mode. It won't appear in production.
17132
17287
  }
17133
17288
  };
17134
17289
  const getElementColor = (elementType) => {
17135
- if (elementType in ELEMENT_COLORS) {
17136
- return ELEMENT_COLORS[elementType];
17290
+ const colors = elementColors || ELEMENT_COLORS;
17291
+ if (elementType in colors) {
17292
+ return colors[elementType];
17137
17293
  }
17138
17294
  return ELEMENT_COLORS.element;
17139
17295
  };
@@ -17209,7 +17365,8 @@ This message will only show in development mode. It won't appear in production.
17209
17365
  selectedItem,
17210
17366
  onItemSelection,
17211
17367
  onDrag,
17212
- allowOverlap = false
17368
+ allowOverlap = false,
17369
+ elementColors
17213
17370
  }) => {
17214
17371
  const trackRef = React.useRef(null);
17215
17372
  const trackWidthStyle = `${Math.max(100, duration * zoom * 100)}px`;
@@ -17232,6 +17389,7 @@ This message will only show in development mode. It won't appear in production.
17232
17389
  selectedItem,
17233
17390
  onSelection: onItemSelection,
17234
17391
  onDrag,
17392
+ elementColors,
17235
17393
  nextStart: index < elements.length - 1 ? elements[index + 1].getStart() : null,
17236
17394
  prevEnd: index > 0 ? elements[index - 1].getEnd() : 0
17237
17395
  },
@@ -17249,7 +17407,8 @@ This message will only show in development mode. It won't appear in production.
17249
17407
  onAddTrack,
17250
17408
  onReorder,
17251
17409
  onSelectionChange,
17252
- onElementDrag
17410
+ onElementDrag,
17411
+ elementColors
17253
17412
  }) {
17254
17413
  const containerRef = React.useRef(null);
17255
17414
  const seekContainerRef = React.useRef(null);
@@ -17361,7 +17520,8 @@ This message will only show in development mode. It won't appear in production.
17361
17520
  allowOverlap: false,
17362
17521
  trackWidth: timelineWidth - labelWidth,
17363
17522
  onItemSelection: handleItemSelection,
17364
- onDrag: onElementDrag
17523
+ onDrag: onElementDrag,
17524
+ elementColors
17365
17525
  }
17366
17526
  )
17367
17527
  ] }, track.getId())) })
@@ -17424,7 +17584,9 @@ This message will only show in development mode. It won't appear in production.
17424
17584
  };
17425
17585
  };
17426
17586
  const TimelineManager = ({
17427
- trackZoom
17587
+ trackZoom,
17588
+ timelineTickConfigs,
17589
+ elementColors
17428
17590
  }) => {
17429
17591
  var _a2;
17430
17592
  const { timelineData, totalDuration, selectedItem, onAddTrack, onReorder, onElementDrag, onSeek, onSelectionChange } = useTimelineManager();
@@ -17442,13 +17604,15 @@ This message will only show in development mode. It won't appear in production.
17442
17604
  onElementDrag,
17443
17605
  onSeek,
17444
17606
  onSelectionChange,
17607
+ elementColors,
17445
17608
  seekTrack: /* @__PURE__ */ jsxRuntime.jsx(
17446
17609
  SeekControl,
17447
17610
  {
17448
17611
  duration: totalDuration,
17449
17612
  zoom: trackZoom,
17450
17613
  onSeek,
17451
- timelineCount: ((_a2 = timelineData == null ? void 0 : timelineData.tracks) == null ? void 0 : _a2.length) ?? 0
17614
+ timelineCount: ((_a2 = timelineData == null ? void 0 : timelineData.tracks) == null ? void 0 : _a2.length) ?? 0,
17615
+ timelineTickConfigs
17452
17616
  }
17453
17617
  )
17454
17618
  }
@@ -17476,9 +17640,6 @@ This message will only show in development mode. It won't appear in production.
17476
17640
  )
17477
17641
  ] });
17478
17642
  };
17479
- const MAX_ZOOM = 3;
17480
- const MIN_ZOOM = 0.5;
17481
- const ZOOM_STEP = 0.25;
17482
17643
  const PlayerControls = ({
17483
17644
  selectedItem,
17484
17645
  duration,
@@ -17493,8 +17654,12 @@ This message will only show in development mode. It won't appear in production.
17493
17654
  onDelete,
17494
17655
  zoomLevel = 1,
17495
17656
  setZoomLevel,
17496
- className = ""
17657
+ className = "",
17658
+ zoomConfig = DEFAULT_TIMELINE_ZOOM_CONFIG
17497
17659
  }) => {
17660
+ const MAX_ZOOM = zoomConfig.max;
17661
+ const MIN_ZOOM = zoomConfig.min;
17662
+ const ZOOM_STEP = zoomConfig.step;
17498
17663
  const formatTime = React.useCallback((time2) => {
17499
17664
  const minutes = Math.floor(time2 / 60);
17500
17665
  const seconds = Math.floor(time2 % 60);
@@ -17520,67 +17685,67 @@ This message will only show in development mode. It won't appear in production.
17520
17685
  setZoomLevel(zoomLevel - ZOOM_STEP);
17521
17686
  }
17522
17687
  }, [zoomLevel, setZoomLevel]);
17523
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `h-16 bg-gray-800 border-t border-gray-700 p-4 ${className}`, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
17524
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-4", children: [
17525
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
17526
- /* @__PURE__ */ jsxRuntime.jsx(
17527
- "button",
17528
- {
17529
- onClick: handleDelete,
17530
- disabled: !selectedItem,
17531
- title: "Delete",
17532
- className: `btn btn-ghost ${!!selectedItem ? "text-red-400 hover:text-red-300" : "text-gray-500 cursor-not-allowed"}`,
17533
- children: /* @__PURE__ */ jsxRuntime.jsx(Trash2, { className: "w-5 h-5" })
17534
- }
17535
- ),
17536
- /* @__PURE__ */ jsxRuntime.jsx(
17537
- "button",
17538
- {
17539
- onClick: handleSplit,
17540
- disabled: !(selectedItem instanceof timeline.TrackElement),
17541
- title: "Split",
17542
- className: `btn btn-ghost ${selectedItem instanceof timeline.TrackElement ? "text-purple-400 hover:text-purple-300" : "text-gray-500 cursor-not-allowed"}`,
17543
- children: /* @__PURE__ */ jsxRuntime.jsx(Scissors, { className: "w-5 h-5" })
17544
- }
17545
- ),
17546
- /* @__PURE__ */ jsxRuntime.jsx(
17547
- UndoRedoControls,
17548
- {
17549
- canUndo,
17550
- canRedo,
17551
- onUndo,
17552
- onRedo
17553
- }
17554
- )
17555
- ] }),
17688
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `player-controls ${className}`, children: [
17689
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "edit-controls", children: [
17690
+ /* @__PURE__ */ jsxRuntime.jsx(
17691
+ "button",
17692
+ {
17693
+ onClick: handleDelete,
17694
+ disabled: !selectedItem,
17695
+ title: "Delete",
17696
+ className: `control-btn delete-btn ${!selectedItem ? "btn-disabled" : ""}`,
17697
+ children: /* @__PURE__ */ jsxRuntime.jsx(Trash2, { className: "icon-md" })
17698
+ }
17699
+ ),
17700
+ /* @__PURE__ */ jsxRuntime.jsx(
17701
+ "button",
17702
+ {
17703
+ onClick: handleSplit,
17704
+ disabled: !(selectedItem instanceof timeline.TrackElement),
17705
+ title: "Split",
17706
+ className: `control-btn split-btn ${!(selectedItem instanceof timeline.TrackElement) ? "btn-disabled" : ""}`,
17707
+ children: /* @__PURE__ */ jsxRuntime.jsx(Scissors, { className: "icon-md" })
17708
+ }
17709
+ ),
17710
+ /* @__PURE__ */ jsxRuntime.jsx(
17711
+ UndoRedoControls,
17712
+ {
17713
+ canUndo,
17714
+ canRedo,
17715
+ onUndo,
17716
+ onRedo
17717
+ }
17718
+ )
17719
+ ] }),
17720
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "playback-controls", children: [
17556
17721
  /* @__PURE__ */ jsxRuntime.jsx(
17557
17722
  "button",
17558
17723
  {
17559
17724
  onClick: togglePlayback,
17560
17725
  disabled: playerState === livePlayer.PLAYER_STATE.REFRESH,
17561
17726
  title: playerState === livePlayer.PLAYER_STATE.PLAYING ? "Pause" : playerState === livePlayer.PLAYER_STATE.REFRESH ? "Refreshing" : "Play",
17562
- className: "btn btn-ghost text-white",
17563
- children: playerState === livePlayer.PLAYER_STATE.PLAYING ? /* @__PURE__ */ jsxRuntime.jsx(Pause, { className: "w-6 h-6" }) : playerState === livePlayer.PLAYER_STATE.REFRESH ? /* @__PURE__ */ jsxRuntime.jsx(LoaderCircle, { className: "w-6 h-6 animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(Play, { className: "w-6 h-6" })
17727
+ className: "control-btn play-pause-btn",
17728
+ children: playerState === livePlayer.PLAYER_STATE.PLAYING ? /* @__PURE__ */ jsxRuntime.jsx(Pause, { className: "icon-lg" }) : playerState === livePlayer.PLAYER_STATE.REFRESH ? /* @__PURE__ */ jsxRuntime.jsx(LoaderCircle, { className: "icon-lg animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(Play, { className: "icon-lg" })
17564
17729
  }
17565
17730
  ),
17566
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-gray-300", children: [
17567
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium", children: formatTime(currentTime) }),
17568
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-500", children: "/" }),
17569
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm", children: formatTime(duration) })
17731
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "time-display", children: [
17732
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "current-time", children: formatTime(currentTime) }),
17733
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "time-separator", children: "/" }),
17734
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "total-time", children: formatTime(duration) })
17570
17735
  ] })
17571
17736
  ] }),
17572
- setZoomLevel && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
17737
+ setZoomLevel && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twick-track-zoom-container", children: [
17573
17738
  /* @__PURE__ */ jsxRuntime.jsx(
17574
17739
  "button",
17575
17740
  {
17576
17741
  onClick: handleZoomOut,
17577
17742
  disabled: zoomLevel <= MIN_ZOOM,
17578
17743
  title: "Zoom Out",
17579
- className: `btn btn-ghost ${zoomLevel > MIN_ZOOM ? "text-gray-300 hover:text-white" : "text-gray-500 cursor-not-allowed"}`,
17580
- children: /* @__PURE__ */ jsxRuntime.jsx(ZoomOut, { className: "w-5 h-5" })
17744
+ className: `control-btn ${zoomLevel <= MIN_ZOOM ? "btn-disabled" : ""}`,
17745
+ children: /* @__PURE__ */ jsxRuntime.jsx(ZoomOut, { className: "icon-md" })
17581
17746
  }
17582
17747
  ),
17583
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-sm text-gray-300 font-medium min-w-[3rem] text-center", children: [
17748
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "zoom-level", children: [
17584
17749
  Math.round(zoomLevel * 100),
17585
17750
  "%"
17586
17751
  ] }),
@@ -17590,12 +17755,12 @@ This message will only show in development mode. It won't appear in production.
17590
17755
  onClick: handleZoomIn,
17591
17756
  disabled: zoomLevel >= MAX_ZOOM,
17592
17757
  title: "Zoom In",
17593
- className: `btn btn-ghost ${zoomLevel < MAX_ZOOM ? "text-gray-300 hover:text-white" : "text-gray-500 cursor-not-allowed"}`,
17594
- children: /* @__PURE__ */ jsxRuntime.jsx(ZoomIn, { className: "w-5 h-5" })
17758
+ className: `control-btn ${zoomLevel >= MAX_ZOOM ? "btn-disabled" : ""}`,
17759
+ children: /* @__PURE__ */ jsxRuntime.jsx(ZoomIn, { className: "icon-md" })
17595
17760
  }
17596
17761
  )
17597
17762
  ] })
17598
- ] }) });
17763
+ ] });
17599
17764
  };
17600
17765
  const usePlayerControl = () => {
17601
17766
  const { playerState, setPlayerState } = livePlayer.useLivePlayerContext();
@@ -17651,7 +17816,8 @@ This message will only show in development mode. It won't appear in production.
17651
17816
  };
17652
17817
  const ControlManager = ({
17653
17818
  trackZoom,
17654
- setTrackZoom
17819
+ setTrackZoom,
17820
+ zoomConfig
17655
17821
  }) => {
17656
17822
  const { currentTime, playerState } = livePlayer.useLivePlayerContext();
17657
17823
  const { togglePlayback } = usePlayerControl();
@@ -17672,7 +17838,8 @@ This message will only show in development mode. It won't appear in production.
17672
17838
  onUndo: handleUndo,
17673
17839
  onRedo: handleRedo,
17674
17840
  zoomLevel: trackZoom,
17675
- setZoomLevel: setTrackZoom
17841
+ setZoomLevel: setTrackZoom,
17842
+ zoomConfig
17676
17843
  }
17677
17844
  ) });
17678
17845
  };
@@ -17683,7 +17850,16 @@ This message will only show in development mode. It won't appear in production.
17683
17850
  editorConfig,
17684
17851
  defaultPlayControls = true
17685
17852
  }) => {
17686
- const [trackZoom, setTrackZoom] = React.useState(DEFAULT_TIMELINE_ZOOM);
17853
+ const zoomConfig = editorConfig.timelineZoomConfig ?? DEFAULT_TIMELINE_ZOOM_CONFIG;
17854
+ const timelineTickConfigs = (editorConfig == null ? void 0 : editorConfig.timelineTickConfigs) ?? DEFAULT_TIMELINE_TICK_CONFIGS;
17855
+ const elementColors = React.useMemo(
17856
+ () => ({
17857
+ ...DEFAULT_ELEMENT_COLORS,
17858
+ ...(editorConfig == null ? void 0 : editorConfig.elementColors) || {}
17859
+ }),
17860
+ [editorConfig == null ? void 0 : editorConfig.elementColors]
17861
+ );
17862
+ const [trackZoom, setTrackZoom] = React.useState(zoomConfig.default);
17687
17863
  const useMemoizedPlayerManager = React.useMemo(
17688
17864
  () => /* @__PURE__ */ jsxRuntime.jsx(
17689
17865
  PlayerManager,
@@ -17703,8 +17879,22 @@ This message will only show in development mode. It won't appear in production.
17703
17879
  ] }),
17704
17880
  bottomPanel ? bottomPanel : null,
17705
17881
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "twick-editor-timeline-section", children: [
17706
- defaultPlayControls ? /* @__PURE__ */ jsxRuntime.jsx(ControlManager, { trackZoom, setTrackZoom }) : null,
17707
- /* @__PURE__ */ jsxRuntime.jsx(TimelineManager, { trackZoom })
17882
+ defaultPlayControls ? /* @__PURE__ */ jsxRuntime.jsx(
17883
+ ControlManager,
17884
+ {
17885
+ trackZoom,
17886
+ setTrackZoom,
17887
+ zoomConfig
17888
+ }
17889
+ ) : null,
17890
+ /* @__PURE__ */ jsxRuntime.jsx(
17891
+ TimelineManager,
17892
+ {
17893
+ trackZoom,
17894
+ timelineTickConfigs,
17895
+ elementColors
17896
+ }
17897
+ )
17708
17898
  ] })
17709
17899
  ] });
17710
17900
  };
@@ -18054,7 +18244,9 @@ This message will only show in development mode. It won't appear in production.
18054
18244
  exports2.BaseMediaManager = BaseMediaManager;
18055
18245
  exports2.BrowserMediaManager = BrowserMediaManager;
18056
18246
  exports2.DEFAULT_ELEMENT_COLORS = DEFAULT_ELEMENT_COLORS;
18247
+ exports2.DEFAULT_TIMELINE_TICK_CONFIGS = DEFAULT_TIMELINE_TICK_CONFIGS;
18057
18248
  exports2.DEFAULT_TIMELINE_ZOOM = DEFAULT_TIMELINE_ZOOM;
18249
+ exports2.DEFAULT_TIMELINE_ZOOM_CONFIG = DEFAULT_TIMELINE_ZOOM_CONFIG;
18058
18250
  exports2.DRAG_TYPE = DRAG_TYPE;
18059
18251
  exports2.INITIAL_TIMELINE_DATA = INITIAL_TIMELINE_DATA;
18060
18252
  exports2.MIN_DURATION = MIN_DURATION;