feat(web): close remaining editor, layout and persistence gaps
- Area headers get a per-area editor switcher backed by a shared AreaEditorContext; Preferences can hide it. - Animation camera area is a real navigable viewport aligned to the scene camera; Graph Editor adds channel selection, keyframe drag/ insert/delete and interpolation. - Geometry Nodes node locations round-trip through the native writer and reader, so saved graphs keep their layout. - Texture Paint can make linked library images local via a new native makeImageLocal command before packing strokes. - Compositor IMAGE and Render Layers nodes now persist to Main and execute against packed images / the last PBR render. - Spreadsheet row cap raised to 250k; Outliner gains Ctrl+A and arrow/ Home/End navigation. Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -89,6 +89,8 @@ export interface GeometryNodeIR {
|
||||
sockets: GeometryNodeSocketIR[];
|
||||
groupTreeId?: string | null;
|
||||
properties?: Record<string, boolean | number | string | number[]>;
|
||||
/** Canvas position in Blender node-space units. */
|
||||
location?: { x: number; y: number };
|
||||
}
|
||||
|
||||
export interface GeometryNodeLinkIR {
|
||||
@@ -421,6 +423,13 @@ export function parseGeometryNodeGraph(value: unknown): GeometryNodeGraphIR {
|
||||
if (record(socket)) socketIds.add(socket.id as string);
|
||||
});
|
||||
if (node.groupTreeId !== undefined && node.groupTreeId !== null) boundedText(node.groupTreeId, `nodes[${index}].groupTreeId`, GEOMETRY_NODE_GRAPH_BUDGET.maxIdentifierBytes);
|
||||
if (node.location !== undefined) {
|
||||
if (!record(node.location) || typeof node.location.x !== "number" || typeof node.location.y !== "number" ||
|
||||
!Number.isFinite(node.location.x) || !Number.isFinite(node.location.y) ||
|
||||
Math.abs(node.location.x) > 1_000_000 || Math.abs(node.location.y) > 1_000_000) {
|
||||
throw new GeometryNodeGraphError("GN_INVALID_GRAPH", `nodes[${index}].location is invalid`, `nodes[${index}].location`);
|
||||
}
|
||||
}
|
||||
if (node.properties !== undefined) {
|
||||
if (!record(node.properties) || Object.keys(node.properties).length > 64) throw new GeometryNodeGraphError("GN_INVALID_GRAPH", `nodes[${index}].properties is invalid`, `nodes[${index}].properties`);
|
||||
for (const [name, property] of Object.entries(node.properties)) {
|
||||
|
||||
Reference in New Issue
Block a user