feat(web): classify and route complete Blender inventory
This commit is contained in:
@@ -34,3 +34,65 @@ export interface BlenderOperatorInvocation {
|
||||
properties?: Record<string, BlenderOperatorPropertyValue>;
|
||||
baseRevision?: number;
|
||||
}
|
||||
|
||||
export type BlenderFeatureRoute = "WEB_MAIN" | "SERVER_NATIVE" | "BROWSER_EQUIVALENT" | "UNAVAILABLE";
|
||||
|
||||
export interface BlenderFeatureRouteLocation {
|
||||
path: string;
|
||||
line?: number;
|
||||
evidence?: string;
|
||||
}
|
||||
|
||||
/** Source-unit route metadata used by the F3 feature palette. */
|
||||
export interface BlenderFeatureRouteEntry {
|
||||
id: string;
|
||||
kind: string;
|
||||
identifier: string;
|
||||
family: string;
|
||||
route: BlenderFeatureRoute;
|
||||
reason: string;
|
||||
label?: string;
|
||||
description?: string;
|
||||
properties?: BlenderOperatorPropertySpec[];
|
||||
locations?: BlenderFeatureRouteLocation[];
|
||||
}
|
||||
|
||||
export interface BlenderFeatureRouteInvocation {
|
||||
route: BlenderFeatureRouteEntry;
|
||||
properties?: Record<string, BlenderOperatorPropertyValue>;
|
||||
baseRevision?: number;
|
||||
}
|
||||
|
||||
/** SERVER_NATIVE jobs deliberately carry only typed scalar operator properties. */
|
||||
export const BLENDER_OPERATOR_BACKGROUND_CONTEXT = "BACKGROUND" as const;
|
||||
|
||||
export type BlenderMacroStepKind = "MAIN_COMMAND" | "SERVER_NATIVE" | "CONTEXT_DEPENDENT";
|
||||
|
||||
export interface BlenderMacroStep {
|
||||
id: string;
|
||||
kind: BlenderMacroStepKind;
|
||||
context: "MAIN" | "BACKGROUND" | "UI";
|
||||
operator?: string;
|
||||
command?: Record<string, unknown>;
|
||||
source: BlenderFeatureRouteLocation[];
|
||||
}
|
||||
|
||||
/** A bounded, source-backed native plan assembled from Blender operator units. */
|
||||
export interface BlenderMacroDescriptor {
|
||||
id: string;
|
||||
label: string;
|
||||
description?: string;
|
||||
family: string;
|
||||
route: BlenderFeatureRoute;
|
||||
contextDependent: boolean;
|
||||
source: BlenderFeatureRouteLocation[];
|
||||
steps: BlenderMacroStep[];
|
||||
properties?: BlenderOperatorPropertySpec[];
|
||||
}
|
||||
|
||||
export interface BlenderMacroInvocation {
|
||||
macro: string;
|
||||
properties?: Record<string, BlenderOperatorPropertyValue>;
|
||||
baseRevision?: number;
|
||||
serverJob?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
@@ -678,10 +678,11 @@ export interface SceneRNADataBlockIR {
|
||||
|
||||
export interface SceneRNAPropertyIR {
|
||||
identifier: string;
|
||||
type: "BOOLEAN" | "INT" | "FLOAT" | "STRING" | "ENUM";
|
||||
type: "BOOLEAN" | "INT" | "FLOAT" | "STRING" | "ENUM" | "COLLECTION";
|
||||
arrayLength: number;
|
||||
readonly: boolean;
|
||||
value?: boolean | number | string | null | Array<boolean | number | string | null>;
|
||||
collection?: { itemType?: string; nestedPath?: string };
|
||||
}
|
||||
|
||||
export interface SceneSnapshotIR {
|
||||
|
||||
154
web/protocol/server-operator-job.ts
Normal file
154
web/protocol/server-operator-job.ts
Normal file
@@ -0,0 +1,154 @@
|
||||
import type { ErrorCode } from "./error";
|
||||
import type { BlenderOperatorPropertyValue } from "./blender-operator";
|
||||
|
||||
export const SERVER_OPERATOR_JOB_SCHEMA = 1 as const;
|
||||
export const SERVER_OPERATOR_JOB_ROUTE = "SERVER_NATIVE" as const;
|
||||
export const SERVER_OPERATOR_JOB_BUDGET = {
|
||||
maxJobIdBytes: 128,
|
||||
maxOperatorBytes: 128,
|
||||
maxPropertiesBytes: 64 * 1024,
|
||||
maxPropertyCount: 128,
|
||||
maxStringPropertyBytes: 4096,
|
||||
maxSourceBytes: 512 * 1024 * 1024,
|
||||
minTimeoutMs: 1000,
|
||||
maxTimeoutMs: 10 * 60 * 1000,
|
||||
minMemoryBytes: 64 * 1024 * 1024,
|
||||
maxMemoryBytes: 4 * 1024 * 1024 * 1024,
|
||||
} as const;
|
||||
export const SERVER_OPERATOR_JOB_STATUSES = ["QUEUED", "RUNNING", "SUCCEEDED", "FAILED", "CANCELLED"] as const;
|
||||
export type ServerOperatorJobStatus = typeof SERVER_OPERATOR_JOB_STATUSES[number];
|
||||
export type ServerOperatorResultBinding = "json" | "snapshot" | "scene-delta";
|
||||
|
||||
export interface ServerOperatorBuildIdentityIR {
|
||||
version: string;
|
||||
buildSha256: string;
|
||||
}
|
||||
export interface ServerOperatorJobRequestIR {
|
||||
schemaVersion: typeof SERVER_OPERATOR_JOB_SCHEMA;
|
||||
route: typeof SERVER_OPERATOR_JOB_ROUTE;
|
||||
jobId: string;
|
||||
sourceBlendSha256: string;
|
||||
sourceBlendByteLength: number;
|
||||
sourceRevision: number;
|
||||
blenderBuild: ServerOperatorBuildIdentityIR;
|
||||
operator: string;
|
||||
properties: Record<string, BlenderOperatorPropertyValue>;
|
||||
propertiesSha256: string;
|
||||
timeoutMs: number;
|
||||
memoryBytes: number;
|
||||
resultBinding: { kind: ServerOperatorResultBinding; key: string };
|
||||
context?: "BACKGROUND";
|
||||
requestSha256: string;
|
||||
}
|
||||
|
||||
export interface ServerOperatorJobResultIR {
|
||||
schemaVersion: typeof SERVER_OPERATOR_JOB_SCHEMA;
|
||||
route: typeof SERVER_OPERATOR_JOB_ROUTE;
|
||||
jobId: string;
|
||||
status: ServerOperatorJobStatus;
|
||||
sourceBlendSha256: string;
|
||||
sourceRevision: number;
|
||||
blenderBuild: ServerOperatorBuildIdentityIR;
|
||||
propertiesSha256: string;
|
||||
requestSha256: string;
|
||||
resultSha256: string;
|
||||
result?: unknown;
|
||||
errorCode?: ErrorCode;
|
||||
errorMessage?: string;
|
||||
}
|
||||
|
||||
export class ServerOperatorJobValidationError extends Error {
|
||||
readonly code: ErrorCode;
|
||||
|
||||
constructor(message: string, code: ErrorCode = "INVALID_ARGUMENT") {
|
||||
super(`${code}: ${message}`);
|
||||
this.name = "ServerOperatorJobValidationError";
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
|
||||
const SHA256 = /^[a-f0-9]{64}$/;
|
||||
const JOB_ID = /^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$/;
|
||||
const KEY = /^[A-Za-z][A-Za-z0-9_.-]{0,127}$/;
|
||||
|
||||
function record(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === "object" && value !== null && !Array.isArray(value);
|
||||
}
|
||||
|
||||
function exactKeys(value: Record<string, unknown>, allowed: readonly string[], name: string): void {
|
||||
const set = new Set(allowed);
|
||||
if (Object.keys(value).some((key) => !set.has(key))) throw new ServerOperatorJobValidationError(`${name} contains undeclared fields`);
|
||||
}
|
||||
|
||||
function digest(value: unknown, name: string): string {
|
||||
if (typeof value !== "string" || !SHA256.test(value)) throw new ServerOperatorJobValidationError(`${name} must be a lowercase SHA-256 digest`);
|
||||
return value;
|
||||
}
|
||||
|
||||
function stableJSON(value: unknown, state = { nodes: 0, depth: 0 }): string {
|
||||
state.nodes += 1;
|
||||
if (state.nodes > SERVER_OPERATOR_JOB_BUDGET.maxPropertyCount * 4 || state.depth > 8) throw new ServerOperatorJobValidationError("properties exceed the JSON budget");
|
||||
if (value === null || typeof value === "string" || typeof value === "boolean") return JSON.stringify(value);
|
||||
if (typeof value === "number" && Number.isFinite(value)) return JSON.stringify(value);
|
||||
if (Array.isArray(value)) return `[${value.map((item) => stableJSON(item, { ...state, depth: state.depth + 1 })).join(",")}]`;
|
||||
if (record(value)) return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableJSON(value[key], { ...state, depth: state.depth + 1 })}`).join(",")}}`;
|
||||
throw new ServerOperatorJobValidationError("properties contain a non-JSON value");
|
||||
}
|
||||
|
||||
function validateProperties(value: unknown): Record<string, BlenderOperatorPropertyValue> {
|
||||
if (!record(value)) throw new ServerOperatorJobValidationError("properties must be an object");
|
||||
const entries = Object.entries(value);
|
||||
if (entries.length > SERVER_OPERATOR_JOB_BUDGET.maxPropertyCount) throw new ServerOperatorJobValidationError("properties exceed the property-count budget");
|
||||
for (const [key, item] of entries) {
|
||||
if (!KEY.test(key)) throw new ServerOperatorJobValidationError(`property key ${key} is invalid`);
|
||||
if (!((typeof item === "string" && new TextEncoder().encode(item).byteLength <= SERVER_OPERATOR_JOB_BUDGET.maxStringPropertyBytes) ||
|
||||
(typeof item === "number" && Number.isFinite(item)) || typeof item === "boolean")) {
|
||||
throw new ServerOperatorJobValidationError(`property ${key} must be a bounded typed scalar`);
|
||||
}
|
||||
}
|
||||
if (new TextEncoder().encode(stableJSON(value)).byteLength > SERVER_OPERATOR_JOB_BUDGET.maxPropertiesBytes) throw new ServerOperatorJobValidationError("properties exceed the byte budget");
|
||||
return value as Record<string, BlenderOperatorPropertyValue>;
|
||||
}
|
||||
|
||||
export function canonicalServerOperatorJobRequest(request: Omit<ServerOperatorJobRequestIR, "requestSha256">): string {
|
||||
return stableJSON({ ...request, context: request.context ?? "BACKGROUND" });
|
||||
}
|
||||
|
||||
export function parseServerOperatorJobRequest(value: unknown): ServerOperatorJobRequestIR {
|
||||
if (!record(value)) throw new ServerOperatorJobValidationError("request must be an object");
|
||||
exactKeys(value, ["schemaVersion", "route", "jobId", "sourceBlendSha256", "sourceBlendByteLength", "sourceRevision", "blenderBuild", "operator", "properties", "propertiesSha256", "timeoutMs", "memoryBytes", "resultBinding", "context", "requestSha256"], "request");
|
||||
if (value.schemaVersion !== SERVER_OPERATOR_JOB_SCHEMA || value.route !== SERVER_OPERATOR_JOB_ROUTE) throw new ServerOperatorJobValidationError("schemaVersion and route do not match SERVER_NATIVE contract");
|
||||
if (typeof value.jobId !== "string" || !JOB_ID.test(value.jobId)) throw new ServerOperatorJobValidationError("jobId is invalid");
|
||||
const sourceBlendByteLength = value.sourceBlendByteLength;
|
||||
if (typeof sourceBlendByteLength !== "number" || !Number.isSafeInteger(sourceBlendByteLength) || sourceBlendByteLength < 1 || sourceBlendByteLength > SERVER_OPERATOR_JOB_BUDGET.maxSourceBytes) throw new ServerOperatorJobValidationError("source blend size is outside the budget");
|
||||
if (typeof value.sourceRevision !== "number" || !Number.isSafeInteger(value.sourceRevision) || value.sourceRevision < 0) throw new ServerOperatorJobValidationError("sourceRevision is invalid");
|
||||
if (typeof value.operator !== "string" || value.operator.length === 0 || new TextEncoder().encode(value.operator).byteLength > SERVER_OPERATOR_JOB_BUDGET.maxOperatorBytes || !/^[A-Za-z_][A-Za-z0-9_.-]*$/.test(value.operator)) throw new ServerOperatorJobValidationError("operator is invalid");
|
||||
if (!record(value.blenderBuild)) throw new ServerOperatorJobValidationError("blenderBuild must be an object");
|
||||
exactKeys(value.blenderBuild, ["version", "buildSha256"], "blenderBuild");
|
||||
if (typeof value.blenderBuild.version !== "string" || value.blenderBuild.version.length === 0 || value.blenderBuild.version.length > 64) throw new ServerOperatorJobValidationError("blenderBuild.version is invalid");
|
||||
const blenderBuild = { version: value.blenderBuild.version, buildSha256: digest(value.blenderBuild.buildSha256, "blenderBuild.buildSha256") };
|
||||
const properties = validateProperties(value.properties);
|
||||
const timeoutMs = value.timeoutMs;
|
||||
const memoryBytes = value.memoryBytes;
|
||||
if (typeof timeoutMs !== "number" || !Number.isSafeInteger(timeoutMs) || timeoutMs < SERVER_OPERATOR_JOB_BUDGET.minTimeoutMs || timeoutMs > SERVER_OPERATOR_JOB_BUDGET.maxTimeoutMs) throw new ServerOperatorJobValidationError("timeoutMs is outside the budget");
|
||||
if (typeof memoryBytes !== "number" || !Number.isSafeInteger(memoryBytes) || memoryBytes < SERVER_OPERATOR_JOB_BUDGET.minMemoryBytes || memoryBytes > SERVER_OPERATOR_JOB_BUDGET.maxMemoryBytes) throw new ServerOperatorJobValidationError("memoryBytes is outside the budget");
|
||||
if (!record(value.resultBinding) || !["json", "snapshot", "scene-delta"].includes(value.resultBinding.kind as string) || typeof value.resultBinding.key !== "string" || !KEY.test(value.resultBinding.key)) throw new ServerOperatorJobValidationError("resultBinding is invalid");
|
||||
if (value.context !== undefined && value.context !== "BACKGROUND") throw new ServerOperatorJobValidationError("UI, window and file-browser contexts are not supported");
|
||||
return {
|
||||
schemaVersion: SERVER_OPERATOR_JOB_SCHEMA,
|
||||
route: SERVER_OPERATOR_JOB_ROUTE,
|
||||
jobId: value.jobId,
|
||||
sourceBlendSha256: digest(value.sourceBlendSha256, "sourceBlendSha256"),
|
||||
sourceBlendByteLength,
|
||||
sourceRevision: value.sourceRevision,
|
||||
blenderBuild,
|
||||
operator: value.operator,
|
||||
properties,
|
||||
propertiesSha256: digest(value.propertiesSha256, "propertiesSha256"),
|
||||
timeoutMs,
|
||||
memoryBytes,
|
||||
resultBinding: { kind: value.resultBinding.kind as ServerOperatorResultBinding, key: value.resultBinding.key },
|
||||
...(value.context === undefined ? {} : { context: "BACKGROUND" as const }),
|
||||
requestSha256: digest(value.requestSha256, "requestSha256"),
|
||||
};
|
||||
}
|
||||
@@ -26,6 +26,10 @@ export const WEB_ENGINE_COMMAND_CAPABILITIES = {
|
||||
setObjectVisibility: { family: "OBJECT", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setModifierEnabled: { family: "MODIFIER", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setModifierVisibility: { family: "MODIFIER", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
addModifier: { family: "MODIFIER", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
removeModifier: { family: "MODIFIER", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
moveModifier: { family: "MODIFIER", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setModifierParameters: { family: "MODIFIER", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
decimateMesh: { family: "MESH", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
previewDecimateMesh: { family: "MESH", route: "PREVIEW", implementation: "SOURCE_BACKED" },
|
||||
createPrimitive: { family: "OBJECT", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
@@ -34,6 +38,12 @@ export const WEB_ENGINE_COMMAND_CAPABILITIES = {
|
||||
setObjectTransform: { family: "OBJECT", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
translateMeshVertices: { family: "MESH", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
meshEdit: { family: "MESH", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
meshSubdivide: { family: "MESH", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
meshTriangulate: { family: "MESH", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
meshRecalculateNormals: { family: "MESH", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
meshBridgeLoops: { family: "MESH", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
meshDeleteElements: { family: "MESH", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
meshUVProject: { family: "MESH", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
createUVMap: { family: "MESH", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setActiveUVMap: { family: "MESH", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
unwrapUV: { family: "MESH", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
@@ -47,6 +57,22 @@ export const WEB_ENGINE_COMMAND_CAPABILITIES = {
|
||||
deleteObjectKeyframe: { family: "ANIMATION", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setFCurveInterpolation: { family: "ANIMATION", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setActiveAction: { family: "ANIMATION", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
createAction: { family: "ANIMATION", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
removeAction: { family: "ANIMATION", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setKeyframe: { family: "ANIMATION", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setTimelineMarkers: { family: "ANIMATION", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setFCurveKeyframes: { family: "ANIMATION", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setDriverExpression: { family: "ANIMATION", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setActionGroups: { family: "ANIMATION", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setKeyingSet: { family: "ANIMATION", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
bakeAnimation: { family: "ANIMATION", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setArmatureBones: { family: "ANIMATION", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setPoseBoneTransform: { family: "ANIMATION", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
armatureEditOperations: { family: "ANIMATION", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setBoneRoll: { family: "ANIMATION", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setBoneSymmetry: { family: "ANIMATION", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setBoneParenting: { family: "ANIMATION", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setPoseConstraint: { family: "ANIMATION", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setConstraint: { family: "ANIMATION", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setParent: { family: "OBJECT", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
createCollection: { family: "OBJECT", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
@@ -56,13 +82,21 @@ export const WEB_ENGINE_COMMAND_CAPABILITIES = {
|
||||
separateMeshFaces: { family: "MESH", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
applyObjectTransform: { family: "OBJECT", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setObjectOrigin: { family: "OBJECT", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
clearObjectTransform: { family: "OBJECT", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
createObjectData: { family: "OBJECT", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setObjectSelection: { family: "OBJECT", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setCollectionVisibility: { family: "OBJECT", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
createCurve: { family: "GEOMETRY", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
deleteNonMeshData: { family: "GEOMETRY", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setCurveControlPoints: { family: "GEOMETRY", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setCurveBevel: { family: "GEOMETRY", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setCurveTaper: { family: "GEOMETRY", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
convertCurveType: { family: "GEOMETRY", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setCurveHandle: { family: "GEOMETRY", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setCurveTopology: { family: "GEOMETRY", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setCurveSplines: { family: "GEOMETRY", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setSurfaceTopology: { family: "GEOMETRY", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setSurfaceResolution: { family: "GEOMETRY", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setFontBody: { family: "FONT", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setFontProperties: { family: "FONT", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setFontAdvanced: { family: "FONT", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
@@ -81,6 +115,10 @@ export const WEB_ENGINE_COMMAND_CAPABILITIES = {
|
||||
insertGreasePencilFrame: { family: "GREASE_PENCIL", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
removeGreasePencilFrame: { family: "GREASE_PENCIL", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setGreasePencilStrokes: { family: "GREASE_PENCIL", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setGreasePencilDrawing: { family: "GREASE_PENCIL", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setGreasePencilMaterial: { family: "GREASE_PENCIL", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setGreasePencilSelection: { family: "GREASE_PENCIL", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setGreasePencilInterpolate: { family: "GREASE_PENCIL", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setVertexColors: { family: "MESH", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setVertexWeights: { family: "MESH", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setLightProperties: { family: "RENDER", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
@@ -110,6 +148,11 @@ export const WEB_ENGINE_COMMAND_CAPABILITIES = {
|
||||
setSceneViewLayerPassSettings: { family: "RENDER", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setSceneViewLayerAOVs: { family: "RENDER", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setSceneViewLayerLightgroups: { family: "RENDER", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
renderFrame: { family: "RENDER", route: "MAIN_GUARDED", implementation: "SOURCE_BACKED" },
|
||||
renderAnimation: { family: "RENDER", route: "MAIN_GUARDED", implementation: "SOURCE_BACKED" },
|
||||
setRenderPasses: { family: "RENDER", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setCameraView: { family: "RENDER", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setLightLinking: { family: "RENDER", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setObjectLightgroup: { family: "RENDER", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setWorldLightgroup: { family: "RENDER", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setMetaballElements: { family: "GEOMETRY", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
@@ -117,8 +160,22 @@ export const WEB_ENGINE_COMMAND_CAPABILITIES = {
|
||||
setSculptMeshAttributes: { family: "SCULPT", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setGeometryNodeGraph: { family: "GEOMETRY_NODES", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setPhysicsSimulation: { family: "SIMULATION", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setRigidBodySettings: { family: "SIMULATION", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setClothSettings: { family: "SIMULATION", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setFluidSettings: { family: "SIMULATION", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setParticleCache: { family: "SIMULATION", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setSimulationPlayback: { family: "SIMULATION", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setShaderGraph: { family: "SHADER", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setNLAStack: { family: "ANIMATION", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setSequencerStrips: { family: "MEDIA", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setSequencerStripModifiers: { family: "MEDIA", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setSequencerTransitions: { family: "MEDIA", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setSequencerColorBalance: { family: "MEDIA", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setCompositorNodeGraph: { family: "COMPOSITOR", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setTrackingMarkers: { family: "TRACKING", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setTrackingSolve: { family: "TRACKING", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setMaskSpline: { family: "TRACKING", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
setMaskPoint: { family: "TRACKING", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
moveNLAStrip: { family: "ANIMATION", route: "MAIN_AUTHORITATIVE", implementation: "SOURCE_BACKED" },
|
||||
undo: { family: "HISTORY", route: "LOCAL_HISTORY", implementation: "SOURCE_BACKED" },
|
||||
redo: { family: "HISTORY", route: "LOCAL_HISTORY", implementation: "SOURCE_BACKED" },
|
||||
|
||||
@@ -290,18 +290,20 @@ export interface RNAPropertyDescriptor {
|
||||
identifier: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
type: "BOOLEAN" | "INT" | "FLOAT" | "STRING" | "ENUM";
|
||||
type: "BOOLEAN" | "INT" | "FLOAT" | "STRING" | "ENUM" | "COLLECTION";
|
||||
subtype?: number;
|
||||
arrayLength: number;
|
||||
readonly: boolean;
|
||||
value?: RNAPropertyValue | RNAPropertyValue[];
|
||||
enumItems?: Array<{ identifier: string; name: string; value: number }>;
|
||||
collection?: { itemType?: string; nestedPath?: string };
|
||||
}
|
||||
export interface RNACollectionDescriptor {
|
||||
identifier: string;
|
||||
name: string;
|
||||
itemType?: string;
|
||||
items: Array<{ name: string; type?: string }>;
|
||||
path?: string;
|
||||
items: Array<{ name?: string; index?: number; type?: string; items?: Array<unknown> }>;
|
||||
}
|
||||
|
||||
/** Read-only commands supported by the stable Worker-to-WASM JSON facade. */
|
||||
@@ -315,6 +317,10 @@ export type WebEngineJsonCommand =
|
||||
| "redo"
|
||||
| "listBlenderOperators"
|
||||
| "invokeBlenderOperator"
|
||||
| "listBlenderMacros"
|
||||
| "invokeBlenderMacro"
|
||||
| "listFeatureRoutes"
|
||||
| "invokeFeatureRoute"
|
||||
| "assetMark"
|
||||
| "assetCatalog"
|
||||
| "libraryLink"
|
||||
@@ -323,7 +329,11 @@ export type WebEngineJsonCommand =
|
||||
| "listRNADataBlocks"
|
||||
| "readRNAProperties"
|
||||
| "setRNAProperty"
|
||||
| "listRNACollections";
|
||||
| "listRNACollections"
|
||||
| "readRNAPropertySchema"
|
||||
| "setRNAArrayProperty"
|
||||
| "setRNAEnumProperty"
|
||||
| "readRNACollection";
|
||||
|
||||
export type WebEngineRequest =
|
||||
| { requestId: string; command: { type: "init" } }
|
||||
|
||||
Reference in New Issue
Block a user