From fc68faea43183089ab9d462545e77a95248d5f7a Mon Sep 17 00:00:00 2001 From: shiva raj badu Date: Sat, 26 Jul 2025 02:42:54 +0545 Subject: [PATCH] feat(nextjs): Image edit with object fit & focal point added --- .../components/EditableLayoutWrapper.tsx | 67 +++++-------------- .../components/ImageEditor.tsx | 4 +- .../outline/hooks/useOutlineStreaming.ts | 11 ++- .../store/slices/presentationGeneration.ts | 24 +++++++ 4 files changed, 53 insertions(+), 53 deletions(-) diff --git a/servers/nextjs/app/(presentation-generator)/components/EditableLayoutWrapper.tsx b/servers/nextjs/app/(presentation-generator)/components/EditableLayoutWrapper.tsx index fe46d952..e53c1927 100644 --- a/servers/nextjs/app/(presentation-generator)/components/EditableLayoutWrapper.tsx +++ b/servers/nextjs/app/(presentation-generator)/components/EditableLayoutWrapper.tsx @@ -2,7 +2,7 @@ import React, { ReactNode, useRef, useEffect, useState } from 'react'; import { useDispatch } from 'react-redux'; -import { updateSlideImage, updateSlideIcon } from '@/store/slices/presentationGeneration'; +import { updateSlideImage, updateSlideIcon, updateImageProperties } from '@/store/slices/presentationGeneration'; import ImageEditor from './ImageEditor'; import IconsEditor from './IconsEditor'; @@ -170,10 +170,10 @@ const EditableLayoutWrapper: React.FC = ({ htmlImg.setAttribute('data-editable-processed', 'true'); // Add a unique identifier to help with debugging - htmlImg.setAttribute('data-editable-id', `${type}-${dataPath}-${index}`); + htmlImg.setAttribute('data-editable-id', `${slideIndex}-${type}-${dataPath}-${index}`); const editableElement: EditableElement = { - id: `${type}-${dataPath}-${index}`, + id: `${slideIndex}-${type}-${dataPath}-${index}`, type, src, dataPath, @@ -192,9 +192,14 @@ const EditableLayoutWrapper: React.FC = ({ htmlImg.addEventListener('click', clickHandler); + const itemIndex = parseInt(`${slideIndex}-${type}-${dataPath}-${index}`.split('-').pop() || '0'); + const properties = slideData.properties?.[itemIndex]; + // Add hover effects without changing layout htmlImg.style.cursor = 'pointer'; htmlImg.style.transition = 'opacity 0.2s, transform 0.2s'; + htmlImg.style.objectFit = properties?.objectFit; + htmlImg.style.objectPosition = `${properties?.focusPoint?.x}% ${properties?.focusPoint?.y}%`; const mouseEnterHandler = () => { htmlImg.style.opacity = '0.8'; @@ -327,16 +332,22 @@ const EditableLayoutWrapper: React.FC = ({ } }; const handleFocusPointClick = (propertiesData: any) => { - console.log('activeEditor', activeEditor); + const id = activeEditor?.id; const editableId = document.querySelector(`[data-editable-id="${id}"]`); - console.log('editableId', editableId); + if (editableId) { const editableElement = editableId as HTMLImageElement; - editableElement.style.objectPosition = `${propertiesData.initialFocusPoint.x}px ${propertiesData.initialFocusPoint.y}px`; editableElement.style.objectFit = propertiesData.initialObjectFit; + editableElement.style.objectPosition = `${propertiesData.initialFocusPoint.x}% ${propertiesData.initialFocusPoint.y}%`; } + dispatch(updateImageProperties({ + slideIndex, + itemIndex: parseInt(activeEditor?.id.split('-').pop() || '0'), + properties: propertiesData + })); + }; return ( @@ -374,47 +385,3 @@ const EditableLayoutWrapper: React.FC = ({ export default EditableLayoutWrapper; - - - -const setNestedImageValue = (obj: any, path: string, url: string, promptText?: string) => { - const keys = path.split(/[.\[\]]+/).filter(Boolean); - let current = obj; - - // Navigate to the parent object - for (let i = 0; i < keys.length - 1; i++) { - const key = keys[i]; - if (isNaN(Number(key))) { - if (!current[key]) { - current[key] = {}; - } - current = current[key]; - } else { - const index = Number(key); - if (!current[index]) { - current[index] = {}; - } - current = current[index]; - } - } - - // Set the image properties - const finalKey = keys[keys.length - 1]; - const target = isNaN(Number(finalKey)) ? current[finalKey] : current[Number(finalKey)]; - - // Preserve existing properties if the target already exists - const updatedValue = { - ...(target && typeof target === 'object' ? target : {}), - __image_url__: url, - __image_prompt__: promptText || (target?.__image_prompt__) || '' - }; - - if (isNaN(Number(finalKey))) { - current[finalKey] = updatedValue; - } else { - current[Number(finalKey)] = updatedValue; - } - - // Add debugging - console.log('Redux: Updated slide image at path:', path, 'with URL:', url); -}; \ No newline at end of file diff --git a/servers/nextjs/app/(presentation-generator)/components/ImageEditor.tsx b/servers/nextjs/app/(presentation-generator)/components/ImageEditor.tsx index 604d0a9b..da85c01a 100644 --- a/servers/nextjs/app/(presentation-generator)/components/ImageEditor.tsx +++ b/servers/nextjs/app/(presentation-generator)/components/ImageEditor.tsx @@ -269,14 +269,14 @@ const ImageEditor = ({
- + AI Generate Upload - {/* Edit */} + Edit {/* Generate Tab */} diff --git a/servers/nextjs/app/(presentation-generator)/outline/hooks/useOutlineStreaming.ts b/servers/nextjs/app/(presentation-generator)/outline/hooks/useOutlineStreaming.ts index 24a81604..28a2e736 100644 --- a/servers/nextjs/app/(presentation-generator)/outline/hooks/useOutlineStreaming.ts +++ b/servers/nextjs/app/(presentation-generator)/outline/hooks/useOutlineStreaming.ts @@ -32,7 +32,7 @@ export const useOutlineStreaming = (presentationId: string | null) => { eventSource.addEventListener("response", (event) => { const data = JSON.parse(event.data); - + console.log('data', data); switch (data.type) { case "chunk": accumulatedChunks += data.chunk; @@ -66,6 +66,15 @@ export const useOutlineStreaming = (presentationId: string | null) => { setStreamState({ isStreaming: false, isLoading: false }); eventSource.close(); break; + case "error": + setStreamState({ isStreaming: false, isLoading: false }); + eventSource.close(); + toast.error('Error in outline streaming', + { + description: data.detail || 'Failed to connect to the server. Please try again.', + } + ); + break; } }); diff --git a/servers/nextjs/store/slices/presentationGeneration.ts b/servers/nextjs/store/slices/presentationGeneration.ts index f16cbfe3..5044629a 100644 --- a/servers/nextjs/store/slices/presentationGeneration.ts +++ b/servers/nextjs/store/slices/presentationGeneration.ts @@ -289,6 +289,29 @@ const presentationGenerationSlice = createSlice({ } }, + updateImageProperties: ( + state, + action: PayloadAction<{ + slideIndex: number; + itemIndex: number; + properties: any; + }> + ) => { + if ( + state.presentationData && + state.presentationData.slides && + state.presentationData.slides[action.payload.slideIndex] + ) { + const slide = state.presentationData.slides[action.payload.slideIndex]; + const { itemIndex, properties } = action.payload; + slide['properties'] = { + ...slide.properties, + [itemIndex]: properties + }; + + } + }, + // Update slide icon at specific data path updateSlideIcon: ( state, @@ -385,6 +408,7 @@ export const { deletePresentationSlide, updateSlideContent, updateSlideImage, + updateImageProperties, updateSlideIcon, addNewSlide, } = presentationGenerationSlice.actions;