Search
Loading...
Skip to content

Set Editing Constraints

Control what users can edit in templates by setting fine-grained permissions on individual blocks or globally across your scene using CE.SDK’s Scope system in headless Node.js environments.

15 mins
estimated time
Download
StackBlitz
GitHub

Editing constraints in CE.SDK allow you to lock specific properties of design elements programmatically from your server or Node.js application. The Scope system provides granular control over 20+ editing capabilities including movement, resizing, rotation, fill changes, text editing, and lifecycle operations.

This guide demonstrates how to apply editing constraints in a headless environment to create templates that can be adopted and used with predefined limitations, ensuring brand consistency and design integrity.

Understanding Scopes#

What are Scopes?#

A scope is a permission key that controls a specific editing capability in CE.SDK. Each scope represents a distinct action users can perform, such as moving blocks ('layer/move'), changing fills ('fill/change'), or editing text content ('text/edit'). By enabling or disabling scopes, you control exactly what users can and cannot do with each design element.

Scopes exist at two levels:

  • Block-level scopes: Per-block permissions set using setScopeEnabled()
  • Global scopes: Default behavior for all blocks set using setGlobalScope()

Available Scope Categories#

CE.SDK provides scopes organized into logical categories:

CategoryPurposeExample Scopes
Text EditingControl text content and formattingtext/edit, text/character
Fill & StrokeManage colors and gradientsfill/change, fill/changeType, stroke/change
ShapeModify shape propertiesshape/change
Layer TransformControl position and dimensionslayer/move, layer/resize, layer/rotate, layer/flip, layer/crop
Layer AppearanceManage visual propertieslayer/opacity, layer/blendMode, layer/visibility
Effects & FiltersApply visual effectsappearance/adjustments, appearance/filter, appearance/effect, appearance/blur, appearance/shadow
LifecycleControl creation and deletionlifecycle/destroy, lifecycle/duplicate
EditorManage scene-level actionseditor/add, editor/select

Scope Configuration#

Global Scope Modes#

Global scopes set the default behavior for all blocks in the scene. They have three modes:

  • Allow: Always allow the action, overriding block-level settings
  • Deny: Always deny the action, overriding block-level settings
  • Defer: Use block-level settings (default mode)

To ensure block-level scope settings are respected, set relevant global scopes to ‘Defer’. Without setting global scopes to ‘Defer’, default ‘Allow’ settings might override your block-level restrictions. This is essential when applying fine-grained constraints.

Scope Resolution Priority#

When both global and block-level scopes are set, they resolve in this order:

  1. Global Deny takes highest priority (action always denied)
  2. Global Allow takes second priority (action always allowed)
  3. Global Defer defers to block-level settings (default behavior)

Setting Block-Level Constraints#

Locking Position#

Prevent users from moving or repositioning a block while allowing other edits:

const disableMoveScope = (block: number) => {
// Disable move scope
engine.block.setScopeEnabled(block, 'layer/move', false);
// Explicitly enable other transform scopes
engine.block.setScopeEnabled(block, 'layer/resize', true);
engine.block.setScopeEnabled(block, 'layer/rotate', true);
engine.block.setScopeEnabled(block, 'layer/flip', true);
// Explicitly enable lifecycle scopes
engine.block.setScopeEnabled(block, 'lifecycle/destroy', true);
engine.block.setScopeEnabled(block, 'lifecycle/duplicate', true);
};
const moveLockedBlock = createExampleBlock(
'Locked\nposition',
{
r: 0.5,
g: 0.75,
b: 0.9,
},
disableMoveScope
);
// Block position is locked - users cannot move or reposition it
// Other scopes are explicitly enabled: resizing, rotation, flipping, deletion, duplication

The block position is locked—users cannot move or reposition it. Other scopes remain enabled, allowing resizing, editing, and deletion. This pattern maintains layout integrity while allowing content updates.

Preventing Deletion#

Protect blocks from being deleted or duplicated:

const disableLifecycleScopes = (block: number) => {
// Disable lifecycle scopes
engine.block.setScopeEnabled(block, 'lifecycle/destroy', false);
engine.block.setScopeEnabled(block, 'lifecycle/duplicate', false);
// Explicitly enable transform scopes
engine.block.setScopeEnabled(block, 'layer/move', true);
engine.block.setScopeEnabled(block, 'layer/resize', true);
engine.block.setScopeEnabled(block, 'layer/rotate', true);
engine.block.setScopeEnabled(block, 'layer/flip', true);
};
const lifecycleLockedBlock = createExampleBlock(
'Cannot\ndelete',
{
r: 0.75,
g: 0.75,
b: 0.75,
},
disableLifecycleScopes
);
// Block cannot be deleted or duplicated
// Other scopes are explicitly enabled: moving, resizing, rotation, flipping

Users cannot delete or duplicate the block but can still move, resize, and edit it. Use this for essential template elements that must remain present.

Enabling All Scopes#

Allow complete editing freedom by explicitly enabling all scopes:

const enableAllScopes = (block: number) => {
// Explicitly enable all transform scopes
engine.block.setScopeEnabled(block, 'layer/move', true);
engine.block.setScopeEnabled(block, 'layer/resize', true);
engine.block.setScopeEnabled(block, 'layer/rotate', true);
engine.block.setScopeEnabled(block, 'layer/flip', true);
// Explicitly enable lifecycle scopes
engine.block.setScopeEnabled(block, 'lifecycle/destroy', true);
engine.block.setScopeEnabled(block, 'lifecycle/duplicate', true);
// Explicitly enable fill scopes
engine.block.setScopeEnabled(block, 'fill/change', true);
engine.block.setScopeEnabled(block, 'fill/changeType', true);
// Explicitly enable text scopes
engine.block.setScopeEnabled(block, 'text/edit', true);
engine.block.setScopeEnabled(block, 'text/character', true);
};
const fullyEditableBlock = createExampleBlock(
'Fully\neditable',
{
r: 0.5,
g: 0.85,
b: 0.5,
},
enableAllScopes
);
// All scopes are explicitly enabled - users have full editing capabilities
// This is the default behavior, but explicitly enabling shows clear intent

All scopes are explicitly enabled, providing users with full editing capabilities. While this is the default behavior, explicitly enabling shows clear intent.

Disabling All Scopes#

Create completely locked blocks by disabling all editing capabilities:

const disableAllScopes = (block: number) => {
// Disable all transform scopes
engine.block.setScopeEnabled(block, 'layer/move', false);
engine.block.setScopeEnabled(block, 'layer/resize', false);
engine.block.setScopeEnabled(block, 'layer/rotate', false);
engine.block.setScopeEnabled(block, 'layer/flip', false);
engine.block.setScopeEnabled(block, 'layer/crop', false);
// Disable lifecycle scopes
engine.block.setScopeEnabled(block, 'lifecycle/destroy', false);
engine.block.setScopeEnabled(block, 'lifecycle/duplicate', false);
// Disable fill scopes
engine.block.setScopeEnabled(block, 'fill/change', false);
engine.block.setScopeEnabled(block, 'fill/changeType', false);
engine.block.setScopeEnabled(block, 'stroke/change', false);
// Disable text scopes
engine.block.setScopeEnabled(block, 'text/edit', false);
engine.block.setScopeEnabled(block, 'text/character', false);
// Disable shape scopes
engine.block.setScopeEnabled(block, 'shape/change', false);
// Disable editor scopes
engine.block.setScopeEnabled(block, 'editor/select', false);
// Disable appearance scopes
engine.block.setScopeEnabled(block, 'layer/opacity', false);
engine.block.setScopeEnabled(block, 'layer/blendMode', false);
engine.block.setScopeEnabled(block, 'layer/visibility', false);
};
const fullyLockedBlock = createExampleBlock(
'Fully\nlocked',
{
r: 0.9,
g: 0.5,
b: 0.5,
},
disableAllScopes
);
// All scopes are disabled - block is completely locked and cannot be edited
// Useful for watermarks, logos, or legal disclaimers

All scopes are disabled, making the block completely locked and uneditable. Useful for watermarks, logos, or legal disclaimers that must remain unchanged.

Configuring Global Scopes#

Setting Global Scope Modes#

Configure global scopes to control default behavior across all blocks:

// Set global scopes to 'Defer' to respect block-level scope settings
// Without this, global 'Allow' settings might override block-level restrictions
engine.editor.setGlobalScope('layer/move', 'Defer');
engine.editor.setGlobalScope('layer/resize', 'Defer');
engine.editor.setGlobalScope('lifecycle/destroy', 'Defer');
engine.editor.setGlobalScope('lifecycle/duplicate', 'Defer');
// Global scope modes:
// - 'Allow': Always allow (overrides block-level settings)
// - 'Deny': Always deny (overrides block-level settings)
// - 'Defer': Use block-level settings (respects setScopeEnabled)

Setting global scopes to ‘Defer’ ensures block-level scope settings are respected. Without this, default ‘Allow’ settings might override your block-level restrictions.

Checking Scope State#

Checking Block-Level Scopes#

Query the current state of any scope for a block:

// Check if a scope is enabled for a specific block
const canMove = engine.block.isScopeEnabled(moveLockedBlock, 'layer/move');
const canDelete = engine.block.isScopeEnabled(
lifecycleLockedBlock,
'lifecycle/destroy'
);
const canEditFully = engine.block.isScopeEnabled(
fullyEditableBlock,
'layer/move'
);
const canEditLocked = engine.block.isScopeEnabled(
fullyLockedBlock,
'layer/move'
);
// eslint-disable-next-line no-console
console.log('Move-locked block - layer/move enabled:', canMove); // false
// eslint-disable-next-line no-console
console.log('Lifecycle-locked block - lifecycle/destroy enabled:', canDelete); // false
// eslint-disable-next-line no-console
console.log('Fully editable block - layer/move enabled:', canEditFully); // true
// eslint-disable-next-line no-console
console.log('Fully locked block - layer/move enabled:', canEditLocked); // false

Use isScopeEnabled() to check the block-level setting. This returns whether the scope is enabled at the block level, but doesn’t consider global scope settings.

Checking Effective Permissions#

Check the effective permission considering both block and global settings:

// Check if scope is allowed (considers global + block settings)
const moveAllowed = engine.block.isAllowedByScope(block, 'layer/move');

isAllowedByScope() returns the final permission after resolving block-level and global scope settings. Use this when you need to know if an action is actually permitted.

Saving Constrained Scenes#

Scope constraints are preserved when saving scenes:

// Save the scene with all scope constraints preserved
const outputDir = './output';
if (!existsSync(outputDir)) {
mkdirSync(outputDir, { recursive: true });
}
// Save scene to file
const sceneData = await engine.scene.saveToString();
writeFileSync(`${outputDir}/constrained-scene.scene`, sceneData);
// eslint-disable-next-line no-console
console.log('✓ Scene saved to output/constrained-scene.scene');
// eslint-disable-next-line no-console
console.log(' All scope constraints are preserved in the scene file');

When this scene is loaded in a browser or client application, all scope constraints will be maintained. This allows you to:

  1. Create templates server-side with predefined constraints
  2. Save them to your storage/database
  3. Load them client-side with constraints already applied
  4. Ensure users can only edit allowed properties

API Reference#

MethodDescription
engine.block.setScopeEnabled()Enable or disable a scope for a specific block
engine.block.isScopeEnabled()Check if a scope is enabled at the block level
engine.block.isAllowedByScope()Check if a scope is allowed considering both block and global settings
engine.editor.setGlobalScope()Set global scope policy ('Allow', 'Deny', or 'Defer')
engine.editor.findAllScopes()List all available scope keys