diff --git a/packages/image-studio/src/assets/video/styles/highlights-soon.webp b/packages/image-studio/src/assets/video/styles/highlights-soon.webp deleted file mode 100644 index 920fa0ed114b..000000000000 Binary files a/packages/image-studio/src/assets/video/styles/highlights-soon.webp and /dev/null differ diff --git a/packages/image-studio/src/components/style-picker/index.test.tsx b/packages/image-studio/src/components/style-picker/index.test.tsx index e3622be64fe0..0bcaccdc41a4 100644 --- a/packages/image-studio/src/components/style-picker/index.test.tsx +++ b/packages/image-studio/src/components/style-picker/index.test.tsx @@ -4,6 +4,7 @@ import { render, screen, waitFor, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; // Mock dependencies - MUST be before imports that use them jest.mock( '@automattic/agenttic-ui', () => { + // eslint-disable-next-line @typescript-eslint/no-require-imports const React = require( 'react' ); const { useState } = React; @@ -435,22 +436,26 @@ describe( 'StylePicker', () => { } ); } ); - it( 'exports Cinematic (active) + Highlights (disabled teaser) video styles', () => { + it( 'exports Highlights + Cinematic video styles, both active', () => { expect( VIDEO_STYLE_OPTIONS ).toHaveLength( 2 ); + // Highlights leads the list — it is the default video style and reads + // first in the picker. expect( VIDEO_STYLE_OPTIONS[ 0 ] ).toMatchObject( { - label: 'Cinematic', - value: 'cinematic', + label: 'Highlights', + value: 'highlights', } ); expect( VIDEO_STYLE_OPTIONS[ 0 ].disabled ).toBeFalsy(); expect( VIDEO_STYLE_OPTIONS[ 0 ].preview ).toBeTruthy(); + expect( VIDEO_STYLE_OPTIONS[ 0 ].badge ).toBe( 'New' ); expect( VIDEO_STYLE_OPTIONS[ 1 ] ).toMatchObject( { - label: 'Highlights (Coming Soon)', - value: 'highlights', - disabled: true, + label: 'Cinematic', + value: 'cinematic', } ); + expect( VIDEO_STYLE_OPTIONS[ 1 ].disabled ).toBeFalsy(); expect( VIDEO_STYLE_OPTIONS[ 1 ].preview ).toBeTruthy(); + expect( VIDEO_STYLE_OPTIONS[ 1 ].badge ).toBeFalsy(); } ); it( 'renders video options when variant="video"', async () => { @@ -467,16 +472,6 @@ describe( 'StylePicker', () => { expect( dropdown ).not.toHaveTextContent( 'Pixel Art' ); } ); - it( 'renders the disabled video style as an inert (native-disabled) card', async () => { - const user = userEvent.setup(); - render( ); - - await user.click( screen.getByTestId( 'toolbar-button' ) ); - const dropdown = screen.getByTestId( 'dropdown-content' ); - const highlightsCard = within( dropdown ).getByRole( 'button', { name: /Highlights/ } ); - expect( highlightsCard ).toBeDisabled(); - } ); - it( 'maps style values correctly', async () => { const user = userEvent.setup(); @@ -514,8 +509,8 @@ describe( 'StylePicker', () => { } ); } ); - describe( 'Dev-mode Highlights gating', () => { - it( 'keeps Highlights disabled with the Coming Soon teaser when isDevMode is unset', async () => { + describe( 'Highlights video style', () => { + it( 'shows Highlights as an enabled option with a preview for all users', async () => { const user = userEvent.setup(); render( ); @@ -523,23 +518,29 @@ describe( 'StylePicker', () => { const dropdown = screen.getByTestId( 'dropdown-content' ); const highlightsCard = within( dropdown ).getByRole( 'button', { name: /Highlights/ } ); - expect( highlightsCard ).toBeDisabled(); - expect( highlightsCard ).toHaveTextContent( 'Highlights (Coming Soon)' ); + expect( highlightsCard ).not.toBeDisabled(); + expect( highlightsCard ).toHaveTextContent( 'Highlights' ); + expect( highlightsCard ).not.toHaveTextContent( 'Coming Soon' ); + expect( highlightsCard.querySelector( 'img' ) ).toBeInTheDocument(); } ); - it( 'unlocks Highlights with the a12s label, enabled card, and a preview when isDevMode is true', async () => { + it( 'renders a "New" badge on the Highlights card only', async () => { const user = userEvent.setup(); - ( window as Record< string, unknown > ).imageStudioData = { isDevMode: true }; - render( ); await user.click( screen.getByTestId( 'toolbar-button' ) ); const dropdown = screen.getByTestId( 'dropdown-content' ); - const highlightsCard = within( dropdown ).getByRole( 'button', { name: /Highlights/ } ); - expect( highlightsCard ).not.toBeDisabled(); - expect( highlightsCard ).toHaveTextContent( 'Highlights (a12s only)' ); - expect( highlightsCard.querySelector( 'img' ) ).toBeInTheDocument(); + const highlightsCard = within( dropdown ).getByRole( 'button', { name: /Highlights/ } ); + const badge = highlightsCard.querySelector( '.image-studio-input-toolbar-card__badge' ); + expect( badge ).toBeInTheDocument(); + expect( badge ).toHaveTextContent( 'New' ); + + // The non-badged Cinematic card must not render a badge. + const cinematicCard = within( dropdown ).getByRole( 'button', { name: /Cinematic/ } ); + expect( + cinematicCard.querySelector( '.image-studio-input-toolbar-card__badge' ) + ).not.toBeInTheDocument(); } ); } ); diff --git a/packages/image-studio/src/components/style-picker/index.tsx b/packages/image-studio/src/components/style-picker/index.tsx index dc24d9026931..b408c31685cb 100644 --- a/packages/image-studio/src/components/style-picker/index.tsx +++ b/packages/image-studio/src/components/style-picker/index.tsx @@ -19,7 +19,6 @@ import photographicPreview from '../../assets/photographic.webp'; import pixelArtPreview from '../../assets/pixel-art.webp'; import texturePreview from '../../assets/texture.webp'; import videoCinematicPreview from '../../assets/video/styles/cinematic.webp'; -import videoHighlightsSoonPreview from '../../assets/video/styles/highlights-soon.webp'; import videoHighlightsPreview from '../../assets/video/styles/highlights.webp'; import vividPreview from '../../assets/vivid.webp'; import { store as imageStudioStore } from '../../store'; @@ -46,6 +45,8 @@ interface StyleOption { // Renders the card with the native disabled attribute + a greyed-out // .is-disabled style — used to tease an upcoming style. disabled?: boolean; + // Small pill rendered on the card corner (e.g. "New") to flag a fresh style. + badge?: string; } export const STYLE_OPTIONS: StyleOption[] = [ @@ -140,27 +141,22 @@ export const STYLE_OPTIONS: StyleOption[] = [ // (LLM-composed HTML → EditFrame /api/v1/renders → MP4 → media library); // the in-browser encoding implementation lives on the older compositor // branches and is preserved there. -// -// Production default ships Cinematic-only; Highlights is disabled with a -// "coming soon" preview. The StylePicker component swaps in the live -// preview and enables the card when window.imageStudioData.isDevMode is -// true (a12s testing). export const VIDEO_STYLE_OPTIONS: StyleOption[] = [ { - label: __( 'Cinematic', __i18n_text_domain__ ), - value: 'cinematic', - preview: videoCinematicPreview, - description: __( 'Create an 8-second b-roll mood clip from a prompt.', __i18n_text_domain__ ), - }, - { - label: __( 'Highlights (Coming Soon)', __i18n_text_domain__ ), + label: __( 'Highlights', __i18n_text_domain__ ), value: 'highlights', - preview: videoHighlightsSoonPreview, + preview: videoHighlightsPreview, description: __( "Build a 20-second recap clip using your post's images and key points.", __i18n_text_domain__ ), - disabled: true, + badge: __( 'New', __i18n_text_domain__ ), + }, + { + label: __( 'Cinematic', __i18n_text_domain__ ), + value: 'cinematic', + preview: videoCinematicPreview, + description: __( 'Create an 8-second b-roll mood clip from a prompt.', __i18n_text_domain__ ), }, ]; @@ -180,23 +176,7 @@ export function StylePicker( { disabled = false, mode, variant = 'image' }: Styl [ targetStore ] ); - // Dev-mode override: unlock Highlights, swap to the live preview, and - // flip the label to "(a12s only)". Production default keeps the - // "Coming Soon" label + teaser preview + disabled state while we - // launch Cinematic-only. - const isDevMode = typeof window !== 'undefined' && window.imageStudioData?.isDevMode === true; - const options = isVideo - ? VIDEO_STYLE_OPTIONS.map( ( opt ) => - opt.value === 'highlights' && isDevMode - ? { - ...opt, - label: __( 'Highlights (a12s only)', __i18n_text_domain__ ), - preview: videoHighlightsPreview, - disabled: false, - } - : opt - ) - : STYLE_OPTIONS; + const options = isVideo ? VIDEO_STYLE_OPTIONS : STYLE_OPTIONS; const handleStyleSelect = ( value: string ) => { setSelectedStyle( value ); @@ -238,6 +218,9 @@ export function StylePicker( { disabled = false, mode, variant = 'image' }: Styl } ) } onClick={ () => handleStyleSelect( option.value ) } > + { option.badge ? ( + { option.badge } + ) : null } { const state = getInitialState(); expect( state ).toEqual( { - selectedStyle: 'cinematic', + selectedStyle: 'highlights', currentVideoUrl: null, currentAttachmentId: null, currentDurationSeconds: null, } ); } ); + + it( 'defaults the selected style to highlights', () => { + expect( getInitialState().selectedStyle ).toBe( 'highlights' ); + } ); } ); describe( 'Actions', () => { @@ -102,7 +106,7 @@ describe( 'Video Studio Store', () => { ); expect( state.currentVideoUrl ).toBe( 'https://example.com/clip.mp4' ); - expect( state.selectedStyle ).toBe( 'cinematic' ); + expect( state.selectedStyle ).toBe( 'highlights' ); expect( state.currentAttachmentId ).toBeNull(); expect( state.currentDurationSeconds ).toBeNull(); } ); @@ -178,7 +182,7 @@ describe( 'Video Studio Store', () => { it( 'selectors return correct values on the initial state', () => { const state = getInitialState(); - expect( selectors.getSelectedStyle( state ) ).toBe( 'cinematic' ); + expect( selectors.getSelectedStyle( state ) ).toBe( 'highlights' ); expect( selectors.getCurrentVideoUrl( state ) ).toBeNull(); expect( selectors.getCurrentAttachmentId( state ) ).toBeNull(); expect( selectors.getCurrentDurationSeconds( state ) ).toBeNull(); diff --git a/packages/image-studio/src/stores/video-studio/index.ts b/packages/image-studio/src/stores/video-studio/index.ts index 8a324e6c9e7c..946489ae5412 100644 --- a/packages/image-studio/src/stores/video-studio/index.ts +++ b/packages/image-studio/src/stores/video-studio/index.ts @@ -55,9 +55,9 @@ type VideoStudioAction = * Initial state for the video studio store */ const initialState: VideoStudioState = { - // Default to 'cinematic' so the Style picker shows a pre-selected option + // Default to 'highlights' so the Style picker shows a pre-selected option // on first open instead of leaving the user with an indeterminate picker. - selectedStyle: 'cinematic', + selectedStyle: 'highlights', currentVideoUrl: null, currentAttachmentId: null, currentDurationSeconds: null,