Browse Source

feat: implement development-only click-to-component navigation by injecting source location data via custom Webpack loader and IDE API route

front-test-2
ghorbani 4 weeks ago
parent
commit
af88ecff69
  1. 4
      .babelrc
  2. 90
      AUTO_CLICK_SETUP_GUIDE.md
  3. 42
      next.config.ts
  4. 757
      package-lock.json
  5. 5
      package.json
  6. 79
      src/app/api/open-in-ide/route.ts
  7. 35
      src/components/dev/dev-click-to-component.tsx
  8. 1
      src/components/utils/dev-click-to-component.tsx
  9. 42
      src/plugins/jsx-locator-loader.cjs

4
.babelrc

@ -1,4 +0,0 @@
{
"presets": ["next/babel"],
"plugins": ["./src/plugins/add-data-locator.cjs"]
}

90
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 (
<>
<Component {...pageProps} />
{/* این خط فقط در حالت لوکال اجرا میشود */}
{process.env.NODE_ENV === 'development' && <DevClickToComponent />}
</>
);
}
```
---
## نحوه استفاده
پس از اتمام مراحل بالا، دستور `npm run dev` را مجدداً اجرا کنید.
وارد مرورگر شوید، کلید **`Alt`** را روی کیبورد نگه دارید و روی هر بخشی از سایت کلیک کنید. می‌بینید که فایل مربوطه مستقیماً در ادیتور شما (روی همان خط کد) باز خواهد شد!

42
next.config.ts

@ -1,15 +1,17 @@
import type { NextConfig } from "next"; import type { NextConfig } from "next";
import path from "path";
const nextConfig: NextConfig = { const nextConfig: NextConfig = {
output: 'standalone',
allowedDevOrigins: ['192.168.1.64'],
output: "standalone",
allowedDevOrigins: ["192.168.1.64"],
turbopack: {},
// Compression // Compression
compress: true, compress: true,
// Image optimization // Image optimization
images: { images: {
formats: ['image/avif', 'image/webp'],
formats: ["image/avif", "image/webp"],
remotePatterns: [ remotePatterns: [
{ {
protocol: "https", 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 // Headers for caching and preload
async headers() { async headers() {
return [ return [
{ {
source: '/:path*',
source: "/:path*",
headers: [ 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: [ headers: [
{ {
key: 'Cache-Control',
value: 'public, max-age=31536000, immutable',
key: "Cache-Control",
value: "public, max-age=31536000, immutable",
}, },
], ],
}, },

757
package-lock.json

@ -17,6 +17,9 @@
"react-icons": "^5.6.0" "react-icons": "^5.6.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^8.0.1",
"@babel/preset-react": "^8.0.1",
"@babel/preset-typescript": "^8.0.1",
"@biomejs/biome": "2.2.0", "@biomejs/biome": "2.2.0",
"@tailwindcss/postcss": "^4", "@tailwindcss/postcss": "^4",
"@types/google-libphonenumber": "^7.4.30", "@types/google-libphonenumber": "^7.4.30",
@ -40,6 +43,532 @@
"url": "https://github.com/sponsors/sindresorhus" "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": { "node_modules/@biomejs/biome": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.2.0.tgz", "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.2.0.tgz",
@ -774,9 +1303,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -793,9 +1319,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -812,9 +1335,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -831,9 +1351,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -1181,6 +1698,13 @@
"react": "^18 || ^19" "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": { "node_modules/@types/google-libphonenumber": {
"version": "7.4.30", "version": "7.4.30",
"resolved": "https://registry.npmjs.org/@types/google-libphonenumber/-/google-libphonenumber-7.4.30.tgz", "resolved": "https://registry.npmjs.org/@types/google-libphonenumber/-/google-libphonenumber-7.4.30.tgz",
@ -1188,6 +1712,13 @@
"dev": true, "dev": true,
"license": "MIT" "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": { "node_modules/@types/node": {
"version": "20.19.39", "version": "20.19.39",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.39.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.39.tgz",
@ -1249,9 +1780,9 @@
} }
}, },
"node_modules/baseline-browser-mapping": { "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", "license": "Apache-2.0",
"bin": { "bin": {
"baseline-browser-mapping": "dist/cli.cjs" "baseline-browser-mapping": "dist/cli.cjs"
@ -1260,6 +1791,40 @@
"node": ">=6.0.0" "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": { "node_modules/call-bind-apply-helpers": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "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": { "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": [ "funding": [
{ {
"type": "opencollective", "type": "opencollective",
@ -1311,6 +1876,13 @@
"node": ">= 0.8" "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": { "node_modules/csstype": {
"version": "3.2.3", "version": "3.2.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
@ -1368,6 +1940,23 @@
"node": ">= 0.4" "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": { "node_modules/enhanced-resolve": {
"version": "5.20.1", "version": "5.20.1",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz",
@ -1427,6 +2016,16 @@
"node": ">= 0.4" "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": { "node_modules/follow-redirects": {
"version": "1.16.0", "version": "1.16.0",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz",
@ -1472,6 +2071,16 @@
"url": "https://github.com/sponsors/ljharb" "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": { "node_modules/get-intrinsic": {
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
@ -1589,6 +2198,17 @@
"node": ">= 6" "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": { "node_modules/jiti": {
"version": "2.6.1", "version": "2.6.1",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
@ -1599,6 +2219,39 @@
"jiti": "lib/jiti-cli.mjs" "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": { "node_modules/lightningcss": {
"version": "1.32.0", "version": "1.32.0",
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
@ -1860,6 +2513,16 @@
"url": "https://opencollective.com/parcel" "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": { "node_modules/magic-string": {
"version": "0.30.21", "version": "0.30.21",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", "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": { "node_modules/picocolors": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@ -1987,7 +2674,6 @@
"version": "8.5.19", "version": "8.5.19",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.19.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.19.tgz",
"integrity": "sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ==", "integrity": "sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ==",
"dev": true,
"funding": [ "funding": [
{ {
"type": "opencollective", "type": "opencollective",
@ -2061,8 +2747,8 @@
"version": "7.7.4", "version": "7.7.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"devOptional": true,
"license": "ISC", "license": "ISC",
"optional": true,
"bin": { "bin": {
"semver": "bin/semver.js" "semver": "bin/semver.js"
}, },
@ -2194,6 +2880,37 @@
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"dev": true, "dev": true,
"license": "MIT" "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"
}
} }
} }
} }

5
package.json

@ -3,7 +3,7 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev",
"dev": "next dev --webpack",
"build": "next build", "build": "next build",
"start": "next start", "start": "next start",
"lint": "biome check", "lint": "biome check",
@ -19,6 +19,9 @@
"react-icons": "^5.6.0" "react-icons": "^5.6.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^8.0.1",
"@babel/preset-react": "^8.0.1",
"@babel/preset-typescript": "^8.0.1",
"@biomejs/biome": "2.2.0", "@biomejs/biome": "2.2.0",
"@tailwindcss/postcss": "^4", "@tailwindcss/postcss": "^4",
"@types/google-libphonenumber": "^7.4.30", "@types/google-libphonenumber": "^7.4.30",

79
src/app/api/open-in-ide/route.ts

@ -1,90 +1,35 @@
import { NextRequest } from "next/server"; import { NextRequest } from "next/server";
import { exec } from "child_process"; import { exec } from "child_process";
import os from "os";
export const dynamic = "force-dynamic"; export const dynamic = "force-dynamic";
export const runtime = "nodejs"; 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) { export async function POST(request: NextRequest) {
if (process.env.NODE_ENV !== "development") { if (process.env.NODE_ENV !== "development") {
return Response.json({ error: "Forbidden in production" }, { status: 403 }); return Response.json({ error: "Forbidden in production" }, { status: 403 });
} }
try { try {
const { locator, userAgent } = await request.json();
const { locator } = await request.json();
if (!locator || typeof locator !== "string") { if (!locator || typeof locator !== "string") {
return Response.json({ error: "Locator is required" }, { status: 400 }); 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) => { exec(command, (error) => {
if (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}`);
}
});
} }
}); });

35
src/components/dev/dev-click-to-component.tsx

@ -118,17 +118,29 @@ export function DevClickToComponent() {
const positionSuffix = const positionSuffix =
line === null ? "" : `:${line}${column === null ? "" : `:${column}`}`; 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); document.addEventListener("click", handleDesktopClick, true);
@ -217,7 +229,6 @@ export function DevClickToComponent() {
body: JSON.stringify({ type, args }), body: JSON.stringify({ type, args }),
}); });
} catch (err) { } catch (err) {
// Print failure directly to the original console to prevent recursion
originalError.call(console, "[DevClickToComponent] Failed to forward log to server:", err); originalError.call(console, "[DevClickToComponent] Failed to forward log to server:", err);
} finally { } finally {
isSending = false; isSending = false;
@ -377,7 +388,6 @@ export function DevClickToComponent() {
try { try {
responseBody = this.responseText; responseBody = this.responseText;
} catch { } catch {
// If responseText is not accessible (e.g. responseType is not '' or 'text'), fall back to response
try { try {
if (typeof this.response === "string") { if (typeof this.response === "string") {
responseBody = this.response; responseBody = this.response;
@ -609,4 +619,3 @@ export function DevClickToComponent() {
} }
export default DevClickToComponent; export default DevClickToComponent;

1
src/components/utils/dev-click-to-component.tsx

@ -0,0 +1 @@
export { DevClickToComponent, default } from "@/components/dev/dev-click-to-component";

42
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;
};
Loading…
Cancel
Save