chore: checkpoint web Blender migration source, tools and docs
Accumulated uncommitted work: GHOST Web backend, WebGL/GPU web layer, web workflow engine and protocol, tooling, docs and task records. Large generated evidence, user-testing archives, build snapshots and imported test assets remain untracked. Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
86
web/protocol/native-workflow.ts
Normal file
86
web/protocol/native-workflow.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
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[] };
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user