Window and page declarations
Find size, position, target-region, and page declarations. A declaration does not open a window.
Declaration and opening are separate steps
web
creates page, size, and position declarations without opening a window. The bound client's
plugin.open_window
requests display. Check declarations first, then open results. For the full introduction, see Window position, size, and visibility.
from .nexus_web import web
spec = web.window(
web.local_app("web/index.html"),
size=web.window_size((640, 420), minimum=(360, 260)),
title="我的工具",
placement=web.window_placement("top_right", margin=(24, 24)),
transparent=True,
)Displayed types describe exact argument/result structures, not a recommendation to import internal modules. Prefer web declaration functions rather than copying implementations from internal type paths.
Page sources: local, development server, and remote
def local_app(path: 'str', *, root: 'Path | str | None' = None, auto_reload: 'bool' = False) -> 'WebLocalApp'
path is an entry path relative to the resource root, such as web/index.html. root defaults to None, using the client default or add-on package directory on opening. Returns WebLocalApp; trusted local pages can use the bridge. Resources must respect local boundaries. See Local resources and page restrictions.
auto_reload=False Disabled by default. True watches the resource root during development and reloads the whole page after writes settle. This is not module hot replacement and has no fixed latency guarantee. It resets page memory, input, and scroll state. Point built frontends at runnable output.
def dev_server(url: 'str') -> 'WebDevServerApp'Accepts loopback HTTP/HTTPS URLs and returns WebDevServerApp without starting a server. These pages receive neither Python communication nor acquireNexusWebUI. Use for visual development, not action/state acceptance.
def remote_page(url: 'str') -> 'WebRemotePageApp'Accepts only HTTPS and returns WebRemotePageApp without a bridge. Remote pages and public browser navigation are frozen. This documents existing boundaries, not remote bridging or Blender control integration.
View all return-type fields
class WebLocalApp (dataclass frozen slots):
__init__(path: 'str', root: 'Path | str | None' = None, auto_reload: 'bool' = False) -> None
path: str
root: Path | str | None = None
auto_reload: bool = Falseclass WebDevServerApp (dataclass frozen slots):
__init__(url: 'str') -> None
url: strclass WebRemotePageApp (dataclass frozen slots):
__init__(url: 'str') -> None
url: strWebWindowApp = nexus_web.contracts.source.WebLocalApp | nexus_web.contracts.source.WebDevServerApp | nexus_web.contracts.source.WebRemotePageAppwindow_size: dimensions and constraints
def window_size(initial: 'tuple[int, int]', *, minimum: 'tuple[int, int] | None' = None, maximum: 'tuple[int, int] | None' = None, resizable: 'bool' = False, auto_fit: 'bool' = False) -> 'WebWindowSize'initial is required as positive integer (width, height), each at most 4096. minimum and maximum share that cap, default to None, and must be consistent with the initial size and each other. resizable defaults to False; only explicit True enables gestures on all edges and corners. Returns WebWindowSize.
Resize handles cover the entire rectangle, including transparent margins. Bounds constrain the viewport; equal bounds lock an axis. The framework changes available space in the same page without reloading or scaling the whole page. The page owns text and button sizes and responsive layout. Reopening uses initial dimensions. Old embedded copies remain non-resizable; authors must upgrade and opt in.
View all return-type fields
class WebWindowSize (dataclass frozen slots):
__init__(initial: 'tuple[int, int]', minimum: 'tuple[int, int] | None' = None, maximum: 'tuple[int, int] | None' = None, resizable: 'bool' = False, auto_fit: 'bool' = False) -> None
initial: tuple[int, int]
minimum: tuple[int, int] | None = None
maximum: tuple[int, int] | None = None
resizable: bool = False
auto_fit: bool = FalseInitial position: anchors and exact coordinates
def window_placement(placement: 'str', *, margin: 'int | float | tuple[int | float, int | float]' = 0, offset: 'tuple[int | float, int | float]' = (0, 0), padding: 'int | float' = 8, overflow: 'WebWindowOverflowMode' = 'shift', stick: 'bool' = False) -> 'WebWindowPlacement'| Parameter | Defaults and meaning |
|---|---|
| placement | Required: top_left, top, top_right, left, center, right, bottom_left, bottom, or bottom_right, corresponding to nine anchors. |
| margin | 0; nonnegative space from the relevant edges. A scalar applies to both axes; (x,y) is also accepted. |
| offset | (0,0); finite per-axis offsets from the anchored position. |
| padding | 8; nonnegative safety padding in host UI units for shift overflow correction. |
| overflow | shift moves out-of-bounds positions back into the region; none preserves them. |
| stick | False; must be boolean. Currently stored without a promise of continuous edge sticking or automatic reflow. |
def window_position(x: 'int | float', y: 'int | float', *, padding: 'int | float' = 8, overflow: 'WebWindowOverflowMode' = 'shift', stick: 'bool' = False) -> 'WebWindowPlacement'x/y are local coordinates in the host editor content region, not global screen or CSS coordinates. padding, overflow, and stick have the same meaning above. Both functions return WebWindowPlacement.
Placement applies only on opening; dragging preserves manual position. Region changes may vertically adjust the header for reachability without recentering or scaling content. overflow='none' preserves out-of-bounds behavior. Without an explicit target or usable region rectangle, placement is not resolved; arbitrary background contexts are not normal opening environments.
View all return-type fields
class WebWindowPlacement (dataclass frozen slots):
__init__(mode: 'WebWindowPlacementMode', placement: 'WebWindowPlacementName | None' = None, position: 'tuple[float, float] | None' = None, container: 'WebWindowPlacementContainer' = 'area', margin: 'tuple[float, float]' = (0.0, 0.0), offset: 'tuple[float, float]' = (0.0, 0.0), padding: 'float' = 8.0, overflow: 'WebWindowOverflowMode' = 'shift', stick: 'bool' = False) -> None
mode: WebWindowPlacementMode
placement: WebWindowPlacementName | None = None
position: tuple[float, float] | None = None
container: WebWindowPlacementContainer = 'area'
margin: tuple[float, float] = (0.0, 0.0)
offset: tuple[float, float] = (0.0, 0.0)
padding: float = 8.0
overflow: WebWindowOverflowMode = 'shift'
stick: bool = Falsewindow: combine declarations
def window(app: 'WebWindowApp', *, size: 'WebWindowSize', title: 'str | None' = None, placement: 'WebWindowPlacement | None' = None, browser_state: 'WebBrowserStateSpec | None' = None, transparent: 'bool | None' = None, external_files: 'WebExternalFilesSpec | None' = None, resource_io: 'WebResourceIOSpec | None' = None, debug_label: 'str | None' = None) -> 'WebWindowSpec'| Parameter | Purpose |
|---|---|
| app, size | Required values from page-source and window_size functions, not a URL string with an arbitrary dictionary. |
| title | Optional title, default None. |
| placement | Optional placement, default None. Supply it for an explicit initial position. |
| browser_state | Optional browser storage scope, defaulting to plugin scope; see below. |
| transparent | Defaults to None, allowing the local page hint to decide. Explicit True/False takes precedence. Visual transparency and click-through are separate; also configure Page interaction markers. |
| external_files, resource_io | Default None, mutually exclusive, and only for local-page user file interaction. These are declarations, not file paths. See File import and File export; for tools, see Page file-tool reference. |
| debug_label | Optional diagnostic label, default None. Does not determine window keys or add-on identity. |
Returns WebWindowSpec. Invalid pages, dimensions, placement, or conflicting file policies fail. Fix declarations before opening; ignoring exceptions does not yield a partially valid window.
View all return-type fields
class WebWindowSpec (dataclass frozen slots):
__init__(app: 'WebWindowApp', size: 'WebWindowSize', title: 'str | None' = None, placement: 'WebWindowPlacement | None' = None, browser_state: 'WebBrowserStateSpec | None' = None, transparent: 'bool | None' = None, external_files: 'WebExternalFilesSpec | None' = None, resource_io: 'WebResourceIOSpec | None' = None, debug_label: 'str | None' = None) -> None
app: WebWindowApp
size: WebWindowSize
title: str | None = None
placement: WebWindowPlacement | None = None
browser_state: WebBrowserStateSpec | None = None
transparent: bool | None = None
external_files: WebExternalFilesSpec | None = None
resource_io: WebResourceIOSpec | None = None
debug_label: str | None = Nonebrowser_state: browser storage scope
def browser_state(kind: 'WebBrowserStateKind' = 'plugin', name: 'str | None' = None) -> 'WebBrowserStateSpec'Returns WebBrowserStateSpec. kind defaults to plugin and controls cookie, localStorage, and cache reuse. It does not widen resource, action, or page permissions.
plugin: shared within the add-on (default).client: separate per client.-
window: separate per real window instance, useful for multi-region isolation. -
ephemeral: temporary browser state, not persistent named storage. -
named: an explicit named scope within one add-on, requiring nonempty name. Other kinds reject name.
View all return-type fields
class WebBrowserStateSpec (dataclass frozen slots):
__init__(kind: 'WebBrowserStateKind' = 'plugin', name: 'str | None' = None) -> None
kind: WebBrowserStateKind = 'plugin'
name: str | None = Nonearea_target: choose editor regions
def area_target(scope: 'WebAreaScope' = 'current', *, area_type: 'str | None' = None, space_type: 'str | None' = None, tree_type: 'str | None' = None, existing: 'WebExistingPolicy' = 'error') -> 'WebAreaTarget'Returns WebAreaTarget for open_window's target, not the window declaration. scope defaults to current for the context region; same_type matches visible, bindable regions of the same type across windows in this Blender process.
area_type, space_type, and tree_type default to None. For same_type, omitted filters are inferred from the current region. Omitting tree_type in the shader editor does not match all node trees. There is no special value to explicitly disable one inferred filter.
existing defaults to error on conflicts. reuse reuses a surviving instance only for the same client, logical key, and region. With target, keep replace_existing False and use existing rather than combining strategies.
View all return-type fields
class WebAreaTarget (dataclass frozen slots):
__init__(scope: 'WebAreaScope' = 'current', area_type: 'str | None' = None, space_type: 'str | None' = None, tree_type: 'str | None' = None, existing: 'WebExistingPolicy' = 'error') -> None
scope: WebAreaScope = 'current'
area_type: str | None = None
space_type: str | None = None
tree_type: str | None = None
existing: WebExistingPolicy = 'error'open_window: open and inspect results
plugin.open_window(window_key, spec, *, target=None,
replace_existing=False)
Returns WebWindowOpenResult. window_key must be stable and nonempty. Without target, an existing key fails by default. replace_existing=True replaces only this client's window and is not a toggle.
result = plugin.open_window("main", spec)
if not result.ok:
self.report({"ERROR"}, result.message or "窗口未能打开")
if result.hint:
self.report({"WARNING"}, result.hint)
return {"CANCELLED"}
return {"FINISHED"}Success means required host entry points were activated; verify actual page scripts and application state separately. Failed multi-instance preflight opens none. Failure after successful preflight can leave already opened instances, so overall failure does not imply no windows exist.
View all return-type fields
class WebWindowOpenResult (dataclass frozen slots):
__init__(ok: 'bool', window_key: 'str', state: 'WebWindowState | None', error: 'WebErrorKind | None' = None, message: 'str' = '', hint: 'str | None' = None, logical_window_key: 'str | None' = None, instances: 'tuple[WebWindowInstance, ...]' = ()) -> None
ok: bool
window_key: str
state: WebWindowState | None
error: WebErrorKind | None = None
message: str = ''
hint: str | None = None
logical_window_key: str | None = None
instances: tuple[WebWindowInstance, ...] = ()class WebWindowInstance (dataclass frozen slots):
__init__(ok: 'bool', logical_window_key: 'str', window_key: 'str', state: 'WebWindowState | None', target_index: 'int', target: 'WebAreaSummary | None', action: 'WebWindowInstanceAction | None' = None, error: 'WebErrorKind | None' = None, message: 'str' = '') -> None
ok: bool
logical_window_key: str
window_key: str
state: WebWindowState | None
target_index: int
target: WebAreaSummary | None
action: WebWindowInstanceAction | None = None
error: WebErrorKind | None = None
message: str = ''class WebAreaSummary (dataclass frozen slots):
__init__(area_type: 'str | None', space_type: 'str | None', tree_type: 'str | None', is_current: 'bool' = False) -> None
area_type: str | None
space_type: str | None
tree_type: str | None
is_current: bool = False-
ok: true only if all instances succeed; do not inspect only the primary state. -
window_key / state: real key and state of the primary instance;logical_window_keyis the logical group key. -
error / message / hint: failure category, message, and optional hint. Multi-instance summaries use the first failed instance. -
instances: per-instance ok, real key, state, target_index, and target summary. action may be opened, reused, or None. Inspect each result; state is not guaranteed on every item. -
targetThe target summary includes area/space/tree types and is_current, not a Blender region object.
Use returned real keys for multi-instance records. Do not construct them or assume restart stability. For complete toggle behavior, read Choose how users open the interface.
close_window and close_windows
plugin.close_window(window_key, *, reason="window_close")
closes one real window; plugin.close_windows(logical_window_key, *,
reason="window_close")
closes the client's logical group. reason is a string normally left at its default. Both return WebWindowCloseResult.
View all return-type fields
class WebWindowCloseResult (dataclass frozen slots):
__init__(ok: 'bool', window_key: 'str', closed: 'bool', error: 'WebErrorKind | None' = None, message: 'str' = '', hint: 'str | None' = None, logical_window_key: 'str | None' = None, instances: 'tuple[WebWindowCloseInstance, ...]' = ()) -> None
ok: bool
window_key: str
closed: bool
error: WebErrorKind | None = None
message: str = ''
hint: str | None = None
logical_window_key: str | None = None
instances: tuple[WebWindowCloseInstance, ...] = ()class WebWindowCloseInstance (dataclass frozen slots):
__init__(ok: 'bool', logical_window_key: 'str', window_key: 'str', closed: 'bool', error: 'WebErrorKind | None' = None, message: 'str' = '', hint: 'str | None' = None) -> None
ok: bool
logical_window_key: str
window_key: str
closed: bool
error: WebErrorKind | None = None
message: str = ''
hint: str | None = None
Check ok before closed. ok=True, closed=False
may mean no registered window exists to close; ok=False
means closing failed. Do not treat it as not open and create another window. For groups, inspect each instance's ok, closed, error, message, and hint to catch partial failures.
Client ownership limits window keys. close_window neither interprets a group key as close-all nor closes other clients' windows. Closing does not dispose the whole client; disabling still requires Client cleanup.
Add-on image dragging
plugin.begin_drag(window_key, preview=..., mode="release") starts an image drag from a real window owned by this client. preview is a WebDragPreview; its local image is copied at the start. size and offset use CSS pixels, and opacity ranges from 0 to 1.
View all return-type fields
class WebDragPreview (dataclass frozen slots):
__init__(path: 'Path | str', size: 'tuple[float, float]' = (180.0, 180.0), opacity: 'float' = 0.65, offset: 'tuple[float, float]' = (18.0, -18.0)) -> None
path: Path | str
size: tuple[float, float] = (180.0, 180.0)
opacity: float = 0.65
offset: tuple[float, float] = (18.0, -18.0)class WebDragState (dataclass frozen slots):
__init__(drag_id: 'str', phase: "Literal['dragging', 'dropped', 'cancelled']", target: 'WebDragTarget | None' = None, reason: 'str' = '') -> None
drag_id: str
phase: Literal['dragging', 'dropped', 'cancelled']
target: WebDragTarget | None = None
reason: str = ''class WebDragTarget (dataclass frozen slots):
__init__(window_pointer: 'int', area_pointer: 'int', area_type: 'str', x: 'float', y: 'float', over_web: 'bool' = False, window_key: 'str | None' = None, web_x: 'float | None' = None, web_y: 'float | None' = None, region_pointer: 'int | None' = None) -> None
window_pointer: int
area_pointer: int
area_type: str
x: float
y: float
over_web: bool = False
window_key: str | None = None
web_x: float | None = None
web_y: float | None = None
region_pointer: int | None = Nonerelease mode continues a left-button press that started in this Web window and is still held. It ends on release; starting after asynchronous preparation is rejected if the button is already released. click mode ends on the next left-button press. Esc, right-click, or window deactivation cancels it. Dragging stays within the original Blender window; cross-window and desktop dragging are not promised.
begin_drag returns WebDragState. Inspect it with plugin.drag_state(drag_id) or cancel it with plugin.cancel_drag(drag_id). phase is dragging, dropped, or cancelled; unknown or cleared identifiers return None. dropped means the gesture ended. Target acceptance, data copying, and Blender changes remain the add-on's responsibility.
target may be absent. x/y use physical pixels in the target editor content area, with a bottom-left origin. web_x/web_y use CSS pixels with a top-left origin. over_web indicates a Web hit; window_key and Web coordinates are provided only for this client's own Web windows. Window and area pointers are transient and must not be persisted.
Only one such drag can be active in the process. Each client retains its active or most recent terminal state, replaced by the next drag. Handle a terminal state once and stop polling to avoid duplicate business objects. Closing the owning window, reloading the host, switching files, or disposing the client cancels it and releases the preview. This API is separate from numeric page transforms and external file imports.
WebBrowser and window state
plugin.browser(window_key) Returns WebBrowser for a real window key. Obtaining controls does not prove the window exists; calls may still fail.
class WebBrowser (dataclass frozen slots):
back(self) -> 'WebWindowState'
forward(self) -> 'WebWindowState'
navigate(self, url: 'str') -> 'WebWindowState'
reload(self) -> 'WebWindowState'
state(self) -> 'WebWindowState'
stop(self) -> 'WebWindowState'state reads a snapshot; back/forward navigate history; reload reloads the page; stop stops loading rather than closing. navigate accepts a URL, but public navigation is frozen and is not a new remote-integration entry point.
class WebWindowState (dataclass frozen slots):
__init__(status: 'str', url: 'str | None', title: 'str | None', loading: 'bool', can_go_back: 'bool', can_go_forward: 'bool', error: 'WebErrorKind | None', message: 'str', hint: 'str | None' = None) -> None
status: str
url: str | None
title: str | None
loading: bool
can_go_back: bool
can_go_forward: bool
error: WebErrorKind | None
message: str
hint: str | None = NoneAll methods return WebWindowState: status is a string; url/title may be None; loading indicates loading; can_go_back/can_go_forward describe history; error/message/hint describe failure. State reflects received events, not a promise that every script finished.
Browser controls may raise WebOperationError; also inspect error in returned state. For complete client signatures, see Framework entry points and lifecycle. Do not replace public state with internal session access.