diff --git a/src/components/canvas/players/player.ts b/src/components/canvas/players/player.ts index 81efb1a..644a42b 100644 --- a/src/components/canvas/players/player.ts +++ b/src/components/canvas/players/player.ts @@ -59,7 +59,6 @@ export enum PlayerType { */ export abstract class Player extends Entity { private static readonly DiscardedFrameCount = 0; - private static readonly DoubleClickThresholdMs = 350; public layer: number; public shouldDispose: boolean; @@ -493,13 +492,8 @@ export abstract class Player extends Entity { return this.getPlaybackTime() < Player.DiscardedFrameCount; } - /** Timestamp of last single-click on this player, used for double-click detection. */ - private lastClickAt = 0; - /** - * Handle pointer down - emit click event for selection handling. - * Two clicks within DoubleClickThresholdMs on the same player also emit - * CanvasClipDoubleClicked so text-clip editing can be triggered from the canvas. + * Handle pointer down — emit click event for selection handling. * All drag/resize/rotate interaction is handled by SelectionHandles. */ private onPointerDown(event: pixi.FederatedPointerEvent): void { @@ -508,14 +502,6 @@ export abstract class Player extends Entity { } this.edit.getInternalEvents().emit(InternalEvent.CanvasClipClicked, { player: this }); - - const now = performance.now(); - if (now - this.lastClickAt <= Player.DoubleClickThresholdMs) { - this.edit.getInternalEvents().emit(InternalEvent.CanvasClipDoubleClicked, { player: this }); - this.lastClickAt = 0; - } else { - this.lastClickAt = now; - } } private clipHasKeyframes(): boolean { diff --git a/src/core/edit-session.ts b/src/core/edit-session.ts index f9106af..3e37ba6 100644 --- a/src/core/edit-session.ts +++ b/src/core/edit-session.ts @@ -2236,15 +2236,34 @@ export class Edit { return bestMatch; } + private lastClipClick: { player: Player; at: number } | null = null; + private static readonly DoubleClickThresholdMs = 500; + // ─── Intent Listeners ──────────────────────────────────────────────────────── private setupIntentListeners(): void { this.internalEvents.on(InternalEvent.CanvasClipClicked, data => { - this.selectPlayer(data.player); + const wasSelected = this.getSelectedClipInfo()?.player === data.player; + + if (!wasSelected) { + this.selectPlayer(data.player); + this.lastClipClick = null; + return; + } + + const now = performance.now(); + const within = this.lastClipClick && now - this.lastClipClick.at <= Edit.DoubleClickThresholdMs; + if (this.lastClipClick?.player === data.player && within) { + this.internalEvents.emit(InternalEvent.CanvasClipDoubleClicked, { player: data.player }); + this.lastClipClick = null; + } else { + this.lastClipClick = { player: data.player, at: now }; + } }); this.internalEvents.on(InternalEvent.CanvasBackgroundClicked, () => { this.clearSelection(); + this.lastClipClick = null; }); } diff --git a/src/core/ui/base-toolbar.ts b/src/core/ui/base-toolbar.ts index 3151538..b60298d 100644 --- a/src/core/ui/base-toolbar.ts +++ b/src/core/ui/base-toolbar.ts @@ -43,8 +43,7 @@ export const TOOLBAR_ICONS = { chevron: ``, transition: ``, effect: ``, - trash: ``, - textCursor: `` + trash: `` }; /** @@ -256,9 +255,11 @@ export abstract class BaseToolbar { */ protected setupOutsideClickHandler(): void { this.clickOutsideHandler = (e: MouseEvent) => { - if (!this.container?.contains(e.target as Node)) { - this.closeAllPopups(); - } + const target = e.target as HTMLElement | null; + if (!target) return; + if (this.container?.contains(target)) return; + if (target.tagName === "CANVAS") return; + this.closeAllPopups(); }; document.addEventListener("click", this.clickOutsideHandler); } diff --git a/src/core/ui/rich-text-toolbar.ts b/src/core/ui/rich-text-toolbar.ts index a997a45..0b6ba5c 100644 --- a/src/core/ui/rich-text-toolbar.ts +++ b/src/core/ui/rich-text-toolbar.ts @@ -8,7 +8,7 @@ import { injectShotstackStyles } from "@styles/inject"; import { GOOGLE_FONTS_BY_FILENAME } from "../fonts/google-fonts"; import { BackgroundColorPicker } from "./background-color-picker"; -import { BaseToolbar, FONT_SIZES, TOOLBAR_ICONS } from "./base-toolbar"; +import { BaseToolbar, FONT_SIZES } from "./base-toolbar"; import { EffectPanel } from "./composites/EffectPanel"; import { SpacingPanel } from "./composites/SpacingPanel"; import { StylePanel, type StylePanelOptions } from "./composites/StylePanel"; @@ -127,24 +127,6 @@ export class RichTextToolbar extends BaseToolbar {
-
- -
-
Edit Text
-
- -
-
-
-
-
-
-
+
+
Edit Text
+
+ +
+
+
+
+
+
+ +
+ -
-
Edit Text
-
- -
-
- -
+
+
Edit Text
+
+ +
+
+
+ +
+