@twick/video-editor 0.15.15 → 0.15.18

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.
@@ -4,17 +4,6 @@ import { TimelineTickConfig } from '../components/video-editor';
4
4
  /**
5
5
  * Initial timeline data structure for new video editor projects.
6
6
  * Provides a default timeline with a sample text element to get started.
7
- *
8
- * @example
9
- * ```js
10
- * import { INITIAL_TIMELINE_DATA } from '@twick/video-editor';
11
- *
12
- * // Use as starting point for new projects
13
- * const newProject = {
14
- * ...INITIAL_TIMELINE_DATA,
15
- * tracks: [...INITIAL_TIMELINE_DATA.tracks, newTrack]
16
- * };
17
- * ```
18
7
  */
19
8
  export declare const INITIAL_TIMELINE_DATA: {
20
9
  tracks: {
@@ -39,43 +28,8 @@ export declare const INITIAL_TIMELINE_DATA: {
39
28
  /**
40
29
  * Minimum duration for timeline elements in seconds.
41
30
  * Used to prevent elements from having zero or negative duration.
42
- *
43
- * @example
44
- * ```js
45
- * import { MIN_DURATION } from '@twick/video-editor';
46
- *
47
- * const elementDuration = Math.max(duration, MIN_DURATION);
48
- * // Ensures element has at least 0.1 seconds duration
49
- * ```
50
31
  */
51
32
  export declare const MIN_DURATION = 0.1;
52
- /**
53
- * Drag operation types for timeline interactions.
54
- * Defines the different phases of drag operations on timeline elements.
55
- *
56
- * @example
57
- * ```js
58
- * import { DRAG_TYPE } from '@twick/video-editor';
59
- *
60
- * function handleDrag(type) {
61
- * switch (type) {
62
- * case DRAG_TYPE.START:
63
- * // Handle drag start
64
- * break;
65
- * case DRAG_TYPE.MOVE:
66
- * // Handle drag move
67
- * break;
68
- * case DRAG_TYPE.END:
69
- * // Handle drag end
70
- * break;
71
- * }
72
- * }
73
- * ```
74
- */
75
- /**
76
- * MIME type for media items dragged from the studio's media panels (video, audio, image)
77
- * to the timeline. The data format is JSON: { type: "video"|"audio"|"image", url: string }.
78
- */
79
33
  export declare const TIMELINE_DROP_MEDIA_TYPE = "application/x-twick-media";
80
34
  export declare const DRAG_TYPE: {
81
35
  /** Drag operation is starting */
@@ -85,35 +39,10 @@ export declare const DRAG_TYPE: {
85
39
  /** Drag operation has ended */
86
40
  readonly END: "end";
87
41
  };
88
- /**
89
- * Default zoom level for timeline view.
90
- * Controls the initial magnification of the timeline interface.
91
- *
92
- * @example
93
- * ```js
94
- * import { DEFAULT_TIMELINE_ZOOM } from '@twick/video-editor';
95
- *
96
- * const [zoom, setZoom] = useState(DEFAULT_TIMELINE_ZOOM);
97
- * // Timeline starts with 1.5x zoom
98
- * ```
99
- */
100
42
  export declare const DEFAULT_TIMELINE_ZOOM = 1.5;
101
43
  /**
102
44
  * Default timeline zoom configuration including min, max, step, and default values.
103
45
  * Controls the zoom behavior and constraints for the timeline view.
104
- *
105
- * @example
106
- * ```js
107
- * import { DEFAULT_TIMELINE_ZOOM_CONFIG } from '@twick/video-editor';
108
- *
109
- * // Use default zoom configuration
110
- * <VideoEditor
111
- * editorConfig={{
112
- * videoProps: { width: 1920, height: 1080 },
113
- * timelineZoomConfig: DEFAULT_TIMELINE_ZOOM_CONFIG
114
- * }}
115
- * />
116
- * ```
117
46
  */
118
47
  /**
119
48
  * Default frames per second for timeline time display.
@@ -139,54 +68,16 @@ export declare const DEFAULT_TIMELINE_ZOOM_CONFIG: {
139
68
  *
140
69
  * Each configuration applies when the duration is less than the specified threshold.
141
70
  * Configurations are ordered by duration threshold ascending.
142
- *
143
- * @example
144
- * ```js
145
- * import { DEFAULT_TIMELINE_TICK_CONFIGS } from '@twick/video-editor';
146
- *
147
- * // Use default configurations
148
- * <VideoEditor
149
- * editorConfig={{
150
- * videoProps: { width: 1920, height: 1080 },
151
- * timelineTickConfigs: DEFAULT_TIMELINE_TICK_CONFIGS
152
- * }}
153
- * />
154
- * ```
155
71
  */
156
72
  export declare const DEFAULT_TIMELINE_TICK_CONFIGS: TimelineTickConfig[];
157
73
  /**
158
74
  * Default color scheme for different element types in the timeline.
159
75
  * Provides consistent visual distinction between various timeline elements.
160
- *
161
- * @example
162
- * ```js
163
- * import { DEFAULT_ELEMENT_COLORS } from '@twick/video-editor';
164
- *
165
- * const videoColor = DEFAULT_ELEMENT_COLORS.video; // "#4B2E83"
166
- * const textColor = DEFAULT_ELEMENT_COLORS.text; // "#375A7F"
167
- *
168
- * // Apply colors to timeline elements
169
- * element.style.backgroundColor = DEFAULT_ELEMENT_COLORS[element.type];
170
- * ```
171
76
  */
172
77
  export declare const DEFAULT_ELEMENT_COLORS: ElementColors;
173
78
  /**
174
79
  * Available text fonts for video editor text elements.
175
80
  * Includes Google Fonts, display fonts, and custom CDN fonts.
176
- *
177
- * @example
178
- * ```js
179
- * import { AVAILABLE_TEXT_FONTS } from '@twick/video-editor';
180
- *
181
- * // Use Google Fonts
182
- * const googleFont = AVAILABLE_TEXT_FONTS.ROBOTO; // "Roboto"
183
- *
184
- * // Use decorative fonts
185
- * const decorativeFont = AVAILABLE_TEXT_FONTS.BANGERS; // "Bangers"
186
- *
187
- * // Apply font to text element
188
- * textElement.style.fontFamily = AVAILABLE_TEXT_FONTS.POPPINS;
189
- * ```
190
81
  */
191
82
  export declare const AVAILABLE_TEXT_FONTS: {
192
83
  /** Modern sans-serif font */
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Creates a debounced version of a function.
3
+ * The function will only be called after it has not been invoked
4
+ * for the specified delay.
5
+ *
6
+ * Useful for expensive operations that should not run on every
7
+ * keystroke / mouse move (e.g. resize handlers, search, etc.).
8
+ */
9
+ export declare function debounce<T extends (...args: any[]) => any>(fn: T, delay: number): (...args: Parameters<T>) => void;
10
+ /**
11
+ * Creates a throttled version of a function.
12
+ * The function will be called at most once in every `interval`
13
+ * milliseconds, ignoring additional calls in between.
14
+ *
15
+ * Useful for high–frequency events like scroll / mousemove where
16
+ * you still want regular updates but not on every event.
17
+ */
18
+ export declare function throttle<T extends (...args: any[]) => any>(fn: T, interval: number): (...args: Parameters<T>) => void;
@@ -27,8 +27,9 @@ export declare const usePlayerManager: ({ videoProps, canvasConfig, }: {
27
27
  }) => {
28
28
  twickCanvas: any;
29
29
  projectData: any;
30
- updateCanvas: (seekTime: number) => void;
30
+ updateCanvas: (seekTime: number, forceRefresh?: boolean) => void;
31
31
  buildCanvas: any;
32
+ resizeCanvas: any;
32
33
  onPlayerUpdate: (event: CustomEvent) => void;
33
34
  playerUpdating: boolean;
34
35
  handleDropOnCanvas: (payload: CanvasDropPayload) => Promise<void>;
package/dist/index.d.ts CHANGED
@@ -15,6 +15,7 @@ import { setElementColors } from './helpers/editor.utils';
15
15
  export { setElementColors };
16
16
  export type { MediaItem, PaginationOptions, SearchOptions, Animation, TextEffect, ElementColors };
17
17
  export type { PlayerControlsProps, VideoEditorProps, VideoEditorConfig, TimelineTickConfig, TimelineZoomConfig, CanvasConfig };
18
+ export { throttle, debounce } from './helpers/function.utils';
18
19
  export { ANIMATIONS, TEXT_EFFECTS };
19
20
  export { usePlayerControl, useEditorManager, BrowserMediaManager, BaseMediaManager, animationGifs, getAnimationGif, PlayerControls, TimelineManager, useTimelineControl };
20
21
  export * from './helpers/constants';
package/dist/index.js CHANGED
@@ -6885,9 +6885,9 @@ const CANVAS_OPERATIONS = {
6885
6885
  CAPTION_PROPS_UPDATED: "CAPTION_PROPS_UPDATED",
6886
6886
  /** Watermark has been updated */
6887
6887
  WATERMARK_UPDATED: "WATERMARK_UPDATED",
6888
- /** A new element was added via drop on canvas; payload is { element } */
6888
+ /** A new element was added via drop on canvas; payload is &#123; element &#125; */
6889
6889
  ADDED_NEW_ELEMENT: "ADDED_NEW_ELEMENT",
6890
- /** Z-order changed (bring to front / send to back). Payload is { elementId, direction }. Timeline should reorder tracks. */
6890
+ /** Z-order changed (bring to front / send to back). Payload is &#123; elementId, direction &#125;. Timeline should reorder tracks. */
6891
6891
  Z_ORDER_CHANGED: "Z_ORDER_CHANGED"
6892
6892
  };
6893
6893
  const ELEMENT_TYPES = {
@@ -8443,6 +8443,30 @@ const useTwickCanvas = ({
8443
8443
  scaleX: 1,
8444
8444
  scaleY: 1
8445
8445
  });
8446
+ const resizeCanvas = ({
8447
+ canvasSize,
8448
+ videoSize = videoSizeRef.current
8449
+ }) => {
8450
+ const canvas = twickCanvasRef.current;
8451
+ if (!canvas || !getCanvasContext(canvas)) return;
8452
+ if (!(videoSize == null ? void 0 : videoSize.width) || !(videoSize == null ? void 0 : videoSize.height)) return;
8453
+ if (canvasResolutionRef.current.width === canvasSize.width && canvasResolutionRef.current.height === canvasSize.height) {
8454
+ return;
8455
+ }
8456
+ canvasMetadataRef.current = {
8457
+ width: canvasSize.width,
8458
+ height: canvasSize.height,
8459
+ aspectRatio: canvasSize.width / canvasSize.height,
8460
+ scaleX: Number((canvasSize.width / videoSize.width).toFixed(2)),
8461
+ scaleY: Number((canvasSize.height / videoSize.height).toFixed(2))
8462
+ };
8463
+ canvas.setDimensions({
8464
+ width: canvasSize.width,
8465
+ height: canvasSize.height
8466
+ });
8467
+ canvasResolutionRef.current = canvasSize;
8468
+ canvas.requestRenderAll();
8469
+ };
8446
8470
  const onVideoSizeChange = (videoSize) => {
8447
8471
  if (videoSize) {
8448
8472
  videoSizeRef.current = videoSize;
@@ -8716,9 +8740,7 @@ const useTwickCanvas = ({
8716
8740
  reorderElementsByZIndex(twickCanvas);
8717
8741
  }
8718
8742
  };
8719
- const addWatermarkToCanvas = ({
8720
- element
8721
- }) => {
8743
+ const addWatermarkToCanvas = ({ element }) => {
8722
8744
  if (!twickCanvas) return;
8723
8745
  const handler = elementController.get("watermark");
8724
8746
  if (handler) {
@@ -8748,6 +8770,7 @@ const useTwickCanvas = ({
8748
8770
  return {
8749
8771
  twickCanvas,
8750
8772
  buildCanvas,
8773
+ resizeCanvas,
8751
8774
  onVideoSizeChange,
8752
8775
  addWatermarkToCanvas,
8753
8776
  addElementToCanvas,
@@ -9166,10 +9189,12 @@ const usePlayerManager = ({
9166
9189
  changeLog,
9167
9190
  videoResolution
9168
9191
  } = timeline.useTimelineContext();
9169
- const { getCurrentTime } = livePlayer.useLivePlayerContext();
9192
+ const { getCurrentTime, setSeekTime } = livePlayer.useLivePlayerContext();
9170
9193
  const currentChangeLog = React.useRef(changeLog);
9171
9194
  const prevSeekTime = React.useRef(0);
9172
9195
  const [playerUpdating, setPlayerUpdating] = React.useState(false);
9196
+ const updateCanvasRef = React.useRef(() => {
9197
+ });
9173
9198
  const handleCanvasReady = (_canvas) => {
9174
9199
  };
9175
9200
  const handleCanvasOperation = (operation, data) => {
@@ -9205,13 +9230,14 @@ const usePlayerManager = ({
9205
9230
  }
9206
9231
  editor.reorderTracks(reordered);
9207
9232
  currentChangeLog.current = currentChangeLog.current + 1;
9233
+ updateCanvasRef.current(getCurrentTime(), true);
9208
9234
  return;
9209
9235
  }
9210
9236
  if (operation === CANVAS_OPERATIONS.CAPTION_PROPS_UPDATED) {
9211
9237
  const captionsTrack = editor.getCaptionsTrack();
9212
9238
  captionsTrack == null ? void 0 : captionsTrack.setProps(data.props);
9213
- setSelectedItem(data.element);
9214
9239
  editor.refresh();
9240
+ updateCanvasRef.current(getCurrentTime(), true);
9215
9241
  } else if (operation === CANVAS_OPERATIONS.WATERMARK_UPDATED) {
9216
9242
  const w2 = editor.getWatermark();
9217
9243
  if (w2 && data) {
@@ -9222,6 +9248,7 @@ const usePlayerManager = ({
9222
9248
  editor.setWatermark(w2);
9223
9249
  currentChangeLog.current = currentChangeLog.current + 1;
9224
9250
  }
9251
+ updateCanvasRef.current(getCurrentTime(), true);
9225
9252
  } else {
9226
9253
  const element = timeline.ElementDeserializer.fromJSON(data);
9227
9254
  switch (operation) {
@@ -9233,6 +9260,7 @@ const usePlayerManager = ({
9233
9260
  const updatedElement = editor.updateElement(element);
9234
9261
  currentChangeLog.current = currentChangeLog.current + 1;
9235
9262
  setSelectedItem(updatedElement);
9263
+ updateCanvasRef.current(getCurrentTime(), true);
9236
9264
  }
9237
9265
  break;
9238
9266
  }
@@ -9250,17 +9278,20 @@ const usePlayerManager = ({
9250
9278
  setSelectedItem(element);
9251
9279
  currentChangeLog.current = currentChangeLog.current + 1;
9252
9280
  editor.refresh();
9281
+ setSeekTime(currentTime);
9282
+ updateCanvasRef.current(currentTime, true);
9253
9283
  handleCanvasOperation(CANVAS_OPERATIONS.ADDED_NEW_ELEMENT, {
9254
9284
  element,
9255
9285
  canvasPosition: canvasX != null && canvasY != null ? { x: canvasX, y: canvasY } : void 0
9256
9286
  });
9257
9287
  }
9258
9288
  },
9259
- [editor, videoResolution, getCurrentTime, setSelectedItem]
9289
+ [editor, videoResolution, getCurrentTime, setSelectedItem, setSeekTime]
9260
9290
  );
9261
9291
  const {
9262
9292
  twickCanvas,
9263
9293
  buildCanvas,
9294
+ resizeCanvas,
9264
9295
  setCanvasElements,
9265
9296
  bringToFront,
9266
9297
  sendToBack,
@@ -9271,9 +9302,9 @@ const usePlayerManager = ({
9271
9302
  onCanvasOperation: handleCanvasOperation,
9272
9303
  enableShiftAxisLock: (canvasConfig == null ? void 0 : canvasConfig.enableShiftAxisLock) ?? false
9273
9304
  });
9274
- const updateCanvas = (seekTime) => {
9305
+ const updateCanvas = (seekTime, forceRefresh = false) => {
9275
9306
  var _a;
9276
- if (changeLog === currentChangeLog.current && seekTime === prevSeekTime.current) {
9307
+ if (!forceRefresh && changeLog === currentChangeLog.current && seekTime === prevSeekTime.current) {
9277
9308
  return;
9278
9309
  }
9279
9310
  prevSeekTime.current = seekTime;
@@ -9314,6 +9345,7 @@ const usePlayerManager = ({
9314
9345
  });
9315
9346
  currentChangeLog.current = changeLog;
9316
9347
  };
9348
+ updateCanvasRef.current = updateCanvas;
9317
9349
  const onPlayerUpdate = (event) => {
9318
9350
  var _a;
9319
9351
  if (((_a = event == null ? void 0 : event.detail) == null ? void 0 : _a.status) === "ready") {
@@ -9367,6 +9399,7 @@ const usePlayerManager = ({
9367
9399
  projectData,
9368
9400
  updateCanvas,
9369
9401
  buildCanvas,
9402
+ resizeCanvas,
9370
9403
  onPlayerUpdate,
9371
9404
  playerUpdating,
9372
9405
  handleDropOnCanvas,
@@ -9460,6 +9493,47 @@ function getCanvasY(e3, container, videoSize) {
9460
9493
  const relY = (e3.clientY - rect.top) / rect.height;
9461
9494
  return Math.max(0, Math.min(videoSize.height, relY * videoSize.height));
9462
9495
  }
9496
+ function debounce(fn2, delay2) {
9497
+ let timeoutId = null;
9498
+ return (...args) => {
9499
+ if (timeoutId !== null) {
9500
+ clearTimeout(timeoutId);
9501
+ }
9502
+ timeoutId = setTimeout(() => {
9503
+ timeoutId = null;
9504
+ fn2(...args);
9505
+ }, delay2);
9506
+ };
9507
+ }
9508
+ function throttle(fn2, interval) {
9509
+ let lastCallTime = 0;
9510
+ let trailingTimeoutId = null;
9511
+ let lastArgs = null;
9512
+ return (...args) => {
9513
+ const now2 = Date.now();
9514
+ const remaining = interval - (now2 - lastCallTime);
9515
+ if (remaining <= 0) {
9516
+ if (trailingTimeoutId !== null) {
9517
+ clearTimeout(trailingTimeoutId);
9518
+ trailingTimeoutId = null;
9519
+ }
9520
+ lastCallTime = now2;
9521
+ fn2(...args);
9522
+ } else {
9523
+ lastArgs = args;
9524
+ if (trailingTimeoutId === null) {
9525
+ trailingTimeoutId = setTimeout(() => {
9526
+ trailingTimeoutId = null;
9527
+ lastCallTime = Date.now();
9528
+ if (lastArgs) {
9529
+ fn2(...lastArgs);
9530
+ lastArgs = null;
9531
+ }
9532
+ }, remaining);
9533
+ }
9534
+ }
9535
+ };
9536
+ }
9463
9537
  const CanvasContextMenu = ({
9464
9538
  x: x2,
9465
9539
  y: y2,
@@ -9555,6 +9629,7 @@ const CanvasContextMenu = ({
9555
9629
  }
9556
9630
  );
9557
9631
  };
9632
+ const RESIZE_THROTTLE_MS = 200;
9558
9633
  const PlayerManager = ({
9559
9634
  videoProps,
9560
9635
  playerProps,
@@ -9564,6 +9639,7 @@ const PlayerManager = ({
9564
9639
  const containerRef = React.useRef(null);
9565
9640
  const canvasRef = React.useRef(null);
9566
9641
  const durationRef = React.useRef(0);
9642
+ const seekTimeRef = React.useRef(0);
9567
9643
  const { changeLog } = timeline.useTimelineContext();
9568
9644
  const {
9569
9645
  playerState,
@@ -9576,6 +9652,7 @@ const PlayerManager = ({
9576
9652
  twickCanvas,
9577
9653
  projectData,
9578
9654
  updateCanvas,
9655
+ resizeCanvas,
9579
9656
  playerUpdating,
9580
9657
  onPlayerUpdate,
9581
9658
  buildCanvas,
@@ -9597,19 +9674,44 @@ const PlayerManager = ({
9597
9674
  React.useEffect(() => {
9598
9675
  const container = containerRef.current;
9599
9676
  const canvasSize = {
9600
- width: container == null ? void 0 : container.clientWidth,
9601
- height: container == null ? void 0 : container.clientHeight
9677
+ width: (container == null ? void 0 : container.clientWidth) ?? 0,
9678
+ height: (container == null ? void 0 : container.clientHeight) ?? 0
9602
9679
  };
9603
- buildCanvas({
9604
- backgroundColor: videoProps.backgroundColor,
9605
- videoSize: {
9606
- width: videoProps.width,
9607
- height: videoProps.height
9608
- },
9609
- canvasSize,
9610
- canvasRef: canvasRef.current
9611
- });
9680
+ if (canvasSize.width > 0 && canvasSize.height > 0) {
9681
+ buildCanvas({
9682
+ backgroundColor: videoProps.backgroundColor,
9683
+ videoSize: {
9684
+ width: videoProps.width,
9685
+ height: videoProps.height
9686
+ },
9687
+ canvasSize,
9688
+ canvasRef: canvasRef.current
9689
+ });
9690
+ }
9612
9691
  }, [videoProps]);
9692
+ const handleResize = React.useMemo(
9693
+ () => throttle(() => {
9694
+ const container = containerRef.current;
9695
+ if (!container || !canvasMode || !twickCanvas) return;
9696
+ const width = container.clientWidth;
9697
+ const height = container.clientHeight;
9698
+ if (width <= 0 || height <= 0) return;
9699
+ resizeCanvas({
9700
+ canvasSize: { width, height },
9701
+ videoSize: { width: videoProps.width, height: videoProps.height }
9702
+ });
9703
+ updateCanvas(seekTimeRef.current, true);
9704
+ }, RESIZE_THROTTLE_MS),
9705
+ [canvasMode, twickCanvas, resizeCanvas, updateCanvas, videoProps.width, videoProps.height]
9706
+ );
9707
+ React.useEffect(() => {
9708
+ const container = containerRef.current;
9709
+ if (!container || !canvasMode) return;
9710
+ const resizeObserver = new ResizeObserver(handleResize);
9711
+ resizeObserver.observe(container);
9712
+ return () => resizeObserver.disconnect();
9713
+ }, [canvasMode, handleResize]);
9714
+ seekTimeRef.current = seekTime;
9613
9715
  React.useEffect(() => {
9614
9716
  if (twickCanvas && playerState === livePlayer.PLAYER_STATE.PAUSED) {
9615
9717
  updateCanvas(seekTime);
@@ -11119,13 +11221,23 @@ function SeekTrack({
11119
11221
  minorIntervalSec: minors > 0 ? major / (minors + 1) : major
11120
11222
  };
11121
11223
  }, [duration, timelineTickConfigs]);
11122
- const handleSeek = (clientX) => {
11123
- if (!containerRef.current) return;
11124
- const rect = containerRef.current.getBoundingClientRect();
11125
- const x2 = clientX - rect.left + (containerRef.current.scrollLeft || 0);
11126
- const newTime = Math.max(0, Math.min(duration, x2 / pixelsPerSecond));
11127
- onSeek(newTime);
11128
- };
11224
+ const seekToTime = React.useCallback(
11225
+ (time2) => {
11226
+ const clamped = Math.max(0, Math.min(duration, time2));
11227
+ onSeek(clamped);
11228
+ },
11229
+ [duration, onSeek]
11230
+ );
11231
+ const seekFromClientX = React.useCallback(
11232
+ (clientX) => {
11233
+ if (!containerRef.current) return;
11234
+ const rect = containerRef.current.getBoundingClientRect();
11235
+ const x2 = clientX - rect.left + (containerRef.current.scrollLeft || 0);
11236
+ const newTime = Math.max(0, Math.min(duration, x2 / pixelsPerSecond));
11237
+ seekToTime(newTime);
11238
+ },
11239
+ [duration, pixelsPerSecond, seekToTime]
11240
+ );
11129
11241
  const bind = useDrag(({ event, xy: [x2], active }) => {
11130
11242
  if (event) {
11131
11243
  event.stopPropagation();
@@ -11135,10 +11247,11 @@ function SeekTrack({
11135
11247
  const rect = containerRef.current.getBoundingClientRect();
11136
11248
  const xPos = x2 - rect.left + (containerRef.current.scrollLeft || 0);
11137
11249
  const newTime = Math.max(0, Math.min(duration, xPos / pixelsPerSecond));
11138
- setDragPosition(xPos);
11139
- onSeek(newTime);
11140
- if (!active) {
11250
+ if (active) {
11251
+ setDragPosition(xPos);
11252
+ } else {
11141
11253
  setDragPosition(null);
11254
+ seekToTime(newTime);
11142
11255
  }
11143
11256
  });
11144
11257
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "twick-seek-track", children: /* @__PURE__ */ jsxRuntime.jsxs(
@@ -11146,7 +11259,7 @@ function SeekTrack({
11146
11259
  {
11147
11260
  ref: containerRef,
11148
11261
  className: "twick-seek-track-container-no-scrollbar",
11149
- onClick: (e3) => handleSeek(e3.clientX),
11262
+ onClick: (e3) => seekFromClientX(e3.clientX),
11150
11263
  style: {
11151
11264
  overflowX: "auto",
11152
11265
  overflowY: "hidden",
@@ -18960,16 +19073,20 @@ function usePlayheadScroll(scrollContainerRef, playheadPositionPx, isActive, con
18960
19073
  const container = scrollContainerRef.current;
18961
19074
  const contentX = labelWidth + playheadPositionPx;
18962
19075
  const scrollToKeepPlayheadVisible = () => {
18963
- const { scrollLeft, clientWidth } = container;
19076
+ const { scrollLeft, clientWidth, scrollWidth } = container;
19077
+ const maxScrollLeft = Math.max(0, scrollWidth - clientWidth);
18964
19078
  const minVisible = scrollLeft + margin;
18965
19079
  const maxVisible = scrollLeft + clientWidth - margin;
18966
19080
  let newScrollLeft = null;
18967
19081
  if (contentX < minVisible) {
18968
- newScrollLeft = Math.max(0, contentX - margin);
19082
+ newScrollLeft = Math.max(0, Math.min(maxScrollLeft, contentX - margin));
18969
19083
  } else if (contentX > maxVisible) {
18970
- newScrollLeft = contentX - clientWidth + margin;
19084
+ newScrollLeft = Math.max(
19085
+ 0,
19086
+ Math.min(maxScrollLeft, contentX - clientWidth + margin)
19087
+ );
18971
19088
  }
18972
- if (newScrollLeft !== null) {
19089
+ if (newScrollLeft !== null && Math.abs(newScrollLeft - scrollLeft) > 0.5) {
18973
19090
  container.scrollLeft = newScrollLeft;
18974
19091
  }
18975
19092
  };
@@ -19356,15 +19473,6 @@ const useTimelineManager = () => {
19356
19473
  for (const el of elements) {
19357
19474
  const newStart = el.getStart() + clampedDelta;
19358
19475
  const newEnd = el.getEnd() + clampedDelta;
19359
- if (el instanceof timeline.VideoElement || el instanceof timeline.AudioElement) {
19360
- const elementProps = el.getProps();
19361
- const startDelta = newStart - el.getStart() * ((elementProps == null ? void 0 : elementProps.playbackRate) || 1);
19362
- if (el instanceof timeline.AudioElement) {
19363
- el.setStartAt(el.getStartAt() + startDelta);
19364
- } else {
19365
- el.setStartAt(el.getStartAt() + startDelta);
19366
- }
19367
- }
19368
19476
  el.setStart(newStart);
19369
19477
  el.setEnd(newEnd);
19370
19478
  editor.updateElement(el);
@@ -20381,9 +20489,11 @@ exports.TEXT_EFFECTS = TEXT_EFFECTS;
20381
20489
  exports.TIMELINE_DROP_MEDIA_TYPE = TIMELINE_DROP_MEDIA_TYPE;
20382
20490
  exports.TimelineManager = TimelineManager;
20383
20491
  exports.animationGifs = animationGifs;
20492
+ exports.debounce = debounce;
20384
20493
  exports.default = VideoEditor;
20385
20494
  exports.getAnimationGif = getAnimationGif;
20386
20495
  exports.setElementColors = setElementColors;
20496
+ exports.throttle = throttle;
20387
20497
  exports.useEditorManager = useEditorManager;
20388
20498
  exports.usePlayerControl = usePlayerControl;
20389
20499
  exports.useTimelineControl = useTimelineControl;