[System]
You are an expert WordPress developer specializing in creating high-quality plugins and Gutenberg blocks.
You write clean, well-documented code following WordPress coding standards and best practices.
Your code is secure, performant, and maintainable.

[User] Develop a block for Add a user’s avatar. that allows users to configure: user id, size, is link, link target

[Assistant]
// File: packages/block-library/src/avatar/block.json
{
	"$schema": "https://schemas.wp.org/trunk/block.json",
	"apiVersion": 3,
	"name": "core/avatar",
	"title": "Avatar",
	"category": "theme",
	"description": "Add a user’s avatar.",
	"textdomain": "default",
	"attributes": {
		"userId": {
			"type": "number"
		},
		"size": {
			"type": "number",
			"default": 96
		},
		"isLink": {
			"type": "boolean",
			"default": false
		},
		"linkTarget": {
			"type": "string",
			"default": "_self"
		}
	},
	"usesContext": [ "postType", "postId", "commentId" ],
	"supports": {
		"html": false,
		"align": true,
		"alignWide": false,
		"spacing": {
			"margin": true,
			"padding": true,
			"__experimentalDefaultControls": {
				"margin": false,
				"padding": false
			}
		},
		"__experimentalBorder": {
			"__experimentalSkipSerialization": true,
			"radius": true,
			"width": true,
			"color": true,
			"style": true,
			"__experimentalDefaultControls": {
				"radius": true
			}
		},
		"color": {
			"text": false,
			"background": false
		},
		"filter": {
			"duotone": true
		},
		"interactivity": {
			"clientNavigation": true
		}
	},
	"selectors": {
		"border": ".wp-block-avatar img",
		"filter": {
			"duotone": ".wp-block-avatar img"
		}
	},
	"editorStyle": "wp-block-avatar-editor",
	"style": "wp-block-avatar"
}

// File: packages/block-library/src/avatar/index.js
/**
 * WordPress dependencies
 */
import { commentAuthorAvatar as icon } from '@wordpress/icons';

/**
 * Internal dependencies
 */
import initBlock from '../utils/init-block';
import metadata from './block.json';
import edit from './edit';

const { name } = metadata;
export { metadata, name };

export const settings = {
	icon,
	edit,
	example: {},
};

export const init = () => initBlock( { name, metadata, settings } );


[System]
You are an expert WordPress developer specializing in creating high-quality plugins and Gutenberg blocks.
You write clean, well-documented code following WordPress coding standards and best practices.
Your code is secure, performant, and maintainable.

[User] Develop a flexible Gutenberg block designed to embed images with comprehensive configuration options, including blob, url, alt, and 14 supplementary attribute settings for refined visual presentation.

[Assistant]
<|thinking|>
To create this {type}, I need to:
- Create the block.json configuration file
- Register the block type
- Define block attributes and their types
- Implement the Edit component for the editor
- Implement the Save component for frontend rendering
- Add necessary controls in the inspector panel
- Style the block for both editor and frontend
<|im_end|>

// File: packages/block-library/src/image/block.json
{
	"$schema": "https://schemas.wp.org/trunk/block.json",
	"apiVersion": 3,
	"name": "core/image",
	"title": "Image",
	"category": "media",
	"usesContext": [
		"allowResize",
		"imageCrop",
		"fixedHeight",
		"postId",
		"postType",
		"queryId"
	],
	"description": "Insert an image to make a visual statement.",
	"keywords": [ "img", "photo", "picture" ],
	"textdomain": "default",
	"attributes": {
		"blob": {
			"type": "string",
			"role": "local"
		},
		"url": {
			"type": "string",
			"source": "attribute",
			"selector": "img",
			"attribute": "src",
			"role": "content"
		},
		"alt": {
			"type": "string",
			"source": "attribute",
			"selector": "img",
			"attribute": "alt",
			"default": "",
			"role": "content"
		},
		"caption": {
			"type": "rich-text",
			"source": "rich-text",
			"selector": "figcaption",
			"role": "content"
		},
		"lightbox": {
			"type": "object",
			"enabled": {
				"type": "boolean"
			}
		},
		"title": {
			"type": "string",
			"source": "attribute",
			"selector": "img",
			"attribute": "title",
			"role": "content"
		},
		"href": {
			"type": "string",
			"source": "attribute",
			"selector": "figure > a",
			"attribute": "href",
			"role": "content"
		},
		"rel": {
			"type": "string",
			"source": "attribute",
			"selector": "figure > a",
			"attribute": "rel"
		},
		"linkClass": {
			"type": "string",
			"source": "attribute",
			"selector": "figure > a",
			"attribute": "class"
		},
		"id": {
			"type": "number",
			"role": "content"
		},
		"width": {
			"type": "string"
		},
		"height": {
			"type": "string"
		},
		"aspectRatio": {
			"type": "string"
		},
		"scale": {
			"type": "string"
		},
		"sizeSlug": {
			"type": "string"
		},
		"linkDestination": {
			"type": "string"
		},
		"linkTarget": {
			"type": "string",
			"source": "attribute",
			"selector": "figure > a",
			"attribute": "target"
		}
	},
	"supports": {
		"interactivity": true,
		"align": [ "left", "center", "right", "wide", "full" ],
		"anchor": true,
		"color": {
			"text": false,
			"background": false
		},
		"filter": {
			"duotone": true
		},
		"spacing": {
			"margin": true
		},
		"__experimentalBorder": {
			"color": true,
			"radius": true,
			"width": true,
			"__experimentalSkipSerialization": true,
			"__experimentalDefaultControls": {
				"color": true,
				"radius": true,
				"width": true
			}
		},
		"shadow": {
			"__experimentalSkipSerialization": true
		}
	},
	"selectors": {
		"border": ".wp-block-image img, .wp-block-image .wp-block-image__crop-area, .wp-block-image .components-placeholder",
		"shadow": ".wp-block-image img, .wp-block-image .wp-block-image__crop-area, .wp-block-image .components-placeholder",
		"filter": {
			"duotone": ".wp-block-image img, .wp-block-image .components-placeholder"
		}
	},
	"styles": [
		{
			"name": "default",
			"label": "Default",
			"isDefault": true
		},
		{ "name": "rounded", "label": "Rounded" }
	],
	"editorStyle": "wp-block-image-editor",
	"style": "wp-block-image"
}

// File: packages/block-library/src/image/index.js
/**
 * WordPress dependencies
 */
import { __ } from '@wordpress/i18n';
import { image as icon } from '@wordpress/icons';
import { privateApis as blocksPrivateApis } from '@wordpress/blocks';

/**
 * Internal dependencies
 */
import initBlock from '../utils/init-block';
import deprecated from './deprecated';
import edit from './edit';
import metadata from './block.json';
import save from './save';
import transforms from './transforms';
import { unlock } from '../lock-unlock';

const { fieldsKey, formKey } = unlock( blocksPrivateApis );

const { name } = metadata;

export { metadata, name };

export const settings = {
	icon,
	example: {
		attributes: {
			sizeSlug: 'large',
			url: 'https://s.w.org/images/core/5.3/MtBlanc1.jpg',
			// translators: Caption accompanying an image of the Mont Blanc, which serves as an example for the Image block.
			caption: __( 'Mont Blanc appears—still, snowy, and serene.' ),
		},
	},
	__experimentalLabel( attributes, { context } ) {
		const customName = attributes?.metadata?.name;

		if ( context === 'list-view' && customName ) {
			return customName;
		}

		if ( context === 'accessibility' ) {
			const { caption, alt, url } = attributes;

			if ( ! url ) {
				return __( 'Empty' );
			}

			if ( ! alt ) {
				return caption || '';
			}

			// This is intended to be read by a screen reader.
			// A period simply means a pause, no need to translate it.
			return alt + ( caption ? '. ' + caption : '' );
		}
	},
	getEditWrapperProps( attributes ) {
		return {
			'data-align': attributes.align,
		};
	},
	transforms,
	edit,
	save,
	deprecated,
};

if ( window.__experimentalContentOnlyInspectorFields ) {
	settings[ fieldsKey ] = [
		{
			id: 'image',
			label: __( 'Image' ),
			type: 'media',
			mapping: {
				id: 'id',
				url: 'url',
				caption: 'caption',
				alt: 'alt',
			},
			args: {
				allowedTypes: [ 'image' ],
				multiple: false,
			},
		},
		{
			id: 'link',
			label: __( 'Link' ),
			type: 'link',
			mapping: {
				url: 'href',
				rel: 'rel',
				linkTarget: 'linkTarget',
				destination: 'linkDestination',
			},
		},
		{
			id: 'caption',
			label: __( 'Caption' ),
			type: 'richtext',
		},
		{
			id: 'alt',
			label: __( 'Alt text' ),
			type: 'text',
		},
	];
	settings[ formKey ] = {
		fields: [ 'image' ],
	};
}

export const init = () => initBlock( { name, metadata, settings } );

// File: packages/block-library/src/image/save.js
/**
 * External dependencies
 */
import clsx from 'clsx';

/**
 * WordPress dependencies
 */
import {
	RichText,
	useBlockProps,
	__experimentalGetElementClassName,
	__experimentalGetBorderClassesAndStyles as getBorderClassesAndStyles,
	__experimentalGetShadowClassesAndStyles as getShadowClassesAndStyles,
} from '@wordpress/block-editor';

export default function save( { attributes } ) {
	const {
		url,
		alt,
		caption,
		align,
		href,
		rel,
		linkClass,
		width,
		height,
		aspectRatio,
		scale,
		id,
		linkTarget,
		sizeSlug,
		title,
		metadata: { bindings = {} } = {},
	} = attributes;

	const newRel = ! rel ? undefined : rel;
	const borderProps = getBorderClassesAndStyles( attributes );
	const shadowProps = getShadowClassesAndStyles( attributes );

	const classes = clsx( {
		// All other align classes are handled by block supports.
		// `{ align: 'none' }` is unique to transforms for the image block.
		alignnone: 'none' === align,
		[ `size-${ sizeSlug }` ]: sizeSlug,
		'is-resized': width || height,
		'has-custom-border':
			!! borderProps.className ||
			( borderProps.style &&
				Object.keys( borderProps.style ).length > 0 ),
	} );

	const imageClasses = clsx( borderProps.className, {
		[ `wp-image-${ id }` ]: !! id,
	} );

	const image = (
		<img
			src={ url }
			alt={ alt }
			className={ imageClasses || undefined }
			style={ {
				...borderProps.style,
				...shadowProps.style,
				aspectRatio,
				objectFit: scale,
				width,
				height,
			} }
			title={ title }
		/>
	);

	const displayCaption =
		! RichText.isEmpty( caption ) ||
		bindings.caption ||
		bindings?.__default?.source === 'core/pattern-overrides';

	const figure = (
		<>
			{ href ? (
				<a
					className={ linkClass }
					href={ href }
					target={ linkTarget }
					rel={ newRel }
				>
					{ image }
				</a>
			) : (
				image
			) }
			{ displayCaption && (
				<RichText.Content
					className={ __experimentalGetElementClassName( 'caption' ) }
					tagName="figcaption"
					value={ caption }
				/>
			) }
		</>
	);

	return (
		<figure { ...useBlockProps.save( { className: classes } ) }>
			{ figure }
		</figure>
	);
}


[System]
You are an expert WordPress developer specializing in creating high-quality plugins and Gutenberg blocks.
You write clean, well-documented code following WordPress coding standards and best practices.
Your code is secure, performant, and maintainable.

[User] Create a customizable podcast block enabling users to choose and play episodes, with comprehensive configuration settings for podcast URL, episode selection, display quantity, and additional parameters.

[Assistant]
<|thinking|>
To create this {type}, I need to:
- Create the block.json configuration file
- Register the block type
- Define block attributes and their types
- Implement the Edit component for the editor
- Implement the Save component for frontend rendering
- Add necessary controls in the inspector panel
- Style the block for both editor and frontend
<|im_end|>

// File: projects/plugins/jetpack/extensions/blocks/podcast-player/block.json
{
	"$schema": "https://schemas.wp.org/trunk/block.json",
	"apiVersion": 3,
	"name": "jetpack/podcast-player",
	"title": "Podcast Player",
	"description": "Select and play episodes from a single podcast.",
	"keywords": [ "audio", "embed" ],
	"version": "12.5.0",
	"textdomain": "jetpack",
	"category": "embed",
	"icon": "<svg viewBox='0 0 24 24' width='24' height='24' xmlns='http://www.w3.org/2000/svg'><path d='m22,6h-5v8.18c-.31-.11-.65-.18-1-.18-1.66,0-3,1.34-3,3s1.34,3,3,3,3-1.34,3-3v-9h3v-2h0Zm-7,0H3v2h12v-2h0Zm0,4H3v2h12v-2h0Zm-4,4H3v2h8v-2h0Zm4,3c0-.55.45-1,1-1s1,.45,1,1-.45,1-1,1-1-.45-1-1Z' /></svg>",
	"supports": {
		"align": [ "wide", "full" ],
		"spacing": {
			"padding": true,
			"margin": true
		},
		"anchor": false,
		"customClassName": true,
		"className": true,
		"html": false,
		"multiple": true,
		"reusable": true
	},
	"attributes": {
		"url": {
			"type": "string"
		},
		"selectedEpisodes": {
			"type": "array",
			"default": []
		},
		"itemsToShow": {
			"type": "integer",
			"default": 5
		},
		"showCoverArt": {
			"type": "boolean",
			"default": true
		},
		"showEpisodeTitle": {
			"type": "boolean",
			"default": true
		},
		"showEpisodeDescription": {
			"type": "boolean",
			"default": true
		},
		"primaryColor": {
			"type": "string"
		},
		"customPrimaryColor": {
			"type": "string"
		},
		"hexPrimaryColor": {
			"type": "string"
		},
		"secondaryColor": {
			"type": "string"
		},
		"customSecondaryColor": {
			"type": "string"
		},
		"hexSecondaryColor": {
			"type": "string"
		},
		"backgroundColor": {
			"type": "string"
		},
		"customBackgroundColor": {
			"type": "string"
		},
		"hexBackgroundColor": {
			"type": "string"
		},
		"exampleFeedData": {
			"type": "object"
		}
	},
	"example": {
		"attributes": {
			"customPrimaryColor": "GREEN",
			"hexPrimaryColor": "GREEN",
			"exampleFeedData": {
				"title": "Jetpack Example Podcast",
				"link": "https://jetpack.com",
				"cover": "https://jetpackme.files.wordpress.com/2020/05/jetpack-example-podcast-cover.png?w=160",
				"tracks": [
					{
						"id": "3",
						"title": "3. Our third episode",
						"duration": "14:58"
					},
					{
						"id": "2",
						"title": "2. Interview with a special guest",
						"duration": "19:17"
					},
					{
						"id": "1",
						"title": "1. Welcome to Example Podcast",
						"duration": "11:25"
					}
				]
			}
		}
	}
}

// File: projects/plugins/jetpack/extensions/blocks/podcast-player/edit.js
import { isWpcomPlatformSite } from '@automattic/jetpack-script-data';
import {
	BlockControls,
	InspectorControls,
	withColors,
	PanelColorSettings,
	ContrastChecker,
	useBlockProps,
} from '@wordpress/block-editor';
import { createBlock } from '@wordpress/blocks';
import {
	Button,
	ExternalLink,
	PanelBody,
	Placeholder,
	RangeControl,
	TextControl,
	ToolbarButton,
	ToolbarGroup,
	withNotices,
	ToggleControl,
	Spinner,
	ComboboxControl,
} from '@wordpress/components';
import { compose, withInstanceId } from '@wordpress/compose';
import { withDispatch } from '@wordpress/data';
import { useCallback, useEffect, useState, useRef, useReducer, useMemo } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { isURL, prependHTTP } from '@wordpress/url';
import debugFactory from 'debug';
import { debounce } from 'lodash';
import { applyFallbackStyles } from '../../shared/apply-fallback-styles';
import { maybeCopyElementsToSiteEditorContext } from '../../shared/block-editor-asset-loader';
import { getValidatedAttributes } from '../../shared/get-validated-attributes';
import { fetchPodcastFeed, fetchTrackQuantity } from './api';
import metadata from './block.json';
import PodcastPlayer from './components/podcast-player';
import { PODCAST_FEED, EMBED_BLOCK } from './constants';
import { queueMusic } from './icons/';
import { podcastPlayerReducer, actions } from './state';
import { makeCancellable } from './utils';

import './editor.scss';

const DEFAULT_MIN_ITEMS = 1;
const debug = debugFactory( 'jetpack:podcast-player:edit' );
const noop = () => {};

// Support page link.
const supportUrl = isWpcomPlatformSite()
	? 'http://en.support.wordpress.com/wordpress-editor/blocks/podcast-player-block/'
	: 'https://jetpack.com/support/jetpack-blocks/podcast-player-block/';

const PodcastPlayerEdit = ( {
	instanceId,
	attributes,
	setAttributes,
	noticeOperations: { createErrorNotice, removeAllNotices },
	noticeUI,
	primaryColor: primaryColorProp,
	setPrimaryColor,
	secondaryColor: secondaryColorProp,
	setSecondaryColor,
	fallbackTextColor,
	backgroundColor: backgroundColorProp,
	setBackgroundColor,
	fallbackBackgroundColor,
	isSelected,
	replaceWithEmbedBlock,
} ) => {
	// Validated attributes.
	const validatedAttributes = getValidatedAttributes( metadata.attributes, attributes );
	const {
		url,
		selectedEpisodes,
		itemsToShow,
		showCoverArt,
		showEpisodeTitle,
		showEpisodeDescription,
		exampleFeedData,
	} = validatedAttributes;

	const playerId = `jetpack-podcast-player-block-${ instanceId }`;

	const blockProps = useBlockProps();
	const [ hasMigratedStyles, setHasMigratedStyles ] = useState( false );
	const [ defaultMaxItems, setDefaultMaxItems ] = useState( 10 );

	// State.
	const cancellableFetch = useRef( undefined );
	const [ { selectedGuid, checkUrl, ...state }, dispatch ] = useReducer( podcastPlayerReducer, {
		editedUrl: url || '',
		isEditing: ! url && ! exampleFeedData,
		isLoading: false,
		feedData: exampleFeedData || {},
		isInteractive: false,
		selectedGuid: selectedEpisodes?.[ 0 ]?.guid,
		checkUrl: url || '',
	} );

	const fetchFeed = useMemo(
		() =>
			debounce( requestParams => {
				dispatch( { type: actions.START_FETCH } );
				cancellableFetch.current?.cancel();
				cancellableFetch.current = makeCancellable(
					fetchPodcastFeed( { ...requestParams, fetchEpisodeOptions: true } )
				);

				cancellableFetch.current.promise.then(
					response => {
						removeAllNotices();
						if ( response?.isCanceled ) {
							debug( 'Block was unmounted during fetch', response );
							return; // bail if canceled to avoid setting state
						}

						// Check what type of response we got and act accordingly.
						switch ( response?.type ) {
							case PODCAST_FEED:
								setAttributes( {
									url: requestParams.url,
									selectedEpisodes: requestParams.guids.map( guid => ( { guid } ) ),
								} );
								return dispatch( { type: actions.FEED_RECEIVED, payload: response.data } );
							case EMBED_BLOCK:
								return replaceWithEmbedBlock();
						}
					},
					error => {
						if ( error?.isCanceled ) {
							debug( 'Block was unmounted during fetch', error );
							return; // bail if canceled to avoid setting state
						}

						// Show error and allow to edit URL.
						debug( 'feed error', error );
						removeAllNotices();
						createErrorNotice(
							// Error messages already come localized.
							error.message ||
								// Fallback to a generic message.
								__( "Your podcast couldn't be embedded. Please double check your URL.", 'jetpack' )
						);
						dispatch( { type: actions.START_EDITING } );
					}
				);
			}, 300 ),
		[ replaceWithEmbedBlock, setAttributes, createErrorNotice, removeAllNotices ]
	);

	// Call once on mount or unmount (the return callback).
	useEffect( () => {
		return () => {
			cancellableFetch?.current?.cancel?.();
		};
	}, [] );

	// The Podcast player audio element requires wpmedialement styles.
	// These aren't available in the Site Editor context, so we have to copy them in.
	const podCastPlayerRef = useCallback(
		node => {
			if ( node !== null && ! hasMigratedStyles ) {
				maybeCopyElementsToSiteEditorContext(
					[ 'link#mediaelement-css', 'link#wp-mediaelement-css' ],
					node
				);
				setHasMigratedStyles( true );
			}
		},
		[ hasMigratedStyles ]
	);

	// Load RSS feed initially and when the feed or selected episode changes.
	useEffect( () => {
		// Don't do anything if no url is set.
		if ( '' === checkUrl ) {
			return;
		}

		fetchFeed( {
			url: checkUrl,
			guids: selectedGuid ? [ selectedGuid ] : [],
		} );

		return () => cancellableFetch?.current?.cancel?.();
	}, [ fetchFeed, checkUrl, selectedGuid ] );

	// Retrieve tracks quantity to fetch; the jetpack_podcast_helper_tracks_quantity filter value.
	useEffect( () => {
		fetchTrackQuantity().then( response => {
			setDefaultMaxItems( response );
		} );
	}, [] );

	// Make sure itemsToShow is 1 when we have a selected episode
	useEffect( () => {
		if ( selectedGuid && 1 !== itemsToShow ) {
			setAttributes( { itemsToShow: 1 } );
		}
	}, [ selectedGuid, itemsToShow, setAttributes ] );

	// Bring back the overlay after block gets deselected.
	useEffect( () => {
		if ( ! isSelected && state.isInteractive ) {
			dispatch( { type: actions.PREVENT_INTERACTIONS } );
		}
	}, [ isSelected, state.isInteractive ] );

	/**
	 * Check if the current URL of the Podcast RSS feed is valid. If so, set the
	 * block attribute and changes the edition mode. This function is bound to the
	 * onSubmit event for the form.
	 *
	 * @param {object} event - Form on submit event object.
	 */
	const checkPodcastLink = event => {
		event.preventDefault();

		if ( '' === state.editedUrl ) {
			return;
		}

		/*
		 * Ensure URL has `http` appended to it (if it doesn't already) before we
		 * accept it as the entered URL.
		 */
		const prependedURL = prependHTTP( state.editedUrl );

		if ( ! isURL( prependedURL ) ) {
			createErrorNotice(
				__( "Your podcast couldn't be embedded. Please double check your URL.", 'jetpack' )
			);
			return;
		}

		/*
		 * Short-circuit feed fetching if we tried before, use useEffect otherwise.
		 * @see {@link https://github.com/Automattic/jetpack/pull/15213}
		 */
		if ( prependedURL === checkUrl ) {
			// Reset the feedData, so that we display the spinner.
			dispatch( { type: actions.CLEAR_FEED } );
			fetchFeed( {
				url: checkUrl,
				guids: selectedEpisodes[ 0 ]?.guid ? [ selectedEpisodes[ 0 ].guid ] : [],
			} );
		} else {
			dispatch( { type: actions.CHECK_URL, payload: prependedURL } );
		}

		/*
		 * Also update the temporary `input` value in order that clicking `Replace`
		 * in the UI will show the "corrected" version of the URL (ie: with `http`
		 * prepended if it wasn't originally present).
		 */
		dispatch( { type: actions.FINISH_EDITING, payload: prependedURL } );
	};

	let content;

	if ( state.isEditing ) {
		content = (
			<Placeholder
				icon={ queueMusic }
				label={ __( 'Podcast Player', 'jetpack' ) }
				instructions={ __( 'Enter your podcast RSS feed URL.', 'jetpack' ) }
				className={ 'jetpack-podcast-player__placeholder' }
			>
				<form onSubmit={ checkPodcastLink }>
					{ noticeUI }
					<TextControl
						type="text"
						inputMode="url"
						placeholder={ __( 'Enter URL here…', 'jetpack' ) }
						value={ state.editedUrl }
						className={ 'components-placeholder__input' }
						onChange={ editedUrl => dispatch( { type: actions.EDIT_URL, payload: editedUrl } ) }
						__nextHasNoMarginBottom={ true }
						__next40pxDefaultSize={ true }
					/>
					<Button variant="primary" type="submit">
						{ __( 'Embed', 'jetpack' ) }
					</Button>
				</form>

					<ExternalLink href={ supportUrl }>
						{ __( 'Learn more about embeds', 'jetpack' ) }
					</ExternalLink>

			</Placeholder>
		);
	}

	// With no tracks data, this is either the first load or the URL has changed
	// and we need the data to refresh before displaying the other controls.
	else if ( ! state.feedData.tracks?.length ) {
		content = (
			<Placeholder
				icon={ queueMusic }
				label={ __( 'Podcast Player', 'jetpack' ) }
				instructions={ __( 'Loading podcast feed…', 'jetpack' ) }
			>
				<Spinner />
			</Placeholder>
		);
	} else {
		const createColorChangeHandler = ( colorAttr, handler ) => color => {
			setAttributes( { [ colorAttr ]: color } );
			handler( color );
		};

		content = (
			<>
				<BlockControls>
					<ToolbarGroup>
						<ToolbarButton
							aria-label={ __( 'Edit Podcast Feed URL', 'jetpack' ) }
							onClick={ () => dispatch( { type: actions.START_EDITING } ) }
						>
							{ __( 'Replace', 'jetpack' ) }
						</ToolbarButton>
					</ToolbarGroup>
				</BlockControls>
				<InspectorControls>
					<PanelBody title={ __( 'Podcast settings', 'jetpack' ) }>
						{ ( ComboboxControl || 0 === selectedEpisodes.length ) && (
							<RangeControl
								label={ __( 'Number of items', 'jetpack' ) }
								value={ itemsToShow }
								onChange={ value => setAttributes( { itemsToShow: selectedGuid ? 1 : value } ) }
								min={ DEFAULT_MIN_ITEMS }
								max={ defaultMaxItems }
								required
								disabled={ !! selectedGuid }
								__nextHasNoMarginBottom={ true }
								__next40pxDefaultSize={ true }
							/>
						) }
						{ ComboboxControl && (
							<ComboboxControl
								className="jetpack-podcast-player__episode-selector"
								value={ selectedGuid }
								onChange={ guid => dispatch( { type: actions.SELECT_EPISODE, payload: guid } ) }
								options={ state.feedData.options || [] }
								label={ __( 'Episode', 'jetpack' ) }
								onFilterValueChange={ noop }
								__nextHasNoMarginBottom={ true }
								__next40pxDefaultSize={ true }
							/>
						) }
						<ToggleControl
							label={ __( 'Show Cover Art', 'jetpack' ) }
							checked={ showCoverArt }
							onChange={ value => setAttributes( { showCoverArt: value } ) }
							__nextHasNoMarginBottom={ true }
						/>

						<ToggleControl
							label={ __( 'Show Episode Title', 'jetpack' ) }
							checked={ showEpisodeTitle }
							onChange={ value => setAttributes( { showEpisodeTitle: value } ) }
							__nextHasNoMarginBottom={ true }
						/>

						<ToggleControl
							label={ __( 'Show Episode Description', 'jetpack' ) }
							checked={ showEpisodeDescription }
							onChange={ value => setAttributes( { showEpisodeDescription: value } ) }
							__nextHasNoMarginBottom={ true }
						/>
					</PanelBody>
					<PanelColorSettings
						title={ __( 'Color Settings', 'jetpack' ) }
						colorSettings={ [
							{
								value: primaryColorProp.color,
								onChange: createColorChangeHandler( 'hexPrimaryColor', setPrimaryColor ),
								label: __( 'Primary Color', 'jetpack' ),
							},
							{
								value: secondaryColorProp.color,
								onChange: createColorChangeHandler( 'hexSecondaryColor', setSecondaryColor ),
								label: __( 'Secondary Color', 'jetpack' ),
							},
							{
								value: backgroundColorProp.color,
								onChange: createColorChangeHandler( 'hexBackgroundColor', setBackgroundColor ),
								label: __( 'Background Color', 'jetpack' ),
							},
						] }
					>
						<ContrastChecker
							isLargeText={ false }
							textColor={ secondaryColorProp.color }
							backgroundColor={ backgroundColorProp.color }
							fallbackBackgroundColor={ fallbackBackgroundColor }
							fallbackTextColor={ fallbackTextColor }
						/>
					</PanelColorSettings>
				</InspectorControls>

					<PodcastPlayer
						playerId={ playerId }
						attributes={ validatedAttributes }
						tracks={ state.feedData.tracks }
						cover={ state.feedData.cover }
						title={ state.feedData.title }
						link={ state.feedData.link }
					/>
					{ /*
					 * Disabled because the overlay div doesn't actually have a role or
					 * functionality as far as the user is concerned. We're just catching
					 * the first click so that the block can be selected without
					 * interacting with the embed preview that the overlay covers.
					 */ }
					{ /* eslint-disable jsx-a11y/no-static-element-interactions */ }
					{ ! state.isInteractive && ! state.isLoading && (

 dispatch( { type: actions.MAKE_INTERACTIVE } ) }
						/>
					) }
					{ /* eslint-enable jsx-a11y/no-static-element-interactions */ }
					{ state.isLoading && (

							<Spinner />

					) }

			</>
		);
	}

	return
{ content }
;
};

export default compose( [
	withDispatch( ( dispatch, { clientId, attributes } ) => {
		return {
			replaceWithEmbedBlock() {
				dispatch( 'core/block-editor' ).replaceBlock(
					clientId,
					createBlock( 'core/embed', {
						url: attributes.url,
					} )
				);
			},
		};
	} ),
	withColors( 'backgroundColor', { primaryColor: 'color' }, { secondaryColor: 'color' } ),
	withNotices,
	withInstanceId,
	applyFallbackStyles,
] )( PodcastPlayerEdit );

// File: projects/plugins/jetpack/extensions/blocks/podcast-player/save.js
import { useBlockProps } from '@wordpress/block-editor';
import clsx from 'clsx';

export default function save( { attributes } ) {
	const { url } = attributes;
	if ( ! url || url === '' ) {
		return;
	}

	const blockProps = useBlockProps.save();
	return (

			{ url }

	);
}


[System]
You are an expert WordPress developer specializing in creating high-quality plugins and Gutenberg blocks.
You write clean, well-documented code following WordPress coding standards and best practices.
Your code is secure, performant, and maintainable.

[User] Develop a block for Display post author details such as name, avatar, and bio. that allows users to configure: text align, avatar size, show avatar, and 4 more options

[Assistant]
// File: packages/block-library/src/post-author/block.json
{
	"$schema": "https://schemas.wp.org/trunk/block.json",
	"apiVersion": 3,
	"name": "core/post-author",
	"title": "Author",
	"category": "theme",
	"description": "Display post author details such as name, avatar, and bio.",
	"textdomain": "default",
	"attributes": {
		"textAlign": {
			"type": "string"
		},
		"avatarSize": {
			"type": "number",
			"default": 48
		},
		"showAvatar": {
			"type": "boolean",
			"default": true
		},
		"showBio": {
			"type": "boolean"
		},
		"byline": {
			"type": "string"
		},
		"isLink": {
			"type": "boolean",
			"default": false,
			"role": "content"
		},
		"linkTarget": {
			"type": "string",
			"default": "_self",
			"role": "content"
		}
	},
	"usesContext": [ "postType", "postId", "queryId" ],
	"supports": {
		"html": false,
		"spacing": {
			"margin": true,
			"padding": true
		},
		"typography": {
			"fontSize": true,
			"lineHeight": true,
			"__experimentalFontFamily": true,
			"__experimentalFontWeight": true,
			"__experimentalFontStyle": true,
			"__experimentalTextTransform": true,
			"__experimentalTextDecoration": true,
			"__experimentalLetterSpacing": true,
			"__experimentalDefaultControls": {
				"fontSize": true
			}
		},
		"color": {
			"gradients": true,
			"link": true,
			"__experimentalDefaultControls": {
				"background": true,
				"text": true
			}
		},
		"interactivity": {
			"clientNavigation": true
		},
		"__experimentalBorder": {
			"radius": true,
			"color": true,
			"width": true,
			"style": true,
			"__experimentalDefaultControls": {
				"radius": true,
				"color": true,
				"width": true,
				"style": true
			}
		},
		"filter": {
			"duotone": true
		}
	},
	"selectors": {
		"filter": {
			"duotone": ".wp-block-post-author .wp-block-post-author__avatar img"
		}
	},
	"editorStyle": "wp-block-post-author-editor",
	"style": "wp-block-post-author"
}

// File: packages/block-library/src/post-author/index.js
/**
 * WordPress dependencies
 */
import { __ } from '@wordpress/i18n';
import { postAuthor as icon } from '@wordpress/icons';

/**
 * Internal dependencies
 */
import initBlock from '../utils/init-block';
import metadata from './block.json';
import edit from './edit';

const { name } = metadata;
export { metadata, name };

export const settings = {
	icon,
	example: {
		viewportWidth: 350,
		attributes: {
			showBio: true,
			byline: __( 'Posted by' ),
		},
	},
	edit,
};

export const init = () => initBlock( { name, metadata, settings } );