diff --git a/scaffold/dyad-shim.js b/scaffold/dyad-shim.js new file mode 100644 index 0000000..bb042a9 --- /dev/null +++ b/scaffold/dyad-shim.js @@ -0,0 +1,185 @@ +(function () { + const isInsideIframe = window.parent !== window; + if (!isInsideIframe) return; + + let previousUrl = window.location.href; + const PARENT_TARGET_ORIGIN = "*"; + + // --- History API Overrides --- + const originalPushState = history.pushState; + const originalReplaceState = history.replaceState; + + const handleStateChangeAndNotify = (originalMethod, state, title, url) => { + const oldUrlForMessage = previousUrl; + let newUrl; + try { + newUrl = url + ? new URL(url, window.location.href).href + : window.location.href; + } catch (e) { + newUrl = window.location.href; + } + + const navigationType = + originalMethod === originalPushState ? "pushState" : "replaceState"; + + try { + // Pass the original state directly + originalMethod.call(history, state, title, url); + previousUrl = window.location.href; + window.parent.postMessage( + { + type: navigationType, + payload: { oldUrl: oldUrlForMessage, newUrl: newUrl }, + }, + PARENT_TARGET_ORIGIN + ); + } catch (e) { + console.error( + `[vite-dev-plugin] Error calling original ${navigationType}: `, + e + ); + window.parent.postMessage( + { + type: "navigation-error", + payload: { + operation: navigationType, + message: e.message, + error: e.toString(), + stateAttempted: state, + urlAttempted: url, + }, + }, + PARENT_TARGET_ORIGIN + ); + } + }; + + history.pushState = function (state, title, url) { + handleStateChangeAndNotify(originalPushState, state, title, url); + }; + + history.replaceState = function (state, title, url) { + handleStateChangeAndNotify(originalReplaceState, state, title, url); + }; + + // --- Listener for Back/Forward Navigation (popstate event) --- + window.addEventListener("popstate", (event) => { + const currentUrl = window.location.href; + previousUrl = currentUrl; + }); + + // --- Listener for Commands from Parent --- + window.addEventListener("message", (event) => { + if ( + event.source !== window.parent || + !event.data || + typeof event.data !== "object" + ) + return; + if (event.data.type === "navigate") { + const direction = event.data.payload?.direction; + if (direction === "forward") history.forward(); + else if (direction === "backward") history.back(); + } + }); + + // --- Sourcemapped Error Handling --- + function sendSourcemappedErrorToParent(error, sourceType) { + if (typeof window.StackTrace === "undefined") { + console.error("[vite-dev-plugin] StackTrace object not found."); + // Send simplified raw data if StackTrace isn't available + window.parent.postMessage( + { + type: sourceType, + payload: { + message: error?.message || String(error), + stack: + error?.stack || "", + }, + }, + PARENT_TARGET_ORIGIN + ); + return; + } + + window.StackTrace.fromError(error) + .then((stackFrames) => { + const sourcemappedStack = stackFrames + .map((sf) => sf.toString()) + .join("\n"); + + const payload = { + message: error?.message || String(error), + stack: sourcemappedStack, + }; + + window.parent.postMessage( + { + type: "iframe-sourcemapped-error", + payload: { ...payload, originalSourceType: sourceType }, + }, + PARENT_TARGET_ORIGIN + ); + }) + .catch((mappingError) => { + console.error( + "[vite-dev-plugin] Error during stacktrace sourcemapping:", + mappingError + ); + + const payload = { + message: error?.message || String(error), + // Provide the raw stack or an indication of mapping failure + stack: error?.stack + ? `Sourcemapping failed: ${mappingError.message}\n--- Raw Stack ---\n${error.stack}` + : `Sourcemapping failed: ${mappingError.message}\n`, + }; + + window.parent.postMessage( + { + type: "iframe-sourcemapped-error", + payload: { ...payload, originalSourceType: sourceType }, + }, + PARENT_TARGET_ORIGIN + ); + }); + } + + window.addEventListener("error", (event) => { + let error = event.error; + if (!(error instanceof Error)) { + window.parent.postMessage( + { + type: "window-error", + payload: { + message: error.toString(), + stack: "", + }, + }, + PARENT_TARGET_ORIGIN + ); + return; + } + sendSourcemappedErrorToParent(error, "window-error"); + }); + + window.addEventListener("unhandledrejection", (event) => { + let error = event.reason; + if (!(error instanceof Error)) { + window.parent.postMessage( + { + type: "unhandled-rejection", + payload: { + message: event.reason.toString(), + stack: + "", + }, + }, + PARENT_TARGET_ORIGIN + ); + return; + } + sendSourcemappedErrorToParent(error, "unhandled-rejection"); + }); +})(); diff --git a/scaffold/package.json b/scaffold/package.json index f8ba3b0..9a94ca2 100644 --- a/scaffold/package.json +++ b/scaffold/package.json @@ -56,6 +56,7 @@ "react-router-dom": "^6.26.2", "recharts": "^2.12.7", "sonner": "^1.5.0", + "stacktrace-js": "^2.0.2", "tailwind-merge": "^2.5.2", "tailwindcss-animate": "^1.0.7", "vaul": "^0.9.3", @@ -67,7 +68,7 @@ "@types/node": "^22.5.5", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", - "@vitejs/plugin-react-swc": "^3.5.0", + "@vitejs/plugin-react-swc": "^3.9.0", "autoprefixer": "^10.4.20", "eslint": "^9.9.0", "eslint-plugin-react-hooks": "^5.1.0-rc.0", @@ -77,6 +78,6 @@ "tailwindcss": "^3.4.11", "typescript": "^5.5.3", "typescript-eslint": "^8.0.1", - "vite": "^5.4.1" + "vite": "^6.3.4" } } \ No newline at end of file diff --git a/scaffold/pnpm-lock.yaml b/scaffold/pnpm-lock.yaml index a8cdfe4..fd77d4f 100644 --- a/scaffold/pnpm-lock.yaml +++ b/scaffold/pnpm-lock.yaml @@ -143,6 +143,9 @@ importers: sonner: specifier: ^1.5.0 version: 1.7.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + stacktrace-js: + specifier: ^2.0.2 + version: 2.0.2 tailwind-merge: specifier: ^2.5.2 version: 2.6.0 @@ -172,8 +175,8 @@ importers: specifier: ^18.3.0 version: 18.3.6(@types/react@18.3.20) '@vitejs/plugin-react-swc': - specifier: ^3.5.0 - version: 3.9.0(vite@5.4.18(@types/node@22.14.1)) + specifier: ^3.9.0 + version: 3.9.0(vite@6.3.4(@types/node@22.14.1)(jiti@1.21.7)(yaml@2.7.1)) autoprefixer: specifier: ^10.4.20 version: 10.4.21(postcss@8.5.3) @@ -202,8 +205,8 @@ importers: specifier: ^8.0.1 version: 8.30.1(eslint@9.24.0(jiti@1.21.7))(typescript@5.8.3) vite: - specifier: ^5.4.1 - version: 5.4.18(@types/node@22.14.1) + specifier: ^6.3.4 + version: 6.3.4(@types/node@22.14.1)(jiti@1.21.7)(yaml@2.7.1) packages: @@ -215,141 +218,153 @@ packages: resolution: {integrity: sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==} engines: {node: '>=6.9.0'} - '@esbuild/aix-ppc64@0.21.5': - resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} - engines: {node: '>=12'} + '@esbuild/aix-ppc64@0.25.3': + resolution: {integrity: sha512-W8bFfPA8DowP8l//sxjJLSLkD8iEjMc7cBVyP+u4cEv9sM7mdUCkgsj+t0n/BWPFtv7WWCN5Yzj0N6FJNUUqBQ==} + engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.21.5': - resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} - engines: {node: '>=12'} + '@esbuild/android-arm64@0.25.3': + resolution: {integrity: sha512-XelR6MzjlZuBM4f5z2IQHK6LkK34Cvv6Rj2EntER3lwCBFdg6h2lKbtRjpTTsdEjD/WSe1q8UyPBXP1x3i/wYQ==} + engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.21.5': - resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} - engines: {node: '>=12'} + '@esbuild/android-arm@0.25.3': + resolution: {integrity: sha512-PuwVXbnP87Tcff5I9ngV0lmiSu40xw1At6i3GsU77U7cjDDB4s0X2cyFuBiDa1SBk9DnvWwnGvVaGBqoFWPb7A==} + engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.21.5': - resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} - engines: {node: '>=12'} + '@esbuild/android-x64@0.25.3': + resolution: {integrity: sha512-ogtTpYHT/g1GWS/zKM0cc/tIebFjm1F9Aw1boQ2Y0eUQ+J89d0jFY//s9ei9jVIlkYi8AfOjiixcLJSGNSOAdQ==} + engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.21.5': - resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} - engines: {node: '>=12'} + '@esbuild/darwin-arm64@0.25.3': + resolution: {integrity: sha512-eESK5yfPNTqpAmDfFWNsOhmIOaQA59tAcF/EfYvo5/QWQCzXn5iUSOnqt3ra3UdzBv073ykTtmeLJZGt3HhA+w==} + engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.21.5': - resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} - engines: {node: '>=12'} + '@esbuild/darwin-x64@0.25.3': + resolution: {integrity: sha512-Kd8glo7sIZtwOLcPbW0yLpKmBNWMANZhrC1r6K++uDR2zyzb6AeOYtI6udbtabmQpFaxJ8uduXMAo1gs5ozz8A==} + engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.21.5': - resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} - engines: {node: '>=12'} + '@esbuild/freebsd-arm64@0.25.3': + resolution: {integrity: sha512-EJiyS70BYybOBpJth3M0KLOus0n+RRMKTYzhYhFeMwp7e/RaajXvP+BWlmEXNk6uk+KAu46j/kaQzr6au+JcIw==} + engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.21.5': - resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} - engines: {node: '>=12'} + '@esbuild/freebsd-x64@0.25.3': + resolution: {integrity: sha512-Q+wSjaLpGxYf7zC0kL0nDlhsfuFkoN+EXrx2KSB33RhinWzejOd6AvgmP5JbkgXKmjhmpfgKZq24pneodYqE8Q==} + engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.21.5': - resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} - engines: {node: '>=12'} + '@esbuild/linux-arm64@0.25.3': + resolution: {integrity: sha512-xCUgnNYhRD5bb1C1nqrDV1PfkwgbswTTBRbAd8aH5PhYzikdf/ddtsYyMXFfGSsb/6t6QaPSzxtbfAZr9uox4A==} + engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.21.5': - resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} - engines: {node: '>=12'} + '@esbuild/linux-arm@0.25.3': + resolution: {integrity: sha512-dUOVmAUzuHy2ZOKIHIKHCm58HKzFqd+puLaS424h6I85GlSDRZIA5ycBixb3mFgM0Jdh+ZOSB6KptX30DD8YOQ==} + engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.21.5': - resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} - engines: {node: '>=12'} + '@esbuild/linux-ia32@0.25.3': + resolution: {integrity: sha512-yplPOpczHOO4jTYKmuYuANI3WhvIPSVANGcNUeMlxH4twz/TeXuzEP41tGKNGWJjuMhotpGabeFYGAOU2ummBw==} + engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.21.5': - resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} - engines: {node: '>=12'} + '@esbuild/linux-loong64@0.25.3': + resolution: {integrity: sha512-P4BLP5/fjyihmXCELRGrLd793q/lBtKMQl8ARGpDxgzgIKJDRJ/u4r1A/HgpBpKpKZelGct2PGI4T+axcedf6g==} + engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.21.5': - resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} - engines: {node: '>=12'} + '@esbuild/linux-mips64el@0.25.3': + resolution: {integrity: sha512-eRAOV2ODpu6P5divMEMa26RRqb2yUoYsuQQOuFUexUoQndm4MdpXXDBbUoKIc0iPa4aCO7gIhtnYomkn2x+bag==} + engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.21.5': - resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} - engines: {node: '>=12'} + '@esbuild/linux-ppc64@0.25.3': + resolution: {integrity: sha512-ZC4jV2p7VbzTlnl8nZKLcBkfzIf4Yad1SJM4ZMKYnJqZFD4rTI+pBG65u8ev4jk3/MPwY9DvGn50wi3uhdaghg==} + engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.21.5': - resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} - engines: {node: '>=12'} + '@esbuild/linux-riscv64@0.25.3': + resolution: {integrity: sha512-LDDODcFzNtECTrUUbVCs6j9/bDVqy7DDRsuIXJg6so+mFksgwG7ZVnTruYi5V+z3eE5y+BJZw7VvUadkbfg7QA==} + engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.21.5': - resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} - engines: {node: '>=12'} + '@esbuild/linux-s390x@0.25.3': + resolution: {integrity: sha512-s+w/NOY2k0yC2p9SLen+ymflgcpRkvwwa02fqmAwhBRI3SC12uiS10edHHXlVWwfAagYSY5UpmT/zISXPMW3tQ==} + engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.21.5': - resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} - engines: {node: '>=12'} + '@esbuild/linux-x64@0.25.3': + resolution: {integrity: sha512-nQHDz4pXjSDC6UfOE1Fw9Q8d6GCAd9KdvMZpfVGWSJztYCarRgSDfOVBY5xwhQXseiyxapkiSJi/5/ja8mRFFA==} + engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-x64@0.21.5': - resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} - engines: {node: '>=12'} + '@esbuild/netbsd-arm64@0.25.3': + resolution: {integrity: sha512-1QaLtOWq0mzK6tzzp0jRN3eccmN3hezey7mhLnzC6oNlJoUJz4nym5ZD7mDnS/LZQgkrhEbEiTn515lPeLpgWA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.3': + resolution: {integrity: sha512-i5Hm68HXHdgv8wkrt+10Bc50zM0/eonPb/a/OFVfB6Qvpiirco5gBA5bz7S2SHuU+Y4LWn/zehzNX14Sp4r27g==} + engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-x64@0.21.5': - resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} - engines: {node: '>=12'} + '@esbuild/openbsd-arm64@0.25.3': + resolution: {integrity: sha512-zGAVApJEYTbOC6H/3QBr2mq3upG/LBEXr85/pTtKiv2IXcgKV0RT0QA/hSXZqSvLEpXeIxah7LczB4lkiYhTAQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.3': + resolution: {integrity: sha512-fpqctI45NnCIDKBH5AXQBsD0NDPbEFczK98hk/aa6HJxbl+UtLkJV2+Bvy5hLSLk3LHmqt0NTkKNso1A9y1a4w==} + engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/sunos-x64@0.21.5': - resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} - engines: {node: '>=12'} + '@esbuild/sunos-x64@0.25.3': + resolution: {integrity: sha512-ROJhm7d8bk9dMCUZjkS8fgzsPAZEjtRJqCAmVgB0gMrvG7hfmPmz9k1rwO4jSiblFjYmNvbECL9uhaPzONMfgA==} + engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.21.5': - resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} - engines: {node: '>=12'} + '@esbuild/win32-arm64@0.25.3': + resolution: {integrity: sha512-YWcow8peiHpNBiIXHwaswPnAXLsLVygFwCB3A7Bh5jRkIBFWHGmNQ48AlX4xDvQNoMZlPYzjVOQDYEzWCqufMQ==} + engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.21.5': - resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} - engines: {node: '>=12'} + '@esbuild/win32-ia32@0.25.3': + resolution: {integrity: sha512-qspTZOIGoXVS4DpNqUYUs9UxVb04khS1Degaw/MnfMe7goQ3lTfQ13Vw4qY/Nj0979BGvMRpAYbs/BAxEvU8ew==} + engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.21.5': - resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} - engines: {node: '>=12'} + '@esbuild/win32-x64@0.25.3': + resolution: {integrity: sha512-ICgUR+kPimx0vvRzf+N/7L7tVSQeE3BYY+NhHRHXS1kBuPO7z2+7ea2HbhDyZdTephgvNvKrlDDKUexuCVBVvg==} + engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -1603,9 +1618,12 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - esbuild@0.21.5: - resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} - engines: {node: '>=12'} + error-stack-parser@2.1.4: + resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==} + + esbuild@0.25.3: + resolution: {integrity: sha512-qKA6Pvai73+M2FtftpNKRxJ78GIjmFXFxd/1DVBqGo/qNhLSfv+G12n9pNoWdytJC8U00TrViOwpjT0zgqQS8Q==} + engines: {node: '>=18'} hasBin: true escalade@3.2.0: @@ -1692,6 +1710,14 @@ packages: fastq@1.19.1: resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + fdir@6.4.4: + resolution: {integrity: sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} @@ -1974,6 +2000,10 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + pify@2.3.0: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} @@ -2200,6 +2230,22 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + source-map@0.5.6: + resolution: {integrity: sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==} + engines: {node: '>=0.10.0'} + + stack-generator@2.0.10: + resolution: {integrity: sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==} + + stackframe@1.3.4: + resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} + + stacktrace-gps@3.1.2: + resolution: {integrity: sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ==} + + stacktrace-js@2.0.2: + resolution: {integrity: sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==} + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -2256,6 +2302,10 @@ packages: tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + tinyglobby@0.2.13: + resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==} + engines: {node: '>=12.0.0'} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -2332,22 +2382,27 @@ packages: victory-vendor@36.9.2: resolution: {integrity: sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==} - vite@5.4.18: - resolution: {integrity: sha512-1oDcnEp3lVyHCuQ2YFelM4Alm2o91xNoMncRm1U7S+JdYfYOvbiGZ3/CxGttrOu2M/KcGz7cRC2DoNUA6urmMA==} - engines: {node: ^18.0.0 || >=20.0.0} + vite@6.3.4: + resolution: {integrity: sha512-BiReIiMS2fyFqbqNT/Qqt4CVITDU9M9vE+DKcVAsB+ZV0wvTKd+3hMbkpxz1b+NmEDMegpVbisKiAZOnvO92Sw==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' less: '*' lightningcss: ^1.21.0 sass: '*' sass-embedded: '*' stylus: '*' sugarss: '*' - terser: ^5.4.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 peerDependenciesMeta: '@types/node': optional: true + jiti: + optional: true less: optional: true lightningcss: @@ -2362,6 +2417,10 @@ packages: optional: true terser: optional: true + tsx: + optional: true + yaml: + optional: true which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} @@ -2400,73 +2459,79 @@ snapshots: dependencies: regenerator-runtime: 0.14.1 - '@esbuild/aix-ppc64@0.21.5': + '@esbuild/aix-ppc64@0.25.3': optional: true - '@esbuild/android-arm64@0.21.5': + '@esbuild/android-arm64@0.25.3': optional: true - '@esbuild/android-arm@0.21.5': + '@esbuild/android-arm@0.25.3': optional: true - '@esbuild/android-x64@0.21.5': + '@esbuild/android-x64@0.25.3': optional: true - '@esbuild/darwin-arm64@0.21.5': + '@esbuild/darwin-arm64@0.25.3': optional: true - '@esbuild/darwin-x64@0.21.5': + '@esbuild/darwin-x64@0.25.3': optional: true - '@esbuild/freebsd-arm64@0.21.5': + '@esbuild/freebsd-arm64@0.25.3': optional: true - '@esbuild/freebsd-x64@0.21.5': + '@esbuild/freebsd-x64@0.25.3': optional: true - '@esbuild/linux-arm64@0.21.5': + '@esbuild/linux-arm64@0.25.3': optional: true - '@esbuild/linux-arm@0.21.5': + '@esbuild/linux-arm@0.25.3': optional: true - '@esbuild/linux-ia32@0.21.5': + '@esbuild/linux-ia32@0.25.3': optional: true - '@esbuild/linux-loong64@0.21.5': + '@esbuild/linux-loong64@0.25.3': optional: true - '@esbuild/linux-mips64el@0.21.5': + '@esbuild/linux-mips64el@0.25.3': optional: true - '@esbuild/linux-ppc64@0.21.5': + '@esbuild/linux-ppc64@0.25.3': optional: true - '@esbuild/linux-riscv64@0.21.5': + '@esbuild/linux-riscv64@0.25.3': optional: true - '@esbuild/linux-s390x@0.21.5': + '@esbuild/linux-s390x@0.25.3': optional: true - '@esbuild/linux-x64@0.21.5': + '@esbuild/linux-x64@0.25.3': optional: true - '@esbuild/netbsd-x64@0.21.5': + '@esbuild/netbsd-arm64@0.25.3': optional: true - '@esbuild/openbsd-x64@0.21.5': + '@esbuild/netbsd-x64@0.25.3': optional: true - '@esbuild/sunos-x64@0.21.5': + '@esbuild/openbsd-arm64@0.25.3': optional: true - '@esbuild/win32-arm64@0.21.5': + '@esbuild/openbsd-x64@0.25.3': optional: true - '@esbuild/win32-ia32@0.21.5': + '@esbuild/sunos-x64@0.25.3': optional: true - '@esbuild/win32-x64@0.21.5': + '@esbuild/win32-arm64@0.25.3': + optional: true + + '@esbuild/win32-ia32@0.25.3': + optional: true + + '@esbuild/win32-x64@0.25.3': optional: true '@eslint-community/eslint-utils@4.6.1(eslint@9.24.0(jiti@1.21.7))': @@ -3498,10 +3563,10 @@ snapshots: '@typescript-eslint/types': 8.30.1 eslint-visitor-keys: 4.2.0 - '@vitejs/plugin-react-swc@3.9.0(vite@5.4.18(@types/node@22.14.1))': + '@vitejs/plugin-react-swc@3.9.0(vite@6.3.4(@types/node@22.14.1)(jiti@1.21.7)(yaml@2.7.1))': dependencies: '@swc/core': 1.11.21 - vite: 5.4.18(@types/node@22.14.1) + vite: 6.3.4(@types/node@22.14.1)(jiti@1.21.7)(yaml@2.7.1) transitivePeerDependencies: - '@swc/helpers' @@ -3717,31 +3782,37 @@ snapshots: emoji-regex@9.2.2: {} - esbuild@0.21.5: + error-stack-parser@2.1.4: + dependencies: + stackframe: 1.3.4 + + esbuild@0.25.3: optionalDependencies: - '@esbuild/aix-ppc64': 0.21.5 - '@esbuild/android-arm': 0.21.5 - '@esbuild/android-arm64': 0.21.5 - '@esbuild/android-x64': 0.21.5 - '@esbuild/darwin-arm64': 0.21.5 - '@esbuild/darwin-x64': 0.21.5 - '@esbuild/freebsd-arm64': 0.21.5 - '@esbuild/freebsd-x64': 0.21.5 - '@esbuild/linux-arm': 0.21.5 - '@esbuild/linux-arm64': 0.21.5 - '@esbuild/linux-ia32': 0.21.5 - '@esbuild/linux-loong64': 0.21.5 - '@esbuild/linux-mips64el': 0.21.5 - '@esbuild/linux-ppc64': 0.21.5 - '@esbuild/linux-riscv64': 0.21.5 - '@esbuild/linux-s390x': 0.21.5 - '@esbuild/linux-x64': 0.21.5 - '@esbuild/netbsd-x64': 0.21.5 - '@esbuild/openbsd-x64': 0.21.5 - '@esbuild/sunos-x64': 0.21.5 - '@esbuild/win32-arm64': 0.21.5 - '@esbuild/win32-ia32': 0.21.5 - '@esbuild/win32-x64': 0.21.5 + '@esbuild/aix-ppc64': 0.25.3 + '@esbuild/android-arm': 0.25.3 + '@esbuild/android-arm64': 0.25.3 + '@esbuild/android-x64': 0.25.3 + '@esbuild/darwin-arm64': 0.25.3 + '@esbuild/darwin-x64': 0.25.3 + '@esbuild/freebsd-arm64': 0.25.3 + '@esbuild/freebsd-x64': 0.25.3 + '@esbuild/linux-arm': 0.25.3 + '@esbuild/linux-arm64': 0.25.3 + '@esbuild/linux-ia32': 0.25.3 + '@esbuild/linux-loong64': 0.25.3 + '@esbuild/linux-mips64el': 0.25.3 + '@esbuild/linux-ppc64': 0.25.3 + '@esbuild/linux-riscv64': 0.25.3 + '@esbuild/linux-s390x': 0.25.3 + '@esbuild/linux-x64': 0.25.3 + '@esbuild/netbsd-arm64': 0.25.3 + '@esbuild/netbsd-x64': 0.25.3 + '@esbuild/openbsd-arm64': 0.25.3 + '@esbuild/openbsd-x64': 0.25.3 + '@esbuild/sunos-x64': 0.25.3 + '@esbuild/win32-arm64': 0.25.3 + '@esbuild/win32-ia32': 0.25.3 + '@esbuild/win32-x64': 0.25.3 escalade@3.2.0: {} @@ -3846,6 +3917,10 @@ snapshots: dependencies: reusify: 1.1.0 + fdir@6.4.4(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 @@ -4082,6 +4157,8 @@ snapshots: picomatch@2.3.1: {} + picomatch@4.0.2: {} + pify@2.3.0: {} pirates@4.0.7: {} @@ -4312,6 +4389,25 @@ snapshots: source-map-js@1.2.1: {} + source-map@0.5.6: {} + + stack-generator@2.0.10: + dependencies: + stackframe: 1.3.4 + + stackframe@1.3.4: {} + + stacktrace-gps@3.1.2: + dependencies: + source-map: 0.5.6 + stackframe: 1.3.4 + + stacktrace-js@2.0.2: + dependencies: + error-stack-parser: 2.1.4 + stack-generator: 2.0.10 + stacktrace-gps: 3.1.2 + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -4393,6 +4489,11 @@ snapshots: tiny-invariant@1.3.3: {} + tinyglobby@0.2.13: + dependencies: + fdir: 6.4.4(picomatch@4.0.2) + picomatch: 4.0.2 + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -4476,14 +4577,19 @@ snapshots: d3-time: 3.1.0 d3-timer: 3.0.1 - vite@5.4.18(@types/node@22.14.1): + vite@6.3.4(@types/node@22.14.1)(jiti@1.21.7)(yaml@2.7.1): dependencies: - esbuild: 0.21.5 + esbuild: 0.25.3 + fdir: 6.4.4(picomatch@4.0.2) + picomatch: 4.0.2 postcss: 8.5.3 rollup: 4.40.0 + tinyglobby: 0.2.13 optionalDependencies: '@types/node': 22.14.1 fsevents: 2.3.3 + jiti: 1.21.7 + yaml: 2.7.1 which@2.0.2: dependencies: diff --git a/scaffold/vite.config.ts b/scaffold/vite.config.ts index 7129c2c..5620379 100644 --- a/scaffold/vite.config.ts +++ b/scaffold/vite.config.ts @@ -1,207 +1,92 @@ -import { defineConfig, Plugin } from "vite"; +import { defineConfig, Plugin, HtmlTagDescriptor } from "vite"; import react from "@vitejs/plugin-react-swc"; import path from "path"; +import fs from "fs"; export function devErrorAndNavigationPlugin(): Plugin { + let stacktraceJsContent: string | null = null; + let dyadShimContent: string | null = null; + return { name: "dev-error-and-navigation-handler", apply: "serve", + + configResolved() { + const stackTraceLibPath = path.join( + "node_modules", + "stacktrace-js", + "dist", + "stacktrace.min.js" + ); + if (stackTraceLibPath) { + try { + stacktraceJsContent = fs.readFileSync(stackTraceLibPath, "utf-8"); + } catch (error) { + console.error( + `[dyad-shim] Failed to read stacktrace.js from ${stackTraceLibPath}:`, + error + ); + stacktraceJsContent = null; + } + } else { + console.error(`[dyad-shim] stacktrace.js not found.`); + } + + const dyadShimPath = path.join("dyad-shim.js"); + if (dyadShimPath) { + try { + dyadShimContent = fs.readFileSync(dyadShimPath, "utf-8"); + } catch (error) { + console.error( + `[dyad-shim] Failed to read dyad-shim from ${dyadShimPath}:`, + error + ); + dyadShimContent = null; + } + } else { + console.error(`[dyad-shim] stacktrace.js not found.`); + } + }, + transformIndexHtml(html) { - return { - html, - tags: [ - { - tag: "script", - injectTo: "head", - children: ` - (function() { - // Check if running inside an iframe immediately - const isInsideIframe = window.parent !== window; - if (!isInsideIframe) { - // If not in an iframe, no need for the rest of the script - // console.log('[vite-dev-navigation] Not inside an iframe. Skipping setup.'); - return; - } + const tags: HtmlTagDescriptor[] = []; - // Use a unique key for our timestamp to avoid conflicts (optional, but kept for state consistency) - const NAV_TIMESTAMP_KEY = '__viteDevNavTimestamp'; - let previousUrl = window.location.href; - let lastNavigationTimestamp = Date.now(); // Initialize with current time + // 1. Inject stacktrace.js + if (stacktraceJsContent) { + tags.push({ + tag: "script", + injectTo: "head-prepend", + children: stacktraceJsContent, + }); + } else { + tags.push({ + tag: "script", + injectTo: "head-prepend", + children: + "console.warn('[dyad-shim] stacktrace.js library was not injected.');", + }); + } - // --- Initial State Timestamp Setup (Optional but helps consistency) --- - try { - const initialState = history.state || {}; - if (!initialState[NAV_TIMESTAMP_KEY]) { - initialState[NAV_TIMESTAMP_KEY] = lastNavigationTimestamp; - // Use try-catch for replaceState as well, in case state is not serializable initially - try { - history.replaceState(initialState, '', window.location.href); - // console.log('[vite-dev-navigation] Initial navigation timestamp set:', lastNavigationTimestamp); - } catch(replaceError) { - console.warn('[vite-dev-navigation] Could not set initial navigation timestamp via replaceState:', replaceError); - } - } else { - lastNavigationTimestamp = initialState[NAV_TIMESTAMP_KEY]; - // console.log('[vite-dev-navigation] Using existing initial navigation timestamp:', lastNavigationTimestamp); - } - } catch (e) { - console.warn('[vite-dev-navigation] Could not access or modify initial history state:', e); - } + // 2. Inject dyad shim + if (dyadShimContent) { + tags.push({ + tag: "script", + injectTo: "head-prepend", + children: dyadShimContent, + }); + } else { + tags.push({ + tag: "script", + injectTo: "head-prepend", + children: "console.warn('[dyad-shim] dyad shim was not injected.');", + }); + } - // --- History API Overrides --- - const originalPushState = history.pushState; - const originalReplaceState = history.replaceState; - - const handleStateChangeAndNotify = (originalMethod, state, title, url) => { - const newTimestamp = Date.now(); - const oldUrlForMessage = previousUrl; // Capture previous URL before potential change - - // Prepare new state with timestamp - let newState = state || {}; - if (typeof newState !== 'object' || newState === null) { - newState = {}; - } else if (Object.isFrozen(newState)) { // Handle frozen state objects - newState = { ...newState }; - } - newState[NAV_TIMESTAMP_KEY] = newTimestamp; - - // Determine the intended new URL *before* calling the original method - let newUrl; - try { - // Resolve the potentially relative URL against the current location - newUrl = url ? new URL(url, window.location.href).href : window.location.href; - } catch (e) { - console.warn('[vite-dev-navigation] Error constructing URL:', url, e); - newUrl = window.location.href; // Fallback - } - - // Determine the type of operation - const navigationType = (originalMethod === originalPushState ? 'pushState' : 'replaceState'); - - // Call the original history method - try { - originalMethod.call(history, newState, title, url); - // Update internal state *after* successful call - lastNavigationTimestamp = newTimestamp; - previousUrl = window.location.href; // Use the actual URL after the call - - // Post message to parent *after* successful state change - // Use the 'newUrl' we calculated earlier as the intended target - // Use 'oldUrlForMessage' as the URL before this operation started - // console.log(\`[vite-dev-navigation] Emitting message: { type: '\${navigationType}', payload: { oldUrl: '\${oldUrlForMessage}', newUrl: '\${newUrl}' } }\`); - window.parent.postMessage({ - type: navigationType, // 'pushState' or 'replaceState' - payload: { - oldUrl: oldUrlForMessage, - newUrl: newUrl // The URL passed to pushState/replaceState, resolved - } - }, '*'); // Consider a specific targetOrigin - - } catch (e) { - console.error(\`[vite-dev-navigation] Error calling original \${navigationType}: \`, e); - // Optionally notify parent about the error during navigation attempt - window.parent.postMessage({ - type: 'navigation-error', - payload: { - operation: navigationType, - message: e.message, - error: e.toString(), - stateAttempted: state, // Be careful sending state, might be large or sensitive - urlAttempted: url - } - }, '*'); - } - }; - - history.pushState = function(state, title, url) { - handleStateChangeAndNotify(originalPushState, state, title, url); - }; - - history.replaceState = function(state, title, url) { - handleStateChangeAndNotify(originalReplaceState, state, title, url); - }; - - // --- Listener for Back/Forward Navigation (popstate event) --- - // We keep this listener primarily to update our internal 'previousUrl' - // and 'lastNavigationTimestamp' state so that subsequent push/replace - // messages report the correct 'oldUrl'. We no longer send messages from here. - window.addEventListener('popstate', (event) => { - const currentUrl = window.location.href; - // console.log('[vite-dev-navigation] Popstate event detected. Previous URL was:', previousUrl, 'New URL is:', currentUrl); - - const newStateTimestamp = event.state?.[NAV_TIMESTAMP_KEY]; - if (typeof newStateTimestamp === 'number') { - lastNavigationTimestamp = newStateTimestamp; // Update timestamp from popped state - // console.log('[vite-dev-navigation] Updated lastNavigationTimestamp from popstate:', lastNavigationTimestamp); - } else { - // console.warn('[vite-dev-navigation] Popstate event state missing navigation timestamp.'); - // If timestamp is missing, we might lose track, but there's not much we can do reliably. - // Keep the last known timestamp. - } - - // Update previousUrl to reflect the new reality AFTER the popstate event - previousUrl = currentUrl; - }); - - // --- Listener for Commands from Parent --- - window.addEventListener('message', (event) => { - // Security check: Ensure message is from parent - if (event.source !== window.parent || !event.data || typeof event.data !== 'object') { - return; - } - - if (event.data.type === 'navigate') { - const direction = event.data.payload?.direction; - // console.log(\`[vite-dev-navigation] Received command: \${direction}\`); - - if (direction === 'forward') { - history.forward(); - } else if (direction === 'backward') { - history.back(); - } else { - console.warn('[vite-dev-navigation] Received navigate command with invalid direction:', direction); - } - } - }); - - - // --- Existing Error Handling --- - window.addEventListener('error', (event) => { - // console.log('[vite-dev-navigation] Forwarding error event to parent.'); - window.parent.postMessage({ - type: 'window-error', - payload: { - message: event.message, - filename: event.filename, - lineno: event.lineno, - colno: event.colno, - error: event.error?.toString() // Include stack trace if available - } - }, '*'); - }); - - window.addEventListener('unhandledrejection', (event) => { - // console.log('[vite-dev-navigation] Forwarding unhandledrejection event to parent.'); - window.parent.postMessage({ - type: 'unhandled-rejection', - payload: { - reason: event.reason instanceof Error ? event.reason.toString() : JSON.stringify(event.reason) // Attempt to serialize reason - } - }, '*'); - }); - - // console.log('[vite-dev-navigation] Navigation/error script initialized inside iframe. Initial URL:', previousUrl, 'Initial Timestamp:', lastNavigationTimestamp); - - })(); // End of IIFE - `, - }, - ], - }; + return { html, tags }; }, }; } -// https://vitejs.dev/config/ export default defineConfig(({ mode }) => ({ server: { host: "::", diff --git a/src/components/preview_panel/PreviewIframe.tsx b/src/components/preview_panel/PreviewIframe.tsx index 8135ea5..b4cbd23 100644 --- a/src/components/preview_panel/PreviewIframe.tsx +++ b/src/components/preview_panel/PreviewIframe.tsx @@ -45,7 +45,9 @@ const ErrorBanner = ({ error, onDismiss, onAIFix }: ErrorBannerProps) => { {/* Error message in the middle */}
-
{error}
+
+ {error} +
{/* Tip message */} @@ -159,8 +161,16 @@ export const PreviewIframe = ({ const { type, payload } = event.data; - if (type === "window-error") { - const errorMessage = `Error in ${payload.filename} (line ${payload.lineno}, col ${payload.colno}): ${payload.message}`; + if ( + type === "window-error" || + type === "unhandled-rejection" || + type === "iframe-sourcemapped-error" + ) { + const stack = + type === "iframe-sourcemapped-error" + ? payload.stack.split("\n").slice(0, 1).join("\n") + : payload.stack; + const errorMessage = `Error ${payload.message}\nStack trace: ${stack}`; console.error("Iframe error:", errorMessage); setErrorMessage(errorMessage); setAppOutput((prev) => [ @@ -171,18 +181,6 @@ export const PreviewIframe = ({ appId: selectedAppId!, }, ]); - } else if (type === "unhandled-rejection") { - const errorMessage = `Unhandled Promise Rejection: ${payload.reason}`; - console.error("Iframe unhandled rejection:", errorMessage); - setErrorMessage(errorMessage); - setAppOutput((prev) => [ - ...prev, - { - message: `Iframe unhandled rejection: ${errorMessage}`, - type: "client-error", - appId: selectedAppId!, - }, - ]); } else if (type === "pushState" || type === "replaceState") { console.debug(`Navigation event: ${type}`, payload); @@ -201,10 +199,6 @@ export const PreviewIframe = ({ newHistory[currentHistoryPosition] = payload.newUrl; setNavigationHistory(newHistory); } - - // Update navigation buttons state - setCanGoBack(currentHistoryPosition > 0); - setCanGoForward(currentHistoryPosition < navigationHistory.length - 1); } }; @@ -212,6 +206,12 @@ export const PreviewIframe = ({ return () => window.removeEventListener("message", handleMessage); }, [navigationHistory, currentHistoryPosition, selectedAppId]); + useEffect(() => { + // Update navigation buttons state + setCanGoBack(currentHistoryPosition > 0); + setCanGoForward(currentHistoryPosition < navigationHistory.length - 1); + }, [navigationHistory, currentHistoryPosition]); + // Initialize navigation history when iframe loads useEffect(() => { if (appUrl) {