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 {