window.ClaraVE is one JavaScript surface for both editors: the Gutenberg
workspace used on block themes and the raw-HTML editor used on converted
themes. The popup and any extension — Visual Edit Pro's assistant, for
example — change a page through the same operations, with the same
permission checks.
Nothing here saves. In the workspace every operation is an ordinary unsaved editor change: Undo reverts it and the toolbar's Save publishes it. In the HTML editor operations join the patch queue that Save publishes.
Depend on the clara-ve-api script handle and enqueue from the
clara_ve_workspace_enqueue action:
add_action( 'clara_ve_workspace_enqueue', function ( $workspace, $is_site, $mode ) {
if ( 'native' === $mode ) {
return; // the plain WordPress editor, without the VE chrome
}
wp_enqueue_script( 'my-extension', plugins_url( 'extension.js', __FILE__ ), array( 'clara-ve-api', 'wp-hooks' ), '1.0', true );
}, 10, 3 );ClaraVE.ready( function ( ve ) {
ve.on( 'select', function ( selection ) { console.log( selection ); } );
} );| Method | Returns |
|---|---|
ClaraVE.mode |
'block', 'html', or null before an editor registers |
ready( callback ) |
Runs callback( ClaraVE ) now, or once an editor registers |
getSelection() |
{ id, name, title, attributes, editingMode, parents, sectionName }, or null |
select( id ) |
Selects a block and opens its popup. false in the HTML editor |
openPopup() / closePopup() |
Reopens or closes the popup for the selection |
apply( ops ) |
Promise of { applied: [ index ], refused: [ { index, op, reason } ] } |
saveSection( { id?, title, categories? } ) |
Promise of { id, name: 'core/block/<id>', title, ignoredCategories } or { error }. Block mode only |
getDocument() |
{ mode, type, id, title, content } for the open document |
history.list() |
Promise of { entity: { type, id, title }, entries: [...] } for the open document, newest first |
history.restore( id ) |
Promise. Block mode stages the version (Save publishes); HTML mode restores directly |
on( event, callback ) |
Subscribe; returns an unsubscribe function |
Events: ready, select, apply, save, save-section, restore, unlock.
saveSection is the one method here that is not an editor change. It saves the
section around id (the selection by default) as an unsynced wp_block post,
published at once: Undo does not take it back, and the open document is not
touched. categories are existing wp_pattern_category slugs; unknown ones are
ignored and returned as ignoredCategories, and none are created. A section
holding a synced pattern, a template part or bound attributes is refused with a
reason. The saved section then appears in + Section and, on the server, in
GET /block-patterns with source: "saved".
id is a block client ID in the workspace and an element address such as
path-0-2-1 in the HTML editor.
| Operation | Payload | Workspace | HTML editor |
|---|---|---|---|
set-text |
{ id, html } (or text) |
paragraph, heading, list item, button | text, tags stripped |
set-attrs |
{ id, attrs } |
Top-level attributes; metadata and lock are refused |
— |
set-style |
{ id, style: { 'typography.fontSize': '32px' } } |
Paths under the block's style |
Block paths are translated to CSS; CSS property names also work |
set-link |
{ id, href, target } |
button, navigation link | any link |
set-image |
{ id, url, attachmentId, alt, mediaType } |
image, cover, video, audio, Kadence image | image |
convert-to-video |
{ id, url, attachmentId, poster } |
An image (core or Kadence) becomes a muted, looping core/video; a cover switches its background to the video |
image → video |
set-responsive |
{ id, breakpoint: 'tablet' | 'mobile', path, value } |
claraVe.responsive |
— |
set-ornament |
{ id, pseudo: 'before' | 'after', props } |
claraVe.ornaments |
— |
set-motion |
{ id, entrance, hover } |
cve-anim-* / cve-hover-* classes |
— |
remove / duplicate |
{ id } |
Structural, respecting locks | — |
move |
{ id, direction: 'up' | 'down' } |
Structural, respecting locks | — |
group |
{ ids } |
Wraps a run of neighbouring siblings in one Group block (whatever getGroupingBlockName() is). Blocks with a gap between them, or in different containers, are refused |
— |
ungroup |
{ id } |
Replaces a Group with the blocks inside it, in its place | — |
move-to |
{ ids, target: { id, position: 'before' | 'after' | 'into', index? } } |
Moves a run of neighbouring siblings elsewhere, including into another container. A target inside the blocks being moved is refused | — |
insert-pattern |
{ pattern, id?, position?: 'before' | 'after' } |
One of the active theme's own sections, or one saved on this site (core/block/<id>) |
— |
Every write re-checks WordPress's editing mode and bindings at the moment it runs. Content-only blocks accept content attributes only; bound attributes are refused. An empty value removes a style or responsive value. Operations are applied in order, and a refusal does not stop the ones after it.
Responsive paths are the ones the renderer understands:
spacing.padding.{top,right,bottom,left}, spacing.margin.{top,bottom},
typography.fontSize, typography.textAlign, dimensions.minHeight and
display (none hides the block on that screen).
These wp.hooks filters run in the workspace:
| Filter | Value | Context |
|---|---|---|
clara_ve.popup.groups |
Array of { key, tab: 'content' | 'style' | 'section', title, open, render }; render() returns React elements |
{ block, attributes, mode, screen, registry, write, writeMany, canWrite, element, fields } |
clara_ve.popup.top |
Array of { key, label, title, onClick } shown as full-width buttons at the top of the popup's first tab |
{ block, attributes, mode, registry } |
clara_ve.popup.footer |
Array of { key, label, title, onClick } |
{ block, attributes, mode, registry } |
clara_ve.toolbar.extras |
Array of elements (each with a key) placed in the toolbar just before the save status |
{ registry, status, element } |
clara_ve.toolbar.more |
Array of { key, label, onClick } or { key, label, href, top } for the More menu |
{ registry, status } |
clara_ve.form.blocks |
Block names that always get the Form labels / fields / button style groups. Default [ 'core/shortcode', 'core/html', 'clara-ve/form' ]; blocks whose name contains form, and blocks showing form controls on the canvas, get them regardless |
block |
wp.hooks.addFilter( 'clara_ve.popup.groups', 'my-extension/ask', function ( groups, context ) {
return groups.concat( [ {
key: 'ask', tab: 'content', title: 'Ask',
render: function () { return [ context.element.createElement( 'p', { key: 'p' }, 'Selected: ' + context.block.name ) ]; }
} ] );
} );Docks on the right edge. History and any extension's side panel share the
right edge of the workspace. Whoever opens a dock dispatches
clara-ve-dock-opened on window with detail.dock naming it; every other
dock closes when it hears an event that is not its own. History uses
dock: 'history'.
window.dispatchEvent( new CustomEvent( 'clara-ve-dock-opened', { detail: { dock: 'my-panel' } } ) );
window.addEventListener( 'clara-ve-dock-opened', function ( event ) {
if ( event.detail.dock !== 'my-panel' ) { closeMyPanel(); }
} );ClaraVEValues.placePopup( rect, pointer, size, viewport ) is the placement
rule both popups use, for an extension that positions its own panel.
| Hook | Type | Arguments |
|---|---|---|
clara_ve_workspace_config |
filter | $config, $workspace — the claraVeGutenberg script configuration |
clara_ve_workspace_enqueue |
action | $workspace, $is_site, $mode (block, native or html) |
clara_ve_native_entity_saved |
action | $type, $id, $request — after a successful native save is versioned |
The canonical place is the block's own claraVe.responsive attribute. It
travels with copies, patterns and revisions, and it never changes the block's
markup, so a block cannot become invalid through it. The front end renders it
through a content-addressed class.
Older pages carry rules in the _clara_ve_responsive post meta keyed by a
cve-r-* anchor class. Those rules keep rendering and are read by both
editors. The workspace moves a block's rules into the attribute the first time
they are edited there; the front-end block editor writes into the attribute
whenever a block already has one. An extension that renders pages without
Visual Edit Lite active must emit claraVe.responsive itself, or those
small-screen values will not appear.