Browse Source

fix: PR review fixes — lazy-load three.js, blob URL cleanup, missing integrations

- Remove unused `useLoader` import from GLBViewerNode
- Add useEffect cleanup to revoke blob URL on node unmount (memory leak)
- Replace alert() with useToast for file validation errors
- Lazy-load GLBViewerNode via next/dynamic to avoid bundling three.js for all users
- Add glbViewer to ConnectionDropMenu IMAGE_SOURCE_OPTIONS
- Add glbViewer case to quickstart validation createDefaultNodeData (build fix)
- Restore trailing newline in gemini.ts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
handoff-20260429-1057
shrimbly 5 months ago
parent
commit
7bd2e996aa
  1. 2
      src/app/api/generate/providers/gemini.ts
  2. 9
      src/components/ConnectionDropMenu.tsx
  3. 5
      src/components/WorkflowCanvas.tsx
  4. 17
      src/components/nodes/GLBViewerNode.tsx
  5. 6
      src/lib/quickstart/validation.ts

2
src/app/api/generate/providers/gemini.ts

@ -180,4 +180,4 @@ export async function generateWithGemini(
},
{ status: 500 }
);
}
}

9
src/components/ConnectionDropMenu.tsx

@ -149,6 +149,15 @@ const IMAGE_SOURCE_OPTIONS: MenuOption[] = [
</svg>
),
},
{
type: "glbViewer",
label: "3D Viewer",
icon: (
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
<path strokeLinecap="round" strokeLinejoin="round" d="M21 7.5l-2.25-1.313M21 7.5v2.25m0-2.25l-2.25 1.313M3 7.5l2.25-1.313M3 7.5l2.25 1.313M3 7.5v2.25m9 3l2.25-1.313M12 12.75l-2.25-1.313M12 12.75V15m0 6.75l2.25-1.313M12 21.75V19.5m0 2.25l-2.25-1.313m0-16.875L12 2.25l2.25 1.313M21 14.25v2.25l-2.25 1.313m-13.5 0L3 16.5v-2.25" />
</svg>
),
},
{
type: "annotation",
label: "Annotate",

5
src/components/WorkflowCanvas.tsx

@ -19,6 +19,7 @@ import "@xyflow/react/dist/style.css";
import { useWorkflowStore, WorkflowFile } from "@/store/workflowStore";
import { useToast } from "@/components/Toast";
import dynamic from "next/dynamic";
import {
ImageInputNode,
AudioInputNode,
@ -34,8 +35,10 @@ import {
ImageCompareNode,
VideoStitchNode,
EaseCurveNode,
GLBViewerNode,
} from "./nodes";
// Lazy-load GLBViewerNode to avoid bundling three.js for users who don't use 3D nodes
const GLBViewerNode = dynamic(() => import("./nodes/GLBViewerNode").then(mod => ({ default: mod.GLBViewerNode })), { ssr: false });
import { EditableEdge, ReferenceEdge } from "./edges";
import { ConnectionDropMenu, MenuAction } from "./ConnectionDropMenu";
import { MultiSelectToolbar } from "./MultiSelectToolbar";

17
src/components/nodes/GLBViewerNode.tsx

@ -2,11 +2,12 @@
import { useCallback, useRef, useState, useEffect, Suspense } from "react";
import { Handle, Position, NodeProps, Node, useReactFlow } from "@xyflow/react";
import { Canvas, useThree, useFrame, useLoader } from "@react-three/fiber";
import { Canvas, useThree, useFrame } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";
import { BaseNode } from "./BaseNode";
import { useCommentNavigation } from "@/hooks/useCommentNavigation";
import { useWorkflowStore } from "@/store/workflowStore";
import { useToast } from "@/components/Toast";
import { GLBViewerNodeData } from "@/types";
import * as THREE from "three";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
@ -255,16 +256,26 @@ export function GLBViewerNode({ id, data, selected }: NodeProps<GLBViewerNodeTyp
});
}, [id, nodeData.capturedImage, getNodes, setNodes]);
// Revoke blob URL when node is unmounted (e.g. deleted from canvas)
useEffect(() => {
return () => {
if (nodeData.glbUrl) {
URL.revokeObjectURL(nodeData.glbUrl);
}
};
// eslint-disable-next-line react-hooks/exhaustive-deps -- only revoke on unmount
}, []);
// Shared file processing logic for both click-to-upload and drag-and-drop
const processFile = useCallback(
(file: File) => {
if (!file.name.toLowerCase().endsWith(".glb")) {
alert("Please upload a .GLB file.");
useToast.getState().show("Please upload a .GLB file", "warning");
return;
}
if (file.size > 100 * 1024 * 1024) {
alert("File too large. Maximum size is 100MB.");
useToast.getState().show("File too large. Maximum size is 100MB", "warning");
return;
}

6
src/lib/quickstart/validation.ts

@ -316,6 +316,12 @@ function createDefaultNodeData(type: NodeType): WorkflowNodeData {
progress: 0,
encoderSupported: null,
};
case "glbViewer":
return {
glbUrl: null,
filename: null,
capturedImage: null,
};
}
}

Loading…
Cancel
Save