From d18dfaf26992fc392808561e02be24b91a38f488 Mon Sep 17 00:00:00 2001 From: Chirag Date: Wed, 20 May 2026 03:13:03 +0530 Subject: [PATCH 1/2] feat(editor): add always-use-pointer-cursor toggle Adds a toggle in the editor's Cursor settings panel that overrides every captured cursor type with the pointer (hand) cursor at playback and export time, eliminating cursor-shape flicker on text inputs and links. Closes #515 --- src/components/video-editor/SettingsPanel.tsx | 18 ++++++++++++++++++ src/components/video-editor/VideoEditor.tsx | 18 ++++++++++++++++++ src/components/video-editor/VideoPlayback.tsx | 9 +++++++++ .../video-editor/editorPreferences.ts | 5 +++++ .../video-editor/projectPersistence.ts | 5 +++++ .../videoPlayback/cursorRenderer.ts | 14 ++++++++++++-- src/i18n/locales/en/settings.json | 1 + src/i18n/locales/es/settings.json | 1 + src/i18n/locales/fr/settings.json | 1 + src/i18n/locales/it/settings.json | 1 + src/i18n/locales/ko/settings.json | 1 + src/i18n/locales/nl/settings.json | 1 + src/i18n/locales/pt-BR/settings.json | 1 + src/i18n/locales/ru/settings.json | 1 + src/i18n/locales/zh-CN/settings.json | 1 + src/i18n/locales/zh-TW/settings.json | 1 + src/lib/exporter/frameRenderer.ts | 2 ++ src/lib/exporter/gifExporter.ts | 2 ++ src/lib/exporter/modernFrameRenderer.ts | 2 ++ src/lib/exporter/modernVideoExporter.ts | 3 +++ .../exporter/nativeStaticLayoutTelemetry.ts | 8 ++++++-- src/lib/exporter/videoExporter.ts | 2 ++ 22 files changed, 94 insertions(+), 4 deletions(-) diff --git a/src/components/video-editor/SettingsPanel.tsx b/src/components/video-editor/SettingsPanel.tsx index cff898a53..5b84863ae 100644 --- a/src/components/video-editor/SettingsPanel.tsx +++ b/src/components/video-editor/SettingsPanel.tsx @@ -512,6 +512,8 @@ interface SettingsPanelProps { onShowCursorChange?: (enabled: boolean) => void; loopCursor?: boolean; onLoopCursorChange?: (enabled: boolean) => void; + alwaysUsePointerCursor?: boolean; + onAlwaysUsePointerCursorChange?: (enabled: boolean) => void; cursorStyle?: CursorStyle; onCursorStyleChange?: (style: CursorStyle) => void; cursorSize?: number; @@ -897,6 +899,8 @@ export function SettingsPanel({ onShowCursorChange, loopCursor = false, onLoopCursorChange, + alwaysUsePointerCursor = false, + onAlwaysUsePointerCursorChange, cursorStyle = DEFAULT_CURSOR_STYLE, onCursorStyleChange, cursorSize = 5, @@ -1523,6 +1527,7 @@ export function SettingsPanel({ const resetCursorSection = () => { onShowCursorChange?.(initialEditorPreferences.showCursor); onLoopCursorChange?.(initialEditorPreferences.loopCursor); + onAlwaysUsePointerCursorChange?.(initialEditorPreferences.alwaysUsePointerCursor); onCursorStyleChange?.(initialEditorPreferences.cursorStyle); onCursorSizeChange?.(initialEditorPreferences.cursorSize); onCursorSmoothingChange?.(initialEditorPreferences.cursorSmoothing); @@ -3308,6 +3313,19 @@ export function SettingsPanel({ +
( initialEditorPreferences.cursorStyle ?? DEFAULT_CURSOR_STYLE, ); @@ -848,6 +851,7 @@ export default function VideoEditor() { connectedZoomEasing, showCursor, loopCursor, + alwaysUsePointerCursor, cursorStyle, cursorSize, cursorSmoothing, @@ -899,6 +903,7 @@ export default function VideoEditor() { connectedZoomEasing, showCursor, loopCursor, + alwaysUsePointerCursor, cursorStyle, cursorSize, cursorSmoothing, @@ -991,6 +996,7 @@ export default function VideoEditor() { setConnectedZoomEasing(snapshot.connectedZoomEasing); setShowCursor(snapshot.showCursor); setLoopCursor(snapshot.loopCursor); + setAlwaysUsePointerCursor(snapshot.alwaysUsePointerCursor); setCursorStyle(snapshot.cursorStyle); setCursorSize(snapshot.cursorSize); setCursorSmoothing(snapshot.cursorSmoothing); @@ -1261,6 +1267,7 @@ export default function VideoEditor() { previewHeight, cursorTelemetry, showCursor: effectiveShowCursor, + alwaysUsePointerCursor, cursorStyle, cursorSize, cursorSmoothing, @@ -1733,6 +1740,7 @@ export default function VideoEditor() { connectedZoomEasing: ZoomTransitionEasing; showCursor: boolean; loopCursor: boolean; + alwaysUsePointerCursor: boolean; cursorStyle: CursorStyle; cursorSize: number; cursorSmoothing: number; @@ -1836,6 +1844,7 @@ export default function VideoEditor() { connectedZoomEasing, showCursor, loopCursor, + alwaysUsePointerCursor, cursorStyle, cursorSize, cursorSmoothing, @@ -1898,6 +1907,7 @@ export default function VideoEditor() { connectedZoomEasing, showCursor, loopCursor, + alwaysUsePointerCursor, cursorStyle, cursorSize, cursorSmoothing, @@ -2096,6 +2106,7 @@ export default function VideoEditor() { setConnectedZoomEasing(normalizedEditor.connectedZoomEasing); setShowCursor(normalizedEditor.showCursor); setLoopCursor(normalizedEditor.loopCursor); + setAlwaysUsePointerCursor(normalizedEditor.alwaysUsePointerCursor); setCursorStyle(normalizedEditor.cursorStyle); setCursorSize(normalizedEditor.cursorSize); setCursorSmoothing(normalizedEditor.cursorSmoothing); @@ -2572,6 +2583,7 @@ export default function VideoEditor() { connectedZoomEasing, showCursor, loopCursor, + alwaysUsePointerCursor, cursorStyle, cursorSize, cursorSmoothing, @@ -2623,6 +2635,7 @@ export default function VideoEditor() { connectedZoomEasing, showCursor, loopCursor, + alwaysUsePointerCursor, cursorStyle, cursorSize, cursorSmoothing, @@ -4274,6 +4287,7 @@ export default function VideoEditor() { zoomRegions: effectiveZoomRegions, cursorTelemetry: effectiveCursorTelemetry, showCursor: effectiveShowCursor, + alwaysUsePointerCursor, cursorStyle, cursorSize, cursorSmoothing, @@ -4446,6 +4460,7 @@ export default function VideoEditor() { zoomRegions: effectiveZoomRegions, cursorTelemetry: effectiveCursorTelemetry, showCursor: effectiveShowCursor, + alwaysUsePointerCursor, cursorStyle, cursorSize, cursorSmoothing, @@ -5882,6 +5897,8 @@ export default function VideoEditor() { onShowCursorChange={handleShowCursorChange} loopCursor={loopCursor} onLoopCursorChange={setLoopCursor} + alwaysUsePointerCursor={alwaysUsePointerCursor} + onAlwaysUsePointerCursorChange={setAlwaysUsePointerCursor} cursorStyle={cursorStyle} onCursorStyleChange={setCursorStyle} cursorSize={cursorSize} @@ -6106,6 +6123,7 @@ export default function VideoEditor() { onAnnotationSizeChange={handleAnnotationSizeChange} cursorTelemetry={effectiveCursorTelemetry} showCursor={effectiveShowCursor} + alwaysUsePointerCursor={alwaysUsePointerCursor} cursorStyle={cursorStyle} cursorSize={cursorSize} cursorSmoothing={cursorSmoothing} diff --git a/src/components/video-editor/VideoPlayback.tsx b/src/components/video-editor/VideoPlayback.tsx index c64e6b425..39bb2a569 100644 --- a/src/components/video-editor/VideoPlayback.tsx +++ b/src/components/video-editor/VideoPlayback.tsx @@ -352,6 +352,7 @@ interface VideoPlaybackProps { onAnnotationSizeChange?: (id: string, size: { width: number; height: number }) => void; cursorTelemetry?: CursorTelemetryPoint[]; showCursor?: boolean; + alwaysUsePointerCursor?: boolean; cursorStyle?: CursorStyle; cursorSize?: number; cursorSmoothing?: number; @@ -430,6 +431,7 @@ const VideoPlayback = forwardRef( onAnnotationSizeChange, cursorTelemetry = [], showCursor = false, + alwaysUsePointerCursor = false, cursorStyle = DEFAULT_CURSOR_STYLE, cursorSize = DEFAULT_CURSOR_SIZE, cursorSmoothing = DEFAULT_CURSOR_SMOOTHING, @@ -554,6 +556,7 @@ const VideoPlayback = forwardRef( const cursorEffectsCanvasRef = useRef(null); const cursorTelemetryRef = useRef([]); const showCursorRef = useRef(showCursor); + const alwaysUsePointerCursorRef = useRef(alwaysUsePointerCursor); const cursorSizeRef = useRef(cursorSize); const cursorStyleRef = useRef(cursorStyle); const cursorSmoothingRef = useRef(cursorSmoothing); @@ -1511,6 +1514,11 @@ const VideoPlayback = forwardRef( cursorStyleRef.current = cursorStyle; }, [cursorStyle]); + useEffect(() => { + alwaysUsePointerCursorRef.current = alwaysUsePointerCursor; + cursorOverlayRef.current?.setAlwaysUsePointerCursor(alwaysUsePointerCursor); + }, [alwaysUsePointerCursor]); + useEffect(() => { cursorSizeRef.current = cursorSize; }, [cursorSize]); @@ -1866,6 +1874,7 @@ const VideoPlayback = forwardRef( const cursorOverlay = new PixiCursorOverlay({ dotRadius: DEFAULT_CURSOR_CONFIG.dotRadius * cursorSizeRef.current, style: cursorStyleRef.current, + alwaysUsePointerCursor: alwaysUsePointerCursorRef.current, smoothingFactor: cursorSmoothingRef.current, springTuning: { stiffnessMultiplier: cursorSpringStiffnessMultiplierRef.current, diff --git a/src/components/video-editor/editorPreferences.ts b/src/components/video-editor/editorPreferences.ts index cbd417561..803a247b8 100644 --- a/src/components/video-editor/editorPreferences.ts +++ b/src/components/video-editor/editorPreferences.ts @@ -29,6 +29,7 @@ type PersistedEditorControls = Pick< | "connectedZoomEasing" | "showCursor" | "loopCursor" + | "alwaysUsePointerCursor" | "cursorStyle" | "cursorSize" | "cursorSmoothing" @@ -110,6 +111,7 @@ export const DEFAULT_EDITOR_PREFERENCES: EditorPreferences = { connectedZoomEasing: DEFAULT_EDITOR_CONTROLS.connectedZoomEasing, showCursor: DEFAULT_EDITOR_CONTROLS.showCursor, loopCursor: DEFAULT_EDITOR_CONTROLS.loopCursor, + alwaysUsePointerCursor: DEFAULT_EDITOR_CONTROLS.alwaysUsePointerCursor, cursorStyle: DEFAULT_EDITOR_CONTROLS.cursorStyle, cursorSize: DEFAULT_EDITOR_CONTROLS.cursorSize, cursorSmoothing: DEFAULT_EDITOR_CONTROLS.cursorSmoothing, @@ -289,6 +291,8 @@ function normalizeEditorControls( sanitizedRaw.connectedZoomEasing ?? fallback.connectedZoomEasing, showCursor: sanitizedRaw.showCursor ?? fallback.showCursor, loopCursor: sanitizedRaw.loopCursor ?? fallback.loopCursor, + alwaysUsePointerCursor: + sanitizedRaw.alwaysUsePointerCursor ?? fallback.alwaysUsePointerCursor, cursorStyle: sanitizedRaw.cursorStyle ?? fallback.cursorStyle, cursorSize: sanitizedRaw.cursorSize ?? fallback.cursorSize, cursorSmoothing: sanitizedRaw.cursorSmoothing ?? fallback.cursorSmoothing, @@ -361,6 +365,7 @@ function normalizeEditorControls( connectedZoomEasing: normalized.connectedZoomEasing, showCursor: normalized.showCursor, loopCursor: normalized.loopCursor, + alwaysUsePointerCursor: normalized.alwaysUsePointerCursor, cursorStyle: normalized.cursorStyle, cursorSize: normalized.cursorSize, cursorSmoothing: normalized.cursorSmoothing, diff --git a/src/components/video-editor/projectPersistence.ts b/src/components/video-editor/projectPersistence.ts index bb3d00dfe..106e72f05 100644 --- a/src/components/video-editor/projectPersistence.ts +++ b/src/components/video-editor/projectPersistence.ts @@ -96,6 +96,7 @@ export interface ProjectEditorState { connectedZoomEasing: ZoomTransitionEasing; showCursor: boolean; loopCursor: boolean; + alwaysUsePointerCursor: boolean; cursorStyle: CursorStyle; cursorSize: number; cursorSmoothing: number; @@ -880,6 +881,10 @@ export function normalizeProjectEditor(editor: Partial): Pro ), showCursor: typeof editor.showCursor === "boolean" ? editor.showCursor : true, loopCursor: typeof editor.loopCursor === "boolean" ? editor.loopCursor : false, + alwaysUsePointerCursor: + typeof editor.alwaysUsePointerCursor === "boolean" + ? editor.alwaysUsePointerCursor + : false, cursorStyle: normalizedCursorStyle, cursorSize: normalizedMotionPreset.cursorSize, cursorSmoothing: normalizedMotionPreset.cursorSmoothing, diff --git a/src/components/video-editor/videoPlayback/cursorRenderer.ts b/src/components/video-editor/videoPlayback/cursorRenderer.ts index 644cf8992..71175762d 100644 --- a/src/components/video-editor/videoPlayback/cursorRenderer.ts +++ b/src/components/video-editor/videoPlayback/cursorRenderer.ts @@ -89,6 +89,7 @@ export interface CursorRenderConfig { sway: number; /** Cursor visual style. */ style: CursorStyle; + alwaysUsePointerCursor: boolean; } export const DEFAULT_CURSOR_CONFIG: CursorRenderConfig = { @@ -107,6 +108,7 @@ export const DEFAULT_CURSOR_CONFIG: CursorRenderConfig = { clickBounceDuration: DEFAULT_CURSOR_CLICK_BOUNCE_DURATION, sway: 0, style: DEFAULT_CURSOR_STYLE, + alwaysUsePointerCursor: false, }; const REFERENCE_WIDTH = 1920; @@ -1011,6 +1013,10 @@ export class PixiCursorOverlay { this.config.sway = clamp(sway, 0, 2); } + setAlwaysUsePointerCursor(enabled: boolean) { + this.config.alwaysUsePointerCursor = enabled; + } + setStyle(style: CursorStyle) { this.config.style = style; if (isStatefulCursorStyle(style)) { @@ -1108,11 +1114,14 @@ export class PixiCursorOverlay { const px = viewport.x + this.state.x * viewport.width; const py = viewport.y + this.state.y * viewport.height; const h = this.config.dotRadius * getCursorViewportScale(viewport); - const { cursorType, clickBounceProgress } = getCursorVisualState( + const { cursorType: rawCursorType, clickBounceProgress } = getCursorVisualState( samples, timeMs, this.config.clickBounceDuration, ); + const cursorType: CursorAssetKey = this.config.alwaysUsePointerCursor + ? "pointer" + : rawCursorType; const bounceScale = Math.max( 0.72, 1 - Math.sin(clickBounceProgress * Math.PI) * (0.08 * this.config.clickBounce), @@ -1317,11 +1326,12 @@ export function drawCursorOnCanvas( const px = viewport.x + smoothedState.x * viewport.width; const py = viewport.y + smoothedState.y * viewport.height; const h = config.dotRadius * getCursorViewportScale(viewport); - const { cursorType, clickBounceProgress } = getCursorVisualState( + const { cursorType: rawCursorType, clickBounceProgress } = getCursorVisualState( samples, timeMs, config.clickBounceDuration, ); + const cursorType: CursorAssetKey = config.alwaysUsePointerCursor ? "pointer" : rawCursorType; const spriteKey = ( cursorType && loadedCursorAssets[cursorType] ? cursorType : "arrow" ) as CursorAssetKey; diff --git a/src/i18n/locales/en/settings.json b/src/i18n/locales/en/settings.json index b9a2d00ad..ff5b4c30e 100644 --- a/src/i18n/locales/en/settings.json +++ b/src/i18n/locales/en/settings.json @@ -30,6 +30,7 @@ "show": "Show", "showCursor": "Show Cursor", "loopCursor": "Loop cursor", + "alwaysUsePointerCursor": "Always use pointer cursor", "cursorStyle": "Cursor Style", "cursorStyleOptions": { "macos": "macOS", diff --git a/src/i18n/locales/es/settings.json b/src/i18n/locales/es/settings.json index 66a05e89c..ebfa96778 100644 --- a/src/i18n/locales/es/settings.json +++ b/src/i18n/locales/es/settings.json @@ -30,6 +30,7 @@ "show": "Mostrar", "showCursor": "Mostrar cursor", "loopCursor": "Cursor en bucle", + "alwaysUsePointerCursor": "Usar siempre el cursor puntero", "cursorStyle": "Estilo del cursor", "cursorStyleOptions": { "macos": "macOS", diff --git a/src/i18n/locales/fr/settings.json b/src/i18n/locales/fr/settings.json index 200ecaa02..28ece8600 100644 --- a/src/i18n/locales/fr/settings.json +++ b/src/i18n/locales/fr/settings.json @@ -30,6 +30,7 @@ "show": "Afficher", "showCursor": "Afficher le curseur", "loopCursor": "Boucler le curseur", + "alwaysUsePointerCursor": "Toujours utiliser le curseur pointeur", "cursorStyle": "Style du curseur", "cursorStyleOptions": { "macos": "macOS", diff --git a/src/i18n/locales/it/settings.json b/src/i18n/locales/it/settings.json index d9b9a1a18..a546b4587 100644 --- a/src/i18n/locales/it/settings.json +++ b/src/i18n/locales/it/settings.json @@ -30,6 +30,7 @@ "show": "Mostra", "showCursor": "Mostra cursore", "loopCursor": "Cursore in loop", + "alwaysUsePointerCursor": "Usa sempre il cursore puntatore", "cursorStyle": "Stile cursore", "cursorStyleOptions": { "macos": "macOS", diff --git a/src/i18n/locales/ko/settings.json b/src/i18n/locales/ko/settings.json index c528a98fb..30aaa172f 100644 --- a/src/i18n/locales/ko/settings.json +++ b/src/i18n/locales/ko/settings.json @@ -30,6 +30,7 @@ "show": "표시", "showCursor": "커서 표시", "loopCursor": "커서 반복", + "alwaysUsePointerCursor": "항상 포인터 커서 사용", "cursorStyle": "커서 스타일", "cursorStyleOptions": { "macos": "macOS", diff --git a/src/i18n/locales/nl/settings.json b/src/i18n/locales/nl/settings.json index d88e4278c..5112e4992 100644 --- a/src/i18n/locales/nl/settings.json +++ b/src/i18n/locales/nl/settings.json @@ -30,6 +30,7 @@ "show": "Tonen", "showCursor": "Cursor tonen", "loopCursor": "Cursor herhalen", + "alwaysUsePointerCursor": "Altijd aanwijzer-cursor gebruiken", "cursorStyle": "Cursorstijl", "cursorStyleOptions": { "macos": "macOS", diff --git a/src/i18n/locales/pt-BR/settings.json b/src/i18n/locales/pt-BR/settings.json index a34e6e4d2..8180f7960 100644 --- a/src/i18n/locales/pt-BR/settings.json +++ b/src/i18n/locales/pt-BR/settings.json @@ -30,6 +30,7 @@ "show": "Mostrar", "showCursor": "Mostrar cursor", "loopCursor": "Loop do cursor", + "alwaysUsePointerCursor": "Usar sempre cursor de ponteiro", "cursorStyle": "Estilo do cursor", "cursorStyleOptions": { "macos": "macOS", diff --git a/src/i18n/locales/ru/settings.json b/src/i18n/locales/ru/settings.json index 687f8383b..2c6ee61b0 100644 --- a/src/i18n/locales/ru/settings.json +++ b/src/i18n/locales/ru/settings.json @@ -30,6 +30,7 @@ "show": "Показать", "showCursor": "Показать курсор", "loopCursor": "Зациклить курсор", + "alwaysUsePointerCursor": "Всегда использовать курсор-указатель", "cursorStyle": "Стиль курсора", "cursorStyleOptions": { "macos": "macOS", diff --git a/src/i18n/locales/zh-CN/settings.json b/src/i18n/locales/zh-CN/settings.json index a68d2a04f..c06d9001f 100644 --- a/src/i18n/locales/zh-CN/settings.json +++ b/src/i18n/locales/zh-CN/settings.json @@ -30,6 +30,7 @@ "show": "显示", "showCursor": "显示光标", "loopCursor": "循环光标", + "alwaysUsePointerCursor": "始终使用指针光标", "cursorStyle": "光标样式", "cursorStyleOptions": { "macos": "macOS", diff --git a/src/i18n/locales/zh-TW/settings.json b/src/i18n/locales/zh-TW/settings.json index db0ea7e04..36d925219 100644 --- a/src/i18n/locales/zh-TW/settings.json +++ b/src/i18n/locales/zh-TW/settings.json @@ -30,6 +30,7 @@ "show": "顯示", "showCursor": "顯示游標", "loopCursor": "游標循環", + "alwaysUsePointerCursor": "始終使用指針游標", "cursorStyle": "游標樣式", "cursorStyleOptions": { "macos": "macOS", diff --git a/src/lib/exporter/frameRenderer.ts b/src/lib/exporter/frameRenderer.ts index 4ebc12059..cd0c629d1 100644 --- a/src/lib/exporter/frameRenderer.ts +++ b/src/lib/exporter/frameRenderer.ts @@ -118,6 +118,7 @@ interface FrameRenderConfig { previewHeight?: number; cursorTelemetry?: CursorTelemetryPoint[]; showCursor?: boolean; + alwaysUsePointerCursor?: boolean; cursorStyle?: CursorStyle; cursorSize?: number; cursorSmoothing?: number; @@ -434,6 +435,7 @@ export class FrameRenderer { this.cursorOverlay = new PixiCursorOverlay({ dotRadius: DEFAULT_CURSOR_CONFIG.dotRadius * (this.config.cursorSize ?? 1.4), style: this.config.cursorStyle ?? "tahoe", + alwaysUsePointerCursor: this.config.alwaysUsePointerCursor ?? false, smoothingFactor: this.config.cursorSmoothing ?? DEFAULT_CURSOR_CONFIG.smoothingFactor, springTuning: { diff --git a/src/lib/exporter/gifExporter.ts b/src/lib/exporter/gifExporter.ts index 4c8338529..a58b13384 100644 --- a/src/lib/exporter/gifExporter.ts +++ b/src/lib/exporter/gifExporter.ts @@ -67,6 +67,7 @@ interface GifExporterConfig { autoCaptionSettings?: AutoCaptionSettings; cursorTelemetry?: CursorTelemetryPoint[]; showCursor?: boolean; + alwaysUsePointerCursor?: boolean; cursorStyle?: CursorStyle; cursorSize?: number; cursorSmoothing?: number; @@ -197,6 +198,7 @@ export class GifExporter { previewHeight: this.config.previewHeight, cursorTelemetry: this.config.cursorTelemetry, showCursor: this.config.showCursor, + alwaysUsePointerCursor: this.config.alwaysUsePointerCursor, cursorStyle: this.config.cursorStyle, cursorSize: this.config.cursorSize, cursorSmoothing: this.config.cursorSmoothing, diff --git a/src/lib/exporter/modernFrameRenderer.ts b/src/lib/exporter/modernFrameRenderer.ts index 5372937fd..e4ebd98d7 100644 --- a/src/lib/exporter/modernFrameRenderer.ts +++ b/src/lib/exporter/modernFrameRenderer.ts @@ -136,6 +136,7 @@ interface FrameRenderConfig { previewHeight?: number; cursorTelemetry?: CursorTelemetryPoint[]; showCursor?: boolean; + alwaysUsePointerCursor?: boolean; cursorStyle?: CursorStyle; cursorSize?: number; cursorSmoothing?: number; @@ -608,6 +609,7 @@ export class FrameRenderer { this.cursorOverlay = new PixiCursorOverlay({ dotRadius: DEFAULT_CURSOR_CONFIG.dotRadius * (this.config.cursorSize ?? 1.4), style: this.config.cursorStyle ?? "tahoe", + alwaysUsePointerCursor: this.config.alwaysUsePointerCursor ?? false, smoothingFactor: this.config.cursorSmoothing ?? DEFAULT_CURSOR_CONFIG.smoothingFactor, springTuning: { diff --git a/src/lib/exporter/modernVideoExporter.ts b/src/lib/exporter/modernVideoExporter.ts index 679c9a9da..1210ecc77 100644 --- a/src/lib/exporter/modernVideoExporter.ts +++ b/src/lib/exporter/modernVideoExporter.ts @@ -120,6 +120,7 @@ interface VideoExporterConfig extends ExportConfig { autoCaptionSettings?: AutoCaptionSettings; cursorTelemetry?: CursorTelemetryPoint[]; showCursor?: boolean; + alwaysUsePointerCursor?: boolean; cursorStyle?: CursorStyle; cursorSize?: number; cursorSmoothing?: number; @@ -603,6 +604,7 @@ export class ModernVideoExporter { previewHeight: this.config.previewHeight, cursorTelemetry: this.config.cursorTelemetry, showCursor: this.config.showCursor, + alwaysUsePointerCursor: this.config.alwaysUsePointerCursor, cursorStyle: this.config.cursorStyle, cursorSize: this.config.cursorSize, cursorSmoothing: this.config.cursorSmoothing, @@ -2021,6 +2023,7 @@ export class ModernVideoExporter { clickBounce: this.config.cursorClickBounce, clickBounceDurationMs: this.config.cursorClickBounceDuration, sourceCrop: this.config.cropRegion, + alwaysUsePointerCursor: this.config.alwaysUsePointerCursor, }); } diff --git a/src/lib/exporter/nativeStaticLayoutTelemetry.ts b/src/lib/exporter/nativeStaticLayoutTelemetry.ts index 7a8cda118..402e4d53e 100644 --- a/src/lib/exporter/nativeStaticLayoutTelemetry.ts +++ b/src/lib/exporter/nativeStaticLayoutTelemetry.ts @@ -18,6 +18,7 @@ export type NativeStaticLayoutCursorTelemetryOptions = { clickBounce?: number; clickBounceDurationMs?: number; sourceCrop?: CropRegion; + alwaysUsePointerCursor?: boolean; }; const CURSOR_POSITION_EPSILON = 0.00001; @@ -144,12 +145,15 @@ function createMonotonicCursorRenderSampler( } const projectedPosition = projectCursorPositionToViewport(position, options.sourceCrop); + const effectiveCursorType: CursorTelemetryPoint["cursorType"] = options.alwaysUsePointerCursor + ? "pointer" + : latestStableCursorType; return { ...position, cx: projectedPosition.cx, cy: projectedPosition.cy, - cursorType: latestStableCursorType, - cursorTypeIndex: getCursorTypeIndex(latestStableCursorType), + cursorType: effectiveCursorType, + cursorTypeIndex: getCursorTypeIndex(effectiveCursorType), bounceScale: getCursorBounceScale(latestClick, timeMs, options), ...(options.sourceCrop ? { visible: projectedPosition.visible } : {}), }; diff --git a/src/lib/exporter/videoExporter.ts b/src/lib/exporter/videoExporter.ts index b99f5f96c..19b66b840 100644 --- a/src/lib/exporter/videoExporter.ts +++ b/src/lib/exporter/videoExporter.ts @@ -76,6 +76,7 @@ interface VideoExporterConfig extends ExportConfig { autoCaptionSettings?: AutoCaptionSettings; cursorTelemetry?: CursorTelemetryPoint[]; showCursor?: boolean; + alwaysUsePointerCursor?: boolean; cursorStyle?: CursorStyle; cursorSize?: number; cursorSmoothing?: number; @@ -251,6 +252,7 @@ export class VideoExporter { previewHeight: this.config.previewHeight, cursorTelemetry: this.config.cursorTelemetry, showCursor: this.config.showCursor, + alwaysUsePointerCursor: this.config.alwaysUsePointerCursor, cursorStyle: this.config.cursorStyle, cursorSize: this.config.cursorSize, cursorSmoothing: this.config.cursorSmoothing, From 232ea79828c68db61d409067bc4327e3d18e112b Mon Sep 17 00:00:00 2001 From: Chirag Date: Wed, 20 May 2026 03:21:25 +0530 Subject: [PATCH 2/2] fix(editor): include alwaysUsePointerCursor in callback deps Adds the flag to captureProjectThumbnail and handleExport useCallback dependency arrays so thumbnail capture and export pick up the latest value after the user toggles the setting. --- src/components/video-editor/VideoEditor.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/video-editor/VideoEditor.tsx b/src/components/video-editor/VideoEditor.tsx index ebc9cbbb4..07394c7a2 100644 --- a/src/components/video-editor/VideoEditor.tsx +++ b/src/components/video-editor/VideoEditor.tsx @@ -1367,6 +1367,7 @@ export default function VideoEditor() { resolvedWebcamVideoUrl, shadowIntensity, effectiveShowCursor, + alwaysUsePointerCursor, speedRegions, wallpaper, webcam, @@ -4712,6 +4713,7 @@ export default function VideoEditor() { zoomOutEasing, connectedZoomEasing, effectiveShowCursor, + alwaysUsePointerCursor, cursorStyle, effectiveCursorTelemetry, cursorSize,