chore: checkpoint web Blender migration source, tools and docs
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled

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:
mes123456
2026-11-10 11:18:01 +08:00
parent 122142c26c
commit f3e21c6b98
6887 changed files with 257154 additions and 12294 deletions

View 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;
}