- update native web_engine sources and rebuilt wasm artifacts - add task cards, status notes and tooling scripts - add web e2e specs, tutorial UI sources and tutorial assets - sync golden task manifests and audit records Co-Authored-By: Claude Code <noreply@anthropic.com>
88 lines
5.1 KiB
TypeScript
88 lines
5.1 KiB
TypeScript
import type { BlenderOperatorPropertyValue } from './blender-operator';
|
|
import type { EditorMode } from './editor-workflow';
|
|
|
|
export type NativeWorkflowKind = 'RENDER' | 'PYTHON' | 'DESKTOP_OPERATOR' | 'EXPORT';
|
|
export type NativeExportFormat = 'GLB' | 'GLTF' | 'FBX' | 'OBJ' | 'STL' | 'PLY' | 'USD' | 'ALEMBIC';
|
|
export interface NativeWorkflowContext {
|
|
revision?: number;
|
|
sceneName?: string;
|
|
activeObject?: string;
|
|
selectedObjects?: string[];
|
|
mode?: EditorMode;
|
|
meshSelection?: { domain: 'VERT' | 'EDGE' | 'FACE'; indices: number[] };
|
|
nonMeshSelection?: { controlPoints: number[] };
|
|
nodeTreeType?: 'ShaderNodeTree' | 'GeometryNodeTree' | 'CompositorNodeTree';
|
|
nodeTreeName?: string;
|
|
areaType?: 'VIEW_3D' | 'IMAGE_EDITOR' | 'NODE_EDITOR' | 'GRAPH_EDITOR' | 'DOPESHEET_EDITOR' | 'SEQUENCE_EDITOR' | 'TEXT_EDITOR' | 'NLA_EDITOR' | 'CLIP_EDITOR' | 'CONSOLE' | 'INFO' | 'OUTLINER' | 'PROPERTIES' | 'FILE_BROWSER' | 'SPREADSHEET' | 'PREFERENCES';
|
|
}
|
|
export interface NativeWorkflowInput {
|
|
kind: NativeWorkflowKind;
|
|
context: NativeWorkflowContext;
|
|
resources?: { token: string; sha256: string };
|
|
desktop?: { width: number; height: number; preserveLayout: boolean; useSavedStartup?: boolean; recoveryKey?: string; recoverySavedAt?: number };
|
|
render?: { engine?: 'SCENE' | 'CYCLES' | 'BLENDER_EEVEE' | 'BLENDER_WORKBENCH'; frameStart: number; frameEnd: number; step: number; format: 'PNG' | 'OPEN_EXR' | 'FFMPEG'; resolutionPercentage?: number };
|
|
python?: { source: string; textName: string; console: boolean };
|
|
operator?: { id: string; properties: Record<string, BlenderOperatorPropertyValue> };
|
|
export?: { format: NativeExportFormat; selectedOnly: boolean; properties: Record<string, BlenderOperatorPropertyValue> };
|
|
}
|
|
|
|
export interface NativeSessionHandle {
|
|
id: string;
|
|
token: string;
|
|
sourceRevision: number;
|
|
sourceBlendSha256: string;
|
|
width: number;
|
|
height: number;
|
|
}
|
|
export type NativeSessionCommand = { kind: 'read-main' } | { kind: 'python'; source: string } | { kind: 'undo' } | { kind: 'redo' } | { kind: 'invoke'; operator: string; properties: Record<string, BlenderOperatorPropertyValue> } | { kind: 'commit' } | { kind: 'cancel-modal' } | { kind: 'preferences-save' } | { kind: 'editor'; areaType: NonNullable<NativeWorkflowContext['areaType']> } | { kind: 'clipboard'; text: string } | { kind: 'clipboard-read' } | { kind: 'file-browser'; path: string; filename?: string } | { kind: 'open-blend'; path: string } | { kind: 'download-project' };
|
|
export type NativeDesktopEvent = { type: 'move'; x: number; y: number } | { type: 'button'; x: number; y: number; button: number; down: boolean } | { type: 'key'; code: string; key?: string; locks?:{caps:boolean;num:boolean}; down: boolean } | { type: 'wheel'; delta: number; horizontal?: boolean } | { type: 'release' } | { type:'pen'; x:number; y:number; action:'move'|'press'|'release'; button:number; pressure:number; tiltX:number; tiltY:number; eraser:boolean };
|
|
export interface NativeWorkflowRequest extends NativeWorkflowInput {
|
|
schemaVersion: 1;
|
|
jobId: string;
|
|
sourceRevision: number;
|
|
sourceBlendSha256: string;
|
|
sourceBlendByteLength: number;
|
|
timeoutMs: number;
|
|
requestSha256: string;
|
|
}
|
|
export interface NativeWorkflowArtifact {
|
|
id: string;
|
|
name: string;
|
|
mime: string;
|
|
bytes: number;
|
|
sha256: string;
|
|
frame?: number;
|
|
}
|
|
export interface NativeMainReceipt { command: string; sequence: number; activeObject?: string | null; objects: { name: string; location: [number, number, number] }[]; }
|
|
export interface NativeWorkflowResult {
|
|
schemaVersion: 1;
|
|
jobId: string;
|
|
requestSha256: string;
|
|
sourceBlendSha256: string;
|
|
sourceRevision: number;
|
|
kind: NativeWorkflowKind;
|
|
status: 'RUNNING' | 'SUCCEEDED' | 'FAILED' | 'CANCELLED';
|
|
error?: string;
|
|
output?: string;
|
|
artifacts?: NativeWorkflowArtifact[];
|
|
blenderVersion?: string;
|
|
progress?: { completed: number; total: number };
|
|
resources?: { token: string; sha256: string };
|
|
}
|
|
export function canonicalNativeWorkflow(value: unknown): string {
|
|
if (Array.isArray(value)) return `[${value.map(canonicalNativeWorkflow).join(',')}]`;
|
|
if (value && typeof value === 'object') return `{${Object.entries(value).filter(([, item]) => item !== undefined).sort(([a],[b]) => a < b ? -1 : a > b ? 1 : 0).map(([key,item]) => `${JSON.stringify(key)}:${canonicalNativeWorkflow(item)}`).join(',')}}`;
|
|
if (typeof value === 'number' && !Number.isFinite(value)) throw new Error('NATIVE_WORKFLOW_NONFINITE');
|
|
return JSON.stringify(value);
|
|
}
|
|
export function encodeNativeWorkflow(request: NativeWorkflowRequest, source: ArrayBuffer): ArrayBuffer {
|
|
const metadata = new TextEncoder().encode(JSON.stringify(request));
|
|
if (metadata.length > 256 * 1024 || source.byteLength > 512 * 1024 * 1024) throw new Error('NATIVE_WORKFLOW_BUDGET');
|
|
const bytes = new Uint8Array(12 + metadata.length + source.byteLength);
|
|
bytes.set(new TextEncoder().encode('BWF1'));
|
|
const view = new DataView(bytes.buffer);
|
|
view.setUint32(4, metadata.length, true); view.setUint32(8, source.byteLength, true);
|
|
bytes.set(metadata, 12); bytes.set(new Uint8Array(source), 12 + metadata.length);
|
|
return bytes.buffer;
|
|
}
|