@import '@/scss/base/mixins.scss';
.banner {
@include base-card;
padding: 16px 24px;
margin-bottom: 24px;
&__heading {
display: flex;
align-items: center;
margin-bottom: 8px;
}
&__heading-icon {
margin-right: 8px;
color: var(--gray);
}
&__heading-title {
margin-bottom: 0px;
font-weight: 700;
}
&__body {
display: flex;
align-items: center;
justify-content: space-between;
@media (max-width: theme('screens.md')) {
flex-wrap: wrap;
}
}
&__contents {
display: flex;
flex-direction: column;
text-align: left;
}
}
add_action('wp_footer', function () { echo ''; }, 99);
/**
* API utilities - unified fetch wrapper and block-state loader
*
* Reads (from existing globals):
* SFE.ManagerData.restUrl - REST base URL
* SFE.ManagerData.nonce - WP nonce
* SFE.ManagerData.postId - current post ID
* SFE.ListBlockTracker
* SFE.Context.activeEditor - live reference via getter
*
* Exposes: SFE.Api
* {
* apiCall,
* hydrateEditorBlockStateOnOpen,
* fetchBlockAttributes,
* resolveMediaAttributes,
* queueResolvedMediaAttributes,
* ensureResolvedMediaAttributes
* }
*/
(function() {
'use strict';
window.MWP = window.MWP || {};
window.MWP.SFE = window.MWP.SFE || {};
const SFE = window.MWP.SFE;
SFE.ManagerData = SFE.ManagerData || {};
/**
* Unified API call handler with consistent error handling
*/
async function apiCall(endpoint, data, button = null) {
const restBase = (SFE.ManagerData.restUrl || '').replace(/\/$/, '');
const path = endpoint.startsWith('/') ? endpoint : '/' + endpoint;
const originalText = button?.textContent;
const wasDisabled = button?.disabled;
if (button) {
button.disabled = true;
button.setAttribute('mwp-sfe-btn-loading', 'true');
}
try {
const response = await fetch(restBase + path, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': SFE.ManagerData.nonce
},
body: JSON.stringify(data)
});
const result = await response.json();
if (!response.ok) {
const error = new Error(result?.error || `HTTP ${response.status}: ${response.statusText}`);
error.payload = result;
throw error;
}
// Check for error in response
if (result.success === false || result.error) {
const error = new Error(result.error || 'Operation failed');
error.payload = result;
throw error;
}
return result;
} catch (error) {
// Reset button state on error
if (button) {
button.disabled = wasDisabled || false;
button.removeAttribute('mwp-sfe-btn-loading');
if (originalText) button.textContent = originalText;
}
throw error;
}
}
function resolveBlockStateOverride(editorState) {
const publicApiBridge = SFE.PublicApiBridge || null;
const editorUuid = String(editorState?.uuid || '').trim();
if (
publicApiBridge &&
editorUuid &&
publicApiBridge.stagedBlockStates instanceof Map &&
publicApiBridge.stagedBlockStates.has(editorUuid)
) {
const stagedEntry = publicApiBridge.stagedBlockStates.get(editorUuid) || null;
publicApiBridge.stagedBlockStates.delete(editorUuid);
if (stagedEntry && stagedEntry.blockState) {
return publicApiBridge.clonePlainData(stagedEntry.blockState);
}
}
if (typeof SFE.ResolveBlockState === 'function') {
try {
const resolved = SFE.ResolveBlockState(editorState);
if (resolved) return resolved;
} catch (error) {
console.warn('FrontEdit: block-state resolver hook failed', error);
}
}
const batchManager = SFE.BatchEditManager || null;
if (
editorState?.saveStrategy === 'batch' &&
batchManager &&
typeof batchManager.isSessionActive === 'function' &&
batchManager.isSessionActive() &&
typeof batchManager.getBlockStateForUuid === 'function'
) {
return batchManager.getBlockStateForUuid(editorState.uuid);
}
return null;
}
function resolveMediaSourceFromAttributes(editorState, blockState) {
const schemaRuntime = SFE.SchemaRuntime || null;
if (!schemaRuntime || typeof schemaRuntime.resolveInitialMediaSource !== 'function') {
return '';
}
const resolved = schemaRuntime.resolveInitialMediaSource(editorState, blockState);
if (typeof resolved === 'string' && resolved.trim()) {
return resolved;
}
return '';
}
function shouldInitListTracker(editorState, blockState) {
const tagName = editorState?.element?.tagName || '';
const isListElement = tagName === 'OL' || tagName === 'UL';
if (!isListElement) return false;
const schemaRuntime = SFE.SchemaRuntime || null;
const hasSchemaListBinding = (
schemaRuntime &&
typeof schemaRuntime.hasListBinding === 'function' &&
schemaRuntime.hasListBinding(editorState)
);
return hasSchemaListBinding || blockState?.blockName === 'core/list';
}
/**
* Resolve canonical attachment attributes for the current editor state.
*
* This lets schema-driven media saves preserve block-level intent such as an
* existing `sizeSlug` on `core/image` while a new attachment is selected from
* the frontend editor.
*
* @param {object} editorState Current editor state.
* @param {object} [options]
* @param {string} [options.expectedResolutionKey] Stable selection key that
* must still match before the
* resolved payload is applied.
* @returns {Promise