Unstable: proposed services (@witwin/host/proposed) are for system Plugins and for Plugins loaded from a local path while you develop. They can change in any release and an installed Plugin cannot use them, so build a Plugin you release on the stable services only.
What a web half sees. How to use it: Web API. Source: studio/packages/witwin-host/src/index.ts (stable) and proposed.ts (proposed); ledger: dev_docs/frontend/HOST_API_PROPOSALS.md.
- Stable services (8):
scene, commands, contextKeys, notifications, opener, events, resources, rpc.
- Proposed services (27):
selection, history, fs, localFiles, platform, connection, panels, settings, slots, sceneEditor, parameters, runs, agent, results, arrays, timeline, agentChat, sceneView, tools, meshAssets, studies, thumbnails, assetImport, sceneFiles, components, componentForm, gallery. A system Plugin may inject them; a third-party Plugin only from a local path and only the names in its enabledApiProposals.
| Field | What it does |
|---|
id: string | Must equal the manifest id. |
inject?: readonly string[] | Services that must exist before activate runs. |
dependencies?: readonly PluginDependency[] | Plugins this one needs. |
slots?: readonly string[] | Slots this Plugin declares, so a dependent's entries can wait for them (§5.1). |
replaces?: readonly string[] | What this Plugin replaces (§5.7): a Plugin id ("studio.console") stays off while this one is active; "<slot id>:<key>" takes that one entry ("properties.inspector:component:transform"). |
hosts?: readonly PluginHostKind[] | Hosts this web half runs on; omitted means every host. |
activate(host: FrontendHost): void | Promise<void> | |
deactivate?(): void | Promise<void> | |
| Member | What it does |
|---|
readonly version: string | Host SDK version (semver). |
readonly pluginId: string | The Plugin this host is scoped to. |
readonly system: boolean | A system Plugin (source = "builtin"): pre-trusted, may use proposed services. |
readonly services: HostServices | |
| Member | What it does |
|---|
registerPanel(cfg: PanelConfig): Disposable | |
registerSettingsPage(cfg: SettingsPageConfig): Disposable | |
registerCommand(cfg: CommandConfig, handler: CommandHandler): Disposable | |
registerMenuItem(cfg: MenuItemConfig): Disposable | |
registerKeybinding(cfg: KeybindingConfig): Disposable | |
registerTopBarItem(cfg: TopBarItemConfig): Disposable | |
registerFileType(cfg: FileTypeConfig): Disposable | |
| Member | What it does |
|---|
registerVisualizer(cfg: VisualizerConfig): Disposable | |
registerGizmo(cfg: GizmoProvider): Disposable | |
registerOverlay(zone: OverlayZone, render: () => React.ReactNode): Disposable | |
registerPass(pass: RenderPass): Disposable | |
registerTool(tool: ViewportTool): Disposable | |
| Member | What it does |
|---|
registerWidgetDecorator(cfg: WidgetDecoratorConfig): Disposable | Claim the values this Plugin owns inside a built-in widget slot. |
registerInspector(componentName: string, component: React.ComponentType<InspectorProps>): Disposable | Draw the Properties section body of every component named componentName (an entry of properties.inspector, key component:<name>). |
| Member | What it does |
|---|
registerAssistantCard(cfg: AssistantCardConfig): Disposable | |
registerToolResultEffect(cfg: ToolResultEffectConfig): Disposable | |
registerToolArgumentPolicy(cfg: ToolArgumentPolicyConfig): Disposable | |
registerChatLink(cfg: ChatLinkConfig): Disposable | Hover, embed and existence of witwin://<pluginId>.<name>/… links; opening goes through opener. |
registerAgentContext(provider: AgentContextProvider): Disposable | |
| Member | What it does |
|---|
readonly state: PluginStateApi | |
readonly theme: { tokens(): Record<string, string>; onChange(cb: () => void): Disposable } | |
readonly assets: ViewportAssetsApi | |
readonly interaction: ViewportInteractionApi | |
readonly playback: PlaybackApi | |
readonly assistant: AssistantHostApi | |
readonly ui: { Button: React.ComponentType<any>; Select: React.ComponentType<any>; DraggableNumberInput: React.ComponentType<any>; Checkbox: React.ComponentType<any>; /** * Render the built-in widget for props.config.widget_type with the * Inspector's widget props ({ value, field, config, ... }). One argument: * the config travels inside props. */ renderWidget: (props: any) => React.ReactNode; } | Host UI widgets, live components that share the host's React. |
Scene objects of the current Scene, read and written through the op stream.
| Member | What it does |
|---|
getObjects(): Record<string, HostSceneObject> | Current objects, keyed by id. |
getSelection(): string[] | Ids of the selected scene objects. |
currentSceneId(): string | null | Backend id of the current Scene, or null while none is loaded. |
subscribe(listener: () => void): Disposable | Called on object and selection changes. |
getObjectComponents(objectId: string): unknown[] | Component display metadata (fields, buttons, groups) of one object; [] if unknown. |
updateComponentField(objectId: string, componentName: string, fieldName: string, value: unknown): Promise<void> | One undo step through the scene command stream. |
updateObjectTransform( objectId: string, transform: { position?: [number, number, number]; rotation?: [number, number, number, number] | [number, number, number]; scale?: [number, number, number]; }, ): Promise<void> | |
| Member | What it does |
|---|
execute(id: string, args?: unknown): Promise<unknown> | |
register(command: CommandConfig, handler: CommandHandler): Disposable | Register a command run in this window. |
| Member | What it does |
|---|
get(key: string): unknown | |
set(key: string, value: unknown): void | |
subscribe(listener: () => void): Disposable | Called after any key changed. |
| Member | What it does |
|---|
show(notification: HostNotification): void | A toast plus an entry in the notification panel; the same id updates it. |
Show a thing by its URI instead of naming the panel that shows it: witwin://file/data/a.npz, witwin://scene/world/object/box, witwin://result/12, witwin://panel/<panelId>.
| Member | What it does |
|---|
open(uri: string, options?: OpenOptions): Promise<boolean> | Resolves true when a handler took the URI. |
canOpen(uri: string, options?: OpenOptions): boolean | |
registerHandler(handler: OpenHandler): Disposable | |
"Something happened" notices (project.file.created, run.finished, ...).
| Member | What it does |
|---|
emit(name: string, payload: unknown): void | |
on(name: string, listener: (payload: any) => void): Disposable | |
| Member | What it does |
|---|
fetch(ref: ResourceRef, init?: RequestInit): Promise<Response> | fetch with the connection's authorization. |
objectUrl(ref: ResourceRef): Promise<{ url: string; release(): void }> | A blob: URL for an <img>; call release when done. |
signedUrl(ref: ResourceRef): Promise<string> | A URL that loads without headers (iframes, loaders): short-lived and signed where the connection needs it. |
Calls into the Plugin's Python half over the Project connection.
| Member | What it does |
|---|
call<T = unknown>(method: string, params?: Record<string, unknown>, options?: RpcCallOptions): Promise<T> | |
subscribe(topic: string, listener: (event: RpcEvent) => void, options?: { fromSeq?: number }): Disposable | |
The one "current thing" of the window.
- Used by: Project (
studio.project: the projectFile kind), Properties (studio.properties: which inspector to draw)
- Stable when: Hierarchy and Properties use it with no Studio store import
| Member | What it does |
|---|
current(): Subject | null | |
set(subject: Subject | null): void | |
subscribe(listener: (subject: Subject | null) => void): Disposable | |
registerKind(kind: { kind: string; clear(): void }): Disposable | clear runs when a Subject of another kind replaces this kind's. |
The Scene's undo history.
- Used by: Modeling (
studio.modeling), Properties (studio.properties: ScriptGeometry Design Apply)
- Stable when: Viewport tools record through it and Ctrl+Z works across Plugins
| Member | What it does |
|---|
undo(): void | |
redo(): void | |
canUndo(): boolean | |
canRedo(): boolean | |
subscribe(listener: () => void): Disposable | |
push(step: HistoryStep): void | Record a step. |
recordComponentEdit( objectId: string, componentName: string, oldValues: Record<string, unknown>, newValues: Record<string, unknown>, ): void | Record component fields the Plugin already wrote as one step; values by field name. |
runSceneEdit<T>(body: () => Promise<T>, options?: { label?: string }): Promise<SceneEditOutcome<T>> | Run body (Tool calls or ops that edit the current Scene) and record everything it changed, once its broadcasts arrived, as one step: objects created and deleted (restored with their geometry), parents, transforms, component fields. |
Project files, on the server.
- Used by: Git (
studio.git: refresh when Project files change), Project (studio.project: the whole panel)
- Stable when: Project panel runs on it on Local, Router and Control
| Member | What it does |
|---|
list(path: string, options?: { offset?: number; limit?: number; filter?: string; sort?: FsSort; signal?: AbortSignal }): Promise<FsListResult> | One folder, or one page of it with offset / limit. |
read(path: string): Promise<FsReadResult> | |
write(path: string, content: string, options?: { expectedMtime?: number }): Promise<void> | expectedMtime: refuse with conflict when the file changed meanwhile. |
createFolder(path: string): Promise<void> | |
rename(path: string, newName: string): Promise<void> | |
move(paths: string[], destination: string): Promise<void> | |
delete(paths: string[]): Promise<void> | |
copy(paths: string[], destination: string): Promise<void> | Copy into destination; a name that exists there gets a number (name 2). |
createIn(parent: string, kind: 'file' | 'folder'): Promise<string> | A new empty file or folder in parent, named by the server ("Untitled", "New Folder"). |
reveal(path: string): Promise<void> | Show the file in the file manager of the computer that runs the ProjectServer. |
openExternal(path: string): Promise<void> | Open the file with that computer's default application. |
onDidChange(listener: (path: string) => void): Disposable | Called with the changed path after a file appeared, changed or went away. |
Files on the user's own computer: pick, read, import into the Project; native open dialog, fromNativePath, projectPath (P5c); the window's import queue (imports: tasks with progress, conflicts, retryable, folder; start, cancel, retry with a conflict policy, dismiss)
- Used by: Modeling (
studio.modeling, Import CAD), Project (studio.project: drop and pick imports, the queue, the conflict dialog)
- Stable when: Import works in the browser and the desktop app through the same calls
| Member | What it does |
|---|
fromFile(file: File): LocalFileHandle | |
pick(options?: { accept?: string; multiple?: boolean }): Promise<LocalFileHandle[]> | |
fromDroppedFolders(items: DataTransferItemList): Promise<{ folder: string; files: LocalFileHandle[] }[]> | |
showOpenDialog(options?: { title?: string; filters?: { name: string; extensions: string[] }[] }): Promise<string[] | null> | The native open dialog, where the platform has nativeDialogs; it starts in the Project folder. |
fromNativePath(path: string): LocalFileHandle | A native path (from showOpenDialog) as a handle, for importInto. |
projectPath(nativePath: string): string | null | The Project-relative POSIX path of a native path inside the Project, else null. |
imports: ProjectImportsApi | Files being copied or uploaded into the Project, and their progress (D16 P5b2). |
What this connection can do (sameMachine, nativeDialogs, revealLocal, serverProcess, pluginInstall)
- Used by: none yet
- Stable when: Control sends the full table and no Plugin guesses from the host name
| Member | What it does |
|---|
has(capability: PlatformCapability): boolean | |
capabilities(): Readonly<Record<PlatformCapability, boolean>> | |
subscribe(listener: () => void): Disposable | |
State of the Project connection and binary frames; the open Project (project(), onProjectChanged). onMessage (flat broadcasts by type) was removed with the flat bus (D16 P9, 2026-11-04)
- Used by: Terminal (
studio.terminal), Console, Git, Tasks, Library, Results (studio.console, studio.git, studio.tasks, studio.library, studio.results)
- Stable when: One more system Plugin uses binary frames without changes
| Member | What it does |
|---|
isConnected(): boolean | |
onConnected(listener: () => void): Disposable | After every (re)connect. |
onReset(listener: () => void): Disposable | The server restarted or lost state: refetch everything. |
onBinary(listener: (buffer: ArrayBuffer) => void): Disposable | Binary frames on the Project connection (terminal output, streams). |
project(): { id: string; name: string } | null | The Project this window serves; null while none is open. |
onProjectChanged(listener: () => void): Disposable | After project() changed (opened, closed, switched). |
The track layout.
- Used by: Terminal (
studio.terminal), Console, Tasks, Results (studio.console, studio.tasks, studio.results), Hierarchy (studio.hierarchy), Parameters (studio.parameters), Project (studio.project: focus-scoped shortcuts), Gallery (studio.gallery: its tab, and its page with no Project open)
- Stable when: The Window menu and the palette use it for every Plugin panel
| Member | What it does |
|---|
open(panelId: string): void | |
focus(panelId: string): void | |
close(panelId: string): void | |
isOpen(panelId: string): boolean | |
list(): PanelInfo[] | |
openInstance(panelId: string, key: string, options?: { title?: string; params?: Record<string, unknown>; preview?: boolean }): void | Open instance key of a multi-instance panel as tab <id>:<key> with params (the component reads them). |
focused(): string | null | The panel the user last pressed in (hierarchy, project, ...), for shortcuts that act only in the focused panel; null when none. |
setFocused(panelId: string | null): void | Call on pointer-down in the panel's root. |
setTitle(tabId: string, title: string): void | Retitle an open instance tab (<prefix>:<key>), e.g. |
openPage(pageId: string): void | Show the workbench.pages entry pageId over the editor area, in place of the stage and Chat; the Plugins page closes. |
closePage(pageId?: string): void | Close the open page; with pageId, only when that page is the open one. |
openPageId(): string | null | The workbench.pages entry showing now; null when none. |
Values of settings entries the Python halves declare (settings_id → fields).
- Used by: none yet
- Stable when: Settings pages of two Plugins read and write through it
| Member | What it does |
|---|
get(settingsId: string, field: string): unknown | |
set(settingsId: string, field: string, value: unknown): void | |
subscribe(listener: () => void): Disposable | |
openPage(pageId?: string): void | Open the Settings dialog at a page (settings.pages id or a built-in page). |
Raw slot access (plan §5.4): declare a slot this Plugin owns, register into any slot.
- Used by: Console (
console.channels), Tasks (tasks.step.editor), Library (viewport.create, viewport.drop, hierarchy.*, agent.drops), Results (viewport.overlay, plot.result, workbench.root), Hierarchy (hierarchy.*), Parameters (parameters.variedBy), Project (project.fileTypes), Properties (properties.inspector), Arrays (studio.arrays), Viewport (viewport.toolbar, viewport.sceneLayers, viewport.overlayMenu, viewport.mode), Modeling (those and viewport.overlay), Document (document.viewer), Agent (agent.*), Studies (studies.kind.editor, studies.method.editor; fills parameters.variedBy), Materials (project.fileTypes, properties.inspector, workbench.root), EM (properties.inspector facet component:material#em), Gallery (workbench.pages)
- Stable when: Two Plugins extend each other through declared slots
| Member | What it does |
|---|
declare<T>(slot: { id: string; shape: SlotShape; keyOf?: (entry: T) => string; orderOf?: (entry: T) => number | null | undefined; fallback?: T; }): Disposable | |
register<T>(slotId: string, value: T, options?: { key?: string; order?: number; replaces?: string }): Disposable | replaces: the owner whose entry for the same key this one replaces on purpose (§5.7), e.g. |
values<T>(slotId: string): T[] | |
get<T>(slotId: string, key: string): T | undefined | |
subscribe(listener: () => void): Disposable | |
Edit the current Scene.
- Used by: Hierarchy (
studio.hierarchy), Parameters (studio.parameters), Library (studio.library), Results (studio.results), Modeling (studio.modeling), Properties (studio.properties: component edits, transform, previews), Materials (studio.materials)
- Stable when: Viewport and Properties edit the Scene only through it, and a third-party outline panel works on it
| Member | What it does |
|---|
state(): SceneEditorState | |
subscribe(listener: () => void): Disposable | |
outline(): SceneOutlineNode[] | The objects as a tree in child order. |
select(ids: readonly string[], mode?: SceneSelectMode): void | [] clears. |
rename(id: string, name: string): void | Undoable. |
setVisible(id: string, visible: boolean): void | |
setLocked(id: string, locked: boolean): void | |
createObject(spec: { name: string; mesh?: string; parentId?: string | null }): void | |
deleteSelection(): void | |
copySelection(): void | |
paste(): void | |
duplicate(id: string): void | |
reparent(ids: readonly string[], parentId: string | null, index?: number): void | Move under parentId (null: top level) at index. |
reorder(id: string, targetId: string, position: 'before' | 'after'): void | Reorder among the same siblings. |
extractToScene(id: string, name?: string): Promise<void> | Move the subtree into its own Scene file and reference it. |
focus(id: string): void | Frame the object in the viewport. |
chatAttachment(id: string): { label: string; [key: string]: unknown } | An Agent chat attachment for the object (opaque; pass it to AddToChatMenuItem). |
applyOp(op: Record<string, unknown>): Promise<SceneOpResult> | Send one scene op ({type, ...}, snake_case keys) through the op stream and resolve with the server's answer. |
onPatch(type: string, listener: (patch: Record<string, any>) => void): Disposable | Each patch of type in the committed scene ops ({type, ...}, snake_case keys, as the server records it; e.g. |
loadComponents(id: string): void | Ask the server for an object's components when it arrived without them (large Scenes load lazily). |
addComponent(id: string, componentName: string): void | |
removeComponent(id: string, componentName: string): void | |
previewObject(object: SceneEditorObject): void | Show an edited copy of the object in this window until the next server update (live preview while dragging). |
setComponentField(id: string, componentName: string, fieldName: string, value: unknown, options?: { from?: unknown }): void | Write one component field. |
transformOf(id: string): SceneTransform | null | The object's transform, or null when there is no such object. |
previewTransform(id: string, transform: SceneTransform): void | Show a transform in this window and on the drawn object; no server write. |
setTransform(id: string, transform: SceneTransform, options?: { from?: SceneTransform }): void | Write a transform. |
recordTransform(id: string, from: SceneTransform, to: SceneTransform): void | One undo step for a transform already written by setTransform calls without from (a drag that wrote on every release of one axis). |
The scene's parameters and the formula editors (D16 P5b).
- Used by: Properties (
studio.properties, optional), Studies (studio.studies: variables table, value text)
- Stable when: Studies and Properties use it with no Parameters import, and both work while Parameters is disabled
| Member | What it does |
|---|
state(): ParametersState | |
subscribe(listener: () => void): Disposable | |
update(paramId: string, patch: ParameterPatch): Promise<ParameterOpResult> | |
bindField(field: FieldRef, expr: string, unit?: string | null): Promise<BindingOpResult> | Bind one field component to expr, read in unit. |
unbindField(field: FieldRef): Promise<BindingOpResult> | |
setFieldUnit(objectId: string, target: string, unit: string | null): Promise<BindingOpResult> | Change a field's display unit (null: SI). |
editFieldFormula(context: FieldEquationContext): void | Open the Equation Editor on a field; OK binds it. |
formatValue(value: number | null): string | Display text of a parameter or constant value. |
Runs of the Project: read live state, stop one, show their metrics.
- Used by: Console (
studio.console, a Run's output channel), Tasks (studio.tasks, Run rows), Studies (studio.studies: partials, progress, train curves)
- Stable when: A frontend
studio.runs system Plugin provides it instead of the kernel
| Member | What it does |
|---|
get(runId: string): RunInfo | undefined | |
subscribe(listener: () => void): Disposable | |
cancel(runId: string): Promise<void> | |
reportsItself(pluginId: string): Disposable | Runs of pluginId show their progress where they were started, so they leave the panel layout alone: Console and Results do not open or close for them. |
MetricsView: ComponentType<RunMetricsViewProps> | The charts of the Run metrics panel, for a Plugin that shows them inline. |
This window's Agent runtime, for panels that start work in a Session.
- Used by: Tasks (
studio.tasks: Save as Workflow, Workflow runs whose agent steps start here)
- Stable when: The Chat column itself is a Plugin and starts turns through it
| Member | What it does |
|---|
sendMessage(sessionId: string, text: string): Promise<void> | Send text as the user's message in Session sessionId and start a turn. |
turnRequest(): Record<string, unknown> | The model, effort, permission mode and client Tools a turn started here would use. |
The Project's Results history (the results.* RPCs and the results topic).
- Used by: Results (
studio.results: both panels, the result / result_view openers, the Agent's Result View Tools, Save to Results)
- Stable when: The history moves into
studio.results once those readers take it from a Results-owned service or event
| Member | What it does |
|---|
history(): readonly ResultEntry[] | Oldest first. |
subscribe(listener: () => void): Disposable | |
deleteVersion(version: number, deleteFiles: boolean): Promise<void> | deleteFiles also removes the saved files; otherwise the version returns on restart. |
clear(deleteFiles: boolean): Promise<void> | |
showScene(sceneId: string | null | undefined): Promise<boolean> | Make sceneId the current Scene, asking "Switch Scene" first. |
openPlots(entry: { version: number; sceneId: string; title: string }): void | Open the Plot panel tab on a Result's plots; the tab's body comes from plot.result. |
snapshotWidget(widget: { widgetType: string; value: unknown; config: Record<string, unknown>; title?: string; }): Promise<Record<string, unknown> | null> | A plot record (RFC-017, the plot of results.commit_plot) of a widget's current value: a plot value as it is, the latest frame of a stream widget, an image-preview as an RGB imshow. |
Windows of arrays in Project .npy / .npz files, read without loading the whole file.
- Used by: Results (
studio.results: Result views with array data)
- Stable when: A stable
resources route for array windows
| Member | What it does |
|---|
window(path: string, key: string, selection: ArrayAxisSelection[], maxCells?: number): Promise<ArrayWindow> | key is the array's name in an .npz, '' for an .npy. |
The current Scene's timeline: clip content, the Scene clock and the edits on them.
- Used by: Timeline (
studio.timeline), Results (studio.results: views whose frames follow the Timeline)
- Stable when: The viewport and the top-bar transport use it with no timeline store import
| Member | What it does |
|---|
state(): TimelineState | |
subscribe(listener: () => void): Disposable | |
currentTime(): number | The playhead, seconds; Results views follow it. |
activate(): void | Show the current Scene's timeline; fetches its clip when the Scene changed. |
keyframeRange(): { start: number; end: number } | Keyframe range of the clip, seconds; the clip length when there are no keyframes. |
replayRange(datasetId: string | null): { start: number; end: number } | Time range of a replay dataset, seconds; zeros when unknown. |
preview(time: number): void | Show time locally (scrubbing): playhead and keyframed values; nothing is sent. |
seek(time: number): void | preview, then pause the Scene clock there. |
playKeyframes(): void | Play the keyframes from the playhead to the clip end. |
play(options?: { time?: number; loop?: boolean }): void | |
pause(options?: { time?: number; loop?: boolean }): void | |
stop(): void | Leave replay or live: back to edit mode. |
setMode(mode: 'edit' | 'live' | 'replay', options?: { source?: string }): void | |
setRecording(recording: boolean): void | Edit mode: record field edits as keyframes. |
setLiveRecording(recording: boolean): void | Live mode: record the simulation into a dataset. |
addKeyframe(objectId: string, componentName: string, fieldName: string, value: unknown, time: number): void | |
removeKeyframe(objectId: string, componentName: string, fieldName: string, time: number): void | |
moveKeyframes(moves: TimelineKeyframeMove[]): void | Moves the keys at once on screen, then sends one request. |
bakeProducers(): Promise<{ id: string; label: string }[]> | |
bake(params: { producerId: string; range: [number, number]; fps: number; config?: Record<string, unknown>; sceneId?: string; label?: string; kind?: string; }): void | Bake producerId over range (seconds) at fps; a pending dataset row shows until frames arrive. |
cancelBake(runId: string): void | |
deleteDataset(datasetId: string): void | |
The Agent chat, still kernel code (D16 P5c): the chat column and the window's part of the Agent loop (model relay, local.* tools, plan and question responders).
- Used by: Agent (
studio.agent)
- Stable when: Never stable: removed when the chat code moves into
plugins/agent and reads the agent.* slots itself
| Member | What it does |
|---|
Chat: ComponentType<AgentChatProps> | |
start(): Disposable | Answer the ProjectServer's model and local.* calls from this window, and hand the plugins' assistant contributions in the agent.* slots to the chat. |
The kernel's Scene view component, start() for the hand-over of the viewport.* slots to it, the view state tools read and set (transform mode and space, Measure, gizmo drags, the three.js object of a scene object) and the spawn point (cameraTarget(), Scene metres)
- Used by: Viewport (
studio.viewport), Modeling (studio.modeling), Library (studio.library: where an added item goes)
- Stable when: Never stable as a whole: removed when the viewport code moves into
plugins/viewport; studio.viewport then provides the tool part and the spawn point itself
| Member | What it does |
|---|
View: ComponentType<{ isDarkTheme: boolean }> | |
start(): Disposable | Hand the plugins' 3D contributions in the viewport.* slots to the view. |
state(): SceneViewState | Unchanged inputs give the same object. |
subscribe(listener: () => void): Disposable | |
setTransformMode(mode: ViewportTransformMode): void | |
setMeasuring(on: boolean): void | |
setGizmoDragging(kind: 'transform' | 'handle', dragging: boolean): void | A drag of a Plugin's gizmo is in progress: transform for a transform gizmo (object selection and camera keys stand down), handle for a drag handle (the object gizmo hides). |
object3D(objectId: string): unknown | The three.js object drawn for a scene object, or null when it is not drawn. |
cameraTarget(): [number, number, number] | Work that must finish before the current Scene goes away (a scene switch, a Project close): resolve true once done, false to stop it. |
registerLeaveGuard(guard: (reason: 'scene_switch' | 'project_close') => boolean | Promise<boolean>): Disposable | |
Run a ProjectServer Tool as a user action (D16 P5c, Modeling): the click is the approval, so a Tool that needs confirmation is approved once by the window's confirmation authority.
- Used by: Modeling (
studio.modeling)
- Stable when: One confirmation path for UI-run and Agent-run Tools, and a third-party name rule that Plugins accept
| Member | What it does |
|---|
call<R = unknown>(name: string, args?: Record<string, unknown>): Promise<R> | |
Mesh geometry that scene ops and RPCs do not carry inline (D16 P5c, Modeling).
- Used by: Modeling (
studio.modeling)
- Stable when: The meshbin format and the upload route are frozen
| Member | What it does |
|---|
load(ref: MeshAssetRef): Promise<{ positions: Float32Array; indices: Uint32Array }> | Flat xyz positions and triangle indices, local frame; cached by asset version. |
upload(positions: Float32Array, indices: Uint32Array): Promise<{ assetId: string }> | Upload a mesh to the current Scene's assets; put the id into a scene op. |
The Project's Studies (studies/<id>.toml).
- Used by: none yet (Parameters uses the
parameters.variedBy slot entry studio.studies registers)
- Stable when: One Plugin outside Studies reads Studies through it with no Studies import
| Member | What it does |
|---|
list(): readonly StudyInfo[] | |
subscribe(listener: () => void): Disposable | |
reveal(studyId: string, options?: { variable?: string }): void | Open the Studies page on a Study; variable scrolls its Variables table to that row. |
Offscreen renders of Project files and the live material view (D16 P5b2).
- Used by: Project (
studio.project: cards and the detail preview), Materials (studio.materials: .wtmat cards and the editor preview)
- Stable when: A third-party file type draws its card through it, and the renderer behind it can change without a Plugin change
| Member | What it does |
|---|
mesh(file: ProjectFileRef): Promise<string | null> | A renderable mesh (obj, glb, gltf, ply, stl) rendered offscreen; null for other files or no connection. |
image(file: ProjectFileRef): Promise<string | null> | An image: the import cache's 256 px preview once ready, else the image itself. |
material(key: string, values: MaterialValues): Promise<string | null> | A lit sphere with these material values, on a transparent background. |
MaterialView: ComponentType<MaterialViewProps> | An interactive view of a material (orbit, sky lighting), sized by its parent. |
Imported meshes and textures (D16 P5b2): their import records, settings and placing them in the current Scene.
- Used by: Project (
studio.project: built-in types, import settings view, card status)
- Stable when: A Plugin file type with its own importer shows its state and settings through it
| Member | What it does |
|---|
get(path: string): ImportedAssetInfo | null | The record of the file at path, or null when it is not an imported asset. |
subscribe(listener: () => void): Disposable | Calls listener when any record changes. |
watch(path: string, listener: () => void): Disposable | Calls listener when the record of path changes (status, settings, removal). |
describe(uid: string): Promise<ImportedAssetDescription> | |
ensureCache(target: { uid: string } | { path: string }): Promise<void> | Build the cache now (by UID, or by path for a file with no sidecar yet). |
setSettings(uid: string, settings: Record<string, unknown>): Promise<void> | Merge settings into the sidecar; the server rebuilds the cache. |
reimport(uid: string): Promise<void> | |
placeInScene(file: ProjectFileRef, as: 'mesh' | 'splat' | 'cad'): void | Place the file in the current Scene in front of the camera. |
The Project's Scenes as files (D16 P5b2): the Scene a .wtscene holds, open one, make it the main Scene, set its stage.
- Used by: Project (
studio.project: .wtscene type and menu)
- Stable when: The scene list moves behind a Scenes service and this becomes its file view
| Member | What it does |
|---|
sceneOf(file: ProjectFileRef): SceneFileInfo | null | The Scene in the .wtscene file at path; falls back to the file name before the catalog loads. |
subscribe(listener: () => void): Disposable | Calls listener when the catalog changes. |
open(sceneId: string, title: string): void | Open the Scene in the Scene view (asks first when the current Scene has unsaved edits). |
setMain(sceneId: string): Promise<void> | |
setStage(sceneId: string, stage: 'world' | 'bench'): Promise<void> | |
Scene component definitions and component buttons (D16 P5b2): what can be added, which Plugin hides a component while it is off, an object's components in form shape, and button clicks with their operations.
- Used by: Properties (
studio.properties), Materials (studio.materials: the material editor)
- Stable when: A Plugin outside Studio draws a component and clicks its buttons with no Studio import
| Member | What it does |
|---|
catalog(): readonly ComponentCatalogEntry[] | Components the server offers; empty until the definitions arrive. |
disabledBy(componentName: string): string | null | The Plugin whose disabling hides componentName, or null. |
subscribe(listener: () => void): Disposable | Calls listener when the catalog, the disabled set or an operation changes. |
of(object: SceneEditorObject): FormComponent[] | The object's components in form shape, in object order. |
fromWire(component: unknown): FormComponent | Convert one backend component (Component.to_dict()) to form shape. |
activeOperations(): readonly ComponentOperationInfo[] | Operations not yet finished, newest first. |
clickButton(target: { sceneId: string | null; objectId: string; componentName: string; button: FormButton }): void | Click a component button. |
cancelOperation(operationId: string): void | Ask the operation's Run to stop; the operation shows "cancelling" until it ends. |
The schema form (D16 P5b2): the rows, groups, widgets, units, show_if rules and formula bindings that components, schema panels and Settings pages draw.
- Used by: Properties (
studio.properties), Materials (studio.materials)
- Stable when: Settings and one Plugin panel draw with it, and its props are frozen
| Member | What it does |
|---|
Section: ComponentType<FormSectionProps> | One component: header (fold, menu), rows, groups, buttons, facet sections. |
Field: ComponentType<FormFieldProps> | The control of one field (no label). |
Row: ComponentType<FormRowProps> | Label plus control, at the table's label width. |
InspectorBody: ComponentType<FormInspectorBodyProps> | A Plugin's component inspector body (InspectorProps) on the form's edit callbacks, inside an error boundary: a throwing body draws nothing. |
useLabelWidth(): FormLabelWidth | React hook: the label column of a table. |
useExpanded(objectId: string, componentName: string): [boolean, (expanded: boolean) => void] | React hook: whether a section is unfolded, per object and component, and its setter. |
useFieldHighlight(objectId: string | null | undefined, target: string): { key: number; index?: number } | undefined | React hook: the pending "show me this field" request for a field (witwin://scene/.../object/<id>#Component.field), consumed once drawn. |
valuesOf(fields: readonly FormField[]): Record<string, unknown> | Field name to value, the map isVisible and isEnabled read. |
isVisible(field: FormField, values: Record<string, unknown>): boolean | show_if / hide_if and the field's hidden, against the component's values. |
useFieldAnimated(objectId: string | null | undefined, componentName: string, fieldName: string): boolean | React hook: the field has animation keyframes, so it cannot take a formula (formula plan §5). |
animatedReason: string | Tooltip of a disabled fx button on an animated field. |
isEnabled(field: FormField, values: Record<string, unknown>): boolean | enable_if / disable_if, against the component's values. |
rootGroup(group: string): string | Root of a group path: Tabs for Tabs.Advanced. |
The Gallery's homepage-api reads and its two ways into a Project (View: a trial; Create: a persistent Project).
- Used by: Gallery (
studio.gallery)
- Stable when: A second Plugin lists or opens Gallery items through it
| Member | What it does |
|---|
list(filter: GalleryListFilter, cursor?: string | null): Promise<GalleryListPage> | One page of published items, best first. |
get(itemId: string): Promise<GalleryItem> | |
errorMessage(error: unknown): string | The sentence to show for a failed Gallery request. |
view(item: GalleryItem): Promise<boolean> | View: open item as a trial Project (a Demo sends its prompt). |
create(item: GalleryItem, name: string, options?: { sendDemo?: boolean }): Promise<boolean> | Create: a persistent Project named name; sendDemo (default true) sends a Demo's prompt. |
studioTooOld(item: GalleryItem): boolean | True when item needs a newer Studio than this one. |
missingPlugin(item: GalleryItem): Promise<GalleryMissingPlugin | null> | Desktop: the Plugin a template item needs and lacks; null when none is missing. |
NameForm: ComponentType<GalleryNameFormProps> | |