From af88ecff697455037a1245f212f94d4c4dcd2bdf Mon Sep 17 00:00:00 2001 From: ghorbani Date: Sun, 26 Jul 2026 15:41:39 +0330 Subject: [PATCH] feat: implement development-only click-to-component navigation by injecting source location data via custom Webpack loader and IDE API route --- .babelrc | 4 - AUTO_CLICK_SETUP_GUIDE.md | 90 +++ next.config.ts | 42 +- package-lock.json | 757 +++++++++++++++++- package.json | 5 +- src/app/api/open-in-ide/route.ts | 79 +- src/components/dev/dev-click-to-component.tsx | 35 +- .../utils/dev-click-to-component.tsx | 1 + src/plugins/jsx-locator-loader.cjs | 42 + 9 files changed, 938 insertions(+), 117 deletions(-) delete mode 100644 .babelrc create mode 100644 AUTO_CLICK_SETUP_GUIDE.md create mode 100644 src/components/utils/dev-click-to-component.tsx create mode 100644 src/plugins/jsx-locator-loader.cjs diff --git a/.babelrc b/.babelrc deleted file mode 100644 index fe49131..0000000 --- a/.babelrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "presets": ["next/babel"], - "plugins": ["./src/plugins/add-data-locator.cjs"] -} diff --git a/AUTO_CLICK_SETUP_GUIDE.md b/AUTO_CLICK_SETUP_GUIDE.md new file mode 100644 index 0000000..72ca0a4 --- /dev/null +++ b/AUTO_CLICK_SETUP_GUIDE.md @@ -0,0 +1,90 @@ +# راهنمای نصب و راه‌اندازی قابلیت Auto-Click-to-IDE + +این قابلیت به برنامه‌نویسان اجازه می‌دهد تا در حالت توسعه (Development)، با نگه داشتن کلید `Alt` و کلیک روی هر بخش از رابط کاربری وب‌سایت، مستقیماً به فایل و خط کد مربوطه در محیط ادیتور (IDE) منتقل شوند. این ابزار بهینه‌سازی شده تا فایل‌ها را در **پنجره فعلی ادیتور** باز کند و از باز شدن نرم‌افزارهای تکراری جلوگیری نماید. + +## فایل‌های مورد نیاز +برای انتقال این قابلیت به پروژه‌های جدید Next.js، به ۳ فایل زیر نیاز دارید: + +1. `dev-click-to-component.tsx` (کامپوننت اصلی رابط کاربری) +2. `add-data-locator.cjs` (پلاگین Babel برای یافتن خطوط کد) +3. `open-in-ide.ts` (رابط API برای ارتباط با ادیتور) + +--- + +## مراحل نصب در پروژه‌های جدید Next.js + +### مرحله ۱: انتقال فایل‌ها +ابتدا فایل‌های مورد نیاز را در مسیرهای مناسب در پروژه جدید قرار دهید: +- فایل `dev-click-to-component.tsx` را در پوشه کامپوننت‌ها (مثلاً `src/components/utils/`) کپی کنید. +- فایل `add-data-locator.cjs` را در ریشه پروژه (یا پوشه `src/plugins/`) کپی کنید. + +### مرحله ۲: ایجاد API Route (بسیار مهم) +برای اینکه فایل‌ها در همان پنجره‌ی باز ادیتور لود شوند (نه در پنجره جدید)، باید یک API بسازیم. +در پروژه خود (اگر از Pages Router استفاده می‌کنید) فایلی در مسیر `src/pages/api/open-in-ide.ts` بسازید و کدهای زیر را در آن قرار دهید: + +```typescript +import type { NextApiRequest, NextApiResponse } from 'next'; +import { exec } from 'child_process'; +import os from 'os'; + +export default function handler(req: NextApiRequest, res: NextApiResponse) { + if (req.method !== 'POST') return res.status(405).json({ message: 'Method Not Allowed' }); + + const { locator } = req.body; + if (!locator) return res.status(400).json({ message: 'Locator is required' }); + + // اجرای کامند برای باز کردن در ادیتور (-r برای جلوگیری از ساخت پنجره جدید) + const isWindows = os.platform() === 'win32'; + const bin = isWindows ? 'antigravity-ide.cmd' : 'antigravity-ide'; + const cmd = `${bin} -r -g "${locator}"`; + + exec(cmd, (error) => { + if (error) { + console.error(`[open-in-ide] Error: ${error.message}`); + return res.status(500).json({ message: 'Failed to open file in IDE' }); + } + res.status(200).json({ success: true }); + }); +} +``` + +### مرحله ۳: تنظیمات Babel +Next.js باید بتواند پلاگین ما را بخواند. یک فایل به نام `.babelrc` در ریشه (Root) پروژه خود بسازید (اگر ندارید) و کدهای زیر را در آن قرار دهید: + +```json +{ + "presets": ["next/babel"], + "env": { + "development": { + "plugins": [ + "./add-data-locator.cjs" + ] + } + } +} +``` +*(نکته: آدرس `./add-data-locator.cjs` را بر اساس مکانی که فایل را در مرحله ۱ قرار داده‌اید تنظیم کنید).* + +### مرحله ۴: اضافه کردن کامپوننت به Layout +در نهایت، فایل اصلی پروژه خود (مثلاً `src/pages/_app.tsx` یا `src/app/layout.tsx`) را باز کنید و کامپوننت را در محیط توسعه (Development) فراخوانی کنید: + +```tsx +import { DevClickToComponent } from '@/components/utils/dev-click-to-component'; + +export default function App({ Component, pageProps }) { + return ( + <> + + + {/* این خط فقط در حالت لوکال اجرا میشود */} + {process.env.NODE_ENV === 'development' && } + + ); +} +``` + +--- + +## نحوه استفاده +پس از اتمام مراحل بالا، دستور `npm run dev` را مجدداً اجرا کنید. +وارد مرورگر شوید، کلید **`Alt`** را روی کیبورد نگه دارید و روی هر بخشی از سایت کلیک کنید. می‌بینید که فایل مربوطه مستقیماً در ادیتور شما (روی همان خط کد) باز خواهد شد! diff --git a/next.config.ts b/next.config.ts index 225faba..d679207 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,15 +1,17 @@ import type { NextConfig } from "next"; +import path from "path"; const nextConfig: NextConfig = { - output: 'standalone', - allowedDevOrigins: ['192.168.1.64'], - + output: "standalone", + allowedDevOrigins: ["192.168.1.64"], + turbopack: {}, + // Compression compress: true, - + // Image optimization images: { - formats: ['image/avif', 'image/webp'], + formats: ["image/avif", "image/webp"], remotePatterns: [ { protocol: "https", @@ -17,25 +19,41 @@ const nextConfig: NextConfig = { }, ], }, - + + // Webpack config for data-locator injection in development + webpack: (config, { dev }) => { + if (dev) { + config.module.rules.push({ + test: /\.(tsx|jsx)$/, + exclude: /node_modules/, + use: [ + { + loader: path.resolve(__dirname, "src/plugins/jsx-locator-loader.cjs"), + }, + ], + }); + } + return config; + }, + // Headers for caching and preload async headers() { return [ { - source: '/:path*', + source: "/:path*", headers: [ { - key: 'Cache-Control', - value: 'public, max-age=3600, stale-while-revalidate=86400', + key: "Cache-Control", + value: "public, max-age=3600, stale-while-revalidate=86400", }, ], }, { - source: '/fonts/:path*', + source: "/fonts/:path*", headers: [ { - key: 'Cache-Control', - value: 'public, max-age=31536000, immutable', + key: "Cache-Control", + value: "public, max-age=31536000, immutable", }, ], }, diff --git a/package-lock.json b/package-lock.json index a813a4b..576689b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,9 @@ "react-icons": "^5.6.0" }, "devDependencies": { + "@babel/core": "^8.0.1", + "@babel/preset-react": "^8.0.1", + "@babel/preset-typescript": "^8.0.1", "@biomejs/biome": "2.2.0", "@tailwindcss/postcss": "^4", "@types/google-libphonenumber": "^7.4.30", @@ -40,6 +43,532 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@babel/code-frame": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-8.0.0.tgz", + "integrity": "sha512-dYYg153EyN2Ekbqw2zAsbd6/JR+9N2SEoC7YV2GyyqMM7x9bLDTjBD6XBhSMLH0wtIVyJj03jWNriQhaN+eoCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^8.0.0", + "js-tokens": "^10.0.0" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-8.0.0.tgz", + "integrity": "sha512-DOjnob/cXOUgDOozCDeq/aK2p5y8dUIVdf6tNhEV1HQRd6I8aQ4f4fbtHRVEvb6lP3BGomrKHiS8ICAASSVQSw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@babel/core": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-8.0.1.tgz", + "integrity": "sha512-5FgxM4dLQpMJHSiVATk8foW263dVHQHBVpXYiimNECVWG01f4nFyEbQixeT6Mwvg7TayREJ2gpKl3o2RoMdnqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^8.0.0", + "@babel/generator": "^8.0.0", + "@babel/helper-compilation-targets": "^8.0.0", + "@babel/helpers": "^8.0.0", + "@babel/parser": "^8.0.0", + "@babel/template": "^8.0.0", + "@babel/traverse": "^8.0.0", + "@babel/types": "^8.0.0", + "@types/gensync": "^1.0.5", + "convert-source-map": "^2.0.0", + "empathic": "^2.0.1", + "gensync": "^1.0.0-beta.2", + "import-meta-resolve": "^4.2.0", + "json5": "^2.2.3", + "obug": "^2.1.1", + "semver": "^7.7.3" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-8.0.0.tgz", + "integrity": "sha512-NT9NrVwJsbSV6Y2FSstWa71EETOnzrjkL5/wX3D2mYHtKM+qvqB1DvR4D0Setb/gDBsHzRICifwEWMO8CnTF6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^8.0.0", + "@babel/types": "^8.0.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "@types/jsesc": "^2.5.0", + "jsesc": "^3.0.2" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-8.0.0.tgz", + "integrity": "sha512-NSpMkMsvvZqzThJ0p1B02cbtA2ObEyfBvq950bmNkyxsxvcxwhvvCB036rKhlEnuBBo30bOrk13u3FzlKSoRrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^8.0.0" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-8.0.0.tgz", + "integrity": "sha512-JwculLABZvyPvyLBpwU/E/IbH2uM3mnxNtIJpxnIfb24y1PrdVxK5Dqjle4DpgqpGRnwgC7G8IkzPdSXZrO1Ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^8.0.0", + "@babel/helper-validator-option": "^8.0.0", + "browserslist": "^4.24.0", + "lru-cache": "^11.0.0", + "semver": "^7.7.3" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-8.0.1.tgz", + "integrity": "sha512-++t3ZktzlLmASAxIlxeXQK9Z2YwUafYGYcvGBFevqOqt16HozVHStUoQvWD09fzAZOb/uJGpUTBuGK41AJAuOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^8.0.0", + "@babel/helper-member-expression-to-functions": "^8.0.0", + "@babel/helper-optimise-call-expression": "^8.0.0", + "@babel/helper-replace-supers": "^8.0.1", + "@babel/helper-skip-transparent-expression-wrappers": "^8.0.0", + "@babel/traverse": "^8.0.0", + "semver": "^7.7.3" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + }, + "peerDependencies": { + "@babel/core": "^8.0.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-8.0.0.tgz", + "integrity": "sha512-lLozHOM6sWWlxNo8CYqHy4MBZeTvHXNgVPBfPOGsjPKUzHC2Az9QwB6gxdQmpwHl6GlQtbGgS+lj5887guDiLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-8.0.0.tgz", + "integrity": "sha512-xkXrMbtk87Gk7+oKBVmBc6EORg/Qwx++AHESldmHkpvG8wgccdhJJFwrzqlF382Fk8wfXhJHWE/g/43QvEGNPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^8.0.0", + "@babel/types": "^8.0.0" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-8.0.0.tgz", + "integrity": "sha512-NZ7mSS93o4ndX4KrbD7W8Sf3QT8Qe24PrnFyUcuOPDzK6faqDFKjY9RG7he7+I7FdiQ4llpnosFqzrXa+Vy3Ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^8.0.0", + "@babel/types": "^8.0.0" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-8.0.1.tgz", + "integrity": "sha512-UgAhl1kqiW5ciE0yCXqqvnb4H2n3IELJ7lIIQRezwDPilPEZX5i+Rvbja9MFTkwUn2biEiSMeV31aUzR4Lwakw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^8.0.0", + "@babel/helper-validator-identifier": "^8.0.0", + "@babel/traverse": "^8.0.0" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + }, + "peerDependencies": { + "@babel/core": "^8.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-8.0.0.tgz", + "integrity": "sha512-3W6satvtPuCUkUx63S2jMoW9EQNYkADgs1HTfufmL7gCmAulHMKupA/12WNz4A0GMMFn/YnWWwqOT9IZrJHQjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^8.0.0" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-8.0.1.tgz", + "integrity": "sha512-3PKFgjTyPlhFhorfP+SjKQxLViIL++zWjFOO4hGriYU+Bsm983DxEM1JmDRJVWXV0O9npu+xXRqz7Pbd3mh70g==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^22.18.0 || >=24.11.0" + }, + "peerDependencies": { + "@babel/core": "^8.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-8.0.1.tgz", + "integrity": "sha512-B1SZADIcy3tmH8CmWvj4SHi/oAPom4UL3uknTc2QRNsPVLFk/sPnZvQL/8kj7Y5omvjMqie0vklvs6XM4OLW5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^8.0.0", + "@babel/helper-optimise-call-expression": "^8.0.0", + "@babel/traverse": "^8.0.0" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + }, + "peerDependencies": { + "@babel/core": "^8.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-8.0.0.tgz", + "integrity": "sha512-xmCA9kP3IhySsqhzwIdWGlDN/1A4cCKNBO/uwZx/3YzmDoMePwno2Q5/Bq0q+tYaKbeF940YiKV/kaW8Mzvpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^8.0.0", + "@babel/types": "^8.0.0" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-8.0.0.tgz", + "integrity": "sha512-6mJgmFFFIIO82vvoLt9XtRC7/TkzXfts1t/SpRX4IHSzMgqoPYCWesVu1udUPUWioAE/2fcG6WuI8zrkE1gwrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.4.tgz", + "integrity": "sha512-4wFaiLd0bVo4cIoTXI3zKI038NIWE/cr3jvBjejOVYVxV/m8Ltav1USiGzG1fmS5J2RhgEOgXNNK46cRPnRsrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-8.0.0.tgz", + "integrity": "sha512-U4Dybxh4WESWHt5XhBeExi4DrY0/DNK1aHpQbsrQXCUbFHuMweT0TpLEWKvaraV2Y6fS+ZXunsZ8zIuZIgvF2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@babel/helpers": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-8.0.0.tgz", + "integrity": "sha512-wfbi91pM3py96oIiJEz7qIpyXDytgr9zQC1HEWwlGNVRAEmItuU/0a41ZUKu1sJGyhhOIpc4t5vk4PYzt8wpsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^8.0.0", + "@babel/types": "^8.0.0" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@babel/parser": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-8.0.4.tgz", + "integrity": "sha512-srpptsAkEbbNIC/q8nT7o+m6CQe8CJUTV/t7MYc9NnWlgYVtHOb7JH6SorxMhN0kuRJjVqXbKClG6xSbPtzz+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^8.0.4" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-8.0.1.tgz", + "integrity": "sha512-n0jtCOxEovhU7METqSQjcZO9pX53nu9uNIjMS+hEt+Nt9jA7oOZoBIgbCxhhASmF6T6rPDGge5UAvh6Z4eFz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^8.0.1" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + }, + "peerDependencies": { + "@babel/core": "^8.0.0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-8.0.3.tgz", + "integrity": "sha512-jmTPwps7oSQSZaV1SxkQ3C12UWyufGysGc5OzDpZzvPAIX4mO7dJT3hoqkWVrSImvkcMiknir1iLN1SNV/CZzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^8.0.1" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + }, + "peerDependencies": { + "@babel/core": "^8.0.0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-8.0.1.tgz", + "integrity": "sha512-PMuzulWrrzFNmY3lXSk/tV9NRb7y0eZZLJY4UEo2TKszroxvUZHAPPi+T9FDyrQhod+TQA+t+8/QYaaMpiEuhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^8.0.1", + "@babel/helper-plugin-utils": "^8.0.1" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + }, + "peerDependencies": { + "@babel/core": "^8.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-display-name": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-8.0.1.tgz", + "integrity": "sha512-soLishXlkyu6jcICPyO3HEP7A3GCzKEnn7XfvYrImuWEOwFAz93qShmWSYPf5ww0ZkO4By0zsN2bVIDF54fSdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^8.0.1" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + }, + "peerDependencies": { + "@babel/core": "^8.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-8.0.1.tgz", + "integrity": "sha512-NgkoF7Uq+30TmOPDdNUimT0Nta02uVjqJRFNlVWKrbOCu/CkzfHa4aMnIs0lMpkMmZmWA1e42Va+F04i/pY1zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^8.0.0", + "@babel/helper-module-imports": "^8.0.0", + "@babel/helper-plugin-utils": "^8.0.1", + "@babel/plugin-syntax-jsx": "^8.0.1", + "@babel/types": "^8.0.0" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + }, + "peerDependencies": { + "@babel/core": "^8.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-development": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-8.0.1.tgz", + "integrity": "sha512-Hb+HUZpV9KFHjm+F+P3aLDMi8QXU9l3ROCQv20z18Me2sGyW5nNNR5YTevNlgHvCpFek3BnAwhDGq/BRndXViw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-transform-react-jsx": "^8.0.1" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + }, + "peerDependencies": { + "@babel/core": "^8.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-pure-annotations": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-8.0.1.tgz", + "integrity": "sha512-7/8UwU8hoPBurXa9tUiTTC8aACTRy5tCqLUtqikHp2eGiWoEB57AduOdbQ71OOMTEvawKrGhv3WfzkDpI+/oSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^8.0.0", + "@babel/helper-plugin-utils": "^8.0.1" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + }, + "peerDependencies": { + "@babel/core": "^8.0.0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-8.0.1.tgz", + "integrity": "sha512-0Svqp3413Eg0GElldykF/T7SNsxQO5YVGD70fZyAdZTnX8WRgcopmbiU7GTa5xY5ZnJcEpNbfns8/GjX+/1yeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^8.0.0", + "@babel/helper-create-class-features-plugin": "^8.0.1", + "@babel/helper-plugin-utils": "^8.0.1", + "@babel/helper-skip-transparent-expression-wrappers": "^8.0.0", + "@babel/plugin-syntax-typescript": "^8.0.1" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + }, + "peerDependencies": { + "@babel/core": "^8.0.0" + } + }, + "node_modules/@babel/preset-react": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-8.0.1.tgz", + "integrity": "sha512-jrFuPp/pTddFZbtmWhdLNAYc6UMcpboeUPnw0BBrm4nOmcAko/1TRcFi1PzWCeOFRU+VaSiKmat87W1HvR7mIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^8.0.1", + "@babel/helper-validator-option": "^8.0.0", + "@babel/plugin-transform-react-display-name": "^8.0.1", + "@babel/plugin-transform-react-jsx": "^8.0.1", + "@babel/plugin-transform-react-jsx-development": "^8.0.1", + "@babel/plugin-transform-react-pure-annotations": "^8.0.1" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + }, + "peerDependencies": { + "@babel/core": "^8.0.0" + } + }, + "node_modules/@babel/preset-typescript": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-8.0.1.tgz", + "integrity": "sha512-qrPhQIN1NLrPmzgazF9XKQqXrOcp/WJly+K+6ReFonn24FZqRJO7clxOJo6Ni75L+2vAqI3cHVU2OJLBxoPp5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^8.0.1", + "@babel/helper-validator-option": "^8.0.0", + "@babel/plugin-transform-modules-commonjs": "^8.0.1", + "@babel/plugin-transform-typescript": "^8.0.1" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + }, + "peerDependencies": { + "@babel/core": "^8.0.0" + } + }, + "node_modules/@babel/template": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-8.0.0.tgz", + "integrity": "sha512-eAD0QW/AlbamBbw0FeGiwasbCVPq5ncW0HNVyLP3B9czqLyh4gvw+5JTSNt6le9+ziAU7mqDZsKTHf3jTb4chQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^8.0.0", + "@babel/parser": "^8.0.0", + "@babel/types": "^8.0.0" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@babel/traverse": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-8.0.4.tgz", + "integrity": "sha512-bZnmqzGG8UZneG1lLxBoWIH0G6Gr1D846Yu4/3XnY6FhCndMR49u26nTY08u/dAxWmLWF9vGQOuC+84FfIUoeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^8.0.0", + "@babel/generator": "^8.0.0", + "@babel/helper-globals": "^8.0.0", + "@babel/parser": "^8.0.4", + "@babel/template": "^8.0.0", + "@babel/types": "^8.0.4", + "obug": "^2.1.1" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@babel/types": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-8.0.4.tgz", + "integrity": "sha512-eY+Yn3dCqTGmyiq2QRU66lA5FL8lqqqvecHt0fF3uHONIa7ToYsaCiWV8lOKqAs0Rb2SjixiKFROngnulPtt2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^8.0.0", + "@babel/helper-validator-identifier": "^8.0.4" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, "node_modules/@biomejs/biome": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.2.0.tgz", @@ -774,9 +1303,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -793,9 +1319,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -812,9 +1335,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -831,9 +1351,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1181,6 +1698,13 @@ "react": "^18 || ^19" } }, + "node_modules/@types/gensync": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/gensync/-/gensync-1.0.5.tgz", + "integrity": "sha512-MbsRCT7mTikHwKZ0X+LVUTLRrZZRLipTuXEO9qOYO+zmjMVk81axyClMROf6uoPD9MRVu46bx8zoR0Ad9q3NAg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/google-libphonenumber": { "version": "7.4.30", "resolved": "https://registry.npmjs.org/@types/google-libphonenumber/-/google-libphonenumber-7.4.30.tgz", @@ -1188,6 +1712,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/jsesc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@types/jsesc/-/jsesc-2.5.1.tgz", + "integrity": "sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { "version": "20.19.39", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.39.tgz", @@ -1249,9 +1780,9 @@ } }, "node_modules/baseline-browser-mapping": { - "version": "2.10.18", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.18.tgz", - "integrity": "sha512-VSnGQAOLtP5mib/DPyg2/t+Tlv65NTBz83BJBJvmLVHHuKJVaDOBvJJykiT5TR++em5nfAySPccDZDa4oSrn8A==", + "version": "2.11.3", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.3.tgz", + "integrity": "sha512-sbT0Ui/CZwyAyy7icT1Gw5P1LKRlFaHwaF6tDCW5YHq2X5SeeZFphBuIagopSfwSSZq3sQcbmEL072yphxm7ew==", "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.cjs" @@ -1260,6 +1791,40 @@ "node": ">=6.0.0" } }, + "node_modules/browserslist": { + "version": "4.28.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.7.tgz", + "integrity": "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.44", + "caniuse-lite": "^1.0.30001806", + "electron-to-chromium": "^1.5.393", + "node-releases": "^2.0.51", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", @@ -1274,9 +1839,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001787", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001787.tgz", - "integrity": "sha512-mNcrMN9KeI68u7muanUpEejSLghOKlVhRqS/Za2IeyGllJ9I9otGpR9g3nsw7n4W378TE/LyIteA0+/FOZm4Kg==", + "version": "1.0.30001806", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz", + "integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==", "funding": [ { "type": "opencollective", @@ -1311,6 +1876,13 @@ "node": ">= 0.8" } }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, "node_modules/csstype": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", @@ -1368,6 +1940,23 @@ "node": ">= 0.4" } }, + "node_modules/electron-to-chromium": { + "version": "1.5.396", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.396.tgz", + "integrity": "sha512-yHiw2Y3C3H9U6TMbOfoWK/BPreiOPXRfTWPBwQBoZG6/8TB6eOPnsy5oaRYuatR7Fw2SJ4kKforgufeo7fq0EQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/empathic": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.1.tgz", + "integrity": "sha512-YGRs8knHhKHVShLkFET/rWAU8kmHbOV5LwN938RHI0pljAJ1Gf6SzXsSmRaEzcXTtOOmVqJ5+WtQPL5uigY50Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/enhanced-resolve": { "version": "5.20.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", @@ -1427,6 +2016,16 @@ "node": ">= 0.4" } }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/follow-redirects": { "version": "1.16.0", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", @@ -1472,6 +2071,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -1589,6 +2198,17 @@ "node": ">= 6" } }, + "node_modules/import-meta-resolve": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", + "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/jiti": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", @@ -1599,6 +2219,39 @@ "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/js-tokens": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", + "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/lightningcss": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", @@ -1860,6 +2513,16 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", @@ -1977,6 +2640,30 @@ } } }, + "node_modules/node-releases": { + "version": "2.0.51", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz", + "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/obug": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.4.tgz", + "integrity": "sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -1987,7 +2674,6 @@ "version": "8.5.19", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.19.tgz", "integrity": "sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ==", - "dev": true, "funding": [ { "type": "opencollective", @@ -2061,8 +2747,8 @@ "version": "7.7.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "devOptional": true, "license": "ISC", - "optional": true, "bin": { "semver": "bin/semver.js" }, @@ -2194,6 +2880,37 @@ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } } } } diff --git a/package.json b/package.json index adca4cf..9ccb902 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "scripts": { - "dev": "next dev", + "dev": "next dev --webpack", "build": "next build", "start": "next start", "lint": "biome check", @@ -19,6 +19,9 @@ "react-icons": "^5.6.0" }, "devDependencies": { + "@babel/core": "^8.0.1", + "@babel/preset-react": "^8.0.1", + "@babel/preset-typescript": "^8.0.1", "@biomejs/biome": "2.2.0", "@tailwindcss/postcss": "^4", "@types/google-libphonenumber": "^7.4.30", diff --git a/src/app/api/open-in-ide/route.ts b/src/app/api/open-in-ide/route.ts index 0875883..aa25ed0 100644 --- a/src/app/api/open-in-ide/route.ts +++ b/src/app/api/open-in-ide/route.ts @@ -1,90 +1,35 @@ import { NextRequest } from "next/server"; import { exec } from "child_process"; +import os from "os"; export const dynamic = "force-dynamic"; export const runtime = "nodejs"; -const IDE_SCHEMES = [ - { - matches: ["antigravity"], - createUrl: (locator: string) => `antigravity://file/${locator}`, - }, - { - matches: ["cursor"], - createUrl: (locator: string) => `cursor://file/${locator}`, - }, - { - matches: ["vscode", "code"], - createUrl: (locator: string) => `vscode://file/${locator}`, - }, - { - matches: ["webstorm", "intellij"], - createUrl: (locator: string) => `webstorm://open?file=${locator}`, - }, - { - matches: ["sublime"], - createUrl: (locator: string) => `subl://open?url=file://${locator}`, - }, - { - matches: ["atom", "nova"], - createUrl: (locator: string) => `atom://open?url=file://${locator}`, - }, -] as const; - -function parseLocator(locator: string) { - const match = locator.match(/^(.*):(\d+|unknown):(\d+|unknown)$/); - - if (!match) { - return { filePath: locator, line: null, column: null }; - } - - const [, filePath, line, column] = match; - - return { - filePath, - line: line === "unknown" ? null : Number(line), - column: column === "unknown" ? null : Number(column), - }; -} - export async function POST(request: NextRequest) { if (process.env.NODE_ENV !== "development") { return Response.json({ error: "Forbidden in production" }, { status: 403 }); } try { - const { locator, userAgent } = await request.json(); + const { locator } = await request.json(); if (!locator || typeof locator !== "string") { return Response.json({ error: "Locator is required" }, { status: 400 }); } - const { filePath, line, column } = parseLocator(locator); - - // Sanitize filePath to prevent shell injection - // Windows paths: "C:\path\to\file" or "C:/path/to/file" - if (!/^[a-zA-Z]:[\\/][a-zA-Z0-9_\-\.\/\\ ]+$/.test(filePath)) { - return Response.json({ error: "Invalid path character detected" }, { status: 400 }); - } - - const positionSuffix = - line === null ? "" : `:${line}${column === null ? "" : `:${column}`}`; - - const browserUserAgent = (userAgent || "").toLowerCase(); - - const ideUrl = - IDE_SCHEMES.find(({ matches }) => - matches.some((match) => browserUserAgent.includes(match)), - )?.createUrl(`${filePath}${positionSuffix}`) ?? - `antigravity://file/${filePath}${positionSuffix}`; // Default to antigravity to match desktop behaviour - - // Run the shell command to open the custom URL in Windows - // Using start command to trigger registered protocol handler: start "" "url" - const command = `start "" "${ideUrl}"`; + const isWindows = os.platform() === "win32"; + const bin = isWindows ? "antigravity-ide.cmd" : "antigravity-ide"; + const command = `${bin} -r -g "${locator}"`; exec(command, (error) => { if (error) { - console.error(`Failed to execute open command: ${command}`, error); + console.error(`[open-in-ide] Primary CLI command failed: ${error.message}. Trying URL scheme fallback...`); + const fallbackCmd = `start "" "antigravity://file/${locator}"`; + exec(fallbackCmd, (fallbackErr) => { + if (fallbackErr) { + console.error(`[open-in-ide] Fallback failed: ${fallbackErr.message}`); + } + }); } }); diff --git a/src/components/dev/dev-click-to-component.tsx b/src/components/dev/dev-click-to-component.tsx index c47f797..3ce2122 100644 --- a/src/components/dev/dev-click-to-component.tsx +++ b/src/components/dev/dev-click-to-component.tsx @@ -118,17 +118,29 @@ export function DevClickToComponent() { const positionSuffix = line === null ? "" : `:${line}${column === null ? "" : `:${column}`}`; - const ideUrl = - IDE_SCHEMES.find(({ matches }) => - matches.some((match) => userAgent.includes(match)), - )?.createUrl(`${filePath}${positionSuffix}`) ?? - `antigravity://file/${filePath}${positionSuffix}`; + const locatorString = `${filePath}${positionSuffix}`; - try { - window.location.href = ideUrl; - } catch { - window.open(`file://${filePath}`, "_blank", "noopener,noreferrer"); - } + // Use the API route to execute the shell command which reuses the existing window + fetch("/api/open-in-ide", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ locator: locatorString, userAgent }), + }).catch((err) => { + console.error("[DevClickToComponent] Error sending API request:", err); + + // Fallback to URL scheme + const ideUrl = + IDE_SCHEMES.find(({ matches }) => + matches.some((match) => userAgent.includes(match)), + )?.createUrl(locatorString) ?? + `antigravity://file/${locatorString}`; + + try { + window.location.href = ideUrl; + } catch { + window.open(`file://${filePath}`, "_blank", "noopener,noreferrer"); + } + }); }; document.addEventListener("click", handleDesktopClick, true); @@ -217,7 +229,6 @@ export function DevClickToComponent() { body: JSON.stringify({ type, args }), }); } catch (err) { - // Print failure directly to the original console to prevent recursion originalError.call(console, "[DevClickToComponent] Failed to forward log to server:", err); } finally { isSending = false; @@ -377,7 +388,6 @@ export function DevClickToComponent() { try { responseBody = this.responseText; } catch { - // If responseText is not accessible (e.g. responseType is not '' or 'text'), fall back to response try { if (typeof this.response === "string") { responseBody = this.response; @@ -609,4 +619,3 @@ export function DevClickToComponent() { } export default DevClickToComponent; - diff --git a/src/components/utils/dev-click-to-component.tsx b/src/components/utils/dev-click-to-component.tsx new file mode 100644 index 0000000..0d798d2 --- /dev/null +++ b/src/components/utils/dev-click-to-component.tsx @@ -0,0 +1 @@ +export { DevClickToComponent, default } from "@/components/dev/dev-click-to-component"; diff --git a/src/plugins/jsx-locator-loader.cjs b/src/plugins/jsx-locator-loader.cjs new file mode 100644 index 0000000..a72aae5 --- /dev/null +++ b/src/plugins/jsx-locator-loader.cjs @@ -0,0 +1,42 @@ +const babel = require("@babel/core"); +const addDataLocator = require("./add-data-locator.cjs"); + +module.exports = function (source, inputSourceMap) { + if (process.env.NODE_ENV !== "development") { + return source; + } + + const filename = this.resourcePath; + if (!filename || filename.includes("node_modules")) { + return source; + } + + // Quick check: if there are no angle brackets, no JSX to transform + if (!source.includes("<")) { + return source; + } + + try { + const result = babel.transformSync(source, { + filename, + presets: [ + ["@babel/preset-react", { runtime: "automatic" }], + "@babel/preset-typescript", + ], + plugins: [addDataLocator], + configFile: false, + babelrc: false, + sourceMaps: true, + inputSourceMap: inputSourceMap || undefined, + }); + + if (result && result.code) { + this.callback(null, result.code, result.map); + return; + } + } catch (err) { + console.error(`[jsx-locator-loader] Skipped ${filename} due to parse notice: ${err.message}`); + } + + return source; +};