# speechkit/7.0.0/src/editor/components/settings-panel/player-section.js

BeyondWords – AI audio for publishers, version 7.0.0. 85 lines.

- Page: https://pluginprobe.com/plugins/speechkit/7.0.0/code/src/editor/components/settings-panel/player-section.js
- Raw: https://pluginprobe.com/plugins/speechkit/7.0.0/raw/src/editor/components/settings-panel/player-section.js
- Modified: 2026-08-12T09:46:22+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/speechkit/7.0.0/code/src/editor/components/settings-panel/player-section.js#L10-L20`.

```javascript
/**
 * WordPress dependencies
 */
import { __ } from '@wordpress/i18n';
import { PanelBody, SelectControl } from '@wordpress/components';
import { useEntityProp } from '@wordpress/core-data';
import { useSelect } from '@wordpress/data';
import { useEffect } from '@wordpress/element';

/**
 * Internal dependencies
 */
import {
	getDefaultEmbed,
	getEmbedOptions,
	isEmbedValid,
	normalizeSource,
	OUTPUT_AUDIO,
} from './helpers';
import Stack from '../stack';

export function PlayerSection( { withPanel = true } ) {
	const postType = useSelect(
		( select ) => select( 'core/editor' ).getCurrentPostType(),
		[]
	);

	// `useEntityProp` yields undefined meta until the post entity record is hydrated.
	const [ rawMeta, setMeta ] = useEntityProp( 'postType', postType, 'meta' );
	const meta = rawMeta ?? {};

	const source = normalizeSource( meta.beyondwords_source );
	const output = meta.beyondwords_output || OUTPUT_AUDIO;

	const stored = meta.beyondwords_embed;
	// Unset → default to the first asset so the player shows ("None" is the
	// opt-out; the v7.0.0 migration converts legacy `beyondwords_disabled` posts).
	const embed = stored || getDefaultEmbed( source, output );

	const embedOptions = getEmbedOptions( source, output );

	const setEmbed = ( value ) => {
		setMeta( { ...meta, beyondwords_embed: value } );
	};

	// Never write meta on mount: an unset value already means "show", and a
	// mount-time write races the other panels' preselect writes (e.g. Generate audio).
	// Only a stale value lands here — None is valid for every combination.
	useEffect( () => {
		if ( stored && ! isEmbedValid( stored, source, output ) ) {
			setEmbed( getDefaultEmbed( source, output ) );
		}
		// eslint-disable-next-line react-hooks/exhaustive-deps
	}, [ source, output ] );

	const field = (
		<SelectControl
			className="beyondwords--embed"
			label={ __( 'Embed', 'speechkit' ) }
			help={ __(
				'Pick which generated asset is shown on this post. All other generated assets stay available in BeyondWords.',
				'speechkit'
			) }
			options={ embedOptions }
			value={ embed }
			onChange={ setEmbed }
			__nextHasNoMarginBottom
			__next40pxDefaultSize
		/>
	);

	// Document/pre-publish panels render the field without nesting another panel.
	if ( ! withPanel ) {
		return <Stack>{ field }</Stack>;
	}

	return (
		<PanelBody title={ __( 'Player', 'speechkit' ) } initialOpen={ true }>
			<Stack>{ field }</Stack>
		</PanelBody>
	);
}

export default PlayerSection;

```
