From fd4c270182341a9a4ddd350a1494e1c910f9587d Mon Sep 17 00:00:00 2001 From: tejasM17 Date: Thu, 16 Jul 2026 13:03:27 +0530 Subject: [PATCH] Revert "Development" --- components/editor/canvas-node.tsx | 83 -------- components/editor/canvas.tsx | 203 +++---------------- components/editor/shape-panel.tsx | 117 ----------- components/editor/workspace-shell.tsx | 65 +++--- context/feature-specs/12-shape-panel.md | 43 ---- context/feature-specs/frontend-componants.md | 80 ++++++++ context/progress-tracker.md | 8 +- 7 files changed, 147 insertions(+), 452 deletions(-) delete mode 100644 components/editor/canvas-node.tsx delete mode 100644 components/editor/shape-panel.tsx delete mode 100644 context/feature-specs/12-shape-panel.md create mode 100644 context/feature-specs/frontend-componants.md diff --git a/components/editor/canvas-node.tsx b/components/editor/canvas-node.tsx deleted file mode 100644 index 51d3b5e..0000000 --- a/components/editor/canvas-node.tsx +++ /dev/null @@ -1,83 +0,0 @@ -"use client"; - -import { memo, useMemo } from "react"; -import { Handle, Position, type NodeProps } from "@xyflow/react"; - -import { NODE_COLORS, type NodeColorName, type NodeShape } from "@/types/canvas"; - -/** - * Data attached to canvas nodes. - */ -interface CanvasNodeData { - label: string; - color: NodeColorName; - shape: NodeShape; -} - -/** - * Basic canvas node renderer. For this unit, renders every shape as a - * simple bordered rectangle with the label centered. Shape-specific - * visuals will be added later. - */ -function CanvasNode({ data, selected }: NodeProps) { - const { label, color, shape } = data as unknown as CanvasNodeData; - - // Get color from palette - const colorPair = useMemo(() => { - return NODE_COLORS.find((c) => c.name === color) ?? NODE_COLORS[0]; - }, [color]); - - // Basic shape styles - for now, all shapes render as bordered rectangles - // Shape-specific rendering will be added in future features - const shapeStyles = useMemo(() => { - const baseStyles = { - width: "100%", - height: "100%", - display: "flex", - alignItems: "center", - justifyContent: "center", - borderRadius: shape === "circle" ? "9999px" : shape === "pill" ? "9999px" : "8px", - backgroundColor: colorPair.fill, - border: `2px ${selected ? "var(--accent-primary)" : colorPair.fill}`, - boxShadow: selected - ? `0 0 0 2px var(--accent-primary), 0 4px 12px rgba(0,0,0,0.3)` - : "none", - }; - return baseStyles; - }, [colorPair.fill, selected, shape]); - - return ( -
- - - {label || "Node"} - - -
- ); -} - -/** - * Memoized custom node component for React Flow. - */ -export const CanvasNodeComponent = memo(CanvasNode, (prev, next) => { - return ( - prev.data.label === next.data.label && - prev.data.color === next.data.color && - prev.data.shape === next.data.shape && - prev.selected === next.selected - ); -}); - -CanvasNodeComponent.displayName = "CanvasNode"; \ No newline at end of file diff --git a/components/editor/canvas.tsx b/components/editor/canvas.tsx index 11d9c5d..10c72ae 100644 --- a/components/editor/canvas.tsx +++ b/components/editor/canvas.tsx @@ -2,11 +2,8 @@ import { Component, - useCallback, useEffect, - useMemo, useState, - type DragEvent, type ErrorInfo, type ReactNode, } from "react"; @@ -16,12 +13,6 @@ import { ConnectionMode, MiniMap, ReactFlow, - ReactFlowProvider, - useReactFlow, - type NodeChange, - type EdgeChange, - type OnConnect, - type OnDelete, } from "@xyflow/react"; import "@xyflow/react/dist/style.css"; @@ -36,12 +27,7 @@ import "@liveblocks/react-flow/styles.css"; import type { CanvasEdge, CanvasNode, - CanvasNodeData, - NodeShape, } from "@/types/canvas"; -import { NODE_COLORS, type NodeColorName } from "@/types/canvas"; -import { ShapePanel, type ShapePanelPayload } from "./shape-panel"; -import { CanvasNodeComponent } from "./canvas-node"; interface CanvasProps { roomId: string; @@ -117,25 +103,9 @@ class CanvasErrorBoundary extends Component< } } -/** - * Counter for generating unique node IDs. - */ -let nodeIdCounter = 0; - -/** - * Generate a unique node ID using shape name, timestamp, and counter. - */ -function generateNodeId(shape: NodeShape): string { - const timestamp = Date.now(); - return `${shape}-${timestamp}-${++nodeIdCounter}`; -} - /** * The actual React Flow surface, rendered after Liveblocks has connected * and the storage layer is ready (via Suspense). - * - * Note: This component is wrapped with ReactFlowProvider to allow using - * useReactFlow() hook for coordinate conversion. */ function CollaborativeFlow() { // `useLiveblocksFlow` returns the synced nodes/edges plus change @@ -149,145 +119,38 @@ function CollaborativeFlow() { }); return ( - - - - ); -} - -/** - * Inner component that has access to ReactFlow context via ReactFlowProvider. - * This is where we can use useReactFlow() hook. - */ -function FlowCanvas({ - nodes, - edges, - onNodesChange, - onEdgesChange, - onConnect, - onDelete, -}: { - nodes: CanvasNode[]; - edges: CanvasEdge[]; - onNodesChange: (changes: NodeChange[]) => void; - onEdgesChange: (changes: EdgeChange[]) => void; - onConnect: OnConnect; - onDelete: (params: { nodes: CanvasNode[]; edges: CanvasEdge[] }) => void; -}) { - // Get the React Flow instance for coordinate conversion - const { screenToFlowPosition } = useReactFlow(); - - // Custom node types - memoized to keep references stable - const nodeTypes = useMemo( - () => ({ canvasNode: CanvasNodeComponent }), - [], - ); - - // Handle drag over on the canvas wrapper - const handleDragOver = useCallback((event: DragEvent) => { - event.preventDefault(); - event.dataTransfer.dropEffect = "copy"; - }, []); - - // Handle drop on the canvas wrapper - const handleDrop = useCallback( - (event: DragEvent) => { - event.preventDefault(); - - // Read the shape payload from dataTransfer - const payloadData = event.dataTransfer.getData("application/json"); - if (!payloadData) return; - - try { - const payload: ShapePanelPayload = JSON.parse(payloadData); - const { shape, width, height } = payload; - - // Convert screen position to canvas coordinates - const position = screenToFlowPosition({ - x: event.clientX, - y: event.clientY, - }); - - // Create new node - use CanvasNode type from types/canvas - const newNode: CanvasNode = { - id: generateNodeId(shape), - type: "canvasNode", - position, - data: { - label: "", - color: NODE_COLORS[0].name as NodeColorName, - shape, - }, - measured: { - width, - height, - }, - }; - - // Add the node via onNodesChange with add change - const addChange: NodeChange = { - type: "add", - item: newNode, - }; - onNodesChange([addChange]); - } catch (error) { - console.error("Failed to parse shape payload:", error); - } - }, - [screenToFlowPosition, onNodesChange], - ); - - return ( -
- - - - - -
+ + + ); } /** - * Loading state shown inside the Liveblocks Suspense boundary. - * Uses transparent background to blend with the infinite canvas. + * Loading state shown inside the Liveblocks Suspense boundary. Matches + * the editor's dark surface palette so it doesn't flash a light fallback. */ function CanvasLoading() { const [dots, setDots] = useState(1); @@ -300,9 +163,9 @@ function CanvasLoading() { }, []); return ( -
+
-
+

@@ -321,13 +184,13 @@ interface CanvasErrorProps { /** * Fallback rendered when the Liveblocks connection cannot be established * (auth failure, room ID changed, full room, etc.). Keeps the user on the - * page with a clear next step. Uses transparent background for infinite canvas look. + * page with a clear next step. */ function CanvasError({ message, onRetry }: CanvasErrorProps) { return ( -

+
-
+

Canvas unavailable

@@ -336,7 +199,7 @@ function CanvasError({ message, onRetry }: CanvasErrorProps) { diff --git a/components/editor/shape-panel.tsx b/components/editor/shape-panel.tsx deleted file mode 100644 index d3b0867..0000000 --- a/components/editor/shape-panel.tsx +++ /dev/null @@ -1,117 +0,0 @@ -"use client"; - -import { useCallback, useRef, useState } from "react"; -import { - Square, - Diamond, - Circle, - Pill, - Cylinder, - Hexagon, - type LucideIcon, -} from "lucide-react"; - -import type { NodeShape } from "@/types/canvas"; - -/** - * Shape definitions with their icons and default sizes. - * Default sizes follow the spec: rectangles wider than tall, circles square, - * diamonds slightly larger for labels. - */ -const SHAPES: Array<{ - name: NodeShape; - icon: LucideIcon; - width: number; - height: number; -}> = [ - { name: "rectangle", icon: Square, width: 180, height: 100 }, - { name: "diamond", icon: Diamond, width: 140, height: 140 }, - { name: "circle", icon: Circle, width: 120, height: 120 }, - { name: "pill", icon: Pill, width: 180, height: 80 }, - { name: "cylinder", icon: Cylinder, width: 100, height: 120 }, - { name: "hexagon", icon: Hexagon, width: 140, height: 120 }, -]; - -/** - * Shape panel payload transferred via drag-and-drop. - */ -export interface ShapePanelPayload { - shape: NodeShape; - width: number; - height: number; -} - -/** - * Floating pill-shaped toolbar at the bottom-center of the canvas. - * Contains draggable icon buttons for each supported shape. - */ -export function ShapePanel() { - return ( -
-
- {SHAPES.map((shape) => ( - - ))} -
-
- ); -} - -interface ShapeButtonProps { - name: NodeShape; - Icon: LucideIcon; - width: number; - height: number; -} - -function ShapeButton({ name, Icon, width, height }: ShapeButtonProps) { - const [isDragging, setIsDragging] = useState(false); - - const handleDragStart = useCallback( - (e: React.DragEvent) => { - const payload: ShapePanelPayload = { shape: name, width, height }; - e.dataTransfer.setData("application/json", JSON.stringify(payload)); - e.dataTransfer.effectAllowed = "copy"; - setIsDragging(true); - }, - [name, width, height], - ); - - const handleDragEnd = useCallback(() => { - setIsDragging(false); - }, []); - - return ( - - ); -} \ No newline at end of file diff --git a/components/editor/workspace-shell.tsx b/components/editor/workspace-shell.tsx index 19bab94..f7eafa9 100644 --- a/components/editor/workspace-shell.tsx +++ b/components/editor/workspace-shell.tsx @@ -57,21 +57,20 @@ export function WorkspaceShell({ aiSidebarOpen={isAiSidebarOpen} onShareClick={() => {}} /> - {/* Canvas area with access denied message */} -
+
+ setIsSidebarOpen(false)} + ownedProjects={ownedProjects} + sharedProjects={sharedProjects} + currentRoomId={currentRoomId} + /> -
- setIsSidebarOpen(false)} - ownedProjects={ownedProjects} - sharedProjects={sharedProjects} - currentRoomId={currentRoomId} - /> - setIsAiSidebarOpen(false)} - /> + setIsAiSidebarOpen(false)} + /> +
); } @@ -86,22 +85,20 @@ export function WorkspaceShell({ aiSidebarOpen={isAiSidebarOpen} onShareClick={shareDialog.onOpenChange} /> - {/* Canvas fills full viewport - sidebars float over it */} -
+
+ setIsSidebarOpen(false)} + ownedProjects={ownedProjects} + sharedProjects={sharedProjects} + currentRoomId={currentRoomId} + /> -
- {/* Sidebars rendered outside canvas container so they float over it */} - setIsSidebarOpen(false)} - ownedProjects={ownedProjects} - sharedProjects={sharedProjects} - currentRoomId={currentRoomId} - /> - setIsAiSidebarOpen(false)} - /> + setIsAiSidebarOpen(false)} + /> + {/* Share Dialog */}
@@ -389,7 +386,7 @@ function AiSidebar({ isOpen, onClose }: AiSidebarProps) { onClick={onClose} aria-label="Close AI chat" className={cn( - "fixed inset-0 z-40 bg-bg-base/60 backdrop-blur-sm transition-opacity duration-300 ease-in-out md:hidden", + "fixed inset-0 z-30 bg-bg-base/60 backdrop-blur-sm transition-opacity duration-300 ease-in-out md:hidden", isOpen ? "pointer-events-auto opacity-100" : "pointer-events-none opacity-0", @@ -398,7 +395,7 @@ function AiSidebar({ isOpen, onClose }: AiSidebarProps) {