From affffe09c801ab0c17fef12fea0a11621cc1803c Mon Sep 17 00:00:00 2001 From: Vishnu Date: Mon, 25 Aug 2025 14:08:25 +0530 Subject: [PATCH 1/6] feat: measurement tool axis lock added, style update, no animation ui update --- .../properties/AssetProperties.tsx | 11 +- .../contextControls/contextControls.tsx | 441 +++++++++--------- .../modules/scene/tools/measurementTool.tsx | 93 +++- app/src/styles/layout/sidebar.scss | 4 + 4 files changed, 332 insertions(+), 217 deletions(-) diff --git a/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx b/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx index 4f76584..5d0a97c 100644 --- a/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx @@ -103,9 +103,16 @@ const AssetProperties: React.FC = () => {
Animations
- {assets.map((asset) => { + {assets.map((asset, i) => { if (asset.modelUuid !== selectedFloorItem.uuid || !asset.animations) - return null; + return ( + i === 0 && ( +
+ Looks like there are no preset animations yet. Stay tuned for + future additions! +
+ ) + ); return asset.animations.map((animation, index) => (
diff --git a/app/src/modules/scene/controls/contextControls/contextControls.tsx b/app/src/modules/scene/controls/contextControls/contextControls.tsx index 1d0bbfa..142f8d1 100644 --- a/app/src/modules/scene/controls/contextControls/contextControls.tsx +++ b/app/src/modules/scene/controls/contextControls/contextControls.tsx @@ -1,222 +1,243 @@ -import { useEffect, useRef, useState } from 'react'; -import { useThree } from '@react-three/fiber'; -import { CameraControls, Html, ScreenSpace } from '@react-three/drei'; -import { useContextActionStore, useRenameModeStore, useSelectedAssets } from '../../../../store/builder/store'; -import ContextMenu from '../../../../components/ui/menu/contextMenu'; +import { useEffect, useRef, useState } from "react"; +import { useThree } from "@react-three/fiber"; +import { CameraControls, Html, ScreenSpace } from "@react-three/drei"; +import { + useContextActionStore, + useRenameModeStore, + useSelectedAssets, +} from "../../../../store/builder/store"; +import ContextMenu from "../../../../components/ui/menu/contextMenu"; function ContextControls() { - const { gl, controls } = useThree(); - const [canRender, setCanRender] = useState(false); - const [visibility, setVisibility] = useState({ rename: true, focus: true, flipX: true, flipZ: true, move: true, rotate: true, duplicate: true, copy: true, paste: true, modifier: false, group: false, array: false, delete: true, }); - const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 }); - const { selectedAssets } = useSelectedAssets(); - const { setContextAction } = useContextActionStore(); - const { setIsRenameMode } = useRenameModeStore(); - const rightDrag = useRef(false); - const isRightMouseDown = useRef(false); + const { gl, controls } = useThree(); + const [canRender, setCanRender] = useState(false); + const [visibility, setVisibility] = useState({ + rename: true, + focus: true, + flipX: true, + flipZ: true, + move: true, + rotate: true, + duplicate: true, + copy: true, + paste: true, + modifier: false, + group: false, + array: false, + delete: true, + }); + const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 }); + const { selectedAssets } = useSelectedAssets(); + const { setContextAction } = useContextActionStore(); + const { setIsRenameMode } = useRenameModeStore(); + const rightDrag = useRef(false); + const isRightMouseDown = useRef(false); - useEffect(() => { - if (selectedAssets.length === 1) { - setVisibility({ - rename: true, - focus: true, - flipX: true, - flipZ: true, - move: true, - rotate: true, - duplicate: true, - copy: true, - paste: true, - modifier: false, - group: false, - array: false, - delete: true, - }); - } else if (selectedAssets.length > 1) { - setVisibility({ - rename: false, - focus: true, - flipX: true, - flipZ: true, - move: true, - rotate: true, - duplicate: true, - copy: true, - paste: true, - modifier: false, - group: true, - array: false, - delete: true, - }); - } else { - setVisibility({ - rename: false, - focus: false, - flipX: false, - flipZ: false, - move: false, - rotate: false, - duplicate: false, - copy: false, - paste: false, - modifier: false, - group: false, - array: false, - delete: false, - }); + useEffect(() => { + if (selectedAssets.length === 1) { + setVisibility({ + rename: true, + focus: true, + flipX: true, + flipZ: true, + move: true, + rotate: true, + duplicate: true, + copy: true, + paste: true, + modifier: false, + group: false, + array: false, + delete: true, + }); + } else if (selectedAssets.length > 1) { + setVisibility({ + rename: false, + focus: true, + flipX: true, + flipZ: true, + move: true, + rotate: true, + duplicate: true, + copy: true, + paste: true, + modifier: false, + group: true, + array: false, + delete: true, + }); + } else { + setVisibility({ + rename: false, + focus: false, + flipX: false, + flipZ: false, + move: false, + rotate: false, + duplicate: false, + copy: false, + paste: false, + modifier: false, + group: false, + array: false, + delete: false, + }); + } + }, [selectedAssets]); + + useEffect(() => { + const canvasElement = gl.domElement; + + const onPointerDown = (evt: any) => { + if (evt.button === 2) { + isRightMouseDown.current = true; + rightDrag.current = false; + } + }; + + const onPointerMove = () => { + if (isRightMouseDown.current) { + rightDrag.current = true; + } + }; + + const onPointerUp = (evt: any) => { + if (evt.button === 2) { + isRightMouseDown.current = false; + } + }; + + const handleContextClick = (event: MouseEvent) => { + event.preventDefault(); + if (rightDrag.current) return; + if (selectedAssets.length > 0) { + setMenuPosition({ + x: event.clientX - gl.domElement.width / 2, + y: event.clientY - gl.domElement.height / 2, + }); + setCanRender(true); + if (controls) { + (controls as CameraControls).enabled = false; } - }, [selectedAssets]); - - useEffect(() => { - const canvasElement = gl.domElement; - - const onPointerDown = (evt: any) => { - if (evt.button === 2) { - isRightMouseDown.current = true; - rightDrag.current = false; - } - }; - - const onPointerMove = () => { - if (isRightMouseDown.current) { - rightDrag.current = true; - } - }; - - const onPointerUp = (evt: any) => { - if (evt.button === 2) { - isRightMouseDown.current = false; - } - }; - - const handleContextClick = (event: MouseEvent) => { - event.preventDefault(); - if (rightDrag.current) return; - if (selectedAssets.length > 0) { - setMenuPosition({ x: event.clientX - gl.domElement.width / 2, y: event.clientY - gl.domElement.height / 2 }); - setCanRender(true); - if (controls) { - (controls as CameraControls).enabled = false; - } - } else { - setCanRender(false); - if (controls) { - (controls as CameraControls).enabled = true; - } - } - }; - - if (selectedAssets.length > 0) { - canvasElement.addEventListener('pointerdown', onPointerDown); - canvasElement.addEventListener('pointermove', onPointerMove); - canvasElement.addEventListener('pointerup', onPointerUp); - canvasElement.addEventListener('contextmenu', handleContextClick) - } else { - setCanRender(false); - if (controls) { - (controls as CameraControls).enabled = true; - } - setMenuPosition({ x: 0, y: 0 }); - } - - return () => { - canvasElement.removeEventListener('pointerdown', onPointerDown); - canvasElement.removeEventListener('pointermove', onPointerMove); - canvasElement.removeEventListener('pointerup', onPointerUp); - canvasElement.removeEventListener('contextmenu', handleContextClick); - }; - }, [gl, selectedAssets]); - - const handleAssetRename = () => { + } else { setCanRender(false); if (controls) { - (controls as CameraControls).enabled = true; + (controls as CameraControls).enabled = true; } - setContextAction("renameAsset"); - setIsRenameMode(true); - } - const handleAssetFocus = () => { - setCanRender(false); - if (controls) { - (controls as CameraControls).enabled = true; - } - setContextAction("focusAsset"); - } - const handleAssetMove = () => { - setCanRender(false); - if (controls) { - (controls as CameraControls).enabled = true; - } - setContextAction("moveAsset") - } - const handleAssetRotate = () => { - setCanRender(false); - if (controls) { - (controls as CameraControls).enabled = true; - } - setContextAction("rotateAsset") - } - const handleAssetCopy = () => { - setCanRender(false); - if (controls) { - (controls as CameraControls).enabled = true; - } - setContextAction("copyAsset") - } - const handleAssetPaste = () => { - setCanRender(false); - if (controls) { - (controls as CameraControls).enabled = true; - } - setContextAction("pasteAsset") - } - const handleAssetDelete = () => { - setCanRender(false); - if (controls) { - (controls as CameraControls).enabled = true; - } - setContextAction("deleteAsset") - } - const handleAssetDuplicate = () => { - setCanRender(false); - if (controls) { - (controls as CameraControls).enabled = true; - } - setContextAction("duplicateAsset") + } + }; + + if (selectedAssets.length > 0) { + canvasElement.addEventListener("pointerdown", onPointerDown); + canvasElement.addEventListener("pointermove", onPointerMove); + canvasElement.addEventListener("pointerup", onPointerUp); + canvasElement.addEventListener("contextmenu", handleContextClick); + } else { + setCanRender(false); + if (controls) { + (controls as CameraControls).enabled = true; + } + setMenuPosition({ x: 0, y: 0 }); } - return ( - <> - {canRender && ( - - - handleAssetRename()} - onFocus={() => handleAssetFocus()} - onFlipX={() => console.log("Flip to X")} - onFlipZ={() => console.log("Flip to Z")} - onMove={() => handleAssetMove()} - onRotate={() => handleAssetRotate()} - onDuplicate={() => handleAssetDuplicate()} - onCopy={() => handleAssetCopy()} - onPaste={() => handleAssetPaste()} - onGroup={() => console.log("Group")} - onArray={() => console.log("Array")} - onDelete={() => handleAssetDelete()} - /> - - - )} - - ); + return () => { + canvasElement.removeEventListener("pointerdown", onPointerDown); + canvasElement.removeEventListener("pointermove", onPointerMove); + canvasElement.removeEventListener("pointerup", onPointerUp); + canvasElement.removeEventListener("contextmenu", handleContextClick); + }; + }, [controls, gl, selectedAssets]); + + const handleAssetRename = () => { + setCanRender(false); + if (controls) { + (controls as CameraControls).enabled = true; + } + setContextAction("renameAsset"); + setIsRenameMode(true); + }; + const handleAssetFocus = () => { + setCanRender(false); + if (controls) { + (controls as CameraControls).enabled = true; + } + setContextAction("focusAsset"); + }; + const handleAssetMove = () => { + setCanRender(false); + if (controls) { + (controls as CameraControls).enabled = true; + } + setContextAction("moveAsset"); + }; + const handleAssetRotate = () => { + setCanRender(false); + if (controls) { + (controls as CameraControls).enabled = true; + } + setContextAction("rotateAsset"); + }; + const handleAssetCopy = () => { + setCanRender(false); + if (controls) { + (controls as CameraControls).enabled = true; + } + setContextAction("copyAsset"); + }; + const handleAssetPaste = () => { + setCanRender(false); + if (controls) { + (controls as CameraControls).enabled = true; + } + setContextAction("pasteAsset"); + }; + const handleAssetDelete = () => { + setCanRender(false); + if (controls) { + (controls as CameraControls).enabled = true; + } + setContextAction("deleteAsset"); + }; + const handleAssetDuplicate = () => { + setCanRender(false); + if (controls) { + (controls as CameraControls).enabled = true; + } + setContextAction("duplicateAsset"); + }; + + return ( + <> + {canRender && ( + + + handleAssetRename()} + onFocus={() => handleAssetFocus()} + onFlipX={() => console.log("Flip to X")} + onFlipZ={() => console.log("Flip to Z")} + onMove={() => handleAssetMove()} + onRotate={() => handleAssetRotate()} + onDuplicate={() => handleAssetDuplicate()} + onCopy={() => handleAssetCopy()} + onPaste={() => handleAssetPaste()} + onGroup={() => console.log("Group")} + onArray={() => console.log("Array")} + onDelete={() => handleAssetDelete()} + /> + + + )} + + ); } export default ContextControls; diff --git a/app/src/modules/scene/tools/measurementTool.tsx b/app/src/modules/scene/tools/measurementTool.tsx index 4ca56fd..9f8189d 100644 --- a/app/src/modules/scene/tools/measurementTool.tsx +++ b/app/src/modules/scene/tools/measurementTool.tsx @@ -1,5 +1,5 @@ import * as THREE from "three"; -import { useEffect, useRef, useState } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; import { useThree, useFrame } from "@react-three/fiber"; import { useToolMode } from "../../../store/builder/store"; import { Html, Line } from "@react-three/drei"; @@ -10,7 +10,81 @@ const MeasurementTool = () => { const [points, setPoints] = useState([]); const [linePoints, setLinePoints] = useState(null); + const [axisLock, setAxisLock] = useState<"x" | "y" | "z" | null>(null); const groupRef = useRef(null); + const keysPressed = useRef>(new Set()); + + // Axis lock key handling + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if (e.altKey) { + if (e.key.toLowerCase() === "x") keysPressed.current.add("x"); + else if (e.key.toLowerCase() === "y") keysPressed.current.add("y"); + else if (e.key.toLowerCase() === "z") keysPressed.current.add("z"); + + if (keysPressed.current.has("x")) setAxisLock("x"); + else if (keysPressed.current.has("y")) setAxisLock("y"); + else if (keysPressed.current.has("z")) setAxisLock("z"); + } else if (e.key === "Escape") { + setPoints([]); + setLinePoints(null); + setAxisLock(null); + } + }; + + const handleKeyUp = (e: KeyboardEvent) => { + keysPressed.current.delete(e.key.toLowerCase()); + if (keysPressed.current.has("x")) setAxisLock("x"); + else if (keysPressed.current.has("y")) setAxisLock("y"); + else if (keysPressed.current.has("z")) setAxisLock("z"); + else setAxisLock(null); + }; + + window.addEventListener("keydown", handleKeyDown); + window.addEventListener("keyup", handleKeyUp); + return () => { + window.removeEventListener("keydown", handleKeyDown); + window.removeEventListener("keyup", handleKeyUp); + }; + }, []); + + const getLineColor = useCallback(() => { + switch (axisLock) { + case "x": + return "#d94522"; // Red for X axis + case "y": + return "#22ab2e"; // Green for Y axis + case "z": + return "#227bd9"; // Blue for Z axis + default: + return "#b18ef1"; // Default purple + } + }, [axisLock]); + + // Apply axis lock to a point + const applyAxisLock = useCallback( + (point: THREE.Vector3, referencePoint: THREE.Vector3) => { + const lockedPoint = point.clone(); + + switch (axisLock) { + case "x": + lockedPoint.y = referencePoint.y; + lockedPoint.z = referencePoint.z; + break; + case "y": + lockedPoint.x = referencePoint.x; + lockedPoint.z = referencePoint.z; + break; + case "z": + lockedPoint.x = referencePoint.x; + lockedPoint.y = referencePoint.y; + break; + } + + return lockedPoint; + }, + [axisLock] + ); useEffect(() => { const canvasElement = gl.domElement; @@ -45,7 +119,13 @@ const MeasurementTool = () => { ); if (intersects.length > 0) { - const intersectionPoint = intersects[0].point.clone(); + let intersectionPoint = intersects[0].point.clone(); + if (axisLock && points.length > 0) { + intersectionPoint = applyAxisLock( + intersectionPoint, + points[points.length - 1] + ); + } if (points.length < 2) { setPoints([...points, intersectionPoint]); } else { @@ -84,7 +164,7 @@ const MeasurementTool = () => { canvasElement.removeEventListener("contextmenu", onContextMenu); }; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [toolMode, camera, raycaster, pointer, scene, points]); + }, [toolMode, camera, raycaster, pointer, scene, points, axisLock]); useFrame(() => { if (points.length === 1) { @@ -107,7 +187,10 @@ const MeasurementTool = () => { ); if (intersects.length > 0) { - const tempEnd = intersects[0].point.clone(); + let tempEnd = intersects[0].point.clone(); + if (axisLock) { + tempEnd = applyAxisLock(tempEnd, points[0]); + } updateMeasurement(points[0], tempEnd); } } else if (points.length === 2) { @@ -139,7 +222,7 @@ const MeasurementTool = () => { {/* Main line */} Date: Mon, 25 Aug 2025 15:56:30 +0530 Subject: [PATCH 2/6] refactor: improve code readability and maintainability in MainScene, Messages, and ThreadChat components; adjust comments.scss for better layout --- .../components/layout/scenes/MainScene.tsx | 6 +++- .../components/ui/collaboration/Messages.tsx | 31 ++++++++++++------- .../ui/collaboration/ThreadChat.tsx | 16 +++++++--- app/src/styles/scene/comments.scss | 4 +-- 4 files changed, 38 insertions(+), 19 deletions(-) diff --git a/app/src/components/layout/scenes/MainScene.tsx b/app/src/components/layout/scenes/MainScene.tsx index f5e0c72..350c9e9 100644 --- a/app/src/components/layout/scenes/MainScene.tsx +++ b/app/src/components/layout/scenes/MainScene.tsx @@ -212,7 +212,11 @@ function MainScene() { {activeModule !== "market" && !selectedUser &&
} - {(commentPositionState !== null || selectedComment !== null) && } + + { + (commentPositionState !== null || selectedComment !== null) && + + } ); diff --git a/app/src/components/ui/collaboration/Messages.tsx b/app/src/components/ui/collaboration/Messages.tsx index b3f37e1..11e87b2 100644 --- a/app/src/components/ui/collaboration/Messages.tsx +++ b/app/src/components/ui/collaboration/Messages.tsx @@ -231,7 +231,10 @@ const Messages: React.FC = ({ val, i, setMessages, mode, setIsEdit
{isEditableThread ? getRelativeTime(val.createdAt) : val.createdAt}
{(val as Reply).creatorId === userId && ( -
+
setOpenOptions(false)} + > + {openOptions && (
- {!(isEditableThread) && } + + {!isEditableThread && ( + + )}
)}
)} +
{"comment" in val ? val.comment : val.threadTitle}
+
)} diff --git a/app/src/components/ui/collaboration/ThreadChat.tsx b/app/src/components/ui/collaboration/ThreadChat.tsx index 8e4b24b..5370689 100644 --- a/app/src/components/ui/collaboration/ThreadChat.tsx +++ b/app/src/components/ui/collaboration/ThreadChat.tsx @@ -131,9 +131,10 @@ const ThreadChat: React.FC = () => { if (dragging) updatePosition(e, true); }; - useEffect(() => { - updatePosition({ clientX: position.x, clientY: position.y }, true); - }, [selectedComment]); + // Commented this useEffect to prevent offset after user saved the comment + // useEffect(() => { + // updatePosition({ clientX: position.x, clientY: position.y }, true); + // }, [selectedComment]); const handlePointerUp = (event: React.PointerEvent) => { @@ -144,6 +145,10 @@ const ThreadChat: React.FC = () => { }; const handleCreateComments = async (e: any) => { + // Continue send or create message only there is only value avalibale + // To prevent empty value + + if (!value) return; e.preventDefault(); try { // const createComments = await addCommentsApi(projectId, value, selectedComment?.threadId, selectedVersion?.versionId || "")/ @@ -163,6 +168,7 @@ const ThreadChat: React.FC = () => { // } + if (threadSocket && mode === "create") { const addComment = { versionId: selectedVersion?.versionId || "", @@ -190,7 +196,7 @@ const ThreadChat: React.FC = () => { // removeComment(selectedComment?.threadId) // setSelectedComment([]) // } - console.log('threadSocket:threadChat ', threadSocket); + if (threadSocket) { // projectId, userId, organization, threadId const deleteThread = { @@ -258,7 +264,7 @@ const ThreadChat: React.FC = () => { }; if (threadSocket) { - console.log('createThread: ', createThread); + setInputActive(false); threadSocket.emit("v1:thread:create", createThread); diff --git a/app/src/styles/scene/comments.scss b/app/src/styles/scene/comments.scss index 480594a..ed4a1be 100644 --- a/app/src/styles/scene/comments.scss +++ b/app/src/styles/scene/comments.scss @@ -171,8 +171,8 @@ .messages-wrapper { padding: 12px; padding-top: 0; - max-height: 50vh; - overflow-y: auto; + max-height: 36vh; + overflow: auto; .edit-container { .input-container { textarea { From 246236c15fe085cdca5fb007a3f04594b69d4d14 Mon Sep 17 00:00:00 2001 From: Vishnu Date: Mon, 25 Aug 2025 16:22:41 +0530 Subject: [PATCH 3/6] fix: - react-hooks/exhaustive-deps added - style update - key fix --- .../layout/sidebarRight/properties/AssetProperties.tsx | 4 ++-- .../selectionControls/selection3D/moveControls3D.tsx | 3 +++ .../selectionControls/selection3D/selectionControls3D.tsx | 5 ++++- app/src/styles/components/form.scss | 1 + 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx b/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx index 5d0a97c..a62abda 100644 --- a/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx @@ -78,8 +78,8 @@ const AssetProperties: React.FC = () => {
User Data
- {userData.map((data) => ( -
+ {userData.map((data, i) => ( +
{ @@ -176,6 +177,7 @@ function MoveControls3D({ boundingBoxRef }: any) { canvasElement.removeEventListener("keydown", onKeyDown); canvasElement?.removeEventListener("keyup", onKeyUp); }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, [camera, controls, scene, toggleView, selectedAssets, socket, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects, keyEvent, initialStates]); const calculateDragOffset = useCallback((point: THREE.Object3D, hitPoint: THREE.Vector3) => { @@ -223,6 +225,7 @@ function MoveControls3D({ boundingBoxRef }: any) { setDragOffset(newOffset); } } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [axisConstraint, camera, movedObjects]) useFrame(() => { diff --git a/app/src/modules/scene/controls/selectionControls/selection3D/selectionControls3D.tsx b/app/src/modules/scene/controls/selectionControls/selection3D/selectionControls3D.tsx index 1dab7bc..ed458c2 100644 --- a/app/src/modules/scene/controls/selectionControls/selection3D/selectionControls3D.tsx +++ b/app/src/modules/scene/controls/selectionControls/selection3D/selectionControls3D.tsx @@ -67,6 +67,7 @@ const SelectionControls3D: React.FC = () => { setContextAction(null); deleteSelection() } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [contextAction]) useEffect(() => { @@ -222,12 +223,14 @@ const SelectionControls3D: React.FC = () => { helper.enabled = false; helper.dispose(); }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, [camera, controls, scene, toggleView, selectedAssets, copiedObjects, pastedObjects, duplicatedObjects, movedObjects, socket, rotatedObjects, activeModule, toolMode]); useEffect(() => { if (activeModule !== "builder" || toolMode !== 'cursor' || toggleView) { clearSelection(); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [activeModule, toolMode, toggleView]); const selectAssets = useCallback(() => { @@ -362,7 +365,7 @@ const SelectionControls3D: React.FC = () => { removeAsset(uuid); }); - echo.success("Selected models removed!"); + echo.warn("Selected models removed!"); clearSelection(); } }; diff --git a/app/src/styles/components/form.scss b/app/src/styles/components/form.scss index 67accd2..31b9015 100644 --- a/app/src/styles/components/form.scss +++ b/app/src/styles/components/form.scss @@ -9,6 +9,7 @@ border-radius: #{$border-radius-large}; outline: 1px solid var(--border-color); z-index: 100; + backdrop-filter: blur(4px); .header { @include flex-center; gap: 8px; From 6f308ddda4cc364b6dd512fd47be7c8b6112f31a Mon Sep 17 00:00:00 2001 From: Nalvazhuthi Date: Mon, 25 Aug 2025 16:23:21 +0530 Subject: [PATCH 4/6] refactor: improve formatting and readability in ShortcutHelper, DisplayZone, and KeyPressListener components --- app/src/components/footer/shortcutHelper.tsx | 5 ++-- .../visualization/zone/DisplayZone.tsx | 25 ++++++++----------- .../utils/shortcutkeys/handleShortcutKeys.ts | 6 ++++- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/app/src/components/footer/shortcutHelper.tsx b/app/src/components/footer/shortcutHelper.tsx index afbaa0e..01ada84 100644 --- a/app/src/components/footer/shortcutHelper.tsx +++ b/app/src/components/footer/shortcutHelper.tsx @@ -326,9 +326,8 @@ const ShortcutHelper: React.FC = ({
{activeShortcuts.map((item) => (
= ({ {Object.keys(zonesData).length !== 0 ? ( <> {Object.values(zonesData).map((zone, index) => ( - <> - { } -
{ +
{ - handleSelect2dZoneData(zonesData[zone.zoneUuid]?.zoneUuid, zone.zoneName) - } - } - > - {zone.zoneName} -
- + handleSelect2dZoneData(zonesData[zone.zoneUuid]?.zoneUuid, zone.zoneName) + } + } + > + {zone.zoneName} +
))} ) : ( diff --git a/app/src/utils/shortcutkeys/handleShortcutKeys.ts b/app/src/utils/shortcutkeys/handleShortcutKeys.ts index 507e31b..6a03563 100644 --- a/app/src/utils/shortcutkeys/handleShortcutKeys.ts +++ b/app/src/utils/shortcutkeys/handleShortcutKeys.ts @@ -221,8 +221,12 @@ const KeyPressListener: React.FC = () => { // Shortcuts specific for sidebar visibility toggle and others specific to sidebar if added handleSidebarShortcuts(keyCombination); + + // Active module selection (builder, simulation, etc.) - handleModuleSwitch(keyCombination); + if (event.location === 0) { // Location 0 = standard keyboard (not numpad) + handleModuleSwitch(keyCombination); + } // Common editing tools: cursor | delete | free-hand handlePrimaryTools(keyCombination); // Shortcuts specific to the builder module (e.g., drawing and measurement tools) From 60cdf4e6af6ba405083f65ccc1b02f7cfb041381 Mon Sep 17 00:00:00 2001 From: Nalvazhuthi Date: Mon, 25 Aug 2025 17:49:18 +0530 Subject: [PATCH 5/6] feat: add camera shortcut functionality for improved navigation in 3D space; refactor ShortcutHelper and Builder components for better structure --- app/src/components/footer/shortcutHelper.tsx | 2 + app/src/hooks/useCameraShortcuts.ts | 47 ++++++++++++++++++++ app/src/modules/builder/builder.tsx | 1 + app/src/modules/scene/controls/controls.tsx | 2 + 4 files changed, 52 insertions(+) create mode 100644 app/src/hooks/useCameraShortcuts.ts diff --git a/app/src/components/footer/shortcutHelper.tsx b/app/src/components/footer/shortcutHelper.tsx index 01ada84..230dff2 100644 --- a/app/src/components/footer/shortcutHelper.tsx +++ b/app/src/components/footer/shortcutHelper.tsx @@ -55,6 +55,7 @@ interface ShortcutHelperProps { const ShortcutHelper: React.FC = ({ setShowShortcuts, }) => { + const shortcuts: ShortcutGroup[] = [ // Essential { @@ -310,6 +311,7 @@ const ShortcutHelper: React.FC = ({ > +
{shortcuts.map((group) => ( diff --git a/app/src/hooks/useCameraShortcuts.ts b/app/src/hooks/useCameraShortcuts.ts new file mode 100644 index 0000000..e4b897a --- /dev/null +++ b/app/src/hooks/useCameraShortcuts.ts @@ -0,0 +1,47 @@ +import { useEffect } from "react"; +import { useThree } from "@react-three/fiber"; +import * as THREE from "three"; +import type { CameraControls } from "@react-three/drei"; + +export const useCameraShortcuts = (controlsRef: React.RefObject) => { + const { camera } = useThree(); + + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if (!controlsRef.current) return; + + // get current distance from camera to target + const target = new THREE.Vector3(); + controlsRef.current.getTarget(target); + + const distance = camera.position.distanceTo(target); + let pos: THREE.Vector3 | null = null; + + switch (e.key) { + case "1": // Front + pos = new THREE.Vector3(0, 0, distance).add(target); + break; + case "3": // Right + pos = new THREE.Vector3(distance, 0, 0).add(target); + break; + case "7": // Top + pos = new THREE.Vector3(0, distance, 0).add(target); + break; + case "9": // Back + pos = new THREE.Vector3(0, 0, -distance).add(target); + break; + } + + if (pos) { + controlsRef.current.setLookAt( + pos.x, pos.y, pos.z, // camera position + target.x, target.y, target.z, // keep same target + true // smooth transition + ); + } + }; + + window.addEventListener("keydown", handleKeyDown); + return () => window.removeEventListener("keydown", handleKeyDown); + }, [controlsRef, camera]); +}; diff --git a/app/src/modules/builder/builder.tsx b/app/src/modules/builder/builder.tsx index ab9434b..ef310fc 100644 --- a/app/src/modules/builder/builder.tsx +++ b/app/src/modules/builder/builder.tsx @@ -57,6 +57,7 @@ export default function Builder() { const { setHoveredPoint, setHoveredLine } = useBuilderStore(); const { userId, organization } = getUserData(); + useEffect(() => { if (!toggleView) { setHoveredLine(null); diff --git a/app/src/modules/scene/controls/controls.tsx b/app/src/modules/scene/controls/controls.tsx index c41a4bd..d63f0ac 100644 --- a/app/src/modules/scene/controls/controls.tsx +++ b/app/src/modules/scene/controls/controls.tsx @@ -18,6 +18,7 @@ import ContextControls from "./contextControls/contextControls"; import SelectionControls2D from "./selectionControls/selection2D/selectionControls2D"; import UndoRedo2DControls from "./undoRedoControls/undoRedo2D/undoRedo2DControls"; import UndoRedo3DControls from "./undoRedoControls/undoRedo3D/undoRedo3DControls"; +import { useCameraShortcuts } from "../../../hooks/useCameraShortcuts"; export default function Controls() { const controlsRef = useRef(null); @@ -116,6 +117,7 @@ export default function Controls() { stopInterval(); }; }, [toggleView, state, socket]); + useCameraShortcuts(controlsRef); return ( <> From e813f194c7c19fc3b8c04768f337dc7d999aefca Mon Sep 17 00:00:00 2001 From: Vishnu Date: Mon, 25 Aug 2025 18:21:28 +0530 Subject: [PATCH 6/6] resource manager style update --- app/src/styles/layout/resourceManagement.scss | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/styles/layout/resourceManagement.scss b/app/src/styles/layout/resourceManagement.scss index d62afe8..131220f 100644 --- a/app/src/styles/layout/resourceManagement.scss +++ b/app/src/styles/layout/resourceManagement.scss @@ -105,7 +105,7 @@ header { position: relative; @include flex-space-between; - padding: 3px 0; + padding-bottom: 6px; .user-details { display: flex; @@ -136,6 +136,9 @@ .details { max-width: 144px; + .input-value{ + max-width: 120px; + } .employee-id { color: #b7b7c6; font-size: $tiny;