Frontend adapters and existing helpers
Explore the repository's existing frontend adapters and file-interaction helpers, including their requirements.
Adapters connect state; they are not component libraries
The framework does not bundle React, Vue, Svelte, or Fabric. Supply third-party libraries through your own build, then load the matching helpers. All three state adapters use the same state.store. They do not turn an ordinary browser into Blender or automatically convert web nodes into Blender nodes.
React: read state snapshots in components
export interface NexusWebUIReactModule {
createWebUIHooks(react: ReactLikeForWebUI, webui?: NexusWebUI): NexusWebUIReactHooks;
}export interface NexusWebUIReactHooks {
useWebUIState<TItem = Record<string, WebUIJSONValue>>(
key: string,
query?: WebUIStateQuery | null,
options?: WebUIStoreOptions & { shape?: 'list' },
): WebUIListStoreSnapshot<TItem>;
useWebUIState<TItem = Record<string, WebUIJSONValue>>(
key: string,
query: WebUIStateQuery | null | undefined,
options: WebUIStoreOptions & { shape: 'item' },
): WebUIItemStoreSnapshot<TItem>;
}createWebUIHooks(React, webui?) requires useSyncExternalStore from React 18 or later. useWebUIState(key, query?, options?) returns a snapshot and defaults to list shape; specify shape: 'item' for detail state. The adapter creates the store when subscribing and releases it on unmount, avoiding render-time side effects. Changes to key/query/options content rebuild the subscription.
const { useWebUIState } = NexusWebUIReact.createWebUIHooks(React);
function ObjectCount() {
const state = useWebUIState("blender.objects", { fields: ["id", "name"] });
if (state.loading) return React.createElement("p", null, "加载中");
if (state.error) return React.createElement("p", null, state.error.message);
return React.createElement("p", null, "当前列表:" + state.items.length);
}Lists may be truncated. items.length is not necessarily the scene's total object count; inspect the snapshot fields for more results.
Vue: shallow reactive snapshots and cleanup
export interface NexusWebUIVueModule {
createWebUIComposables(vue: VueLikeForWebUI, webui?: NexusWebUI): NexusWebUIVueComposables;
}export interface NexusWebUIVueComposables {
useWebUIState<TItem = Record<string, WebUIJSONValue>>(
key: string,
query?: WebUIStateQuery | null,
options?: WebUIStoreOptions & { shape?: 'list' },
): NexusWebUIVueState<WebUIListStoreSnapshot<TItem>>;
useWebUIState<TItem = Record<string, WebUIJSONValue>>(
key: string,
query: WebUIStateQuery | null | undefined,
options: WebUIStoreOptions & { shape: 'item' },
): NexusWebUIVueState<WebUIItemStoreSnapshot<TItem>>;
}export interface NexusWebUIVueState<TSnapshot extends WebUIStoreSnapshotBase = WebUIStoreSnapshot> {
snapshot: { value: TSnapshot };
store: WebUIStateStore<TSnapshot>;
dispose(): void;
}createWebUIComposables(Vue, webui?) uses Vue 3 shallowRef. useWebUIState returns snapshot, store, and dispose. Read snapshot.value in JavaScript; templates unwrap it according to Vue rules. Cleanup is automatic inside a component scope; outside one, call dispose yourself. Later mutations to a query object are not observed automatically. Recreate the store and dispose the old one when changing the query.
Svelte: subscriptions and resource release are separate
export interface NexusWebUISvelteModule {
createWebUIStores(webui?: NexusWebUI): NexusWebUISvelteStores;
}export interface NexusWebUISvelteStores {
stateStore<TItem = Record<string, WebUIJSONValue>>(
key: string,
query?: WebUIStateQuery | null,
options?: WebUIStoreOptions & { shape?: 'list' },
): NexusWebUISvelteStore<WebUIListStoreSnapshot<TItem>>;
stateStore<TItem = Record<string, WebUIJSONValue>>(
key: string,
query: WebUIStateQuery | null | undefined,
options: WebUIStoreOptions & { shape: 'item' },
): NexusWebUISvelteStore<WebUIItemStoreSnapshot<TItem>>;
}export interface NexusWebUISvelteStore<TSnapshot extends WebUIStoreSnapshotBase = WebUIStoreSnapshot> {
subscribe(run: (snapshot: TSnapshot) => void): () => void;
getSnapshot(): TSnapshot;
dispose(): void;
}stateStore from createWebUIStores(webui?) returns a Svelte-compatible subscribable without requiring a framework namespace. Subscribers immediately receive the current snapshot. Removing the last view subscription does not destroy the underlying host store; call dispose when its owning component is destroyed.
Files and complex canvas adapters
export interface NexusResourceAdapter {
id: string;
/** Higher runs first. Built-ins: canvas=10, DOM default=0. User adapters default to 50. */
priority?: number;
canHandle(ctx: NexusResourceAdapterContext): boolean;
import(ctx: NexusResourceAdapterContext): void | Promise<void>;
export?(ctx: NexusResourceAdapterExportContext): Promise<{ blob: Blob; mime: string; suggestedName?: string }>;
dispose?(): void;
}export interface NexusResourceAdapterContext {
element: Element;
importEvent: NexusImportEvent;
point: NexusImportPoint;
placement: NexusImportPlacement;
webui: NexusWebUI;
/** Converts a page CSS point (defaults to placement.fallbackPoint) into target-element-local coordinates. */
localPoint(target?: Element): NexusImportPoint;
}export interface NexusResourceAdapterHandle {
unregister(): void;
}export interface NexusResourceAdapterFabricModule {
readonly NEXUS_RESOURCE_ADAPTER_FABRIC_VERSION: string;
readonly version: string;
readonly ADAPTED_LIBRARY: 'fabric';
readonly ADAPTED_LIBRARY_VERSION_RANGE: string;
create(options: NexusResourceAdapterFabricOptions): NexusResourceAdapter;
}export interface NexusResourceAdapterFabricOptions {
canvas: NexusFabricCanvasLike;
fabric?: NexusFabricNamespaceLike;
id?: string;
priority?: number;
}registerAdapter returns an asynchronous registration result. Save its handle and call unregister on unmount. canHandle identifies owned content; import converts events to the component's data model; export returns a Blob and MIME type; dispose releases resources. Adding an img beside a complex canvas does not import into that component.
| Item | Usage and limitations |
|---|---|
| priority | Higher values run first. Custom adapters default to 50; built-in canvas uses 10 and DOM uses 0. Make canHandle match only your component. |
| Coordinates | ctx.point uses CSS page coordinates. localPoint converts to element-local coordinates; account for the component's own zoom and pan using its coordinate system. |
| Fabric | Supply the canvas and matching fabric namespace, call create, and pass the adapter to resourceIO.registerAdapter. Supported versions are defined by ADAPTED_LIBRARY_VERSION_RANGE; do not assume all versions work. |
| Standard interception | NexusFileInputTakeover and NexusDownloadTakeover both provide enable/disable and enable on load. Disabling stops only this interception layer; it does not revoke Python permissions. |
Maintain one lifecycle
Do not maintain the same state through both an adapter and manual state.subscribe, or leave old DOM handlers after hot reload. Adapters consume snapshots; use the corresponding public APIs for actions, resources, and application logic. See State stores, Resource and file tools, Frontend integration guide.