diff --git a/package-lock.json b/package-lock.json index 9028ef4..36680ca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -57,6 +57,7 @@ "react": "^19.0.0", "react-day-picker": "^9.7.0", "react-dom": "^19.0.0", + "react-error-boundary": "^6.0.0", "react-hook-form": "^7.57.0", "react-konva": "^19.0.6", "react-resizable-panels": "^3.0.2", @@ -7208,6 +7209,18 @@ "react": "^19.1.0" } }, + "node_modules/react-error-boundary": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-6.0.0.tgz", + "integrity": "sha512-gdlJjD7NWr0IfkPlaREN2d9uUZUlksrfOx7SX62VRerwXbMY6ftGCIZua1VG1aXFNOimhISsTq+Owp725b9SiA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "peerDependencies": { + "react": ">=16.13.1" + } + }, "node_modules/react-hook-form": { "version": "7.57.0", "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.57.0.tgz", diff --git a/package.json b/package.json index e12f6fe..e344180 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "react": "^19.0.0", "react-day-picker": "^9.7.0", "react-dom": "^19.0.0", + "react-error-boundary": "^6.0.0", "react-hook-form": "^7.57.0", "react-konva": "^19.0.6", "react-resizable-panels": "^3.0.2", diff --git a/resources/js/app.tsx b/resources/js/app.tsx index f3e5e5c..912dda2 100644 --- a/resources/js/app.tsx +++ b/resources/js/app.tsx @@ -3,6 +3,8 @@ import '../css/app.css'; import { createInertiaApp } from '@inertiajs/react'; import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers'; import { createRoot } from 'react-dom/client'; +import { ErrorBoundary } from 'react-error-boundary'; +import DetailedErrorFallback from './components/custom/detailed-error-feedback'; // Import your component import { initializeTheme } from './hooks/use-appearance'; import { AxiosProvider } from './plugins/AxiosContext'; import { MittProvider } from './plugins/MittContext'; @@ -16,13 +18,26 @@ createInertiaApp({ const root = createRoot(el); const app = ( - <> + { + // Log to console for debugging + console.error('Error caught by boundary:', error, errorInfo); + + // You could also send to an error reporting service here + // e.g., Sentry, LogRocket, etc. + }} + onReset={() => { + // Optional: Clear any error state in your app + console.log('Error boundary reset'); + }} + > - + ); root.render(app); @@ -32,5 +47,4 @@ createInertiaApp({ }, }); -// This will set light / dark mode on load... initializeTheme(); diff --git a/resources/js/components/custom/detailed-error-feedback.jsx b/resources/js/components/custom/detailed-error-feedback.jsx new file mode 100644 index 0000000..14440c6 --- /dev/null +++ b/resources/js/components/custom/detailed-error-feedback.jsx @@ -0,0 +1,71 @@ +import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'; +import { AlertCircle } from 'lucide-react'; + +function DetailedErrorFallback({ error, resetErrorBoundary }) { + return ( +
+ + + Runtime Error + +
+ {/* Error Message */} +
+

Error Message:

+

{error.message}

+
+ + {/* Component Stack */} +
+

Component Stack:

+
+                                {error.componentStack || error.stack}
+                            
+
+ + {/* Additional Error Info */} + {error.fileName && ( +
+

File:

+

{error.fileName}

+
+ )} + + {error.lineNumber && ( +
+

Line:

+

{error.lineNumber}

+
+ )} + + {/* Error Properties */} +
+

Additional Details:

+
+                                {JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}
+                            
+
+ + {/* Actions */} +
+ + +
+
+
+
+
+ ); +} + +export default DetailedErrorFallback; diff --git a/resources/js/modules/editor/partials/canvas/video-preview.jsx b/resources/js/modules/editor/partials/canvas/video-preview.jsx index ff26ffa..d4efa27 100644 --- a/resources/js/modules/editor/partials/canvas/video-preview.jsx +++ b/resources/js/modules/editor/partials/canvas/video-preview.jsx @@ -294,9 +294,8 @@ const VideoPreview = ({ const scaleX = node.scaleX(); const scaleY = node.scaleY(); - let newWidth, - newHeight, - updates = {}; + let newWidth, newHeight; + let updates = {}; if (element.type === 'text') { // OPTION A: Convert scale change to fontSize change @@ -310,9 +309,13 @@ const VideoPreview = ({ node.scaleX(1); node.scaleY(1); - // The width/height will be automatically calculated by Konva based on fontSize - // For text elements, we let Konva handle the natural dimensions + // ✅ FIX: Always get current width/height for text elements too + newWidth = node.width(); + newHeight = node.height(); + updates.fontSize = clampedFontSize; + updates.width = newWidth; // ✅ Always include width + updates.height = newHeight; // ✅ Always include height console.log(`Text transform: scale=${scale.toFixed(2)}, oldFontSize=${element.fontSize}, newFontSize=${clampedFontSize}`); } else { @@ -342,10 +345,8 @@ const VideoPreview = ({ // Convert center position to top-left for snapping const centerX = node.x(); const centerY = node.y(); - const currentWidth = element.type === 'text' ? node.width() : newWidth; - const currentHeight = element.type === 'text' ? node.height() : newHeight; - topLeftX = centerX - currentWidth / 2; - topLeftY = centerY - currentHeight / 2; + topLeftX = centerX - newWidth / 2; // ✅ newWidth is now always defined + topLeftY = centerY - newHeight / 2; // ✅ newHeight is now always defined } else { // Use position directly for text topLeftX = node.x(); @@ -355,15 +356,13 @@ const VideoPreview = ({ // Check for position snapping during transform (but be less aggressive during rotation) const isRotating = Math.abs(rotation % 90) > 5; // Not close to perpendicular if (!isRotating) { - const currentWidth = element.type === 'text' ? node.width() : newWidth; - const currentHeight = element.type === 'text' ? node.height() : newHeight; - const snapResult = calculateSnapAndGuides(elementId, topLeftX, topLeftY, currentWidth, currentHeight); + const snapResult = calculateSnapAndGuides(elementId, topLeftX, topLeftY, newWidth, newHeight); if (Math.abs(snapResult.x - topLeftX) > 5 || Math.abs(snapResult.y - topLeftY) > 5) { if (usesCenterPositioning(element.type)) { // Convert back to center position - const newCenterX = snapResult.x + currentWidth / 2; - const newCenterY = snapResult.y + currentHeight / 2; + const newCenterX = snapResult.x + newWidth / 2; + const newCenterY = snapResult.y + newHeight / 2; node.x(newCenterX); node.y(newCenterY); } else {