From 004726b7589560c5c909542da4432bbd992dd921 Mon Sep 17 00:00:00 2001 From: yoma Date: Thu, 16 Jul 2026 18:21:01 +0800 Subject: [PATCH] fix(studio): preserve zero caption scale --- .../components/CaptionOverlayUtils.test.ts | 39 +++++++++++++++++++ .../components/CaptionOverlayUtils.ts | 21 +++++++--- .../components/CaptionPropertyPanel.tsx | 9 +++-- 3 files changed, 60 insertions(+), 9 deletions(-) create mode 100644 packages/studio/src/captions/components/CaptionOverlayUtils.test.ts diff --git a/packages/studio/src/captions/components/CaptionOverlayUtils.test.ts b/packages/studio/src/captions/components/CaptionOverlayUtils.test.ts new file mode 100644 index 0000000000..735e980fee --- /dev/null +++ b/packages/studio/src/captions/components/CaptionOverlayUtils.test.ts @@ -0,0 +1,39 @@ +// @vitest-environment happy-dom + +import { describe, expect, it } from "vitest"; +import { readGsapTransform } from "./CaptionOverlayUtils"; + +describe("readGsapTransform", () => { + it("preserves a zero GSAP scale", () => { + const element = document.createElement("span"); + const values: Record = { x: 0, y: 0, scale: 0, rotation: 0 }; + const iframeWindow = { + gsap: { + getProperty: (_element: HTMLElement, property: string) => values[property], + }, + } as unknown as Window; + + expect(readGsapTransform(element, iframeWindow)).toEqual({ + x: 0, + y: 0, + scale: 0, + rotation: 0, + }); + }); + + it("falls back for non-finite GSAP values", () => { + const element = document.createElement("span"); + const iframeWindow = { + gsap: { + getProperty: () => Number.NaN, + }, + } as unknown as Window; + + expect(readGsapTransform(element, iframeWindow)).toEqual({ + x: 0, + y: 0, + scale: 1, + rotation: 0, + }); + }); +}); diff --git a/packages/studio/src/captions/components/CaptionOverlayUtils.ts b/packages/studio/src/captions/components/CaptionOverlayUtils.ts index 4739586179..0a5bc9cb17 100644 --- a/packages/studio/src/captions/components/CaptionOverlayUtils.ts +++ b/packages/studio/src/captions/components/CaptionOverlayUtils.ts @@ -11,6 +11,16 @@ export interface WordBox { height: number; } +export function readGsapNumber( + getProperty: (el: HTMLElement, prop: string) => unknown, + el: HTMLElement, + property: string, + fallback: number, +): number { + const value = Number(getProperty(el, property)); + return Number.isFinite(value) ? value : fallback; +} + export function readWordBoxes( iframe: HTMLIFrameElement, model: { @@ -144,12 +154,13 @@ export function readGsapTransform( const gsap = ( iframeWin as unknown as { gsap?: { getProperty?: (el: HTMLElement, prop: string) => number } } ).gsap; - if (gsap && gsap.getProperty) { + const getProperty = gsap?.getProperty; + if (getProperty) { return { - x: gsap.getProperty(el, "x") || 0, - y: gsap.getProperty(el, "y") || 0, - scale: gsap.getProperty(el, "scale") || 1, - rotation: gsap.getProperty(el, "rotation") || 0, + x: readGsapNumber(getProperty, el, "x", 0), + y: readGsapNumber(getProperty, el, "y", 0), + scale: readGsapNumber(getProperty, el, "scale", 1), + rotation: readGsapNumber(getProperty, el, "rotation", 0), }; } const t = el.style.transform || ""; diff --git a/packages/studio/src/captions/components/CaptionPropertyPanel.tsx b/packages/studio/src/captions/components/CaptionPropertyPanel.tsx index 3ac838c04b..37cdcf7e47 100644 --- a/packages/studio/src/captions/components/CaptionPropertyPanel.tsx +++ b/packages/studio/src/captions/components/CaptionPropertyPanel.tsx @@ -2,6 +2,7 @@ import { memo, useCallback, useState } from "react"; import { useCaptionStore } from "../store"; import type { CaptionStyle } from "../types"; import { CaptionAnimationPanel } from "./CaptionAnimationPanel"; +import { readGsapNumber } from "./CaptionOverlayUtils"; import { Section, Row, inputCls } from "./shared"; // --------------------------------------------------------------------------- @@ -129,10 +130,10 @@ export const CaptionPropertyPanel = memo(function CaptionPropertyPanel({ wrapper.appendChild(el); } // Read current wrapper state and merge with updates - const curX = iframeGsap.getProperty(wrapper, "x") || 0; - const curY = iframeGsap.getProperty(wrapper, "y") || 0; - const curScale = iframeGsap.getProperty(wrapper, "scale") || 1; - const curRotation = iframeGsap.getProperty(wrapper, "rotation") || 0; + const curX = readGsapNumber(iframeGsap.getProperty, wrapper, "x", 0); + const curY = readGsapNumber(iframeGsap.getProperty, wrapper, "y", 0); + const curScale = readGsapNumber(iframeGsap.getProperty, wrapper, "scale", 1); + const curRotation = readGsapNumber(iframeGsap.getProperty, wrapper, "rotation", 0); iframeGsap.set(wrapper, { x: updates.x ?? curX, y: updates.y ?? curY,