Actions and call context
Find action registration, arguments, context, resource attachments, and result contracts.
action: register an operation callable from the page
After binding successfully, use
plugin.bridge()
to obtain the bridge and register the action once. Only trusted local pages can call it; development-server and remote pages do not receive the bridge. See Call Python from the interface.
class WebBridge (Protocol):
action(self, name: 'str', handler: 'WebBridgeActionHandler | None' = None, *, invalidates: 'tuple[str, ...]' = (), permissions: 'tuple[str, ...]' = ('nexus_web.action.plugin.call',), state_guard_keys: 'tuple[str, ...]' = ()) -> 'WebBridgeActionHandler | Callable[[WebBridgeActionHandler], WebBridgeActionHandler]'
dispose(self) -> 'None'
@property event(self) -> 'WebBridgeEvent'
remove_action(self, name: 'str') -> 'None'
@property resource(self) -> 'WebBridgeResource'
@property state(self) -> 'WebBridgeState'| Parameter | Meaning and default behavior |
|---|---|
| name | Required. Use a plugin.* name such as plugin.my_addon.inspect, identical on both sides. Do not impersonate built-in blender.* operations. |
| handler | Handler function. Passing it registers and returns that function; omitting it returns a decorator. Registration does not return an action execution result. |
| invalidates | An empty tuple by default. After a successful protocol call, invalidates these plugin.* states in this client so subscription snapshots can be regenerated. |
| permissions | Includes nexus_web.action.plugin.call by default. To consume uploaded resources, also declare nexus_web.resource.write. This list replaces the defaults; permissions are not appended automatically. |
| state_guard_keys | An empty tuple by default. A nonempty value requires valid subscription snapshots for the specified plugin.* states. See state guards below. |
Registering the same action name twice fails. Do not register on every click or window opening. Remove the original registration before replacing its handler.
Handlers: input, return values, and execution context
WebBridgeActionHandler = collections.abc.Callable[[str | int | float | bool | None | list['WebJSONValue'] | dict[str, 'WebJSONValue'], nexus_web.public.WebBridgeContext], str | int | float | bool | None | list['WebJSONValue'] | dict[str, 'WebJSONValue']]
The function receives two positional arguments: (payload, context). The payload is JSON data from the page and is not necessarily a dictionary. Validate structure, types, and business limits before accessing data. context is not bpy.context. Callbacks run synchronously on Blender's main thread; do not return a coroutine or perform long blocking work.
WebJSONValue = str | int | float | bool | None | list['WebJSONValue'] | dict[str, 'WebJSONValue']View JSON scalar types
WebJSONScalar = str | int | float | bool | NoneResults may contain only strings, finite numbers, booleans, None, lists, and dictionaries with string keys. Do not return Blender objects, vectors, Path, bytes, sets, or coroutines. Pass binary content through Resource references. JSON null corresponds to Python None.
from .nexus_web import WebBridgeContext, WebJSONValue, WebPlugin
def echo_text(payload: WebJSONValue, context: WebBridgeContext) -> WebJSONValue:
if not isinstance(payload, dict):
return {"ok": False, "message": "请输入文本参数。"}
text = payload.get("text")
if not isinstance(text, str) or len(text) > 200:
return {"ok": False, "message": "文本须为 200 字以内的字符串。"}
return {"ok": True, "text": text}
def setup_actions(plugin: WebPlugin) -> None:
plugin.bridge().action("plugin.my_addon.echo", echo_text)
Replace my_addon with your add-on's name and update the page call to match. The decorator form is also available:
@bridge.action("plugin.my_addon.echo"). Choose one registration form; do not register the same action with both.
WebBridgeContext: where this call originated
class WebBridgeContext (dataclass frozen slots):
__init__(origin: 'str', user_initiated: 'bool' = True, window_key: 'str | None' = None, resource: 'WebBridgeActionResource' = <factory>) -> None
origin: str
user_initiated: bool = True
window_key: str | None = None
resource: WebBridgeActionResource = <factory: _unavailable_action_resource> # repr=False compare=False- origin: the page's source identifier for this call. Context information, not a user identity.
- user_initiated: a public context flag, True by default. It is not independent proof of application authorization or a physical mouse click.
- window_key: an optional real window key. Use it to identify the current instance in multi-window workflows instead of guessing from selection.
- resource: receives uploaded resources for this action. Ordinary application code does not construct it manually.
The default resource factory in the type definition cannot receive real resources. Use the context supplied by the framework, and do not retain it for another operation.
Distinguish application results from call failures
The page uses await webui.action.call(name, payload) to obtain the Python return value. A normal return of
{"ok": False}
is still a successful protocol call. Check the application result fields; configured invalidations may still run. Exceptions, missing registrations, insufficient permissions, or invalid connections are call failures.
async function echoFromClick(output) {
try {
const webui = window.acquireNexusWebUI();
const result = await webui.action.call("plugin.my_addon.echo", { text: "你好" });
output.textContent = result.ok ? result.text : result.message;
} catch (error) {
output.textContent = `调用失败:${error.message || String(error)}`;
}
}Actions do not guarantee Blender undo and do not automatically roll back changes made before a handler fails. Use your add-on's undo and error-handling design. For transform interactions, see Value dragging and undo.
State guards: avoid writing to the wrong object from a stale list
With state_guard_keys, page payloads must include stateKey, canonicalKey, subscriptionId, and snapshotRevision from the matching live subscription. Do not invent revision numbers or use a one-time query's stateRef as a custom subscription guard.
With resource-write permission, also include targetId, targetIds, or targetRef.id(s). Targets must belong to the guarded snapshot. If the snapshot is stale or targets do not match, the handler does not run. Refresh state and ask the user to confirm targets instead of removing guard fields to force a retry.
The add-on must still verify that write targets exist and meet type and application requirements. Guards do not replace business validation. For page-side fields, see Persistent snapshots and release.
Receive a one-time uploaded resource
class WebBridgeActionResource (Protocol):
take(self, resource_id: 'str') -> 'WebTakenResource'class WebTakenResource (dataclass frozen slots):
__init__(resource_id: 'str', path: 'Path', mime: 'str', byte_length: 'int') -> None
resource_id: str
path: Path
mime: str
byte_length: intAfter callWithResource uploads a resource, payload.resourceId is its receipt key. Call context.resource.take(resourceId) in the handler. Only actions with resource-write permission may consume uploads, and the resource must belong to the current action source. Arbitrary disk paths are not accepted.
A successful result contains resource_id, temporary path, mime, and byte_length. Each resource can be taken successfully once. The framework cleans up the temporary file after the handler ends. Read its content or write it to the application target inside the callback; do not pass its temporary path to a later task. Validate format, size, and targets. See Resources and file configuration.
Remove registrations and clean up
bridge.remove_action(name) Removes the specified action for this client and returns None. It does not undo completed application changes. bridge.dispose()
Clears this bridge's registrations. When disabling the whole add-on, use Client release flow rather than clearing the bridge and leaving windows behind.
Preserve actual Python registration errors and show understandable page-side call errors. Do not infer built-in versus custom action error categories by matching Chinese messages.