diff --git a/apps/demo-nextjs-antd/.eslintignore b/apps/demo-nextjs-antd/.eslintignore
new file mode 100644
index 00000000..5d52327c
--- /dev/null
+++ b/apps/demo-nextjs-antd/.eslintignore
@@ -0,0 +1,2 @@
+.eslintrc.js
+src/editor/plugins/context-menu-plugin/context-menu-layer.tsx
diff --git a/apps/demo-nextjs-antd/.eslintrc.js b/apps/demo-nextjs-antd/.eslintrc.js
new file mode 100644
index 00000000..08d7f75b
--- /dev/null
+++ b/apps/demo-nextjs-antd/.eslintrc.js
@@ -0,0 +1,21 @@
+const { defineConfig } = require('@flowgram.ai/eslint-config');
+
+module.exports = defineConfig({
+ parser: '@babel/eslint-parser',
+ preset: 'web',
+ packageRoot: __dirname,
+ parserOptions: {
+ requireConfigFile: false,
+ },
+ rules: {
+ 'no-console': 'off',
+ 'react/prop-types': 'off',
+ },
+ plugins: ['json'],
+ extends: ['next', 'next/core-web-vitals'],
+ settings: {
+ react: {
+ version: 'detect',
+ },
+ },
+});
diff --git a/apps/demo-nextjs-antd/.gitignore b/apps/demo-nextjs-antd/.gitignore
new file mode 100644
index 00000000..5ef6a520
--- /dev/null
+++ b/apps/demo-nextjs-antd/.gitignore
@@ -0,0 +1,41 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.*
+.yarn/*
+!.yarn/patches
+!.yarn/plugins
+!.yarn/releases
+!.yarn/versions
+
+# testing
+/coverage
+
+# next.js
+/.next/
+/out/
+
+# production
+/build
+
+# misc
+.DS_Store
+*.pem
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+.pnpm-debug.log*
+
+# env files (can opt-in for committing if needed)
+.env*
+
+# vercel
+.vercel
+
+# typescript
+*.tsbuildinfo
+next-env.d.ts
diff --git a/apps/demo-nextjs-antd/README.md b/apps/demo-nextjs-antd/README.md
new file mode 100644
index 00000000..e215bc4c
--- /dev/null
+++ b/apps/demo-nextjs-antd/README.md
@@ -0,0 +1,36 @@
+This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
+
+## Getting Started
+
+First, run the development server:
+
+```bash
+npm run dev
+# or
+yarn dev
+# or
+pnpm dev
+# or
+bun dev
+```
+
+Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
+
+You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
+
+This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
+
+## Learn More
+
+To learn more about Next.js, take a look at the following resources:
+
+- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
+- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
+
+You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
+
+## Deploy on Vercel
+
+The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
+
+Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
diff --git a/apps/demo-nextjs-antd/next.config.ts b/apps/demo-nextjs-antd/next.config.ts
new file mode 100644
index 00000000..fc85415f
--- /dev/null
+++ b/apps/demo-nextjs-antd/next.config.ts
@@ -0,0 +1,19 @@
+import path from 'path';
+
+import type { NextConfig } from 'next';
+
+const __dirname = new URL('.', import.meta.url).pathname;
+
+const nextConfig: NextConfig = {
+ reactStrictMode: false,
+ webpack: (config) => {
+ config.resolve.alias = {
+ ...config.resolve.alias,
+ '@app': path.resolve(__dirname, 'src/app'),
+ '@editor': path.resolve(__dirname, 'src/editor'),
+ };
+ return config;
+ },
+};
+
+export default nextConfig;
diff --git a/apps/demo-nextjs-antd/package.json b/apps/demo-nextjs-antd/package.json
new file mode 100644
index 00000000..b55d3c6c
--- /dev/null
+++ b/apps/demo-nextjs-antd/package.json
@@ -0,0 +1,70 @@
+{
+ "name": "@flowgram.ai/demo-nextjs-antd",
+ "version": "0.1.0",
+ "description": "",
+ "keywords": [],
+ "license": "MIT",
+ "files": [
+ "public/",
+ "src/",
+ ".eslintrc.js",
+ ".gitignore",
+ "next.config.ts",
+ "pnpm-lock.yaml",
+ "postcss.config.mjs",
+ "package.json",
+ "tsconfig.json",
+ "README.md"
+ ],
+ "scripts": {
+ "dev": "next dev",
+ "build": "next build",
+ "start": "next start",
+ "lint": "eslint ./src --cache",
+ "lint:fix": "eslint ./src --fix"
+ },
+ "dependencies": {
+ "antd": "^5.25.4",
+ "react": "^18",
+ "react-dom": "^18",
+ "next": "15.2.4",
+ "lodash-es": "^4.17.21",
+ "classnames": "^2.5.1",
+ "server-only": "^0.0.1",
+ "styled-components": "^5",
+ "nanoid": "^4.0.2",
+ "@ant-design/icons": "5.x",
+ "@flowgram.ai/free-layout-editor": "workspace:*",
+ "@flowgram.ai/free-snap-plugin": "workspace:*",
+ "@flowgram.ai/free-lines-plugin": "workspace:*",
+ "@flowgram.ai/free-node-panel-plugin": "workspace:*",
+ "@flowgram.ai/minimap-plugin": "workspace:*",
+ "@flowgram.ai/free-container-plugin": "workspace:*",
+ "@flowgram.ai/free-group-plugin": "workspace:*",
+ "@flowgram.ai/form-antd-materials": "workspace:*"
+ },
+ "devDependencies": {
+ "@flowgram.ai/ts-config": "workspace:*",
+ "@flowgram.ai/eslint-config": "workspace:*",
+ "@types/styled-components": "^5",
+ "typescript": "^5.0.4",
+ "@types/lodash-es": "^4.17.12",
+ "@types/node": "^18",
+ "@types/next": "^9.0.0",
+ "@types/react": "^18",
+ "@types/react-dom": "^18",
+ "@tailwindcss/postcss": "^4",
+ "tailwindcss": "^4",
+ "eslint": "^8.54.0",
+ "@babel/eslint-parser": "~7.19.1",
+ "eslint-plugin-json": "^4.0.1",
+ "eslint-plugin-next": "0.0.0",
+ "eslint-config-next": "^15.3.1",
+ "@eslint/eslintrc": "^3",
+ "sass": "^1.89.1"
+ },
+ "publishConfig": {
+ "access": "public",
+ "registry": "https://registry.npmjs.org/"
+ }
+}
diff --git a/apps/demo-nextjs-antd/pnpm-lock.yaml b/apps/demo-nextjs-antd/pnpm-lock.yaml
new file mode 100644
index 00000000..726fe38f
--- /dev/null
+++ b/apps/demo-nextjs-antd/pnpm-lock.yaml
@@ -0,0 +1,4849 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+importers:
+
+ .:
+ dependencies:
+ '@flowgram.ai/free-container-plugin':
+ specifier: 0.1.17
+ version: 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(styled-components@5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0))
+ '@flowgram.ai/free-layout-editor':
+ specifier: 0.1.17
+ version: 0.1.17(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)(styled-components@5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0))
+ '@flowgram.ai/free-lines-plugin':
+ specifier: 0.1.17
+ version: 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(styled-components@5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0))
+ '@flowgram.ai/free-node-panel-plugin':
+ specifier: 0.1.17
+ version: 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(styled-components@5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0))
+ '@flowgram.ai/free-snap-plugin':
+ specifier: 0.1.17
+ version: 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(styled-components@5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0))
+ '@flowgram.ai/minimap-plugin':
+ specifier: 0.1.17
+ version: 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(styled-components@5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0))
+ next:
+ specifier: 15.2.4
+ version: 15.2.4(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ react:
+ specifier: ^19.0.0
+ version: 19.1.0
+ react-dom:
+ specifier: ^19.0.0
+ version: 19.1.0(react@19.1.0)
+ devDependencies:
+ '@eslint/eslintrc':
+ specifier: ^3
+ version: 3.3.1
+ '@tailwindcss/postcss':
+ specifier: ^4
+ version: 4.1.0
+ '@types/node':
+ specifier: ^20
+ version: 20.17.30
+ '@types/react':
+ specifier: ^19
+ version: 19.1.0
+ '@types/react-dom':
+ specifier: ^19
+ version: 19.1.1(@types/react@19.1.0)
+ eslint:
+ specifier: ^9
+ version: 9.23.0(jiti@2.4.2)
+ eslint-config-next:
+ specifier: 15.2.4
+ version: 15.2.4(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)
+ tailwindcss:
+ specifier: ^4
+ version: 4.1.0
+ typescript:
+ specifier: ^5
+ version: 5.8.2
+
+packages:
+
+ '@alloc/quick-lru@5.2.0':
+ resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
+ engines: {node: '>=10'}
+
+ '@ampproject/remapping@2.3.0':
+ resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
+ engines: {node: '>=6.0.0'}
+
+ '@babel/code-frame@7.26.2':
+ resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/compat-data@7.26.8':
+ resolution: {integrity: sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/core@7.26.10':
+ resolution: {integrity: sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/generator@7.27.0':
+ resolution: {integrity: sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-annotate-as-pure@7.25.9':
+ resolution: {integrity: sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-compilation-targets@7.27.0':
+ resolution: {integrity: sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-imports@7.25.9':
+ resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-transforms@7.26.0':
+ resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-plugin-utils@7.26.5':
+ resolution: {integrity: sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-string-parser@7.25.9':
+ resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-identifier@7.25.9':
+ resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-option@7.25.9':
+ resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helpers@7.27.0':
+ resolution: {integrity: sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/parser@7.27.0':
+ resolution: {integrity: sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ '@babel/plugin-syntax-jsx@7.25.9':
+ resolution: {integrity: sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/template@7.27.0':
+ resolution: {integrity: sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/traverse@7.27.0':
+ resolution: {integrity: sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/types@7.27.0':
+ resolution: {integrity: sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==}
+ engines: {node: '>=6.9.0'}
+
+ '@dagrejs/graphlib@2.2.2':
+ resolution: {integrity: sha512-CbyGpCDKsiTg/wuk79S7Muoj8mghDGAESWGxcSyhHX5jD35vYMBZochYVFzlHxynpE9unpu6O+4ZuhrLxASsOg==}
+ engines: {node: '>17.0.0'}
+
+ '@emnapi/core@1.4.0':
+ resolution: {integrity: sha512-H+N/FqT07NmLmt6OFFtDfwe8PNygprzBikrEMyQfgqSmT0vzE515Pz7R8izwB9q/zsH/MA64AKoul3sA6/CzVg==}
+
+ '@emnapi/runtime@1.4.0':
+ resolution: {integrity: sha512-64WYIf4UYcdLnbKn/umDlNjQDSS8AgZrI/R9+x5ilkUVFxXcA1Ebl+gQLc/6mERA4407Xof0R7wEyEuj091CVw==}
+
+ '@emnapi/wasi-threads@1.0.1':
+ resolution: {integrity: sha512-iIBu7mwkq4UQGeMEM8bLwNK962nXdhodeScX4slfQnRhEMMzvYivHhutCIk8uojvmASXXPC2WNEjwxFWk72Oqw==}
+
+ '@emotion/is-prop-valid@1.3.1':
+ resolution: {integrity: sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==}
+
+ '@emotion/memoize@0.9.0':
+ resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==}
+
+ '@emotion/stylis@0.8.5':
+ resolution: {integrity: sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==}
+
+ '@emotion/unitless@0.7.5':
+ resolution: {integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==}
+
+ '@eslint-community/eslint-utils@4.5.1':
+ resolution: {integrity: sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ peerDependencies:
+ eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
+
+ '@eslint-community/regexpp@4.12.1':
+ resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==}
+ engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+
+ '@eslint/config-array@0.19.2':
+ resolution: {integrity: sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/config-helpers@0.2.1':
+ resolution: {integrity: sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/core@0.12.0':
+ resolution: {integrity: sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/core@0.13.0':
+ resolution: {integrity: sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/eslintrc@3.3.1':
+ resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/js@9.23.0':
+ resolution: {integrity: sha512-35MJ8vCPU0ZMxo7zfev2pypqTwWTofFZO6m4KAtdoFhRpLJUpHTZZ+KB3C7Hb1d7bULYwO4lJXGCi5Se+8OMbw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/object-schema@2.1.6':
+ resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/plugin-kit@0.2.8':
+ resolution: {integrity: sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@flowgram.ai/background-plugin@0.1.17':
+ resolution: {integrity: sha512-h+atlG+ZETPc9Ocfo5/Asq5uL/O7g8CP7a3yY/Fs0lvvunDl5ZPY/V/pA/mi7ErFlZEebY/d9pemKUCjP3GgNQ==}
+ peerDependencies:
+ react: '>=17'
+ react-dom: '>=17'
+
+ '@flowgram.ai/command@0.1.17':
+ resolution: {integrity: sha512-39q6lEJQsOYs4zcJUkya1MIDB0NJFM+FFhRyN8IHqNJ7cwjRs7zlheiT9HIIsCrQtI8p8xfz3gbmaKgs4VUQPg==}
+ peerDependencies:
+ react: '>=17'
+ react-dom: '>=17'
+
+ '@flowgram.ai/core@0.1.17':
+ resolution: {integrity: sha512-HnCEaDN+jZzuIbWa4VUXJ9qLeSWo8RgWNcuWlQstFNYKR2dGLvB+5uMZJQPr5cIwWtW6YNgY2wI0yt72UsZ6kQ==}
+ peerDependencies:
+ react: '>=17'
+ react-dom: '>=17'
+
+ '@flowgram.ai/document@0.1.17':
+ resolution: {integrity: sha512-poFpnmCJgIEajrbKSCpVDG1RpyPc7yCxLvgiPMkKKmrCx737FYE3/yMchCGcOVWyExurIxcxcdsZucDDy66qTg==}
+
+ '@flowgram.ai/editor@0.1.17':
+ resolution: {integrity: sha512-7urN2f7+FKmjZxQ/gU26yC18/HYKhgZPQwrql3cKaa4XqAne0W6jzgXpSq0h1+4O9/lLVF+2q981UOaCU3QBkw==}
+ peerDependencies:
+ react: '>=17'
+ react-dom: '>=17'
+
+ '@flowgram.ai/form-core@0.1.17':
+ resolution: {integrity: sha512-deCVo3Y2ltw8ebQ6ht8xQnQ+fECo1eGhLlluPlHDRR1/sHt7LQC2cBBCY3jJS8D+rgNAHMA7+S0/gFBRRy+BZw==}
+ peerDependencies:
+ react: '>=17'
+ react-dom: '>=17'
+
+ '@flowgram.ai/form@0.1.17':
+ resolution: {integrity: sha512-B5stRohfWcFdUq1gUPt5c4FZoWGrDpPOZoDEbuH8W/3Rk5k3T6UvUQyJ9Kt39PPe4kAu4g8JNetSq+9NW7MPCA==}
+ peerDependencies:
+ react: '>=17'
+ react-dom: '>=17'
+
+ '@flowgram.ai/free-auto-layout-plugin@0.1.17':
+ resolution: {integrity: sha512-DRqFwWVJMAzKALA1d5eowmvDTIIgr8kUYw0mH36hKHGlAqG4s4NA+Gr/cq+35zAQOjHAgTQ/u/ztHjtXaZPBIg==}
+ peerDependencies:
+ react: '>=17'
+ react-dom: '>=17'
+ styled-components: '>=4'
+
+ '@flowgram.ai/free-container-plugin@0.1.17':
+ resolution: {integrity: sha512-ftIP2xaC/Dub3BzWCR4h52gK+c6/HROwYjWrFo+0bcTZ9F0aKIiILVS4GDfyq716TK0A4AB15IGfsb2h+tpNzg==}
+ peerDependencies:
+ react: '>=17'
+ react-dom: '>=17'
+ styled-components: '>=4'
+
+ '@flowgram.ai/free-history-plugin@0.1.17':
+ resolution: {integrity: sha512-y6cEeRjuJ0rCGEJqLmqpZqMyAFmk/KjdOxNA6ZjwL2YlmCrQoutNY/P1oUrbbxrDfgdle6N3gmnqXmTFJUWfag==}
+ peerDependencies:
+ react: '>=17'
+ react-dom: '>=17'
+
+ '@flowgram.ai/free-hover-plugin@0.1.17':
+ resolution: {integrity: sha512-8DcrReZU+PbVpOZL3wZQ6x7RWDEOpUy+X0kchi0s2PYxJccMfrMML8l1mhhD0o5YZP1wqjKO66FQAUYYtgxxgg==}
+ peerDependencies:
+ react: '>=17'
+ react-dom: '>=17'
+
+ '@flowgram.ai/free-layout-core@0.1.17':
+ resolution: {integrity: sha512-xgTkAiTSBZId0y24QTNFYj53iv6zPd7JfLgkCOdccfUV5TqphPAh67Sb0Z+zwXiWUOWnK7DdUfo12n5aIBN+aQ==}
+ peerDependencies:
+ react: '>=17'
+ react-dom: '>=17'
+
+ '@flowgram.ai/free-layout-editor@0.1.17':
+ resolution: {integrity: sha512-kRe3aILfg1Yp4pCKegC0J/OBKbAycIWyd6719wxtQRSL3u1te1fVWbynY3oq1RMy7Tk05lWwGyNXJtx4zQqg7g==}
+ peerDependencies:
+ react: '>=17'
+ react-dom: '>=17'
+
+ '@flowgram.ai/free-lines-plugin@0.1.17':
+ resolution: {integrity: sha512-/IQscRPI7sxhy1FZTXKx3m8b9uTRED9GNJ6R0waCtaEnCf+E+f4ajHdRHNapd15tEKS4zzbEBR6ZcUKHlcbW1A==}
+ peerDependencies:
+ react: '>=17'
+ react-dom: '>=17'
+ styled-components: '>=4'
+
+ '@flowgram.ai/free-node-panel-plugin@0.1.17':
+ resolution: {integrity: sha512-s+6HjINJZO3TOmZ+mnqaN3BU+Wt3DzJUrALOXzas24JSynM1GmkHduYALwhMsWqlrfLz/3IondfnnNdFLMK5fQ==}
+ peerDependencies:
+ react: '>=17'
+ react-dom: '>=17'
+ styled-components: '>=4'
+
+ '@flowgram.ai/free-snap-plugin@0.1.17':
+ resolution: {integrity: sha512-V/s3Lr+WWyJQFth1RWJc628faGaXuoRBWAChl7SVTz7ANBD2HU1ZWqhDcyUgE+bdhfE05kosEAHJjnyHa9mdtQ==}
+ peerDependencies:
+ react: '>=17'
+ react-dom: '>=17'
+ styled-components: '>=4'
+
+ '@flowgram.ai/free-stack-plugin@0.1.17':
+ resolution: {integrity: sha512-zn+J2G2pbPT7/2V3bQyTEkhoUcE9BQw/zppqZJPhB8YKr58zbZ1mDgiCMBgZs97USK000ZZR64VD99YjYA0IzA==}
+ peerDependencies:
+ react: '>=17'
+ react-dom: '>=17'
+ styled-components: '>=4'
+
+ '@flowgram.ai/group-plugin@0.1.17':
+ resolution: {integrity: sha512-cLldQhMj5C4IqQSGjRMMxnjhuOvoccEBjD7Vm24iW0NYB/UHPYiirBnAcrsHopwGHGU56zmJh+ZDfOw8X3rvHQ==}
+ peerDependencies:
+ react: '>=17'
+ react-dom: '>=17'
+
+ '@flowgram.ai/history-node-plugin@0.1.17':
+ resolution: {integrity: sha512-nwVpaFdS+oOE3KB3nRSFs1ja0ye9owqhltB6kVYUqg/ewYiv9SahIuzw1pR2paysb/wnJa7jlZBWz5wH8aSgvA==}
+ peerDependencies:
+ react: '>=17'
+ react-dom: '>=17'
+ styled-components: '>=4'
+
+ '@flowgram.ai/history@0.1.17':
+ resolution: {integrity: sha512-+TQGhfKOzMgmHFIE7wMeFVKrUUFu0mJVtCJBIS2r4awh5ZdwX/ramw6AYKXeixtlp+y6mf+TFkp42OUR00x2/Q==}
+
+ '@flowgram.ai/i18n-plugin@0.1.17':
+ resolution: {integrity: sha512-zF7BvTjdOAaxQW8w9lQ8SzXYpK11vBkbKZh7SBPFcCLw0l9+KHN+xjzXwqjjrNtaMPGxKqitRKK0oeEpGSTVUQ==}
+
+ '@flowgram.ai/i18n@0.1.17':
+ resolution: {integrity: sha512-9oUI9wCb/hSZ2UFKoPwFgoCU+nkNiqkTKeB+GelDuY6X68RtxRly9nM4nSBKCK0KT0qQddMq3E23+qmq/uG/Uw==}
+
+ '@flowgram.ai/materials-plugin@0.1.17':
+ resolution: {integrity: sha512-4bLL20V5FRnV+RTwUbqh0cRGcWx5mvtm3++bSH9T1dW0kh3lb5FvshUVUBweJ2WRKKYDN43Y6uAbqtXiHqh1ng==}
+ peerDependencies:
+ react: '>=17'
+ react-dom: '>=17'
+ styled-components: '>=4'
+
+ '@flowgram.ai/minimap-plugin@0.1.17':
+ resolution: {integrity: sha512-qYYpxOpQDPtcuvr+qBv+Q35hBMGjGUZxoldVrtIrQAjHjOV4f/PU35dZQ0QjLZCOKTv2C+H3CRPG/7q5cQQcoA==}
+ peerDependencies:
+ react: '>=17'
+ react-dom: '>=17'
+ styled-components: '>=4'
+
+ '@flowgram.ai/node-core-plugin@0.1.17':
+ resolution: {integrity: sha512-Arnt+GuG3UcgSH1gXbiuTgHuiWzYYTW22T3MeS9RmMgnLgALSfcdY+0kvKD2YiuPIS1C1CTeVBkV9HjkPigzsA==}
+ peerDependencies:
+ react: '>=17'
+ react-dom: '>=17'
+ styled-components: '>=4'
+
+ '@flowgram.ai/node-variable-plugin@0.1.17':
+ resolution: {integrity: sha512-91XvQK2bLutajZnGeeh0ahbVpe2Y2Sdif02z3N/IejHHKNdjEx74VuOy+v3MXX0IZSu6c4q8YVcbW+L1ULOs4g==}
+ peerDependencies:
+ react: '>=17'
+ react-dom: '>=17'
+ styled-components: '>=4'
+
+ '@flowgram.ai/node@0.1.17':
+ resolution: {integrity: sha512-Ko2sT93peiMbwpBeDU1SO8JLci+cLyuERKD33kcmdBVeGCJI+qa+qIWLhzxHFeKUBQ+Hloqapic70HWeD55STw==}
+ peerDependencies:
+ react: '>=17'
+ react-dom: '>=17'
+
+ '@flowgram.ai/playground-react@0.1.17':
+ resolution: {integrity: sha512-Tf4PrAIEP65NAmPkdNE0KxmtlHwE+6S2YU0iB3W8JEGNR1P1B4+OfIbaJswIP8y5kkGhw2oGd05LUcU3ZHC14g==}
+ peerDependencies:
+ react: '>=17'
+ react-dom: '>=17'
+
+ '@flowgram.ai/reactive@0.1.17':
+ resolution: {integrity: sha512-tjr9f3y9QfV2pcVNAOCzqXT2yRXl6jp7ejwe3AWkseTpG7lnd5hcZQ+OlDnzlY/P7yQtxs9QGVkmHbpHnv9xBw==}
+ peerDependencies:
+ react: '>=17'
+ react-dom: '>=17'
+
+ '@flowgram.ai/redux-devtool-plugin@0.1.17':
+ resolution: {integrity: sha512-Yk7IZb7bE6kJTWN4inhBt6fH2TuwRQ3Lq3ae22+MAUyTZcPS1ULQlZecupsZxi5Su7iho5CZ8uG06gDCE7SSfw==}
+
+ '@flowgram.ai/renderer@0.1.17':
+ resolution: {integrity: sha512-kfQdFVBoay1Gir71JYkzCHxNyZ0e1vj3gFFjMl2TV1JSw/4oQdAahR8CzDpZ3ksBLVu42I6L9SAAiytGW75ZPw==}
+ peerDependencies:
+ react: '>=17'
+ react-dom: '>=17'
+
+ '@flowgram.ai/select-box-plugin@0.1.17':
+ resolution: {integrity: sha512-VtRKsFk8vpNpnynBoG9w/P2YwYWzez42cd23Aq3x6pcMafSIZ8gT6CT/Jkdsr4pnki+cawJOkHuHJn/aHpumJQ==}
+ peerDependencies:
+ react: '>=17'
+ react-dom: '>=17'
+
+ '@flowgram.ai/shortcuts-plugin@0.1.17':
+ resolution: {integrity: sha512-ysj/JKG76f/2YiXcVqEi72PcE9oqMGtLpa5NNJ8OaPHW2GSIX06CoprvxIMOACo3oXKIyD7J3WzrXo8jG5IyvQ==}
+
+ '@flowgram.ai/utils@0.1.17':
+ resolution: {integrity: sha512-639r+a3MYJ6GFBL3dVmQxFObphy5FWkoyG1hV3K9K+2Tlw9q/q4JJuNHp46/wgRzmUEPXIYXysZfbbJV7Knixg==}
+ peerDependencies:
+ react: '>=17'
+ react-dom: '>=17'
+
+ '@flowgram.ai/variable-core@0.1.17':
+ resolution: {integrity: sha512-AQwjZVKapV7oAlQ0/Y8q6wcCVHci3tW1dZ0/nVhVqtQeesjGdG9Q1v3vuRM5ofOCjlMbiKJDJ9f0qRXkcmxREw==}
+ peerDependencies:
+ react: '>=17'
+ react-dom: '>=17'
+
+ '@flowgram.ai/variable-layout@0.1.17':
+ resolution: {integrity: sha512-Zi+Zeaf8VC3hcU7UQV9goE6cX0T2wafidpagv23fL2BI0kSLuTh1tNJyAcR5PumI+pfTPgcrED8Qk0VDadUGlQ==}
+
+ '@flowgram.ai/variable-plugin@0.1.17':
+ resolution: {integrity: sha512-53nH41d1Zrg8vzh67e0BGOh/QWvhqD8o2077E/q6oFZYomaboweG09615w+KqUmuOoVWTxhcdU4c58n8DcKtLg==}
+
+ '@humanfs/core@0.19.1':
+ resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
+ engines: {node: '>=18.18.0'}
+
+ '@humanfs/node@0.16.6':
+ resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==}
+ engines: {node: '>=18.18.0'}
+
+ '@humanwhocodes/module-importer@1.0.1':
+ resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
+ engines: {node: '>=12.22'}
+
+ '@humanwhocodes/retry@0.3.1':
+ resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==}
+ engines: {node: '>=18.18'}
+
+ '@humanwhocodes/retry@0.4.2':
+ resolution: {integrity: sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==}
+ engines: {node: '>=18.18'}
+
+ '@img/sharp-darwin-arm64@0.33.5':
+ resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@img/sharp-darwin-x64@0.33.5':
+ resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [darwin]
+
+ '@img/sharp-libvips-darwin-arm64@1.0.4':
+ resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@img/sharp-libvips-darwin-x64@1.0.4':
+ resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@img/sharp-libvips-linux-arm64@1.0.4':
+ resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-libvips-linux-arm@1.0.5':
+ resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==}
+ cpu: [arm]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-libvips-linux-s390x@1.0.4':
+ resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==}
+ cpu: [s390x]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-libvips-linux-x64@1.0.4':
+ resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-libvips-linuxmusl-arm64@1.0.4':
+ resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@img/sharp-libvips-linuxmusl-x64@1.0.4':
+ resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@img/sharp-linux-arm64@0.33.5':
+ resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-linux-arm@0.33.5':
+ resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-linux-s390x@0.33.5':
+ resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [s390x]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-linux-x64@0.33.5':
+ resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-linuxmusl-arm64@0.33.5':
+ resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@img/sharp-linuxmusl-x64@0.33.5':
+ resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@img/sharp-wasm32@0.33.5':
+ resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [wasm32]
+
+ '@img/sharp-win32-ia32@0.33.5':
+ resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [ia32]
+ os: [win32]
+
+ '@img/sharp-win32-x64@0.33.5':
+ resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [win32]
+
+ '@inversifyjs/common@1.4.0':
+ resolution: {integrity: sha512-qfRJ/3iOlCL/VfJq8+4o5X4oA14cZSBbpAmHsYj8EsIit1xDndoOl0xKOyglKtQD4u4gdNVxMHx4RWARk/I4QA==}
+
+ '@inversifyjs/core@1.3.5':
+ resolution: {integrity: sha512-B4MFXabhNTAmrfgB+yeD6wd/GIvmvWC6IQ8Rh/j2C3Ix69kmqwz9pr8Jt3E+Nho9aEHOQCZaGmrALgtqRd+oEQ==}
+
+ '@inversifyjs/reflect-metadata-utils@0.2.4':
+ resolution: {integrity: sha512-u95rV3lKfG+NT2Uy/5vNzoDujos8vN8O18SSA5UyhxsGYd4GLQn/eUsGXfOsfa7m34eKrDelTKRUX1m/BcNX5w==}
+ peerDependencies:
+ reflect-metadata: 0.2.2
+
+ '@jridgewell/gen-mapping@0.3.8':
+ resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/resolve-uri@3.1.2':
+ resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/set-array@1.2.1':
+ resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/sourcemap-codec@1.5.0':
+ resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
+
+ '@jridgewell/trace-mapping@0.3.25':
+ resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
+
+ '@napi-rs/wasm-runtime@0.2.8':
+ resolution: {integrity: sha512-OBlgKdX7gin7OIq4fadsjpg+cp2ZphvAIKucHsNfTdJiqdOmOEwQd/bHi0VwNrcw5xpBJyUw6cK/QilCqy1BSg==}
+
+ '@next/env@15.2.4':
+ resolution: {integrity: sha512-+SFtMgoiYP3WoSswuNmxJOCwi06TdWE733D+WPjpXIe4LXGULwEaofiiAy6kbS0+XjM5xF5n3lKuBwN2SnqD9g==}
+
+ '@next/eslint-plugin-next@15.2.4':
+ resolution: {integrity: sha512-O8ScvKtnxkp8kL9TpJTTKnMqlkZnS+QxwoQnJwPGBxjBbzd6OVVPEJ5/pMNrktSyXQD/chEfzfFzYLM6JANOOQ==}
+
+ '@next/swc-darwin-arm64@15.2.4':
+ resolution: {integrity: sha512-1AnMfs655ipJEDC/FHkSr0r3lXBgpqKo4K1kiwfUf3iE68rDFXZ1TtHdMvf7D0hMItgDZ7Vuq3JgNMbt/+3bYw==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@next/swc-darwin-x64@15.2.4':
+ resolution: {integrity: sha512-3qK2zb5EwCwxnO2HeO+TRqCubeI/NgCe+kL5dTJlPldV/uwCnUgC7VbEzgmxbfrkbjehL4H9BPztWOEtsoMwew==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@next/swc-linux-arm64-gnu@15.2.4':
+ resolution: {integrity: sha512-HFN6GKUcrTWvem8AZN7tT95zPb0GUGv9v0d0iyuTb303vbXkkbHDp/DxufB04jNVD+IN9yHy7y/6Mqq0h0YVaQ==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@next/swc-linux-arm64-musl@15.2.4':
+ resolution: {integrity: sha512-Oioa0SORWLwi35/kVB8aCk5Uq+5/ZIumMK1kJV+jSdazFm2NzPDztsefzdmzzpx5oGCJ6FkUC7vkaUseNTStNA==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@next/swc-linux-x64-gnu@15.2.4':
+ resolution: {integrity: sha512-yb5WTRaHdkgOqFOZiu6rHV1fAEK0flVpaIN2HB6kxHVSy/dIajWbThS7qON3W9/SNOH2JWkVCyulgGYekMePuw==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@next/swc-linux-x64-musl@15.2.4':
+ resolution: {integrity: sha512-Dcdv/ix6srhkM25fgXiyOieFUkz+fOYkHlydWCtB0xMST6X9XYI3yPDKBZt1xuhOytONsIFJFB08xXYsxUwJLw==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@next/swc-win32-arm64-msvc@15.2.4':
+ resolution: {integrity: sha512-dW0i7eukvDxtIhCYkMrZNQfNicPDExt2jPb9AZPpL7cfyUo7QSNl1DjsHjmmKp6qNAqUESyT8YFl/Aw91cNJJg==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@next/swc-win32-x64-msvc@15.2.4':
+ resolution: {integrity: sha512-SbnWkJmkS7Xl3kre8SdMF6F/XDh1DTFEhp0jRTj/uB8iPKoU2bb2NDfcu+iifv1+mxQEd1g2vvSxcZbXSKyWiQ==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [win32]
+
+ '@nodelib/fs.scandir@2.1.5':
+ resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
+ engines: {node: '>= 8'}
+
+ '@nodelib/fs.stat@2.0.5':
+ resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
+ engines: {node: '>= 8'}
+
+ '@nodelib/fs.walk@1.2.8':
+ resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
+ engines: {node: '>= 8'}
+
+ '@nolyfill/is-core-module@1.0.39':
+ resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==}
+ engines: {node: '>=12.4.0'}
+
+ '@phosphor/algorithm@1.2.0':
+ resolution: {integrity: sha512-C9+dnjXyU2QAkWCW6QVDGExk4hhwxzAKf5/FIuYlHAI9X5vFv99PYm0EREDxX1PbMuvfFBZhPNu0PvuSDQ7sFA==}
+
+ '@phosphor/collections@1.2.0':
+ resolution: {integrity: sha512-T9/0EjSuY6+ga2LIFRZ0xupciOR3Qnyy8Q95lhGTC0FXZUFwC8fl9e8On6IcwasCszS+1n8dtZUWSIynfgdpzw==}
+
+ '@phosphor/messaging@1.3.0':
+ resolution: {integrity: sha512-k0JE+BTMKlkM335S2AmmJxoYYNRwOdW5jKBqLgjJdGRvUQkM0+2i60ahM45+J23atGJDv9esKUUBINiKHFhLew==}
+
+ '@rtsao/scc@1.1.0':
+ resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==}
+
+ '@rushstack/eslint-patch@1.11.0':
+ resolution: {integrity: sha512-zxnHvoMQVqewTJr/W4pKjF0bMGiKJv1WX7bSrkl46Hg0QjESbzBROWK0Wg4RphzSOS5Jiy7eFimmM3UgMrMZbQ==}
+
+ '@swc/counter@0.1.3':
+ resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}
+
+ '@swc/helpers@0.5.15':
+ resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
+
+ '@tailwindcss/node@4.1.0':
+ resolution: {integrity: sha512-mfgxGxFaxbsUbaGwKIAQXUSm7Qoojw53FftpoKwo4ANwr9wnDaByz4vi1gMti/xfJvmQ5lzA1DvPiX5yCHtBkQ==}
+
+ '@tailwindcss/oxide-android-arm64@4.1.0':
+ resolution: {integrity: sha512-UredFljuHey2Kh5qyYfQVBr0Xfq70ZE5Df6i5IubNYQGs2JXXT4VL0SIUjwzHx5W9T6t7dT7banunlV6lthGPQ==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [android]
+
+ '@tailwindcss/oxide-darwin-arm64@4.1.0':
+ resolution: {integrity: sha512-QHQ/46lRVwH9zEBNiRk8AJ3Af4pMq6DuZAI//q323qrPOXjsRdrhLsH9LUO3mqBfHr5EZNUxN3Am5vpO89sntw==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@tailwindcss/oxide-darwin-x64@4.1.0':
+ resolution: {integrity: sha512-lEMgYHCvQQ6x2KOZ4FwnPprwfnc+UnjzwXRqEYIhB/NlYvXQD1QMf7oKEDRqy94DiZaYox9ZRfG2YJOBgM0UkA==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@tailwindcss/oxide-freebsd-x64@4.1.0':
+ resolution: {integrity: sha512-9fdImTc+2lA5yHqJ61oeTXfCtzylNOzJVFhyWwVQAJESJJbVCPnj6f+b+Zf/AYAdKQfS6FCThbPEahkQrDCgLQ==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.0':
+ resolution: {integrity: sha512-HB0bTkUOuTLLSdadyRhKE9yps4/ZBjrojbHTPMSvvf/8yBLZRPpWb+A6IgW5R+2A2AL4KhVPgLwWfoXsErxJFg==}
+ engines: {node: '>= 10'}
+ cpu: [arm]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-arm64-gnu@4.1.0':
+ resolution: {integrity: sha512-+QtYCwvKLjC46h6RikKkpELJWrpiMMtgyK0aaqhwPLEx1icGgIhwz8dqrkAiqbFRE0KiRrE2aenhYoEkplyRmA==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@tailwindcss/oxide-linux-arm64-musl@4.1.0':
+ resolution: {integrity: sha512-nApadFKM9GauzuPZPlt6TKfELavMHqJ0gVd+GYkYBTwr2t9KhgCAb2sKiFDDIhs1a7gOjsU7P1lEauv3iKFp+Q==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@tailwindcss/oxide-linux-x64-gnu@4.1.0':
+ resolution: {integrity: sha512-cp0Rf9Wit2kZHhrV8HIoDFD8dxU2+ZTCFCFbDj3a07pGyyPwLCJm5H5VipKXgYrBaLmlYu73ERidW0S5sdEXEg==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@tailwindcss/oxide-linux-x64-musl@4.1.0':
+ resolution: {integrity: sha512-4/wf42XWBJGXsOS6BhgPhdQbg/qyfdZ1nZvTL9sJoxYN+Ah+cfY5Dd7R0smzI8hmgCRt3TD1lYb72ChTyIA59w==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@tailwindcss/oxide-win32-arm64-msvc@4.1.0':
+ resolution: {integrity: sha512-caXJJ0G6NwGbcoxEYdH3MZYN84C3PldaMdAEPMU6bjJXURQlKdSlQ/Ecis7/nSgBkMkicZyhqWmb36Tw/BFSIw==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.1.0':
+ resolution: {integrity: sha512-ZHXRXRxB7HBmkUE8U13nmkGGYfR1I2vsuhiYjeDDUFIYpk1BL6caU8hvzkSlL/X5CAQNdIUUJRGom5I0ZyfJOA==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [win32]
+
+ '@tailwindcss/oxide@4.1.0':
+ resolution: {integrity: sha512-A33oyZKpPFH08d7xkl13Dc8OTsbPhsuls0z9gUCxIHvn8c1BsUACddQxL6HwaeJR1fSYyXZUw8bdWcD8bVawpQ==}
+ engines: {node: '>= 10'}
+
+ '@tailwindcss/postcss@4.1.0':
+ resolution: {integrity: sha512-b2NWFAFfLXY7960jLY5QkKbuYKrQUULx60XU3BCzyaUQpU/7lLf3n2CiHibZPdBq5CIXrUp10wdxhV0EI0Js2g==}
+
+ '@tweenjs/tween.js@18.6.4':
+ resolution: {integrity: sha512-lB9lMjuqjtuJrx7/kOkqQBtllspPIN+96OvTCeJ2j5FEzinoAXTdAMFnDAQT1KVPRlnYfBrqxtqP66vDM40xxQ==}
+
+ '@tybys/wasm-util@0.9.0':
+ resolution: {integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==}
+
+ '@types/estree@1.0.7':
+ resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==}
+
+ '@types/json-schema@7.0.15':
+ resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
+
+ '@types/json5@0.0.29':
+ resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
+
+ '@types/node@20.17.30':
+ resolution: {integrity: sha512-7zf4YyHA+jvBNfVrk2Gtvs6x7E8V+YDW05bNfG2XkWDJfYRXrTiP/DsB2zSYTaHX0bGIujTBQdMVAhb+j7mwpg==}
+
+ '@types/react-dom@19.1.1':
+ resolution: {integrity: sha512-jFf/woGTVTjUJsl2O7hcopJ1r0upqoq/vIOoCj0yLh3RIXxWcljlpuZ+vEBRXsymD1jhfeJrlyTy/S1UW+4y1w==}
+ peerDependencies:
+ '@types/react': ^19.0.0
+
+ '@types/react@19.1.0':
+ resolution: {integrity: sha512-UaicktuQI+9UKyA4njtDOGBD/67t8YEBt2xdfqu8+gP9hqPUPsiXlNPcpS2gVdjmis5GKPG3fCxbQLVgxsQZ8w==}
+
+ '@typescript-eslint/eslint-plugin@8.29.0':
+ resolution: {integrity: sha512-PAIpk/U7NIS6H7TEtN45SPGLQaHNgB7wSjsQV/8+KYokAb2T/gloOA/Bee2yd4/yKVhPKe5LlaUGhAZk5zmSaQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <5.9.0'
+
+ '@typescript-eslint/parser@8.29.0':
+ resolution: {integrity: sha512-8C0+jlNJOwQso2GapCVWWfW/rzaq7Lbme+vGUFKE31djwNncIpgXD7Cd4weEsDdkoZDjH0lwwr3QDQFuyrMg9g==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <5.9.0'
+
+ '@typescript-eslint/scope-manager@8.29.0':
+ resolution: {integrity: sha512-aO1PVsq7Gm+tcghabUpzEnVSFMCU4/nYIgC2GOatJcllvWfnhrgW0ZEbnTxm36QsikmCN1K/6ZgM7fok2I7xNw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/type-utils@8.29.0':
+ resolution: {integrity: sha512-ahaWQ42JAOx+NKEf5++WC/ua17q5l+j1GFrbbpVKzFL/tKVc0aYY8rVSYUpUvt2hUP1YBr7mwXzx+E/DfUWI9Q==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <5.9.0'
+
+ '@typescript-eslint/types@8.29.0':
+ resolution: {integrity: sha512-wcJL/+cOXV+RE3gjCyl/V2G877+2faqvlgtso/ZRbTCnZazh0gXhe+7gbAnfubzN2bNsBtZjDvlh7ero8uIbzg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/typescript-estree@8.29.0':
+ resolution: {integrity: sha512-yOfen3jE9ISZR/hHpU/bmNvTtBW1NjRbkSFdZOksL1N+ybPEE7UVGMwqvS6CP022Rp00Sb0tdiIkhSCe6NI8ow==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <5.9.0'
+
+ '@typescript-eslint/utils@8.29.0':
+ resolution: {integrity: sha512-gX/A0Mz9Bskm8avSWFcK0gP7cZpbY4AIo6B0hWYFCaIsz750oaiWR4Jr2CI+PQhfW1CpcQr9OlfPS+kMFegjXA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <5.9.0'
+
+ '@typescript-eslint/visitor-keys@8.29.0':
+ resolution: {integrity: sha512-Sne/pVz8ryR03NFK21VpN88dZ2FdQXOlq3VIklbrTYEt8yXtRFr9tvUhqvCeKjqYk5FSim37sHbooT6vzBTZcg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@unrs/resolver-binding-darwin-arm64@1.3.3':
+ resolution: {integrity: sha512-EpRILdWr3/xDa/7MoyfO7JuBIJqpBMphtu4+80BK1bRfFcniVT74h3Z7q1+WOc92FuIAYatB1vn9TJR67sORGw==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@unrs/resolver-binding-darwin-x64@1.3.3':
+ resolution: {integrity: sha512-ntj/g7lPyqwinMJWZ+DKHBse8HhVxswGTmNgFKJtdgGub3M3zp5BSZ3bvMP+kBT6dnYJLSVlDqdwOq1P8i0+/g==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@unrs/resolver-binding-freebsd-x64@1.3.3':
+ resolution: {integrity: sha512-l6BT8f2CU821EW7U8hSUK8XPq4bmyTlt9Mn4ERrfjJNoCw0/JoHAh9amZZtV3cwC3bwwIat+GUnrcHTG9+qixw==}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@unrs/resolver-binding-linux-arm-gnueabihf@1.3.3':
+ resolution: {integrity: sha512-8ScEc5a4y7oE2BonRvzJ+2GSkBaYWyh0/Ko4Q25e/ix6ANpJNhwEPZvCR6GVRmsQAYMIfQvYLdM6YEN+qRjnAQ==}
+ cpu: [arm]
+ os: [linux]
+
+ '@unrs/resolver-binding-linux-arm-musleabihf@1.3.3':
+ resolution: {integrity: sha512-8qQ6l1VTzLNd3xb2IEXISOKwMGXDCzY/UNy/7SovFW2Sp0K3YbL7Ao7R18v6SQkLqQlhhqSBIFRk+u6+qu5R5A==}
+ cpu: [arm]
+ os: [linux]
+
+ '@unrs/resolver-binding-linux-arm64-gnu@1.3.3':
+ resolution: {integrity: sha512-v81R2wjqcWXJlQY23byqYHt9221h4anQ6wwN64oMD/WAE+FmxPHFZee5bhRkNVtzqO/q7wki33VFWlhiADwUeQ==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@unrs/resolver-binding-linux-arm64-musl@1.3.3':
+ resolution: {integrity: sha512-cAOx/j0u5coMg4oct/BwMzvWJdVciVauUvsd+GQB/1FZYKQZmqPy0EjJzJGbVzFc6gbnfEcSqvQE6gvbGf2N8Q==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@unrs/resolver-binding-linux-ppc64-gnu@1.3.3':
+ resolution: {integrity: sha512-mq2blqwErgDJD4gtFDlTX/HZ7lNP8YCHYFij2gkXPtMzrXxPW1hOtxL6xg4NWxvnj4bppppb0W3s/buvM55yfg==}
+ cpu: [ppc64]
+ os: [linux]
+ libc: [glibc]
+
+ '@unrs/resolver-binding-linux-s390x-gnu@1.3.3':
+ resolution: {integrity: sha512-u0VRzfFYysarYHnztj2k2xr+eu9rmgoTUUgCCIT37Nr+j0A05Xk2c3RY8Mh5+DhCl2aYibihnaAEJHeR0UOFIQ==}
+ cpu: [s390x]
+ os: [linux]
+ libc: [glibc]
+
+ '@unrs/resolver-binding-linux-x64-gnu@1.3.3':
+ resolution: {integrity: sha512-OrVo5ZsG29kBF0Ug95a2KidS16PqAMmQNozM6InbquOfW/udouk063e25JVLqIBhHLB2WyBnixOQ19tmeC/hIg==}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@unrs/resolver-binding-linux-x64-musl@1.3.3':
+ resolution: {integrity: sha512-PYnmrwZ4HMp9SkrOhqPghY/aoL+Rtd4CQbr93GlrRTjK6kDzfMfgz3UH3jt6elrQAfupa1qyr1uXzeVmoEAxUA==}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@unrs/resolver-binding-wasm32-wasi@1.3.3':
+ resolution: {integrity: sha512-81AnQY6fShmktQw4hWDUIilsKSdvr/acdJ5azAreu2IWNlaJOKphJSsUVWE+yCk6kBMoQyG9ZHCb/krb5K0PEA==}
+ engines: {node: '>=14.0.0'}
+ cpu: [wasm32]
+
+ '@unrs/resolver-binding-win32-arm64-msvc@1.3.3':
+ resolution: {integrity: sha512-X/42BMNw7cW6xrB9syuP5RusRnWGoq+IqvJO8IDpp/BZg64J1uuIW6qA/1Cl13Y4LyLXbJVYbYNSKwR/FiHEng==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@unrs/resolver-binding-win32-ia32-msvc@1.3.3':
+ resolution: {integrity: sha512-EGNnNGQxMU5aTN7js3ETYvuw882zcO+dsVjs+DwO2j/fRVKth87C8e2GzxW1L3+iWAXMyJhvFBKRavk9Og1Z6A==}
+ cpu: [ia32]
+ os: [win32]
+
+ '@unrs/resolver-binding-win32-x64-msvc@1.3.3':
+ resolution: {integrity: sha512-GraLbYqOJcmW1qY3osB+2YIiD62nVf2/bVLHZmrb4t/YSUwE03l7TwcDJl08T/Tm3SVhepX8RQkpzWbag/Sb4w==}
+ cpu: [x64]
+ os: [win32]
+
+ acorn-jsx@5.3.2:
+ resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
+ peerDependencies:
+ acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
+
+ acorn@8.14.1:
+ resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==}
+ engines: {node: '>=0.4.0'}
+ hasBin: true
+
+ ajv@6.12.6:
+ resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
+
+ ansi-styles@4.3.0:
+ resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+ engines: {node: '>=8'}
+
+ argparse@2.0.1:
+ resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+
+ aria-query@5.3.2:
+ resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==}
+ engines: {node: '>= 0.4'}
+
+ array-buffer-byte-length@1.0.2:
+ resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==}
+ engines: {node: '>= 0.4'}
+
+ array-includes@3.1.8:
+ resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==}
+ engines: {node: '>= 0.4'}
+
+ array.prototype.findlast@1.2.5:
+ resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==}
+ engines: {node: '>= 0.4'}
+
+ array.prototype.findlastindex@1.2.6:
+ resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==}
+ engines: {node: '>= 0.4'}
+
+ array.prototype.flat@1.3.3:
+ resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==}
+ engines: {node: '>= 0.4'}
+
+ array.prototype.flatmap@1.3.3:
+ resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==}
+ engines: {node: '>= 0.4'}
+
+ array.prototype.tosorted@1.1.4:
+ resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==}
+ engines: {node: '>= 0.4'}
+
+ arraybuffer.prototype.slice@1.0.4:
+ resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==}
+ engines: {node: '>= 0.4'}
+
+ ast-types-flow@0.0.8:
+ resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==}
+
+ async-function@1.0.0:
+ resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==}
+ engines: {node: '>= 0.4'}
+
+ available-typed-arrays@1.0.7:
+ resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
+ engines: {node: '>= 0.4'}
+
+ axe-core@4.10.3:
+ resolution: {integrity: sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==}
+ engines: {node: '>=4'}
+
+ axobject-query@4.1.0:
+ resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==}
+ engines: {node: '>= 0.4'}
+
+ babel-plugin-styled-components@2.1.4:
+ resolution: {integrity: sha512-Xgp9g+A/cG47sUyRwwYxGM4bR/jDRg5N6it/8+HxCnbT5XNKSKDT9xm4oag/osgqjC2It/vH0yXsomOG6k558g==}
+ peerDependencies:
+ styled-components: '>= 2'
+
+ balanced-match@1.0.2:
+ resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+
+ bezier-js@6.1.4:
+ resolution: {integrity: sha512-PA0FW9ZpcHbojUCMu28z9Vg/fNkwTj5YhusSAjHHDfHDGLxJ6YUKrAN2vk1fP2MMOxVw4Oko16FMlRGVBGqLKg==}
+
+ brace-expansion@1.1.11:
+ resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
+
+ brace-expansion@2.0.1:
+ resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
+
+ braces@3.0.3:
+ resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
+ engines: {node: '>=8'}
+
+ browserslist@4.24.4:
+ resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==}
+ engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+ hasBin: true
+
+ busboy@1.6.0:
+ resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==}
+ engines: {node: '>=10.16.0'}
+
+ call-bind-apply-helpers@1.0.2:
+ resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
+ engines: {node: '>= 0.4'}
+
+ call-bind@1.0.8:
+ resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==}
+ engines: {node: '>= 0.4'}
+
+ call-bound@1.0.4:
+ resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
+ engines: {node: '>= 0.4'}
+
+ callsites@3.1.0:
+ resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
+ engines: {node: '>=6'}
+
+ camelize@1.0.1:
+ resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==}
+
+ caniuse-lite@1.0.30001707:
+ resolution: {integrity: sha512-3qtRjw/HQSMlDWf+X79N206fepf4SOOU6SQLMaq/0KkZLmSjPxAkBOQQ+FxbHKfHmYLZFfdWsO3KA90ceHPSnw==}
+
+ chalk@4.1.2:
+ resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
+ engines: {node: '>=10'}
+
+ client-only@0.0.1:
+ resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
+
+ clsx@1.2.1:
+ resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==}
+ engines: {node: '>=6'}
+
+ color-convert@2.0.1:
+ resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+ engines: {node: '>=7.0.0'}
+
+ color-name@1.1.4:
+ resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+
+ color-string@1.9.1:
+ resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==}
+
+ color@4.2.3:
+ resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==}
+ engines: {node: '>=12.5.0'}
+
+ concat-map@0.0.1:
+ resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+
+ convert-source-map@2.0.0:
+ resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+
+ cross-spawn@7.0.6:
+ resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
+ engines: {node: '>= 8'}
+
+ css-color-keywords@1.0.0:
+ resolution: {integrity: sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==}
+ engines: {node: '>=4'}
+
+ css-to-react-native@3.2.0:
+ resolution: {integrity: sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==}
+
+ csstype@3.1.3:
+ resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
+
+ damerau-levenshtein@1.0.8:
+ resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==}
+
+ data-view-buffer@1.0.2:
+ resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==}
+ engines: {node: '>= 0.4'}
+
+ data-view-byte-length@1.0.2:
+ resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==}
+ engines: {node: '>= 0.4'}
+
+ data-view-byte-offset@1.0.1:
+ resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==}
+ engines: {node: '>= 0.4'}
+
+ debug@3.2.7:
+ resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ debug@4.4.0:
+ resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ deep-is@0.1.4:
+ resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
+
+ define-data-property@1.1.4:
+ resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
+ engines: {node: '>= 0.4'}
+
+ define-properties@1.2.1:
+ resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
+ engines: {node: '>= 0.4'}
+
+ detect-libc@2.0.3:
+ resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==}
+ engines: {node: '>=8'}
+
+ doctrine@2.1.0:
+ resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
+ engines: {node: '>=0.10.0'}
+
+ dunder-proto@1.0.1:
+ resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
+ engines: {node: '>= 0.4'}
+
+ electron-to-chromium@1.5.129:
+ resolution: {integrity: sha512-JlXUemX4s0+9f8mLqib/bHH8gOHf5elKS6KeWG3sk3xozb/JTq/RLXIv8OKUWiK4Ah00Wm88EFj5PYkFr4RUPA==}
+
+ emoji-regex@9.2.2:
+ resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
+
+ enhanced-resolve@5.18.1:
+ resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==}
+ engines: {node: '>=10.13.0'}
+
+ es-abstract@1.23.9:
+ resolution: {integrity: sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==}
+ engines: {node: '>= 0.4'}
+
+ es-define-property@1.0.1:
+ resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
+ engines: {node: '>= 0.4'}
+
+ es-errors@1.3.0:
+ resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
+ engines: {node: '>= 0.4'}
+
+ es-iterator-helpers@1.2.1:
+ resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==}
+ engines: {node: '>= 0.4'}
+
+ es-object-atoms@1.1.1:
+ resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
+ engines: {node: '>= 0.4'}
+
+ es-set-tostringtag@2.1.0:
+ resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
+ engines: {node: '>= 0.4'}
+
+ es-shim-unscopables@1.1.0:
+ resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==}
+ engines: {node: '>= 0.4'}
+
+ es-to-primitive@1.3.0:
+ resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==}
+ engines: {node: '>= 0.4'}
+
+ escalade@3.2.0:
+ resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
+ engines: {node: '>=6'}
+
+ escape-string-regexp@4.0.0:
+ resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
+ engines: {node: '>=10'}
+
+ eslint-config-next@15.2.4:
+ resolution: {integrity: sha512-v4gYjd4eYIme8qzaJItpR5MMBXJ0/YV07u7eb50kEnlEmX7yhOjdUdzz70v4fiINYRjLf8X8TbogF0k7wlz6sA==}
+ peerDependencies:
+ eslint: ^7.23.0 || ^8.0.0 || ^9.0.0
+ typescript: '>=3.3.1'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ eslint-import-resolver-node@0.3.9:
+ resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==}
+
+ eslint-import-resolver-typescript@3.10.0:
+ resolution: {integrity: sha512-aV3/dVsT0/H9BtpNwbaqvl+0xGMRGzncLyhm793NFGvbwGGvzyAykqWZ8oZlZuGwuHkwJjhWJkG1cM3ynvd2pQ==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+ peerDependencies:
+ eslint: '*'
+ eslint-plugin-import: '*'
+ eslint-plugin-import-x: '*'
+ peerDependenciesMeta:
+ eslint-plugin-import:
+ optional: true
+ eslint-plugin-import-x:
+ optional: true
+
+ eslint-module-utils@2.12.0:
+ resolution: {integrity: sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==}
+ engines: {node: '>=4'}
+ peerDependencies:
+ '@typescript-eslint/parser': '*'
+ eslint: '*'
+ eslint-import-resolver-node: '*'
+ eslint-import-resolver-typescript: '*'
+ eslint-import-resolver-webpack: '*'
+ peerDependenciesMeta:
+ '@typescript-eslint/parser':
+ optional: true
+ eslint:
+ optional: true
+ eslint-import-resolver-node:
+ optional: true
+ eslint-import-resolver-typescript:
+ optional: true
+ eslint-import-resolver-webpack:
+ optional: true
+
+ eslint-plugin-import@2.31.0:
+ resolution: {integrity: sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==}
+ engines: {node: '>=4'}
+ peerDependencies:
+ '@typescript-eslint/parser': '*'
+ eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9
+ peerDependenciesMeta:
+ '@typescript-eslint/parser':
+ optional: true
+
+ eslint-plugin-jsx-a11y@6.10.2:
+ resolution: {integrity: sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==}
+ engines: {node: '>=4.0'}
+ peerDependencies:
+ eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9
+
+ eslint-plugin-react-hooks@5.2.0:
+ resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0
+
+ eslint-plugin-react@7.37.4:
+ resolution: {integrity: sha512-BGP0jRmfYyvOyvMoRX/uoUeW+GqNj9y16bPQzqAHf3AYII/tDs+jMN0dBVkl88/OZwNGwrVFxE7riHsXVfy/LQ==}
+ engines: {node: '>=4'}
+ peerDependencies:
+ eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7
+
+ eslint-scope@8.3.0:
+ resolution: {integrity: sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ eslint-visitor-keys@3.4.3:
+ resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+ eslint-visitor-keys@4.2.0:
+ resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ eslint@9.23.0:
+ resolution: {integrity: sha512-jV7AbNoFPAY1EkFYpLq5bslU9NLNO8xnEeQXwErNibVryjk67wHVmddTBilc5srIttJDBrB0eMHKZBFbSIABCw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ hasBin: true
+ peerDependencies:
+ jiti: '*'
+ peerDependenciesMeta:
+ jiti:
+ optional: true
+
+ espree@10.3.0:
+ resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ esquery@1.6.0:
+ resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
+ engines: {node: '>=0.10'}
+
+ esrecurse@4.3.0:
+ resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
+ engines: {node: '>=4.0'}
+
+ estraverse@5.3.0:
+ resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
+ engines: {node: '>=4.0'}
+
+ esutils@2.0.3:
+ resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
+ engines: {node: '>=0.10.0'}
+
+ fast-deep-equal@3.1.3:
+ resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+
+ fast-equals@2.0.4:
+ resolution: {integrity: sha512-caj/ZmjHljPrZtbzJ3kfH5ia/k4mTJe/qSiXAGzxZWRZgsgDV0cvNaQULqUX8t0/JVlzzEdYOwCN5DmzTxoD4w==}
+
+ fast-glob@3.3.1:
+ resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==}
+ engines: {node: '>=8.6.0'}
+
+ fast-glob@3.3.3:
+ resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
+ engines: {node: '>=8.6.0'}
+
+ fast-json-stable-stringify@2.1.0:
+ resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+
+ fast-levenshtein@2.0.6:
+ resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
+
+ fastq@1.19.1:
+ resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
+
+ fdir@6.4.3:
+ resolution: {integrity: sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==}
+ 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'}
+
+ fill-range@7.1.1:
+ resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
+ engines: {node: '>=8'}
+
+ find-up@5.0.0:
+ resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
+ engines: {node: '>=10'}
+
+ flat-cache@4.0.1:
+ resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
+ engines: {node: '>=16'}
+
+ flatted@3.3.3:
+ resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==}
+
+ for-each@0.3.5:
+ resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==}
+ engines: {node: '>= 0.4'}
+
+ function-bind@1.1.2:
+ resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
+
+ function.prototype.name@1.1.8:
+ resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==}
+ engines: {node: '>= 0.4'}
+
+ functions-have-names@1.2.3:
+ resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
+
+ gensync@1.0.0-beta.2:
+ resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
+ engines: {node: '>=6.9.0'}
+
+ get-intrinsic@1.3.0:
+ resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
+ engines: {node: '>= 0.4'}
+
+ get-proto@1.0.1:
+ resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
+ engines: {node: '>= 0.4'}
+
+ get-symbol-description@1.1.0:
+ resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==}
+ engines: {node: '>= 0.4'}
+
+ get-tsconfig@4.10.0:
+ resolution: {integrity: sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==}
+
+ glob-parent@5.1.2:
+ resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
+ engines: {node: '>= 6'}
+
+ glob-parent@6.0.2:
+ resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
+ engines: {node: '>=10.13.0'}
+
+ globals@11.12.0:
+ resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
+ engines: {node: '>=4'}
+
+ globals@14.0.0:
+ resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
+ engines: {node: '>=18'}
+
+ globalthis@1.0.4:
+ resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==}
+ engines: {node: '>= 0.4'}
+
+ gopd@1.2.0:
+ resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
+ engines: {node: '>= 0.4'}
+
+ graceful-fs@4.2.11:
+ resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+
+ graphemer@1.4.0:
+ resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
+
+ has-bigints@1.1.0:
+ resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==}
+ engines: {node: '>= 0.4'}
+
+ has-flag@3.0.0:
+ resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
+ engines: {node: '>=4'}
+
+ has-flag@4.0.0:
+ resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
+ engines: {node: '>=8'}
+
+ has-property-descriptors@1.0.2:
+ resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
+
+ has-proto@1.2.0:
+ resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==}
+ engines: {node: '>= 0.4'}
+
+ has-symbols@1.1.0:
+ resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
+ engines: {node: '>= 0.4'}
+
+ has-tostringtag@1.0.2:
+ resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
+ engines: {node: '>= 0.4'}
+
+ hasown@2.0.2:
+ resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
+ engines: {node: '>= 0.4'}
+
+ hoist-non-react-statics@3.3.2:
+ resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==}
+
+ ignore@5.3.2:
+ resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
+ engines: {node: '>= 4'}
+
+ import-fresh@3.3.1:
+ resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
+ engines: {node: '>=6'}
+
+ imurmurhash@0.1.4:
+ resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
+ engines: {node: '>=0.8.19'}
+
+ internal-slot@1.1.0:
+ resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
+ engines: {node: '>= 0.4'}
+
+ inversify@6.2.2:
+ resolution: {integrity: sha512-KB836KHbZ9WrUnB8ax5MtadOwnqQYa+ZJO3KWbPFgcr4RIEnHM621VaqFZzOZd9+U7ln6upt9n0wJei7x2BNqw==}
+ peerDependencies:
+ reflect-metadata: ~0.2.2
+
+ is-array-buffer@3.0.5:
+ resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==}
+ engines: {node: '>= 0.4'}
+
+ is-arrayish@0.3.2:
+ resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==}
+
+ is-async-function@2.1.1:
+ resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==}
+ engines: {node: '>= 0.4'}
+
+ is-bigint@1.1.0:
+ resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==}
+ engines: {node: '>= 0.4'}
+
+ is-boolean-object@1.2.2:
+ resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==}
+ engines: {node: '>= 0.4'}
+
+ is-bun-module@2.0.0:
+ resolution: {integrity: sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==}
+
+ is-callable@1.2.7:
+ resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
+ engines: {node: '>= 0.4'}
+
+ is-core-module@2.16.1:
+ resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
+ engines: {node: '>= 0.4'}
+
+ is-data-view@1.0.2:
+ resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==}
+ engines: {node: '>= 0.4'}
+
+ is-date-object@1.1.0:
+ resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==}
+ engines: {node: '>= 0.4'}
+
+ is-extglob@2.1.1:
+ resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+ engines: {node: '>=0.10.0'}
+
+ is-finalizationregistry@1.1.1:
+ resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==}
+ engines: {node: '>= 0.4'}
+
+ is-generator-function@1.1.0:
+ resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==}
+ engines: {node: '>= 0.4'}
+
+ is-glob@4.0.3:
+ resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+ engines: {node: '>=0.10.0'}
+
+ is-map@2.0.3:
+ resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==}
+ engines: {node: '>= 0.4'}
+
+ is-number-object@1.1.1:
+ resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==}
+ engines: {node: '>= 0.4'}
+
+ is-number@7.0.0:
+ resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+ engines: {node: '>=0.12.0'}
+
+ is-regex@1.2.1:
+ resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==}
+ engines: {node: '>= 0.4'}
+
+ is-set@2.0.3:
+ resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==}
+ engines: {node: '>= 0.4'}
+
+ is-shared-array-buffer@1.0.4:
+ resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==}
+ engines: {node: '>= 0.4'}
+
+ is-string@1.1.1:
+ resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==}
+ engines: {node: '>= 0.4'}
+
+ is-symbol@1.1.1:
+ resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==}
+ engines: {node: '>= 0.4'}
+
+ is-typed-array@1.1.15:
+ resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==}
+ engines: {node: '>= 0.4'}
+
+ is-weakmap@2.0.2:
+ resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==}
+ engines: {node: '>= 0.4'}
+
+ is-weakref@1.1.1:
+ resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==}
+ engines: {node: '>= 0.4'}
+
+ is-weakset@2.0.4:
+ resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==}
+ engines: {node: '>= 0.4'}
+
+ isarray@2.0.5:
+ resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
+
+ isexe@2.0.0:
+ resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+
+ iterator.prototype@1.1.5:
+ resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==}
+ engines: {node: '>= 0.4'}
+
+ jiti@2.4.2:
+ resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==}
+ hasBin: true
+
+ js-tokens@4.0.0:
+ resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+
+ js-yaml@4.1.0:
+ resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
+ hasBin: true
+
+ jsesc@3.1.0:
+ resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ json-buffer@3.0.1:
+ resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
+
+ json-schema-traverse@0.4.1:
+ resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
+
+ json-stable-stringify-without-jsonify@1.0.1:
+ resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
+
+ json5@1.0.2:
+ resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==}
+ hasBin: true
+
+ json5@2.2.3:
+ resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ jsx-ast-utils@3.3.5:
+ resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==}
+ engines: {node: '>=4.0'}
+
+ keyv@4.5.4:
+ resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
+
+ language-subtag-registry@0.3.23:
+ resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==}
+
+ language-tags@1.0.9:
+ resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==}
+ engines: {node: '>=0.10'}
+
+ levn@0.4.1:
+ resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
+ engines: {node: '>= 0.8.0'}
+
+ lightningcss-darwin-arm64@1.29.2:
+ resolution: {integrity: sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [darwin]
+
+ lightningcss-darwin-x64@1.29.2:
+ resolution: {integrity: sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [darwin]
+
+ lightningcss-freebsd-x64@1.29.2:
+ resolution: {integrity: sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [freebsd]
+
+ lightningcss-linux-arm-gnueabihf@1.29.2:
+ resolution: {integrity: sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm]
+ os: [linux]
+
+ lightningcss-linux-arm64-gnu@1.29.2:
+ resolution: {integrity: sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ lightningcss-linux-arm64-musl@1.29.2:
+ resolution: {integrity: sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ lightningcss-linux-x64-gnu@1.29.2:
+ resolution: {integrity: sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ lightningcss-linux-x64-musl@1.29.2:
+ resolution: {integrity: sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ lightningcss-win32-arm64-msvc@1.29.2:
+ resolution: {integrity: sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [win32]
+
+ lightningcss-win32-x64-msvc@1.29.2:
+ resolution: {integrity: sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [win32]
+
+ lightningcss@1.29.2:
+ resolution: {integrity: sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==}
+ engines: {node: '>= 12.0.0'}
+
+ locate-path@6.0.0:
+ resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
+ engines: {node: '>=10'}
+
+ lodash-es@4.17.21:
+ resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
+
+ lodash.merge@4.6.2:
+ resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
+
+ lodash@4.17.21:
+ resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
+
+ loose-envify@1.4.0:
+ resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
+ hasBin: true
+
+ lru-cache@5.1.1:
+ resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
+
+ math-intrinsics@1.1.0:
+ resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
+ engines: {node: '>= 0.4'}
+
+ merge2@1.4.1:
+ resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
+ engines: {node: '>= 8'}
+
+ micromatch@4.0.8:
+ resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
+ engines: {node: '>=8.6'}
+
+ minimatch@3.1.2:
+ resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+
+ minimatch@9.0.5:
+ resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
+ minimist@1.2.8:
+ resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
+
+ ms@2.1.3:
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+ nanoid@3.3.11:
+ resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+
+ nanoid@4.0.2:
+ resolution: {integrity: sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw==}
+ engines: {node: ^14 || ^16 || >=18}
+ hasBin: true
+
+ natural-compare@1.4.0:
+ resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+
+ next@15.2.4:
+ resolution: {integrity: sha512-VwL+LAaPSxEkd3lU2xWbgEOtrM8oedmyhBqaVNmgKB+GvZlCy9rgaEc+y2on0wv+l0oSFqLtYD6dcC1eAedUaQ==}
+ engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
+ hasBin: true
+ peerDependencies:
+ '@opentelemetry/api': ^1.1.0
+ '@playwright/test': ^1.41.2
+ babel-plugin-react-compiler: '*'
+ react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
+ react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
+ sass: ^1.3.0
+ peerDependenciesMeta:
+ '@opentelemetry/api':
+ optional: true
+ '@playwright/test':
+ optional: true
+ babel-plugin-react-compiler:
+ optional: true
+ sass:
+ optional: true
+
+ node-releases@2.0.19:
+ resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==}
+
+ object-assign@4.1.1:
+ resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
+ engines: {node: '>=0.10.0'}
+
+ object-inspect@1.13.4:
+ resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==}
+ engines: {node: '>= 0.4'}
+
+ object-keys@1.1.1:
+ resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
+ engines: {node: '>= 0.4'}
+
+ object.assign@4.1.7:
+ resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==}
+ engines: {node: '>= 0.4'}
+
+ object.entries@1.1.9:
+ resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==}
+ engines: {node: '>= 0.4'}
+
+ object.fromentries@2.0.8:
+ resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==}
+ engines: {node: '>= 0.4'}
+
+ object.groupby@1.0.3:
+ resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==}
+ engines: {node: '>= 0.4'}
+
+ object.values@1.2.1:
+ resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==}
+ engines: {node: '>= 0.4'}
+
+ optionator@0.9.4:
+ resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
+ engines: {node: '>= 0.8.0'}
+
+ own-keys@1.0.1:
+ resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==}
+ engines: {node: '>= 0.4'}
+
+ p-limit@3.1.0:
+ resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
+ engines: {node: '>=10'}
+
+ p-locate@5.0.0:
+ resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
+ engines: {node: '>=10'}
+
+ parent-module@1.0.1:
+ resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
+ engines: {node: '>=6'}
+
+ path-exists@4.0.0:
+ resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
+ engines: {node: '>=8'}
+
+ path-key@3.1.1:
+ resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
+ engines: {node: '>=8'}
+
+ path-parse@1.0.7:
+ resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
+
+ picocolors@1.1.1:
+ resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+ picomatch@2.3.1:
+ resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
+ engines: {node: '>=8.6'}
+
+ picomatch@4.0.2:
+ resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==}
+ engines: {node: '>=12'}
+
+ possible-typed-array-names@1.1.0:
+ resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
+ engines: {node: '>= 0.4'}
+
+ postcss-value-parser@4.2.0:
+ resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
+
+ postcss@8.4.31:
+ resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==}
+ engines: {node: ^10 || ^12 || >=14}
+
+ postcss@8.5.3:
+ resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==}
+ engines: {node: ^10 || ^12 || >=14}
+
+ prelude-ls@1.2.1:
+ resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
+ engines: {node: '>= 0.8.0'}
+
+ prop-types@15.8.1:
+ resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
+
+ punycode@2.3.1:
+ resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
+ engines: {node: '>=6'}
+
+ queue-microtask@1.2.3:
+ resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+
+ react-dom@19.1.0:
+ resolution: {integrity: sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==}
+ peerDependencies:
+ react: ^19.1.0
+
+ react-is@16.13.1:
+ resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
+
+ react@19.1.0:
+ resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==}
+ engines: {node: '>=0.10.0'}
+
+ reflect-metadata@0.2.2:
+ resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==}
+
+ reflect.getprototypeof@1.0.10:
+ resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==}
+ engines: {node: '>= 0.4'}
+
+ regexp.prototype.flags@1.5.4:
+ resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==}
+ engines: {node: '>= 0.4'}
+
+ resolve-from@4.0.0:
+ resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
+ engines: {node: '>=4'}
+
+ resolve-pkg-maps@1.0.0:
+ resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
+
+ resolve@1.22.10:
+ resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==}
+ engines: {node: '>= 0.4'}
+ hasBin: true
+
+ resolve@2.0.0-next.5:
+ resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==}
+ hasBin: true
+
+ reusify@1.1.0:
+ resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
+ engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+
+ run-parallel@1.2.0:
+ resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+
+ rxjs@7.8.2:
+ resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==}
+
+ safe-array-concat@1.1.3:
+ resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==}
+ engines: {node: '>=0.4'}
+
+ safe-push-apply@1.0.0:
+ resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==}
+ engines: {node: '>= 0.4'}
+
+ safe-regex-test@1.1.0:
+ resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==}
+ engines: {node: '>= 0.4'}
+
+ scheduler@0.26.0:
+ resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==}
+
+ semver@6.3.1:
+ resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
+ hasBin: true
+
+ semver@7.7.1:
+ resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ set-function-length@1.2.2:
+ resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
+ engines: {node: '>= 0.4'}
+
+ set-function-name@2.0.2:
+ resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==}
+ engines: {node: '>= 0.4'}
+
+ set-proto@1.0.0:
+ resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==}
+ engines: {node: '>= 0.4'}
+
+ shallowequal@1.1.0:
+ resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==}
+
+ sharp@0.33.5:
+ resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+
+ shebang-command@2.0.0:
+ resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
+ engines: {node: '>=8'}
+
+ shebang-regex@3.0.0:
+ resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
+ engines: {node: '>=8'}
+
+ side-channel-list@1.0.0:
+ resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==}
+ engines: {node: '>= 0.4'}
+
+ side-channel-map@1.0.1:
+ resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==}
+ engines: {node: '>= 0.4'}
+
+ side-channel-weakmap@1.0.2:
+ resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==}
+ engines: {node: '>= 0.4'}
+
+ side-channel@1.1.0:
+ resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==}
+ engines: {node: '>= 0.4'}
+
+ simple-swizzle@0.2.2:
+ resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==}
+
+ source-map-js@1.2.1:
+ resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+ engines: {node: '>=0.10.0'}
+
+ stable-hash@0.0.5:
+ resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==}
+
+ streamsearch@1.1.0:
+ resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
+ engines: {node: '>=10.0.0'}
+
+ string.prototype.includes@2.0.1:
+ resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==}
+ engines: {node: '>= 0.4'}
+
+ string.prototype.matchall@4.0.12:
+ resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==}
+ engines: {node: '>= 0.4'}
+
+ string.prototype.repeat@1.0.0:
+ resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==}
+
+ string.prototype.trim@1.2.10:
+ resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==}
+ engines: {node: '>= 0.4'}
+
+ string.prototype.trimend@1.0.9:
+ resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==}
+ engines: {node: '>= 0.4'}
+
+ string.prototype.trimstart@1.0.8:
+ resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==}
+ engines: {node: '>= 0.4'}
+
+ strip-bom@3.0.0:
+ resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==}
+ engines: {node: '>=4'}
+
+ strip-json-comments@3.1.1:
+ resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
+ engines: {node: '>=8'}
+
+ styled-components@5.3.11:
+ resolution: {integrity: sha512-uuzIIfnVkagcVHv9nE0VPlHPSCmXIUGKfJ42LNjxCCTDTL5sgnJ8Z7GZBq0EnLYGln77tPpEpExt2+qa+cZqSw==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ react: '>= 16.8.0'
+ react-dom: '>= 16.8.0'
+ react-is: '>= 16.8.0'
+
+ styled-jsx@5.1.6:
+ resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==}
+ engines: {node: '>= 12.0.0'}
+ peerDependencies:
+ '@babel/core': '*'
+ babel-plugin-macros: '*'
+ react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0'
+ peerDependenciesMeta:
+ '@babel/core':
+ optional: true
+ babel-plugin-macros:
+ optional: true
+
+ supports-color@5.5.0:
+ resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
+ engines: {node: '>=4'}
+
+ supports-color@7.2.0:
+ resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+ engines: {node: '>=8'}
+
+ supports-preserve-symlinks-flag@1.0.0:
+ resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
+ engines: {node: '>= 0.4'}
+
+ tailwindcss@4.1.0:
+ resolution: {integrity: sha512-vBYstoFnvUZCDxaauNGQQEvJNQgCd1vSMDRYuZZMH1xRRcTboOk1rJrW5yFkEabU9X6Yx1C4LQ+QvPOvQj4Daw==}
+
+ tapable@2.2.1:
+ resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
+ engines: {node: '>=6'}
+
+ tinyglobby@0.2.12:
+ resolution: {integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==}
+ engines: {node: '>=12.0.0'}
+
+ to-regex-range@5.0.1:
+ resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+ engines: {node: '>=8.0'}
+
+ ts-api-utils@2.1.0:
+ resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==}
+ engines: {node: '>=18.12'}
+ peerDependencies:
+ typescript: '>=4.8.4'
+
+ tsconfig-paths@3.15.0:
+ resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==}
+
+ tslib@2.8.1:
+ resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
+
+ type-check@0.4.0:
+ resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
+ engines: {node: '>= 0.8.0'}
+
+ typed-array-buffer@1.0.3:
+ resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==}
+ engines: {node: '>= 0.4'}
+
+ typed-array-byte-length@1.0.3:
+ resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==}
+ engines: {node: '>= 0.4'}
+
+ typed-array-byte-offset@1.0.4:
+ resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==}
+ engines: {node: '>= 0.4'}
+
+ typed-array-length@1.0.7:
+ resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==}
+ engines: {node: '>= 0.4'}
+
+ typescript@5.8.2:
+ resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
+ unbox-primitive@1.1.0:
+ resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==}
+ engines: {node: '>= 0.4'}
+
+ undici-types@6.19.8:
+ resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==}
+
+ unrs-resolver@1.3.3:
+ resolution: {integrity: sha512-PFLAGQzYlyjniXdbmQ3dnGMZJXX5yrl2YS4DLRfR3BhgUsE1zpRIrccp9XMOGRfIHpdFvCn/nr5N1KMVda4x3A==}
+
+ update-browserslist-db@1.1.3:
+ resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==}
+ hasBin: true
+ peerDependencies:
+ browserslist: '>= 4.21.0'
+
+ uri-js@4.4.1:
+ resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+
+ which-boxed-primitive@1.1.1:
+ resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==}
+ engines: {node: '>= 0.4'}
+
+ which-builtin-type@1.2.1:
+ resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==}
+ engines: {node: '>= 0.4'}
+
+ which-collection@1.0.2:
+ resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==}
+ engines: {node: '>= 0.4'}
+
+ which-typed-array@1.1.19:
+ resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==}
+ engines: {node: '>= 0.4'}
+
+ which@2.0.2:
+ resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+ engines: {node: '>= 8'}
+ hasBin: true
+
+ word-wrap@1.2.5:
+ resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
+ engines: {node: '>=0.10.0'}
+
+ yallist@3.1.1:
+ resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
+
+ yocto-queue@0.1.0:
+ resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
+ engines: {node: '>=10'}
+
+snapshots:
+
+ '@alloc/quick-lru@5.2.0': {}
+
+ '@ampproject/remapping@2.3.0':
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.8
+ '@jridgewell/trace-mapping': 0.3.25
+
+ '@babel/code-frame@7.26.2':
+ dependencies:
+ '@babel/helper-validator-identifier': 7.25.9
+ js-tokens: 4.0.0
+ picocolors: 1.1.1
+
+ '@babel/compat-data@7.26.8': {}
+
+ '@babel/core@7.26.10':
+ dependencies:
+ '@ampproject/remapping': 2.3.0
+ '@babel/code-frame': 7.26.2
+ '@babel/generator': 7.27.0
+ '@babel/helper-compilation-targets': 7.27.0
+ '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.10)
+ '@babel/helpers': 7.27.0
+ '@babel/parser': 7.27.0
+ '@babel/template': 7.27.0
+ '@babel/traverse': 7.27.0(supports-color@5.5.0)
+ '@babel/types': 7.27.0
+ convert-source-map: 2.0.0
+ debug: 4.4.0(supports-color@5.5.0)
+ gensync: 1.0.0-beta.2
+ json5: 2.2.3
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/generator@7.27.0':
+ dependencies:
+ '@babel/parser': 7.27.0
+ '@babel/types': 7.27.0
+ '@jridgewell/gen-mapping': 0.3.8
+ '@jridgewell/trace-mapping': 0.3.25
+ jsesc: 3.1.0
+
+ '@babel/helper-annotate-as-pure@7.25.9':
+ dependencies:
+ '@babel/types': 7.27.0
+
+ '@babel/helper-compilation-targets@7.27.0':
+ dependencies:
+ '@babel/compat-data': 7.26.8
+ '@babel/helper-validator-option': 7.25.9
+ browserslist: 4.24.4
+ lru-cache: 5.1.1
+ semver: 6.3.1
+
+ '@babel/helper-module-imports@7.25.9(supports-color@5.5.0)':
+ dependencies:
+ '@babel/traverse': 7.27.0(supports-color@5.5.0)
+ '@babel/types': 7.27.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.10)':
+ dependencies:
+ '@babel/core': 7.26.10
+ '@babel/helper-module-imports': 7.25.9(supports-color@5.5.0)
+ '@babel/helper-validator-identifier': 7.25.9
+ '@babel/traverse': 7.27.0(supports-color@5.5.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-plugin-utils@7.26.5': {}
+
+ '@babel/helper-string-parser@7.25.9': {}
+
+ '@babel/helper-validator-identifier@7.25.9': {}
+
+ '@babel/helper-validator-option@7.25.9': {}
+
+ '@babel/helpers@7.27.0':
+ dependencies:
+ '@babel/template': 7.27.0
+ '@babel/types': 7.27.0
+
+ '@babel/parser@7.27.0':
+ dependencies:
+ '@babel/types': 7.27.0
+
+ '@babel/plugin-syntax-jsx@7.25.9(@babel/core@7.26.10)':
+ dependencies:
+ '@babel/core': 7.26.10
+ '@babel/helper-plugin-utils': 7.26.5
+
+ '@babel/template@7.27.0':
+ dependencies:
+ '@babel/code-frame': 7.26.2
+ '@babel/parser': 7.27.0
+ '@babel/types': 7.27.0
+
+ '@babel/traverse@7.27.0(supports-color@5.5.0)':
+ dependencies:
+ '@babel/code-frame': 7.26.2
+ '@babel/generator': 7.27.0
+ '@babel/parser': 7.27.0
+ '@babel/template': 7.27.0
+ '@babel/types': 7.27.0
+ debug: 4.4.0(supports-color@5.5.0)
+ globals: 11.12.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/types@7.27.0':
+ dependencies:
+ '@babel/helper-string-parser': 7.25.9
+ '@babel/helper-validator-identifier': 7.25.9
+
+ '@dagrejs/graphlib@2.2.2': {}
+
+ '@emnapi/core@1.4.0':
+ dependencies:
+ '@emnapi/wasi-threads': 1.0.1
+ tslib: 2.8.1
+ optional: true
+
+ '@emnapi/runtime@1.4.0':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
+ '@emnapi/wasi-threads@1.0.1':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
+ '@emotion/is-prop-valid@1.3.1':
+ dependencies:
+ '@emotion/memoize': 0.9.0
+
+ '@emotion/memoize@0.9.0': {}
+
+ '@emotion/stylis@0.8.5': {}
+
+ '@emotion/unitless@0.7.5': {}
+
+ '@eslint-community/eslint-utils@4.5.1(eslint@9.23.0(jiti@2.4.2))':
+ dependencies:
+ eslint: 9.23.0(jiti@2.4.2)
+ eslint-visitor-keys: 3.4.3
+
+ '@eslint-community/regexpp@4.12.1': {}
+
+ '@eslint/config-array@0.19.2':
+ dependencies:
+ '@eslint/object-schema': 2.1.6
+ debug: 4.4.0(supports-color@5.5.0)
+ minimatch: 3.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@eslint/config-helpers@0.2.1': {}
+
+ '@eslint/core@0.12.0':
+ dependencies:
+ '@types/json-schema': 7.0.15
+
+ '@eslint/core@0.13.0':
+ dependencies:
+ '@types/json-schema': 7.0.15
+
+ '@eslint/eslintrc@3.3.1':
+ dependencies:
+ ajv: 6.12.6
+ debug: 4.4.0(supports-color@5.5.0)
+ espree: 10.3.0
+ globals: 14.0.0
+ ignore: 5.3.2
+ import-fresh: 3.3.1
+ js-yaml: 4.1.0
+ minimatch: 3.1.2
+ strip-json-comments: 3.1.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@eslint/js@9.23.0': {}
+
+ '@eslint/object-schema@2.1.6': {}
+
+ '@eslint/plugin-kit@0.2.8':
+ dependencies:
+ '@eslint/core': 0.13.0
+ levn: 0.4.1
+
+ '@flowgram.ai/background-plugin@0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@flowgram.ai/core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/utils': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ inversify: 6.2.2(reflect-metadata@0.2.2)
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ reflect-metadata: 0.2.2
+
+ '@flowgram.ai/command@0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@flowgram.ai/utils': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ inversify: 6.2.2(reflect-metadata@0.2.2)
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ reflect-metadata: 0.2.2
+
+ '@flowgram.ai/core@0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@flowgram.ai/command': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/utils': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@phosphor/messaging': 1.3.0
+ '@tweenjs/tween.js': 18.6.4
+ clsx: 1.2.1
+ inversify: 6.2.2(reflect-metadata@0.2.2)
+ lodash: 4.17.21
+ nanoid: 4.0.2
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ reflect-metadata: 0.2.2
+
+ '@flowgram.ai/document@0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@flowgram.ai/core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/utils': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ inversify: 6.2.2(reflect-metadata@0.2.2)
+ lodash: 4.17.21
+ nanoid: 4.0.2
+ reflect-metadata: 0.2.2
+ transitivePeerDependencies:
+ - react
+ - react-dom
+
+ '@flowgram.ai/editor@0.1.17(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)(styled-components@5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0))':
+ dependencies:
+ '@flowgram.ai/background-plugin': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/document': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/form': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/form-core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/group-plugin': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/history': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/history-node-plugin': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(styled-components@5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0))
+ '@flowgram.ai/i18n-plugin': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/materials-plugin': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(styled-components@5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0))
+ '@flowgram.ai/node': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/node-core-plugin': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(styled-components@5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0))
+ '@flowgram.ai/node-variable-plugin': 0.1.17(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)(styled-components@5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0))
+ '@flowgram.ai/playground-react': 0.1.17(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)
+ '@flowgram.ai/redux-devtool-plugin': 0.1.17(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)
+ '@flowgram.ai/renderer': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/shortcuts-plugin': 0.1.17(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)
+ '@flowgram.ai/utils': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/variable-plugin': 0.1.17(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)
+ inversify: 6.2.2(reflect-metadata@0.2.2)
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ reflect-metadata: 0.2.2
+ transitivePeerDependencies:
+ - '@babel/core'
+ - react-is
+ - styled-components
+
+ '@flowgram.ai/form-core@0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@flowgram.ai/core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/document': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/utils': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ inversify: 6.2.2(reflect-metadata@0.2.2)
+ lodash: 4.17.21
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ reflect-metadata: 0.2.2
+
+ '@flowgram.ai/form@0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@flowgram.ai/reactive': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/utils': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ lodash: 4.17.21
+ nanoid: 4.0.2
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+
+ '@flowgram.ai/free-auto-layout-plugin@0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(styled-components@5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0))':
+ dependencies:
+ '@dagrejs/graphlib': 2.2.2
+ '@flowgram.ai/core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/document': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/free-layout-core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/renderer': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/utils': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ inversify: 6.2.2(reflect-metadata@0.2.2)
+ lodash: 4.17.21
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ reflect-metadata: 0.2.2
+ styled-components: 5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)
+
+ '@flowgram.ai/free-container-plugin@0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(styled-components@5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0))':
+ dependencies:
+ '@flowgram.ai/core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/document': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/free-history-plugin': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/free-layout-core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/free-lines-plugin': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(styled-components@5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0))
+ '@flowgram.ai/renderer': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/utils': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ inversify: 6.2.2(reflect-metadata@0.2.2)
+ lodash: 4.17.21
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ reflect-metadata: 0.2.2
+ styled-components: 5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)
+
+ '@flowgram.ai/free-history-plugin@0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@flowgram.ai/core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/document': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/form-core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/free-layout-core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/history': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/utils': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ inversify: 6.2.2(reflect-metadata@0.2.2)
+ lodash: 4.17.21
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ reflect-metadata: 0.2.2
+
+ '@flowgram.ai/free-hover-plugin@0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@flowgram.ai/core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/document': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/free-layout-core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/renderer': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/select-box-plugin': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/utils': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ inversify: 6.2.2(reflect-metadata@0.2.2)
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ reflect-metadata: 0.2.2
+
+ '@flowgram.ai/free-layout-core@0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@flowgram.ai/core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/document': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/form-core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/node': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/reactive': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/utils': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ inversify: 6.2.2(reflect-metadata@0.2.2)
+ lodash-es: 4.17.21
+ nanoid: 4.0.2
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ reflect-metadata: 0.2.2
+
+ '@flowgram.ai/free-layout-editor@0.1.17(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)(styled-components@5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0))':
+ dependencies:
+ '@flowgram.ai/editor': 0.1.17(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)(styled-components@5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0))
+ '@flowgram.ai/free-auto-layout-plugin': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(styled-components@5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0))
+ '@flowgram.ai/free-history-plugin': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/free-hover-plugin': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/free-layout-core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/free-lines-plugin': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(styled-components@5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0))
+ '@flowgram.ai/free-stack-plugin': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(styled-components@5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0))
+ '@flowgram.ai/history': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/minimap-plugin': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(styled-components@5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0))
+ '@flowgram.ai/select-box-plugin': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/utils': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ clsx: 1.2.1
+ inversify: 6.2.2(reflect-metadata@0.2.2)
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ reflect-metadata: 0.2.2
+ transitivePeerDependencies:
+ - '@babel/core'
+ - react-is
+ - styled-components
+
+ '@flowgram.ai/free-lines-plugin@0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(styled-components@5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0))':
+ dependencies:
+ '@flowgram.ai/core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/free-layout-core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/free-stack-plugin': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(styled-components@5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0))
+ '@flowgram.ai/renderer': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/utils': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ bezier-js: 6.1.4
+ clsx: 1.2.1
+ inversify: 6.2.2(reflect-metadata@0.2.2)
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ reflect-metadata: 0.2.2
+ styled-components: 5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)
+
+ '@flowgram.ai/free-node-panel-plugin@0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(styled-components@5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0))':
+ dependencies:
+ '@flowgram.ai/core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/document': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/free-history-plugin': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/free-layout-core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/renderer': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/utils': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ inversify: 6.2.2(reflect-metadata@0.2.2)
+ lodash: 4.17.21
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ reflect-metadata: 0.2.2
+ styled-components: 5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)
+
+ '@flowgram.ai/free-snap-plugin@0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(styled-components@5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0))':
+ dependencies:
+ '@flowgram.ai/core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/document': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/free-layout-core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/renderer': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/utils': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ inversify: 6.2.2(reflect-metadata@0.2.2)
+ lodash: 4.17.21
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ reflect-metadata: 0.2.2
+ styled-components: 5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)
+
+ '@flowgram.ai/free-stack-plugin@0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(styled-components@5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0))':
+ dependencies:
+ '@flowgram.ai/core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/document': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/free-layout-core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/renderer': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/utils': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ inversify: 6.2.2(reflect-metadata@0.2.2)
+ lodash: 4.17.21
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ reflect-metadata: 0.2.2
+ styled-components: 5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)
+
+ '@flowgram.ai/group-plugin@0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@flowgram.ai/core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/document': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/renderer': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/utils': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ inversify: 6.2.2(reflect-metadata@0.2.2)
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ reflect-metadata: 0.2.2
+
+ '@flowgram.ai/history-node-plugin@0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(styled-components@5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0))':
+ dependencies:
+ '@flowgram.ai/core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/document': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/form': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/form-core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/history': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/node': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/utils': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ inversify: 6.2.2(reflect-metadata@0.2.2)
+ lodash: 4.17.21
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ reflect-metadata: 0.2.2
+ styled-components: 5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)
+
+ '@flowgram.ai/history@0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@flowgram.ai/core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/utils': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ inversify: 6.2.2(reflect-metadata@0.2.2)
+ lodash: 4.17.21
+ nanoid: 4.0.2
+ reflect-metadata: 0.2.2
+ transitivePeerDependencies:
+ - react
+ - react-dom
+
+ '@flowgram.ai/i18n-plugin@0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@flowgram.ai/core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/i18n': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ transitivePeerDependencies:
+ - react
+ - react-dom
+
+ '@flowgram.ai/i18n@0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@flowgram.ai/utils': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ transitivePeerDependencies:
+ - react
+ - react-dom
+
+ '@flowgram.ai/materials-plugin@0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(styled-components@5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0))':
+ dependencies:
+ '@flowgram.ai/core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/form-core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/renderer': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/utils': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ inversify: 6.2.2(reflect-metadata@0.2.2)
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ reflect-metadata: 0.2.2
+ styled-components: 5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)
+
+ '@flowgram.ai/minimap-plugin@0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(styled-components@5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0))':
+ dependencies:
+ '@flowgram.ai/core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/document': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/renderer': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/utils': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ inversify: 6.2.2(reflect-metadata@0.2.2)
+ lodash: 4.17.21
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ reflect-metadata: 0.2.2
+ styled-components: 5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)
+
+ '@flowgram.ai/node-core-plugin@0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(styled-components@5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0))':
+ dependencies:
+ '@flowgram.ai/core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/document': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/form-core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/node': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/renderer': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/utils': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ inversify: 6.2.2(reflect-metadata@0.2.2)
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ reflect-metadata: 0.2.2
+ styled-components: 5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)
+
+ '@flowgram.ai/node-variable-plugin@0.1.17(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)(styled-components@5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0))':
+ dependencies:
+ '@flowgram.ai/core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/document': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/form-core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/node': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/utils': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/variable-plugin': 0.1.17(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)
+ inversify: 6.2.2(reflect-metadata@0.2.2)
+ lodash: 4.17.21
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ reflect-metadata: 0.2.2
+ styled-components: 5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)
+ transitivePeerDependencies:
+ - '@babel/core'
+ - react-is
+
+ '@flowgram.ai/node@0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@flowgram.ai/core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/document': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/form': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/form-core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/utils': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ inversify: 6.2.2(reflect-metadata@0.2.2)
+ lodash: 4.17.21
+ nanoid: 4.0.2
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ reflect-metadata: 0.2.2
+
+ '@flowgram.ai/playground-react@0.1.17(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)':
+ dependencies:
+ '@flowgram.ai/background-plugin': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/shortcuts-plugin': 0.1.17(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)
+ '@flowgram.ai/utils': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ inversify: 6.2.2(reflect-metadata@0.2.2)
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ reflect-metadata: 0.2.2
+ transitivePeerDependencies:
+ - '@babel/core'
+ - react-is
+
+ '@flowgram.ai/reactive@0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@flowgram.ai/utils': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+
+ '@flowgram.ai/redux-devtool-plugin@0.1.17(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)':
+ dependencies:
+ '@flowgram.ai/core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/variable-core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ inversify: 6.2.2(reflect-metadata@0.2.2)
+ reflect-metadata: 0.2.2
+ styled-components: 5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)
+ transitivePeerDependencies:
+ - '@babel/core'
+ - react
+ - react-dom
+ - react-is
+
+ '@flowgram.ai/renderer@0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@flowgram.ai/core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/document': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/i18n': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/utils': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ inversify: 6.2.2(reflect-metadata@0.2.2)
+ lodash: 4.17.21
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ reflect-metadata: 0.2.2
+
+ '@flowgram.ai/select-box-plugin@0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@flowgram.ai/core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/renderer': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ inversify: 6.2.2(reflect-metadata@0.2.2)
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ reflect-metadata: 0.2.2
+
+ '@flowgram.ai/shortcuts-plugin@0.1.17(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)':
+ dependencies:
+ '@flowgram.ai/core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/document': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/utils': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ inversify: 6.2.2(reflect-metadata@0.2.2)
+ reflect-metadata: 0.2.2
+ styled-components: 5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)
+ transitivePeerDependencies:
+ - '@babel/core'
+ - react
+ - react-dom
+ - react-is
+
+ '@flowgram.ai/utils@0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ clsx: 1.2.1
+ inversify: 6.2.2(reflect-metadata@0.2.2)
+ nanoid: 4.0.2
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ reflect-metadata: 0.2.2
+
+ '@flowgram.ai/variable-core@0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@flowgram.ai/core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/utils': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ fast-equals: 2.0.4
+ inversify: 6.2.2(reflect-metadata@0.2.2)
+ lodash: 4.17.21
+ nanoid: 4.0.2
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ reflect-metadata: 0.2.2
+ rxjs: 7.8.2
+
+ '@flowgram.ai/variable-layout@0.1.17(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)':
+ dependencies:
+ '@flowgram.ai/core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/document': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/free-layout-core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/utils': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/variable-core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ inversify: 6.2.2(reflect-metadata@0.2.2)
+ reflect-metadata: 0.2.2
+ styled-components: 5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)
+ transitivePeerDependencies:
+ - '@babel/core'
+ - react
+ - react-dom
+ - react-is
+
+ '@flowgram.ai/variable-plugin@0.1.17(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)':
+ dependencies:
+ '@flowgram.ai/core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/document': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/free-layout-core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/utils': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/variable-core': 0.1.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@flowgram.ai/variable-layout': 0.1.17(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)
+ inversify: 6.2.2(reflect-metadata@0.2.2)
+ reflect-metadata: 0.2.2
+ styled-components: 5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)
+ transitivePeerDependencies:
+ - '@babel/core'
+ - react
+ - react-dom
+ - react-is
+
+ '@humanfs/core@0.19.1': {}
+
+ '@humanfs/node@0.16.6':
+ dependencies:
+ '@humanfs/core': 0.19.1
+ '@humanwhocodes/retry': 0.3.1
+
+ '@humanwhocodes/module-importer@1.0.1': {}
+
+ '@humanwhocodes/retry@0.3.1': {}
+
+ '@humanwhocodes/retry@0.4.2': {}
+
+ '@img/sharp-darwin-arm64@0.33.5':
+ optionalDependencies:
+ '@img/sharp-libvips-darwin-arm64': 1.0.4
+ optional: true
+
+ '@img/sharp-darwin-x64@0.33.5':
+ optionalDependencies:
+ '@img/sharp-libvips-darwin-x64': 1.0.4
+ optional: true
+
+ '@img/sharp-libvips-darwin-arm64@1.0.4':
+ optional: true
+
+ '@img/sharp-libvips-darwin-x64@1.0.4':
+ optional: true
+
+ '@img/sharp-libvips-linux-arm64@1.0.4':
+ optional: true
+
+ '@img/sharp-libvips-linux-arm@1.0.5':
+ optional: true
+
+ '@img/sharp-libvips-linux-s390x@1.0.4':
+ optional: true
+
+ '@img/sharp-libvips-linux-x64@1.0.4':
+ optional: true
+
+ '@img/sharp-libvips-linuxmusl-arm64@1.0.4':
+ optional: true
+
+ '@img/sharp-libvips-linuxmusl-x64@1.0.4':
+ optional: true
+
+ '@img/sharp-linux-arm64@0.33.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-arm64': 1.0.4
+ optional: true
+
+ '@img/sharp-linux-arm@0.33.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-arm': 1.0.5
+ optional: true
+
+ '@img/sharp-linux-s390x@0.33.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-s390x': 1.0.4
+ optional: true
+
+ '@img/sharp-linux-x64@0.33.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-x64': 1.0.4
+ optional: true
+
+ '@img/sharp-linuxmusl-arm64@0.33.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linuxmusl-arm64': 1.0.4
+ optional: true
+
+ '@img/sharp-linuxmusl-x64@0.33.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linuxmusl-x64': 1.0.4
+ optional: true
+
+ '@img/sharp-wasm32@0.33.5':
+ dependencies:
+ '@emnapi/runtime': 1.4.0
+ optional: true
+
+ '@img/sharp-win32-ia32@0.33.5':
+ optional: true
+
+ '@img/sharp-win32-x64@0.33.5':
+ optional: true
+
+ '@inversifyjs/common@1.4.0': {}
+
+ '@inversifyjs/core@1.3.5(reflect-metadata@0.2.2)':
+ dependencies:
+ '@inversifyjs/common': 1.4.0
+ '@inversifyjs/reflect-metadata-utils': 0.2.4(reflect-metadata@0.2.2)
+ transitivePeerDependencies:
+ - reflect-metadata
+
+ '@inversifyjs/reflect-metadata-utils@0.2.4(reflect-metadata@0.2.2)':
+ dependencies:
+ reflect-metadata: 0.2.2
+
+ '@jridgewell/gen-mapping@0.3.8':
+ dependencies:
+ '@jridgewell/set-array': 1.2.1
+ '@jridgewell/sourcemap-codec': 1.5.0
+ '@jridgewell/trace-mapping': 0.3.25
+
+ '@jridgewell/resolve-uri@3.1.2': {}
+
+ '@jridgewell/set-array@1.2.1': {}
+
+ '@jridgewell/sourcemap-codec@1.5.0': {}
+
+ '@jridgewell/trace-mapping@0.3.25':
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.2
+ '@jridgewell/sourcemap-codec': 1.5.0
+
+ '@napi-rs/wasm-runtime@0.2.8':
+ dependencies:
+ '@emnapi/core': 1.4.0
+ '@emnapi/runtime': 1.4.0
+ '@tybys/wasm-util': 0.9.0
+ optional: true
+
+ '@next/env@15.2.4': {}
+
+ '@next/eslint-plugin-next@15.2.4':
+ dependencies:
+ fast-glob: 3.3.1
+
+ '@next/swc-darwin-arm64@15.2.4':
+ optional: true
+
+ '@next/swc-darwin-x64@15.2.4':
+ optional: true
+
+ '@next/swc-linux-arm64-gnu@15.2.4':
+ optional: true
+
+ '@next/swc-linux-arm64-musl@15.2.4':
+ optional: true
+
+ '@next/swc-linux-x64-gnu@15.2.4':
+ optional: true
+
+ '@next/swc-linux-x64-musl@15.2.4':
+ optional: true
+
+ '@next/swc-win32-arm64-msvc@15.2.4':
+ optional: true
+
+ '@next/swc-win32-x64-msvc@15.2.4':
+ optional: true
+
+ '@nodelib/fs.scandir@2.1.5':
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ run-parallel: 1.2.0
+
+ '@nodelib/fs.stat@2.0.5': {}
+
+ '@nodelib/fs.walk@1.2.8':
+ dependencies:
+ '@nodelib/fs.scandir': 2.1.5
+ fastq: 1.19.1
+
+ '@nolyfill/is-core-module@1.0.39': {}
+
+ '@phosphor/algorithm@1.2.0': {}
+
+ '@phosphor/collections@1.2.0':
+ dependencies:
+ '@phosphor/algorithm': 1.2.0
+
+ '@phosphor/messaging@1.3.0':
+ dependencies:
+ '@phosphor/algorithm': 1.2.0
+ '@phosphor/collections': 1.2.0
+
+ '@rtsao/scc@1.1.0': {}
+
+ '@rushstack/eslint-patch@1.11.0': {}
+
+ '@swc/counter@0.1.3': {}
+
+ '@swc/helpers@0.5.15':
+ dependencies:
+ tslib: 2.8.1
+
+ '@tailwindcss/node@4.1.0':
+ dependencies:
+ enhanced-resolve: 5.18.1
+ jiti: 2.4.2
+ lightningcss: 1.29.2
+ tailwindcss: 4.1.0
+
+ '@tailwindcss/oxide-android-arm64@4.1.0':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-arm64@4.1.0':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-x64@4.1.0':
+ optional: true
+
+ '@tailwindcss/oxide-freebsd-x64@4.1.0':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.0':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm64-gnu@4.1.0':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm64-musl@4.1.0':
+ optional: true
+
+ '@tailwindcss/oxide-linux-x64-gnu@4.1.0':
+ optional: true
+
+ '@tailwindcss/oxide-linux-x64-musl@4.1.0':
+ optional: true
+
+ '@tailwindcss/oxide-win32-arm64-msvc@4.1.0':
+ optional: true
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.1.0':
+ optional: true
+
+ '@tailwindcss/oxide@4.1.0':
+ optionalDependencies:
+ '@tailwindcss/oxide-android-arm64': 4.1.0
+ '@tailwindcss/oxide-darwin-arm64': 4.1.0
+ '@tailwindcss/oxide-darwin-x64': 4.1.0
+ '@tailwindcss/oxide-freebsd-x64': 4.1.0
+ '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.0
+ '@tailwindcss/oxide-linux-arm64-gnu': 4.1.0
+ '@tailwindcss/oxide-linux-arm64-musl': 4.1.0
+ '@tailwindcss/oxide-linux-x64-gnu': 4.1.0
+ '@tailwindcss/oxide-linux-x64-musl': 4.1.0
+ '@tailwindcss/oxide-win32-arm64-msvc': 4.1.0
+ '@tailwindcss/oxide-win32-x64-msvc': 4.1.0
+
+ '@tailwindcss/postcss@4.1.0':
+ dependencies:
+ '@alloc/quick-lru': 5.2.0
+ '@tailwindcss/node': 4.1.0
+ '@tailwindcss/oxide': 4.1.0
+ postcss: 8.5.3
+ tailwindcss: 4.1.0
+
+ '@tweenjs/tween.js@18.6.4': {}
+
+ '@tybys/wasm-util@0.9.0':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
+ '@types/estree@1.0.7': {}
+
+ '@types/json-schema@7.0.15': {}
+
+ '@types/json5@0.0.29': {}
+
+ '@types/node@20.17.30':
+ dependencies:
+ undici-types: 6.19.8
+
+ '@types/react-dom@19.1.1(@types/react@19.1.0)':
+ dependencies:
+ '@types/react': 19.1.0
+
+ '@types/react@19.1.0':
+ dependencies:
+ csstype: 3.1.3
+
+ '@typescript-eslint/eslint-plugin@8.29.0(@typescript-eslint/parser@8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)':
+ dependencies:
+ '@eslint-community/regexpp': 4.12.1
+ '@typescript-eslint/parser': 8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)
+ '@typescript-eslint/scope-manager': 8.29.0
+ '@typescript-eslint/type-utils': 8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)
+ '@typescript-eslint/utils': 8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)
+ '@typescript-eslint/visitor-keys': 8.29.0
+ eslint: 9.23.0(jiti@2.4.2)
+ graphemer: 1.4.0
+ ignore: 5.3.2
+ natural-compare: 1.4.0
+ ts-api-utils: 2.1.0(typescript@5.8.2)
+ typescript: 5.8.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/parser@8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)':
+ dependencies:
+ '@typescript-eslint/scope-manager': 8.29.0
+ '@typescript-eslint/types': 8.29.0
+ '@typescript-eslint/typescript-estree': 8.29.0(typescript@5.8.2)
+ '@typescript-eslint/visitor-keys': 8.29.0
+ debug: 4.4.0(supports-color@5.5.0)
+ eslint: 9.23.0(jiti@2.4.2)
+ typescript: 5.8.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/scope-manager@8.29.0':
+ dependencies:
+ '@typescript-eslint/types': 8.29.0
+ '@typescript-eslint/visitor-keys': 8.29.0
+
+ '@typescript-eslint/type-utils@8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)':
+ dependencies:
+ '@typescript-eslint/typescript-estree': 8.29.0(typescript@5.8.2)
+ '@typescript-eslint/utils': 8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)
+ debug: 4.4.0(supports-color@5.5.0)
+ eslint: 9.23.0(jiti@2.4.2)
+ ts-api-utils: 2.1.0(typescript@5.8.2)
+ typescript: 5.8.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/types@8.29.0': {}
+
+ '@typescript-eslint/typescript-estree@8.29.0(typescript@5.8.2)':
+ dependencies:
+ '@typescript-eslint/types': 8.29.0
+ '@typescript-eslint/visitor-keys': 8.29.0
+ debug: 4.4.0(supports-color@5.5.0)
+ fast-glob: 3.3.3
+ is-glob: 4.0.3
+ minimatch: 9.0.5
+ semver: 7.7.1
+ ts-api-utils: 2.1.0(typescript@5.8.2)
+ typescript: 5.8.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/utils@8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)':
+ dependencies:
+ '@eslint-community/eslint-utils': 4.5.1(eslint@9.23.0(jiti@2.4.2))
+ '@typescript-eslint/scope-manager': 8.29.0
+ '@typescript-eslint/types': 8.29.0
+ '@typescript-eslint/typescript-estree': 8.29.0(typescript@5.8.2)
+ eslint: 9.23.0(jiti@2.4.2)
+ typescript: 5.8.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/visitor-keys@8.29.0':
+ dependencies:
+ '@typescript-eslint/types': 8.29.0
+ eslint-visitor-keys: 4.2.0
+
+ '@unrs/resolver-binding-darwin-arm64@1.3.3':
+ optional: true
+
+ '@unrs/resolver-binding-darwin-x64@1.3.3':
+ optional: true
+
+ '@unrs/resolver-binding-freebsd-x64@1.3.3':
+ optional: true
+
+ '@unrs/resolver-binding-linux-arm-gnueabihf@1.3.3':
+ optional: true
+
+ '@unrs/resolver-binding-linux-arm-musleabihf@1.3.3':
+ optional: true
+
+ '@unrs/resolver-binding-linux-arm64-gnu@1.3.3':
+ optional: true
+
+ '@unrs/resolver-binding-linux-arm64-musl@1.3.3':
+ optional: true
+
+ '@unrs/resolver-binding-linux-ppc64-gnu@1.3.3':
+ optional: true
+
+ '@unrs/resolver-binding-linux-s390x-gnu@1.3.3':
+ optional: true
+
+ '@unrs/resolver-binding-linux-x64-gnu@1.3.3':
+ optional: true
+
+ '@unrs/resolver-binding-linux-x64-musl@1.3.3':
+ optional: true
+
+ '@unrs/resolver-binding-wasm32-wasi@1.3.3':
+ dependencies:
+ '@napi-rs/wasm-runtime': 0.2.8
+ optional: true
+
+ '@unrs/resolver-binding-win32-arm64-msvc@1.3.3':
+ optional: true
+
+ '@unrs/resolver-binding-win32-ia32-msvc@1.3.3':
+ optional: true
+
+ '@unrs/resolver-binding-win32-x64-msvc@1.3.3':
+ optional: true
+
+ acorn-jsx@5.3.2(acorn@8.14.1):
+ dependencies:
+ acorn: 8.14.1
+
+ acorn@8.14.1: {}
+
+ ajv@6.12.6:
+ dependencies:
+ fast-deep-equal: 3.1.3
+ fast-json-stable-stringify: 2.1.0
+ json-schema-traverse: 0.4.1
+ uri-js: 4.4.1
+
+ ansi-styles@4.3.0:
+ dependencies:
+ color-convert: 2.0.1
+
+ argparse@2.0.1: {}
+
+ aria-query@5.3.2: {}
+
+ array-buffer-byte-length@1.0.2:
+ dependencies:
+ call-bound: 1.0.4
+ is-array-buffer: 3.0.5
+
+ array-includes@3.1.8:
+ dependencies:
+ call-bind: 1.0.8
+ define-properties: 1.2.1
+ es-abstract: 1.23.9
+ es-object-atoms: 1.1.1
+ get-intrinsic: 1.3.0
+ is-string: 1.1.1
+
+ array.prototype.findlast@1.2.5:
+ dependencies:
+ call-bind: 1.0.8
+ define-properties: 1.2.1
+ es-abstract: 1.23.9
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.1
+ es-shim-unscopables: 1.1.0
+
+ array.prototype.findlastindex@1.2.6:
+ dependencies:
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ define-properties: 1.2.1
+ es-abstract: 1.23.9
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.1
+ es-shim-unscopables: 1.1.0
+
+ array.prototype.flat@1.3.3:
+ dependencies:
+ call-bind: 1.0.8
+ define-properties: 1.2.1
+ es-abstract: 1.23.9
+ es-shim-unscopables: 1.1.0
+
+ array.prototype.flatmap@1.3.3:
+ dependencies:
+ call-bind: 1.0.8
+ define-properties: 1.2.1
+ es-abstract: 1.23.9
+ es-shim-unscopables: 1.1.0
+
+ array.prototype.tosorted@1.1.4:
+ dependencies:
+ call-bind: 1.0.8
+ define-properties: 1.2.1
+ es-abstract: 1.23.9
+ es-errors: 1.3.0
+ es-shim-unscopables: 1.1.0
+
+ arraybuffer.prototype.slice@1.0.4:
+ dependencies:
+ array-buffer-byte-length: 1.0.2
+ call-bind: 1.0.8
+ define-properties: 1.2.1
+ es-abstract: 1.23.9
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ is-array-buffer: 3.0.5
+
+ ast-types-flow@0.0.8: {}
+
+ async-function@1.0.0: {}
+
+ available-typed-arrays@1.0.7:
+ dependencies:
+ possible-typed-array-names: 1.1.0
+
+ axe-core@4.10.3: {}
+
+ axobject-query@4.1.0: {}
+
+ babel-plugin-styled-components@2.1.4(@babel/core@7.26.10)(styled-components@5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0))(supports-color@5.5.0):
+ dependencies:
+ '@babel/helper-annotate-as-pure': 7.25.9
+ '@babel/helper-module-imports': 7.25.9(supports-color@5.5.0)
+ '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.10)
+ lodash: 4.17.21
+ picomatch: 2.3.1
+ styled-components: 5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)
+ transitivePeerDependencies:
+ - '@babel/core'
+ - supports-color
+
+ balanced-match@1.0.2: {}
+
+ bezier-js@6.1.4: {}
+
+ brace-expansion@1.1.11:
+ dependencies:
+ balanced-match: 1.0.2
+ concat-map: 0.0.1
+
+ brace-expansion@2.0.1:
+ dependencies:
+ balanced-match: 1.0.2
+
+ braces@3.0.3:
+ dependencies:
+ fill-range: 7.1.1
+
+ browserslist@4.24.4:
+ dependencies:
+ caniuse-lite: 1.0.30001707
+ electron-to-chromium: 1.5.129
+ node-releases: 2.0.19
+ update-browserslist-db: 1.1.3(browserslist@4.24.4)
+
+ busboy@1.6.0:
+ dependencies:
+ streamsearch: 1.1.0
+
+ call-bind-apply-helpers@1.0.2:
+ dependencies:
+ es-errors: 1.3.0
+ function-bind: 1.1.2
+
+ call-bind@1.0.8:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ es-define-property: 1.0.1
+ get-intrinsic: 1.3.0
+ set-function-length: 1.2.2
+
+ call-bound@1.0.4:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ get-intrinsic: 1.3.0
+
+ callsites@3.1.0: {}
+
+ camelize@1.0.1: {}
+
+ caniuse-lite@1.0.30001707: {}
+
+ chalk@4.1.2:
+ dependencies:
+ ansi-styles: 4.3.0
+ supports-color: 7.2.0
+
+ client-only@0.0.1: {}
+
+ clsx@1.2.1: {}
+
+ color-convert@2.0.1:
+ dependencies:
+ color-name: 1.1.4
+
+ color-name@1.1.4: {}
+
+ color-string@1.9.1:
+ dependencies:
+ color-name: 1.1.4
+ simple-swizzle: 0.2.2
+ optional: true
+
+ color@4.2.3:
+ dependencies:
+ color-convert: 2.0.1
+ color-string: 1.9.1
+ optional: true
+
+ concat-map@0.0.1: {}
+
+ convert-source-map@2.0.0: {}
+
+ cross-spawn@7.0.6:
+ dependencies:
+ path-key: 3.1.1
+ shebang-command: 2.0.0
+ which: 2.0.2
+
+ css-color-keywords@1.0.0: {}
+
+ css-to-react-native@3.2.0:
+ dependencies:
+ camelize: 1.0.1
+ css-color-keywords: 1.0.0
+ postcss-value-parser: 4.2.0
+
+ csstype@3.1.3: {}
+
+ damerau-levenshtein@1.0.8: {}
+
+ data-view-buffer@1.0.2:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ is-data-view: 1.0.2
+
+ data-view-byte-length@1.0.2:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ is-data-view: 1.0.2
+
+ data-view-byte-offset@1.0.1:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ is-data-view: 1.0.2
+
+ debug@3.2.7:
+ dependencies:
+ ms: 2.1.3
+
+ debug@4.4.0(supports-color@5.5.0):
+ dependencies:
+ ms: 2.1.3
+ optionalDependencies:
+ supports-color: 5.5.0
+
+ deep-is@0.1.4: {}
+
+ define-data-property@1.1.4:
+ dependencies:
+ es-define-property: 1.0.1
+ es-errors: 1.3.0
+ gopd: 1.2.0
+
+ define-properties@1.2.1:
+ dependencies:
+ define-data-property: 1.1.4
+ has-property-descriptors: 1.0.2
+ object-keys: 1.1.1
+
+ detect-libc@2.0.3: {}
+
+ doctrine@2.1.0:
+ dependencies:
+ esutils: 2.0.3
+
+ dunder-proto@1.0.1:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ es-errors: 1.3.0
+ gopd: 1.2.0
+
+ electron-to-chromium@1.5.129: {}
+
+ emoji-regex@9.2.2: {}
+
+ enhanced-resolve@5.18.1:
+ dependencies:
+ graceful-fs: 4.2.11
+ tapable: 2.2.1
+
+ es-abstract@1.23.9:
+ dependencies:
+ array-buffer-byte-length: 1.0.2
+ arraybuffer.prototype.slice: 1.0.4
+ available-typed-arrays: 1.0.7
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ data-view-buffer: 1.0.2
+ data-view-byte-length: 1.0.2
+ data-view-byte-offset: 1.0.1
+ es-define-property: 1.0.1
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.1
+ es-set-tostringtag: 2.1.0
+ es-to-primitive: 1.3.0
+ function.prototype.name: 1.1.8
+ get-intrinsic: 1.3.0
+ get-proto: 1.0.1
+ get-symbol-description: 1.1.0
+ globalthis: 1.0.4
+ gopd: 1.2.0
+ has-property-descriptors: 1.0.2
+ has-proto: 1.2.0
+ has-symbols: 1.1.0
+ hasown: 2.0.2
+ internal-slot: 1.1.0
+ is-array-buffer: 3.0.5
+ is-callable: 1.2.7
+ is-data-view: 1.0.2
+ is-regex: 1.2.1
+ is-shared-array-buffer: 1.0.4
+ is-string: 1.1.1
+ is-typed-array: 1.1.15
+ is-weakref: 1.1.1
+ math-intrinsics: 1.1.0
+ object-inspect: 1.13.4
+ object-keys: 1.1.1
+ object.assign: 4.1.7
+ own-keys: 1.0.1
+ regexp.prototype.flags: 1.5.4
+ safe-array-concat: 1.1.3
+ safe-push-apply: 1.0.0
+ safe-regex-test: 1.1.0
+ set-proto: 1.0.0
+ string.prototype.trim: 1.2.10
+ string.prototype.trimend: 1.0.9
+ string.prototype.trimstart: 1.0.8
+ typed-array-buffer: 1.0.3
+ typed-array-byte-length: 1.0.3
+ typed-array-byte-offset: 1.0.4
+ typed-array-length: 1.0.7
+ unbox-primitive: 1.1.0
+ which-typed-array: 1.1.19
+
+ es-define-property@1.0.1: {}
+
+ es-errors@1.3.0: {}
+
+ es-iterator-helpers@1.2.1:
+ dependencies:
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ define-properties: 1.2.1
+ es-abstract: 1.23.9
+ es-errors: 1.3.0
+ es-set-tostringtag: 2.1.0
+ function-bind: 1.1.2
+ get-intrinsic: 1.3.0
+ globalthis: 1.0.4
+ gopd: 1.2.0
+ has-property-descriptors: 1.0.2
+ has-proto: 1.2.0
+ has-symbols: 1.1.0
+ internal-slot: 1.1.0
+ iterator.prototype: 1.1.5
+ safe-array-concat: 1.1.3
+
+ es-object-atoms@1.1.1:
+ dependencies:
+ es-errors: 1.3.0
+
+ es-set-tostringtag@2.1.0:
+ dependencies:
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ has-tostringtag: 1.0.2
+ hasown: 2.0.2
+
+ es-shim-unscopables@1.1.0:
+ dependencies:
+ hasown: 2.0.2
+
+ es-to-primitive@1.3.0:
+ dependencies:
+ is-callable: 1.2.7
+ is-date-object: 1.1.0
+ is-symbol: 1.1.1
+
+ escalade@3.2.0: {}
+
+ escape-string-regexp@4.0.0: {}
+
+ eslint-config-next@15.2.4(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2):
+ dependencies:
+ '@next/eslint-plugin-next': 15.2.4
+ '@rushstack/eslint-patch': 1.11.0
+ '@typescript-eslint/eslint-plugin': 8.29.0(@typescript-eslint/parser@8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)
+ '@typescript-eslint/parser': 8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)
+ eslint: 9.23.0(jiti@2.4.2)
+ eslint-import-resolver-node: 0.3.9
+ eslint-import-resolver-typescript: 3.10.0(eslint-plugin-import@2.31.0)(eslint@9.23.0(jiti@2.4.2))
+ eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2))(eslint-import-resolver-typescript@3.10.0)(eslint@9.23.0(jiti@2.4.2))
+ eslint-plugin-jsx-a11y: 6.10.2(eslint@9.23.0(jiti@2.4.2))
+ eslint-plugin-react: 7.37.4(eslint@9.23.0(jiti@2.4.2))
+ eslint-plugin-react-hooks: 5.2.0(eslint@9.23.0(jiti@2.4.2))
+ optionalDependencies:
+ typescript: 5.8.2
+ transitivePeerDependencies:
+ - eslint-import-resolver-webpack
+ - eslint-plugin-import-x
+ - supports-color
+
+ eslint-import-resolver-node@0.3.9:
+ dependencies:
+ debug: 3.2.7
+ is-core-module: 2.16.1
+ resolve: 1.22.10
+ transitivePeerDependencies:
+ - supports-color
+
+ eslint-import-resolver-typescript@3.10.0(eslint-plugin-import@2.31.0)(eslint@9.23.0(jiti@2.4.2)):
+ dependencies:
+ '@nolyfill/is-core-module': 1.0.39
+ debug: 4.4.0(supports-color@5.5.0)
+ eslint: 9.23.0(jiti@2.4.2)
+ get-tsconfig: 4.10.0
+ is-bun-module: 2.0.0
+ stable-hash: 0.0.5
+ tinyglobby: 0.2.12
+ unrs-resolver: 1.3.3
+ optionalDependencies:
+ eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2))(eslint-import-resolver-typescript@3.10.0)(eslint@9.23.0(jiti@2.4.2))
+ transitivePeerDependencies:
+ - supports-color
+
+ eslint-module-utils@2.12.0(@typescript-eslint/parser@8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.0)(eslint@9.23.0(jiti@2.4.2)):
+ dependencies:
+ debug: 3.2.7
+ optionalDependencies:
+ '@typescript-eslint/parser': 8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)
+ eslint: 9.23.0(jiti@2.4.2)
+ eslint-import-resolver-node: 0.3.9
+ eslint-import-resolver-typescript: 3.10.0(eslint-plugin-import@2.31.0)(eslint@9.23.0(jiti@2.4.2))
+ transitivePeerDependencies:
+ - supports-color
+
+ eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2))(eslint-import-resolver-typescript@3.10.0)(eslint@9.23.0(jiti@2.4.2)):
+ dependencies:
+ '@rtsao/scc': 1.1.0
+ array-includes: 3.1.8
+ array.prototype.findlastindex: 1.2.6
+ array.prototype.flat: 1.3.3
+ array.prototype.flatmap: 1.3.3
+ debug: 3.2.7
+ doctrine: 2.1.0
+ eslint: 9.23.0(jiti@2.4.2)
+ eslint-import-resolver-node: 0.3.9
+ eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.0)(eslint@9.23.0(jiti@2.4.2))
+ hasown: 2.0.2
+ is-core-module: 2.16.1
+ is-glob: 4.0.3
+ minimatch: 3.1.2
+ object.fromentries: 2.0.8
+ object.groupby: 1.0.3
+ object.values: 1.2.1
+ semver: 6.3.1
+ string.prototype.trimend: 1.0.9
+ tsconfig-paths: 3.15.0
+ optionalDependencies:
+ '@typescript-eslint/parser': 8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)
+ transitivePeerDependencies:
+ - eslint-import-resolver-typescript
+ - eslint-import-resolver-webpack
+ - supports-color
+
+ eslint-plugin-jsx-a11y@6.10.2(eslint@9.23.0(jiti@2.4.2)):
+ dependencies:
+ aria-query: 5.3.2
+ array-includes: 3.1.8
+ array.prototype.flatmap: 1.3.3
+ ast-types-flow: 0.0.8
+ axe-core: 4.10.3
+ axobject-query: 4.1.0
+ damerau-levenshtein: 1.0.8
+ emoji-regex: 9.2.2
+ eslint: 9.23.0(jiti@2.4.2)
+ hasown: 2.0.2
+ jsx-ast-utils: 3.3.5
+ language-tags: 1.0.9
+ minimatch: 3.1.2
+ object.fromentries: 2.0.8
+ safe-regex-test: 1.1.0
+ string.prototype.includes: 2.0.1
+
+ eslint-plugin-react-hooks@5.2.0(eslint@9.23.0(jiti@2.4.2)):
+ dependencies:
+ eslint: 9.23.0(jiti@2.4.2)
+
+ eslint-plugin-react@7.37.4(eslint@9.23.0(jiti@2.4.2)):
+ dependencies:
+ array-includes: 3.1.8
+ array.prototype.findlast: 1.2.5
+ array.prototype.flatmap: 1.3.3
+ array.prototype.tosorted: 1.1.4
+ doctrine: 2.1.0
+ es-iterator-helpers: 1.2.1
+ eslint: 9.23.0(jiti@2.4.2)
+ estraverse: 5.3.0
+ hasown: 2.0.2
+ jsx-ast-utils: 3.3.5
+ minimatch: 3.1.2
+ object.entries: 1.1.9
+ object.fromentries: 2.0.8
+ object.values: 1.2.1
+ prop-types: 15.8.1
+ resolve: 2.0.0-next.5
+ semver: 6.3.1
+ string.prototype.matchall: 4.0.12
+ string.prototype.repeat: 1.0.0
+
+ eslint-scope@8.3.0:
+ dependencies:
+ esrecurse: 4.3.0
+ estraverse: 5.3.0
+
+ eslint-visitor-keys@3.4.3: {}
+
+ eslint-visitor-keys@4.2.0: {}
+
+ eslint@9.23.0(jiti@2.4.2):
+ dependencies:
+ '@eslint-community/eslint-utils': 4.5.1(eslint@9.23.0(jiti@2.4.2))
+ '@eslint-community/regexpp': 4.12.1
+ '@eslint/config-array': 0.19.2
+ '@eslint/config-helpers': 0.2.1
+ '@eslint/core': 0.12.0
+ '@eslint/eslintrc': 3.3.1
+ '@eslint/js': 9.23.0
+ '@eslint/plugin-kit': 0.2.8
+ '@humanfs/node': 0.16.6
+ '@humanwhocodes/module-importer': 1.0.1
+ '@humanwhocodes/retry': 0.4.2
+ '@types/estree': 1.0.7
+ '@types/json-schema': 7.0.15
+ ajv: 6.12.6
+ chalk: 4.1.2
+ cross-spawn: 7.0.6
+ debug: 4.4.0(supports-color@5.5.0)
+ escape-string-regexp: 4.0.0
+ eslint-scope: 8.3.0
+ eslint-visitor-keys: 4.2.0
+ espree: 10.3.0
+ esquery: 1.6.0
+ esutils: 2.0.3
+ fast-deep-equal: 3.1.3
+ file-entry-cache: 8.0.0
+ find-up: 5.0.0
+ glob-parent: 6.0.2
+ ignore: 5.3.2
+ imurmurhash: 0.1.4
+ is-glob: 4.0.3
+ json-stable-stringify-without-jsonify: 1.0.1
+ lodash.merge: 4.6.2
+ minimatch: 3.1.2
+ natural-compare: 1.4.0
+ optionator: 0.9.4
+ optionalDependencies:
+ jiti: 2.4.2
+ transitivePeerDependencies:
+ - supports-color
+
+ espree@10.3.0:
+ dependencies:
+ acorn: 8.14.1
+ acorn-jsx: 5.3.2(acorn@8.14.1)
+ eslint-visitor-keys: 4.2.0
+
+ esquery@1.6.0:
+ dependencies:
+ estraverse: 5.3.0
+
+ esrecurse@4.3.0:
+ dependencies:
+ estraverse: 5.3.0
+
+ estraverse@5.3.0: {}
+
+ esutils@2.0.3: {}
+
+ fast-deep-equal@3.1.3: {}
+
+ fast-equals@2.0.4: {}
+
+ fast-glob@3.3.1:
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ '@nodelib/fs.walk': 1.2.8
+ glob-parent: 5.1.2
+ merge2: 1.4.1
+ micromatch: 4.0.8
+
+ fast-glob@3.3.3:
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ '@nodelib/fs.walk': 1.2.8
+ glob-parent: 5.1.2
+ merge2: 1.4.1
+ micromatch: 4.0.8
+
+ fast-json-stable-stringify@2.1.0: {}
+
+ fast-levenshtein@2.0.6: {}
+
+ fastq@1.19.1:
+ dependencies:
+ reusify: 1.1.0
+
+ fdir@6.4.3(picomatch@4.0.2):
+ optionalDependencies:
+ picomatch: 4.0.2
+
+ file-entry-cache@8.0.0:
+ dependencies:
+ flat-cache: 4.0.1
+
+ fill-range@7.1.1:
+ dependencies:
+ to-regex-range: 5.0.1
+
+ find-up@5.0.0:
+ dependencies:
+ locate-path: 6.0.0
+ path-exists: 4.0.0
+
+ flat-cache@4.0.1:
+ dependencies:
+ flatted: 3.3.3
+ keyv: 4.5.4
+
+ flatted@3.3.3: {}
+
+ for-each@0.3.5:
+ dependencies:
+ is-callable: 1.2.7
+
+ function-bind@1.1.2: {}
+
+ function.prototype.name@1.1.8:
+ dependencies:
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ define-properties: 1.2.1
+ functions-have-names: 1.2.3
+ hasown: 2.0.2
+ is-callable: 1.2.7
+
+ functions-have-names@1.2.3: {}
+
+ gensync@1.0.0-beta.2: {}
+
+ get-intrinsic@1.3.0:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ es-define-property: 1.0.1
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.1
+ function-bind: 1.1.2
+ get-proto: 1.0.1
+ gopd: 1.2.0
+ has-symbols: 1.1.0
+ hasown: 2.0.2
+ math-intrinsics: 1.1.0
+
+ get-proto@1.0.1:
+ dependencies:
+ dunder-proto: 1.0.1
+ es-object-atoms: 1.1.1
+
+ get-symbol-description@1.1.0:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+
+ get-tsconfig@4.10.0:
+ dependencies:
+ resolve-pkg-maps: 1.0.0
+
+ glob-parent@5.1.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ glob-parent@6.0.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ globals@11.12.0: {}
+
+ globals@14.0.0: {}
+
+ globalthis@1.0.4:
+ dependencies:
+ define-properties: 1.2.1
+ gopd: 1.2.0
+
+ gopd@1.2.0: {}
+
+ graceful-fs@4.2.11: {}
+
+ graphemer@1.4.0: {}
+
+ has-bigints@1.1.0: {}
+
+ has-flag@3.0.0: {}
+
+ has-flag@4.0.0: {}
+
+ has-property-descriptors@1.0.2:
+ dependencies:
+ es-define-property: 1.0.1
+
+ has-proto@1.2.0:
+ dependencies:
+ dunder-proto: 1.0.1
+
+ has-symbols@1.1.0: {}
+
+ has-tostringtag@1.0.2:
+ dependencies:
+ has-symbols: 1.1.0
+
+ hasown@2.0.2:
+ dependencies:
+ function-bind: 1.1.2
+
+ hoist-non-react-statics@3.3.2:
+ dependencies:
+ react-is: 16.13.1
+
+ ignore@5.3.2: {}
+
+ import-fresh@3.3.1:
+ dependencies:
+ parent-module: 1.0.1
+ resolve-from: 4.0.0
+
+ imurmurhash@0.1.4: {}
+
+ internal-slot@1.1.0:
+ dependencies:
+ es-errors: 1.3.0
+ hasown: 2.0.2
+ side-channel: 1.1.0
+
+ inversify@6.2.2(reflect-metadata@0.2.2):
+ dependencies:
+ '@inversifyjs/common': 1.4.0
+ '@inversifyjs/core': 1.3.5(reflect-metadata@0.2.2)
+ reflect-metadata: 0.2.2
+
+ is-array-buffer@3.0.5:
+ dependencies:
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ get-intrinsic: 1.3.0
+
+ is-arrayish@0.3.2:
+ optional: true
+
+ is-async-function@2.1.1:
+ dependencies:
+ async-function: 1.0.0
+ call-bound: 1.0.4
+ get-proto: 1.0.1
+ has-tostringtag: 1.0.2
+ safe-regex-test: 1.1.0
+
+ is-bigint@1.1.0:
+ dependencies:
+ has-bigints: 1.1.0
+
+ is-boolean-object@1.2.2:
+ dependencies:
+ call-bound: 1.0.4
+ has-tostringtag: 1.0.2
+
+ is-bun-module@2.0.0:
+ dependencies:
+ semver: 7.7.1
+
+ is-callable@1.2.7: {}
+
+ is-core-module@2.16.1:
+ dependencies:
+ hasown: 2.0.2
+
+ is-data-view@1.0.2:
+ dependencies:
+ call-bound: 1.0.4
+ get-intrinsic: 1.3.0
+ is-typed-array: 1.1.15
+
+ is-date-object@1.1.0:
+ dependencies:
+ call-bound: 1.0.4
+ has-tostringtag: 1.0.2
+
+ is-extglob@2.1.1: {}
+
+ is-finalizationregistry@1.1.1:
+ dependencies:
+ call-bound: 1.0.4
+
+ is-generator-function@1.1.0:
+ dependencies:
+ call-bound: 1.0.4
+ get-proto: 1.0.1
+ has-tostringtag: 1.0.2
+ safe-regex-test: 1.1.0
+
+ is-glob@4.0.3:
+ dependencies:
+ is-extglob: 2.1.1
+
+ is-map@2.0.3: {}
+
+ is-number-object@1.1.1:
+ dependencies:
+ call-bound: 1.0.4
+ has-tostringtag: 1.0.2
+
+ is-number@7.0.0: {}
+
+ is-regex@1.2.1:
+ dependencies:
+ call-bound: 1.0.4
+ gopd: 1.2.0
+ has-tostringtag: 1.0.2
+ hasown: 2.0.2
+
+ is-set@2.0.3: {}
+
+ is-shared-array-buffer@1.0.4:
+ dependencies:
+ call-bound: 1.0.4
+
+ is-string@1.1.1:
+ dependencies:
+ call-bound: 1.0.4
+ has-tostringtag: 1.0.2
+
+ is-symbol@1.1.1:
+ dependencies:
+ call-bound: 1.0.4
+ has-symbols: 1.1.0
+ safe-regex-test: 1.1.0
+
+ is-typed-array@1.1.15:
+ dependencies:
+ which-typed-array: 1.1.19
+
+ is-weakmap@2.0.2: {}
+
+ is-weakref@1.1.1:
+ dependencies:
+ call-bound: 1.0.4
+
+ is-weakset@2.0.4:
+ dependencies:
+ call-bound: 1.0.4
+ get-intrinsic: 1.3.0
+
+ isarray@2.0.5: {}
+
+ isexe@2.0.0: {}
+
+ iterator.prototype@1.1.5:
+ dependencies:
+ define-data-property: 1.1.4
+ es-object-atoms: 1.1.1
+ get-intrinsic: 1.3.0
+ get-proto: 1.0.1
+ has-symbols: 1.1.0
+ set-function-name: 2.0.2
+
+ jiti@2.4.2: {}
+
+ js-tokens@4.0.0: {}
+
+ js-yaml@4.1.0:
+ dependencies:
+ argparse: 2.0.1
+
+ jsesc@3.1.0: {}
+
+ json-buffer@3.0.1: {}
+
+ json-schema-traverse@0.4.1: {}
+
+ json-stable-stringify-without-jsonify@1.0.1: {}
+
+ json5@1.0.2:
+ dependencies:
+ minimist: 1.2.8
+
+ json5@2.2.3: {}
+
+ jsx-ast-utils@3.3.5:
+ dependencies:
+ array-includes: 3.1.8
+ array.prototype.flat: 1.3.3
+ object.assign: 4.1.7
+ object.values: 1.2.1
+
+ keyv@4.5.4:
+ dependencies:
+ json-buffer: 3.0.1
+
+ language-subtag-registry@0.3.23: {}
+
+ language-tags@1.0.9:
+ dependencies:
+ language-subtag-registry: 0.3.23
+
+ levn@0.4.1:
+ dependencies:
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+
+ lightningcss-darwin-arm64@1.29.2:
+ optional: true
+
+ lightningcss-darwin-x64@1.29.2:
+ optional: true
+
+ lightningcss-freebsd-x64@1.29.2:
+ optional: true
+
+ lightningcss-linux-arm-gnueabihf@1.29.2:
+ optional: true
+
+ lightningcss-linux-arm64-gnu@1.29.2:
+ optional: true
+
+ lightningcss-linux-arm64-musl@1.29.2:
+ optional: true
+
+ lightningcss-linux-x64-gnu@1.29.2:
+ optional: true
+
+ lightningcss-linux-x64-musl@1.29.2:
+ optional: true
+
+ lightningcss-win32-arm64-msvc@1.29.2:
+ optional: true
+
+ lightningcss-win32-x64-msvc@1.29.2:
+ optional: true
+
+ lightningcss@1.29.2:
+ dependencies:
+ detect-libc: 2.0.3
+ optionalDependencies:
+ lightningcss-darwin-arm64: 1.29.2
+ lightningcss-darwin-x64: 1.29.2
+ lightningcss-freebsd-x64: 1.29.2
+ lightningcss-linux-arm-gnueabihf: 1.29.2
+ lightningcss-linux-arm64-gnu: 1.29.2
+ lightningcss-linux-arm64-musl: 1.29.2
+ lightningcss-linux-x64-gnu: 1.29.2
+ lightningcss-linux-x64-musl: 1.29.2
+ lightningcss-win32-arm64-msvc: 1.29.2
+ lightningcss-win32-x64-msvc: 1.29.2
+
+ locate-path@6.0.0:
+ dependencies:
+ p-locate: 5.0.0
+
+ lodash-es@4.17.21: {}
+
+ lodash.merge@4.6.2: {}
+
+ lodash@4.17.21: {}
+
+ loose-envify@1.4.0:
+ dependencies:
+ js-tokens: 4.0.0
+
+ lru-cache@5.1.1:
+ dependencies:
+ yallist: 3.1.1
+
+ math-intrinsics@1.1.0: {}
+
+ merge2@1.4.1: {}
+
+ micromatch@4.0.8:
+ dependencies:
+ braces: 3.0.3
+ picomatch: 2.3.1
+
+ minimatch@3.1.2:
+ dependencies:
+ brace-expansion: 1.1.11
+
+ minimatch@9.0.5:
+ dependencies:
+ brace-expansion: 2.0.1
+
+ minimist@1.2.8: {}
+
+ ms@2.1.3: {}
+
+ nanoid@3.3.11: {}
+
+ nanoid@4.0.2: {}
+
+ natural-compare@1.4.0: {}
+
+ next@15.2.4(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
+ dependencies:
+ '@next/env': 15.2.4
+ '@swc/counter': 0.1.3
+ '@swc/helpers': 0.5.15
+ busboy: 1.6.0
+ caniuse-lite: 1.0.30001707
+ postcss: 8.4.31
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ styled-jsx: 5.1.6(@babel/core@7.26.10)(react@19.1.0)
+ optionalDependencies:
+ '@next/swc-darwin-arm64': 15.2.4
+ '@next/swc-darwin-x64': 15.2.4
+ '@next/swc-linux-arm64-gnu': 15.2.4
+ '@next/swc-linux-arm64-musl': 15.2.4
+ '@next/swc-linux-x64-gnu': 15.2.4
+ '@next/swc-linux-x64-musl': 15.2.4
+ '@next/swc-win32-arm64-msvc': 15.2.4
+ '@next/swc-win32-x64-msvc': 15.2.4
+ sharp: 0.33.5
+ transitivePeerDependencies:
+ - '@babel/core'
+ - babel-plugin-macros
+
+ node-releases@2.0.19: {}
+
+ object-assign@4.1.1: {}
+
+ object-inspect@1.13.4: {}
+
+ object-keys@1.1.1: {}
+
+ object.assign@4.1.7:
+ dependencies:
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ define-properties: 1.2.1
+ es-object-atoms: 1.1.1
+ has-symbols: 1.1.0
+ object-keys: 1.1.1
+
+ object.entries@1.1.9:
+ dependencies:
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ define-properties: 1.2.1
+ es-object-atoms: 1.1.1
+
+ object.fromentries@2.0.8:
+ dependencies:
+ call-bind: 1.0.8
+ define-properties: 1.2.1
+ es-abstract: 1.23.9
+ es-object-atoms: 1.1.1
+
+ object.groupby@1.0.3:
+ dependencies:
+ call-bind: 1.0.8
+ define-properties: 1.2.1
+ es-abstract: 1.23.9
+
+ object.values@1.2.1:
+ dependencies:
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ define-properties: 1.2.1
+ es-object-atoms: 1.1.1
+
+ optionator@0.9.4:
+ dependencies:
+ deep-is: 0.1.4
+ fast-levenshtein: 2.0.6
+ levn: 0.4.1
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+ word-wrap: 1.2.5
+
+ own-keys@1.0.1:
+ dependencies:
+ get-intrinsic: 1.3.0
+ object-keys: 1.1.1
+ safe-push-apply: 1.0.0
+
+ p-limit@3.1.0:
+ dependencies:
+ yocto-queue: 0.1.0
+
+ p-locate@5.0.0:
+ dependencies:
+ p-limit: 3.1.0
+
+ parent-module@1.0.1:
+ dependencies:
+ callsites: 3.1.0
+
+ path-exists@4.0.0: {}
+
+ path-key@3.1.1: {}
+
+ path-parse@1.0.7: {}
+
+ picocolors@1.1.1: {}
+
+ picomatch@2.3.1: {}
+
+ picomatch@4.0.2: {}
+
+ possible-typed-array-names@1.1.0: {}
+
+ postcss-value-parser@4.2.0: {}
+
+ postcss@8.4.31:
+ dependencies:
+ nanoid: 3.3.11
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
+ postcss@8.5.3:
+ dependencies:
+ nanoid: 3.3.11
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
+ prelude-ls@1.2.1: {}
+
+ prop-types@15.8.1:
+ dependencies:
+ loose-envify: 1.4.0
+ object-assign: 4.1.1
+ react-is: 16.13.1
+
+ punycode@2.3.1: {}
+
+ queue-microtask@1.2.3: {}
+
+ react-dom@19.1.0(react@19.1.0):
+ dependencies:
+ react: 19.1.0
+ scheduler: 0.26.0
+
+ react-is@16.13.1: {}
+
+ react@19.1.0: {}
+
+ reflect-metadata@0.2.2: {}
+
+ reflect.getprototypeof@1.0.10:
+ dependencies:
+ call-bind: 1.0.8
+ define-properties: 1.2.1
+ es-abstract: 1.23.9
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.1
+ get-intrinsic: 1.3.0
+ get-proto: 1.0.1
+ which-builtin-type: 1.2.1
+
+ regexp.prototype.flags@1.5.4:
+ dependencies:
+ call-bind: 1.0.8
+ define-properties: 1.2.1
+ es-errors: 1.3.0
+ get-proto: 1.0.1
+ gopd: 1.2.0
+ set-function-name: 2.0.2
+
+ resolve-from@4.0.0: {}
+
+ resolve-pkg-maps@1.0.0: {}
+
+ resolve@1.22.10:
+ dependencies:
+ is-core-module: 2.16.1
+ path-parse: 1.0.7
+ supports-preserve-symlinks-flag: 1.0.0
+
+ resolve@2.0.0-next.5:
+ dependencies:
+ is-core-module: 2.16.1
+ path-parse: 1.0.7
+ supports-preserve-symlinks-flag: 1.0.0
+
+ reusify@1.1.0: {}
+
+ run-parallel@1.2.0:
+ dependencies:
+ queue-microtask: 1.2.3
+
+ rxjs@7.8.2:
+ dependencies:
+ tslib: 2.8.1
+
+ safe-array-concat@1.1.3:
+ dependencies:
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ get-intrinsic: 1.3.0
+ has-symbols: 1.1.0
+ isarray: 2.0.5
+
+ safe-push-apply@1.0.0:
+ dependencies:
+ es-errors: 1.3.0
+ isarray: 2.0.5
+
+ safe-regex-test@1.1.0:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ is-regex: 1.2.1
+
+ scheduler@0.26.0: {}
+
+ semver@6.3.1: {}
+
+ semver@7.7.1: {}
+
+ set-function-length@1.2.2:
+ dependencies:
+ define-data-property: 1.1.4
+ es-errors: 1.3.0
+ function-bind: 1.1.2
+ get-intrinsic: 1.3.0
+ gopd: 1.2.0
+ has-property-descriptors: 1.0.2
+
+ set-function-name@2.0.2:
+ dependencies:
+ define-data-property: 1.1.4
+ es-errors: 1.3.0
+ functions-have-names: 1.2.3
+ has-property-descriptors: 1.0.2
+
+ set-proto@1.0.0:
+ dependencies:
+ dunder-proto: 1.0.1
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.1
+
+ shallowequal@1.1.0: {}
+
+ sharp@0.33.5:
+ dependencies:
+ color: 4.2.3
+ detect-libc: 2.0.3
+ semver: 7.7.1
+ optionalDependencies:
+ '@img/sharp-darwin-arm64': 0.33.5
+ '@img/sharp-darwin-x64': 0.33.5
+ '@img/sharp-libvips-darwin-arm64': 1.0.4
+ '@img/sharp-libvips-darwin-x64': 1.0.4
+ '@img/sharp-libvips-linux-arm': 1.0.5
+ '@img/sharp-libvips-linux-arm64': 1.0.4
+ '@img/sharp-libvips-linux-s390x': 1.0.4
+ '@img/sharp-libvips-linux-x64': 1.0.4
+ '@img/sharp-libvips-linuxmusl-arm64': 1.0.4
+ '@img/sharp-libvips-linuxmusl-x64': 1.0.4
+ '@img/sharp-linux-arm': 0.33.5
+ '@img/sharp-linux-arm64': 0.33.5
+ '@img/sharp-linux-s390x': 0.33.5
+ '@img/sharp-linux-x64': 0.33.5
+ '@img/sharp-linuxmusl-arm64': 0.33.5
+ '@img/sharp-linuxmusl-x64': 0.33.5
+ '@img/sharp-wasm32': 0.33.5
+ '@img/sharp-win32-ia32': 0.33.5
+ '@img/sharp-win32-x64': 0.33.5
+ optional: true
+
+ shebang-command@2.0.0:
+ dependencies:
+ shebang-regex: 3.0.0
+
+ shebang-regex@3.0.0: {}
+
+ side-channel-list@1.0.0:
+ dependencies:
+ es-errors: 1.3.0
+ object-inspect: 1.13.4
+
+ side-channel-map@1.0.1:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ object-inspect: 1.13.4
+
+ side-channel-weakmap@1.0.2:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ object-inspect: 1.13.4
+ side-channel-map: 1.0.1
+
+ side-channel@1.1.0:
+ dependencies:
+ es-errors: 1.3.0
+ object-inspect: 1.13.4
+ side-channel-list: 1.0.0
+ side-channel-map: 1.0.1
+ side-channel-weakmap: 1.0.2
+
+ simple-swizzle@0.2.2:
+ dependencies:
+ is-arrayish: 0.3.2
+ optional: true
+
+ source-map-js@1.2.1: {}
+
+ stable-hash@0.0.5: {}
+
+ streamsearch@1.1.0: {}
+
+ string.prototype.includes@2.0.1:
+ dependencies:
+ call-bind: 1.0.8
+ define-properties: 1.2.1
+ es-abstract: 1.23.9
+
+ string.prototype.matchall@4.0.12:
+ dependencies:
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ define-properties: 1.2.1
+ es-abstract: 1.23.9
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.1
+ get-intrinsic: 1.3.0
+ gopd: 1.2.0
+ has-symbols: 1.1.0
+ internal-slot: 1.1.0
+ regexp.prototype.flags: 1.5.4
+ set-function-name: 2.0.2
+ side-channel: 1.1.0
+
+ string.prototype.repeat@1.0.0:
+ dependencies:
+ define-properties: 1.2.1
+ es-abstract: 1.23.9
+
+ string.prototype.trim@1.2.10:
+ dependencies:
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ define-data-property: 1.1.4
+ define-properties: 1.2.1
+ es-abstract: 1.23.9
+ es-object-atoms: 1.1.1
+ has-property-descriptors: 1.0.2
+
+ string.prototype.trimend@1.0.9:
+ dependencies:
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ define-properties: 1.2.1
+ es-object-atoms: 1.1.1
+
+ string.prototype.trimstart@1.0.8:
+ dependencies:
+ call-bind: 1.0.8
+ define-properties: 1.2.1
+ es-object-atoms: 1.1.1
+
+ strip-bom@3.0.0: {}
+
+ strip-json-comments@3.1.1: {}
+
+ styled-components@5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0):
+ dependencies:
+ '@babel/helper-module-imports': 7.25.9(supports-color@5.5.0)
+ '@babel/traverse': 7.27.0(supports-color@5.5.0)
+ '@emotion/is-prop-valid': 1.3.1
+ '@emotion/stylis': 0.8.5
+ '@emotion/unitless': 0.7.5
+ babel-plugin-styled-components: 2.1.4(@babel/core@7.26.10)(styled-components@5.3.11(@babel/core@7.26.10)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0))(supports-color@5.5.0)
+ css-to-react-native: 3.2.0
+ hoist-non-react-statics: 3.3.2
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ react-is: 16.13.1
+ shallowequal: 1.1.0
+ supports-color: 5.5.0
+ transitivePeerDependencies:
+ - '@babel/core'
+
+ styled-jsx@5.1.6(@babel/core@7.26.10)(react@19.1.0):
+ dependencies:
+ client-only: 0.0.1
+ react: 19.1.0
+ optionalDependencies:
+ '@babel/core': 7.26.10
+
+ supports-color@5.5.0:
+ dependencies:
+ has-flag: 3.0.0
+
+ supports-color@7.2.0:
+ dependencies:
+ has-flag: 4.0.0
+
+ supports-preserve-symlinks-flag@1.0.0: {}
+
+ tailwindcss@4.1.0: {}
+
+ tapable@2.2.1: {}
+
+ tinyglobby@0.2.12:
+ dependencies:
+ fdir: 6.4.3(picomatch@4.0.2)
+ picomatch: 4.0.2
+
+ to-regex-range@5.0.1:
+ dependencies:
+ is-number: 7.0.0
+
+ ts-api-utils@2.1.0(typescript@5.8.2):
+ dependencies:
+ typescript: 5.8.2
+
+ tsconfig-paths@3.15.0:
+ dependencies:
+ '@types/json5': 0.0.29
+ json5: 1.0.2
+ minimist: 1.2.8
+ strip-bom: 3.0.0
+
+ tslib@2.8.1: {}
+
+ type-check@0.4.0:
+ dependencies:
+ prelude-ls: 1.2.1
+
+ typed-array-buffer@1.0.3:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ is-typed-array: 1.1.15
+
+ typed-array-byte-length@1.0.3:
+ dependencies:
+ call-bind: 1.0.8
+ for-each: 0.3.5
+ gopd: 1.2.0
+ has-proto: 1.2.0
+ is-typed-array: 1.1.15
+
+ typed-array-byte-offset@1.0.4:
+ dependencies:
+ available-typed-arrays: 1.0.7
+ call-bind: 1.0.8
+ for-each: 0.3.5
+ gopd: 1.2.0
+ has-proto: 1.2.0
+ is-typed-array: 1.1.15
+ reflect.getprototypeof: 1.0.10
+
+ typed-array-length@1.0.7:
+ dependencies:
+ call-bind: 1.0.8
+ for-each: 0.3.5
+ gopd: 1.2.0
+ is-typed-array: 1.1.15
+ possible-typed-array-names: 1.1.0
+ reflect.getprototypeof: 1.0.10
+
+ typescript@5.8.2: {}
+
+ unbox-primitive@1.1.0:
+ dependencies:
+ call-bound: 1.0.4
+ has-bigints: 1.1.0
+ has-symbols: 1.1.0
+ which-boxed-primitive: 1.1.1
+
+ undici-types@6.19.8: {}
+
+ unrs-resolver@1.3.3:
+ optionalDependencies:
+ '@unrs/resolver-binding-darwin-arm64': 1.3.3
+ '@unrs/resolver-binding-darwin-x64': 1.3.3
+ '@unrs/resolver-binding-freebsd-x64': 1.3.3
+ '@unrs/resolver-binding-linux-arm-gnueabihf': 1.3.3
+ '@unrs/resolver-binding-linux-arm-musleabihf': 1.3.3
+ '@unrs/resolver-binding-linux-arm64-gnu': 1.3.3
+ '@unrs/resolver-binding-linux-arm64-musl': 1.3.3
+ '@unrs/resolver-binding-linux-ppc64-gnu': 1.3.3
+ '@unrs/resolver-binding-linux-s390x-gnu': 1.3.3
+ '@unrs/resolver-binding-linux-x64-gnu': 1.3.3
+ '@unrs/resolver-binding-linux-x64-musl': 1.3.3
+ '@unrs/resolver-binding-wasm32-wasi': 1.3.3
+ '@unrs/resolver-binding-win32-arm64-msvc': 1.3.3
+ '@unrs/resolver-binding-win32-ia32-msvc': 1.3.3
+ '@unrs/resolver-binding-win32-x64-msvc': 1.3.3
+
+ update-browserslist-db@1.1.3(browserslist@4.24.4):
+ dependencies:
+ browserslist: 4.24.4
+ escalade: 3.2.0
+ picocolors: 1.1.1
+
+ uri-js@4.4.1:
+ dependencies:
+ punycode: 2.3.1
+
+ which-boxed-primitive@1.1.1:
+ dependencies:
+ is-bigint: 1.1.0
+ is-boolean-object: 1.2.2
+ is-number-object: 1.1.1
+ is-string: 1.1.1
+ is-symbol: 1.1.1
+
+ which-builtin-type@1.2.1:
+ dependencies:
+ call-bound: 1.0.4
+ function.prototype.name: 1.1.8
+ has-tostringtag: 1.0.2
+ is-async-function: 2.1.1
+ is-date-object: 1.1.0
+ is-finalizationregistry: 1.1.1
+ is-generator-function: 1.1.0
+ is-regex: 1.2.1
+ is-weakref: 1.1.1
+ isarray: 2.0.5
+ which-boxed-primitive: 1.1.1
+ which-collection: 1.0.2
+ which-typed-array: 1.1.19
+
+ which-collection@1.0.2:
+ dependencies:
+ is-map: 2.0.3
+ is-set: 2.0.3
+ is-weakmap: 2.0.2
+ is-weakset: 2.0.4
+
+ which-typed-array@1.1.19:
+ dependencies:
+ available-typed-arrays: 1.0.7
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ for-each: 0.3.5
+ get-proto: 1.0.1
+ gopd: 1.2.0
+ has-tostringtag: 1.0.2
+
+ which@2.0.2:
+ dependencies:
+ isexe: 2.0.0
+
+ word-wrap@1.2.5: {}
+
+ yallist@3.1.1: {}
+
+ yocto-queue@0.1.0: {}
diff --git a/apps/demo-nextjs-antd/postcss.config.mjs b/apps/demo-nextjs-antd/postcss.config.mjs
new file mode 100644
index 00000000..ba720fe5
--- /dev/null
+++ b/apps/demo-nextjs-antd/postcss.config.mjs
@@ -0,0 +1,5 @@
+const config = {
+ plugins: ['@tailwindcss/postcss'],
+};
+
+export default config;
diff --git a/apps/demo-nextjs-antd/public/file.svg b/apps/demo-nextjs-antd/public/file.svg
new file mode 100644
index 00000000..004145cd
--- /dev/null
+++ b/apps/demo-nextjs-antd/public/file.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/apps/demo-nextjs-antd/public/globe.svg b/apps/demo-nextjs-antd/public/globe.svg
new file mode 100644
index 00000000..567f17b0
--- /dev/null
+++ b/apps/demo-nextjs-antd/public/globe.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/apps/demo-nextjs-antd/public/next.svg b/apps/demo-nextjs-antd/public/next.svg
new file mode 100644
index 00000000..5174b28c
--- /dev/null
+++ b/apps/demo-nextjs-antd/public/next.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/apps/demo-nextjs-antd/public/vercel.svg b/apps/demo-nextjs-antd/public/vercel.svg
new file mode 100644
index 00000000..77053960
--- /dev/null
+++ b/apps/demo-nextjs-antd/public/vercel.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/apps/demo-nextjs-antd/public/window.svg b/apps/demo-nextjs-antd/public/window.svg
new file mode 100644
index 00000000..b2b2a44f
--- /dev/null
+++ b/apps/demo-nextjs-antd/public/window.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/apps/demo-nextjs-antd/src/app/favicon.ico b/apps/demo-nextjs-antd/src/app/favicon.ico
new file mode 100644
index 00000000..718d6fea
Binary files /dev/null and b/apps/demo-nextjs-antd/src/app/favicon.ico differ
diff --git a/apps/demo-nextjs-antd/src/app/globals.css b/apps/demo-nextjs-antd/src/app/globals.css
new file mode 100644
index 00000000..a2dc41ec
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/app/globals.css
@@ -0,0 +1,26 @@
+@import "tailwindcss";
+
+:root {
+ --background: #ffffff;
+ --foreground: #171717;
+}
+
+@theme inline {
+ --color-background: var(--background);
+ --color-foreground: var(--foreground);
+ --font-sans: var(--font-geist-sans);
+ --font-mono: var(--font-geist-mono);
+}
+
+@media (prefers-color-scheme: dark) {
+ :root {
+ --background: #0a0a0a;
+ --foreground: #ededed;
+ }
+}
+
+body {
+ background: var(--background);
+ color: var(--foreground);
+ font-family: Arial, Helvetica, sans-serif;
+}
diff --git a/apps/demo-nextjs-antd/src/app/layout.tsx b/apps/demo-nextjs-antd/src/app/layout.tsx
new file mode 100644
index 00000000..ce9dc724
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/app/layout.tsx
@@ -0,0 +1,30 @@
+import { Geist, Geist_Mono } from 'next/font/google';
+import type { Metadata } from 'next';
+import './globals.css';
+
+const geistSans = Geist({
+ variable: '--font-geist-sans',
+ subsets: ['latin'],
+});
+
+const geistMono = Geist_Mono({
+ variable: '--font-geist-mono',
+ subsets: ['latin'],
+});
+
+export const metadata: Metadata = {
+ title: 'Workflow Demo',
+ description: 'Workflow Demo Next.js',
+};
+
+export default function RootLayout({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) {
+ return (
+
+
{children}
+
+ );
+}
diff --git a/apps/demo-nextjs-antd/src/app/page.tsx b/apps/demo-nextjs-antd/src/app/page.tsx
new file mode 100644
index 00000000..f9fb9a30
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/app/page.tsx
@@ -0,0 +1,6 @@
+'use client';
+import { EditorClient } from '@editor/index';
+
+export default function Home() {
+ return ;
+}
diff --git a/apps/demo-nextjs-antd/src/editor/assets/icon-auto-layout.tsx b/apps/demo-nextjs-antd/src/editor/assets/icon-auto-layout.tsx
new file mode 100644
index 00000000..6d1b1561
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/assets/icon-auto-layout.tsx
@@ -0,0 +1,8 @@
+export const IconAutoLayout = (
+
+);
diff --git a/apps/demo-nextjs-antd/src/editor/assets/icon-comment.tsx b/apps/demo-nextjs-antd/src/editor/assets/icon-comment.tsx
new file mode 100644
index 00000000..3eede9e6
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/assets/icon-comment.tsx
@@ -0,0 +1,19 @@
+import { CSSProperties, FC } from 'react';
+
+interface IconCommentProps {
+ style?: CSSProperties;
+}
+
+export const IconComment: FC = ({ style }) => (
+
+);
diff --git a/apps/demo-nextjs-antd/src/editor/assets/icon-condition.svg b/apps/demo-nextjs-antd/src/editor/assets/icon-condition.svg
new file mode 100644
index 00000000..be9c2eb4
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/assets/icon-condition.svg
@@ -0,0 +1,9 @@
+
diff --git a/apps/demo-nextjs-antd/src/editor/assets/icon-end.jpg b/apps/demo-nextjs-antd/src/editor/assets/icon-end.jpg
new file mode 100644
index 00000000..46bc47dd
Binary files /dev/null and b/apps/demo-nextjs-antd/src/editor/assets/icon-end.jpg differ
diff --git a/apps/demo-nextjs-antd/src/editor/assets/icon-llm.jpg b/apps/demo-nextjs-antd/src/editor/assets/icon-llm.jpg
new file mode 100644
index 00000000..4db9e0b7
Binary files /dev/null and b/apps/demo-nextjs-antd/src/editor/assets/icon-llm.jpg differ
diff --git a/apps/demo-nextjs-antd/src/editor/assets/icon-loop.jpg b/apps/demo-nextjs-antd/src/editor/assets/icon-loop.jpg
new file mode 100644
index 00000000..dc26db1e
Binary files /dev/null and b/apps/demo-nextjs-antd/src/editor/assets/icon-loop.jpg differ
diff --git a/apps/demo-nextjs-antd/src/editor/assets/icon-minimap.tsx b/apps/demo-nextjs-antd/src/editor/assets/icon-minimap.tsx
new file mode 100644
index 00000000..bd7f93e2
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/assets/icon-minimap.tsx
@@ -0,0 +1,19 @@
+export const IconMinimap = () => (
+
+);
diff --git a/apps/demo-nextjs-antd/src/editor/assets/icon-mouse.tsx b/apps/demo-nextjs-antd/src/editor/assets/icon-mouse.tsx
new file mode 100644
index 00000000..05bdddd8
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/assets/icon-mouse.tsx
@@ -0,0 +1,36 @@
+export function IconMouse(props: { width?: number; height?: number }) {
+ const { width, height } = props;
+ return (
+
+ );
+}
+
+export const IconMouseTool = () => (
+
+);
diff --git a/apps/demo-nextjs-antd/src/editor/assets/icon-pad.tsx b/apps/demo-nextjs-antd/src/editor/assets/icon-pad.tsx
new file mode 100644
index 00000000..e0382fde
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/assets/icon-pad.tsx
@@ -0,0 +1,51 @@
+export function IconPad(props: { width?: number; height?: number }) {
+ const { width, height } = props;
+ return (
+
+ );
+}
+
+export const IconPadTool = () => (
+
+);
diff --git a/apps/demo-nextjs-antd/src/editor/assets/icon-start.jpg b/apps/demo-nextjs-antd/src/editor/assets/icon-start.jpg
new file mode 100644
index 00000000..72a5a48e
Binary files /dev/null and b/apps/demo-nextjs-antd/src/editor/assets/icon-start.jpg differ
diff --git a/apps/demo-nextjs-antd/src/editor/assets/icon-switch-line.tsx b/apps/demo-nextjs-antd/src/editor/assets/icon-switch-line.tsx
new file mode 100644
index 00000000..d614e84c
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/assets/icon-switch-line.tsx
@@ -0,0 +1,10 @@
+export const IconSwitchLine = (
+
+);
diff --git a/apps/demo-nextjs-antd/src/editor/components/base-node/index.tsx b/apps/demo-nextjs-antd/src/editor/components/base-node/index.tsx
new file mode 100644
index 00000000..961b6fc8
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/base-node/index.tsx
@@ -0,0 +1,27 @@
+import { FlowNodeEntity, useNodeRender } from '@flowgram.ai/free-layout-editor';
+
+import { NodeRenderContext } from '@editor/context';
+import { ErrorIcon } from './styles';
+import { NodeWrapper } from './node-wrapper';
+
+export const BaseNode = ({ node }: { node: FlowNodeEntity }) => {
+ /**
+ * Provides methods related to node rendering
+ * 提供节点渲染相关的方法
+ */
+ const nodeRender = useNodeRender();
+ /**
+ * It can only be used when nodeEngine is enabled
+ * 只有在节点引擎开启时候才能使用表单
+ */
+ const form = nodeRender.form;
+
+ return (
+
+
+ {form?.state.invalid && }
+ {form?.render()}
+
+
+ );
+};
diff --git a/apps/demo-nextjs-antd/src/editor/components/base-node/node-wrapper.scss b/apps/demo-nextjs-antd/src/editor/components/base-node/node-wrapper.scss
new file mode 100644
index 00000000..c609ee57
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/base-node/node-wrapper.scss
@@ -0,0 +1,20 @@
+.node-wrapper {
+ align-items: flex-start;
+ background-color: #fff;
+ border: 1px solid rgba(6, 7, 9, 0.15);
+ border-radius: 8px;
+ box-shadow:
+ 0 2px 6px 0 rgba(0, 0, 0, 0.04),
+ 0 4px 12px 0 rgba(0, 0, 0, 0.02);
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ position: relative;
+ min-width: 360px;
+ width: 100%;
+ height: auto;
+
+ &.selected {
+ border: 1px solid #4e40e5;
+ }
+}
diff --git a/apps/demo-nextjs-antd/src/editor/components/base-node/node-wrapper.tsx b/apps/demo-nextjs-antd/src/editor/components/base-node/node-wrapper.tsx
new file mode 100644
index 00000000..459671ce
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/base-node/node-wrapper.tsx
@@ -0,0 +1,67 @@
+import React, { useContext, useState } from 'react';
+
+import { useClientContext, WorkflowPortRender } from '@flowgram.ai/free-layout-editor';
+
+import { SidebarContext } from '@editor/context';
+import { useNodeRenderContext } from '../../hooks';
+import { scrollToView } from './utils';
+import './node-wrapper.scss';
+
+// import { NodeWrapperStyle } from "./styles";
+
+export interface NodeWrapperProps {
+ isScrollToView?: boolean;
+ children: React.ReactNode;
+}
+
+/**
+ * Used for drag-and-drop/click events and ports rendering of nodes
+ * 用于节点的拖拽/点击事件和点位渲染
+ */
+export const NodeWrapper: React.FC = (props) => {
+ // IMPORTANT 这里写了如何处理node的数据
+ const { children, isScrollToView = false } = props;
+ const nodeRender = useNodeRenderContext();
+ const { selected, startDrag, ports, selectNode, nodeRef, onFocus, onBlur } = nodeRender;
+ const [isDragging, setIsDragging] = useState(false);
+ const sidebar = useContext(SidebarContext);
+ const form = nodeRender.form;
+ const ctx = useClientContext();
+
+ const portsRender = ports.map((p) => );
+
+ return (
+ <>
+ {
+ startDrag(e);
+ setIsDragging(true);
+ }}
+ onClick={(e) => {
+ selectNode(e);
+ if (!isDragging) {
+ sidebar.setNodeId(nodeRender.node.id);
+ // 可选:将 isScrollToView 设为 true,可以让节点选中后滚动到画布中间
+ // Optional: Set isScrollToView to true to scroll the node to the center of the canvas after it is selected.
+ if (isScrollToView) {
+ scrollToView(ctx, nodeRender.node);
+ }
+ }
+ }}
+ onMouseUp={() => setIsDragging(false)}
+ onFocus={onFocus}
+ onBlur={onBlur}
+ data-node-selected={String(selected)}
+ style={{
+ outline: form?.state.invalid ? '1px solid red' : 'none',
+ }}
+ >
+ {children}
+
+ {portsRender}
+ >
+ );
+};
diff --git a/apps/demo-nextjs-antd/src/editor/components/base-node/styles.tsx b/apps/demo-nextjs-antd/src/editor/components/base-node/styles.tsx
new file mode 100644
index 00000000..cf2dfada
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/base-node/styles.tsx
@@ -0,0 +1,9 @@
+import { ExclamationCircleOutlined } from '@ant-design/icons';
+
+export const ErrorIcon = () => (
+
+);
diff --git a/apps/demo-nextjs-antd/src/editor/components/base-node/utils.ts b/apps/demo-nextjs-antd/src/editor/components/base-node/utils.ts
new file mode 100644
index 00000000..3769e91a
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/base-node/utils.ts
@@ -0,0 +1,18 @@
+import { FlowNodeEntity, FreeLayoutPluginContext } from '@flowgram.ai/free-layout-editor';
+
+export function scrollToView(
+ ctx: FreeLayoutPluginContext,
+ node: FlowNodeEntity,
+ sidebarWidth = 448
+) {
+ const bounds = node.transform.bounds;
+ ctx.playground.scrollToView({
+ bounds,
+ scrollDelta: {
+ x: sidebarWidth / 2,
+ y: 0,
+ },
+ zoom: 1,
+ scrollToCenter: true,
+ });
+}
diff --git a/apps/demo-nextjs-antd/src/editor/components/editor-client.tsx b/apps/demo-nextjs-antd/src/editor/components/editor-client.tsx
new file mode 100644
index 00000000..90828c12
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/editor-client.tsx
@@ -0,0 +1,22 @@
+'use client';
+
+import { useEffect, useState } from 'react';
+
+import dynamic from 'next/dynamic';
+
+const Editor = dynamic(() => import('./editor').then((module) => module.Editor), { ssr: false });
+
+export const EditorClient = () => {
+ const [isMounted, setIsMounted] = useState(false);
+
+ useEffect(() => {
+ setIsMounted(true);
+ }, []);
+
+ if (!isMounted) {
+ // only render in browser client
+ return null;
+ }
+
+ return ;
+};
diff --git a/apps/demo-nextjs-antd/src/editor/components/editor.tsx b/apps/demo-nextjs-antd/src/editor/components/editor.tsx
new file mode 100644
index 00000000..f4ad94af
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/editor.tsx
@@ -0,0 +1,23 @@
+'use client';
+
+import { EditorRenderer, FreeLayoutEditorProvider } from '@flowgram.ai/free-layout-editor';
+
+import { SidebarProvider, SidebarRenderer } from '@editor/components/sidebar';
+import '@flowgram.ai/free-layout-editor/index.css';
+import { useEditorProps } from '../hooks/use-editor-props';
+import { nodeRegistries } from '../data/node-registries';
+import { initialData } from '../data/initial-data';
+import { Tools } from './tools';
+
+export const Editor = () => {
+ const editorProps = useEditorProps(initialData, nodeRegistries);
+ return (
+
+
+
+
+
+
+
+ );
+};
diff --git a/apps/demo-nextjs-antd/src/editor/components/form-render.tsx b/apps/demo-nextjs-antd/src/editor/components/form-render.tsx
new file mode 100644
index 00000000..60ad493f
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/form-render.tsx
@@ -0,0 +1,37 @@
+import { Field } from '@flowgram.ai/free-layout-editor';
+
+export const FormRender = () => (
+ <>
+
+ name="title">
+ {({ field }) => {field.value}
}
+
+
+
+ >
+);
diff --git a/apps/demo-nextjs-antd/src/editor/components/group/color.ts b/apps/demo-nextjs-antd/src/editor/components/group/color.ts
new file mode 100644
index 00000000..4a079036
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/group/color.ts
@@ -0,0 +1,100 @@
+type GroupColor = {
+ '50': string;
+ '300': string;
+ '400': string;
+};
+
+export const defaultColor = 'Blue';
+
+export const groupColors: Record = {
+ Red: {
+ '50': '#fef2f2',
+ '300': '#fca5a5',
+ '400': '#f87171',
+ },
+ Orange: {
+ '50': '#fff7ed',
+ '300': '#fdba74',
+ '400': '#fb923c',
+ },
+ Amber: {
+ '50': '#fffbeb',
+ '300': '#fcd34d',
+ '400': '#fbbf24',
+ },
+ Yellow: {
+ '50': '#fef9c3',
+ '300': '#fde047',
+ '400': '#facc15',
+ },
+ Lime: {
+ '50': '#f7fee7',
+ '300': '#bef264',
+ '400': '#a3e635',
+ },
+ Green: {
+ '50': '#f0fdf4',
+ '300': '#86efac',
+ '400': '#4ade80',
+ },
+ Emerald: {
+ '50': '#ecfdf5',
+ '300': '#6ee7b7',
+ '400': '#34d399',
+ },
+ Teal: {
+ '50': '#f0fdfa',
+ '300': '#5eead4',
+ '400': '#2dd4bf',
+ },
+ Cyan: {
+ '50': '#ecfeff',
+ '300': '#67e8f9',
+ '400': '#22d3ee',
+ },
+ Sky: {
+ '50': '#ecfeff',
+ '300': '#7dd3fc',
+ '400': '#38bdf8',
+ },
+ Blue: {
+ '50': '#eff6ff',
+ '300': '#93c5fd',
+ '400': '#60a5fa',
+ },
+ Indigo: {
+ '50': '#eef2ff',
+ '300': '#a5b4fc',
+ '400': '#818cf8',
+ },
+ Violet: {
+ '50': '#f5f3ff',
+ '300': '#c4b5fd',
+ '400': '#a78bfa',
+ },
+ Purple: {
+ '50': '#faf5ff',
+ '300': '#d8b4fe',
+ '400': '#c084fc',
+ },
+ Fuchsia: {
+ '50': '#fdf4ff',
+ '300': '#f0abfc',
+ '400': '#e879f9',
+ },
+ Pink: {
+ '50': '#fdf2f8',
+ '300': '#f9a8d4',
+ '400': '#f472b6',
+ },
+ Rose: {
+ '50': '#fff1f2',
+ '300': '#fda4af',
+ '400': '#fb7185',
+ },
+ Gray: {
+ '50': '#f9fafb',
+ '300': '#d1d5db',
+ '400': '#9ca3af',
+ },
+};
diff --git a/apps/demo-nextjs-antd/src/editor/components/group/components/background.tsx b/apps/demo-nextjs-antd/src/editor/components/group/components/background.tsx
new file mode 100644
index 00000000..dc8f2278
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/group/components/background.tsx
@@ -0,0 +1,49 @@
+import { CSSProperties, FC, useEffect } from 'react';
+
+import { WorkflowNodeEntity, useWatch } from '@flowgram.ai/free-layout-editor';
+
+import { GroupField } from '../constant';
+import { defaultColor, groupColors } from '../color';
+
+interface GroupBackgroundProps {
+ node: WorkflowNodeEntity;
+ style?: CSSProperties;
+}
+
+export const GroupBackground: FC = ({ node, style }) => {
+ const colorName = useWatch(GroupField.Color) ?? defaultColor;
+ const color = groupColors[colorName];
+
+ useEffect(() => {
+ const styleElement = document.createElement('style');
+
+ // 使用独特的选择器
+ const styleContent = `
+ .workflow-group-render[data-group-id="${node.id}"] .workflow-group-background {
+ border: 1px solid ${color['300']};
+ }
+
+ .workflow-group-render.selected[data-group-id="${node.id}"] .workflow-group-background {
+ border: 1px solid ${color['400']};
+ }
+ `;
+
+ styleElement.textContent = styleContent;
+ document.head.appendChild(styleElement);
+
+ return () => {
+ styleElement.remove();
+ };
+ }, [color]);
+
+ return (
+
+ );
+};
diff --git a/apps/demo-nextjs-antd/src/editor/components/group/components/color.tsx b/apps/demo-nextjs-antd/src/editor/components/group/components/color.tsx
new file mode 100644
index 00000000..d87e63b0
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/group/components/color.tsx
@@ -0,0 +1,45 @@
+import { FC } from 'react';
+
+import { Popover, Tooltip } from 'antd';
+import { Field } from '@flowgram.ai/free-layout-editor';
+
+import { GroupField } from '../constant';
+import { defaultColor, groupColors } from '../color';
+
+export const GroupColor: FC = () => (
+ name={GroupField.Color}>
+ {({ field }) => {
+ const colorName = field.value ?? defaultColor;
+ return (
+
+ {Object.entries(groupColors).map(([name, color]) => (
+
+ field.onChange(name)}
+ />
+
+ ))}
+
+ }
+ >
+
+
+ );
+ }}
+
+);
diff --git a/apps/demo-nextjs-antd/src/editor/components/group/components/header.tsx b/apps/demo-nextjs-antd/src/editor/components/group/components/header.tsx
new file mode 100644
index 00000000..d7278ad0
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/group/components/header.tsx
@@ -0,0 +1,41 @@
+import type { CSSProperties, FC, MouseEvent, ReactNode } from 'react';
+
+import { useWatch } from '@flowgram.ai/free-layout-editor';
+
+import { GroupField } from '../constant';
+import { defaultColor, groupColors } from '../color';
+
+interface GroupHeaderProps {
+ onMouseDown: (e: MouseEvent) => void;
+ onFocus: () => void;
+ onBlur: () => void;
+ children: ReactNode;
+ style?: CSSProperties;
+}
+
+export const GroupHeader: FC = ({
+ onMouseDown,
+ onFocus,
+ onBlur,
+ children,
+ style,
+}) => {
+ const colorName = useWatch(GroupField.Color) ?? defaultColor;
+ const color = groupColors[colorName];
+ return (
+
+ {children}
+
+ );
+};
diff --git a/apps/demo-nextjs-antd/src/editor/components/group/components/icon-group.tsx b/apps/demo-nextjs-antd/src/editor/components/group/components/icon-group.tsx
new file mode 100644
index 00000000..1a51409f
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/group/components/icon-group.tsx
@@ -0,0 +1,47 @@
+import { FC } from 'react';
+
+interface IconGroupProps {
+ size?: number;
+}
+
+export const IconGroup: FC = ({ size }) => (
+
+);
+
+export const IconUngroup: FC = ({ size }) => (
+
+);
diff --git a/apps/demo-nextjs-antd/src/editor/components/group/components/index.ts b/apps/demo-nextjs-antd/src/editor/components/group/components/index.ts
new file mode 100644
index 00000000..f217671f
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/group/components/index.ts
@@ -0,0 +1,2 @@
+export { GroupNodeRender } from './node-render';
+export { IconGroup } from './icon-group';
diff --git a/apps/demo-nextjs-antd/src/editor/components/group/components/node-render.tsx b/apps/demo-nextjs-antd/src/editor/components/group/components/node-render.tsx
new file mode 100644
index 00000000..b0775942
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/group/components/node-render.tsx
@@ -0,0 +1,75 @@
+import { useEffect } from 'react';
+
+import {
+ FlowNodeFormData,
+ Form,
+ FormModelV2,
+ useNodeRender,
+} from '@flowgram.ai/free-layout-editor';
+import { useNodeSize } from '@flowgram.ai/free-container-plugin';
+
+import { HEADER_HEIGHT, HEADER_PADDING } from '../constant';
+import { UngroupButton } from './ungroup';
+import { GroupTools } from './tools';
+import { GroupTips } from './tips';
+import { GroupHeader } from './header';
+import { GroupBackground } from './background';
+
+export const GroupNodeRender = () => {
+ const { node, selected, selectNode, nodeRef, startDrag, onFocus, onBlur } = useNodeRender();
+ const nodeSize = useNodeSize();
+ const formModel = node.getData(FlowNodeFormData).getFormModel();
+ const formControl = formModel?.formControl;
+
+ const { height, width } = nodeSize ?? {};
+ const nodeHeight = height ?? 0;
+
+ useEffect(() => {
+ // prevent lines in outside cannot be selected - 防止外层线条不可选中
+ const element = node.renderData.node;
+ element.style.pointerEvents = 'none';
+ }, [node]);
+
+ return (
+ {
+ selectNode(e);
+ }}
+ style={{
+ width,
+ height,
+ }}
+ >
+
+
+ );
+};
diff --git a/apps/demo-nextjs-antd/src/editor/components/group/components/tips/global-store.ts b/apps/demo-nextjs-antd/src/editor/components/group/components/tips/global-store.ts
new file mode 100644
index 00000000..b9a18c50
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/group/components/tips/global-store.ts
@@ -0,0 +1,33 @@
+/* eslint-disable @typescript-eslint/naming-convention -- no need */
+
+const STORAGE_KEY = 'workflow-move-into-group-tip-visible';
+const STORAGE_VALUE = 'false';
+
+export class TipsGlobalStore {
+ private static _instance?: TipsGlobalStore;
+
+ public static get instance(): TipsGlobalStore {
+ if (!this._instance) {
+ this._instance = new TipsGlobalStore();
+ }
+ return this._instance;
+ }
+
+ private closed = false;
+
+ public isClosed(): boolean {
+ return this.isCloseForever() || this.closed;
+ }
+
+ public close(): void {
+ this.closed = true;
+ }
+
+ public isCloseForever(): boolean {
+ return localStorage.getItem(STORAGE_KEY) === STORAGE_VALUE;
+ }
+
+ public closeForever(): void {
+ localStorage.setItem(STORAGE_KEY, STORAGE_VALUE);
+ }
+}
diff --git a/apps/demo-nextjs-antd/src/editor/components/group/components/tips/icon-close.tsx b/apps/demo-nextjs-antd/src/editor/components/group/components/tips/icon-close.tsx
new file mode 100644
index 00000000..366a3fbf
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/group/components/tips/icon-close.tsx
@@ -0,0 +1,9 @@
+export const IconClose = () => (
+
+);
diff --git a/apps/demo-nextjs-antd/src/editor/components/group/components/tips/index.tsx b/apps/demo-nextjs-antd/src/editor/components/group/components/tips/index.tsx
new file mode 100644
index 00000000..9fac353c
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/group/components/tips/index.tsx
@@ -0,0 +1,36 @@
+import { useControlTips } from './use-control';
+import { GroupTipsStyle } from './style';
+import { isMacOS } from './is-mac-os';
+import { IconClose } from './icon-close';
+
+export const GroupTips = () => {
+ const { visible, close, closeForever } = useControlTips();
+
+ if (!visible) {
+ return null;
+ }
+
+ return (
+
+
+
+
{`Hold ${isMacOS ? 'Cmd ⌘' : 'Ctrl'} to drag node out`}
+
+
+
+
+ Never Remind
+
+
+
+
+
+
+
+ );
+};
diff --git a/apps/demo-nextjs-antd/src/editor/components/group/components/tips/is-mac-os.ts b/apps/demo-nextjs-antd/src/editor/components/group/components/tips/is-mac-os.ts
new file mode 100644
index 00000000..d0bb76ae
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/group/components/tips/is-mac-os.ts
@@ -0,0 +1 @@
+export const isMacOS = /(Macintosh|MacIntel|MacPPC|Mac68K|iPad)/.test(navigator.userAgent);
diff --git a/apps/demo-nextjs-antd/src/editor/components/group/components/tips/style.ts b/apps/demo-nextjs-antd/src/editor/components/group/components/tips/style.ts
new file mode 100644
index 00000000..7e704b5d
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/group/components/tips/style.ts
@@ -0,0 +1,74 @@
+import styled from 'styled-components';
+
+export const GroupTipsStyle = styled.div`
+ position: absolute;
+ top: 35px;
+
+ width: 100%;
+ height: 28px;
+ white-space: nowrap;
+ pointer-events: auto;
+
+ .container {
+ display: inline-flex;
+ justify-content: center;
+ height: 100%;
+ width: 100%;
+ background-color: rgb(255 255 255);
+ border-radius: 8px 8px 0 0;
+
+ .content {
+ overflow: hidden;
+ display: inline-flex;
+ align-items: center;
+ justify-content: flex-start;
+
+ width: fit-content;
+ height: 100%;
+ padding: 0 12px;
+
+ .text {
+ font-size: 14px;
+ font-weight: 400;
+ font-style: normal;
+ line-height: 20px;
+ color: rgba(15, 21, 40, 82%);
+ text-overflow: ellipsis;
+ margin: 0;
+ }
+
+ .space {
+ width: 128px;
+ }
+ }
+
+ .actions {
+ display: flex;
+ gap: 8px;
+ align-items: center;
+
+ height: 28px;
+ padding: 0 12px;
+
+ .close-forever {
+ cursor: pointer;
+
+ padding: 0 3px;
+
+ font-size: 12px;
+ font-weight: 400;
+ font-style: normal;
+ line-height: 12px;
+ color: rgba(32, 41, 69, 62%);
+ margin: 0;
+ }
+
+ .close {
+ display: flex;
+ cursor: pointer;
+ height: 100%;
+ align-items: center;
+ }
+ }
+ }
+`;
diff --git a/apps/demo-nextjs-antd/src/editor/components/group/components/tips/use-control.ts b/apps/demo-nextjs-antd/src/editor/components/group/components/tips/use-control.ts
new file mode 100644
index 00000000..947ea2d9
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/group/components/tips/use-control.ts
@@ -0,0 +1,66 @@
+import { useCallback, useEffect, useState } from 'react';
+
+import { useCurrentEntity, useService } from '@flowgram.ai/free-layout-editor';
+import {
+ NodeIntoContainerService,
+ NodeIntoContainerType,
+} from '@flowgram.ai/free-container-plugin';
+
+import { TipsGlobalStore } from './global-store';
+
+export const useControlTips = () => {
+ const node = useCurrentEntity();
+ const [visible, setVisible] = useState(false);
+ const globalStore = TipsGlobalStore.instance;
+
+ const nodeIntoContainerService = useService(NodeIntoContainerService);
+
+ const show = useCallback(() => {
+ if (globalStore.isClosed()) {
+ return;
+ }
+
+ setVisible(true);
+ }, [globalStore]);
+
+ const close = useCallback(() => {
+ globalStore.close();
+ setVisible(false);
+ }, [globalStore]);
+
+ const closeForever = useCallback(() => {
+ globalStore.closeForever();
+ close();
+ }, [close, globalStore]);
+
+ useEffect(() => {
+ // 监听移入
+ const inDisposer = nodeIntoContainerService.on((e) => {
+ if (e.type !== NodeIntoContainerType.In) {
+ return;
+ }
+ if (e.targetContainer === node) {
+ show();
+ }
+ });
+ // 监听移出事件
+ const outDisposer = nodeIntoContainerService.on((e) => {
+ if (e.type !== NodeIntoContainerType.Out) {
+ return;
+ }
+ if (e.sourceContainer === node && !node.blocks.length) {
+ setVisible(false);
+ }
+ });
+ return () => {
+ inDisposer.dispose();
+ outDisposer.dispose();
+ };
+ }, [nodeIntoContainerService, node, show, close, visible]);
+
+ return {
+ visible,
+ close,
+ closeForever,
+ };
+};
diff --git a/apps/demo-nextjs-antd/src/editor/components/group/components/title.tsx b/apps/demo-nextjs-antd/src/editor/components/group/components/title.tsx
new file mode 100644
index 00000000..6371280f
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/group/components/title.tsx
@@ -0,0 +1,33 @@
+import { FC, useState } from 'react';
+
+import { Input } from 'antd';
+import { Field } from '@flowgram.ai/free-layout-editor';
+
+import { GroupField } from '../constant';
+
+export const GroupTitle: FC = () => {
+ const [inputting, setInputting] = useState(false);
+ return (
+ name={GroupField.Title}>
+ {({ field }) =>
+ inputting ? (
+ e.stopPropagation()}
+ onBlur={() => setInputting(false)}
+ draggable={false}
+ onSubmit={() => setInputting(false)}
+ />
+ ) : (
+ setInputting(true)}>
+ {field.value ?? 'Group'}
+
+ )
+ }
+
+ );
+};
diff --git a/apps/demo-nextjs-antd/src/editor/components/group/components/tools.tsx b/apps/demo-nextjs-antd/src/editor/components/group/components/tools.tsx
new file mode 100644
index 00000000..8d9dc2d1
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/group/components/tools.tsx
@@ -0,0 +1,14 @@
+import { FC } from 'react';
+
+import { HolderOutlined } from '@ant-design/icons';
+
+import { GroupTitle } from './title';
+import { GroupColor } from './color';
+
+export const GroupTools: FC = () => (
+
+
+
+
+
+);
diff --git a/apps/demo-nextjs-antd/src/editor/components/group/components/ungroup.tsx b/apps/demo-nextjs-antd/src/editor/components/group/components/ungroup.tsx
new file mode 100644
index 00000000..2b43704d
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/group/components/ungroup.tsx
@@ -0,0 +1,30 @@
+import { CSSProperties, FC } from 'react';
+
+import { Button, Tooltip } from 'antd';
+import { CommandRegistry, WorkflowNodeEntity, useService } from '@flowgram.ai/free-layout-editor';
+import { WorkflowGroupCommand } from '@flowgram.ai/free-group-plugin';
+
+import { IconUngroup } from './icon-group';
+
+interface UngroupButtonProps {
+ node: WorkflowNodeEntity;
+ style?: CSSProperties;
+}
+
+export const UngroupButton: FC = ({ node, style }) => {
+ const commandRegistry = useService(CommandRegistry);
+ return (
+
+
+ }
+ style={{ height: 30, width: 30 }}
+ type="text"
+ onClick={() => {
+ commandRegistry.executeCommand(WorkflowGroupCommand.Ungroup, node);
+ }}
+ />
+
+
+ );
+};
diff --git a/apps/demo-nextjs-antd/src/editor/components/group/constant.ts b/apps/demo-nextjs-antd/src/editor/components/group/constant.ts
new file mode 100644
index 00000000..e51a4a07
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/group/constant.ts
@@ -0,0 +1,7 @@
+export const HEADER_HEIGHT = 30;
+export const HEADER_PADDING = 5;
+
+export enum GroupField {
+ Title = 'title',
+ Color = 'color',
+}
diff --git a/apps/demo-nextjs-antd/src/editor/components/group/index.css b/apps/demo-nextjs-antd/src/editor/components/group/index.css
new file mode 100644
index 00000000..c594c53d
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/group/index.css
@@ -0,0 +1,109 @@
+.workflow-group-render {
+ border-radius: 8px;
+ pointer-events: none;
+}
+
+.workflow-group-header {
+ height: 30px;
+ width: fit-content;
+ background-color: #fefce8;
+ border: 1px solid #facc15;
+ border-radius: 8px;
+ padding-right: 8px;
+ pointer-events: auto;
+}
+
+.workflow-group-ungroup {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 30px;
+ width: 30px;
+ position: absolute;
+ top: 35px;
+ right: 0;
+ border-radius: 8px;
+ cursor: pointer;
+ pointer-events: auto;
+}
+
+.workflow-group-ungroup .ant-btn {
+ color: #9ca3af;
+}
+
+.workflow-group-ungroup:hover .ant-btn {
+ color: #374151;
+}
+
+.workflow-group-background {
+ position: absolute;
+ pointer-events: none;
+ top: 0;
+ background-color: #fddf4729;
+ border: 1px solid #fde047;
+ border-radius: 8px;
+ width: 100%;
+}
+
+.workflow-group-render.selected .workflow-group-background {
+ border: 1px solid #facc15;
+}
+
+.workflow-group-tools {
+ display: flex;
+ justify-content: flex-start;
+ align-items: center;
+ gap: 4px;
+ height: 100%;
+ cursor: move;
+ color: oklch(44.6% 0.043 257.281);
+ font-size: 14px;
+}
+.workflow-group-title {
+ margin: 0;
+ max-width: 242px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ font-weight: 500;
+}
+
+.workflow-group-tools-drag {
+ height: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding-left: 4px;
+}
+
+.workflow-group-color {
+ width: 16px;
+ height: 16px;
+ border-radius: 8px;
+ background-color: #fde047;
+ margin-left: 4px;
+ cursor: pointer;
+}
+
+.workflow-group-title-input {
+ width: 242px;
+ border: none;
+ color: #374151;
+}
+
+.workflow-group-color-palette {
+ display: grid;
+ grid-template-columns: repeat(6, 24px);
+ gap: 12px;
+ margin: 8px;
+ padding: 8px;
+}
+
+.workflow-group-color-item {
+ width: 24px;
+ height: 24px;
+ border-radius: 50%;
+ background-color: #fde047;
+ cursor: pointer;
+ border: 3px solid;
+}
diff --git a/apps/demo-nextjs-antd/src/editor/components/group/index.ts b/apps/demo-nextjs-antd/src/editor/components/group/index.ts
new file mode 100644
index 00000000..76a9f010
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/group/index.ts
@@ -0,0 +1,4 @@
+import './index.css';
+
+export { GroupNodeRender } from './components';
+export { IconGroup } from './components';
diff --git a/apps/demo-nextjs-antd/src/editor/components/index.ts b/apps/demo-nextjs-antd/src/editor/components/index.ts
new file mode 100644
index 00000000..62c8ac37
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/index.ts
@@ -0,0 +1,5 @@
+export * from './line-add-button';
+export * from './node-panel';
+export * from './node-comment';
+export * from './group';
+export * from './selector-box-popover';
diff --git a/apps/demo-nextjs-antd/src/editor/components/line-add-button/button.tsx b/apps/demo-nextjs-antd/src/editor/components/line-add-button/button.tsx
new file mode 100644
index 00000000..6bbe4948
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/line-add-button/button.tsx
@@ -0,0 +1,26 @@
+export const IconPlusCircle = () => (
+
+);
diff --git a/apps/demo-nextjs-antd/src/editor/components/line-add-button/index.scss b/apps/demo-nextjs-antd/src/editor/components/line-add-button/index.scss
new file mode 100644
index 00000000..cd48bdb0
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/line-add-button/index.scss
@@ -0,0 +1,8 @@
+.line-add-button {
+ position: absolute;
+ transform: translate(-50%, -60%);
+ width: 24px;
+ height: 24px;
+ cursor: pointer;
+ color: inherit;
+}
diff --git a/apps/demo-nextjs-antd/src/editor/components/line-add-button/index.tsx b/apps/demo-nextjs-antd/src/editor/components/line-add-button/index.tsx
new file mode 100644
index 00000000..18c46a44
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/line-add-button/index.tsx
@@ -0,0 +1,126 @@
+import { IconPlusCircle } from './button';
+import './index.scss';
+import { useVisible } from './use-visible';
+
+import {
+ HistoryService,
+ WorkflowDocument,
+ WorkflowDragService,
+ WorkflowLinesManager,
+ WorkflowNodeEntity,
+ WorkflowNodeJSON,
+ delay,
+ useService,
+} from '@flowgram.ai/free-layout-editor';
+import { LineRenderProps } from '@flowgram.ai/free-lines-plugin';
+import {
+ WorkflowNodePanelService,
+ WorkflowNodePanelUtils,
+} from '@flowgram.ai/free-node-panel-plugin';
+
+import { useCallback } from 'react';
+
+export const LineAddButton = (props: LineRenderProps) => {
+ const { line, selected, hovered, color } = props;
+ const visible = useVisible({ line, selected, hovered });
+ const nodePanelService = useService(WorkflowNodePanelService);
+ const document = useService(WorkflowDocument);
+ const dragService = useService(WorkflowDragService);
+ const linesManager = useService(WorkflowLinesManager);
+ const historyService = useService(HistoryService);
+
+ const { fromPort, toPort } = line;
+
+ const onClick = useCallback(async () => {
+ // calculate the middle point of the line - 计算线条的中点位置
+ const position = {
+ x: (line.position.from.x + line.position.to.x) / 2,
+ y: (line.position.from.y + line.position.to.y) / 2,
+ };
+
+ // get container node for the new node - 获取新节点的容器节点
+ const containerNode = WorkflowNodePanelUtils.getContainerNode({
+ fromPort,
+ });
+
+ // show node selection panel - 显示节点选择面板
+ const result = await nodePanelService.singleSelectNodePanel({
+ position,
+ containerNode,
+ panelProps: {
+ enableScrollClose: true,
+ },
+ });
+ if (!result) {
+ return;
+ }
+
+ const { nodeType, nodeJSON } = result;
+
+ // adjust position for the new node - 调整新节点的位置
+ const nodePosition = WorkflowNodePanelUtils.adjustNodePosition({
+ nodeType,
+ position,
+ fromPort,
+ toPort,
+ containerNode,
+ document,
+ dragService,
+ });
+
+ // create new workflow node - 创建新的工作流节点
+ const node: WorkflowNodeEntity = document.createWorkflowNodeByType(
+ nodeType,
+ nodePosition,
+ nodeJSON ?? ({} as WorkflowNodeJSON),
+ containerNode?.id
+ );
+
+ // auto offset subsequent nodes - 自动偏移后续节点
+ if (fromPort && toPort) {
+ WorkflowNodePanelUtils.subNodesAutoOffset({
+ node,
+ fromPort,
+ toPort,
+ containerNode,
+ historyService,
+ dragService,
+ linesManager,
+ });
+ }
+
+ // wait for node render - 等待节点渲染
+ await delay(20);
+
+ // build connection lines - 构建连接线
+ WorkflowNodePanelUtils.buildLine({
+ fromPort,
+ node,
+ toPort,
+ linesManager,
+ });
+
+ // remove original line - 移除原始线条
+ line.dispose();
+ }, []);
+
+ if (!visible) {
+ return <>>;
+ }
+
+ return (
+
+
+
+ );
+};
diff --git a/apps/demo-nextjs-antd/src/editor/components/line-add-button/use-visible.ts b/apps/demo-nextjs-antd/src/editor/components/line-add-button/use-visible.ts
new file mode 100644
index 00000000..29ade559
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/line-add-button/use-visible.ts
@@ -0,0 +1,22 @@
+import './index.scss';
+import { WorkflowLineEntity, usePlayground } from '@flowgram.ai/free-layout-editor';
+
+export const useVisible = (params: {
+ line: WorkflowLineEntity;
+ selected?: boolean;
+ hovered?: boolean;
+}): boolean => {
+ const playground = usePlayground();
+ const { line, selected = false, hovered } = params;
+ if (line.disposed) {
+ // 在 dispose 后,再去获取 line.to | line.from 会导致错误创建端口
+ return false;
+ }
+ if (playground.config.readonly) {
+ return false;
+ }
+ if (!selected && !hovered) {
+ return false;
+ }
+ return true;
+};
diff --git a/apps/demo-nextjs-antd/src/editor/components/node-comment/components/blank-area.tsx b/apps/demo-nextjs-antd/src/editor/components/node-comment/components/blank-area.tsx
new file mode 100644
index 00000000..9e24e48a
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/node-comment/components/blank-area.tsx
@@ -0,0 +1,43 @@
+import type { FC } from 'react';
+
+import { useNodeRender, usePlayground } from '@flowgram.ai/free-layout-editor';
+
+import type { CommentEditorModel } from '../model';
+import { DragArea } from './drag-area';
+
+interface IBlankArea {
+ model: CommentEditorModel;
+}
+
+export const BlankArea: FC = (props) => {
+ const { model } = props;
+ const playground = usePlayground();
+ const { selectNode } = useNodeRender();
+
+ return (
+ {
+ e.preventDefault();
+ e.stopPropagation();
+ model.setFocus(false);
+ selectNode(e);
+ playground.node.focus(); // 防止节点无法被删除
+ }}
+ onClick={(e) => {
+ model.setFocus(true);
+ model.selectEnd();
+ }}
+ >
+
+
+ );
+};
diff --git a/apps/demo-nextjs-antd/src/editor/components/node-comment/components/border-area.tsx b/apps/demo-nextjs-antd/src/editor/components/node-comment/components/border-area.tsx
new file mode 100644
index 00000000..b60f3f95
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/node-comment/components/border-area.tsx
@@ -0,0 +1,115 @@
+import { type FC } from 'react';
+
+import type { CommentEditorModel } from '../model';
+import { ResizeArea } from './resize-area';
+import { DragArea } from './drag-area';
+
+interface IBorderArea {
+ model: CommentEditorModel;
+ overflow: boolean;
+ onResize?: () => {
+ resizing: (delta: { top: number; right: number; bottom: number; left: number }) => void;
+ resizeEnd: () => void;
+ };
+}
+
+export const BorderArea: FC = (props) => {
+ const { model, overflow, onResize } = props;
+
+ return (
+
+ {/* 左边 */}
+
+ {/* 右边 */}
+
+ {/* 上边 */}
+
+ {/* 下边 */}
+
+ {/** 左上角 */}
+ ({ top: y, right: 0, bottom: 0, left: x })}
+ onResize={onResize}
+ />
+ {/** 右上角 */}
+ ({ top: y, right: x, bottom: 0, left: 0 })}
+ onResize={onResize}
+ />
+ {/** 右下角 */}
+ ({ top: 0, right: x, bottom: y, left: 0 })}
+ onResize={onResize}
+ />
+ {/** 左下角 */}
+ ({ top: 0, right: 0, bottom: y, left: x })}
+ onResize={onResize}
+ />
+
+ );
+};
diff --git a/apps/demo-nextjs-antd/src/editor/components/node-comment/components/container.tsx b/apps/demo-nextjs-antd/src/editor/components/node-comment/components/container.tsx
new file mode 100644
index 00000000..91de8631
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/node-comment/components/container.tsx
@@ -0,0 +1,45 @@
+import type { CSSProperties, FC, ReactNode } from 'react';
+
+interface ICommentContainer {
+ focused: boolean;
+ children?: ReactNode;
+ style?: React.CSSProperties;
+}
+
+export const CommentContainer: FC = (props) => {
+ const { focused, children, style } = props;
+
+ const scrollbarStyle = {
+ // 滚动条样式
+ scrollbarWidth: 'thin',
+ scrollbarColor: 'rgb(159 159 158 / 65%) transparent',
+ // 针对 WebKit 浏览器(如 Chrome、Safari)的样式
+ '&:WebkitScrollbar': {
+ width: '4px',
+ },
+ '&::WebkitScrollbarTrack': {
+ background: 'transparent',
+ },
+ '&::WebkitScrollbarThumb': {
+ backgroundColor: 'rgb(159 159 158 / 65%)',
+ borderRadius: '20px',
+ border: '2px solid transparent',
+ },
+ } as unknown as CSSProperties;
+
+ return (
+
+ {children}
+
+ );
+};
diff --git a/apps/demo-nextjs-antd/src/editor/components/node-comment/components/content-drag-area.tsx b/apps/demo-nextjs-antd/src/editor/components/node-comment/components/content-drag-area.tsx
new file mode 100644
index 00000000..b129207b
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/node-comment/components/content-drag-area.tsx
@@ -0,0 +1,89 @@
+import { type FC, type WheelEventHandler, useEffect, useState } from 'react';
+
+import { useNodeRender, usePlayground } from '@flowgram.ai/free-layout-editor';
+
+import type { CommentEditorModel } from '../model';
+import { DragArea } from './drag-area';
+
+interface IContentDragArea {
+ model: CommentEditorModel;
+ focused: boolean;
+ overflow: boolean;
+}
+
+export const ContentDragArea: FC = (props) => {
+ const { model, focused, overflow } = props;
+ const playground = usePlayground();
+ const { selectNode } = useNodeRender();
+
+ const [active, setActive] = useState(false);
+
+ useEffect(() => {
+ // 当编辑器失去焦点时,取消激活状态
+ if (!focused) {
+ setActive(false);
+ }
+ }, [focused]);
+
+ const handleWheel: WheelEventHandler = (e) => {
+ const editorElement = model.element;
+ if (active || !overflow || !editorElement) {
+ return;
+ }
+ e.stopPropagation();
+ const maxScroll = editorElement.scrollHeight - editorElement.clientHeight;
+ const newScrollTop = Math.min(Math.max(editorElement.scrollTop + e.deltaY, 0), maxScroll);
+ editorElement.scroll(0, newScrollTop);
+ };
+
+ const handleMouseDown = (mouseDownEvent: React.MouseEvent) => {
+ if (active) {
+ return;
+ }
+ mouseDownEvent.preventDefault();
+ mouseDownEvent.stopPropagation();
+ model.setFocus(false);
+ selectNode(mouseDownEvent);
+ playground.node.focus(); // 防止节点无法被删除
+
+ const startX = mouseDownEvent.clientX;
+ const startY = mouseDownEvent.clientY;
+
+ const handleMouseUp = (mouseMoveEvent: MouseEvent) => {
+ const deltaX = mouseMoveEvent.clientX - startX;
+ const deltaY = mouseMoveEvent.clientY - startY;
+ // 判断是拖拽还是点击
+ const delta = 5;
+ if (Math.abs(deltaX) < delta && Math.abs(deltaY) < delta) {
+ // 点击后隐藏
+ setActive(true);
+ }
+ document.removeEventListener('mouseup', handleMouseUp);
+ document.removeEventListener('click', handleMouseUp);
+ };
+
+ document.addEventListener('mouseup', handleMouseUp);
+ document.addEventListener('click', handleMouseUp);
+ };
+
+ return (
+
+
+
+ );
+};
diff --git a/apps/demo-nextjs-antd/src/editor/components/node-comment/components/drag-area.tsx b/apps/demo-nextjs-antd/src/editor/components/node-comment/components/drag-area.tsx
new file mode 100644
index 00000000..316e7b7b
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/node-comment/components/drag-area.tsx
@@ -0,0 +1,40 @@
+import { CSSProperties, type FC } from 'react';
+
+import { useNodeRender, usePlayground } from '@flowgram.ai/free-layout-editor';
+
+import { type CommentEditorModel } from '../model';
+
+interface IDragArea {
+ model: CommentEditorModel;
+ stopEvent?: boolean;
+ style?: CSSProperties;
+}
+
+export const DragArea: FC = (props) => {
+ const { model, stopEvent = true, style } = props;
+
+ const playground = usePlayground();
+
+ const { startDrag: onStartDrag, onFocus, onBlur, selectNode } = useNodeRender();
+
+ return (
+ {
+ if (stopEvent) {
+ e.preventDefault();
+ e.stopPropagation();
+ }
+ model.setFocus(false);
+ onStartDrag(e);
+ selectNode(e);
+ playground.node.focus(); // 防止节点无法被删除
+ }}
+ onFocus={onFocus}
+ onBlur={onBlur}
+ />
+ );
+};
diff --git a/apps/demo-nextjs-antd/src/editor/components/node-comment/components/editor.tsx b/apps/demo-nextjs-antd/src/editor/components/node-comment/components/editor.tsx
new file mode 100644
index 00000000..1f0749de
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/node-comment/components/editor.tsx
@@ -0,0 +1,60 @@
+import { type CSSProperties, type FC, useEffect, useRef } from 'react';
+
+import { usePlayground } from '@flowgram.ai/free-layout-editor';
+
+import { CommentEditorModel } from '../model';
+import { CommentEditorEvent } from '../constant';
+
+interface ICommentEditor {
+ model: CommentEditorModel;
+ style?: CSSProperties;
+ value?: string;
+ onChange?: (value: string) => void;
+}
+
+export const CommentEditor: FC
= (props) => {
+ const { model, style, onChange } = props;
+ const playground = usePlayground();
+ const editorRef = useRef(null);
+ const placeholder = model.value || model.focused ? undefined : 'Enter a comment...';
+
+ // 同步编辑器内部值变化
+ useEffect(() => {
+ const disposer = model.on((params) => {
+ if (params.type !== CommentEditorEvent.Change) {
+ return;
+ }
+ onChange?.(model.value);
+ });
+ return () => disposer.dispose();
+ }, [model, onChange]);
+
+ useEffect(() => {
+ if (!editorRef.current) {
+ return;
+ }
+ model.element = editorRef.current;
+ }, [editorRef]);
+
+ return (
+
+ );
+};
diff --git a/apps/demo-nextjs-antd/src/editor/components/node-comment/components/index.css b/apps/demo-nextjs-antd/src/editor/components/node-comment/components/index.css
new file mode 100644
index 00000000..c0366328
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/node-comment/components/index.css
@@ -0,0 +1,103 @@
+.workflow-comment {
+ width: auto;
+ height: auto;
+ min-width: 120px;
+ min-height: 80px;
+}
+
+.workflow-comment-container {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ justify-content: flex-start;
+ width: 100%;
+ height: 100%;
+ border-radius: 8px;
+ outline: 1px solid;
+ padding: 6px 2px 6px 10px;
+ overflow: hidden;
+}
+
+.workflow-comment-drag-area {
+ position: absolute;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: move;
+}
+
+.workflow-comment-content-drag-area {
+ position: absolute;
+ height: 100%;
+ width: calc(100% - 22px);
+}
+
+.workflow-comment-resize-area {
+ position: absolute;
+ width: 10px;
+ height: 10px;
+}
+
+.workflow-comment-editor {
+ width: 100%;
+ height: 100%;
+}
+
+.workflow-comment-editor-placeholder {
+ margin: 0;
+ position: absolute;
+ pointer-events: none;
+ color: rgba(55, 67, 106, 0.38);
+ font-weight: 500;
+}
+
+.workflow-comment-editor-textarea {
+ width: 100%;
+ height: 100%;
+ box-sizing: border-box;
+ appearance: none;
+ border: none;
+ margin: 0;
+ padding: 0;
+ width: 100%;
+ background: none;
+ color: inherit;
+ font-family: inherit;
+ font-size: 16px;
+ resize: none;
+ outline: none;
+}
+
+.workflow-comment-more-button {
+ position: absolute;
+ right: 6px;
+}
+
+.workflow-comment-more-button > .ant-btn {
+ color: rgba(255, 255, 255, 0);
+ background: none;
+}
+
+.workflow-comment-more-button > .ant-btn:hover {
+ color: #ffa100;
+ background: #fbf2d2cc;
+ backdrop-filter: blur(1px);
+}
+
+.workflow-comment-more-button-focused > .ant-btn:hover {
+ color: #ff811a;
+ background: #ffe3cecc;
+ backdrop-filter: blur(1px);
+}
+
+.workflow-comment-more-button > .ant-btn:active {
+ color: #f2b600;
+ background: #ede5c7cc;
+ backdrop-filter: blur(1px);
+}
+
+.workflow-comment-more-button-focused > .ant-btn:active {
+ color: #ff811a;
+ background: #eed5c1cc;
+ backdrop-filter: blur(1px);
+}
diff --git a/apps/demo-nextjs-antd/src/editor/components/node-comment/components/index.ts b/apps/demo-nextjs-antd/src/editor/components/node-comment/components/index.ts
new file mode 100644
index 00000000..1666338e
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/node-comment/components/index.ts
@@ -0,0 +1,3 @@
+import './index.css';
+
+export { CommentRender } from './render';
diff --git a/apps/demo-nextjs-antd/src/editor/components/node-comment/components/more-button.tsx b/apps/demo-nextjs-antd/src/editor/components/node-comment/components/more-button.tsx
new file mode 100644
index 00000000..4da1e843
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/node-comment/components/more-button.tsx
@@ -0,0 +1,23 @@
+'use client';
+
+import { FC } from 'react';
+
+import { WorkflowNodeEntity } from '@flowgram.ai/free-layout-editor';
+
+import { NodeMenu } from '@editor/components/node-menu';
+
+interface IMoreButton {
+ node: WorkflowNodeEntity;
+ focused: boolean;
+ deleteNode: () => void;
+}
+
+export const MoreButton: FC = ({ node, focused, deleteNode }) => (
+
+ {}} />
+
+);
diff --git a/apps/demo-nextjs-antd/src/editor/components/node-comment/components/render.tsx b/apps/demo-nextjs-antd/src/editor/components/node-comment/components/render.tsx
new file mode 100644
index 00000000..c4773ef4
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/node-comment/components/render.tsx
@@ -0,0 +1,78 @@
+import { FC } from 'react';
+
+import {
+ Field,
+ FieldRenderProps,
+ FlowNodeFormData,
+ Form,
+ FormModelV2,
+ WorkflowNodeEntity,
+ useNodeRender,
+} from '@flowgram.ai/free-layout-editor';
+
+import { useOverflow } from '../hooks/use-overflow';
+import { useModel } from '../hooks/use-model';
+import { useSize } from '../hooks';
+import { CommentEditorFormField } from '../constant';
+import { MoreButton } from './more-button';
+import { CommentEditor } from './editor';
+import { ContentDragArea } from './content-drag-area';
+import { CommentContainer } from './container';
+import { BorderArea } from './border-area';
+
+export const CommentRender: FC<{
+ node: WorkflowNodeEntity;
+}> = (props) => {
+ const { node } = props;
+ const model = useModel();
+
+ const { selected: focused, selectNode, nodeRef, deleteNode } = useNodeRender();
+
+ const formModel = node.getData(FlowNodeFormData).getFormModel();
+ const formControl = formModel?.formControl;
+
+ const { width, height, onResize } = useSize();
+ const { overflow, updateOverflow } = useOverflow({ model, height });
+
+ return (
+ {
+ setTimeout(() => {
+ // 防止 selectNode 拦截事件,导致 slate 编辑器无法聚焦
+ selectNode(e);
+ // eslint-disable-next-line @typescript-eslint/no-magic-numbers -- delay
+ }, 20);
+ }}
+ >
+
+
+ );
+};
diff --git a/apps/demo-nextjs-antd/src/editor/components/node-comment/components/resize-area.tsx b/apps/demo-nextjs-antd/src/editor/components/node-comment/components/resize-area.tsx
new file mode 100644
index 00000000..8f12745e
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/node-comment/components/resize-area.tsx
@@ -0,0 +1,73 @@
+import { CSSProperties, type FC } from 'react';
+
+import { useNodeRender, usePlayground } from '@flowgram.ai/free-layout-editor';
+
+import type { CommentEditorModel } from '../model';
+
+interface IResizeArea {
+ model: CommentEditorModel;
+ onResize?: () => {
+ resizing: (delta: { top: number; right: number; bottom: number; left: number }) => void;
+ resizeEnd: () => void;
+ };
+ getDelta?: (delta: { x: number; y: number }) => {
+ top: number;
+ right: number;
+ bottom: number;
+ left: number;
+ };
+ style?: CSSProperties;
+}
+
+export const ResizeArea: FC = (props) => {
+ const { model, onResize, getDelta, style } = props;
+
+ const playground = usePlayground();
+
+ const { selectNode } = useNodeRender();
+
+ const handleMouseDown = (mouseDownEvent: React.MouseEvent) => {
+ mouseDownEvent.preventDefault();
+ mouseDownEvent.stopPropagation();
+ if (!onResize) {
+ return;
+ }
+ const { resizing, resizeEnd } = onResize();
+ model.setFocus(false);
+ selectNode(mouseDownEvent);
+ playground.node.focus(); // 防止节点无法被删除
+
+ const startX = mouseDownEvent.clientX;
+ const startY = mouseDownEvent.clientY;
+
+ const handleMouseMove = (mouseMoveEvent: MouseEvent) => {
+ const deltaX = mouseMoveEvent.clientX - startX;
+ const deltaY = mouseMoveEvent.clientY - startY;
+ const delta = getDelta?.({ x: deltaX, y: deltaY });
+ if (!delta || !resizing) {
+ return;
+ }
+ resizing(delta);
+ };
+
+ const handleMouseUp = () => {
+ resizeEnd();
+ document.removeEventListener('mousemove', handleMouseMove);
+ document.removeEventListener('mouseup', handleMouseUp);
+ document.removeEventListener('click', handleMouseUp);
+ };
+
+ document.addEventListener('mousemove', handleMouseMove);
+ document.addEventListener('mouseup', handleMouseUp);
+ document.addEventListener('click', handleMouseUp);
+ };
+
+ return (
+
+ );
+};
diff --git a/apps/demo-nextjs-antd/src/editor/components/node-comment/constant.ts b/apps/demo-nextjs-antd/src/editor/components/node-comment/constant.ts
new file mode 100644
index 00000000..159b42e3
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/node-comment/constant.ts
@@ -0,0 +1,20 @@
+/* eslint-disable @typescript-eslint/naming-convention -- enum */
+
+export enum CommentEditorFormField {
+ Size = 'size',
+ Note = 'note',
+}
+
+/** 编辑器事件 */
+export enum CommentEditorEvent {
+ /** 内容变更事件 */
+ Change = 'change',
+ /** 多选事件 */
+ MultiSelect = 'multiSelect',
+ /** 单选事件 */
+ Select = 'select',
+ /** 失焦事件 */
+ Blur = 'blur',
+}
+
+export const CommentEditorDefaultValue = '';
diff --git a/apps/demo-nextjs-antd/src/editor/components/node-comment/hooks/index.ts b/apps/demo-nextjs-antd/src/editor/components/node-comment/hooks/index.ts
new file mode 100644
index 00000000..cc260e37
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/node-comment/hooks/index.ts
@@ -0,0 +1 @@
+export { useSize } from './use-size';
diff --git a/apps/demo-nextjs-antd/src/editor/components/node-comment/hooks/use-model.ts b/apps/demo-nextjs-antd/src/editor/components/node-comment/hooks/use-model.ts
new file mode 100644
index 00000000..5787c784
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/node-comment/hooks/use-model.ts
@@ -0,0 +1,50 @@
+import { useEffect, useMemo } from 'react';
+
+import {
+ FlowNodeFormData,
+ FormModelV2,
+ WorkflowNodeEntity,
+ useEntityFromContext,
+ useNodeRender,
+} from '@flowgram.ai/free-layout-editor';
+
+import { CommentEditorModel } from '../model';
+import { CommentEditorFormField } from '../constant';
+
+export const useModel = () => {
+ const node = useEntityFromContext();
+ const { selected: focused } = useNodeRender();
+
+ const formModel = node.getData(FlowNodeFormData).getFormModel();
+
+ const model = useMemo(() => new CommentEditorModel(), []);
+
+ // 同步失焦状态
+ useEffect(() => {
+ if (focused) {
+ return;
+ }
+ model.setFocus(focused);
+ }, [focused, model]);
+
+ // 同步表单值初始化
+ useEffect(() => {
+ const value = formModel.getValueIn(CommentEditorFormField.Note);
+ model.setValue(value); // 设置初始值
+ model.selectEnd(); // 设置初始化光标位置
+ }, [formModel, model]);
+
+ // 同步表单外部值变化:undo/redo/协同
+ useEffect(() => {
+ const disposer = formModel.onFormValuesChange(({ name }) => {
+ if (name !== CommentEditorFormField.Note) {
+ return;
+ }
+ const value = formModel.getValueIn(CommentEditorFormField.Note);
+ model.setValue(value);
+ });
+ return () => disposer.dispose();
+ }, [formModel, model]);
+
+ return model;
+};
diff --git a/apps/demo-nextjs-antd/src/editor/components/node-comment/hooks/use-overflow.ts b/apps/demo-nextjs-antd/src/editor/components/node-comment/hooks/use-overflow.ts
new file mode 100644
index 00000000..a0762c20
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/node-comment/hooks/use-overflow.ts
@@ -0,0 +1,45 @@
+import { useCallback, useEffect, useState } from 'react';
+
+import { usePlayground } from '@flowgram.ai/free-layout-editor';
+
+import { CommentEditorModel } from '../model';
+import { CommentEditorEvent } from '../constant';
+
+export const useOverflow = (params: { model: CommentEditorModel; height: number }) => {
+ const { model, height } = params;
+ const playground = usePlayground();
+
+ const [overflow, setOverflow] = useState(false);
+
+ const isOverflow = useCallback((): boolean => {
+ if (!model.element) {
+ return false;
+ }
+ return model.element.scrollHeight > model.element.clientHeight;
+ }, [model, height, playground]);
+
+ // 更新 overflow
+ const updateOverflow = useCallback(() => {
+ setOverflow(isOverflow());
+ }, [isOverflow]);
+
+ // 监听高度变化
+ useEffect(() => {
+ updateOverflow();
+ }, [height, updateOverflow]);
+
+ // 监听 change 事件
+ useEffect(() => {
+ const changeDisposer = model.on((params) => {
+ if (params.type !== CommentEditorEvent.Change) {
+ return;
+ }
+ updateOverflow();
+ });
+ return () => {
+ changeDisposer.dispose();
+ };
+ }, [model, updateOverflow]);
+
+ return { overflow, updateOverflow };
+};
diff --git a/apps/demo-nextjs-antd/src/editor/components/node-comment/hooks/use-size.ts b/apps/demo-nextjs-antd/src/editor/components/node-comment/hooks/use-size.ts
new file mode 100644
index 00000000..7309c533
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/node-comment/hooks/use-size.ts
@@ -0,0 +1,163 @@
+import { useCallback, useEffect, useState } from 'react';
+
+import {
+ FlowNodeFormData,
+ FormModelV2,
+ FreeOperationType,
+ HistoryService,
+ TransformData,
+ useCurrentEntity,
+ usePlayground,
+ useService,
+} from '@flowgram.ai/free-layout-editor';
+
+import { CommentEditorFormField } from '../constant';
+
+export const useSize = () => {
+ const node = useCurrentEntity();
+ const nodeMeta = node.getNodeMeta();
+ const playground = usePlayground();
+ const historyService = useService(HistoryService);
+ const { size = { width: 240, height: 150 } } = nodeMeta;
+ const transform = node.getData(TransformData);
+ const formModel = node.getData(FlowNodeFormData).getFormModel();
+ const formSize = formModel.getValueIn<{ width: number; height: number }>(
+ CommentEditorFormField.Size
+ );
+
+ const [width, setWidth] = useState(formSize?.width ?? size.width);
+ const [height, setHeight] = useState(formSize?.height ?? size.height);
+
+ // 初始化表单值
+ useEffect(() => {
+ const initSize = formModel.getValueIn<{ width: number; height: number }>(
+ CommentEditorFormField.Size
+ );
+ if (!initSize) {
+ formModel.setValueIn(CommentEditorFormField.Size, {
+ width,
+ height,
+ });
+ }
+ }, [formModel, width, height]);
+
+ // 同步表单外部值变化:初始化/undo/redo/协同
+ useEffect(() => {
+ const disposer = formModel.onFormValuesChange(({ name }) => {
+ if (name !== CommentEditorFormField.Size) {
+ return;
+ }
+ const newSize = formModel.getValueIn<{ width: number; height: number }>(
+ CommentEditorFormField.Size
+ );
+ if (!newSize) {
+ return;
+ }
+ setWidth(newSize.width);
+ setHeight(newSize.height);
+ });
+ return () => disposer.dispose();
+ }, [formModel]);
+
+ const onResize = useCallback(() => {
+ const resizeState = {
+ width,
+ height,
+ originalWidth: width,
+ originalHeight: height,
+ positionX: transform.position.x,
+ positionY: transform.position.y,
+ offsetX: 0,
+ offsetY: 0,
+ };
+ const resizing = (delta: { top: number; right: number; bottom: number; left: number }) => {
+ if (!resizeState) {
+ return;
+ }
+
+ const { zoom } = playground.config;
+
+ const top = delta.top / zoom;
+ const right = delta.right / zoom;
+ const bottom = delta.bottom / zoom;
+ const left = delta.left / zoom;
+
+ const minWidth = 120;
+ const minHeight = 80;
+
+ const newWidth = Math.max(minWidth, resizeState.originalWidth + right - left);
+ const newHeight = Math.max(minHeight, resizeState.originalHeight + bottom - top);
+
+ // 如果宽度或高度小于最小值,则不更新偏移量
+ const newOffsetX =
+ (left > 0 || right < 0) && newWidth <= minWidth
+ ? resizeState.offsetX
+ : left / 2 + right / 2;
+ const newOffsetY =
+ (top > 0 || bottom < 0) && newHeight <= minHeight ? resizeState.offsetY : top;
+
+ const newPositionX = resizeState.positionX + newOffsetX;
+ const newPositionY = resizeState.positionY + newOffsetY;
+
+ resizeState.width = newWidth;
+ resizeState.height = newHeight;
+ resizeState.offsetX = newOffsetX;
+ resizeState.offsetY = newOffsetY;
+
+ // 更新状态
+ setWidth(newWidth);
+ setHeight(newHeight);
+
+ // 更新偏移量
+ transform.update({
+ position: {
+ x: newPositionX,
+ y: newPositionY,
+ },
+ });
+ };
+
+ const resizeEnd = () => {
+ historyService.transact(() => {
+ historyService.pushOperation(
+ {
+ type: FreeOperationType.dragNodes,
+ value: {
+ ids: [node.id],
+ value: [
+ {
+ x: resizeState.positionX + resizeState.offsetX,
+ y: resizeState.positionY + resizeState.offsetY,
+ },
+ ],
+ oldValue: [
+ {
+ x: resizeState.positionX,
+ y: resizeState.positionY,
+ },
+ ],
+ },
+ },
+ {
+ noApply: true,
+ }
+ );
+ formModel.setValueIn(CommentEditorFormField.Size, {
+ width: resizeState.width,
+ height: resizeState.height,
+ });
+ });
+ };
+
+ return {
+ resizing,
+ resizeEnd,
+ };
+ }, [node, width, height, transform, playground, formModel, historyService]);
+
+ return {
+ width,
+ height,
+ onResize,
+ };
+};
diff --git a/apps/demo-nextjs-antd/src/editor/components/node-comment/index.ts b/apps/demo-nextjs-antd/src/editor/components/node-comment/index.ts
new file mode 100644
index 00000000..d840f324
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/node-comment/index.ts
@@ -0,0 +1 @@
+export { CommentRender } from './components';
diff --git a/apps/demo-nextjs-antd/src/editor/components/node-comment/model.ts b/apps/demo-nextjs-antd/src/editor/components/node-comment/model.ts
new file mode 100644
index 00000000..ce594949
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/node-comment/model.ts
@@ -0,0 +1,106 @@
+import { Emitter } from '@flowgram.ai/free-layout-editor';
+
+import { CommentEditorEventParams } from './type';
+import { CommentEditorDefaultValue, CommentEditorEvent } from './constant';
+
+export class CommentEditorModel {
+ private innerValue: string = CommentEditorDefaultValue;
+
+ private emitter: Emitter = new Emitter();
+
+ private editor: HTMLTextAreaElement;
+
+ /** 注册事件 */
+ public on = this.emitter.event;
+
+ /** 获取当前值 */
+ public get value(): string {
+ return this.innerValue;
+ }
+
+ /** 外部设置模型值 */
+ public setValue(value: string = CommentEditorDefaultValue): void {
+ if (!this.initialized) {
+ return;
+ }
+ if (value === this.innerValue) {
+ return;
+ }
+ this.innerValue = value;
+ this.syncEditorValue();
+ this.emitter.fire({
+ type: CommentEditorEvent.Change,
+ value: this.innerValue,
+ });
+ }
+
+ public set element(el: HTMLTextAreaElement) {
+ if (this.initialized) {
+ return;
+ }
+ this.editor = el;
+ }
+
+ /** 获取编辑器 DOM 节点 */
+ public get element(): HTMLTextAreaElement {
+ return this.editor;
+ }
+
+ /** 编辑器聚焦/失焦 */
+ public setFocus(focused: boolean): void {
+ if (!this.initialized) {
+ return;
+ }
+ if (focused && !this.focused) {
+ this.editor.focus();
+ } else if (!focused && this.focused) {
+ this.editor.blur();
+ this.deselect();
+ this.emitter.fire({
+ type: CommentEditorEvent.Blur,
+ });
+ }
+ }
+
+ /** 选择末尾 */
+ public selectEnd(): void {
+ if (!this.initialized) {
+ return;
+ }
+ // 获取文本长度
+ const length = this.editor.value.length;
+ // 将选择范围设置为文本末尾(开始位置和结束位置都是文本长度)
+ this.editor.setSelectionRange(length, length);
+ }
+
+ /** 获取聚焦状态 */
+ public get focused(): boolean {
+ return document.activeElement === this.editor;
+ }
+
+ /** 取消选择文本 */
+ private deselect(): void {
+ const selection: Selection | null = window.getSelection();
+
+ // 清除所有选择区域
+ if (selection) {
+ selection.removeAllRanges();
+ }
+ }
+
+ /** 是否初始化 */
+ private get initialized(): boolean {
+ return Boolean(this.editor);
+ }
+
+ /**
+ * 同步编辑器实例内容
+ * > **NOTICE:** *为确保不影响性能,应仅在外部值变更导致编辑器值与模型值不一致时调用*
+ */
+ private syncEditorValue(): void {
+ if (!this.initialized) {
+ return;
+ }
+ this.editor.value = this.innerValue;
+ }
+}
diff --git a/apps/demo-nextjs-antd/src/editor/components/node-comment/type.ts b/apps/demo-nextjs-antd/src/editor/components/node-comment/type.ts
new file mode 100644
index 00000000..3de297cf
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/node-comment/type.ts
@@ -0,0 +1,24 @@
+import type { CommentEditorEvent } from './constant';
+
+interface CommentEditorChangeEvent {
+ type: CommentEditorEvent.Change;
+ value: string;
+}
+
+interface CommentEditorMultiSelectEvent {
+ type: CommentEditorEvent.MultiSelect;
+}
+
+interface CommentEditorSelectEvent {
+ type: CommentEditorEvent.Select;
+}
+
+interface CommentEditorBlurEvent {
+ type: CommentEditorEvent.Blur;
+}
+
+export type CommentEditorEventParams =
+ | CommentEditorChangeEvent
+ | CommentEditorMultiSelectEvent
+ | CommentEditorSelectEvent
+ | CommentEditorBlurEvent;
diff --git a/apps/demo-nextjs-antd/src/editor/components/node-menu/index.tsx b/apps/demo-nextjs-antd/src/editor/components/node-menu/index.tsx
new file mode 100644
index 00000000..f794c52a
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/node-menu/index.tsx
@@ -0,0 +1,125 @@
+import { FC, type MouseEvent, useCallback, useState } from 'react';
+
+import { Button, Dropdown } from 'antd';
+import {
+ WorkflowDragService,
+ WorkflowNodeEntity,
+ WorkflowSelectService,
+ delay,
+ useClientContext,
+ useService,
+} from '@flowgram.ai/free-layout-editor';
+import { NodeIntoContainerService } from '@flowgram.ai/free-container-plugin';
+import { EllipsisOutlined } from '@ant-design/icons';
+
+import { FlowNodeRegistry } from '@editor/typings';
+import { PasteShortcut } from '@editor/shortcuts/paste';
+import { CopyShortcut } from '@editor/shortcuts/copy';
+
+interface NodeMenuProps {
+ node: WorkflowNodeEntity;
+ updateTitleEdit: (setEditing: boolean) => void;
+ deleteNode: () => void;
+}
+
+export const NodeMenu: FC = ({ node, deleteNode, updateTitleEdit }) => {
+ const [visible, setVisible] = useState(true);
+ const clientContext = useClientContext();
+ const registry = node.getNodeRegistry();
+ const nodeIntoContainerService = useService(NodeIntoContainerService);
+ const selectService = useService(WorkflowSelectService);
+ const dragService = useService(WorkflowDragService);
+ const canMoveOut = nodeIntoContainerService.canMoveOutContainer(node);
+
+ const rerenderMenu = useCallback(() => {
+ // force destroy component - 强制销毁组件触发重新渲染
+ setVisible(false);
+ requestAnimationFrame(() => {
+ setVisible(true);
+ });
+ }, []);
+
+ const handleMoveOut = useCallback(
+ async (e: MouseEvent) => {
+ e.stopPropagation();
+ const sourceParent = node.parent;
+ // move out of container - 移出容器
+ nodeIntoContainerService.moveOutContainer({ node });
+ // clear invalid lines - 清除非法线条
+ await nodeIntoContainerService.clearInvalidLines({
+ dragNode: node,
+ sourceParent,
+ });
+ rerenderMenu();
+ await delay(16);
+ // select node - 选中节点
+ selectService.selectNode(node);
+ // start drag node - 开始拖拽
+ dragService.startDragSelectedNodes(e);
+ },
+ [nodeIntoContainerService, node, rerenderMenu]
+ );
+
+ const handleCopy = useCallback(
+ (e: React.MouseEvent) => {
+ const copyShortcut = new CopyShortcut(clientContext);
+ const pasteShortcut = new PasteShortcut(clientContext);
+ const data = copyShortcut.toClipboardData([node]);
+ pasteShortcut.apply(data);
+ e.stopPropagation(); // Disable clicking prevents the sidebar from opening
+ },
+ [clientContext, node]
+ );
+
+ const handleDelete = useCallback(
+ (e: React.MouseEvent) => {
+ deleteNode();
+ e.stopPropagation(); // Disable clicking prevents the sidebar from opening
+ },
+ [clientContext, node]
+ );
+ const handleEditTitle = useCallback(() => {
+ updateTitleEdit(true);
+ }, [updateTitleEdit]);
+
+ if (!visible) {
+ return <>>;
+ }
+
+ return (
+ Edit Title,
+ key: 'editTitle',
+ },
+ {
+ label: Move out,
+ key: 'moveOut',
+ disabled: !canMoveOut,
+ },
+ {
+ label: Create Copy,
+ key: 'createCopy',
+ disabled: registry.meta!.copyDisable === true,
+ },
+ {
+ label: Delete,
+ key: 'delete',
+ disabled: !!(registry.canDelete?.(clientContext, node) || registry.meta!.deleteDisable),
+ },
+ ],
+ }}
+ >
+ }
+ onClick={(e) => e.stopPropagation()}
+ />
+
+ );
+};
diff --git a/apps/demo-nextjs-antd/src/editor/components/node-panel/index.scss b/apps/demo-nextjs-antd/src/editor/components/node-panel/index.scss
new file mode 100644
index 00000000..f45c45ca
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/node-panel/index.scss
@@ -0,0 +1,44 @@
+.node-placeholder {
+ width: 360px;
+
+ background-color: rgba(252, 252, 255, 1);
+ border: 1px solid rgba(68, 83, 130, 0.25);
+ border-radius: 8px;
+ box-shadow:
+ 0 4px 12px 0 rgba(0, 0, 0, 2%),
+ 0 2px 6px 0 rgba(0, 0, 0, 4%);
+}
+
+.node-placeholder-skeleton {
+ width: 100%;
+ padding: 12px;
+ background-color: rgba(252, 252, 255, 1);
+ border-radius: 8px;
+}
+
+.node-placeholder-hd {
+ display: flex;
+ align-items: center;
+ margin-bottom: 12px;
+}
+
+.node-placeholder-avatar {
+ width: 24px;
+ height: 24px;
+ margin-right: 8px;
+ border-radius: 6px;
+}
+
+.node-placeholder-content {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 3px;
+}
+
+.node-placeholder-footer {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 2.5px;
+}
diff --git a/apps/demo-nextjs-antd/src/editor/components/node-panel/index.tsx b/apps/demo-nextjs-antd/src/editor/components/node-panel/index.tsx
new file mode 100644
index 00000000..e5ef6a92
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/node-panel/index.tsx
@@ -0,0 +1,50 @@
+import './index.scss';
+import { FC } from 'react';
+
+import { Popover } from 'antd';
+import { NodePanelRenderProps } from '@flowgram.ai/free-node-panel-plugin';
+
+import { NodePlaceholder } from './node-placeholder';
+import { NodeList } from './node-list';
+
+export const NodePanel: FC = (props) => {
+ const { onSelect, position, onClose, panelProps } = props;
+ // @ts-ignore
+ const { enableNodePlaceholder } = panelProps;
+
+ return (
+ (v ? null : onClose())}
+ content={}
+ placement="right"
+ // popupAlign={{ offset: [30, 0] }}
+ overlayStyle={{
+ padding: 0,
+ }}
+ >
+
+ {enableNodePlaceholder && }
+
+
+ );
+};
diff --git a/apps/demo-nextjs-antd/src/editor/components/node-panel/node-list.tsx b/apps/demo-nextjs-antd/src/editor/components/node-panel/node-list.tsx
new file mode 100644
index 00000000..a3058120
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/node-panel/node-list.tsx
@@ -0,0 +1,90 @@
+import React, { FC } from 'react';
+
+import styled from 'styled-components';
+import { NodePanelRenderProps } from '@flowgram.ai/free-node-panel-plugin';
+import { useClientContext } from '@flowgram.ai/free-layout-editor';
+
+import { FlowNodeRegistry } from '@editor/typings';
+
+// import { visibleNodeRegistries } from '../../nodes';
+
+const NodeWrap = styled.div`
+ width: 100%;
+ height: 32px;
+ border-radius: 5px;
+ display: flex;
+ align-items: center;
+ cursor: pointer;
+ font-size: 19px;
+ padding: 0 15px;
+ &:hover {
+ background-color: hsl(252deg 62% 55% / 9%);
+ color: hsl(252 62% 54.9%);
+ }
+`;
+
+const NodeLabel = styled.div`
+ font-size: 12px;
+ margin-left: 10px;
+`;
+
+interface NodeProps {
+ label: string;
+ icon: JSX.Element;
+ onClick: React.MouseEventHandler;
+ disabled: boolean;
+}
+
+function Node(props: NodeProps) {
+ return (
+
+ {props.icon}
+ {props.label}
+
+ );
+}
+
+const NodesWrap = styled.div`
+ max-height: 500px;
+ overflow: auto;
+ &::-webkit-scrollbar {
+ display: none;
+ }
+`;
+
+interface NodeListProps {
+ onSelect: NodePanelRenderProps['onSelect'];
+ visibleNodeRegistries: FlowNodeRegistry[];
+}
+
+export const NodeList: FC = (props) => {
+ const { onSelect } = props;
+ const context = useClientContext();
+ const handleClick = (e: React.MouseEvent, registry: FlowNodeRegistry) => {
+ const json = registry.onAdd?.(context);
+ onSelect({
+ nodeType: registry.type as string,
+ selectEvent: e,
+ nodeJSON: json,
+ });
+ };
+ return (
+
+ {props.visibleNodeRegistries.map((registry) => (
+
+ }
+ label={registry.type as string}
+ onClick={(e) => handleClick(e, registry)}
+ />
+ ))}
+
+ );
+};
diff --git a/apps/demo-nextjs-antd/src/editor/components/node-panel/node-placeholder.tsx b/apps/demo-nextjs-antd/src/editor/components/node-panel/node-placeholder.tsx
new file mode 100644
index 00000000..5becd1de
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/node-panel/node-placeholder.tsx
@@ -0,0 +1,26 @@
+import { Skeleton } from 'antd';
+
+export const NodePlaceholder = () => (
+
+
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ // }
+ />
+
+);
diff --git a/apps/demo-nextjs-antd/src/editor/components/node-render.tsx b/apps/demo-nextjs-antd/src/editor/components/node-render.tsx
new file mode 100644
index 00000000..9f0cd82d
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/node-render.tsx
@@ -0,0 +1,23 @@
+import classnames from 'classnames';
+import {
+ WorkflowNodeProps,
+ WorkflowNodeRenderer,
+ useNodeRender,
+} from '@flowgram.ai/free-layout-editor';
+
+export const NodeRender = (props: WorkflowNodeProps) => {
+ const { form, selected } = useNodeRender();
+ return (
+
+ {form?.render()}
+
+ );
+};
diff --git a/apps/demo-nextjs-antd/src/editor/components/selector-box-popover/index.tsx b/apps/demo-nextjs-antd/src/editor/components/selector-box-popover/index.tsx
new file mode 100644
index 00000000..99e0c0a1
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/selector-box-popover/index.tsx
@@ -0,0 +1,98 @@
+import { FunctionComponent } from 'react';
+
+import { Button, Tooltip } from 'antd';
+import { SelectorBoxPopoverProps } from '@flowgram.ai/free-layout-editor';
+import { WorkflowGroupCommand } from '@flowgram.ai/free-group-plugin';
+import { CopyOutlined, DeleteOutlined, ExpandAltOutlined, ShrinkOutlined } from '@ant-design/icons';
+
+import { FlowCommandId } from '@editor/shortcuts/constants';
+import { IconGroup } from '../group';
+
+const BUTTON_HEIGHT = 24;
+
+export const SelectorBoxPopover: FunctionComponent = ({
+ bounds,
+ children,
+ flowSelectConfig,
+ commandRegistry,
+}) => (
+ <>
+ {
+ e.stopPropagation();
+ }}
+ >
+ {/* */}
+
+ }
+ style={{ height: BUTTON_HEIGHT }}
+ type="primary"
+ // theme="solid"
+ onMouseDown={(e) => {
+ commandRegistry.executeCommand(FlowCommandId.COLLAPSE);
+ }}
+ />
+
+
+
+ }
+ style={{ height: BUTTON_HEIGHT }}
+ type="primary"
+ // theme="solid"
+ onMouseDown={(e) => {
+ commandRegistry.executeCommand(FlowCommandId.EXPAND);
+ }}
+ />
+
+
+
+ }
+ style={{ height: BUTTON_HEIGHT }}
+ type="primary"
+ // theme="solid"
+ onClick={() => {
+ commandRegistry.executeCommand(WorkflowGroupCommand.Group);
+ }}
+ />
+
+
+
+ }
+ style={{ height: BUTTON_HEIGHT }}
+ type="primary"
+ // theme="solid"
+ onClick={() => {
+ commandRegistry.executeCommand(FlowCommandId.COPY);
+ }}
+ />
+
+
+
+ }
+ style={{ height: BUTTON_HEIGHT }}
+ onClick={() => {
+ commandRegistry.executeCommand(FlowCommandId.DELETE);
+ }}
+ />
+
+ {/* */}
+
+ {children}
+ >
+);
diff --git a/apps/demo-nextjs-antd/src/editor/components/sidebar/index.tsx b/apps/demo-nextjs-antd/src/editor/components/sidebar/index.tsx
new file mode 100644
index 00000000..2afd32dc
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/sidebar/index.tsx
@@ -0,0 +1,2 @@
+export { SidebarProvider } from './sidebar-provider';
+export { SidebarRenderer } from './sidebar-renderer';
diff --git a/apps/demo-nextjs-antd/src/editor/components/sidebar/sidebar-node-renderer.tsx b/apps/demo-nextjs-antd/src/editor/components/sidebar/sidebar-node-renderer.tsx
new file mode 100644
index 00000000..1b8ba346
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/sidebar/sidebar-node-renderer.tsx
@@ -0,0 +1,14 @@
+import { FlowNodeEntity, useNodeRender } from '@flowgram.ai/free-layout-editor';
+
+import { NodeRenderContext } from '@editor/context';
+
+export function SidebarNodeRenderer(props: { node: FlowNodeEntity }) {
+ const { node } = props;
+ const nodeRender = useNodeRender(node);
+
+ return (
+
+ {nodeRender.form?.render()}
+
+ );
+}
diff --git a/apps/demo-nextjs-antd/src/editor/components/sidebar/sidebar-provider.tsx b/apps/demo-nextjs-antd/src/editor/components/sidebar/sidebar-provider.tsx
new file mode 100644
index 00000000..cc42be4f
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/sidebar/sidebar-provider.tsx
@@ -0,0 +1,12 @@
+import { useState } from 'react';
+
+import { SidebarContext } from '@editor/context';
+
+export function SidebarProvider({ children }: { children: React.ReactNode }) {
+ const [nodeId, setNodeId] = useState();
+ return (
+
+ {children}
+
+ );
+}
diff --git a/apps/demo-nextjs-antd/src/editor/components/sidebar/sidebar-renderer.tsx b/apps/demo-nextjs-antd/src/editor/components/sidebar/sidebar-renderer.tsx
new file mode 100644
index 00000000..3f76692c
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/sidebar/sidebar-renderer.tsx
@@ -0,0 +1,88 @@
+import { useCallback, useContext, useEffect, useMemo } from 'react';
+
+import { Drawer } from 'antd';
+import {
+ PlaygroundEntityContext,
+ useClientContext,
+ useRefresh,
+} from '@flowgram.ai/free-layout-editor';
+
+import { FlowNodeMeta } from '@editor/typings';
+import { IsSidebarContext, SidebarContext } from '@editor/context';
+import { SidebarNodeRenderer } from './sidebar-node-renderer';
+
+export const SidebarRenderer = () => {
+ const { nodeId, setNodeId } = useContext(SidebarContext);
+ const { selection, playground, document } = useClientContext();
+ const refresh = useRefresh();
+ const handleClose = useCallback(() => {
+ setNodeId(undefined);
+ }, []);
+ const node = nodeId ? document.getNode(nodeId) : undefined;
+ /**
+ * Listen readonly
+ */
+ useEffect(() => {
+ const disposable = playground.config.onReadonlyOrDisabledChange(() => {
+ handleClose();
+ refresh();
+ });
+ return () => disposable.dispose();
+ }, [playground]);
+ /**
+ * Listen selection
+ */
+ useEffect(() => {
+ const toDispose = selection.onSelectionChanged(() => {
+ /**
+ * 如果没有选中任何节点,则自动关闭侧边栏
+ * If no node is selected, the sidebar is automatically closed
+ */
+ if (selection.selection.length === 0) {
+ handleClose();
+ } else if (selection.selection.length === 1 && selection.selection[0] !== node) {
+ handleClose();
+ }
+ });
+ return () => toDispose.dispose();
+ }, [selection, handleClose, node]);
+ /**
+ * Close when node disposed
+ */
+ useEffect(() => {
+ if (node) {
+ const toDispose = node.onDispose(() => {
+ setNodeId(undefined);
+ });
+ return () => toDispose.dispose();
+ }
+ return () => {};
+ }, [node]);
+
+ const visible = useMemo(() => {
+ if (!node) {
+ return false;
+ }
+ const { sidebarDisable = false } = node.getNodeMeta();
+ return !sidebarDisable;
+ }, [node]);
+
+ if (playground.config.readonly) {
+ return null;
+ }
+ /**
+ * Add "key" to rerender the sidebar when the node changes
+ */
+ const content =
+ node && visible ? (
+
+
+
+ ) : null;
+
+ return (
+
+ {content}
+
+ );
+};
diff --git a/apps/demo-nextjs-antd/src/editor/components/tools.tsx b/apps/demo-nextjs-antd/src/editor/components/tools.tsx
new file mode 100644
index 00000000..9f0334c8
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/components/tools.tsx
@@ -0,0 +1,47 @@
+{
+ /* TODO */
+}
+
+// import { WorkflowDocument, useService } from '@flowgram.ai/free-layout-editor';
+// import { useState } from 'react';
+
+export const Tools = () => (
+ // const [isLoading, setIsLoading] = useState(false);
+ // const document = useService(WorkflowDocument);
+
+ // const handleRun = async () => {
+ // try {
+ // setIsLoading(true);
+ // const response = await fetch('/api/runtime', {
+ // method: 'POST',
+ // headers: {
+ // 'Content-Type': 'application/json',
+ // },
+ // body: JSON.stringify({
+ // json: document.toJSON(),
+ // }),
+ // });
+ // const data = await response.json();
+
+ // if (!data.success) {
+ // throw new Error(data.error || 'process failed');
+ // }
+
+ // console.log('run success', data.data);
+ // } catch (error) {
+ // console.error(error instanceof Error ? error.message : 'run failed');
+ // } finally {
+ // setIsLoading(false);
+ // }
+ // };
+
+
+);
diff --git a/apps/demo-nextjs-antd/src/editor/context/index.ts b/apps/demo-nextjs-antd/src/editor/context/index.ts
new file mode 100644
index 00000000..ca6c0067
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/context/index.ts
@@ -0,0 +1,2 @@
+export { NodeRenderContext } from './node-render-context';
+export { SidebarContext, IsSidebarContext } from './sidebar-context';
diff --git a/apps/demo-nextjs-antd/src/editor/context/node-render-context.ts b/apps/demo-nextjs-antd/src/editor/context/node-render-context.ts
new file mode 100644
index 00000000..926016b2
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/context/node-render-context.ts
@@ -0,0 +1,8 @@
+import React from 'react';
+
+import type { NodeRenderReturnType } from '@flowgram.ai/free-layout-editor';
+
+interface INodeRenderContext extends NodeRenderReturnType {}
+
+/** 业务自定义节点上下文 */
+export const NodeRenderContext = React.createContext({} as INodeRenderContext);
diff --git a/apps/demo-nextjs-antd/src/editor/context/sidebar-context.ts b/apps/demo-nextjs-antd/src/editor/context/sidebar-context.ts
new file mode 100644
index 00000000..4f8f3158
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/context/sidebar-context.ts
@@ -0,0 +1,9 @@
+import React from 'react';
+
+export const SidebarContext = React.createContext<{
+ visible: boolean;
+ nodeId?: string;
+ setNodeId: (node: string | undefined) => void;
+}>({ visible: false, setNodeId: () => {} });
+
+export const IsSidebarContext = React.createContext(false);
diff --git a/apps/demo-nextjs-antd/src/editor/data/initial-data.ts b/apps/demo-nextjs-antd/src/editor/data/initial-data.ts
new file mode 100644
index 00000000..3815d4f5
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/data/initial-data.ts
@@ -0,0 +1,439 @@
+import { WorkflowJSON } from '@flowgram.ai/free-layout-editor';
+
+export const initialData: WorkflowJSON = {
+ nodes: [
+ {
+ id: 'start_0',
+ type: 'start',
+ meta: {
+ position: {
+ x: 180,
+ y: 381.75,
+ },
+ },
+ data: {
+ title: 'Start',
+ outputs: {
+ type: 'object',
+ properties: {
+ query: {
+ type: 'string',
+ default: 'Hello Flow.',
+ },
+ enable: {
+ type: 'boolean',
+ default: true,
+ },
+ array_obj: {
+ type: 'array',
+ items: {
+ type: 'object',
+ properties: {
+ int: {
+ type: 'number',
+ },
+ str: {
+ type: 'string',
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ id: 'condition_0',
+ type: 'condition',
+ meta: {
+ position: {
+ x: 640,
+ y: 363.25,
+ },
+ },
+ data: {
+ title: 'Condition',
+ conditions: [
+ {
+ key: 'if_0',
+ value: {
+ left: {
+ type: 'ref',
+ content: ['start_0', 'query'],
+ },
+ operator: 'contains',
+ right: {
+ type: 'constant',
+ content: 'Hello Flow.',
+ },
+ },
+ },
+ {
+ key: 'if_f0rOAt',
+ value: {
+ left: {
+ type: 'ref',
+ content: ['start_0', 'enable'],
+ },
+ operator: 'is_true',
+ },
+ },
+ ],
+ },
+ },
+ {
+ id: 'end_0',
+ type: 'end',
+ meta: {
+ position: {
+ x: 2220,
+ y: 381.75,
+ },
+ },
+ data: {
+ title: 'End',
+ outputs: {
+ type: 'object',
+ properties: {
+ result: {
+ type: 'string',
+ },
+ },
+ },
+ },
+ },
+ {
+ id: 'loop_H8M3U',
+ type: 'loop',
+ meta: {
+ position: {
+ x: 1020,
+ y: 547.96875,
+ },
+ },
+ data: {
+ title: 'Loop_2',
+ batchFor: {
+ type: 'ref',
+ content: ['start_0', 'array_obj'],
+ },
+ outputs: {
+ type: 'object',
+ properties: {
+ result: {
+ type: 'string',
+ },
+ },
+ },
+ },
+ blocks: [
+ {
+ id: 'llm_CBdCg',
+ type: 'llm',
+ meta: {
+ position: {
+ x: 180,
+ y: 0,
+ },
+ },
+ data: {
+ title: 'LLM_4',
+ inputsValues: {
+ modelType: {
+ type: 'constant',
+ content: 'gpt-3.5-turbo',
+ },
+ temperature: {
+ type: 'constant',
+ content: 0.5,
+ },
+ systemPrompt: {
+ type: 'constant',
+ content: 'You are an AI assistant.',
+ },
+ prompt: {
+ type: 'constant',
+ content: '',
+ },
+ },
+ inputs: {
+ type: 'object',
+ required: ['modelType', 'temperature', 'prompt'],
+ properties: {
+ modelType: {
+ type: 'string',
+ },
+ temperature: {
+ type: 'number',
+ },
+ systemPrompt: {
+ type: 'string',
+ },
+ prompt: {
+ type: 'string',
+ },
+ },
+ },
+ outputs: {
+ type: 'object',
+ properties: {
+ result: {
+ type: 'string',
+ },
+ },
+ },
+ },
+ },
+ {
+ id: 'llm_gZafu',
+ type: 'llm',
+ meta: {
+ position: {
+ x: 640,
+ y: 0,
+ },
+ },
+ data: {
+ title: 'LLM_5',
+ inputsValues: {
+ modelType: {
+ type: 'constant',
+ content: 'gpt-3.5-turbo',
+ },
+ temperature: {
+ type: 'constant',
+ content: 0.5,
+ },
+ systemPrompt: {
+ type: 'constant',
+ content: 'You are an AI assistant.',
+ },
+ prompt: {
+ type: 'constant',
+ content: '',
+ },
+ },
+ inputs: {
+ type: 'object',
+ required: ['modelType', 'temperature', 'prompt'],
+ properties: {
+ modelType: {
+ type: 'string',
+ },
+ temperature: {
+ type: 'number',
+ },
+ systemPrompt: {
+ type: 'string',
+ },
+ prompt: {
+ type: 'string',
+ },
+ },
+ },
+ outputs: {
+ type: 'object',
+ properties: {
+ result: {
+ type: 'string',
+ },
+ },
+ },
+ },
+ },
+ ],
+ edges: [
+ {
+ sourceNodeID: 'llm_CBdCg',
+ targetNodeID: 'llm_gZafu',
+ },
+ ],
+ },
+ {
+ id: '159623',
+ type: 'comment',
+ meta: {
+ position: {
+ x: 640,
+ y: 522.46875,
+ },
+ },
+ data: {
+ size: {
+ width: 240,
+ height: 150,
+ },
+ note: 'hi ~\n\nthis is a comment node\n\n- flowgram.ai',
+ },
+ },
+ {
+ id: 'group_V-_st',
+ type: 'group',
+ meta: {
+ position: {
+ x: 1020,
+ y: 96.25,
+ },
+ },
+ data: {
+ title: 'LLM_Group',
+ color: 'Violet',
+ },
+ blocks: [
+ {
+ id: 'llm_0',
+ type: 'llm',
+ meta: {
+ position: {
+ x: 640,
+ y: 0,
+ },
+ },
+ data: {
+ title: 'LLM_0',
+ inputsValues: {
+ modelType: {
+ type: 'constant',
+ content: 'gpt-3.5-turbo',
+ },
+ temperature: {
+ type: 'constant',
+ content: 0.5,
+ },
+ systemPrompt: {
+ type: 'constant',
+ content: 'You are an AI assistant.',
+ },
+ prompt: {
+ type: 'constant',
+ content: '',
+ },
+ },
+ inputs: {
+ type: 'object',
+ required: ['modelType', 'temperature', 'prompt'],
+ properties: {
+ modelType: {
+ type: 'string',
+ },
+ temperature: {
+ type: 'number',
+ },
+ systemPrompt: {
+ type: 'string',
+ },
+ prompt: {
+ type: 'string',
+ },
+ },
+ },
+ outputs: {
+ type: 'object',
+ properties: {
+ result: {
+ type: 'string',
+ },
+ },
+ },
+ },
+ },
+ {
+ id: 'llm_l_TcE',
+ type: 'llm',
+ meta: {
+ position: {
+ x: 180,
+ y: 0,
+ },
+ },
+ data: {
+ title: 'LLM_1',
+ inputsValues: {
+ modelType: {
+ type: 'constant',
+ content: 'gpt-3.5-turbo',
+ },
+ temperature: {
+ type: 'constant',
+ content: 0.5,
+ },
+ systemPrompt: {
+ type: 'constant',
+ content: 'You are an AI assistant.',
+ },
+ prompt: {
+ type: 'constant',
+ content: '',
+ },
+ },
+ inputs: {
+ type: 'object',
+ required: ['modelType', 'temperature', 'prompt'],
+ properties: {
+ modelType: {
+ type: 'string',
+ },
+ temperature: {
+ type: 'number',
+ },
+ systemPrompt: {
+ type: 'string',
+ },
+ prompt: {
+ type: 'string',
+ },
+ },
+ },
+ outputs: {
+ type: 'object',
+ properties: {
+ result: {
+ type: 'string',
+ },
+ },
+ },
+ },
+ },
+ ],
+ edges: [
+ {
+ sourceNodeID: 'llm_l_TcE',
+ targetNodeID: 'llm_0',
+ },
+ {
+ sourceNodeID: 'llm_0',
+ targetNodeID: 'end_0',
+ },
+ {
+ sourceNodeID: 'condition_0',
+ targetNodeID: 'llm_l_TcE',
+ sourcePortID: 'if_0',
+ },
+ ],
+ },
+ ],
+ edges: [
+ {
+ sourceNodeID: 'start_0',
+ targetNodeID: 'condition_0',
+ },
+ {
+ sourceNodeID: 'condition_0',
+ targetNodeID: 'llm_l_TcE',
+ sourcePortID: 'if_0',
+ },
+ {
+ sourceNodeID: 'condition_0',
+ targetNodeID: 'loop_H8M3U',
+ sourcePortID: 'if_f0rOAt',
+ },
+ {
+ sourceNodeID: 'llm_0',
+ targetNodeID: 'end_0',
+ },
+ {
+ sourceNodeID: 'loop_H8M3U',
+ targetNodeID: 'end_0',
+ },
+ ],
+};
diff --git a/apps/demo-nextjs-antd/src/editor/data/node-registries.ts b/apps/demo-nextjs-antd/src/editor/data/node-registries.ts
new file mode 100644
index 00000000..84726008
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/data/node-registries.ts
@@ -0,0 +1,44 @@
+'use client';
+
+import { FlowNodeRegistry } from '@editor/typings';
+import { StartNodeRegistry } from '@editor/nodes/start';
+import { LoopNodeRegistry } from '@editor/nodes/loop';
+import { LLMNodeRegistry } from '@editor/nodes/llm';
+import { EndNodeRegistry } from '@editor/nodes/end';
+import { ConditionNodeRegistry } from '@editor/nodes/condition';
+import { CommentNodeRegistry } from '@editor/nodes/comment';
+
+/**
+ * You can customize your own node registry
+ * 你可以自定义节点的注册器
+ */
+export const nodeRegistries: FlowNodeRegistry[] = [
+ ConditionNodeRegistry,
+ StartNodeRegistry,
+ EndNodeRegistry,
+ LLMNodeRegistry,
+ LoopNodeRegistry,
+ CommentNodeRegistry,
+ {
+ type: 'start2',
+ meta: {
+ isStart: true, // Mark as start
+ deleteDisable: true, // The start node cannot be deleted
+ copyDisable: true, // The start node cannot be copied
+ defaultPorts: [{ type: 'output' }], // Used to define the input and output ports, the start node only has the output port
+ },
+ },
+ {
+ type: 'end',
+ meta: {
+ deleteDisable: true,
+ copyDisable: true,
+ defaultPorts: [{ type: 'input' }],
+ },
+ },
+ {
+ type: 'custom',
+ meta: {},
+ defaultPorts: [{ type: 'output' }, { type: 'input' }], // A normal node has two ports
+ },
+];
diff --git a/apps/demo-nextjs-antd/src/editor/form-components/feedback.tsx b/apps/demo-nextjs-antd/src/editor/form-components/feedback.tsx
new file mode 100644
index 00000000..46802b65
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/form-components/feedback.tsx
@@ -0,0 +1,35 @@
+import styled from 'styled-components';
+import { FieldError, FieldState, FieldWarning } from '@flowgram.ai/free-layout-editor';
+
+interface StatePanelProps {
+ errors?: FieldState['errors'];
+ warnings?: FieldState['warnings'];
+ invalid?: boolean;
+}
+
+const Error = styled.span`
+ font-size: 12px;
+ color: red;
+`;
+
+const Warning = styled.span`
+ font-size: 12px;
+ color: orange;
+`;
+
+export const Feedback = ({ errors, warnings, invalid }: StatePanelProps) => {
+ const renderFeedbacks = (fs: FieldError[] | FieldWarning[] | undefined) => {
+ if (!fs) return null;
+ return fs.map((f) => {f.message});
+ };
+ return (
+
+
+ {renderFeedbacks(errors)}
+
+
+ {renderFeedbacks(warnings)}
+
+
+ );
+};
diff --git a/apps/demo-nextjs-antd/src/editor/form-components/form-content/index.tsx b/apps/demo-nextjs-antd/src/editor/form-components/form-content/index.tsx
new file mode 100644
index 00000000..5bb8c686
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/form-components/form-content/index.tsx
@@ -0,0 +1,26 @@
+'use client';
+
+import React from 'react';
+
+import { FlowNodeRegistry } from '@flowgram.ai/free-layout-editor';
+
+import { useIsSidebar, useNodeRenderContext } from '@editor/hooks';
+import { FormTitleDescription, FormWrapper } from './styles';
+
+/**
+ * @param props
+ * @constructor
+ */
+export function FormContent(props: { children?: React.ReactNode }) {
+ const { node, expanded } = useNodeRenderContext();
+ const isSidebar = useIsSidebar();
+ const registry = node?.getNodeRegistry();
+ return (
+
+ <>
+ {isSidebar && {registry.info?.description}}
+ {(expanded || isSidebar) && props.children}
+ >
+
+ );
+}
diff --git a/apps/demo-nextjs-antd/src/editor/form-components/form-content/styles.tsx b/apps/demo-nextjs-antd/src/editor/form-components/form-content/styles.tsx
new file mode 100644
index 00000000..9f81f119
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/form-components/form-content/styles.tsx
@@ -0,0 +1,19 @@
+import styled from 'styled-components';
+
+export const FormWrapper = styled.div`
+ box-sizing: border-box;
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+ background-color: rgba(0, 0, 0, 0.02);
+ padding: 0 12px 12px;
+`;
+
+export const FormTitleDescription = styled.div`
+ font-size: 12px;
+ line-height: 20px;
+ padding: 0px 4px;
+ word-break: break-all;
+ white-space: break-spaces;
+`;
diff --git a/apps/demo-nextjs-antd/src/editor/form-components/form-header/index.scss b/apps/demo-nextjs-antd/src/editor/form-components/form-header/index.scss
new file mode 100644
index 00000000..3122c77d
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/form-components/form-header/index.scss
@@ -0,0 +1,10 @@
+.node-form-header {
+ &-title {
+ font-size: 20px;
+ flex: 1;
+ width: 0;
+ .title-text {
+ display: flex;
+ }
+ }
+}
diff --git a/apps/demo-nextjs-antd/src/editor/form-components/form-header/index.tsx b/apps/demo-nextjs-antd/src/editor/form-components/form-header/index.tsx
new file mode 100644
index 00000000..6c2e6aa3
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/form-components/form-header/index.tsx
@@ -0,0 +1,49 @@
+'use client';
+
+import './index.scss';
+import { useState } from 'react';
+
+import { Button } from 'antd';
+import { CommandService, useClientContext } from '@flowgram.ai/free-layout-editor';
+import { CaretDownOutlined, CaretUpOutlined } from '@ant-design/icons';
+
+import { FlowCommandId } from '@editor/shortcuts';
+import { useIsSidebar, useNodeRenderContext } from '@editor/hooks';
+import { NodeMenu } from '@editor/components/node-menu';
+import { getIcon } from './utils';
+import { TitleInput } from './title-input';
+import { Header, Operators } from './styles';
+
+export function FormHeader() {
+ const { node, expanded, toggleExpand, readonly } = useNodeRenderContext();
+ const [titleEdit, updateTitleEdit] = useState(false);
+ const ctx = useClientContext();
+ const isSidebar = useIsSidebar();
+ const handleExpand = (e: React.MouseEvent) => {
+ toggleExpand();
+ e.stopPropagation(); // Disable clicking prevents the sidebar from opening
+ };
+ const handleDelete = () => {
+ ctx.get(CommandService).executeCommand(FlowCommandId.DELETE, [node]);
+ };
+
+ return (
+
+ {getIcon(node)}
+
+ {node?.renderData.expandable && !isSidebar && (
+ : }
+ size="small"
+ onClick={handleExpand}
+ />
+ )}
+ {readonly ? undefined : (
+
+
+
+ )}
+
+ );
+}
diff --git a/apps/demo-nextjs-antd/src/editor/form-components/form-header/styles.tsx b/apps/demo-nextjs-antd/src/editor/form-components/form-header/styles.tsx
new file mode 100644
index 00000000..8d2db191
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/form-components/form-header/styles.tsx
@@ -0,0 +1,30 @@
+import styled from 'styled-components';
+
+export const Header = styled.div`
+ box-sizing: border-box;
+ display: flex;
+ justify-content: flex-start;
+ align-items: center;
+ width: 100%;
+ column-gap: 8px;
+ border-radius: 8px 8px 0 0;
+ cursor: move;
+
+ background: linear-gradient(#f2f2ff 0%, rgba(0, 0, 0, 0.02) 100%);
+ overflow: hidden;
+
+ padding: 8px;
+`;
+
+export const Icon = styled.img`
+ width: 24px;
+ height: 24px;
+ scale: 0.8;
+ border-radius: 4px;
+`;
+
+export const Operators = styled.div`
+ display: flex;
+ align-items: center;
+ column-gap: 4px;
+`;
diff --git a/apps/demo-nextjs-antd/src/editor/form-components/form-header/title-input.tsx b/apps/demo-nextjs-antd/src/editor/form-components/form-header/title-input.tsx
new file mode 100644
index 00000000..992647ab
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/form-components/form-header/title-input.tsx
@@ -0,0 +1,46 @@
+import { useEffect, useRef } from 'react';
+
+import { Input, Typography } from 'antd';
+import { Field, FieldRenderProps } from '@flowgram.ai/free-layout-editor';
+
+import { Feedback } from '../feedback';
+// import { Title } from "./styles";
+
+const { Text } = Typography;
+
+export function TitleInput(props: {
+ readonly: boolean;
+ titleEdit: boolean;
+ updateTitleEdit: (setEdit: boolean) => void;
+}): JSX.Element {
+ const { readonly, titleEdit, updateTitleEdit } = props;
+ const ref = useRef();
+ const titleEditing = titleEdit && !readonly;
+ useEffect(() => {
+ if (titleEditing) {
+ ref.current?.focus();
+ }
+ }, [titleEditing]);
+
+ return (
+
+
+ {({ field: { value, onChange }, fieldState }: FieldRenderProps) => (
+
+ {titleEditing ? (
+ updateTitleEdit(false)}
+ />
+ ) : (
+ {value}
+ )}
+
+
+ )}
+
+
+ );
+}
diff --git a/apps/demo-nextjs-antd/src/editor/form-components/form-header/utils.tsx b/apps/demo-nextjs-antd/src/editor/form-components/form-header/utils.tsx
new file mode 100644
index 00000000..d2a7b082
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/form-components/form-header/utils.tsx
@@ -0,0 +1,10 @@
+import { type FlowNodeEntity } from '@flowgram.ai/free-layout-editor';
+
+import { FlowNodeRegistry } from '@editor/typings';
+import { Icon } from './styles';
+
+export const getIcon = (node: FlowNodeEntity) => {
+ const icon = node?.getNodeRegistry().info?.icon;
+ if (!icon) return null;
+ return ;
+};
diff --git a/apps/demo-nextjs-antd/src/editor/form-components/form-inputs/index.tsx b/apps/demo-nextjs-antd/src/editor/form-components/form-inputs/index.tsx
new file mode 100644
index 00000000..570d629e
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/form-components/form-inputs/index.tsx
@@ -0,0 +1,46 @@
+import { Field } from '@flowgram.ai/free-layout-editor';
+import { DynamicValueInput } from '@flowgram.ai/form-antd-materials';
+
+import { JsonSchema } from '@editor/typings';
+import { useNodeRenderContext } from '@editor/hooks';
+import { FormItem } from '../form-item';
+import { Feedback } from '../feedback';
+
+export function FormInputs() {
+ const { readonly } = useNodeRenderContext();
+ return (
+ name="inputs">
+ {({ field: inputsField }) => {
+ const required = inputsField.value?.required || [];
+ const properties = inputsField.value?.properties;
+ if (!properties) {
+ return <>>;
+ }
+ const content = Object.keys(properties).map((key) => {
+ const property = properties[key];
+ return (
+
+ {({ field, fieldState }) => (
+
+ 0}
+ schema={property}
+ />
+
+
+ )}
+
+ );
+ });
+ return <>{content}>;
+ }}
+
+ );
+}
diff --git a/apps/demo-nextjs-antd/src/editor/form-components/form-inputs/styles.tsx b/apps/demo-nextjs-antd/src/editor/form-components/form-inputs/styles.tsx
new file mode 100644
index 00000000..a92a565e
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/form-components/form-inputs/styles.tsx
@@ -0,0 +1,3 @@
+// import styled from 'styled-components';
+
+// TODO
diff --git a/apps/demo-nextjs-antd/src/editor/form-components/form-item/index.css b/apps/demo-nextjs-antd/src/editor/form-components/form-item/index.css
new file mode 100644
index 00000000..27b42da0
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/form-components/form-item/index.css
@@ -0,0 +1,9 @@
+.form-item-type-tag {
+ color: inherit;
+ padding: 0 2px;
+ height: 18px;
+ width: 18px;
+ vertical-align: middle;
+ flex-shrink: 0;
+ flex-grow: 0;
+}
diff --git a/apps/demo-nextjs-antd/src/editor/form-components/form-item/index.tsx b/apps/demo-nextjs-antd/src/editor/form-components/form-item/index.tsx
new file mode 100644
index 00000000..181da2aa
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/form-components/form-item/index.tsx
@@ -0,0 +1,73 @@
+import { TypeTag } from '../type-tag';
+
+import './index.css';
+import { Tooltip, Typography } from 'antd';
+
+import React, { useCallback } from 'react';
+
+const { Text } = Typography;
+
+interface FormItemProps {
+ children: React.ReactNode;
+ name: string;
+ type: string;
+ required?: boolean;
+ description?: string;
+ labelWidth?: number;
+}
+export function FormItem({
+ children,
+ name,
+ required,
+ description,
+ type,
+ labelWidth,
+}: FormItemProps): JSX.Element {
+ const renderTitle = useCallback(
+ (showTooltip?: boolean) => (
+
+ {name}
+ {required && *}
+
+ ),
+ []
+ );
+ return (
+
+
+
+ {description ? {renderTitle()} : renderTitle(true)}
+
+
+
+ {children}
+
+
+ );
+}
diff --git a/apps/demo-nextjs-antd/src/editor/form-components/form-outputs/index.tsx b/apps/demo-nextjs-antd/src/editor/form-components/form-outputs/index.tsx
new file mode 100644
index 00000000..bd946011
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/form-components/form-outputs/index.tsx
@@ -0,0 +1,28 @@
+import { Field } from '@flowgram.ai/free-layout-editor';
+
+import { JsonSchema } from '@editor/typings';
+import { useIsSidebar } from '@editor/hooks';
+import { TypeTag } from '../type-tag';
+import { FormOutputsContainer } from './styles';
+
+export function FormOutputs() {
+ const isSidebar = useIsSidebar();
+ if (isSidebar) {
+ return null;
+ }
+ return (
+ name={'outputs'}>
+ {({ field }) => {
+ const properties = field.value?.properties;
+ if (properties) {
+ const content = Object.keys(properties).map((key) => {
+ const property = properties[key];
+ return ;
+ });
+ return {content};
+ }
+ return <>>;
+ }}
+
+ );
+}
diff --git a/apps/demo-nextjs-antd/src/editor/form-components/form-outputs/styles.tsx b/apps/demo-nextjs-antd/src/editor/form-components/form-outputs/styles.tsx
new file mode 100644
index 00000000..8dae9b4a
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/form-components/form-outputs/styles.tsx
@@ -0,0 +1,9 @@
+import styled from 'styled-components';
+
+export const FormOutputsContainer = styled.div`
+ display: flex;
+ gap: 6px;
+ flex-wrap: wrap;
+ padding: 8px 0 0;
+ width: 100%;
+`;
diff --git a/apps/demo-nextjs-antd/src/editor/form-components/index.ts b/apps/demo-nextjs-antd/src/editor/form-components/index.ts
new file mode 100644
index 00000000..597f3544
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/form-components/index.ts
@@ -0,0 +1,9 @@
+export * from './feedback';
+export * from './form-content';
+export * from './form-outputs';
+export * from './form-inputs';
+export * from './form-header';
+export * from './form-item';
+export * from './type-tag';
+export * from './properties-edit';
+export * from './value-display';
diff --git a/apps/demo-nextjs-antd/src/editor/form-components/properties-edit/index.tsx b/apps/demo-nextjs-antd/src/editor/form-components/properties-edit/index.tsx
new file mode 100644
index 00000000..549229f3
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/form-components/properties-edit/index.tsx
@@ -0,0 +1,114 @@
+import React, { useState } from 'react';
+
+import { Button } from 'antd';
+import { PlusCircleOutlined } from '@ant-design/icons';
+
+import { JsonSchema } from '@editor/typings';
+import { useNodeRenderContext } from '@editor/hooks';
+import { PropertyEdit } from './property-edit';
+
+export interface PropertiesEditProps {
+ value?: Record;
+ onChange: (value: Record) => void;
+ useFx?: boolean;
+}
+
+export const PropertiesEdit: React.FC = (props) => {
+ const value = (props.value || {}) as Record;
+ const { readonly } = useNodeRenderContext();
+ const [newProperty, updateNewPropertyFromCache] = useState<{
+ key: string;
+ value: JsonSchema;
+ }>({
+ key: '',
+ value: { type: 'string' },
+ });
+ const [newPropertyVisible, setNewPropertyVisible] = useState();
+ const clearCache = () => {
+ updateNewPropertyFromCache({ key: '', value: { type: 'string' } });
+ setNewPropertyVisible(false);
+ };
+ const updateProperty = (
+ propertyValue: JsonSchema,
+ propertyKey: string,
+ newPropertyKey?: string
+ ) => {
+ const newValue = { ...value };
+ if (newPropertyKey) {
+ delete newValue[propertyKey];
+ newValue[newPropertyKey] = propertyValue;
+ } else {
+ newValue[propertyKey] = propertyValue;
+ }
+ props.onChange(newValue);
+ };
+ const updateNewProperty = (
+ propertyValue: JsonSchema,
+ propertyKey: string,
+ newPropertyKey?: string
+ ) => {
+ // const newValue = { ...value }
+ if (newPropertyKey) {
+ if (!(newPropertyKey in value)) {
+ updateProperty(propertyValue, propertyKey, newPropertyKey);
+ }
+ clearCache();
+ } else {
+ updateNewPropertyFromCache({
+ key: newPropertyKey || propertyKey,
+ value: propertyValue,
+ });
+ }
+ };
+ return (
+ <>
+ {Object.keys(props.value || {}).map((key) => {
+ const property = (value[key] || {}) as JsonSchema;
+ return (
+ {
+ const newValue = { ...value };
+ delete newValue[key];
+ props.onChange(newValue);
+ }}
+ />
+ );
+ })}
+ {newPropertyVisible && (
+ {
+ const key = newProperty.key;
+ // after onblur
+ setTimeout(() => {
+ const newValue = { ...value };
+ delete newValue[key];
+ props.onChange(newValue);
+ clearCache();
+ }, 10);
+ }}
+ />
+ )}
+ {!readonly && (
+
+ }
+ onClick={() => setNewPropertyVisible(true)}
+ >
+ Add
+
+
+ )}
+ >
+ );
+};
diff --git a/apps/demo-nextjs-antd/src/editor/form-components/properties-edit/property-edit.tsx b/apps/demo-nextjs-antd/src/editor/form-components/properties-edit/property-edit.tsx
new file mode 100644
index 00000000..f0874dbd
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/form-components/properties-edit/property-edit.tsx
@@ -0,0 +1,84 @@
+import React, { useLayoutEffect, useState } from 'react';
+
+import { Button, Input } from 'antd';
+import { DynamicValueInput, TypeSelector } from '@flowgram.ai/form-antd-materials';
+import { CloseCircleOutlined } from '@ant-design/icons';
+
+import { JsonSchema } from '@editor/typings';
+import { LeftColumn, Row } from './styles';
+
+export interface PropertyEditProps {
+ propertyKey: string;
+ value: JsonSchema;
+ useFx?: boolean;
+ disabled?: boolean;
+ onChange: (value: JsonSchema, propertyKey: string, newPropertyKey?: string) => void;
+ onDelete?: () => void;
+}
+
+export const PropertyEdit: React.FC = (props) => {
+ const { value, disabled } = props;
+ const [inputKey, updateKey] = useState(props.propertyKey);
+ const updateProperty = (key: keyof JsonSchema, val: any) => {
+ value[key] = val;
+ props.onChange(value, props.propertyKey);
+ };
+
+ const partialUpdateProperty = (val?: Partial) => {
+ props.onChange({ ...value, ...val }, props.propertyKey);
+ };
+
+ useLayoutEffect(() => {
+ updateKey(props.propertyKey);
+ }, [props.propertyKey]);
+ return (
+
+
+ partialUpdateProperty(val)}
+ />
+ updateKey(v.trim())}
+ onBlur={() => {
+ if (inputKey !== '') {
+ props.onChange(value, props.propertyKey, inputKey);
+ } else {
+ updateKey(props.propertyKey);
+ }
+ }}
+ style={{ paddingLeft: 26 }}
+ />
+
+ {
+ updateProperty('default', val)}
+ schema={value}
+ style={{ flexGrow: 1 }}
+ />
+ }
+ {props.onDelete && !disabled && (
+ }
+ onClick={props.onDelete}
+ />
+ )}
+
+ );
+};
diff --git a/apps/demo-nextjs-antd/src/editor/form-components/properties-edit/styles.tsx b/apps/demo-nextjs-antd/src/editor/form-components/properties-edit/styles.tsx
new file mode 100644
index 00000000..c2001874
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/form-components/properties-edit/styles.tsx
@@ -0,0 +1,15 @@
+import styled from 'styled-components';
+
+export const Row = styled.div`
+ display: flex;
+ justify-content: flex-start;
+ align-items: center;
+ font-size: 12px;
+ margin-bottom: 6px;
+`;
+
+export const LeftColumn = styled.div`
+ width: 120px;
+ margin-right: 5px;
+ position: relative;
+`;
diff --git a/apps/demo-nextjs-antd/src/editor/form-components/type-tag.tsx b/apps/demo-nextjs-antd/src/editor/form-components/type-tag.tsx
new file mode 100644
index 00000000..b0ceca51
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/form-components/type-tag.tsx
@@ -0,0 +1,61 @@
+import styled from 'styled-components';
+import { Tag, Tooltip } from 'antd';
+import { ArrayIcons, VariableTypeIcons } from '@flowgram.ai/form-antd-materials';
+
+interface PropsType {
+ name?: string | JSX.Element;
+ type: string;
+ className?: string;
+ isArray?: boolean;
+}
+
+const TooltipContainer = styled.div`
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ column-gap: 6px;
+`;
+
+export function TypeTag({ name, type, isArray, className }: PropsType) {
+ const icon = isArray ? ArrayIcons[type] : VariableTypeIcons[type];
+ return (
+
+ {icon} {type}
+
+ }
+ >
+
+ {icon}
+ {name && (
+
+ {' '}
+ {name}
+
+ )}
+
+
+ );
+}
diff --git a/apps/demo-nextjs-antd/src/editor/form-components/value-display/index.tsx b/apps/demo-nextjs-antd/src/editor/form-components/value-display/index.tsx
new file mode 100644
index 00000000..d37ee8fd
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/form-components/value-display/index.tsx
@@ -0,0 +1,17 @@
+// import { TypeTag } from '../type-tag'
+import { ValueDisplayStyle } from './styles';
+
+export interface ValueDisplayProps {
+ value: string;
+ placeholder?: string;
+ hasError?: boolean;
+}
+
+export const ValueDisplay: React.FC = (props) => (
+
+ {props.value}
+ {props.value === undefined || props.value === '' ? (
+ {props.placeholder || '--'}
+ ) : null}
+
+);
diff --git a/apps/demo-nextjs-antd/src/editor/form-components/value-display/styles.tsx b/apps/demo-nextjs-antd/src/editor/form-components/value-display/styles.tsx
new file mode 100644
index 00000000..0a0bfe95
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/form-components/value-display/styles.tsx
@@ -0,0 +1,13 @@
+import styled from 'styled-components';
+
+export const ValueDisplayStyle = styled.div`
+ padding-left: 12px;
+ width: 100%;
+ min-height: 24px;
+ line-height: 24px;
+ display: flex;
+ align-items: center;
+ &.has-error {
+ outline: red solid 1px;
+ }
+`;
diff --git a/apps/demo-nextjs-antd/src/editor/hooks/index.ts b/apps/demo-nextjs-antd/src/editor/hooks/index.ts
new file mode 100644
index 00000000..f687af19
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/hooks/index.ts
@@ -0,0 +1,3 @@
+export { useEditorProps } from './use-editor-props';
+export { useNodeRenderContext } from './use-node-render-context';
+export { useIsSidebar } from './use-is-sidebar';
diff --git a/apps/demo-nextjs-antd/src/editor/hooks/use-editor-props.tsx b/apps/demo-nextjs-antd/src/editor/hooks/use-editor-props.tsx
new file mode 100644
index 00000000..5acaf08a
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/hooks/use-editor-props.tsx
@@ -0,0 +1,192 @@
+'use client';
+
+import { useMemo } from 'react';
+
+import { createMinimapPlugin } from '@flowgram.ai/minimap-plugin';
+import { createFreeSnapPlugin } from '@flowgram.ai/free-snap-plugin';
+import { createFreeNodePanelPlugin } from '@flowgram.ai/free-node-panel-plugin';
+import { createFreeLinesPlugin } from '@flowgram.ai/free-lines-plugin';
+import { WorkflowJSON } from '@flowgram.ai/free-layout-editor';
+import { FreeLayoutProps } from '@flowgram.ai/free-layout-editor';
+import { createFreeGroupPlugin } from '@flowgram.ai/free-group-plugin';
+import { createContainerNodePlugin } from '@flowgram.ai/free-container-plugin';
+
+import { onDragLineEnd } from '@editor/utils';
+import { FlowNodeRegistry } from '@editor/typings';
+import { createContextMenuPlugin, createSyncVariablePlugin } from '@editor/plugins';
+import { defaultFormMeta } from '@editor/nodes/default-form-meta';
+import { WorkflowNodeType } from '@editor/nodes';
+import { BaseNode } from '@editor/components/base-node';
+import {
+ CommentRender,
+ GroupNodeRender,
+ LineAddButton,
+ NodePanel,
+ SelectorBoxPopover,
+} from '@editor/components';
+
+export const useEditorProps = (initialData: WorkflowJSON, nodeRegistries: FlowNodeRegistry[]) =>
+ useMemo(
+ () => ({
+ /**
+ * Whether to enable the background
+ */
+ background: true,
+ /**
+ * Whether it is read-only or not, the node cannot be dragged in read-only mode
+ */
+ readonly: false,
+ /**
+ * Initial data
+ * 初始化数据
+ */
+ initialData,
+ /**
+ * Node registries
+ * 节点注册
+ */
+ nodeRegistries,
+ /**
+ * Get the default node registry, which will be merged with the 'nodeRegistries'
+ * 提供默认的节点注册,这个会和 nodeRegistries 做合并
+ */
+ getNodeDefaultRegistry(type) {
+ return {
+ type,
+ meta: {
+ defaultExpanded: true,
+ size: {
+ width: 360,
+ height: 70,
+ },
+ },
+ formMeta: defaultFormMeta,
+ };
+ },
+ onDragLineEnd,
+ selectBox: {
+ SelectorBoxPopover,
+ },
+ materials: {
+ /**
+ * Render Node
+ */
+ renderDefaultNode: BaseNode,
+ renderNodes: {
+ [WorkflowNodeType.Comment]: CommentRender,
+ },
+ },
+ /**
+ * Content change
+ */
+ onContentChange(ctx, event) {
+ // console.log('Auto Save: ', event, ctx.document.toJSON());
+ },
+ // /**
+ // * Node engine enable, you can configure formMeta in the FlowNodeRegistry
+ // */
+ nodeEngine: {
+ enable: true,
+ },
+ /**
+ * Variable engine enable
+ */
+ variableEngine: {
+ enable: true,
+ },
+ /**
+ * Redo/Undo enable
+ */
+ history: {
+ enable: true,
+ enableChangeNode: true, // Listen Node engine data change
+ },
+ /**
+ * Playground init
+ */
+ onInit: (ctx) => {},
+ /**
+ * Playground render
+ */
+ onAllLayersRendered(ctx) {
+ // Fitview
+ ctx.document.fitView(false);
+ },
+ /**
+ * Playground dispose
+ */
+ onDispose() {
+ console.log('---- Playground Dispose ----');
+ },
+ plugins: () => [
+ /**
+ * Line render plugin
+ * 连线渲染插件
+ */
+ createFreeLinesPlugin({
+ renderInsideLine: LineAddButton,
+ }),
+ /**
+ * Minimap plugin
+ * 缩略图插件
+ */
+ createMinimapPlugin({
+ disableLayer: true,
+ canvasStyle: {
+ canvasWidth: 182,
+ canvasHeight: 102,
+ canvasPadding: 50,
+ canvasBackground: 'rgba(245, 245, 245, 1)',
+ canvasBorderRadius: 10,
+ viewportBackground: 'rgba(235, 235, 235, 1)',
+ viewportBorderRadius: 4,
+ viewportBorderColor: 'rgba(201, 201, 201, 1)',
+ viewportBorderWidth: 1,
+ viewportBorderDashLength: 2,
+ nodeColor: 'rgba(255, 255, 255, 1)',
+ nodeBorderRadius: 2,
+ nodeBorderWidth: 0.145,
+ nodeBorderColor: 'rgba(6, 7, 9, 0.10)',
+ overlayColor: 'rgba(255, 255, 255, 0)',
+ },
+ inactiveDebounceTime: 1,
+ }),
+ /**
+ * Variable plugin
+ * 变量插件
+ */
+ createSyncVariablePlugin({}),
+ /**
+ * Snap plugin
+ * 自动对齐及辅助线插件
+ */
+ createFreeSnapPlugin({
+ edgeColor: '#00B2B2',
+ alignColor: '#00B2B2',
+ edgeLineWidth: 1,
+ alignLineWidth: 1,
+ alignCrossWidth: 8,
+ }),
+ /**
+ * NodeAddPanel render plugin
+ * 节点添加面板渲染插件
+ */
+ createFreeNodePanelPlugin({
+ renderer: NodePanel,
+ }),
+ /**
+ * This is used for the rendering of the loop node sub-canvas
+ * 这个用于 loop 节点子画布的渲染
+ */
+ createContainerNodePlugin({}),
+ /**
+ * Group plugin
+ */
+ createFreeGroupPlugin({
+ groupNodeRender: GroupNodeRender,
+ }),
+ createContextMenuPlugin({}),
+ ],
+ }),
+ []
+ );
diff --git a/apps/demo-nextjs-antd/src/editor/hooks/use-is-sidebar.ts b/apps/demo-nextjs-antd/src/editor/hooks/use-is-sidebar.ts
new file mode 100644
index 00000000..65ce37d2
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/hooks/use-is-sidebar.ts
@@ -0,0 +1,7 @@
+import { useContext } from 'react';
+
+import { IsSidebarContext } from '../context';
+
+export function useIsSidebar() {
+ return useContext(IsSidebarContext);
+}
diff --git a/apps/demo-nextjs-antd/src/editor/hooks/use-node-render-context.ts b/apps/demo-nextjs-antd/src/editor/hooks/use-node-render-context.ts
new file mode 100644
index 00000000..b529b8c4
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/hooks/use-node-render-context.ts
@@ -0,0 +1,7 @@
+import { useContext } from 'react';
+
+import { NodeRenderContext } from '../context';
+
+export function useNodeRenderContext() {
+ return useContext(NodeRenderContext);
+}
diff --git a/apps/demo-nextjs-antd/src/editor/index.ts b/apps/demo-nextjs-antd/src/editor/index.ts
new file mode 100644
index 00000000..63ab1a8f
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/index.ts
@@ -0,0 +1,3 @@
+import './style/index.css';
+
+export { EditorClient } from './components/editor-client';
diff --git a/apps/demo-nextjs-antd/src/editor/nodes/comment/index.tsx b/apps/demo-nextjs-antd/src/editor/nodes/comment/index.tsx
new file mode 100644
index 00000000..f15f2c05
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/nodes/comment/index.tsx
@@ -0,0 +1,22 @@
+'use client';
+
+import { FlowNodeRegistry } from '@editor/typings';
+import { WorkflowNodeType } from '../constants';
+
+export const CommentNodeRegistry: FlowNodeRegistry = {
+ type: WorkflowNodeType.Comment,
+ meta: {
+ sidebarDisable: true,
+ defaultPorts: [],
+ renderKey: WorkflowNodeType.Comment,
+ size: {
+ width: 240,
+ height: 150,
+ },
+ },
+ formMeta: {
+ render: () => <>>,
+ },
+ getInputPoints: () => [], // Comment 节点没有输入
+ getOutputPoints: () => [], // Comment 节点没有输出
+};
diff --git a/apps/demo-nextjs-antd/src/editor/nodes/condition/condition-inputs/index.tsx b/apps/demo-nextjs-antd/src/editor/nodes/condition/condition-inputs/index.tsx
new file mode 100644
index 00000000..c063c933
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/nodes/condition/condition-inputs/index.tsx
@@ -0,0 +1,79 @@
+import { nanoid } from 'nanoid';
+import { Button } from 'antd';
+import { Field, FieldArray } from '@flowgram.ai/free-layout-editor';
+import { ConditionRow, ConditionRowValueType } from '@flowgram.ai/form-antd-materials';
+import { CloseCircleOutlined, PlusOutlined } from '@ant-design/icons';
+
+import { useNodeRenderContext } from '@editor/hooks';
+import { FormItem } from '@editor/form-components';
+import { Feedback } from '@editor/form-components';
+import { ConditionPort } from './styles';
+
+// TODO
+// interface ConditionRowValueType{}
+// function ConditionRow(params){
+// return <>{params.children}>
+// }
+
+interface ConditionValue {
+ key: string;
+ value?: ConditionRowValueType;
+}
+
+export function ConditionInputs() {
+ const { readonly } = useNodeRenderContext();
+ return (
+
+ {({ field }) => (
+ <>
+ {field.map((child, index) => (
+ key={child.name} name={child.name}>
+ {({ field: childField, fieldState: childState }) => (
+
+
+
+ childField.onChange({
+ value: v,
+ key: childField.value.key,
+ })
+ }
+ />
+
+ }
+ onClick={() => field.delete(index)}
+ />
+
+
+
+
+
+ )}
+
+ ))}
+ {!readonly && (
+
+ }
+ onClick={() =>
+ field.append({
+ key: `if_${nanoid(6)}`,
+ value: { type: 'expression', content: '' },
+ })
+ }
+ >
+ Add
+
+
+ )}
+ >
+ )}
+
+ );
+}
diff --git a/apps/demo-nextjs-antd/src/editor/nodes/condition/condition-inputs/styles.tsx b/apps/demo-nextjs-antd/src/editor/nodes/condition/condition-inputs/styles.tsx
new file mode 100644
index 00000000..0434dc89
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/nodes/condition/condition-inputs/styles.tsx
@@ -0,0 +1,7 @@
+import styled from 'styled-components';
+
+export const ConditionPort = styled.div`
+ position: absolute;
+ right: -12px;
+ top: 50%;
+`;
diff --git a/apps/demo-nextjs-antd/src/editor/nodes/condition/form-meta.tsx b/apps/demo-nextjs-antd/src/editor/nodes/condition/form-meta.tsx
new file mode 100644
index 00000000..f73e41d1
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/nodes/condition/form-meta.tsx
@@ -0,0 +1,26 @@
+import { FormMeta, FormRenderProps, ValidateTrigger } from '@flowgram.ai/free-layout-editor';
+
+import { FlowNodeJSON } from '@editor/typings';
+import { FormContent, FormHeader } from '@editor/form-components';
+import { ConditionInputs } from './condition-inputs';
+
+export const renderForm = ({ form }: FormRenderProps) => (
+ <>
+
+
+
+
+ >
+);
+
+export const formMeta: FormMeta = {
+ render: renderForm,
+ validateTrigger: ValidateTrigger.onChange,
+ validate: {
+ title: ({ value }: { value: string }) => (value ? undefined : 'Title is required'),
+ 'conditions.*': ({ value }) => {
+ if (!value?.value) return 'Condition is required';
+ return undefined;
+ },
+ },
+};
diff --git a/apps/demo-nextjs-antd/src/editor/nodes/condition/index.ts b/apps/demo-nextjs-antd/src/editor/nodes/condition/index.ts
new file mode 100644
index 00000000..6e0db3d9
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/nodes/condition/index.ts
@@ -0,0 +1,41 @@
+import { nanoid } from 'nanoid';
+
+import { FlowNodeRegistry } from '@editor/typings';
+import { WorkflowNodeType } from '../constants';
+import iconCondition from '../../assets/icon-condition.svg';
+import { formMeta } from './form-meta';
+
+export const ConditionNodeRegistry: FlowNodeRegistry = {
+ type: WorkflowNodeType.Condition,
+ info: {
+ icon: iconCondition,
+ description:
+ 'Connect multiple downstream branches. Only the corresponding branch will be executed if the set conditions are met.',
+ },
+ meta: {
+ defaultPorts: [{ type: 'input' }],
+ // Condition Outputs use dynamic port
+ useDynamicPort: true,
+ expandable: false, // disable expanded
+ },
+ formMeta,
+ onAdd() {
+ return {
+ id: `condition_${nanoid(5)}`,
+ type: 'condition',
+ data: {
+ title: 'Condition',
+ conditions: [
+ {
+ key: `if_${nanoid(5)}`,
+ value: {},
+ },
+ {
+ key: `if_${nanoid(5)}`,
+ value: {},
+ },
+ ],
+ },
+ };
+ },
+};
diff --git a/apps/demo-nextjs-antd/src/editor/nodes/constants.ts b/apps/demo-nextjs-antd/src/editor/nodes/constants.ts
new file mode 100644
index 00000000..d598ad96
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/nodes/constants.ts
@@ -0,0 +1,8 @@
+export enum WorkflowNodeType {
+ Start = 'start',
+ End = 'end',
+ LLM = 'llm',
+ Condition = 'condition',
+ Loop = 'loop',
+ Comment = 'comment',
+}
diff --git a/apps/demo-nextjs-antd/src/editor/nodes/default-form-meta.tsx b/apps/demo-nextjs-antd/src/editor/nodes/default-form-meta.tsx
new file mode 100644
index 00000000..357f5bc4
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/nodes/default-form-meta.tsx
@@ -0,0 +1,37 @@
+import { FormMeta, FormRenderProps, ValidateTrigger } from '@flowgram.ai/free-layout-editor';
+import { autoRenameRefEffect } from '@flowgram.ai/form-antd-materials';
+
+import { FormContent, FormHeader, FormInputs, FormOutputs } from '@editor/form-components';
+import { FlowNodeJSON } from '../typings';
+
+export const renderForm = ({ form }: FormRenderProps) => (
+ <>
+
+
+
+
+
+ >
+);
+
+export const defaultFormMeta: FormMeta = {
+ render: renderForm,
+ validateTrigger: ValidateTrigger.onChange,
+ validate: {
+ title: ({ value }) => (value ? undefined : 'Title is required'),
+ 'inputsValues.*': ({ value, context, formValues, name }) => {
+ const valuePropetyKey = name.replace(/^inputsValues\./, '');
+ const required = formValues.inputs?.required || [];
+ if (
+ required.includes(valuePropetyKey) &&
+ (value === '' || value === undefined || value?.content === '')
+ ) {
+ return `${valuePropetyKey} is required`;
+ }
+ return undefined;
+ },
+ },
+ effect: {
+ inputsValues: autoRenameRefEffect,
+ },
+};
diff --git a/apps/demo-nextjs-antd/src/editor/nodes/end/form-meta.tsx b/apps/demo-nextjs-antd/src/editor/nodes/end/form-meta.tsx
new file mode 100644
index 00000000..a8926d4a
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/nodes/end/form-meta.tsx
@@ -0,0 +1,65 @@
+import { mapValues } from 'lodash-es';
+import { Field, FieldRenderProps, FormMeta } from '@flowgram.ai/free-layout-editor';
+import { IFlowValue } from '@flowgram.ai/form-antd-materials';
+
+import { JsonSchema } from '@editor/typings';
+import { useIsSidebar } from '@editor/hooks';
+import { FormContent, FormHeader, FormOutputs, PropertiesEdit } from '@editor/form-components';
+import { defaultFormMeta } from '../default-form-meta';
+
+export const renderForm = () => {
+ // eslint-disable-next-line react-hooks/rules-of-hooks
+ const isSidebar = useIsSidebar();
+ if (isSidebar) {
+ return (
+ <>
+
+
+ >) => (
+ > name="inputsValues">
+ {({ field: { value: propertiesValue, onChange: propertiesValueChange } }) => {
+ const onChange = (newProperties: Record) => {
+ const newPropertiesValue = mapValues(newProperties, (v) => v.default);
+ const newPropetiesSchema = mapValues(newProperties, (v) => {
+ delete v.default;
+ return v;
+ });
+ propertiesValueChange(newPropertiesValue);
+ propertiesSchemaChange(newPropetiesSchema);
+ };
+ const value = mapValues(propertiesSchemaValue, (v, key) => ({
+ ...v,
+ default: propertiesValue?.[key],
+ }));
+ return (
+ <>
+
+ >
+ );
+ }}
+
+ )}
+ />
+
+
+ >
+ );
+ }
+ return (
+ <>
+
+
+
+
+ >
+ );
+};
+
+export const formMeta: FormMeta = {
+ ...defaultFormMeta,
+ render: renderForm,
+};
diff --git a/apps/demo-nextjs-antd/src/editor/nodes/end/index.ts b/apps/demo-nextjs-antd/src/editor/nodes/end/index.ts
new file mode 100644
index 00000000..cbdb5bfc
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/nodes/end/index.ts
@@ -0,0 +1,32 @@
+import { FlowNodeRegistry } from '@editor/typings';
+import { WorkflowNodeType } from '../constants';
+import iconEnd from '../../assets/icon-end.jpg';
+import { formMeta } from './form-meta';
+
+export const EndNodeRegistry: FlowNodeRegistry = {
+ type: WorkflowNodeType.End,
+ meta: {
+ deleteDisable: true,
+ copyDisable: true,
+ defaultPorts: [{ type: 'input' }],
+ size: {
+ width: 360,
+ height: 211,
+ },
+ },
+ info: {
+ icon: iconEnd,
+ description:
+ 'The final node of the workflow, used to return the result information after the workflow is run.',
+ },
+ /**
+ * Render node via formMeta
+ */
+ formMeta,
+ /**
+ * End Node cannot be added
+ */
+ canAdd() {
+ return false;
+ },
+};
diff --git a/apps/demo-nextjs-antd/src/editor/nodes/index.ts b/apps/demo-nextjs-antd/src/editor/nodes/index.ts
new file mode 100644
index 00000000..2181119e
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/nodes/index.ts
@@ -0,0 +1,25 @@
+'use client';
+
+// import { FlowNodeRegistry } from '../typings';
+// import { ConditionNodeRegistry } from './condition';
+// import { CommentNodeRegistry } from './comment';
+// import { LoopNodeRegistry } from './loop';
+// import { LLMNodeRegistry } from './llm';
+// import { EndNodeRegistry } from './end';
+// import { WorkflowNodeType } from './constants';
+// import { StartNodeRegistry } from './start';
+
+export { WorkflowNodeType } from './constants';
+
+// export const nodeRegistries: FlowNodeRegistry[] = [
+// // ConditionNodeRegistry,
+// StartNodeRegistry,
+// // EndNodeRegistry,
+// // LLMNodeRegistry,
+// // LoopNodeRegistry,
+// // CommentNodeRegistry,
+// ];
+
+// export const visibleNodeRegistries = nodeRegistries.filter(
+// (r) => r.type !== WorkflowNodeType.Comment
+// );
diff --git a/apps/demo-nextjs-antd/src/editor/nodes/llm/index.ts b/apps/demo-nextjs-antd/src/editor/nodes/llm/index.ts
new file mode 100644
index 00000000..99e89baf
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/nodes/llm/index.ts
@@ -0,0 +1,72 @@
+import { nanoid } from 'nanoid';
+
+import { FlowNodeRegistry } from '@editor/typings';
+import { WorkflowNodeType } from '../constants';
+import iconLLM from '../../assets/icon-llm.jpg';
+
+let index = 0;
+export const LLMNodeRegistry: FlowNodeRegistry = {
+ type: WorkflowNodeType.LLM,
+ info: {
+ icon: iconLLM,
+ description:
+ 'Call the large language model and use variables and prompt words to generate responses.',
+ },
+ meta: {
+ size: {
+ width: 360,
+ height: 305,
+ },
+ },
+ onAdd() {
+ return {
+ id: `llm_${nanoid(5)}`,
+ type: 'llm',
+ data: {
+ title: `LLM_${++index}`,
+ inputsValues: {
+ modelType: {
+ type: 'constant',
+ content: 'gpt-3.5-turbo',
+ },
+ temperature: {
+ type: 'constant',
+ content: 0.5,
+ },
+ systemPrompt: {
+ type: 'constant',
+ content: 'You are an AI assistant.',
+ },
+ prompt: {
+ type: 'constant',
+ content: '',
+ },
+ },
+ inputs: {
+ type: 'object',
+ required: ['modelType', 'temperature', 'prompt'],
+ properties: {
+ modelType: {
+ type: 'string',
+ },
+ temperature: {
+ type: 'number',
+ },
+ systemPrompt: {
+ type: 'string',
+ },
+ prompt: {
+ type: 'string',
+ },
+ },
+ },
+ outputs: {
+ type: 'object',
+ properties: {
+ result: { type: 'string' },
+ },
+ },
+ },
+ };
+ },
+};
diff --git a/apps/demo-nextjs-antd/src/editor/nodes/loop/index.ts b/apps/demo-nextjs-antd/src/editor/nodes/loop/index.ts
new file mode 100644
index 00000000..df64c74d
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/nodes/loop/index.ts
@@ -0,0 +1,77 @@
+import { nanoid } from 'nanoid';
+import {
+ FlowNodeTransformData,
+ PositionSchema,
+ WorkflowNodeEntity,
+} from '@flowgram.ai/free-layout-editor';
+import { provideBatchInputEffect } from '@flowgram.ai/form-antd-materials';
+
+import { FlowNodeRegistry } from '@editor/typings';
+import { defaultFormMeta } from '../default-form-meta';
+import { WorkflowNodeType } from '../constants';
+import iconLoop from '../../assets/icon-loop.jpg';
+import { LoopFormRender } from './loop-form-render';
+
+let index = 0;
+export const LoopNodeRegistry: FlowNodeRegistry = {
+ type: WorkflowNodeType.Loop,
+ info: {
+ icon: iconLoop,
+ description:
+ 'Used to repeatedly execute a series of tasks by setting the number of iterations and logic.',
+ },
+ meta: {
+ /**
+ * Mark as subcanvas
+ * 子画布标记
+ */
+ isContainer: true,
+ /**
+ * The subcanvas default size setting
+ * 子画布默认大小设置
+ */
+ size: {
+ width: 560,
+ height: 400,
+ },
+ /**
+ * The subcanvas padding setting
+ * 子画布 padding 设置
+ */
+ padding: () => ({
+ top: 125,
+ bottom: 100,
+ left: 100,
+ right: 100,
+ }),
+ /**
+ * Controls the node selection status within the subcanvas
+ * 控制子画布内的节点选中状态
+ */
+ selectable(node: WorkflowNodeEntity, mousePos?: PositionSchema): boolean {
+ if (!mousePos) {
+ return true;
+ }
+ const transform = node.getData(FlowNodeTransformData);
+ // 鼠标开始时所在位置不包括当前节点时才可选中
+ return !transform.bounds.contains(mousePos.x, mousePos.y);
+ },
+ expandable: false, // disable expanded
+ },
+ onAdd() {
+ return {
+ id: `loop_${nanoid(5)}`,
+ type: 'loop',
+ data: {
+ title: `Loop_${++index}`,
+ },
+ };
+ },
+ formMeta: {
+ ...defaultFormMeta,
+ render: LoopFormRender,
+ effect: {
+ batchFor: provideBatchInputEffect,
+ },
+ },
+};
diff --git a/apps/demo-nextjs-antd/src/editor/nodes/loop/loop-form-render.tsx b/apps/demo-nextjs-antd/src/editor/nodes/loop/loop-form-render.tsx
new file mode 100644
index 00000000..d0f597a0
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/nodes/loop/loop-form-render.tsx
@@ -0,0 +1,56 @@
+import { Field, FlowNodeJSON, FormRenderProps } from '@flowgram.ai/free-layout-editor';
+import { SubCanvasRender } from '@flowgram.ai/free-container-plugin';
+import { BatchVariableSelector, IFlowRefValue } from '@flowgram.ai/form-antd-materials';
+
+import { useIsSidebar, useNodeRenderContext } from '@editor/hooks';
+import { Feedback, FormContent, FormHeader, FormItem, FormOutputs } from '@editor/form-components';
+
+interface LoopNodeJSON extends FlowNodeJSON {
+ data: {
+ batchFor: IFlowRefValue;
+ };
+}
+
+export const LoopFormRender = ({ form }: FormRenderProps) => {
+ const isSidebar = useIsSidebar();
+ const { readonly } = useNodeRenderContext();
+
+ const batchFor = (
+ name={`batchFor`}>
+ {({ field, fieldState }) => (
+
+ field.onChange({ type: 'ref', content: val })}
+ readonly={readonly}
+ hasError={Object.keys(fieldState?.errors || {}).length > 0}
+ />
+
+
+ )}
+
+ );
+
+ if (isSidebar) {
+ return (
+ <>
+
+
+ {batchFor}
+
+
+ >
+ );
+ }
+ return (
+ <>
+
+
+ {batchFor}
+
+
+
+ >
+ );
+};
diff --git a/apps/demo-nextjs-antd/src/editor/nodes/start/form-meta.tsx b/apps/demo-nextjs-antd/src/editor/nodes/start/form-meta.tsx
new file mode 100644
index 00000000..c1e48325
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/nodes/start/form-meta.tsx
@@ -0,0 +1,55 @@
+'use client';
+
+import {
+ Field,
+ FieldRenderProps,
+ FormMeta,
+ FormRenderProps,
+ ValidateTrigger,
+} from '@flowgram.ai/free-layout-editor';
+import { JsonSchemaEditor } from '@flowgram.ai/form-antd-materials';
+
+import { FlowNodeJSON, JsonSchema } from '@editor/typings';
+import { useIsSidebar } from '@editor/hooks';
+import { FormContent, FormHeader, FormOutputs } from '@editor/form-components';
+
+export const renderForm = ({ form }: FormRenderProps) => {
+ // eslint-disable-next-line react-hooks/rules-of-hooks
+ const isSidebar = useIsSidebar();
+ if (isSidebar) {
+ return (
+ <>
+
+
+ ) => (
+ <>
+ onChange(value as JsonSchema)}
+ />
+ >
+ )}
+ />
+
+ >
+ );
+ }
+ return (
+ <>
+
+
+
+
+ >
+ );
+};
+
+export const formMeta: FormMeta = {
+ render: renderForm,
+ validateTrigger: ValidateTrigger.onChange,
+ validate: {
+ title: ({ value }: { value: string }) => (value ? undefined : 'Title is required'),
+ },
+};
diff --git a/apps/demo-nextjs-antd/src/editor/nodes/start/index.ts b/apps/demo-nextjs-antd/src/editor/nodes/start/index.ts
new file mode 100644
index 00000000..16f6e2ee
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/nodes/start/index.ts
@@ -0,0 +1,35 @@
+'use client';
+
+import { FlowNodeRegistry } from '@editor/typings';
+import { WorkflowNodeType } from '../constants';
+import iconStart from '../../assets/icon-start.jpg';
+import { formMeta } from './form-meta';
+
+export const StartNodeRegistry: FlowNodeRegistry = {
+ type: WorkflowNodeType.Start,
+ meta: {
+ isStart: true,
+ deleteDisable: true,
+ copyDisable: true,
+ defaultPorts: [{ type: 'output' }],
+ size: {
+ width: 360,
+ height: 211,
+ },
+ },
+ info: {
+ icon: iconStart,
+ description:
+ 'The starting node of the workflow, used to set the information needed to initiate the workflow.',
+ },
+ /**
+ * Render node via formMeta
+ */
+ formMeta,
+ /**
+ * Start Node cannot be added
+ */
+ canAdd() {
+ return false;
+ },
+};
diff --git a/apps/demo-nextjs-antd/src/editor/plugins/context-menu-plugin/context-menu-layer.tsx b/apps/demo-nextjs-antd/src/editor/plugins/context-menu-plugin/context-menu-layer.tsx
new file mode 100644
index 00000000..10fbd42d
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/plugins/context-menu-plugin/context-menu-layer.tsx
@@ -0,0 +1,52 @@
+import { NodePanelResult, WorkflowNodePanelService } from '@flowgram.ai/free-node-panel-plugin';
+import {
+ Layer,
+ injectable,
+ inject,
+ FreeLayoutPluginContext,
+ WorkflowHoverService,
+ WorkflowNodeEntity,
+ WorkflowNodeJSON,
+} from '@flowgram.ai/free-layout-editor';
+
+@injectable()
+export class ContextMenuLayer extends Layer {
+ @inject(FreeLayoutPluginContext) ctx: FreeLayoutPluginContext;
+
+ @inject(WorkflowNodePanelService) nodePanelService: WorkflowNodePanelService;
+
+ @inject(WorkflowHoverService) hoverService: WorkflowHoverService;
+
+ onReady() {
+ this.listenPlaygroundEvent('contextmenu', (e) => {
+ this.openNodePanel(e);
+ e.preventDefault();
+ e.stopPropagation();
+ });
+ }
+
+ openNodePanel(e: MouseEvent) {
+ const pos = this.getPosFromMouseEvent(e);
+ this.nodePanelService.callNodePanel({
+ position: pos,
+ panelProps: {},
+ // handle node selection from panel - 处理从面板中选择节点
+ onSelect: async (panelParams?: NodePanelResult) => {
+ if (!panelParams) {
+ return;
+ }
+ const { nodeType, nodeJSON } = panelParams;
+ // create new workflow node based on selected type - 根据选择的类型创建新的工作流节点
+ const node: WorkflowNodeEntity = this.ctx.document.createWorkflowNodeByType(
+ nodeType,
+ pos,
+ nodeJSON ?? ({} as WorkflowNodeJSON)
+ );
+ // select the newly created node - 选择新创建的节点
+ this.ctx.selection.selection = [node];
+ },
+ // handle panel close - 处理面板关闭
+ onClose: () => {},
+ });
+ }
+}
diff --git a/apps/demo-nextjs-antd/src/editor/plugins/context-menu-plugin/context-menu-plugin.ts b/apps/demo-nextjs-antd/src/editor/plugins/context-menu-plugin/context-menu-plugin.ts
new file mode 100644
index 00000000..b4545855
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/plugins/context-menu-plugin/context-menu-plugin.ts
@@ -0,0 +1,23 @@
+import {
+ FreeLayoutPluginContext,
+ PluginCreator,
+ definePluginCreator,
+} from '@flowgram.ai/free-layout-editor';
+
+import { ContextMenuLayer } from './context-menu-layer';
+
+export interface ContextMenuPluginOptions {}
+
+/**
+ * Creates a plugin of contextmenu
+ * @param ctx - The plugin context, containing the document and other relevant information.
+ * @param options - Plugin options, currently an empty object.
+ */
+export const createContextMenuPlugin: PluginCreator = definePluginCreator<
+ ContextMenuPluginOptions,
+ FreeLayoutPluginContext
+>({
+ onInit(ctx, options) {
+ ctx.playground.registerLayer(ContextMenuLayer);
+ },
+});
diff --git a/apps/demo-nextjs-antd/src/editor/plugins/context-menu-plugin/index.ts b/apps/demo-nextjs-antd/src/editor/plugins/context-menu-plugin/index.ts
new file mode 100644
index 00000000..9940298d
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/plugins/context-menu-plugin/index.ts
@@ -0,0 +1 @@
+export { createContextMenuPlugin } from './context-menu-plugin';
diff --git a/apps/demo-nextjs-antd/src/editor/plugins/index.ts b/apps/demo-nextjs-antd/src/editor/plugins/index.ts
new file mode 100644
index 00000000..0376fd87
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/plugins/index.ts
@@ -0,0 +1,2 @@
+export { createSyncVariablePlugin } from './sync-variable-plugin/sync-variable-plugin';
+export { createContextMenuPlugin } from './context-menu-plugin';
diff --git a/apps/demo-nextjs-antd/src/editor/plugins/sync-variable-plugin/index.ts b/apps/demo-nextjs-antd/src/editor/plugins/sync-variable-plugin/index.ts
new file mode 100644
index 00000000..3fbb018d
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/plugins/sync-variable-plugin/index.ts
@@ -0,0 +1 @@
+export * from './sync-variable-plugin';
diff --git a/apps/demo-nextjs-antd/src/editor/plugins/sync-variable-plugin/sync-variable-plugin.ts b/apps/demo-nextjs-antd/src/editor/plugins/sync-variable-plugin/sync-variable-plugin.ts
new file mode 100644
index 00000000..02151bfe
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/plugins/sync-variable-plugin/sync-variable-plugin.ts
@@ -0,0 +1,80 @@
+'use client';
+
+import {
+ ASTFactory,
+ FlowNodeVariableData,
+ FreeLayoutPluginContext,
+ PluginCreator,
+ definePluginCreator,
+ getNodeForm,
+} from '@flowgram.ai/free-layout-editor';
+import { JsonSchemaUtils } from '@flowgram.ai/form-antd-materials';
+
+export interface SyncVariablePluginOptions {}
+
+/**
+ * Creates a plugin to synchronize output data to the variable engine when nodes are created or updated.
+ * @param ctx - The plugin context, containing the document and other relevant information.
+ * @param options - Plugin options, currently an empty object.
+ */
+export const createSyncVariablePlugin: PluginCreator =
+ definePluginCreator({
+ onInit(ctx, options) {
+ const flowDocument = ctx.document;
+
+ // Listen for node creation events
+ flowDocument.onNodeCreate(({ node }) => {
+ const form = getNodeForm(node);
+ const variableData = node.getData(FlowNodeVariableData);
+
+ /**
+ * Synchronizes output data to the variable engine.
+ * @param value - The output data to synchronize.
+ */
+ const syncOutputs = (value: any) => {
+ if (!value) {
+ // If the output data is empty, clear the variable
+ variableData?.clearVar();
+ return;
+ }
+
+ // Create an Type AST from the output data's JSON schema
+ // NOTICE: You can create a new function to generate an AST based on YOUR CUSTOM DSL
+ const typeAST = JsonSchemaUtils.schemaToAST(value);
+
+ if (typeAST) {
+ // Use the node's title or its ID as the title for the variable
+ const title = form?.getValueIn('title') || node.id;
+
+ // Set the variable in the variable engine
+ variableData?.setVar(
+ ASTFactory.createVariableDeclaration({
+ meta: {
+ title: `${title}`,
+ icon: node.getNodeRegistry()?.info?.icon,
+ // NOTICE: You can add more metadata here as needed
+ },
+ key: `${node.id}`,
+ type: typeAST,
+ })
+ );
+ } else {
+ // If the AST cannot be created, clear the variable
+ variableData?.clearVar();
+ }
+ };
+
+ if (form) {
+ // Initially synchronize the output data
+ syncOutputs(form.getValueIn('outputs'));
+
+ // Listen for changes in the form values and re-synchronize when outputs change
+ form.onFormValuesChange((props) => {
+ if (props.name.match(/^outputs/) || props.name.match(/^title/)) {
+ syncOutputs(form.getValueIn('outputs'));
+ }
+ });
+ }
+ });
+ },
+ });
diff --git a/apps/demo-nextjs-antd/src/editor/shortcuts/collapse/index.ts b/apps/demo-nextjs-antd/src/editor/shortcuts/collapse/index.ts
new file mode 100644
index 00000000..977e0b51
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/shortcuts/collapse/index.ts
@@ -0,0 +1,30 @@
+import {
+ FreeLayoutPluginContext,
+ ShortcutsHandler,
+ WorkflowSelectService,
+} from '@flowgram.ai/free-layout-editor';
+
+import { FlowCommandId } from '../constants';
+
+export class CollapseShortcut implements ShortcutsHandler {
+ public commandId = FlowCommandId.COLLAPSE;
+
+ public commandDetail: ShortcutsHandler['commandDetail'] = {
+ label: 'Collapse',
+ };
+
+ public shortcuts = ['meta alt openbracket', 'ctrl alt openbracket'];
+
+ private selectService: WorkflowSelectService;
+
+ constructor(context: FreeLayoutPluginContext) {
+ this.selectService = context.get(WorkflowSelectService);
+ this.execute = this.execute.bind(this);
+ }
+
+ public async execute(): Promise {
+ this.selectService.selectedNodes.forEach((node) => {
+ node.renderData.expanded = false;
+ });
+ }
+}
diff --git a/apps/demo-nextjs-antd/src/editor/shortcuts/constants.ts b/apps/demo-nextjs-antd/src/editor/shortcuts/constants.ts
new file mode 100644
index 00000000..735297c1
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/shortcuts/constants.ts
@@ -0,0 +1,17 @@
+export const WorkflowClipboardDataID = 'flowgram-workflow-clipboard-data';
+
+export enum FlowCommandId {
+ COPY = 'COPY',
+ PASTE = 'PASTE',
+ CUT = 'CUT',
+ GROUP = 'GROUP',
+ UNGROUP = 'UNGROUP',
+ COLLAPSE = 'COLLAPSE',
+ EXPAND = 'EXPAND',
+ DELETE = 'DELETE',
+ ZOOM_IN = 'ZOOM_IN',
+ ZOOM_OUT = 'ZOOM_OUT',
+ RESET_ZOOM = 'RESET_ZOOM',
+ SELECT_ALL = 'SELECT_ALL',
+ CANCEL_SELECT = 'CANCEL_SELECT',
+}
diff --git a/apps/demo-nextjs-antd/src/editor/shortcuts/copy/index.ts b/apps/demo-nextjs-antd/src/editor/shortcuts/copy/index.ts
new file mode 100644
index 00000000..e523e3f0
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/shortcuts/copy/index.ts
@@ -0,0 +1,228 @@
+import { message as Toast } from 'antd';
+import {
+ FreeLayoutPluginContext,
+ Rectangle,
+ ShortcutsHandler,
+ TransformData,
+ WorkflowDocument,
+ WorkflowEdgeJSON,
+ WorkflowJSON,
+ WorkflowLineEntity,
+ WorkflowNodeEntity,
+ WorkflowNodeJSON,
+ WorkflowNodeLinesData,
+ WorkflowNodeMeta,
+ WorkflowSelectService,
+} from '@flowgram.ai/free-layout-editor';
+
+import { WorkflowNodeType } from '@editor/nodes';
+import type {
+ WorkflowClipboardData,
+ WorkflowClipboardRect,
+ WorkflowClipboardSource,
+} from '../type';
+import { FlowCommandId, WorkflowClipboardDataID } from '../constants';
+
+export class CopyShortcut implements ShortcutsHandler {
+ public commandId = FlowCommandId.COPY;
+
+ public shortcuts = ['meta c', 'ctrl c'];
+
+ private document: WorkflowDocument;
+
+ private selectService: WorkflowSelectService;
+
+ constructor(context: FreeLayoutPluginContext) {
+ this.document = context.get(WorkflowDocument);
+ this.selectService = context.get(WorkflowSelectService);
+ this.execute = this.execute.bind(this);
+ }
+
+ /**
+ * execute copy operation - 执行复制操作
+ */
+ public async execute(): Promise {
+ if (await this.hasSelectedText()) {
+ return;
+ }
+ if (!this.isValid(this.selectedNodes)) {
+ return;
+ }
+ const data = this.toClipboardData();
+ await this.write(data);
+ }
+
+ /**
+ * has selected text - 是否有文字被选中
+ */
+ private async hasSelectedText(): Promise {
+ if (!window.getSelection()?.toString()) {
+ return false;
+ }
+ await navigator.clipboard.writeText(window.getSelection()?.toString() ?? '');
+ Toast.success({
+ content: 'Text copied',
+ });
+ return true;
+ }
+
+ /**
+ * get selected nodes - 获取选中的节点
+ */
+ private get selectedNodes(): WorkflowNodeEntity[] {
+ return this.selectService.selection.filter(
+ (n) => n instanceof WorkflowNodeEntity
+ ) as WorkflowNodeEntity[];
+ }
+
+ /**
+ * validate selected nodes - 验证选中的节点
+ */
+ private isValid(nodes: WorkflowNodeEntity[]): boolean {
+ if (nodes.length === 0) {
+ Toast.warning({
+ content: 'No nodes selected',
+ });
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * create clipboard data - 转换为剪贴板数据
+ */
+ toClipboardData(nodes?: WorkflowNodeEntity[]): WorkflowClipboardData {
+ const validNodes = this.getValidNodes(nodes ? nodes : this.selectedNodes);
+ const source = this.toSource();
+ const json = this.toJSON(validNodes);
+ const bounds = this.getEntireBounds(validNodes);
+ return {
+ type: WorkflowClipboardDataID,
+ source,
+ json,
+ bounds,
+ };
+ }
+
+ /**
+ * get valid nodes - 获取有效的节点
+ */
+ private getValidNodes(nodes: WorkflowNodeEntity[]): WorkflowNodeEntity[] {
+ return nodes.filter((n) => {
+ if (
+ [WorkflowNodeType.Start, WorkflowNodeType.End].includes(n.flowNodeType as WorkflowNodeType)
+ ) {
+ return false;
+ }
+ if (n.getNodeMeta().copyDisable) {
+ return false;
+ }
+ return true;
+ });
+ }
+
+ /**
+ * get source data - 获取来源数据
+ */
+ private toSource(): WorkflowClipboardSource {
+ return {
+ host: window.location.host,
+ };
+ }
+
+ /**
+ * convert nodes to JSON - 将节点转换为JSON
+ */
+ private toJSON(nodes: WorkflowNodeEntity[]): WorkflowJSON {
+ const nodeJSONs = this.getNodeJSONs(nodes);
+ const edgeJSONs = this.getEdgeJSONs(nodes);
+ return {
+ nodes: nodeJSONs,
+ edges: edgeJSONs,
+ };
+ }
+
+ /**
+ * get JSON representation of nodes - 获取节点的JSON表示
+ */
+ private getNodeJSONs(nodes: WorkflowNodeEntity[]): WorkflowNodeJSON[] {
+ const nodeJSONs = nodes.map((node) => {
+ const nodeJSON = this.document.toNodeJSON(node);
+ if (!nodeJSON.meta?.position) {
+ return nodeJSON;
+ }
+ const { bounds } = node.getData(TransformData);
+ // Use absolute positioning as coordinates - 使用绝对定位作为坐标
+ nodeJSON.meta.position = {
+ x: bounds.x,
+ y: bounds.y,
+ };
+ return nodeJSON;
+ });
+ return nodeJSONs.filter(Boolean);
+ }
+
+ /**
+ * get edges of all nodes - 获取所有节点的边
+ */
+ private getEdgeJSONs(nodes: WorkflowNodeEntity[]): WorkflowEdgeJSON[] {
+ const lineSet = new Set();
+ const nodeIdSet = new Set(nodes.map((n) => n.id));
+ nodes.forEach((node) => {
+ const linesData = node.getData(WorkflowNodeLinesData);
+ const lines = [...linesData.inputLines, ...linesData.outputLines];
+ lines.forEach((line) => {
+ if (nodeIdSet.has(line.from.id) && line.to?.id && nodeIdSet.has(line.to.id)) {
+ lineSet.add(line);
+ }
+ });
+ });
+ return Array.from(lineSet).map((line) => line.toJSON());
+ }
+
+ /**
+ * get bounding rectangle of all nodes - 获取所有节点的边界矩形
+ */
+ private getEntireBounds(nodes: WorkflowNodeEntity[]): WorkflowClipboardRect {
+ const bounds = nodes.map((node) => node.getData(TransformData).bounds);
+ const rect = Rectangle.enlarge(bounds);
+ return {
+ x: rect.x,
+ y: rect.y,
+ width: rect.width,
+ height: rect.height,
+ };
+ }
+
+ /**
+ * write data to clipboard - 将数据写入剪贴板
+ */
+ private async write(data: WorkflowClipboardData): Promise {
+ try {
+ await navigator.clipboard.writeText(JSON.stringify(data));
+ this.notifySuccess();
+ } catch (err) {
+ console.error('Failed to write text: ', err);
+ }
+ }
+
+ /**
+ * show success notification - 显示成功通知
+ */
+ private notifySuccess(): void {
+ const nodeTypes = this.selectedNodes.map((node) => node.flowNodeType);
+ if (nodeTypes.includes('start') || nodeTypes.includes('end')) {
+ Toast.warning({
+ content:
+ 'The Start/End node cannot be duplicated, other nodes have been copied to the clipboard',
+ // showClose: false,
+ });
+ return;
+ }
+ Toast.success({
+ content: 'Nodes have been copied to the clipboard',
+ // showClose: false,
+ });
+ return;
+ }
+}
diff --git a/apps/demo-nextjs-antd/src/editor/shortcuts/delete/index.ts b/apps/demo-nextjs-antd/src/editor/shortcuts/delete/index.ts
new file mode 100644
index 00000000..5465d9f7
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/shortcuts/delete/index.ts
@@ -0,0 +1,108 @@
+import { message as Toast } from 'antd';
+import {
+ FreeLayoutPluginContext,
+ HistoryService,
+ ShortcutsHandler,
+ WorkflowDocument,
+ WorkflowLineEntity,
+ WorkflowNodeEntity,
+ WorkflowNodeMeta,
+ WorkflowSelectService,
+} from '@flowgram.ai/free-layout-editor';
+
+import { WorkflowNodeType } from '@editor/nodes';
+import { FlowCommandId } from '../constants';
+
+export class DeleteShortcut implements ShortcutsHandler {
+ public commandId = FlowCommandId.DELETE;
+
+ public shortcuts = ['backspace', 'delete'];
+
+ private document: WorkflowDocument;
+
+ private selectService: WorkflowSelectService;
+
+ private historyService: HistoryService;
+
+ /**
+ * initialize delete shortcut - 初始化删除快捷键
+ */
+ constructor(context: FreeLayoutPluginContext) {
+ this.document = context.get(WorkflowDocument);
+ this.selectService = context.get(WorkflowSelectService);
+ this.historyService = context.get(HistoryService);
+ this.execute = this.execute.bind(this);
+ }
+
+ /**
+ * execute delete operation - 执行删除操作
+ */
+ public async execute(nodes?: WorkflowNodeEntity[]): Promise {
+ const selection = Array.isArray(nodes) ? nodes : this.selectService.selection;
+ if (
+ !this.isValid(
+ selection.filter((n) => n instanceof WorkflowNodeEntity) as WorkflowNodeEntity[]
+ )
+ ) {
+ return;
+ }
+ // Merge actions to redo/undo
+ this.historyService.startTransaction();
+ // delete selected entities - 删除选中实体
+ selection.forEach((entity) => {
+ if (entity instanceof WorkflowNodeEntity) {
+ this.removeNode(entity);
+ } else if (entity instanceof WorkflowLineEntity) {
+ this.removeLine(entity);
+ } else {
+ entity.dispose();
+ }
+ });
+ // filter out disposed entities - 过滤掉已删除的实体
+ this.selectService.selection = this.selectService.selection.filter((s) => !s.disposed);
+ this.historyService.endTransaction();
+ }
+
+ /**
+ * validate if nodes can be deleted - 验证节点是否可以删除
+ */
+ private isValid(nodes: WorkflowNodeEntity[]): boolean {
+ const hasSystemNodes = nodes.some((n) =>
+ [WorkflowNodeType.Start, WorkflowNodeType.End].includes(n.flowNodeType as WorkflowNodeType)
+ );
+ if (hasSystemNodes) {
+ Toast.error({
+ content: 'Start or End node cannot be deleted',
+ // showClose: false,
+ });
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * remove node from workflow - 从工作流中删除节点
+ */
+ private removeNode(node: WorkflowNodeEntity): void {
+ if (!this.document.canRemove(node)) {
+ return;
+ }
+ const nodeMeta = node.getNodeMeta();
+ const subCanvas = nodeMeta.subCanvas?.(node);
+ if (subCanvas?.isCanvas) {
+ subCanvas.parentNode.dispose();
+ return;
+ }
+ node.dispose();
+ }
+
+ /**
+ * remove line from workflow - 从工作流中删除连线
+ */
+ private removeLine(line: WorkflowLineEntity): void {
+ if (!this.document.linesManager.canRemove(line)) {
+ return;
+ }
+ line.dispose();
+ }
+}
diff --git a/apps/demo-nextjs-antd/src/editor/shortcuts/expand/index.ts b/apps/demo-nextjs-antd/src/editor/shortcuts/expand/index.ts
new file mode 100644
index 00000000..f59dac9f
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/shortcuts/expand/index.ts
@@ -0,0 +1,30 @@
+import {
+ FreeLayoutPluginContext,
+ ShortcutsHandler,
+ WorkflowSelectService,
+} from '@flowgram.ai/free-layout-editor';
+
+import { FlowCommandId } from '../constants';
+
+export class ExpandShortcut implements ShortcutsHandler {
+ public commandId = FlowCommandId.EXPAND;
+
+ public commandDetail: ShortcutsHandler['commandDetail'] = {
+ label: 'Expand',
+ };
+
+ public shortcuts = ['meta alt closebracket', 'ctrl alt openbracket'];
+
+ private selectService: WorkflowSelectService;
+
+ constructor(context: FreeLayoutPluginContext) {
+ this.selectService = context.get(WorkflowSelectService);
+ this.execute = this.execute.bind(this);
+ }
+
+ public async execute(): Promise {
+ this.selectService.selectedNodes.forEach((node) => {
+ node.renderData.expanded = true;
+ });
+ }
+}
diff --git a/apps/demo-nextjs-antd/src/editor/shortcuts/index.ts b/apps/demo-nextjs-antd/src/editor/shortcuts/index.ts
new file mode 100644
index 00000000..84b2b382
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/shortcuts/index.ts
@@ -0,0 +1,2 @@
+export * from './constants';
+export * from './shortcuts';
diff --git a/apps/demo-nextjs-antd/src/editor/shortcuts/paste/index.ts b/apps/demo-nextjs-antd/src/editor/shortcuts/paste/index.ts
new file mode 100644
index 00000000..b41a4dee
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/shortcuts/paste/index.ts
@@ -0,0 +1,192 @@
+import { message as Toast } from 'antd';
+import {
+ EntityManager,
+ FlowNodeTransformData,
+ FreeLayoutPluginContext,
+ IPoint,
+ Rectangle,
+ ShortcutsHandler,
+ WorkflowDocument,
+ WorkflowDragService,
+ WorkflowHoverService,
+ WorkflowJSON,
+ WorkflowNodeEntity,
+ WorkflowNodeMeta,
+ WorkflowSelectService,
+ delay,
+} from '@flowgram.ai/free-layout-editor';
+
+import { WorkflowClipboardData, WorkflowClipboardRect } from '../type';
+import { FlowCommandId, WorkflowClipboardDataID } from '../constants';
+import { generateUniqueWorkflow } from './unique-workflow';
+
+export class PasteShortcut implements ShortcutsHandler {
+ public commandId = FlowCommandId.PASTE;
+
+ public shortcuts = ['meta v', 'ctrl v'];
+
+ private document: WorkflowDocument;
+
+ private selectService: WorkflowSelectService;
+
+ private entityManager: EntityManager;
+
+ private hoverService: WorkflowHoverService;
+
+ private dragService: WorkflowDragService;
+
+ /**
+ * initialize paste shortcut handler - 初始化粘贴快捷键处理器
+ */
+ constructor(context: FreeLayoutPluginContext) {
+ this.document = context.get(WorkflowDocument);
+ this.selectService = context.get(WorkflowSelectService);
+ this.entityManager = context.get(EntityManager);
+ this.hoverService = context.get(WorkflowHoverService);
+ this.dragService = context.get(WorkflowDragService);
+ this.execute = this.execute.bind(this);
+ }
+
+ /**
+ * execute paste action - 执行粘贴操作
+ */
+ public async execute(): Promise {
+ const data = await this.tryReadClipboard();
+ if (!data) {
+ return;
+ }
+ if (!this.isValidData(data)) {
+ return;
+ }
+ const nodes = this.apply(data);
+ if (nodes.length > 0) {
+ Toast.success({
+ content: 'Copy successfully',
+ // showClose: false,
+ });
+ // wait for nodes to render - 等待节点渲染
+ await this.nextTick();
+ // scroll to visible area - 滚动到可视区域
+ this.scrollNodesToView(nodes);
+ }
+ return nodes;
+ }
+
+ /** apply clipboard data - 应用剪切板数据 */
+ public apply(data: WorkflowClipboardData): WorkflowNodeEntity[] {
+ // extract raw json from clipboard data - 从剪贴板数据中提取原始JSON
+ const { json: rawJSON } = data;
+ const json = generateUniqueWorkflow({
+ json: rawJSON,
+ isUniqueId: (id: string) => !this.entityManager.getEntityById(id),
+ });
+
+ const offset = this.calcPasteOffset(data.bounds);
+ const parent = this.getSelectedContainer();
+ this.applyOffset({ json, offset, parent });
+ const { nodes } = this.document.renderJSON(json, {
+ parent,
+ });
+ this.selectNodes(nodes);
+ return nodes;
+ }
+
+ private isValidData(data?: WorkflowClipboardData): boolean {
+ if (data?.type !== WorkflowClipboardDataID) {
+ Toast.error({
+ content: 'Invalid clipboard data',
+ });
+ return false;
+ }
+ // 跨域名表示不同环境,上架插件不同,不能复制
+ if (data.source.host !== window.location.host) {
+ Toast.error({
+ content: 'Cannot paste nodes from different host',
+ });
+ return false;
+ }
+ return true;
+ }
+
+ /** try to read clipboard - 尝试读取剪贴板 */
+ private async tryReadClipboard(): Promise {
+ try {
+ // need user permission to access clipboard, may throw NotAllowedError - 需要用户授予网页剪贴板读取权限, 如果用户没有授予权限, 代码可能会抛出异常 NotAllowedError
+ const text: string = (await navigator.clipboard.readText()) || '';
+ const clipboardData: WorkflowClipboardData = JSON.parse(text);
+ return clipboardData;
+ } catch (e) {
+ // clipboard data is not fixed, no need to show error - 这里本身剪贴板里的数据就不固定,所以没必要报错
+ return;
+ }
+ }
+
+ /** calculate paste offset - 计算粘贴偏移 */
+ private calcPasteOffset(boundsData: WorkflowClipboardRect): IPoint {
+ // extract bounds data - 提取边界数据
+ const { x, y, width, height } = boundsData;
+ const rect = new Rectangle(x, y, width, height);
+ const { center } = rect;
+ const mousePos = this.hoverService.hoveredPos;
+ return {
+ x: mousePos.x - center.x,
+ y: mousePos.y - center.y,
+ };
+ }
+
+ /**
+ * apply offset to node positions - 应用偏移到节点位置
+ */
+ private applyOffset(params: {
+ json: WorkflowJSON;
+ offset: IPoint;
+ parent?: WorkflowNodeEntity;
+ }): void {
+ const { json, offset, parent } = params;
+ json.nodes.forEach((nodeJSON) => {
+ if (!nodeJSON.meta?.position) {
+ return;
+ }
+ // calculate new position - 计算新位置
+ let position = {
+ x: nodeJSON.meta.position.x + offset.x,
+ y: nodeJSON.meta.position.y + offset.y,
+ };
+ if (parent) {
+ position = this.dragService.adjustSubNodePosition(
+ nodeJSON.type as string,
+ parent,
+ position
+ );
+ }
+ nodeJSON.meta.position = position;
+ });
+ }
+
+ /** get selected container node - 获取鼠标选中的容器 */
+ private getSelectedContainer(): WorkflowNodeEntity | undefined {
+ const { activatedNode } = this.selectService;
+ return activatedNode?.getNodeMeta().isContainer ? activatedNode : undefined;
+ }
+
+ /** select nodes - 选中节点 */
+ private selectNodes(nodes: WorkflowNodeEntity[]): void {
+ this.selectService.selection = nodes;
+ }
+
+ /** scroll to nodes - 滚动到节点 */
+ private async scrollNodesToView(nodes: WorkflowNodeEntity[]): Promise {
+ const nodeBounds = nodes.map((node) => node.getData(FlowNodeTransformData).bounds);
+ await this.document.playgroundConfig.scrollToView({
+ bounds: Rectangle.enlarge(nodeBounds),
+ });
+ }
+
+ /** wait for next frame - 等待下一帧 */
+ private async nextTick(): Promise {
+ // 16ms is one render frame - 16ms 为一个渲染帧
+ const frameTime = 16;
+ await delay(frameTime);
+ await new Promise((resolve) => requestAnimationFrame(resolve));
+ }
+}
diff --git a/apps/demo-nextjs-antd/src/editor/shortcuts/paste/traverse.ts b/apps/demo-nextjs-antd/src/editor/shortcuts/paste/traverse.ts
new file mode 100644
index 00000000..7d2c2286
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/shortcuts/paste/traverse.ts
@@ -0,0 +1,184 @@
+// traverse value type - 遍历值类型
+export type TraverseValue = any;
+
+// traverse node interface - 遍历节点接口
+export interface TraverseNode {
+ value: TraverseValue; // node value - 节点值
+ container?: TraverseValue; // parent container - 父容器
+ parent?: TraverseNode; // parent node - 父节点
+ key?: string; // object key - 对象键名
+ index?: number; // array index - 数组索引
+}
+
+// traverse context interface - 遍历上下文接口
+export interface TraverseContext {
+ node: TraverseNode; // current node - 当前节点
+ setValue: (value: TraverseValue) => void; // set value function - 设置值函数
+ getParents: () => TraverseNode[]; // get parents function - 获取父节点函数
+ getPath: () => Array; // get path function - 获取路径函数
+ getStringifyPath: () => string; // get string path function - 获取字符串路径函数
+ deleteSelf: () => void; // delete self function - 删除自身函数
+}
+
+// traverse handler type - 遍历处理器类型
+export type TraverseHandler = (context: TraverseContext) => void;
+
+/**
+ * traverse object deeply and handle each value - 深度遍历对象并处理每个值
+ * @param value traverse target - 遍历目标
+ * @param handle handler function - 处理函数
+ */
+export const traverse = (
+ value: T,
+ handler: TraverseHandler | TraverseHandler[]
+): T => {
+ const traverseHandler: TraverseHandler = Array.isArray(handler)
+ ? (context: TraverseContext) => {
+ handler.forEach((handlerFn) => handlerFn(context));
+ }
+ : handler;
+ TraverseUtils.traverseNodes({ value }, traverseHandler);
+ return value;
+};
+
+namespace TraverseUtils {
+ /**
+ * traverse nodes deeply and handle each value - 深度遍历节点并处理每个值
+ * @param node traverse node - 遍历节点
+ * @param handle handler function - 处理函数
+ */
+ export const traverseNodes = (node: TraverseNode, handle: TraverseHandler): void => {
+ const { value } = node;
+ if (!value) {
+ // handle null value - 处理空值
+ return;
+ }
+ if (Object.prototype.toString.call(value) === '[object Object]') {
+ // traverse object properties - 遍历对象属性
+ Object.entries(value).forEach(([key, item]) =>
+ traverseNodes(
+ {
+ value: item,
+ container: value,
+ key,
+ parent: node,
+ },
+ handle
+ )
+ );
+ } else if (Array.isArray(value)) {
+ // traverse array elements from end to start - 从末尾开始遍历数组元素
+ for (let index = value.length - 1; index >= 0; index--) {
+ const item: string = value[index];
+ traverseNodes(
+ {
+ value: item,
+ container: value,
+ index,
+ parent: node,
+ },
+ handle
+ );
+ }
+ }
+ const context: TraverseContext = createContext({ node });
+ handle(context);
+ };
+
+ /**
+ * create traverse context - 创建遍历上下文
+ * @param node traverse node - 遍历节点
+ */
+ const createContext = ({ node }: { node: TraverseNode }): TraverseContext => ({
+ node,
+ setValue: (value: unknown) => setValue(node, value),
+ getParents: () => getParents(node),
+ getPath: () => getPath(node),
+ getStringifyPath: () => getStringifyPath(node),
+ deleteSelf: () => deleteSelf(node),
+ });
+
+ /**
+ * set node value - 设置节点值
+ * @param node traverse node - 遍历节点
+ * @param value new value - 新值
+ */
+ const setValue = (node: TraverseNode, value: unknown) => {
+ // handle empty value - 处理空值
+ if (!value || !node) {
+ return;
+ }
+ node.value = value;
+ // get container info from parent scope - 从父作用域获取容器信息
+ const { container, key, index } = node;
+ if (key && container) {
+ container[key] = value;
+ } else if (typeof index === 'number') {
+ container[index] = value;
+ }
+ };
+
+ /**
+ * get parent nodes - 获取父节点列表
+ * @param node traverse node - 遍历节点
+ */
+ const getParents = (node: TraverseNode): TraverseNode[] => {
+ const parents: TraverseNode[] = [];
+ let currentNode: TraverseNode | undefined = node;
+ while (currentNode) {
+ parents.unshift(currentNode);
+ currentNode = currentNode.parent;
+ }
+ return parents;
+ };
+
+ /**
+ * get node path - 获取节点路径
+ * @param node traverse node - 遍历节点
+ */
+ const getPath = (node: TraverseNode): Array => {
+ const path: Array = [];
+ const parents = getParents(node);
+ parents.forEach((parent) => {
+ if (parent.key) {
+ path.unshift(parent.key);
+ } else if (parent.index) {
+ path.unshift(parent.index);
+ }
+ });
+ return path;
+ };
+
+ /**
+ * get stringify path - 获取字符串路径
+ * @param node traverse node - 遍历节点
+ */
+ const getStringifyPath = (node: TraverseNode): string => {
+ const path = getPath(node);
+ return path.reduce((stringifyPath: string, pathItem: string | number) => {
+ if (typeof pathItem === 'string') {
+ const re = /\W/g;
+ if (re.test(pathItem)) {
+ // handle special characters - 处理特殊字符
+ return `${stringifyPath}["${pathItem}"]`;
+ }
+ return `${stringifyPath}.${pathItem}`;
+ } else {
+ return `${stringifyPath}[${pathItem}]`;
+ }
+ }, '');
+ };
+
+ /**
+ * delete current node - 删除当前节点
+ * @param node traverse node - 遍历节点
+ */
+ const deleteSelf = (node: TraverseNode): void => {
+ const { container, key, index } = node;
+ if (key && container) {
+ delete container[key];
+ } else if (typeof index === 'number') {
+ container.splice(index, 1);
+ }
+ };
+}
diff --git a/apps/demo-nextjs-antd/src/editor/shortcuts/paste/unique-workflow.ts b/apps/demo-nextjs-antd/src/editor/shortcuts/paste/unique-workflow.ts
new file mode 100644
index 00000000..acf67f2e
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/shortcuts/paste/unique-workflow.ts
@@ -0,0 +1,108 @@
+import { customAlphabet } from 'nanoid';
+import type { WorkflowJSON, WorkflowNodeJSON } from '@flowgram.ai/free-layout-editor';
+
+import { TraverseContext, traverse } from './traverse';
+
+namespace UniqueWorkflowUtils {
+ /** generate unique id - 生成唯一ID */
+ const generateUniqueId = customAlphabet('1234567890', 6); // create a function to generate 6-digit number - 创建一个生成6位数字的函数
+
+ /** get all node ids from workflow json - 从工作流JSON中获取所有节点ID */
+ export const getAllNodeIds = (json: WorkflowJSON): string[] => {
+ const nodeIds = new Set(); // use set to store unique ids - 使用Set存储唯一ID
+ const addNodeId = (node: WorkflowNodeJSON) => {
+ nodeIds.add(node.id);
+ if (node.blocks?.length) {
+ node.blocks.forEach((child) => addNodeId(child)); // recursively add child node ids - 递归添加子节点ID
+ }
+ };
+ json.nodes.forEach((node) => addNodeId(node));
+ return Array.from(nodeIds);
+ };
+
+ /** generate node replacement mapping - 生成节点替换映射 */
+ export const generateNodeReplaceMap = (
+ nodeIds: string[],
+ isUniqueId: (id: string) => boolean
+ ): Map => {
+ const nodeReplaceMap = new Map(); // create map for id replacement - 创建ID替换映射
+ nodeIds.forEach((id) => {
+ if (isUniqueId(id)) {
+ nodeReplaceMap.set(id, id); // keep original id if unique - 如果ID唯一则保持不变
+ } else {
+ let newId: string;
+ do {
+ newId = generateUniqueId(); // generate new id until unique - 生成新ID直到唯一
+ } while (!isUniqueId(newId));
+ nodeReplaceMap.set(id, newId);
+ }
+ });
+ return nodeReplaceMap;
+ };
+
+ /** check if value exists - 检查值是否存在 */
+ const isExist = (value: unknown): boolean => value !== null && value !== undefined;
+
+ /** check if node should be handled - 检查节点是否需要处理 */
+ const shouldHandle = (context: TraverseContext): boolean => {
+ const { node } = context;
+ // check edge data - 检查边数据
+ if (
+ node?.key &&
+ ['sourceNodeID', 'targetNodeID'].includes(node.key) &&
+ node.parent?.parent?.key === 'edges'
+ ) {
+ return true;
+ }
+ // check node data - 检查节点数据
+ if (
+ node?.key === 'id' &&
+ isExist(node.container?.type) &&
+ isExist(node.container?.meta) &&
+ isExist(node.container?.data)
+ ) {
+ return true;
+ }
+ // check variable data - 检查变量数据
+ if (
+ node?.key === 'blockID' &&
+ isExist(node.container?.name) &&
+ node.container?.source === 'block-output'
+ ) {
+ return true;
+ }
+ return false;
+ };
+
+ /**
+ * replace node ids in workflow json - 替换工作流JSON中的节点ID
+ * notice: this method has side effects, it will modify the input json to avoid deep copy overhead
+ * - 注意:此方法有副作用,会修改输入的json以避免深拷贝开销
+ */
+ export const replaceNodeId = (
+ json: WorkflowJSON,
+ nodeReplaceMap: Map
+ ): WorkflowJSON => {
+ traverse(json, (context) => {
+ if (!shouldHandle(context)) {
+ return;
+ }
+ const { node } = context;
+ if (nodeReplaceMap.has(node.value)) {
+ context.setValue(nodeReplaceMap.get(node.value)); // replace old id with new id - 用新ID替换旧ID
+ }
+ });
+ return json;
+ };
+}
+
+/** generate unique workflow json - 生成唯一工作流JSON */
+export const generateUniqueWorkflow = (params: {
+ json: WorkflowJSON;
+ isUniqueId: (id: string) => boolean;
+}): WorkflowJSON => {
+ const { json, isUniqueId } = params;
+ const nodeIds = UniqueWorkflowUtils.getAllNodeIds(json); // get all existing node ids - 获取所有现有节点ID
+ const nodeReplaceMap = UniqueWorkflowUtils.generateNodeReplaceMap(nodeIds, isUniqueId); // generate id replacement map - 生成ID替换映射
+ return UniqueWorkflowUtils.replaceNodeId(json, nodeReplaceMap); // replace all node ids - 替换所有节点ID
+};
diff --git a/apps/demo-nextjs-antd/src/editor/shortcuts/select-all/index.ts b/apps/demo-nextjs-antd/src/editor/shortcuts/select-all/index.ts
new file mode 100644
index 00000000..e70029d6
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/shortcuts/select-all/index.ts
@@ -0,0 +1,29 @@
+import {
+ FreeLayoutPluginContext,
+ Playground,
+ ShortcutsHandler,
+ WorkflowDocument,
+} from '@flowgram.ai/free-layout-editor';
+
+import { FlowCommandId } from '../constants';
+
+export class SelectAllShortcut implements ShortcutsHandler {
+ public commandId = FlowCommandId.SELECT_ALL;
+
+ public shortcuts = ['meta a', 'ctrl a'];
+
+ private document: WorkflowDocument;
+
+ private playground: Playground;
+
+ constructor(context: FreeLayoutPluginContext) {
+ this.document = context.get(WorkflowDocument);
+ this.playground = context.playground;
+ this.execute = this.execute.bind(this);
+ }
+
+ public async execute(): Promise {
+ const allNodes = this.document.getAllNodes();
+ this.playground.selectionService.selection = allNodes;
+ }
+}
diff --git a/apps/demo-nextjs-antd/src/editor/shortcuts/shortcuts.ts b/apps/demo-nextjs-antd/src/editor/shortcuts/shortcuts.ts
new file mode 100644
index 00000000..7228c708
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/shortcuts/shortcuts.ts
@@ -0,0 +1,23 @@
+import { FreeLayoutPluginContext, ShortcutsRegistry } from '@flowgram.ai/free-layout-editor';
+
+import { ZoomOutShortcut } from './zoom-out';
+import { ZoomInShortcut } from './zoom-in';
+import { SelectAllShortcut } from './select-all';
+import { PasteShortcut } from './paste';
+import { ExpandShortcut } from './expand';
+import { DeleteShortcut } from './delete';
+import { CopyShortcut } from './copy';
+import { CollapseShortcut } from './collapse';
+
+export function shortcuts(shortcutsRegistry: ShortcutsRegistry, ctx: FreeLayoutPluginContext) {
+ shortcutsRegistry.addHandlers(
+ new CopyShortcut(ctx),
+ new PasteShortcut(ctx),
+ new SelectAllShortcut(ctx),
+ new CollapseShortcut(ctx),
+ new ExpandShortcut(ctx),
+ new DeleteShortcut(ctx),
+ new ZoomInShortcut(ctx),
+ new ZoomOutShortcut(ctx)
+ );
+}
diff --git a/apps/demo-nextjs-antd/src/editor/shortcuts/type.ts b/apps/demo-nextjs-antd/src/editor/shortcuts/type.ts
new file mode 100644
index 00000000..175fcaec
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/shortcuts/type.ts
@@ -0,0 +1,22 @@
+import type { WorkflowJSON } from '@flowgram.ai/free-layout-editor';
+
+import type { WorkflowClipboardDataID } from './constants';
+
+export interface WorkflowClipboardSource {
+ host: string;
+ // more: id?, workspaceId? etc.
+}
+
+export interface WorkflowClipboardRect {
+ x: number;
+ y: number;
+ width: number;
+ height: number;
+}
+
+export interface WorkflowClipboardData {
+ type: typeof WorkflowClipboardDataID;
+ json: WorkflowJSON;
+ source: WorkflowClipboardSource;
+ bounds: WorkflowClipboardRect;
+}
diff --git a/apps/demo-nextjs-antd/src/editor/shortcuts/zoom-in/index.ts b/apps/demo-nextjs-antd/src/editor/shortcuts/zoom-in/index.ts
new file mode 100644
index 00000000..55d43455
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/shortcuts/zoom-in/index.ts
@@ -0,0 +1,27 @@
+import {
+ FreeLayoutPluginContext,
+ PlaygroundConfigEntity,
+ ShortcutsHandler,
+} from '@flowgram.ai/free-layout-editor';
+
+import { FlowCommandId } from '../constants';
+
+export class ZoomInShortcut implements ShortcutsHandler {
+ public commandId = FlowCommandId.ZOOM_IN;
+
+ public shortcuts = ['meta =', 'ctrl ='];
+
+ private playgroundConfig: PlaygroundConfigEntity;
+
+ constructor(context: FreeLayoutPluginContext) {
+ this.playgroundConfig = context.get(PlaygroundConfigEntity);
+ this.execute = this.execute.bind(this);
+ }
+
+ public async execute(): Promise {
+ if (this.playgroundConfig.zoom > 1.9) {
+ return;
+ }
+ this.playgroundConfig.zoomin();
+ }
+}
diff --git a/apps/demo-nextjs-antd/src/editor/shortcuts/zoom-out/index.ts b/apps/demo-nextjs-antd/src/editor/shortcuts/zoom-out/index.ts
new file mode 100644
index 00000000..a4bab37f
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/shortcuts/zoom-out/index.ts
@@ -0,0 +1,27 @@
+import {
+ FreeLayoutPluginContext,
+ PlaygroundConfigEntity,
+ ShortcutsHandler,
+} from '@flowgram.ai/free-layout-editor';
+
+import { FlowCommandId } from '../constants';
+
+export class ZoomOutShortcut implements ShortcutsHandler {
+ public commandId = FlowCommandId.ZOOM_OUT;
+
+ public shortcuts = ['meta -', 'ctrl -'];
+
+ private playgroundConfig: PlaygroundConfigEntity;
+
+ constructor(context: FreeLayoutPluginContext) {
+ this.playgroundConfig = context.get(PlaygroundConfigEntity);
+ this.execute = this.execute.bind(this);
+ }
+
+ public async execute(): Promise {
+ if (this.playgroundConfig.zoom > 1.9) {
+ return;
+ }
+ this.playgroundConfig.zoomout();
+ }
+}
diff --git a/apps/demo-nextjs-antd/src/editor/style/index.css b/apps/demo-nextjs-antd/src/editor/style/index.css
new file mode 100644
index 00000000..74425001
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/style/index.css
@@ -0,0 +1 @@
+@import './var.css';
diff --git a/apps/demo-nextjs-antd/src/editor/style/var.css b/apps/demo-nextjs-antd/src/editor/style/var.css
new file mode 100644
index 00000000..904bdd4d
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/style/var.css
@@ -0,0 +1,4 @@
+:root {
+ --node-shadow:
+ 0 2px 6px 0 rgba(0, 0, 0, 0.04), 0 4px 12px 0 rgba(0, 0, 0, 0.02);
+}
diff --git a/apps/demo-nextjs-antd/src/editor/typings/flow-value/config.json b/apps/demo-nextjs-antd/src/editor/typings/flow-value/config.json
new file mode 100644
index 00000000..c7faad8d
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/typings/flow-value/config.json
@@ -0,0 +1,5 @@
+{
+ "name": "flow-value",
+ "depMaterials": [],
+ "depPackages": []
+}
diff --git a/apps/demo-nextjs-antd/src/editor/typings/flow-value/index.ts b/apps/demo-nextjs-antd/src/editor/typings/flow-value/index.ts
new file mode 100644
index 00000000..6627f3fe
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/typings/flow-value/index.ts
@@ -0,0 +1,27 @@
+export interface IFlowConstantValue {
+ type: 'constant';
+ content?: string | number | boolean;
+}
+
+export interface IFlowRefValue {
+ type: 'ref';
+ content?: string[];
+}
+
+export interface IFlowExpressionValue {
+ type: 'expression';
+ content?: string;
+}
+
+export interface IFlowTemplateValue {
+ type: 'template';
+ content?: string;
+}
+
+export type IFlowValue =
+ | IFlowConstantValue
+ | IFlowRefValue
+ | IFlowExpressionValue
+ | IFlowTemplateValue;
+
+export type IFlowConstantRefValue = IFlowConstantValue | IFlowRefValue;
diff --git a/apps/demo-nextjs-antd/src/editor/typings/index.ts b/apps/demo-nextjs-antd/src/editor/typings/index.ts
new file mode 100644
index 00000000..70f1cc22
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/typings/index.ts
@@ -0,0 +1,3 @@
+export * from './node';
+export * from './json-schema';
+export * from './flow-value';
diff --git a/apps/demo-nextjs-antd/src/editor/typings/json-schema/config.json b/apps/demo-nextjs-antd/src/editor/typings/json-schema/config.json
new file mode 100644
index 00000000..3a6aa9ff
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/typings/json-schema/config.json
@@ -0,0 +1,5 @@
+{
+ "name": "json-schema",
+ "depMaterials": [],
+ "depPackages": []
+}
diff --git a/apps/demo-nextjs-antd/src/editor/typings/json-schema/index.ts b/apps/demo-nextjs-antd/src/editor/typings/json-schema/index.ts
new file mode 100644
index 00000000..250a9275
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/typings/json-schema/index.ts
@@ -0,0 +1,4 @@
+import type { IJsonSchema, IBasicJsonSchema } from '@flowgram.ai/form-antd-materials';
+
+export type BasicType = IBasicJsonSchema;
+export type JsonSchema = IJsonSchema;
diff --git a/apps/demo-nextjs-antd/src/editor/typings/node.ts b/apps/demo-nextjs-antd/src/editor/typings/node.ts
new file mode 100644
index 00000000..761c8ed0
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/typings/node.ts
@@ -0,0 +1,69 @@
+import type { StaticImageData } from 'next/image';
+import {
+ FlowNodeEntity,
+ WorkflowNodeJSON as FlowNodeJSONDefault,
+ WorkflowNodeRegistry as FlowNodeRegistryDefault,
+ FreeLayoutPluginContext,
+ type WorkflowEdgeJSON,
+ WorkflowNodeMeta,
+} from '@flowgram.ai/free-layout-editor';
+import { IFlowValue } from '@flowgram.ai/form-antd-materials';
+
+import { type JsonSchema } from './json-schema';
+
+/**
+ * You can customize the data of the node, and here you can use JsonSchema to define the input and output of the node
+ * 你可以自定义节点的 data 业务数据, 这里演示 通过 JsonSchema 来定义节点的输入/输出
+ */
+export interface FlowNodeJSON extends FlowNodeJSONDefault {
+ data: {
+ /**
+ * Node title
+ */
+ title?: string;
+ /**
+ * Inputs data values
+ */
+ inputsValues?: Record;
+ /**
+ * Define the inputs data of the node by JsonSchema
+ */
+ inputs?: JsonSchema;
+ /**
+ * Define the outputs data of the node by JsonSchema
+ */
+ outputs?: JsonSchema;
+ /**
+ * Rest properties
+ */
+ [key: string]: any;
+ };
+}
+
+/**
+ * You can customize your own node meta
+ * 你可以自定义节点的meta
+ */
+export interface FlowNodeMeta extends WorkflowNodeMeta {
+ sidebarDisable?: boolean;
+}
+
+/**
+ * You can customize your own node registry
+ * 你可以自定义节点的注册器
+ */
+export interface FlowNodeRegistry extends FlowNodeRegistryDefault {
+ meta: FlowNodeMeta;
+ info?: {
+ icon: StaticImageData;
+ description: string;
+ };
+ canAdd?: (ctx: FreeLayoutPluginContext) => boolean;
+ canDelete?: (ctx: FreeLayoutPluginContext, from: FlowNodeEntity) => boolean;
+ onAdd?: (ctx: FreeLayoutPluginContext) => FlowNodeJSON;
+}
+
+export interface FlowDocumentJSON {
+ nodes: FlowNodeJSON[];
+ edges: WorkflowEdgeJSON[];
+}
diff --git a/apps/demo-nextjs-antd/src/editor/utils/index.ts b/apps/demo-nextjs-antd/src/editor/utils/index.ts
new file mode 100644
index 00000000..6a4ffb59
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/utils/index.ts
@@ -0,0 +1 @@
+export { onDragLineEnd } from './on-drag-line-end';
diff --git a/apps/demo-nextjs-antd/src/editor/utils/on-drag-line-end.ts b/apps/demo-nextjs-antd/src/editor/utils/on-drag-line-end.ts
new file mode 100644
index 00000000..54f4d3e9
--- /dev/null
+++ b/apps/demo-nextjs-antd/src/editor/utils/on-drag-line-end.ts
@@ -0,0 +1,91 @@
+import {
+ WorkflowNodePanelService,
+ WorkflowNodePanelUtils,
+} from '@flowgram.ai/free-node-panel-plugin';
+import {
+ FreeLayoutPluginContext,
+ WorkflowDragService,
+ WorkflowLinesManager,
+ WorkflowNodeEntity,
+ WorkflowNodeJSON,
+ delay,
+ onDragLineEndParams,
+} from '@flowgram.ai/free-layout-editor';
+
+/**
+ * Drag the end of the line to create an add panel (feature optional)
+ * 拖拽线条结束需要创建一个添加面板 (功能可选)
+ */
+export const onDragLineEnd = async (ctx: FreeLayoutPluginContext, params: onDragLineEndParams) => {
+ // get services from context - 从上下文获取服务
+ const nodePanelService = ctx.get(WorkflowNodePanelService);
+ const document = ctx.document;
+ const dragService = ctx.get(WorkflowDragService);
+ const linesManager = ctx.get(WorkflowLinesManager);
+
+ // get params from drag event - 从拖拽事件获取参数
+ const { fromPort, toPort, mousePos, line, originLine } = params;
+
+ // return if invalid line state - 如果线条状态无效则返回
+ if (originLine || !line) {
+ return;
+ }
+
+ // return if target port exists - 如果目标端口存在则返回
+ if (toPort) {
+ return;
+ }
+
+ // get container node for the new node - 获取新节点的容器节点
+ const containerNode = WorkflowNodePanelUtils.getContainerNode({
+ fromPort,
+ });
+
+ // open node selection panel - 打开节点选择面板
+ const result = await nodePanelService.singleSelectNodePanel({
+ position: mousePos,
+ containerNode,
+ panelProps: {
+ enableNodePlaceholder: true,
+ enableScrollClose: true,
+ },
+ });
+
+ // return if no node selected - 如果没有选择节点则返回
+ if (!result) {
+ return;
+ }
+
+ // get selected node type and data - 获取选择的节点类型和数据
+ const { nodeType, nodeJSON } = result;
+
+ // calculate position for the new node - 计算新节点的位置
+ const nodePosition = WorkflowNodePanelUtils.adjustNodePosition({
+ nodeType,
+ position: mousePos,
+ fromPort,
+ toPort,
+ containerNode,
+ document,
+ dragService,
+ });
+
+ // create new workflow node - 创建新的工作流节点
+ const node: WorkflowNodeEntity = document.createWorkflowNodeByType(
+ nodeType,
+ nodePosition,
+ nodeJSON ?? ({} as WorkflowNodeJSON),
+ containerNode?.id
+ );
+
+ // wait for node render - 等待节点渲染
+ await delay(20);
+
+ // build connection line - 构建连接线
+ WorkflowNodePanelUtils.buildLine({
+ fromPort,
+ node,
+ toPort,
+ linesManager,
+ });
+};
diff --git a/apps/demo-nextjs-antd/tsconfig.json b/apps/demo-nextjs-antd/tsconfig.json
new file mode 100644
index 00000000..b1e13fd7
--- /dev/null
+++ b/apps/demo-nextjs-antd/tsconfig.json
@@ -0,0 +1,34 @@
+{
+ "extends": "@flowgram.ai/ts-config/tsconfig.flow.path.json",
+ "compilerOptions": {
+ "target": "ES2017",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "noUnusedLocals": true,
+ "noImplicitAny": true,
+ "experimentalDecorators": true,
+ "strictPropertyInitialization": false,
+ "strict": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true,
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "baseUrl": "src",
+ "paths": {
+ "@app/*": ["app/*"],
+ "@editor/*": ["editor/*"],
+ }
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+ "exclude": ["node_modules"]
+}
diff --git a/common/config/rush/command-line.json b/common/config/rush/command-line.json
index 898293cb..ebf871bf 100644
--- a/common/config/rush/command-line.json
+++ b/common/config/rush/command-line.json
@@ -324,6 +324,14 @@
"autoinstallerName": "rush-commands",
"safeForSimultaneousRushProcesses": true,
"shellCommand": "concurrently --kill-others --prefix \"{name}\" --names [watch],[demo] -c white,blue \"rush build:watch --to-except @flowgram.ai/demo-nextjs\" \"cd apps/demo-nextjs && rushx dev\""
+ },
+ {
+ "name": "dev:demo-nextjs-antd",
+ "commandKind": "global",
+ "summary": "⭐️️ run dev in app/demo-nextjs-antd",
+ "autoinstallerName": "rush-commands",
+ "safeForSimultaneousRushProcesses": true,
+ "shellCommand": "concurrently --kill-others --prefix \"{name}\" --names [watch],[demo] -c white,blue \"rush build:watch --to-except @flowgram.ai/demo-nextjs-antd\" \"cd apps/demo-nextjs-antd && rushx dev\""
}
],
diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml
index 935af3b3..1c3bd060 100644
--- a/common/config/rush/pnpm-lock.yaml
+++ b/common/config/rush/pnpm-lock.yaml
@@ -362,7 +362,7 @@ importers:
version: 4.17.21
next:
specifier: 15.2.4
- version: 15.2.4(@babel/core@7.26.10)(react-dom@18.3.1)(react@18.3.1)
+ version: 15.2.4(@babel/core@7.26.10)(react-dom@18.3.1)(react@18.3.1)(sass@1.89.2)
react:
specifier: ^18
version: 18.3.1
@@ -393,7 +393,7 @@ importers:
version: 4.17.12
'@types/next':
specifier: ^9.0.0
- version: 9.0.0(@babel/core@7.26.10)(react-dom@18.3.1)(react@18.3.1)
+ version: 9.0.0(@babel/core@7.26.10)(react-dom@18.3.1)(react@18.3.1)(sass@1.89.2)
'@types/node':
specifier: ^18
version: 18.19.68
@@ -422,6 +422,118 @@ importers:
specifier: ^5.0.4
version: 5.0.4
+ ../../apps/demo-nextjs-antd:
+ dependencies:
+ '@ant-design/icons':
+ specifier: 5.x
+ version: 5.6.1(react-dom@18.3.1)(react@18.3.1)
+ '@flowgram.ai/form-antd-materials':
+ specifier: workspace:*
+ version: link:../../packages/materials/form-antd-materials
+ '@flowgram.ai/free-container-plugin':
+ specifier: workspace:*
+ version: link:../../packages/plugins/free-container-plugin
+ '@flowgram.ai/free-group-plugin':
+ specifier: workspace:*
+ version: link:../../packages/plugins/free-group-plugin
+ '@flowgram.ai/free-layout-editor':
+ specifier: workspace:*
+ version: link:../../packages/client/free-layout-editor
+ '@flowgram.ai/free-lines-plugin':
+ specifier: workspace:*
+ version: link:../../packages/plugins/free-lines-plugin
+ '@flowgram.ai/free-node-panel-plugin':
+ specifier: workspace:*
+ version: link:../../packages/plugins/free-node-panel-plugin
+ '@flowgram.ai/free-snap-plugin':
+ specifier: workspace:*
+ version: link:../../packages/plugins/free-snap-plugin
+ '@flowgram.ai/minimap-plugin':
+ specifier: workspace:*
+ version: link:../../packages/plugins/minimap-plugin
+ antd:
+ specifier: ^5.25.4
+ version: 5.26.0(react-dom@18.3.1)(react@18.3.1)
+ classnames:
+ specifier: ^2.5.1
+ version: 2.5.1
+ lodash-es:
+ specifier: ^4.17.21
+ version: 4.17.21
+ nanoid:
+ specifier: ^4.0.2
+ version: 4.0.2
+ next:
+ specifier: 15.2.4
+ version: 15.2.4(@babel/core@7.26.10)(react-dom@18.3.1)(react@18.3.1)(sass@1.89.2)
+ react:
+ specifier: ^18
+ version: 18.3.1
+ react-dom:
+ specifier: ^18
+ version: 18.3.1(react@18.3.1)
+ server-only:
+ specifier: ^0.0.1
+ version: 0.0.1
+ styled-components:
+ specifier: ^5
+ version: 5.3.11(@babel/core@7.26.10)(react-dom@18.3.1)(react-is@18.3.1)(react@18.3.1)
+ devDependencies:
+ '@babel/eslint-parser':
+ specifier: ~7.19.1
+ version: 7.19.1(@babel/core@7.26.10)(eslint@8.57.1)
+ '@eslint/eslintrc':
+ specifier: ^3
+ version: 3.3.1
+ '@flowgram.ai/eslint-config':
+ specifier: workspace:*
+ version: link:../../config/eslint-config
+ '@flowgram.ai/ts-config':
+ specifier: workspace:*
+ version: link:../../config/ts-config
+ '@tailwindcss/postcss':
+ specifier: ^4
+ version: 4.1.0
+ '@types/lodash-es':
+ specifier: ^4.17.12
+ version: 4.17.12
+ '@types/next':
+ specifier: ^9.0.0
+ version: 9.0.0(@babel/core@7.26.10)(react-dom@18.3.1)(react@18.3.1)(sass@1.89.2)
+ '@types/node':
+ specifier: ^18
+ version: 18.19.68
+ '@types/react':
+ specifier: ^18
+ version: 18.3.16
+ '@types/react-dom':
+ specifier: ^18
+ version: 18.3.5(@types/react@18.3.16)
+ '@types/styled-components':
+ specifier: ^5
+ version: 5.1.34
+ eslint:
+ specifier: ^8.54.0
+ version: 8.57.1
+ eslint-config-next:
+ specifier: ^15.3.1
+ version: 15.3.1(eslint@8.57.1)(typescript@5.0.4)
+ eslint-plugin-json:
+ specifier: ^4.0.1
+ version: 4.0.1
+ eslint-plugin-next:
+ specifier: 0.0.0
+ version: 0.0.0
+ sass:
+ specifier: ^1.89.1
+ version: 1.89.2
+ tailwindcss:
+ specifier: ^4
+ version: 4.1.0
+ typescript:
+ specifier: ^5.0.4
+ version: 5.0.4
+
../../apps/demo-node-form:
dependencies:
'@douyinfe/semi-icons':
@@ -1931,6 +2043,70 @@ importers:
specifier: ^0.34.6
version: 0.34.6(jsdom@22.1.0)
+ ../../packages/materials/form-antd-materials:
+ dependencies:
+ '@ant-design/icons':
+ specifier: 5.x
+ version: 5.6.1(react-dom@18.3.1)(react@18.3.1)
+ '@flowgram.ai/editor':
+ specifier: workspace:*
+ version: link:../../client/editor
+ antd:
+ specifier: ^5.25.4
+ version: 5.26.0(react-dom@18.3.1)(react@18.3.1)
+ lodash:
+ specifier: ^4.17.21
+ version: 4.17.21
+ nanoid:
+ specifier: ^4.0.2
+ version: 4.0.2
+ devDependencies:
+ '@flowgram.ai/eslint-config':
+ specifier: workspace:*
+ version: link:../../../config/eslint-config
+ '@flowgram.ai/ts-config':
+ specifier: workspace:*
+ version: link:../../../config/ts-config
+ '@types/lodash':
+ specifier: ^4.14.137
+ version: 4.17.13
+ '@types/node':
+ specifier: ^18
+ version: 18.19.68
+ '@types/react':
+ specifier: ^18
+ version: 18.3.16
+ '@types/react-dom':
+ specifier: ^18
+ version: 18.3.5(@types/react@18.3.16)
+ '@types/styled-components':
+ specifier: ^5
+ version: 5.1.34
+ eslint:
+ specifier: ^8.54.0
+ version: 8.57.1
+ react:
+ specifier: ^18
+ version: 18.3.1
+ react-dom:
+ specifier: ^18
+ version: 18.3.1(react@18.3.1)
+ reflect-metadata:
+ specifier: ~0.2.2
+ version: 0.2.2
+ styled-components:
+ specifier: ^5
+ version: 5.3.11(@babel/core@7.26.10)(react-dom@18.3.1)(react-is@18.3.1)(react@18.3.1)
+ tsup:
+ specifier: ^8.0.1
+ version: 8.3.5(typescript@5.0.4)
+ typescript:
+ specifier: ^5.0.4
+ version: 5.0.4
+ vitest:
+ specifier: ^0.34.6
+ version: 0.34.6(jsdom@22.1.0)
+
../../packages/materials/form-materials:
dependencies:
'@douyinfe/semi-icons':
@@ -4054,6 +4230,82 @@ packages:
'@jridgewell/gen-mapping': 0.3.8
'@jridgewell/trace-mapping': 0.3.25
+ /@ant-design/colors@7.2.1:
+ resolution: {integrity: sha512-lCHDcEzieu4GA3n8ELeZ5VQ8pKQAWcGGLRTQ50aQM2iqPpq2evTxER84jfdPvsPAtEcZ7m44NI45edFMo8oOYQ==}
+ dependencies:
+ '@ant-design/fast-color': 2.0.6
+ dev: false
+
+ /@ant-design/cssinjs-utils@1.1.3(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-nOoQMLW1l+xR1Co8NFVYiP8pZp3VjIIzqV6D6ShYF2ljtdwWJn5WSsH+7kvCktXL/yhEtWURKOfH5Xz/gzlwsg==}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@ant-design/cssinjs': 1.23.0(react-dom@18.3.1)(react@18.3.1)
+ '@babel/runtime': 7.26.0
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /@ant-design/cssinjs@1.23.0(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-7GAg9bD/iC9ikWatU9ym+P9ugJhi/WbsTWzcKN6T4gU0aehsprtke1UAaaSxxkjjmkJb3llet/rbUSLPgwlY4w==}
+ peerDependencies:
+ react: '>=16.0.0'
+ react-dom: '>=16.0.0'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@emotion/hash': 0.8.0
+ '@emotion/unitless': 0.7.5
+ classnames: 2.5.1
+ csstype: 3.1.3
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ stylis: 4.3.6
+ dev: false
+
+ /@ant-design/fast-color@2.0.6:
+ resolution: {integrity: sha512-y2217gk4NqL35giHl72o6Zzqji9O7vHh9YmhUVkPtAOpoTCH4uWxo/pr4VE8t0+ChEPs0qo4eJRC5Q1eXWo3vA==}
+ engines: {node: '>=8.x'}
+ dependencies:
+ '@babel/runtime': 7.26.0
+ dev: false
+
+ /@ant-design/icons-svg@4.4.2:
+ resolution: {integrity: sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==}
+ dev: false
+
+ /@ant-design/icons@5.6.1(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-0/xS39c91WjPAZOWsvi1//zjx6kAp4kxWwctR6kuU6p133w8RU0D2dSCvZC19uQyharg/sAvYxGYWl01BbZZfg==}
+ engines: {node: '>=8'}
+ peerDependencies:
+ react: '>=16.0.0'
+ react-dom: '>=16.0.0'
+ dependencies:
+ '@ant-design/colors': 7.2.1
+ '@ant-design/icons-svg': 4.4.2
+ '@babel/runtime': 7.26.0
+ classnames: 2.5.1
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /@ant-design/react-slick@1.1.2(react@18.3.1):
+ resolution: {integrity: sha512-EzlvzE6xQUBrZuuhSAFTdsr4P2bBBHGZwKFemEfq8gIGyIQCxalYfZW/T2ORbtQx5rU69o+WycP3exY/7T1hGA==}
+ peerDependencies:
+ react: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ classnames: 2.5.1
+ json2mq: 0.2.0
+ react: 18.3.1
+ resize-observer-polyfill: 1.5.1
+ throttle-debounce: 5.0.2
+ dev: false
+
/@babel/code-frame@7.26.2:
resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==}
engines: {node: '>=6.9.0'}
@@ -4215,7 +4467,7 @@ packages:
'@babel/helper-optimise-call-expression': 7.25.9
'@babel/helper-replace-supers': 7.25.9(@babel/core@7.26.0)
'@babel/helper-skip-transparent-expression-wrappers': 7.25.9
- '@babel/traverse': 7.26.4(supports-color@5.5.0)
+ '@babel/traverse': 7.27.0(supports-color@5.5.0)
semver: 6.3.1
transitivePeerDependencies:
- supports-color
@@ -4260,8 +4512,8 @@ packages:
resolution: {integrity: sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/traverse': 7.26.4(supports-color@5.5.0)
- '@babel/types': 7.26.3
+ '@babel/traverse': 7.27.0(supports-color@5.5.0)
+ '@babel/types': 7.27.0
transitivePeerDependencies:
- supports-color
dev: false
@@ -4306,7 +4558,7 @@ packages:
resolution: {integrity: sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/types': 7.26.3
+ '@babel/types': 7.27.0
dev: false
/@babel/helper-plugin-utils@7.25.9:
@@ -4327,7 +4579,7 @@ packages:
'@babel/core': 7.26.0
'@babel/helper-annotate-as-pure': 7.25.9
'@babel/helper-wrap-function': 7.25.9
- '@babel/traverse': 7.26.4(supports-color@5.5.0)
+ '@babel/traverse': 7.27.0(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
dev: false
@@ -4341,7 +4593,7 @@ packages:
'@babel/core': 7.26.0
'@babel/helper-member-expression-to-functions': 7.25.9
'@babel/helper-optimise-call-expression': 7.25.9
- '@babel/traverse': 7.26.4(supports-color@5.5.0)
+ '@babel/traverse': 7.27.0(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
dev: false
@@ -4350,7 +4602,7 @@ packages:
resolution: {integrity: sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/traverse': 7.26.4(supports-color@5.5.0)
+ '@babel/traverse': 7.27.0(supports-color@5.5.0)
'@babel/types': 7.26.3
transitivePeerDependencies:
- supports-color
@@ -4372,9 +4624,9 @@ packages:
resolution: {integrity: sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/template': 7.25.9
- '@babel/traverse': 7.26.4(supports-color@5.5.0)
- '@babel/types': 7.26.3
+ '@babel/template': 7.27.0
+ '@babel/traverse': 7.27.0(supports-color@5.5.0)
+ '@babel/types': 7.27.0
transitivePeerDependencies:
- supports-color
dev: false
@@ -5724,6 +5976,10 @@ packages:
tslib: 2.8.1
optional: true
+ /@emotion/hash@0.8.0:
+ resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==}
+ dev: false
+
/@emotion/is-prop-valid@1.3.1:
resolution: {integrity: sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==}
dependencies:
@@ -7150,6 +7406,135 @@ packages:
resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==}
dev: false
+ /@parcel/watcher-android-arm64@2.5.1:
+ resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [android]
+ requiresBuild: true
+ optional: true
+
+ /@parcel/watcher-darwin-arm64@2.5.1:
+ resolution: {integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [darwin]
+ requiresBuild: true
+ optional: true
+
+ /@parcel/watcher-darwin-x64@2.5.1:
+ resolution: {integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [darwin]
+ requiresBuild: true
+ optional: true
+
+ /@parcel/watcher-freebsd-x64@2.5.1:
+ resolution: {integrity: sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [freebsd]
+ requiresBuild: true
+ optional: true
+
+ /@parcel/watcher-linux-arm-glibc@2.5.1:
+ resolution: {integrity: sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm]
+ os: [linux]
+ requiresBuild: true
+ optional: true
+
+ /@parcel/watcher-linux-arm-musl@2.5.1:
+ resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm]
+ os: [linux]
+ requiresBuild: true
+ optional: true
+
+ /@parcel/watcher-linux-arm64-glibc@2.5.1:
+ resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [linux]
+ requiresBuild: true
+ optional: true
+
+ /@parcel/watcher-linux-arm64-musl@2.5.1:
+ resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [linux]
+ requiresBuild: true
+ optional: true
+
+ /@parcel/watcher-linux-x64-glibc@2.5.1:
+ resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [linux]
+ requiresBuild: true
+ optional: true
+
+ /@parcel/watcher-linux-x64-musl@2.5.1:
+ resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [linux]
+ requiresBuild: true
+ optional: true
+
+ /@parcel/watcher-win32-arm64@2.5.1:
+ resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [win32]
+ requiresBuild: true
+ optional: true
+
+ /@parcel/watcher-win32-ia32@2.5.1:
+ resolution: {integrity: sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [ia32]
+ os: [win32]
+ requiresBuild: true
+ optional: true
+
+ /@parcel/watcher-win32-x64@2.5.1:
+ resolution: {integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [win32]
+ requiresBuild: true
+ optional: true
+
+ /@parcel/watcher@2.5.1:
+ resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==}
+ engines: {node: '>= 10.0.0'}
+ requiresBuild: true
+ dependencies:
+ detect-libc: 1.0.3
+ is-glob: 4.0.3
+ micromatch: 4.0.8
+ node-addon-api: 7.1.1
+ optionalDependencies:
+ '@parcel/watcher-android-arm64': 2.5.1
+ '@parcel/watcher-darwin-arm64': 2.5.1
+ '@parcel/watcher-darwin-x64': 2.5.1
+ '@parcel/watcher-freebsd-x64': 2.5.1
+ '@parcel/watcher-linux-arm-glibc': 2.5.1
+ '@parcel/watcher-linux-arm-musl': 2.5.1
+ '@parcel/watcher-linux-arm64-glibc': 2.5.1
+ '@parcel/watcher-linux-arm64-musl': 2.5.1
+ '@parcel/watcher-linux-x64-glibc': 2.5.1
+ '@parcel/watcher-linux-x64-musl': 2.5.1
+ '@parcel/watcher-win32-arm64': 2.5.1
+ '@parcel/watcher-win32-ia32': 2.5.1
+ '@parcel/watcher-win32-x64': 2.5.1
+ optional: true
+
/@phosphor/algorithm@1.2.0:
resolution: {integrity: sha512-C9+dnjXyU2QAkWCW6QVDGExk4hhwxzAKf5/FIuYlHAI9X5vFv99PYm0EREDxX1PbMuvfFBZhPNu0PvuSDQ7sFA==}
dev: false
@@ -7186,6 +7571,121 @@ packages:
playwright: 1.52.0
dev: false
+ /@rc-component/async-validator@5.0.4:
+ resolution: {integrity: sha512-qgGdcVIF604M9EqjNF0hbUTz42bz/RDtxWdWuU5EQe3hi7M8ob54B6B35rOsvX5eSvIHIzT9iH1R3n+hk3CGfg==}
+ engines: {node: '>=14.x'}
+ dependencies:
+ '@babel/runtime': 7.26.0
+ dev: false
+
+ /@rc-component/color-picker@2.0.1(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-WcZYwAThV/b2GISQ8F+7650r5ZZJ043E57aVBFkQ+kSY4C6wdofXgB0hBx+GPGpIU0Z81eETNoDUJMr7oy/P8Q==}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@ant-design/fast-color': 2.0.6
+ '@babel/runtime': 7.26.0
+ classnames: 2.5.1
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /@rc-component/context@1.4.0(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-kFcNxg9oLRMoL3qki0OMxK+7g5mypjgaaJp/pkOis/6rVxma9nJBF/8kCIuTYHUQNr0ii7MxqE33wirPZLJQ2w==}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /@rc-component/mini-decimal@1.1.0:
+ resolution: {integrity: sha512-jS4E7T9Li2GuYwI6PyiVXmxTiM6b07rlD9Ge8uGZSCz3WlzcG5ZK7g5bbuKNeZ9pgUuPK/5guV781ujdVpm4HQ==}
+ engines: {node: '>=8.x'}
+ dependencies:
+ '@babel/runtime': 7.26.0
+ dev: false
+
+ /@rc-component/mutate-observer@1.1.0(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-QjrOsDXQusNwGZPf4/qRQasg7UFEj06XiCJ8iuiq/Io7CrHrgVi6Uuetw60WAMG1799v+aM8kyc+1L/GBbHSlw==}
+ engines: {node: '>=8.x'}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ classnames: 2.5.1
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /@rc-component/portal@1.1.2(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-6f813C0IsasTZms08kfA8kPAGxbbkYToa8ALaiDIGGECU4i9hj8Plgbx0sNJDrey3EtHO30hmdaxtT0138xZcg==}
+ engines: {node: '>=8.x'}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ classnames: 2.5.1
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /@rc-component/qrcode@1.0.0(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-L+rZ4HXP2sJ1gHMGHjsg9jlYBX/SLN2D6OxP9Zn3qgtpMWtO2vUfxVFwiogHpAIqs54FnALxraUy/BCO1yRIgg==}
+ engines: {node: '>=8.x'}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ classnames: 2.5.1
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /@rc-component/tour@1.15.1(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-Tr2t7J1DKZUpfJuDZWHxyxWpfmj8EZrqSgyMZ+BCdvKZ6r1UDsfU46M/iWAAFBy961Ssfom2kv5f3UcjIL2CmQ==}
+ engines: {node: '>=8.x'}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@rc-component/portal': 1.1.2(react-dom@18.3.1)(react@18.3.1)
+ '@rc-component/trigger': 2.2.6(react-dom@18.3.1)(react@18.3.1)
+ classnames: 2.5.1
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /@rc-component/trigger@2.2.6(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-/9zuTnWwhQ3S3WT1T8BubuFTT46kvnXgaERR9f4BTKyn61/wpf/BvbImzYBubzJibU707FxwbKszLlHjcLiv1Q==}
+ engines: {node: '>=8.x'}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@rc-component/portal': 1.1.2(react-dom@18.3.1)(react@18.3.1)
+ classnames: 2.5.1
+ rc-motion: 2.9.5(react-dom@18.3.1)(react@18.3.1)
+ rc-resize-observer: 1.4.3(react-dom@18.3.1)(react@18.3.1)
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
/@react-hook/intersection-observer@3.1.2(react@18.3.1):
resolution: {integrity: sha512-mWU3BMkmmzyYMSuhO9wu3eJVP21N8TcgYm9bZnTrMwuM818bEk+0NRM3hP+c/TqA9Ln5C7qE53p1H0QMtzYdvQ==}
peerDependencies:
@@ -7600,7 +8100,7 @@ packages:
'@rsbuild/core': 1.1.13
deepmerge: 4.3.1
loader-utils: 2.0.4
- postcss: 8.5.1
+ postcss: 8.5.3
reduce-configs: 1.1.0
sass-embedded: 1.83.0
@@ -8583,11 +9083,11 @@ packages:
/@types/ms@0.7.34:
resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==}
- /@types/next@9.0.0(@babel/core@7.26.10)(react-dom@18.3.1)(react@18.3.1):
+ /@types/next@9.0.0(@babel/core@7.26.10)(react-dom@18.3.1)(react@18.3.1)(sass@1.89.2):
resolution: {integrity: sha512-gnBXM8rP1mnCgT1uE2z8SnpFTKRWReJlhbZLZkOLq/CH1ifvTNwjIVtXvsywTy1dwVklf+y/MB0Eh6FOa94yrg==}
deprecated: This is a stub types definition. next provides its own type definitions, so you do not need this installed.
dependencies:
- next: 15.2.4(@babel/core@7.26.10)(react-dom@18.3.1)(react@18.3.1)
+ next: 15.2.4(@babel/core@7.26.10)(react-dom@18.3.1)(react@18.3.1)(sass@1.89.2)
transitivePeerDependencies:
- '@babel/core'
- '@opentelemetry/api'
@@ -9413,6 +9913,69 @@ packages:
resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
engines: {node: '>=12'}
+ /antd@5.26.0(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-iMPYKFTo2HvIRGutUOuN5AG+Uf+B2QaqcGQbdPp/100fqV3FAil6vFZLVuV3C4XEUOlDNkkUlJKhLR9V5rzIEg==}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@ant-design/colors': 7.2.1
+ '@ant-design/cssinjs': 1.23.0(react-dom@18.3.1)(react@18.3.1)
+ '@ant-design/cssinjs-utils': 1.1.3(react-dom@18.3.1)(react@18.3.1)
+ '@ant-design/fast-color': 2.0.6
+ '@ant-design/icons': 5.6.1(react-dom@18.3.1)(react@18.3.1)
+ '@ant-design/react-slick': 1.1.2(react@18.3.1)
+ '@babel/runtime': 7.26.0
+ '@rc-component/color-picker': 2.0.1(react-dom@18.3.1)(react@18.3.1)
+ '@rc-component/mutate-observer': 1.1.0(react-dom@18.3.1)(react@18.3.1)
+ '@rc-component/qrcode': 1.0.0(react-dom@18.3.1)(react@18.3.1)
+ '@rc-component/tour': 1.15.1(react-dom@18.3.1)(react@18.3.1)
+ '@rc-component/trigger': 2.2.6(react-dom@18.3.1)(react@18.3.1)
+ classnames: 2.5.1
+ copy-to-clipboard: 3.3.3
+ dayjs: 1.11.13
+ rc-cascader: 3.34.0(react-dom@18.3.1)(react@18.3.1)
+ rc-checkbox: 3.5.0(react-dom@18.3.1)(react@18.3.1)
+ rc-collapse: 3.9.0(react-dom@18.3.1)(react@18.3.1)
+ rc-dialog: 9.6.0(react-dom@18.3.1)(react@18.3.1)
+ rc-drawer: 7.3.0(react-dom@18.3.1)(react@18.3.1)
+ rc-dropdown: 4.2.1(react-dom@18.3.1)(react@18.3.1)
+ rc-field-form: 2.7.0(react-dom@18.3.1)(react@18.3.1)
+ rc-image: 7.12.0(react-dom@18.3.1)(react@18.3.1)
+ rc-input: 1.8.0(react-dom@18.3.1)(react@18.3.1)
+ rc-input-number: 9.5.0(react-dom@18.3.1)(react@18.3.1)
+ rc-mentions: 2.20.0(react-dom@18.3.1)(react@18.3.1)
+ rc-menu: 9.16.1(react-dom@18.3.1)(react@18.3.1)
+ rc-motion: 2.9.5(react-dom@18.3.1)(react@18.3.1)
+ rc-notification: 5.6.4(react-dom@18.3.1)(react@18.3.1)
+ rc-pagination: 5.1.0(react-dom@18.3.1)(react@18.3.1)
+ rc-picker: 4.11.3(dayjs@1.11.13)(react-dom@18.3.1)(react@18.3.1)
+ rc-progress: 4.0.0(react-dom@18.3.1)(react@18.3.1)
+ rc-rate: 2.13.1(react-dom@18.3.1)(react@18.3.1)
+ rc-resize-observer: 1.4.3(react-dom@18.3.1)(react@18.3.1)
+ rc-segmented: 2.7.0(react-dom@18.3.1)(react@18.3.1)
+ rc-select: 14.16.8(react-dom@18.3.1)(react@18.3.1)
+ rc-slider: 11.1.8(react-dom@18.3.1)(react@18.3.1)
+ rc-steps: 6.0.1(react-dom@18.3.1)(react@18.3.1)
+ rc-switch: 4.1.0(react-dom@18.3.1)(react@18.3.1)
+ rc-table: 7.51.0(react-dom@18.3.1)(react@18.3.1)
+ rc-tabs: 15.6.1(react-dom@18.3.1)(react@18.3.1)
+ rc-textarea: 1.10.0(react-dom@18.3.1)(react@18.3.1)
+ rc-tooltip: 6.4.0(react-dom@18.3.1)(react@18.3.1)
+ rc-tree: 5.13.1(react-dom@18.3.1)(react@18.3.1)
+ rc-tree-select: 5.27.0(react-dom@18.3.1)(react@18.3.1)
+ rc-upload: 4.9.2(react-dom@18.3.1)(react@18.3.1)
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ scroll-into-view-if-needed: 3.1.0
+ throttle-debounce: 5.0.2
+ transitivePeerDependencies:
+ - date-fns
+ - luxon
+ - moment
+ dev: false
+
/any-promise@1.3.0:
resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
dev: true
@@ -9703,7 +10266,7 @@ packages:
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
dependencies:
- caniuse-lite: 1.0.30001688
+ caniuse-lite: 1.0.30001706
electron-to-chromium: 1.5.73
node-releases: 2.0.19
update-browserslist-db: 1.1.1(browserslist@4.24.2)
@@ -9915,7 +10478,6 @@ packages:
engines: {node: '>= 14.16.0'}
dependencies:
readdirp: 4.0.2
- dev: true
/chownr@3.0.0:
resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==}
@@ -10072,6 +10634,10 @@ packages:
resolution: {integrity: sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==}
dev: false
+ /compute-scroll-into-view@3.1.1:
+ resolution: {integrity: sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==}
+ dev: false
+
/concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
@@ -10269,6 +10835,10 @@ packages:
'@babel/runtime': 7.26.0
dev: false
+ /dayjs@1.11.13:
+ resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==}
+ dev: false
+
/debug@3.2.7:
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
dependencies:
@@ -10456,6 +11026,13 @@ packages:
engines: {node: '>=12.20'}
dev: false
+ /detect-libc@1.0.3:
+ resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==}
+ engines: {node: '>=0.10'}
+ hasBin: true
+ requiresBuild: true
+ optional: true
+
/detect-libc@2.0.3:
resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==}
engines: {node: '>=8'}
@@ -13028,6 +13605,12 @@ packages:
/json-stable-stringify-without-jsonify@1.0.1:
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
+ /json2mq@0.2.0:
+ resolution: {integrity: sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==}
+ dependencies:
+ string-convert: 0.2.1
+ dev: false
+
/json5@1.0.2:
resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==}
hasBin: true
@@ -14669,7 +15252,7 @@ packages:
resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==}
dev: false
- /next@15.2.4(@babel/core@7.26.10)(react-dom@18.3.1)(react@18.3.1):
+ /next@15.2.4(@babel/core@7.26.10)(react-dom@18.3.1)(react@18.3.1)(sass@1.89.2):
resolution: {integrity: sha512-VwL+LAaPSxEkd3lU2xWbgEOtrM8oedmyhBqaVNmgKB+GvZlCy9rgaEc+y2on0wv+l0oSFqLtYD6dcC1eAedUaQ==}
engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
hasBin: true
@@ -14698,6 +15281,7 @@ packages:
postcss: 8.4.31
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
+ sass: 1.89.2
styled-jsx: 5.1.6(@babel/core@7.26.10)(react@18.3.1)
optionalDependencies:
'@next/swc-darwin-arm64': 15.2.4
@@ -14717,6 +15301,11 @@ packages:
resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==}
dev: true
+ /node-addon-api@7.1.1:
+ resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
+ requiresBuild: true
+ optional: true
+
/node-domexception@1.0.0:
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
engines: {node: '>=10.5.0'}
@@ -15317,6 +15906,7 @@ packages:
nanoid: 3.3.8
picocolors: 1.1.1
source-map-js: 1.2.1
+ dev: true
/postcss@8.5.3:
resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==}
@@ -15325,7 +15915,6 @@ packages:
nanoid: 3.3.8
picocolors: 1.1.1
source-map-js: 1.2.1
- dev: true
/prelude-ls@1.2.1:
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
@@ -15507,6 +16096,518 @@ packages:
webpack: 5.76.0
dev: true
+ /rc-cascader@3.34.0(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-KpXypcvju9ptjW9FaN2NFcA2QH9E9LHKq169Y0eWtH4e/wHQ5Wh5qZakAgvb8EKZ736WZ3B0zLLOBsrsja5Dag==}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ classnames: 2.5.1
+ rc-select: 14.16.8(react-dom@18.3.1)(react@18.3.1)
+ rc-tree: 5.13.1(react-dom@18.3.1)(react@18.3.1)
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /rc-checkbox@3.5.0(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-aOAQc3E98HteIIsSqm6Xk2FPKIER6+5vyEFMZfo73TqM+VVAIqOkHoPjgKLqSNtVLWScoaM7vY2ZrGEheI79yg==}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ classnames: 2.5.1
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /rc-collapse@3.9.0(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-swDdz4QZ4dFTo4RAUMLL50qP0EY62N2kvmk2We5xYdRwcRn8WcYtuetCJpwpaCbUfUt5+huLpVxhvmnK+PHrkA==}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ classnames: 2.5.1
+ rc-motion: 2.9.5(react-dom@18.3.1)(react@18.3.1)
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /rc-dialog@9.6.0(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-ApoVi9Z8PaCQg6FsUzS8yvBEQy0ZL2PkuvAgrmohPkN3okps5WZ5WQWPc1RNuiOKaAYv8B97ACdsFU5LizzCqg==}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@rc-component/portal': 1.1.2(react-dom@18.3.1)(react@18.3.1)
+ classnames: 2.5.1
+ rc-motion: 2.9.5(react-dom@18.3.1)(react@18.3.1)
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /rc-drawer@7.3.0(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-DX6CIgiBWNpJIMGFO8BAISFkxiuKitoizooj4BDyee8/SnBn0zwO2FHrNDpqqepj0E/TFTDpmEBCyFuTgC7MOg==}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@rc-component/portal': 1.1.2(react-dom@18.3.1)(react@18.3.1)
+ classnames: 2.5.1
+ rc-motion: 2.9.5(react-dom@18.3.1)(react@18.3.1)
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /rc-dropdown@4.2.1(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-YDAlXsPv3I1n42dv1JpdM7wJ+gSUBfeyPK59ZpBD9jQhK9jVuxpjj3NmWQHOBceA1zEPVX84T2wbdb2SD0UjmA==}
+ peerDependencies:
+ react: '>=16.11.0'
+ react-dom: '>=16.11.0'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@rc-component/trigger': 2.2.6(react-dom@18.3.1)(react@18.3.1)
+ classnames: 2.5.1
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /rc-field-form@2.7.0(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-hgKsCay2taxzVnBPZl+1n4ZondsV78G++XVsMIJCAoioMjlMQR9YwAp7JZDIECzIu2Z66R+f4SFIRrO2DjDNAA==}
+ engines: {node: '>=8.x'}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@rc-component/async-validator': 5.0.4
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /rc-image@7.12.0(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-cZ3HTyyckPnNnUb9/DRqduqzLfrQRyi+CdHjdqgsyDpI3Ln5UX1kXnAhPBSJj9pVRzwRFgqkN7p9b6HBDjmu/Q==}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@rc-component/portal': 1.1.2(react-dom@18.3.1)(react@18.3.1)
+ classnames: 2.5.1
+ rc-dialog: 9.6.0(react-dom@18.3.1)(react@18.3.1)
+ rc-motion: 2.9.5(react-dom@18.3.1)(react@18.3.1)
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /rc-input-number@9.5.0(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-bKaEvB5tHebUURAEXw35LDcnRZLq3x1k7GxfAqBMzmpHkDGzjAtnUL8y4y5N15rIFIg5IJgwr211jInl3cipag==}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@rc-component/mini-decimal': 1.1.0
+ classnames: 2.5.1
+ rc-input: 1.8.0(react-dom@18.3.1)(react@18.3.1)
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /rc-input@1.8.0(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-KXvaTbX+7ha8a/k+eg6SYRVERK0NddX8QX7a7AnRvUa/rEH0CNMlpcBzBkhI0wp2C8C4HlMoYl8TImSN+fuHKA==}
+ peerDependencies:
+ react: '>=16.0.0'
+ react-dom: '>=16.0.0'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ classnames: 2.5.1
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /rc-mentions@2.20.0(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-w8HCMZEh3f0nR8ZEd466ATqmXFCMGMN5UFCzEUL0bM/nGw/wOS2GgRzKBcm19K++jDyuWCOJOdgcKGXU3fXfbQ==}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@rc-component/trigger': 2.2.6(react-dom@18.3.1)(react@18.3.1)
+ classnames: 2.5.1
+ rc-input: 1.8.0(react-dom@18.3.1)(react@18.3.1)
+ rc-menu: 9.16.1(react-dom@18.3.1)(react@18.3.1)
+ rc-textarea: 1.10.0(react-dom@18.3.1)(react@18.3.1)
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /rc-menu@9.16.1(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-ghHx6/6Dvp+fw8CJhDUHFHDJ84hJE3BXNCzSgLdmNiFErWSOaZNsihDAsKq9ByTALo/xkNIwtDFGIl6r+RPXBg==}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@rc-component/trigger': 2.2.6(react-dom@18.3.1)(react@18.3.1)
+ classnames: 2.5.1
+ rc-motion: 2.9.5(react-dom@18.3.1)(react@18.3.1)
+ rc-overflow: 1.4.1(react-dom@18.3.1)(react@18.3.1)
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /rc-motion@2.9.5(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-w+XTUrfh7ArbYEd2582uDrEhmBHwK1ZENJiSJVb7uRxdE7qJSYjbO2eksRXmndqyKqKoYPc9ClpPh5242mV1vA==}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ classnames: 2.5.1
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /rc-notification@5.6.4(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-KcS4O6B4qzM3KH7lkwOB7ooLPZ4b6J+VMmQgT51VZCeEcmghdeR4IrMcFq0LG+RPdnbe/ArT086tGM8Snimgiw==}
+ engines: {node: '>=8.x'}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ classnames: 2.5.1
+ rc-motion: 2.9.5(react-dom@18.3.1)(react@18.3.1)
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /rc-overflow@1.4.1(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-3MoPQQPV1uKyOMVNd6SZfONi+f3st0r8PksexIdBTeIYbMX0Jr+k7pHEDvsXtR4BpCv90/Pv2MovVNhktKrwvw==}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ classnames: 2.5.1
+ rc-resize-observer: 1.4.3(react-dom@18.3.1)(react@18.3.1)
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /rc-pagination@5.1.0(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-8416Yip/+eclTFdHXLKTxZvn70duYVGTvUUWbckCCZoIl3jagqke3GLsFrMs0bsQBikiYpZLD9206Ej4SOdOXQ==}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ classnames: 2.5.1
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /rc-picker@4.11.3(dayjs@1.11.13)(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-MJ5teb7FlNE0NFHTncxXQ62Y5lytq6sh5nUw0iH8OkHL/TjARSEvSHpr940pWgjGANpjCwyMdvsEV55l5tYNSg==}
+ engines: {node: '>=8.x'}
+ peerDependencies:
+ date-fns: '>= 2.x'
+ dayjs: '>= 1.x'
+ luxon: '>= 3.x'
+ moment: '>= 2.x'
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ peerDependenciesMeta:
+ date-fns:
+ optional: true
+ dayjs:
+ optional: true
+ luxon:
+ optional: true
+ moment:
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@rc-component/trigger': 2.2.6(react-dom@18.3.1)(react@18.3.1)
+ classnames: 2.5.1
+ dayjs: 1.11.13
+ rc-overflow: 1.4.1(react-dom@18.3.1)(react@18.3.1)
+ rc-resize-observer: 1.4.3(react-dom@18.3.1)(react@18.3.1)
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /rc-progress@4.0.0(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-oofVMMafOCokIUIBnZLNcOZFsABaUw8PPrf1/y0ZBvKZNpOiu5h4AO9vv11Sw0p4Hb3D0yGWuEattcQGtNJ/aw==}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ classnames: 2.5.1
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /rc-rate@2.13.1(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-QUhQ9ivQ8Gy7mtMZPAjLbxBt5y9GRp65VcUyGUMF3N3fhiftivPHdpuDIaWIMOTEprAjZPC08bls1dQB+I1F2Q==}
+ engines: {node: '>=8.x'}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ classnames: 2.5.1
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /rc-resize-observer@1.4.3(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-YZLjUbyIWox8E9i9C3Tm7ia+W7euPItNWSPX5sCcQTYbnwDb5uNpnLHQCG1f22oZWUhLw4Mv2tFmeWe68CDQRQ==}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ classnames: 2.5.1
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ resize-observer-polyfill: 1.5.1
+ dev: false
+
+ /rc-segmented@2.7.0(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-liijAjXz+KnTRVnxxXG2sYDGd6iLL7VpGGdR8gwoxAXy2KglviKCxLWZdjKYJzYzGSUwKDSTdYk8brj54Bn5BA==}
+ peerDependencies:
+ react: '>=16.0.0'
+ react-dom: '>=16.0.0'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ classnames: 2.5.1
+ rc-motion: 2.9.5(react-dom@18.3.1)(react@18.3.1)
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /rc-select@14.16.8(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-NOV5BZa1wZrsdkKaiK7LHRuo5ZjZYMDxPP6/1+09+FB4KoNi8jcG1ZqLE3AVCxEsYMBe65OBx71wFoHRTP3LRg==}
+ engines: {node: '>=8.x'}
+ peerDependencies:
+ react: '*'
+ react-dom: '*'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@rc-component/trigger': 2.2.6(react-dom@18.3.1)(react@18.3.1)
+ classnames: 2.5.1
+ rc-motion: 2.9.5(react-dom@18.3.1)(react@18.3.1)
+ rc-overflow: 1.4.1(react-dom@18.3.1)(react@18.3.1)
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ rc-virtual-list: 3.18.6(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /rc-slider@11.1.8(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-2gg/72YFSpKP+Ja5AjC5DPL1YnV8DEITDQrcc1eASrUYjl0esptaBVJBh5nLTXCCp15eD8EuGjwezVGSHhs9tQ==}
+ engines: {node: '>=8.x'}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ classnames: 2.5.1
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /rc-steps@6.0.1(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-lKHL+Sny0SeHkQKKDJlAjV5oZ8DwCdS2hFhAkIjuQt1/pB81M0cA0ErVFdHq9+jmPmFw1vJB2F5NBzFXLJxV+g==}
+ engines: {node: '>=8.x'}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ classnames: 2.5.1
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /rc-switch@4.1.0(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-TI8ufP2Az9oEbvyCeVE4+90PDSljGyuwix3fV58p7HV2o4wBnVToEyomJRVyTaZeqNPAp+vqeo4Wnj5u0ZZQBg==}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ classnames: 2.5.1
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /rc-table@7.51.0(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-7ZlvW6lB0IDKaSFInD6OfJsCepSJJtfsQv2PZLtzEeZd/PLzQnKliXPaoZqkqDdLdJ3jxE2x4sane4DjxcAg+g==}
+ engines: {node: '>=8.x'}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@rc-component/context': 1.4.0(react-dom@18.3.1)(react@18.3.1)
+ classnames: 2.5.1
+ rc-resize-observer: 1.4.3(react-dom@18.3.1)(react@18.3.1)
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ rc-virtual-list: 3.18.6(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /rc-tabs@15.6.1(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-/HzDV1VqOsUWyuC0c6AkxVYFjvx9+rFPKZ32ejxX0Uc7QCzcEjTA9/xMgv4HemPKwzBNX8KhGVbbumDjnj92aA==}
+ engines: {node: '>=8.x'}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ classnames: 2.5.1
+ rc-dropdown: 4.2.1(react-dom@18.3.1)(react@18.3.1)
+ rc-menu: 9.16.1(react-dom@18.3.1)(react@18.3.1)
+ rc-motion: 2.9.5(react-dom@18.3.1)(react@18.3.1)
+ rc-resize-observer: 1.4.3(react-dom@18.3.1)(react@18.3.1)
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /rc-textarea@1.10.0(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-ai9IkanNuyBS4x6sOL8qu/Ld40e6cEs6pgk93R+XLYg0mDSjNBGey6/ZpDs5+gNLD7urQ14po3V6Ck2dJLt9SA==}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ classnames: 2.5.1
+ rc-input: 1.8.0(react-dom@18.3.1)(react@18.3.1)
+ rc-resize-observer: 1.4.3(react-dom@18.3.1)(react@18.3.1)
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /rc-tooltip@6.4.0(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-kqyivim5cp8I5RkHmpsp1Nn/Wk+1oeloMv9c7LXNgDxUpGm+RbXJGL+OPvDlcRnx9DBeOe4wyOIl4OKUERyH1g==}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@rc-component/trigger': 2.2.6(react-dom@18.3.1)(react@18.3.1)
+ classnames: 2.5.1
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /rc-tree-select@5.27.0(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-2qTBTzwIT7LRI1o7zLyrCzmo5tQanmyGbSaGTIf7sYimCklAToVVfpMC6OAldSKolcnjorBYPNSKQqJmN3TCww==}
+ peerDependencies:
+ react: '*'
+ react-dom: '*'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ classnames: 2.5.1
+ rc-select: 14.16.8(react-dom@18.3.1)(react@18.3.1)
+ rc-tree: 5.13.1(react-dom@18.3.1)(react@18.3.1)
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /rc-tree@5.13.1(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-FNhIefhftobCdUJshO7M8uZTA9F4OPGVXqGfZkkD/5soDeOhwO06T/aKTrg0WD8gRg/pyfq+ql3aMymLHCTC4A==}
+ engines: {node: '>=10.x'}
+ peerDependencies:
+ react: '*'
+ react-dom: '*'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ classnames: 2.5.1
+ rc-motion: 2.9.5(react-dom@18.3.1)(react@18.3.1)
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ rc-virtual-list: 3.18.6(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /rc-upload@4.9.2(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-nHx+9rbd1FKMiMRYsqQ3NkXUv7COHPBo3X1Obwq9SWS6/diF/A0aJ5OHubvwUAIDs+4RMleljV0pcrNUc823GQ==}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ classnames: 2.5.1
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
+ /rc-util@5.44.4(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-resueRJzmHG9Q6rI/DfK6Kdv9/Lfls05vzMs1Sk3M2P+3cJa+MakaZyWY8IPfehVuhPJFKrIY1IK4GqbiaiY5w==}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ react-is: 18.3.1
+ dev: false
+
+ /rc-virtual-list@3.18.6(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-TQ5SsutL3McvWmmxqQtMIbfeoE3dGjJrRSfKekgby7WQMpPIFvv4ghytp5Z0s3D8Nik9i9YNOCqHBfk86AwgAA==}
+ engines: {node: '>=8.x'}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+ dependencies:
+ '@babel/runtime': 7.26.0
+ classnames: 2.5.1
+ rc-resize-observer: 1.4.3(react-dom@18.3.1)(react@18.3.1)
+ rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
/react-devtools-inline@4.4.0:
resolution: {integrity: sha512-ES0GolSrKO8wsKbsEkVeiR/ZAaHQTY4zDh1UW8DImVmm8oaGLl3ijJDvSGe+qDRKPZdPRnDtWWnSvvrgxXdThQ==}
dependencies:
@@ -15715,7 +16816,6 @@ packages:
/readdirp@4.0.2:
resolution: {integrity: sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==}
engines: {node: '>= 14.16.0'}
- dev: true
/real-require@0.2.0:
resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==}
@@ -15981,6 +17081,10 @@ packages:
resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
dev: true
+ /resize-observer-polyfill@1.5.1:
+ resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==}
+ dev: false
+
/resolve-from@4.0.0:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
engines: {node: '>=4'}
@@ -16407,6 +17511,17 @@ packages:
sass-embedded-win32-ia32: 1.83.0
sass-embedded-win32-x64: 1.83.0
+ /sass@1.89.2:
+ resolution: {integrity: sha512-xCmtksBKd/jdJ9Bt9p7nPKiuqrlBMBuuGkQlkhZjjQk3Ty48lv93k5Dq6OPkKt4XwxDJ7tvlfrTa1MPA9bf+QA==}
+ engines: {node: '>=14.0.0'}
+ hasBin: true
+ dependencies:
+ chokidar: 4.0.1
+ immutable: 5.0.3
+ source-map-js: 1.2.1
+ optionalDependencies:
+ '@parcel/watcher': 2.5.1
+
/sax@1.4.1:
resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==}
requiresBuild: true
@@ -16463,6 +17578,12 @@ packages:
compute-scroll-into-view: 1.0.20
dev: false
+ /scroll-into-view-if-needed@3.1.0:
+ resolution: {integrity: sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==}
+ dependencies:
+ compute-scroll-into-view: 3.1.1
+ dev: false
+
/section-matter@1.0.0:
resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==}
engines: {node: '>=4'}
@@ -16856,6 +17977,10 @@ packages:
engines: {node: '>=0.10.0'}
dev: false
+ /string-convert@0.2.1:
+ resolution: {integrity: sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==}
+ dev: false
+
/string-width@4.2.3:
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
engines: {node: '>=8'}
@@ -17068,6 +18193,10 @@ packages:
client-only: 0.0.1
react: 18.3.1
+ /stylis@4.3.6:
+ resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==}
+ dev: false
+
/sucrase@3.35.0:
resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==}
engines: {node: '>=16 || 14 >=14.17'}
@@ -17223,6 +18352,11 @@ packages:
real-require: 0.2.0
dev: false
+ /throttle-debounce@5.0.2:
+ resolution: {integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==}
+ engines: {node: '>=12.22'}
+ dev: false
+
/through@2.3.8:
resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
dev: false
@@ -17986,7 +19120,7 @@ packages:
dependencies:
'@types/node': 18.19.68
esbuild: 0.18.20
- postcss: 8.5.1
+ postcss: 8.5.3
rollup: 3.29.5
optionalDependencies:
fsevents: 2.3.3
diff --git a/packages/materials/form-antd-materials/.eslintrc.cjs b/packages/materials/form-antd-materials/.eslintrc.cjs
new file mode 100644
index 00000000..25f01523
--- /dev/null
+++ b/packages/materials/form-antd-materials/.eslintrc.cjs
@@ -0,0 +1,11 @@
+const { defineConfig } = require('@flowgram.ai/eslint-config');
+
+module.exports = defineConfig({
+ preset: 'web',
+ packageRoot: __dirname,
+ rules: {
+ 'no-console': 'off',
+ 'react/no-deprecated': 'off',
+ '@flowgram.ai/e2e-data-testid': 'off',
+ },
+});
diff --git a/packages/materials/form-antd-materials/package.json b/packages/materials/form-antd-materials/package.json
new file mode 100644
index 00000000..c75d1c00
--- /dev/null
+++ b/packages/materials/form-antd-materials/package.json
@@ -0,0 +1,62 @@
+{
+ "name": "@flowgram.ai/form-antd-materials",
+ "version": "0.1.8",
+ "homepage": "https://flowgram.ai/",
+ "repository": "https://github.com/bytedance/flowgram.ai",
+ "license": "MIT",
+ "exports": {
+ "types": "./dist/index.d.ts",
+ "import": "./dist/esm/index.js",
+ "require": "./dist/index.js"
+ },
+ "main": "./dist/index.js",
+ "module": "./dist/esm/index.js",
+ "types": "./dist/index.d.ts",
+ "files": [
+ "dist",
+ "src"
+ ],
+ "scripts": {
+ "build": "npm run build:fast -- --dts-resolve",
+ "build:fast": "tsup src/index.ts --format cjs,esm --sourcemap --legacy-output",
+ "build:watch": "npm run build:fast -- --dts-resolve",
+ "clean": "rimraf dist",
+ "test": "exit 0",
+ "test:cov": "exit 0",
+ "ts-check": "tsc --noEmit",
+ "watch": "npm run build:fast -- --dts-resolve --watch --ignore-watch dist"
+ },
+ "dependencies": {
+ "@ant-design/icons": "5.x",
+ "@flowgram.ai/editor": "workspace:*",
+ "antd": "^5.25.4",
+ "lodash": "^4.17.21",
+ "nanoid": "^4.0.2"
+ },
+ "devDependencies": {
+ "@flowgram.ai/eslint-config": "workspace:*",
+ "@flowgram.ai/ts-config": "workspace:*",
+ "@types/lodash": "^4.14.137",
+ "@types/node": "^18",
+ "@types/react": "^18",
+ "@types/react-dom": "^18",
+ "@types/styled-components": "^5",
+ "eslint": "^8.54.0",
+ "react": "^18",
+ "react-dom": "^18",
+ "reflect-metadata": "~0.2.2",
+ "styled-components": "^5",
+ "tsup": "^8.0.1",
+ "typescript": "^5.0.4",
+ "vitest": "^0.34.6"
+ },
+ "peerDependencies": {
+ "react": ">=16.8",
+ "react-dom": ">=16.8",
+ "styled-components": ">=4"
+ },
+ "publishConfig": {
+ "access": "public",
+ "registry": "https://registry.npmjs.org/"
+ }
+}
diff --git a/packages/materials/form-antd-materials/src/components/batch-variable-selector/config.json b/packages/materials/form-antd-materials/src/components/batch-variable-selector/config.json
new file mode 100644
index 00000000..c8fa12c1
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/components/batch-variable-selector/config.json
@@ -0,0 +1,5 @@
+{
+ "name": "batch-variable-selector",
+ "depMaterials": ["variable-selector"],
+ "depPackages": []
+}
diff --git a/packages/materials/form-antd-materials/src/components/batch-variable-selector/index.tsx b/packages/materials/form-antd-materials/src/components/batch-variable-selector/index.tsx
new file mode 100644
index 00000000..308e1f90
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/components/batch-variable-selector/index.tsx
@@ -0,0 +1,19 @@
+import React from 'react';
+
+import { PrivateScopeProvider } from '@flowgram.ai/editor';
+
+import { VariableSelector, VariableSelectorProps } from '../variable-selector';
+import { IJsonSchema } from '../../typings';
+
+const batchVariableSchema: IJsonSchema = {
+ type: 'array',
+ extra: { weak: true },
+};
+
+export function BatchVariableSelector(props: VariableSelectorProps) {
+ return (
+
+
+
+ );
+}
diff --git a/packages/materials/form-antd-materials/src/components/condition-row/config.json b/packages/materials/form-antd-materials/src/components/condition-row/config.json
new file mode 100644
index 00000000..7058dd38
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/components/condition-row/config.json
@@ -0,0 +1,5 @@
+{
+ "name": "condition-row",
+ "depMaterials": ["variable-selector", "dynamic-value-input", "flow-value", "utils/json-schema", "typings/json-schema"],
+ "depPackages": ["styled-components"]
+}
diff --git a/packages/materials/form-antd-materials/src/components/condition-row/constants.ts b/packages/materials/form-antd-materials/src/components/condition-row/constants.ts
new file mode 100644
index 00000000..02e86224
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/components/condition-row/constants.ts
@@ -0,0 +1,123 @@
+import { IRules, Op, OpConfigs } from './types';
+
+export const rules: IRules = {
+ string: {
+ [Op.EQ]: 'string',
+ [Op.NEQ]: 'string',
+ [Op.CONTAINS]: 'string',
+ [Op.NOT_CONTAINS]: 'string',
+ [Op.IN]: 'array',
+ [Op.NIN]: 'array',
+ [Op.IS_EMPTY]: 'string',
+ [Op.IS_NOT_EMPTY]: 'string',
+ },
+ number: {
+ [Op.EQ]: 'number',
+ [Op.NEQ]: 'number',
+ [Op.GT]: 'number',
+ [Op.GTE]: 'number',
+ [Op.LT]: 'number',
+ [Op.LTE]: 'number',
+ [Op.IN]: 'array',
+ [Op.NIN]: 'array',
+ [Op.IS_EMPTY]: null,
+ [Op.IS_NOT_EMPTY]: null,
+ },
+ integer: {
+ [Op.EQ]: 'number',
+ [Op.NEQ]: 'number',
+ [Op.GT]: 'number',
+ [Op.GTE]: 'number',
+ [Op.LT]: 'number',
+ [Op.LTE]: 'number',
+ [Op.IN]: 'array',
+ [Op.NIN]: 'array',
+ [Op.IS_EMPTY]: null,
+ [Op.IS_NOT_EMPTY]: null,
+ },
+ boolean: {
+ [Op.EQ]: 'boolean',
+ [Op.NEQ]: 'boolean',
+ [Op.IS_TRUE]: null,
+ [Op.IS_FALSE]: null,
+ [Op.IN]: 'array',
+ [Op.NIN]: 'array',
+ [Op.IS_EMPTY]: null,
+ [Op.IS_NOT_EMPTY]: null,
+ },
+ object: {
+ [Op.IS_EMPTY]: null,
+ [Op.IS_NOT_EMPTY]: null,
+ },
+ array: {
+ [Op.IS_EMPTY]: null,
+ [Op.IS_NOT_EMPTY]: null,
+ },
+ map: {
+ [Op.IS_EMPTY]: null,
+ [Op.IS_NOT_EMPTY]: null,
+ },
+};
+
+export const opConfigs: OpConfigs = {
+ [Op.EQ]: {
+ label: 'Equal',
+ abbreviation: '=',
+ },
+ [Op.NEQ]: {
+ label: 'Not Equal',
+ abbreviation: '≠',
+ },
+ [Op.GT]: {
+ label: 'Greater Than',
+ abbreviation: '>',
+ },
+ [Op.GTE]: {
+ label: 'Greater Than or Equal',
+ abbreviation: '>=',
+ },
+ [Op.LT]: {
+ label: 'Less Than',
+ abbreviation: '<',
+ },
+ [Op.LTE]: {
+ label: 'Less Than or Equal',
+ abbreviation: '<=',
+ },
+ [Op.IN]: {
+ label: 'In',
+ abbreviation: '∈',
+ },
+ [Op.NIN]: {
+ label: 'Not In',
+ abbreviation: '∉',
+ },
+ [Op.CONTAINS]: {
+ label: 'Contains',
+ abbreviation: '⊇',
+ },
+ [Op.NOT_CONTAINS]: {
+ label: 'Not Contains',
+ abbreviation: '⊉',
+ },
+ [Op.IS_EMPTY]: {
+ label: 'Is Empty',
+ abbreviation: '=',
+ rightDisplay: 'Empty',
+ },
+ [Op.IS_NOT_EMPTY]: {
+ label: 'Is Not Empty',
+ abbreviation: '≠',
+ rightDisplay: 'Empty',
+ },
+ [Op.IS_TRUE]: {
+ label: 'Is True',
+ abbreviation: '=',
+ rightDisplay: 'True',
+ },
+ [Op.IS_FALSE]: {
+ label: 'Is False',
+ abbreviation: '=',
+ rightDisplay: 'False',
+ },
+};
diff --git a/packages/materials/form-antd-materials/src/components/condition-row/hooks/styles.tsx b/packages/materials/form-antd-materials/src/components/condition-row/hooks/styles.tsx
new file mode 100644
index 00000000..a76c34bc
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/components/condition-row/hooks/styles.tsx
@@ -0,0 +1,21 @@
+import styled from 'styled-components';
+import type { SelectProps } from 'antd/es/select';
+import { Select } from 'antd';
+
+export const OpSelect: React.ComponentType = styled(Select)`
+ width: 100%;
+ height: 22px;
+ width: 24px;
+
+ & .ant-select-selector {
+ padding: 0 !important;
+ text-align: center;
+ }
+
+ & .ant-select-arrow {
+ right: 6px;
+ & > .anticon {
+ pointer-events: none !important;
+ }
+ }
+`;
diff --git a/packages/materials/form-antd-materials/src/components/condition-row/hooks/useOp.tsx b/packages/materials/form-antd-materials/src/components/condition-row/hooks/useOp.tsx
new file mode 100644
index 00000000..46ba5537
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/components/condition-row/hooks/useOp.tsx
@@ -0,0 +1,52 @@
+import React, { useMemo } from 'react';
+
+import { theme } from 'antd';
+import { DownOutlined } from '@ant-design/icons';
+
+import { IRule, Op } from '../types';
+import { opConfigs } from '../constants';
+import { OpSelect } from './styles';
+
+const { useToken } = theme;
+
+interface HookParams {
+ rule?: IRule;
+ op?: Op;
+ onChange: (op: Op) => void;
+}
+
+export function useOp({ rule, op, onChange }: HookParams) {
+ const options = useMemo(
+ () =>
+ Object.keys(rule || {}).map((_op) => ({
+ ...(opConfigs[_op as Op] || {}),
+ value: _op,
+ })),
+ [rule]
+ );
+
+ const opConfig = useMemo(() => opConfigs[op as Op], [op]);
+
+ const renderOpSelect = () => {
+ const { token } = useToken();
+ return (
+ {
+ onChange(v as Op);
+ }}
+ labelRender={({ value }) => {opConfig?.abbreviation || }}
+ suffixIcon={op ? null : undefined}
+ />
+ );
+ };
+
+ return { renderOpSelect, opConfig };
+}
diff --git a/packages/materials/form-antd-materials/src/components/condition-row/hooks/useRule.ts b/packages/materials/form-antd-materials/src/components/condition-row/hooks/useRule.ts
new file mode 100644
index 00000000..9fb86f35
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/components/condition-row/hooks/useRule.ts
@@ -0,0 +1,30 @@
+'use client';
+
+import { useMemo } from 'react';
+
+import { useScopeAvailable } from '@flowgram.ai/editor';
+
+import { rules } from '../constants';
+import { JsonSchemaUtils } from '../../../utils';
+import { IFlowRefValue, JsonSchemaBasicType } from '../../../typings';
+
+export function useRule(left?: IFlowRefValue) {
+ const available = useScopeAvailable();
+
+ const variable = useMemo(() => {
+ if (!left) return undefined;
+ return available.getByKeyPath(left.content);
+ }, [available, left]);
+
+ const rule = useMemo(() => {
+ if (!variable) return undefined;
+
+ const schema = JsonSchemaUtils.astToSchema(variable.type, {
+ drilldown: false,
+ });
+
+ return rules[schema?.type as JsonSchemaBasicType];
+ }, [variable?.type]);
+
+ return { rule };
+}
diff --git a/packages/materials/form-antd-materials/src/components/condition-row/index.tsx b/packages/materials/form-antd-materials/src/components/condition-row/index.tsx
new file mode 100644
index 00000000..26e5b0fe
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/components/condition-row/index.tsx
@@ -0,0 +1,73 @@
+'use client';
+
+import React, { useMemo } from 'react';
+
+import { VariableSelector } from '../variable-selector';
+import { DynamicValueInput } from '../dynamic-value-input';
+import { UIInput } from '../constant-input/styles';
+import { JsonSchemaBasicType } from '../../typings';
+import { ConditionRowValueType, Op } from './types';
+import { UIContainer, UILeft, UIOperator, UIRight, UIValues } from './styles';
+import { useRule } from './hooks/useRule';
+import { useOp } from './hooks/useOp';
+
+interface PropTypes {
+ value?: ConditionRowValueType;
+ onChange: (value?: ConditionRowValueType) => void;
+ style?: React.CSSProperties;
+ readonly?: boolean;
+}
+
+export function ConditionRow({ style, value, onChange, readonly }: PropTypes) {
+ const { left, operator, right } = value || {};
+ const { rule } = useRule(left);
+ const { renderOpSelect, opConfig } = useOp({
+ rule,
+ op: operator,
+ onChange: (v) => onChange({ ...value, operator: v }),
+ });
+
+ const targetSchema = useMemo(() => {
+ const targetType: JsonSchemaBasicType | null = rule?.[operator as Op] || null;
+ return targetType ? { type: targetType, extra: { weak: true } } : null;
+ }, [rule, opConfig]);
+
+ return (
+
+ {renderOpSelect()}
+
+
+
+ onChange({
+ ...value,
+ left: {
+ type: 'ref',
+ content: v,
+ },
+ })
+ }
+ allowClear={true}
+ />
+
+
+ {targetSchema ? (
+ onChange({ ...value, right: v })}
+ />
+ ) : (
+
+ )}
+
+
+
+ );
+}
+
+export type { ConditionRowValueType, Op, VariableSelector };
diff --git a/packages/materials/form-antd-materials/src/components/condition-row/styles.tsx b/packages/materials/form-antd-materials/src/components/condition-row/styles.tsx
new file mode 100644
index 00000000..308eef7b
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/components/condition-row/styles.tsx
@@ -0,0 +1,25 @@
+import styled from 'styled-components';
+
+export const UIContainer = styled.div`
+ display: flex;
+ align-items: center;
+ gap: 4px;
+`;
+
+export const UIOperator = styled.div``;
+
+export const UILeft = styled.div`
+ width: 100%;
+`;
+
+export const UIRight = styled.div`
+ width: 100%;
+`;
+
+export const UIValues = styled.div`
+ flex-grow: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 4px;
+`;
diff --git a/packages/materials/form-antd-materials/src/components/condition-row/types.ts b/packages/materials/form-antd-materials/src/components/condition-row/types.ts
new file mode 100644
index 00000000..a7a83429
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/components/condition-row/types.ts
@@ -0,0 +1,37 @@
+import { IFlowConstantRefValue, IFlowRefValue, JsonSchemaBasicType } from '../../typings';
+
+export enum Op {
+ EQ = 'eq',
+ NEQ = 'neq',
+ GT = 'gt',
+ GTE = 'gte',
+ LT = 'lt',
+ LTE = 'lte',
+ IN = 'in',
+ NIN = 'nin',
+ CONTAINS = 'contains',
+ NOT_CONTAINS = 'not_contains',
+ IS_EMPTY = 'is_empty',
+ IS_NOT_EMPTY = 'is_not_empty',
+ IS_TRUE = 'is_true',
+ IS_FALSE = 'is_false',
+}
+
+export interface OpConfig {
+ label: string;
+ abbreviation: string;
+ // When right is not a value, display this text
+ rightDisplay?: string;
+}
+
+export type OpConfigs = Record;
+
+export type IRule = Partial>;
+
+export type IRules = Record;
+
+export interface ConditionRowValueType {
+ left?: IFlowRefValue;
+ operator?: Op;
+ right?: IFlowConstantRefValue;
+}
diff --git a/packages/materials/form-antd-materials/src/components/constant-input/config.json b/packages/materials/form-antd-materials/src/components/constant-input/config.json
new file mode 100644
index 00000000..99a15f00
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/components/constant-input/config.json
@@ -0,0 +1,6 @@
+
+{
+ "name": "constant-input",
+ "depMaterials": ["typings/json-schema"],
+ "depPackages": []
+}
diff --git a/packages/materials/form-antd-materials/src/components/constant-input/index.tsx b/packages/materials/form-antd-materials/src/components/constant-input/index.tsx
new file mode 100644
index 00000000..e395b569
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/components/constant-input/index.tsx
@@ -0,0 +1,83 @@
+/* eslint-disable react/prop-types */
+import React, { useMemo } from 'react';
+
+import { PropsType, Strategy } from './types';
+import { UIInput, UIInputNumber, UISelect } from './styles';
+
+const defaultStrategies: Strategy[] = [
+ {
+ hit: (schema) => schema?.type === 'string',
+ Renderer: (props) => (
+
+ ),
+ },
+ {
+ hit: (schema) => schema?.type === 'number',
+ Renderer: (props) => (
+
+ ),
+ },
+ {
+ hit: (schema) => schema?.type === 'integer',
+ Renderer: (props) => (
+
+ ),
+ },
+ {
+ hit: (schema) => schema?.type === 'boolean',
+ Renderer: (props) => {
+ const { value, onChange, ...rest } = props;
+ return (
+ onChange?.(!!value)}
+ {...rest}
+ />
+ );
+ },
+ },
+];
+
+export function ConstantInput(props: PropsType) {
+ const { value, onChange, schema, strategies: extraStrategies, readonly, ...rest } = props;
+
+ const strategies = useMemo(
+ () => [...defaultStrategies, ...(extraStrategies || [])],
+ [extraStrategies]
+ );
+
+ const Renderer = useMemo(() => {
+ const strategy = strategies.find((_strategy) => _strategy.hit(schema));
+
+ return strategy?.Renderer;
+ }, [strategies, schema]);
+
+ if (!Renderer) {
+ return ;
+ }
+
+ return ;
+}
diff --git a/packages/materials/form-antd-materials/src/components/constant-input/styles.tsx b/packages/materials/form-antd-materials/src/components/constant-input/styles.tsx
new file mode 100644
index 00000000..2957eb73
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/components/constant-input/styles.tsx
@@ -0,0 +1,25 @@
+import styled from 'styled-components';
+import type { SelectProps } from 'antd/es/select';
+import type { InputNumberProps } from 'antd/es/input-number';
+import { Input, InputNumber, Select } from 'antd';
+
+const commonStyle = `
+ width: 100%;
+ height: 22px;
+ border-radius: 6px;
+ padding: 4px 11px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+`;
+
+export const UIInput = styled(Input)`
+ ${commonStyle}
+`;
+export const UIInputNumber: React.ComponentType = styled(InputNumber)`
+ ${commonStyle}
+ padding: 4px 4px;
+`;
+export const UISelect: React.ComponentType = styled(Select)`
+ ${commonStyle}
+`;
diff --git a/packages/materials/form-antd-materials/src/components/constant-input/types.ts b/packages/materials/form-antd-materials/src/components/constant-input/types.ts
new file mode 100644
index 00000000..a1548afb
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/components/constant-input/types.ts
@@ -0,0 +1,18 @@
+import { IJsonSchema } from '../../typings';
+
+export interface Strategy {
+ hit: (schema: IJsonSchema) => boolean;
+ Renderer: React.FC>;
+}
+
+export interface RendererProps {
+ value?: Value;
+ onChange?: (value: Value) => void;
+ readonly?: boolean;
+}
+
+export interface PropsType extends RendererProps {
+ schema: IJsonSchema;
+ strategies?: Strategy[];
+ [key: string]: any;
+}
diff --git a/packages/materials/form-antd-materials/src/components/dynamic-value-input/config.json b/packages/materials/form-antd-materials/src/components/dynamic-value-input/config.json
new file mode 100644
index 00000000..f1453850
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/components/dynamic-value-input/config.json
@@ -0,0 +1,5 @@
+{
+ "name": "dynamic-value-input",
+ "depMaterials": ["flow-value", "constant-input", "variable-selector"],
+ "depPackages": ["styled-components"]
+}
diff --git a/packages/materials/form-antd-materials/src/components/dynamic-value-input/index.tsx b/packages/materials/form-antd-materials/src/components/dynamic-value-input/index.tsx
new file mode 100644
index 00000000..fc1c6d90
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/components/dynamic-value-input/index.tsx
@@ -0,0 +1,88 @@
+import React, { useMemo } from 'react';
+
+import { theme } from 'antd';
+import { SettingFilled } from '@ant-design/icons';
+
+import { VariableSelector } from '../variable-selector';
+import { Strategy } from '../constant-input/types';
+import { ConstantInput } from '../constant-input';
+import { IFlowConstantRefValue } from '../../typings/flow-value';
+import { IJsonSchema } from '../../typings';
+import { UIContainer, UIMain, UITrigger } from './styles';
+
+const { useToken } = theme;
+
+interface PropsType {
+ value?: IFlowConstantRefValue;
+ onChange: (value?: IFlowConstantRefValue) => void;
+ readonly?: boolean;
+ hasError?: boolean;
+ style?: React.CSSProperties;
+ schema?: IJsonSchema;
+ constantProps?: {
+ strategies?: Strategy[];
+ [key: string]: any;
+ };
+}
+
+export function DynamicValueInput({
+ value,
+ onChange,
+ readonly,
+ style,
+ schema,
+ constantProps,
+}: PropsType) {
+ const { token } = useToken();
+
+ // When is number type, include integer as well
+ const includeSchema = useMemo(() => {
+ if (schema?.type === 'number') {
+ return [schema, { type: 'integer' }];
+ }
+ return schema;
+ }, [schema]);
+
+ const renderMain = () => {
+ if (value?.type === 'ref') {
+ // Display Variable Or Delete
+ return (
+ onChange(_v ? { type: 'ref', content: _v } : undefined)}
+ includeSchema={includeSchema}
+ readonly={readonly}
+ />
+ );
+ }
+
+ return (
+ onChange({ type: 'constant', content: _v })}
+ schema={schema || { type: 'string' }}
+ readonly={readonly}
+ {...constantProps}
+ />
+ );
+ };
+
+ const renderTrigger = () => (
+ onChange({ type: 'ref', content: _v })}
+ includeSchema={includeSchema}
+ readonly={readonly}
+ triggerRender={() => }
+ />
+ );
+
+ return (
+
+ {renderMain()}
+ {renderTrigger()}
+
+ );
+}
diff --git a/packages/materials/form-antd-materials/src/components/dynamic-value-input/styles.tsx b/packages/materials/form-antd-materials/src/components/dynamic-value-input/styles.tsx
new file mode 100644
index 00000000..3129a88b
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/components/dynamic-value-input/styles.tsx
@@ -0,0 +1,29 @@
+import styled from 'styled-components';
+
+export const UIContainer = styled.div`
+ display: flex;
+ align-items: center;
+ gap: 5px;
+`;
+
+export const UIMain = styled.div`
+ flex-grow: 1;
+`;
+
+export const UITrigger = styled.div`
+ outline: none;
+ height: 22px;
+ min-height: 22px;
+ line-height: 22px;
+
+ & .ant-select-selection-wrap {
+ display: none;
+ }
+
+ & .ant-select-arrow {
+ right: 6px;
+ & > .anticon {
+ pointer-events: none !important;
+ }
+ }
+`;
diff --git a/packages/materials/form-antd-materials/src/components/index.ts b/packages/materials/form-antd-materials/src/components/index.ts
new file mode 100644
index 00000000..019f2934
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/components/index.ts
@@ -0,0 +1,7 @@
+export * from './variable-selector';
+export * from './type-selector';
+export * from './json-schema-editor';
+export * from './batch-variable-selector';
+export * from './constant-input';
+export * from './dynamic-value-input';
+export * from './condition-row';
diff --git a/packages/materials/form-antd-materials/src/components/json-schema-editor/components/blur-input.tsx b/packages/materials/form-antd-materials/src/components/json-schema-editor/components/blur-input.tsx
new file mode 100644
index 00000000..939bf80f
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/components/json-schema-editor/components/blur-input.tsx
@@ -0,0 +1,21 @@
+import React, { useEffect, useState } from 'react';
+
+import { Input, InputProps } from 'antd';
+
+export function BlurInput(props: InputProps) {
+ const [value, setValue] = useState('');
+
+ useEffect(() => {
+ setValue(props.value as string);
+ }, [props.value]);
+
+ return (
+ {
+ setValue((value as any).target?.value || '');
+ }}
+ />
+ );
+}
diff --git a/packages/materials/form-antd-materials/src/components/json-schema-editor/config.json b/packages/materials/form-antd-materials/src/components/json-schema-editor/config.json
new file mode 100644
index 00000000..63c8836d
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/components/json-schema-editor/config.json
@@ -0,0 +1,5 @@
+{
+ "name": "json-schema-editor",
+ "depMaterials": ["type-selector", "typings/json-schema"],
+ "depPackages": ["styled-components"]
+}
diff --git a/packages/materials/form-antd-materials/src/components/json-schema-editor/default-value.tsx b/packages/materials/form-antd-materials/src/components/json-schema-editor/default-value.tsx
new file mode 100644
index 00000000..ae833591
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/components/json-schema-editor/default-value.tsx
@@ -0,0 +1,133 @@
+import React, { useCallback, useRef, useState } from 'react';
+
+import { Button, Tooltip, theme } from 'antd';
+import { CodeOutlined } from '@ant-design/icons';
+
+import { ConstantInput } from '../constant-input';
+import { IJsonSchema } from '../../typings';
+import { getValueType } from './utils';
+import {
+ ConstantInputWrapper,
+ JSONHeader,
+ JSONHeaderLeft,
+ JSONHeaderRight,
+ JSONViewerWrapper,
+} from './styles';
+
+const { useToken } = theme;
+
+/**
+ * 根据不同的数据类型渲染对应的默认值输入组件。
+ * @param props - 组件属性,包括 value, type, placeholder, onChange。
+ * @returns 返回对应类型的输入组件或 null。
+ */
+export function DefaultValue(props: {
+ value: any;
+ schema?: IJsonSchema;
+ name?: string;
+ type?: string;
+ placeholder?: string;
+ jsonFormatText?: string;
+ onChange: (value: any) => void;
+}) {
+ const { token } = useToken();
+ const { value, schema, type, onChange, placeholder, jsonFormatText } = props;
+
+ const wrapperRef = useRef(null);
+
+ // TODO add JsonViewer
+ // const JsonViewerRef = useRef(null);
+
+ // 为 JsonViewer 添加状态管理
+ const [internalJsonValue, setInternalJsonValue] = useState(
+ getValueType(value) === 'string' ? value : ''
+ );
+
+ // 使用 useCallback 创建稳定的回调函数
+ // const handleJsonChange = useCallback((val: string) => {
+ // // 只在值真正改变时才更新状态
+ // if (val !== internalJsonValue) {
+ // setInternalJsonValue(val);
+ // }
+ // }, []);
+
+ // 处理编辑完成事件
+ const handleEditComplete = useCallback(() => {
+ // 只有当存在key,编辑完成时才触发父组件的 onChange
+ onChange(internalJsonValue);
+ // 确保在更新后移除焦点
+ requestAnimationFrame(() => {
+ // JsonViewerRef.current?.format();
+ wrapperRef.current?.blur();
+ });
+ // setJsonReadOnly(true);
+ }, [internalJsonValue, onChange]);
+
+ // const [jsonReadOnly, setJsonReadOnly] = useState(true);
+
+ const handleFormatJson = useCallback(() => {
+ try {
+ const parsed = JSON.parse(internalJsonValue);
+ const formatted = JSON.stringify(parsed, null, 4);
+ setInternalJsonValue(formatted);
+ onChange(formatted);
+ } catch (error) {
+ console.error('Invalid JSON:', error);
+ }
+ }, [internalJsonValue, onChange]);
+
+ return type === 'object' ? (
+ <>
+
+ json
+
+
+ }
+ size="small"
+ onClick={handleFormatJson}
+ />
+
+
+
+
+ {
+ if (wrapperRef.current && !wrapperRef.current?.contains(e.relatedTarget as Node)) {
+ handleEditComplete();
+ }
+ }}
+ onClick={(e: React.MouseEvent) => {
+ // setJsonReadOnly(false);
+ }}
+ >
+ {/* */}
+
+ >
+ ) : (
+
+ onChange(_v)}
+ schema={schema || { type: 'string' }}
+ placeholder={placeholder ?? 'Default value if parameter is not provided'}
+ />
+
+ );
+}
diff --git a/packages/materials/form-antd-materials/src/components/json-schema-editor/hooks.tsx b/packages/materials/form-antd-materials/src/components/json-schema-editor/hooks.tsx
new file mode 100644
index 00000000..6f43b540
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/components/json-schema-editor/hooks.tsx
@@ -0,0 +1,166 @@
+import { useEffect, useMemo, useRef, useState } from 'react';
+
+import { IJsonSchema } from '../../typings';
+import { PropertyValueType } from './types';
+
+let _id = 0;
+function genId() {
+ return _id++;
+}
+
+function getDrilldownSchema(
+ value?: PropertyValueType,
+ path?: (keyof PropertyValueType)[]
+): { schema?: PropertyValueType | null; path?: (keyof PropertyValueType)[] } {
+ if (!value) {
+ return {};
+ }
+
+ if (value.type === 'array' && value.items) {
+ return getDrilldownSchema(value.items, [...(path || []), 'items']);
+ }
+
+ return { schema: value, path };
+}
+
+export function usePropertiesEdit(
+ value?: PropertyValueType,
+ onChange?: (value: PropertyValueType) => void
+) {
+ // Get drilldown (array.items.items...)
+ const drilldown = useMemo(() => getDrilldownSchema(value), [value, value?.type, value?.items]);
+
+ const isDrilldownObject = drilldown.schema?.type === 'object';
+
+ // Generate Init Property List
+ const initPropertyList = useMemo(
+ () =>
+ isDrilldownObject
+ ? Object.entries(drilldown.schema?.properties || {})
+ .sort(([, a], [, b]) => (a.extra?.index ?? 0) - (b.extra?.index ?? 0))
+ .map(
+ ([name, _value], index) =>
+ ({
+ key: genId(),
+ name,
+ isPropertyRequired: drilldown.schema?.required?.includes(name) || false,
+ ..._value,
+ extra: {
+ ...(_value.extra || {}),
+ index,
+ },
+ } as PropertyValueType)
+ )
+ : [],
+ [isDrilldownObject]
+ );
+
+ const [propertyList, setPropertyList] = useState(initPropertyList);
+
+ const mountRef = useRef(false);
+
+ useEffect(() => {
+ // If initRef is true, it means the component has been mounted
+ if (mountRef.current) {
+ // If the value is changed, update the property list
+ setPropertyList((_list) => {
+ const nameMap = new Map();
+
+ for (const _property of _list) {
+ if (_property.name) {
+ nameMap.set(_property.name, _property);
+ }
+ }
+ return Object.entries(drilldown.schema?.properties || {})
+ .sort(([, a], [, b]) => (a.extra?.index ?? 0) - (b.extra?.index ?? 0))
+ .map(([name, _value]) => {
+ const _property = nameMap.get(name);
+ if (_property) {
+ return {
+ key: _property.key,
+ name,
+ isPropertyRequired: drilldown.schema?.required?.includes(name) || false,
+ ..._value,
+ };
+ }
+ return {
+ key: genId(),
+ name,
+ isPropertyRequired: drilldown.schema?.required?.includes(name) || false,
+ ..._value,
+ };
+ });
+ });
+ }
+ mountRef.current = true;
+ }, [drilldown.schema]);
+
+ const updatePropertyList = (updater: (list: PropertyValueType[]) => PropertyValueType[]) => {
+ setPropertyList((_list) => {
+ const next = updater(_list);
+
+ // onChange to parent
+ const nextProperties: Record = {};
+ const nextRequired: string[] = [];
+
+ for (const _property of next) {
+ if (!_property.name) {
+ continue;
+ }
+
+ nextProperties[_property.name] = _property;
+
+ if (_property.isPropertyRequired) {
+ nextRequired.push(_property.name);
+ }
+ }
+
+ let drilldownSchema = value || {};
+ if (drilldown.path) {
+ drilldownSchema = drilldown.path.reduce((acc, key) => acc[key], value || {});
+ }
+ drilldownSchema.properties = nextProperties;
+ drilldownSchema.required = nextRequired;
+
+ onChange?.(value || {});
+
+ return next;
+ });
+ };
+
+ const onAddProperty = () => {
+ updatePropertyList((_list) => [
+ ..._list,
+ {
+ key: genId(),
+ name: '',
+ type: 'string',
+ extra: { index: _list.length + 1 },
+ },
+ ]);
+ };
+
+ const onRemoveProperty = (key: number) => {
+ updatePropertyList((_list) => _list.filter((_property) => _property.key !== key));
+ };
+
+ const onEditProperty = (key: number, nextValue: PropertyValueType) => {
+ updatePropertyList((_list) =>
+ _list.map((_property) => (_property.key === key ? nextValue : _property))
+ );
+ };
+
+ useEffect(() => {
+ if (!isDrilldownObject) {
+ setPropertyList([]);
+ }
+ }, [isDrilldownObject]);
+
+ return {
+ propertyList,
+ isDrilldownObject,
+ onAddProperty,
+ onRemoveProperty,
+ onEditProperty,
+ };
+}
diff --git a/packages/materials/form-antd-materials/src/components/json-schema-editor/index.tsx b/packages/materials/form-antd-materials/src/components/json-schema-editor/index.tsx
new file mode 100644
index 00000000..11b76fb3
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/components/json-schema-editor/index.tsx
@@ -0,0 +1,256 @@
+import React, { useMemo, useState } from 'react';
+
+import { Button, Checkbox } from 'antd';
+import {
+ DownOutlined,
+ ExpandAltOutlined,
+ MinusOutlined,
+ PlusOutlined,
+ RightOutlined,
+ ShrinkOutlined,
+} from '@ant-design/icons';
+
+import { TypeSelector } from '../type-selector';
+import { IJsonSchema } from '../../typings';
+import { ConfigType, PropertyValueType } from './types';
+import {
+ DefaultValueWrapper,
+ IconAddChildren,
+ UIActions,
+ UICollapseTrigger,
+ UICollapsible,
+ UIContainer,
+ UIExpandDetail,
+ UILabel,
+ UIName,
+ UIProperties,
+ UIPropertyLeft,
+ UIPropertyMain,
+ UIPropertyRight,
+ UIRequired,
+ UIRow,
+ UIType,
+} from './styles';
+import { usePropertiesEdit } from './hooks';
+import { DefaultValue } from './default-value';
+import { BlurInput } from './components/blur-input';
+
+export function JsonSchemaEditor(props: {
+ value?: IJsonSchema;
+ onChange?: (value: IJsonSchema) => void;
+ config?: ConfigType;
+}) {
+ const { value = { type: 'object' }, config = {}, onChange: onChangeProps } = props;
+ const { propertyList, onAddProperty, onRemoveProperty, onEditProperty } = usePropertiesEdit(
+ value,
+ onChangeProps
+ );
+
+ return (
+
+
+ {propertyList.map((_property, index) => (
+ {
+ onEditProperty(_property.key!, _v);
+ }}
+ onRemove={() => {
+ onRemoveProperty(_property.key!);
+ }}
+ />
+ ))}
+
+ }
+ onClick={onAddProperty}
+ >
+ {config?.addButtonText ?? 'Add'}
+
+
+ );
+}
+
+function PropertyEdit(props: {
+ value?: PropertyValueType;
+ config?: ConfigType;
+ onChange?: (value: PropertyValueType) => void;
+ onRemove?: () => void;
+ $isLast?: boolean;
+ $index?: number;
+ $isFirst?: boolean;
+ $parentExpand?: boolean;
+ $parentType?: string;
+ $showLine?: boolean;
+ $level?: number; // 添加层级属性
+}) {
+ const {
+ value,
+ config,
+ $level = 0,
+ onChange: onChangeProps,
+ onRemove,
+ $index,
+ $isFirst,
+ $isLast,
+ $parentExpand = false,
+ $parentType = '',
+ $showLine,
+ } = props;
+
+ const [expand, setExpand] = useState(false);
+ const [collapse, setCollapse] = useState(false);
+
+ const { name, type, items, default: defaultValue, description, isPropertyRequired } = value || {};
+
+ const typeSelectorValue = useMemo(() => ({ type, items }), [type, items]);
+
+ const { propertyList, isDrilldownObject, onAddProperty, onRemoveProperty, onEditProperty } =
+ usePropertiesEdit(value, onChangeProps);
+
+ const onChange = (key: string, _value: any) => {
+ onChangeProps?.({
+ ...(value || {}),
+ [key]: _value,
+ });
+ };
+
+ const showCollapse = isDrilldownObject && propertyList.length > 0;
+
+ return (
+ <>
+
+ {showCollapse && (
+ setCollapse((_collapse) => !_collapse)}>
+ {collapse ? : }
+
+ )}
+
+
+
+
+
+ onChange('name', value)}
+ />
+
+
+ {
+ onChangeProps?.({
+ ...(value || {}),
+ ..._value,
+ });
+ }}
+ />
+
+
+ onChange('isPropertyRequired', e.target.checked)}
+ />
+
+
+ : }
+ onClick={() => {
+ setExpand((_expand) => !_expand);
+ }}
+ />
+ {isDrilldownObject && (
+ }
+ onClick={() => {
+ onAddProperty();
+ setCollapse(true);
+ }}
+ />
+ )}
+ } onClick={onRemove} />
+
+
+ {expand && (
+
+ {config?.descTitle ?? 'Description'}
+ onChange('description', value)}
+ placeholder={config?.descPlaceholder ?? 'Help LLM to understand the property'}
+ />
+ {$level === 0 && type && type !== 'array' && (
+ <>
+
+ {config?.defaultValueTitle ?? 'Default Value'}
+
+
+ onChange('default', value)}
+ />
+
+ >
+ )}
+
+ )}
+
+ {showCollapse && (
+
+
+ {propertyList.map((_property, index) => (
+ {
+ onEditProperty(_property.key!, _v);
+ }}
+ onRemove={() => {
+ onRemoveProperty(_property.key!);
+ }}
+ $isLast={index === propertyList.length - 1}
+ $isFirst={index === 0}
+ $index={index}
+ $showLine={true}
+ />
+ ))}
+
+
+ )}
+
+ >
+ );
+}
diff --git a/packages/materials/form-antd-materials/src/components/json-schema-editor/styles.tsx b/packages/materials/form-antd-materials/src/components/json-schema-editor/styles.tsx
new file mode 100644
index 00000000..f3c10176
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/components/json-schema-editor/styles.tsx
@@ -0,0 +1,223 @@
+import React from 'react';
+
+import styled, { css } from 'styled-components';
+
+import { SvgIcon } from '../../utils';
+
+export const UIContainer = styled.div``;
+
+export const UIRow = styled.div`
+ display: flex;
+ align-items: center;
+ gap: 6px;
+`;
+
+export const UICollapseTrigger = styled.div`
+ cursor: pointer;
+ margin-right: 5px;
+`;
+
+export const UIExpandDetail = styled.div`
+ display: flex;
+ flex-direction: column;
+`;
+
+export const UILabel = styled.div`
+ font-size: 12px;
+ color: #999;
+ font-weight: 400;
+ margin-bottom: 2px;
+`;
+
+export const UIProperties = styled.div<{ $shrink?: boolean }>`
+ display: grid;
+ grid-template-columns: auto 1fr;
+
+ ${({ $shrink }) =>
+ $shrink &&
+ css`
+ padding-left: 10px;
+ margin-top: 10px;
+ `}
+`;
+
+export const UIPropertyLeft = styled.div<{
+ $isLast?: boolean;
+ $showLine?: boolean;
+ $isExpand?: boolean;
+ type?: string;
+ $isFirst?: boolean;
+ $index?: number;
+ $parentExpand?: boolean;
+ $parentType?: string;
+}>`
+ grid-column: 1;
+ position: relative;
+ width: 16px;
+
+ ${({ $showLine, $isLast, $parentType }) => {
+ let height = '100%';
+ if ($parentType && $isLast) {
+ height = '24px';
+ }
+
+ return (
+ $showLine &&
+ css`
+ &::before {
+ /* 竖线 */
+ content: '';
+ height: ${height};
+ position: absolute;
+ left: -22px;
+ top: -16px;
+ width: 1px;
+ background: #d9d9d9;
+ display: block;
+ }
+
+ &::after {
+ /* 横线 */
+ content: '';
+ position: absolute;
+ left: -22px; // 横线起点和竖线对齐
+ top: 8px; // 跟随你的行高调整
+ width: 18px; // 横线长度
+ height: 1px;
+ background: #d9d9d9;
+ display: block;
+ }
+ `
+ );
+ }}
+`;
+
+export const UIPropertyRight = styled.div`
+ grid-column: 2;
+ margin-bottom: 10px;
+
+ &:last-child {
+ margin-bottom: 0px;
+ }
+`;
+
+export const UIPropertyMain = styled.div<{
+ $expand?: boolean;
+ type?: string;
+ $collapse?: boolean;
+ $showCollapse?: boolean;
+}>`
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ position: relative;
+
+ ${({ $expand, type, $collapse, $showCollapse }) => {
+ const beforeElement = `
+ &::before {
+ /* 竖线 */
+ content: '';
+ height: 100%;
+ position: absolute;
+ left: -12px;
+ top: 18px;
+ width: 1px;
+ background: #d9d9d9;
+ display: block;
+ }`;
+
+ return (
+ $expand &&
+ css`
+ background-color: #f5f5f5;
+ padding: 10px;
+ border-radius: 4px;
+
+ ${$showCollapse &&
+ $collapse &&
+ (type === 'array' || type === 'object') &&
+ css`
+ ${beforeElement}
+ `}
+ `
+ );
+ }}
+`;
+
+export const UICollapsible = styled.div<{ $collapse?: boolean }>`
+ display: none;
+
+ ${({ $collapse }) =>
+ $collapse &&
+ css`
+ display: block;
+ `}
+`;
+
+export const UIName = styled.div`
+ flex-grow: 1;
+`;
+
+export const UIType = styled.div``;
+
+export const UIRequired = styled.div``;
+
+export const UIActions = styled.div`
+ white-space: nowrap;
+`;
+
+const iconAddChildrenSvg = (
+
+);
+
+export const IconAddChildren = () => ;
+
+export const DefaultValueWrapper = styled.div`
+ margin: 0;
+`;
+
+export const JSONViewerWrapper = styled.div`
+ padding: 0 0 24px;
+ &:first-child {
+ margin-top: 0px;
+ }
+`;
+
+export const JSONHeader = styled.div`
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ border-radius: 6px 6px 0 0;
+ height: 36px;
+ padding: 0 8px 0 12px;
+`;
+
+export const JSONHeaderLeft = styled.div`
+ display: flex;
+ align-items: center;
+ gap: 10px;
+`;
+
+export const JSONHeaderRight = styled.div`
+ display: flex;
+ align-items: center;
+ gap: 10px;
+`;
+
+export const ConstantInputWrapper = styled.div`
+ flex-grow: 1;
+`;
diff --git a/packages/materials/form-antd-materials/src/components/json-schema-editor/types.ts b/packages/materials/form-antd-materials/src/components/json-schema-editor/types.ts
new file mode 100644
index 00000000..17976bdb
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/components/json-schema-editor/types.ts
@@ -0,0 +1,21 @@
+import { IJsonSchema } from '../../typings';
+
+export interface PropertyValueType extends IJsonSchema {
+ name?: string;
+ key?: number;
+ isPropertyRequired?: boolean;
+}
+
+export type PropertiesValueType = Pick;
+
+export type JsonSchemaProperties = IJsonSchema['properties'];
+
+export interface ConfigType {
+ placeholder?: string;
+ descTitle?: string;
+ descPlaceholder?: string;
+ defaultValueTitle?: string;
+ defaultValuePlaceholder?: string;
+ addButtonText?: string;
+ jsonFormatText?: string;
+}
diff --git a/packages/materials/form-antd-materials/src/components/json-schema-editor/utils.ts b/packages/materials/form-antd-materials/src/components/json-schema-editor/utils.ts
new file mode 100644
index 00000000..a5dab196
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/components/json-schema-editor/utils.ts
@@ -0,0 +1,24 @@
+/**
+ * Return the corresponding string description according to the type of the input value.根据输入值的类型返回对应的字符串描述。
+ * @param value - 需要判断类型的值。The value whose type needs to be judged.
+ * @returns 返回值的类型字符串 The type string of the return value('string', 'integer', 'number', 'boolean', 'object', 'array', 'other')。
+ */
+export function getValueType(value: any): string {
+ const type = typeof value;
+
+ if (type === 'string') {
+ return 'string';
+ } else if (type === 'number') {
+ return Number.isInteger(value) ? 'integer' : 'number';
+ } else if (type === 'boolean') {
+ return 'boolean';
+ } else if (type === 'object') {
+ if (value === null) {
+ return 'other';
+ }
+ return Array.isArray(value) ? 'array' : 'object';
+ } else {
+ // undefined, function, symbol, bigint etc.
+ return 'other';
+ }
+}
diff --git a/packages/materials/form-antd-materials/src/components/type-selector/config.json b/packages/materials/form-antd-materials/src/components/type-selector/config.json
new file mode 100644
index 00000000..1b63a1ce
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/components/type-selector/config.json
@@ -0,0 +1,5 @@
+{
+ "name": "type-selector",
+ "depMaterials": ["typings/json-schema"],
+ "depPackages": []
+}
diff --git a/packages/materials/form-antd-materials/src/components/type-selector/constants.tsx b/packages/materials/form-antd-materials/src/components/type-selector/constants.tsx
new file mode 100644
index 00000000..0bcec143
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/components/type-selector/constants.tsx
@@ -0,0 +1,372 @@
+import React from 'react';
+
+import { SvgIcon } from '../../utils';
+import { IJsonSchema } from '../../typings';
+
+interface CascaderData {
+ value: string | number;
+ label?: React.ReactNode;
+ disabled?: boolean;
+ children?: CascaderData[];
+ isLeaf?: boolean;
+}
+
+export const VariableTypeIcons: { [key: string]: React.ReactNode } = {
+ custom: (
+
+ ),
+ object: (
+
+ ),
+ boolean: (
+
+ ),
+ string: (
+
+ ),
+ integer: (
+
+ ),
+ number: (
+
+ ),
+ array: (
+
+ ),
+
+ stream: (
+
+ ),
+
+ map: (
+
+ ),
+};
+
+export const ArrayIcons: { [key: string]: React.ReactNode } = {
+ object: (
+
+ ),
+ boolean: (
+
+ ),
+ string: (
+
+ ),
+ integer: (
+
+ ),
+ number: (
+
+ ),
+};
+
+export const getSchemaIcon = (value?: Partial) => {
+ if (value?.type === 'array') {
+ return ArrayIcons[value.items?.type || 'object'];
+ }
+
+ return VariableTypeIcons[value?.type || 'object'];
+};
+
+const labelStyle: React.CSSProperties = {
+ display: 'flex',
+ alignItems: 'center',
+ gap: 5,
+};
+
+const firstUppercase = (str: string) => str.charAt(0).toUpperCase() + str.slice(1);
+
+const baseOptions: CascaderData[] = [
+ {
+ label: (
+
+
+ {firstUppercase('string')}
+
+ ),
+ value: 'string',
+ },
+ {
+ label: (
+
+
+ {firstUppercase('integer')}
+
+ ),
+ value: 'integer',
+ },
+ {
+ label: (
+
+
+ {firstUppercase('number')}
+
+ ),
+ value: 'number',
+ },
+
+ {
+ label: (
+
+
+ {firstUppercase('boolean')}
+
+ ),
+ value: 'boolean',
+ },
+ {
+ label: (
+
+
+ {firstUppercase('object')}
+
+ ),
+ value: 'object',
+ },
+];
+
+export const options: CascaderData[] = [
+ ...baseOptions,
+ {
+ label: (
+
+
+ {firstUppercase('array')}
+
+ ),
+ value: 'array',
+ children: baseOptions.map((_opt) => ({
+ ..._opt,
+ value: `${_opt.value}`,
+ label: (
+
+
+ {firstUppercase(_opt.value as string)}
+
+ ),
+ })),
+ },
+];
diff --git a/packages/materials/form-antd-materials/src/components/type-selector/index.tsx b/packages/materials/form-antd-materials/src/components/type-selector/index.tsx
new file mode 100644
index 00000000..3c6f3816
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/components/type-selector/index.tsx
@@ -0,0 +1,58 @@
+import React, { useMemo } from 'react';
+
+import { Cascader } from 'antd';
+
+import { IJsonSchema } from '../../typings';
+import { ArrayIcons, VariableTypeIcons, getSchemaIcon } from './constants';
+
+interface PropTypes {
+ value?: Partial;
+ onChange: (value?: Partial) => void;
+ disabled?: boolean;
+ style?: React.CSSProperties;
+}
+
+export const getTypeSelectValue = (value?: Partial): string[] | undefined => {
+ if (value?.type === 'array' && value?.items) {
+ return [value.type, ...(getTypeSelectValue(value.items) || [])];
+ }
+
+ return value?.type ? [value.type] : undefined;
+};
+
+export const parseTypeSelectValue = (value?: string[]): Partial | undefined => {
+ const [type, ...subTypes] = value || [];
+
+ if (type === 'array') {
+ return { type: 'array', items: parseTypeSelectValue(subTypes) };
+ }
+
+ return { type };
+};
+
+export function TypeSelector(props: PropTypes) {
+ const { value, onChange, disabled } = props;
+
+ const selectValue = useMemo(() => getTypeSelectValue(value), [value]);
+
+ return (
+ (
+ //
+ // )}
+ // treeData={options}
+ value={selectValue}
+ // leafOnly={true}
+ onChange={(value) => {
+ onChange(parseTypeSelectValue(value as string[]));
+ }}
+ />
+ );
+}
+
+export { ArrayIcons, VariableTypeIcons, getSchemaIcon };
diff --git a/packages/materials/form-antd-materials/src/components/variable-selector/config.json b/packages/materials/form-antd-materials/src/components/variable-selector/config.json
new file mode 100644
index 00000000..f7bb5ed3
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/components/variable-selector/config.json
@@ -0,0 +1,5 @@
+{
+ "name": "variable-selector",
+ "depMaterials": ["type-selector", "utils/json-schema", "typings/json-schema"],
+ "depPackages": ["styled-components"]
+}
diff --git a/packages/materials/form-antd-materials/src/components/variable-selector/index.tsx b/packages/materials/form-antd-materials/src/components/variable-selector/index.tsx
new file mode 100644
index 00000000..84074fd5
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/components/variable-selector/index.tsx
@@ -0,0 +1,77 @@
+'use client';
+import React from 'react';
+
+import type { TreeSelectProps, TreeNodeProps } from 'antd';
+import { DownOutlined } from '@ant-design/icons';
+
+import { IJsonSchema } from '../../typings/json-schema';
+import { useVariableTree } from './use-variable-tree';
+import { UITreeSelect } from './styles';
+
+interface TriggerRenderProps {
+ value: string[];
+}
+
+interface PropTypes {
+ value?: string[];
+ config?: {
+ placeholder?: string;
+ notFoundContent?: string;
+ };
+ onChange: (value?: string[]) => void;
+ includeSchema?: IJsonSchema | IJsonSchema[];
+ excludeSchema?: IJsonSchema | IJsonSchema[];
+ readonly?: boolean;
+ allowClear?: boolean;
+ hasError?: boolean;
+ style?: React.CSSProperties;
+ triggerRender?: (props: TriggerRenderProps) => React.ReactNode;
+}
+
+export type VariableSelectorProps = PropTypes;
+
+export const VariableSelector = ({
+ value,
+ config = {},
+ onChange,
+ style,
+ readonly = false,
+ allowClear = false,
+ includeSchema,
+ excludeSchema,
+ hasError,
+ triggerRender,
+}: PropTypes) => {
+ const treeData = useVariableTree({ includeSchema, excludeSchema });
+
+ const onPopupScroll: TreeSelectProps['onPopupScroll'] = (e) => {
+ console.log('onPopupScroll', e);
+ };
+
+ return (
+ (
+
+ )}
+ />
+ );
+};
diff --git a/packages/materials/form-antd-materials/src/components/variable-selector/styles.tsx b/packages/materials/form-antd-materials/src/components/variable-selector/styles.tsx
new file mode 100644
index 00000000..8fdd4764
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/components/variable-selector/styles.tsx
@@ -0,0 +1,40 @@
+import styled from 'styled-components';
+import type { TreeSelectProps } from 'antd/es/tree-select';
+import { Tag, TreeSelect } from 'antd';
+
+export const UIRootTitle = styled.span`
+ margin-right: 4px;
+`;
+
+export const UITag = styled(Tag)`
+ width: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: flex-start;
+`;
+
+export const UITreeSelect: React.ComponentType> = styled(TreeSelect)`
+ height: 22px;
+ min-height: 22px;
+ line-height: 22px;
+
+ & .ant-select-clear {
+ right: 20px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ & .ant-select-arrow {
+ right: 6px;
+ & > .anticon {
+ pointer-events: none !important;
+ }
+ }
+`;
+
+export const ImgIconWrapper = styled.div`
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+`;
diff --git a/packages/materials/form-antd-materials/src/components/variable-selector/types.ts b/packages/materials/form-antd-materials/src/components/variable-selector/types.ts
new file mode 100644
index 00000000..bf655319
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/components/variable-selector/types.ts
@@ -0,0 +1,15 @@
+import type { ReactElement } from 'react';
+
+export interface TreeNodeData {
+ value: string | number;
+ title: string;
+ disabled?: boolean;
+ disableCheckbox?: boolean;
+ selectable?: boolean;
+ checkable?: boolean;
+ children?: TreeNodeData[];
+ icon: ReactElement;
+ key: string;
+ keyPath: string[];
+ rootMeta: VariableMeta;
+}
diff --git a/packages/materials/form-antd-materials/src/components/variable-selector/use-variable-tree.tsx b/packages/materials/form-antd-materials/src/components/variable-selector/use-variable-tree.tsx
new file mode 100644
index 00000000..4136d4d4
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/components/variable-selector/use-variable-tree.tsx
@@ -0,0 +1,102 @@
+import React, { useCallback } from 'react';
+
+import { ASTMatch, BaseVariableField, useScopeAvailable } from '@flowgram.ai/editor';
+
+import { ArrayIcons, VariableTypeIcons } from '../type-selector/constants';
+import { JsonSchemaUtils } from '../../utils/json-schema';
+import { SvgIcon } from '../../utils';
+import { IJsonSchema } from '../../typings/json-schema';
+import { TreeNodeData } from './types';
+import { ImgIconWrapper } from './styles';
+
+type VariableField = BaseVariableField<{
+ icon?: string;
+ title?: string;
+}>;
+
+export function useVariableTree(params: {
+ includeSchema?: IJsonSchema | IJsonSchema[];
+ excludeSchema?: IJsonSchema | IJsonSchema[];
+}): TreeNodeData[] {
+ const { includeSchema, excludeSchema } = params;
+
+ const available = useScopeAvailable();
+
+ const getVariableTypeIcon = useCallback((variable: VariableField) => {
+ if (variable.meta?.icon) {
+ return (
+
+
+
+ );
+ }
+
+ const _type = variable.type;
+
+ if (ASTMatch.isArray(_type)) {
+ return (
+
+ );
+ }
+
+ if (ASTMatch.isCustomType(_type)) {
+ return ;
+ }
+
+ return ;
+ }, []);
+
+ const renderVariable = (
+ variable: VariableField,
+ parentFields: VariableField[] = []
+ ): TreeNodeData | null => {
+ let type = variable?.type;
+
+ if (!type) {
+ return null;
+ }
+
+ let children: TreeNodeData[] | undefined;
+
+ if (ASTMatch.isObject(type)) {
+ children = (type.properties || [])
+ .map((_property) => renderVariable(_property as VariableField, [...parentFields, variable]))
+ .filter(Boolean) as TreeNodeData[];
+
+ if (!children?.length) {
+ return null;
+ }
+ }
+
+ const keyPath = [...parentFields.map((_field) => _field.key), variable.key];
+ const key = keyPath.join('.');
+
+ const isSchemaInclude = includeSchema
+ ? JsonSchemaUtils.isASTMatchSchema(type, includeSchema)
+ : true;
+ const isSchemaExclude = excludeSchema
+ ? JsonSchemaUtils.isASTMatchSchema(type, excludeSchema)
+ : false;
+ const isSchemaMatch = isSchemaInclude && !isSchemaExclude;
+
+ // If not match, and no children, return null
+ if (!isSchemaMatch && !children?.length) {
+ return null;
+ }
+
+ return {
+ key: key,
+ title: variable.meta?.title || variable.key,
+ value: key,
+ keyPath,
+ icon: getVariableTypeIcon(variable), // TODO
+ children,
+ disabled: !isSchemaMatch,
+ rootMeta: parentFields[0]?.meta,
+ };
+ };
+
+ return [...available.variables.slice(0).reverse()]
+ .map((_variable) => renderVariable(_variable as VariableField))
+ .filter(Boolean) as TreeNodeData[];
+}
diff --git a/packages/materials/form-antd-materials/src/effects/auto-rename-ref/config.json b/packages/materials/form-antd-materials/src/effects/auto-rename-ref/config.json
new file mode 100644
index 00000000..676054fa
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/effects/auto-rename-ref/config.json
@@ -0,0 +1,5 @@
+{
+ "name": "auto-rename-ref",
+ "depMaterials": ["flow-value"],
+ "depPackages": ["lodash"]
+}
diff --git a/packages/materials/form-antd-materials/src/effects/auto-rename-ref/index.ts b/packages/materials/form-antd-materials/src/effects/auto-rename-ref/index.ts
new file mode 100644
index 00000000..9677fc27
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/effects/auto-rename-ref/index.ts
@@ -0,0 +1,104 @@
+import { isArray, isObject } from 'lodash';
+import {
+ DataEvent,
+ Effect,
+ EffectOptions,
+ VariableFieldKeyRenameService,
+} from '@flowgram.ai/editor';
+
+import { IFlowRefValue } from '../../typings';
+
+/**
+ * Auto rename ref when form item's key is renamed
+ *
+ * Example:
+ *
+ * formMeta: {
+ * effects: {
+ * "inputsValues": autoRenameRefEffect,
+ * }
+ * }
+ */
+export const autoRenameRefEffect: EffectOptions[] = [
+ {
+ event: DataEvent.onValueInit,
+ effect: ((params) => {
+ const { context, form, name } = params;
+
+ const renameService = context.node.getService(VariableFieldKeyRenameService);
+
+ const disposable = renameService.onRename(({ before, after }) => {
+ const beforeKeyPath = [
+ ...before.parentFields.map((_field) => _field.key).reverse(),
+ before.key,
+ ];
+ const afterKeyPath = [
+ ...after.parentFields.map((_field) => _field.key).reverse(),
+ after.key,
+ ];
+
+ // traverse rename refs inside form item 'name'
+ traverseRef(name, form.getValueIn(name), (_drilldownName, _v) => {
+ if (isRefMatch(_v, beforeKeyPath)) {
+ _v.content = [...afterKeyPath, ...(_v.content || [])?.slice(beforeKeyPath.length)];
+ form.setValueIn(_drilldownName, _v);
+ }
+ });
+ });
+
+ return () => {
+ disposable.dispose();
+ };
+ }) as Effect,
+ },
+];
+
+/**
+ * If ref value's keyPath is the under as targetKeyPath
+ * @param value
+ * @param targetKeyPath
+ * @returns
+ */
+function isRefMatch(value: IFlowRefValue, targetKeyPath: string[]) {
+ return targetKeyPath.every((_key, index) => _key === value.content?.[index]);
+}
+
+/**
+ * If value is ref
+ * @param value
+ * @returns
+ */
+function isRef(value: any): value is IFlowRefValue {
+ return (
+ value?.type === 'ref' && Array.isArray(value?.content) && typeof value?.content[0] === 'string'
+ );
+}
+
+/**
+ * Traverse value to find ref
+ * @param value
+ * @param options
+ * @returns
+ */
+function traverseRef(name: string, value: any, cb: (name: string, _v: IFlowRefValue) => void) {
+ if (isObject(value)) {
+ if (isRef(value)) {
+ cb(name, value);
+ return;
+ }
+
+ Object.entries(value).forEach(([_key, _value]) => {
+ traverseRef(`${name}.${_key}`, _value, cb);
+ });
+ return;
+ }
+
+ if (isArray(value)) {
+ value.forEach((_value, idx) => {
+ traverseRef(`${name}[${idx}]`, _value, cb);
+ });
+ return;
+ }
+
+ return;
+}
diff --git a/packages/materials/form-antd-materials/src/effects/index.ts b/packages/materials/form-antd-materials/src/effects/index.ts
new file mode 100644
index 00000000..efaf3d89
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/effects/index.ts
@@ -0,0 +1,3 @@
+export * from './provide-batch-input';
+export * from './provide-batch-outputs';
+export * from './auto-rename-ref';
diff --git a/packages/materials/form-antd-materials/src/effects/provide-batch-input/config.json b/packages/materials/form-antd-materials/src/effects/provide-batch-input/config.json
new file mode 100644
index 00000000..d1f2f479
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/effects/provide-batch-input/config.json
@@ -0,0 +1,5 @@
+{
+ "name": "provide-batch-input",
+ "depMaterials": ["flow-value"],
+ "depPackages": []
+}
diff --git a/packages/materials/form-antd-materials/src/effects/provide-batch-input/index.ts b/packages/materials/form-antd-materials/src/effects/provide-batch-input/index.ts
new file mode 100644
index 00000000..a8dff42d
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/effects/provide-batch-input/index.ts
@@ -0,0 +1,38 @@
+import {
+ ASTFactory,
+ EffectOptions,
+ FlowNodeRegistry,
+ createEffectFromVariableProvider,
+ getNodeForm,
+} from '@flowgram.ai/editor';
+
+import { IFlowRefValue } from '../../typings';
+
+export const provideBatchInputEffect: EffectOptions[] = createEffectFromVariableProvider({
+ private: true,
+ parse: (value: IFlowRefValue, ctx) => [
+ ASTFactory.createVariableDeclaration({
+ key: `${ctx.node.id}_locals`,
+ meta: {
+ title: getNodeForm(ctx.node)?.getValueIn('title'),
+ icon: ctx.node.getNodeRegistry().info?.icon,
+ },
+ type: ASTFactory.createObject({
+ properties: [
+ ASTFactory.createProperty({
+ key: 'item',
+ initializer: ASTFactory.createEnumerateExpression({
+ enumerateFor: ASTFactory.createKeyPathExpression({
+ keyPath: value.content || [],
+ }),
+ }),
+ }),
+ ASTFactory.createProperty({
+ key: 'index',
+ type: ASTFactory.createNumber(),
+ }),
+ ],
+ }),
+ }),
+ ],
+});
diff --git a/packages/materials/form-antd-materials/src/effects/provide-batch-outputs/config.json b/packages/materials/form-antd-materials/src/effects/provide-batch-outputs/config.json
new file mode 100644
index 00000000..6d89dc4a
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/effects/provide-batch-outputs/config.json
@@ -0,0 +1,5 @@
+{
+ "name": "provide-batch-outputs",
+ "depMaterials": ["flow-value"],
+ "depPackages": []
+}
diff --git a/packages/materials/form-antd-materials/src/effects/provide-batch-outputs/index.ts b/packages/materials/form-antd-materials/src/effects/provide-batch-outputs/index.ts
new file mode 100644
index 00000000..c392cd4d
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/effects/provide-batch-outputs/index.ts
@@ -0,0 +1,34 @@
+import {
+ ASTFactory,
+ EffectOptions,
+ FlowNodeRegistry,
+ createEffectFromVariableProvider,
+ getNodeForm,
+} from '@flowgram.ai/editor';
+
+import { IFlowRefValue } from '../../typings';
+
+export const provideBatchOutputsEffect: EffectOptions[] = createEffectFromVariableProvider({
+ private: true,
+ parse: (value: Record, ctx) => [
+ ASTFactory.createVariableDeclaration({
+ key: `${ctx.node.id}`,
+ meta: {
+ title: getNodeForm(ctx.node)?.getValueIn('title'),
+ icon: ctx.node.getNodeRegistry().info?.icon,
+ },
+ type: ASTFactory.createObject({
+ properties: Object.entries(value).map(([_key, value]) =>
+ ASTFactory.createProperty({
+ key: _key,
+ initializer: ASTFactory.createWrapArrayExpression({
+ wrapFor: ASTFactory.createKeyPathExpression({
+ keyPath: value.content || [],
+ }),
+ }),
+ })
+ ),
+ }),
+ }),
+ ],
+});
diff --git a/packages/materials/form-antd-materials/src/index.ts b/packages/materials/form-antd-materials/src/index.ts
new file mode 100644
index 00000000..ef5b7a92
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/index.ts
@@ -0,0 +1,4 @@
+export * from './components';
+export * from './effects';
+export * from './utils';
+export * from './typings';
diff --git a/packages/materials/form-antd-materials/src/typings/flow-value/config.json b/packages/materials/form-antd-materials/src/typings/flow-value/config.json
new file mode 100644
index 00000000..c7faad8d
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/typings/flow-value/config.json
@@ -0,0 +1,5 @@
+{
+ "name": "flow-value",
+ "depMaterials": [],
+ "depPackages": []
+}
diff --git a/packages/materials/form-antd-materials/src/typings/flow-value/index.ts b/packages/materials/form-antd-materials/src/typings/flow-value/index.ts
new file mode 100644
index 00000000..6627f3fe
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/typings/flow-value/index.ts
@@ -0,0 +1,27 @@
+export interface IFlowConstantValue {
+ type: 'constant';
+ content?: string | number | boolean;
+}
+
+export interface IFlowRefValue {
+ type: 'ref';
+ content?: string[];
+}
+
+export interface IFlowExpressionValue {
+ type: 'expression';
+ content?: string;
+}
+
+export interface IFlowTemplateValue {
+ type: 'template';
+ content?: string;
+}
+
+export type IFlowValue =
+ | IFlowConstantValue
+ | IFlowRefValue
+ | IFlowExpressionValue
+ | IFlowTemplateValue;
+
+export type IFlowConstantRefValue = IFlowConstantValue | IFlowRefValue;
diff --git a/packages/materials/form-antd-materials/src/typings/index.ts b/packages/materials/form-antd-materials/src/typings/index.ts
new file mode 100644
index 00000000..ac5f2fc7
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/typings/index.ts
@@ -0,0 +1,2 @@
+export * from './flow-value';
+export * from './json-schema';
diff --git a/packages/materials/form-antd-materials/src/typings/json-schema/config.json b/packages/materials/form-antd-materials/src/typings/json-schema/config.json
new file mode 100644
index 00000000..3a6aa9ff
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/typings/json-schema/config.json
@@ -0,0 +1,5 @@
+{
+ "name": "json-schema",
+ "depMaterials": [],
+ "depPackages": []
+}
diff --git a/packages/materials/form-antd-materials/src/typings/json-schema/index.ts b/packages/materials/form-antd-materials/src/typings/json-schema/index.ts
new file mode 100644
index 00000000..8709b47c
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/typings/json-schema/index.ts
@@ -0,0 +1,31 @@
+export type JsonSchemaBasicType =
+ | 'boolean'
+ | 'string'
+ | 'integer'
+ | 'number'
+ | 'object'
+ | 'array'
+ | 'map';
+
+export interface IJsonSchema {
+ type?: T;
+ default?: any;
+ title?: string;
+ description?: string;
+ enum?: (string | number)[];
+ properties?: Record>;
+ additionalProperties?: IJsonSchema;
+ items?: IJsonSchema;
+ required?: string[];
+ $ref?: string;
+ extra?: {
+ index?: number;
+ // Used in BaseType.isEqualWithJSONSchema, the type comparison will be weak
+ weak?: boolean;
+ // Set the render component
+ formComponent?: string;
+ [key: string]: any;
+ };
+}
+
+export type IBasicJsonSchema = IJsonSchema;
diff --git a/packages/materials/form-antd-materials/src/utils/format-legacy-refs/config.json b/packages/materials/form-antd-materials/src/utils/format-legacy-refs/config.json
new file mode 100644
index 00000000..aaa9ec6a
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/utils/format-legacy-refs/config.json
@@ -0,0 +1,5 @@
+{
+ "name": "format-legacy-ref",
+ "depMaterials": [],
+ "depPackages": []
+}
diff --git a/packages/materials/form-antd-materials/src/utils/format-legacy-refs/index.ts b/packages/materials/form-antd-materials/src/utils/format-legacy-refs/index.ts
new file mode 100644
index 00000000..0bacdc7e
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/utils/format-legacy-refs/index.ts
@@ -0,0 +1,153 @@
+import { isObject } from 'lodash';
+
+interface LegacyFlowRefValueSchema {
+ type: 'ref';
+ content: string;
+}
+
+interface NewFlowRefValueSchema {
+ type: 'ref';
+ content: string[];
+}
+
+/**
+ * In flowgram 0.2.0, for introducing Loop variable functionality,
+ * the FlowRefValueSchema type definition is updated:
+ *
+ * interface LegacyFlowRefValueSchema {
+ * type: 'ref';
+ * content: string;
+ * }
+ *
+ * interface NewFlowRefValueSchema {
+ * type: 'ref';
+ * content: string[];
+ * }
+ *
+ *
+ * For making sure backend json will not be changed, we provide format legacy ref utils for updating the formData
+ *
+ * How to use:
+ *
+ * 1. Call formatLegacyRefOnSubmit on the formData before submitting
+ * 2. Call formatLegacyRefOnInit on the formData after submitting
+ *
+ * Example:
+ * import { formatLegacyRefOnSubmit, formatLegacyRefOnInit } from '@flowgram.ai/form-materials';
+ * formMeta: {
+ * formatOnSubmit: (data) => formatLegacyRefOnSubmit(data),
+ * formatOnInit: (data) => formatLegacyRefOnInit(data),
+ * }
+ */
+export function formatLegacyRefOnSubmit(value: any): any {
+ if (isObject(value)) {
+ if (isLegacyFlowRefValueSchema(value)) {
+ return formatLegacyRefToNewRef(value);
+ }
+
+ return Object.fromEntries(
+ Object.entries(value).map(([key, value]: [string, any]) => [
+ key,
+ formatLegacyRefOnSubmit(value),
+ ])
+ );
+ }
+
+ if (Array.isArray(value)) {
+ return value.map(formatLegacyRefOnSubmit);
+ }
+
+ return value;
+}
+
+/**
+ * In flowgram 0.2.0, for introducing Loop variable functionality,
+ * the FlowRefValueSchema type definition is updated:
+ *
+ * interface LegacyFlowRefValueSchema {
+ * type: 'ref';
+ * content: string;
+ * }
+ *
+ * interface NewFlowRefValueSchema {
+ * type: 'ref';
+ * content: string[];
+ * }
+ *
+ *
+ * For making sure backend json will not be changed, we provide format legacy ref utils for updating the formData
+ *
+ * How to use:
+ *
+ * 1. Call formatLegacyRefOnSubmit on the formData before submitting
+ * 2. Call formatLegacyRefOnInit on the formData after submitting
+ *
+ * Example:
+ * import { formatLegacyRefOnSubmit, formatLegacyRefOnInit } from '@flowgram.ai/form-materials';
+ *
+ * formMeta: {
+ * formatOnSubmit: (data) => formatLegacyRefOnSubmit(data),
+ * formatOnInit: (data) => formatLegacyRefOnInit(data),
+ * }
+ */
+export function formatLegacyRefOnInit(value: any): any {
+ if (isObject(value)) {
+ if (isNewFlowRefValueSchema(value)) {
+ return formatNewRefToLegacyRef(value);
+ }
+
+ return Object.fromEntries(
+ Object.entries(value).map(([key, value]: [string, any]) => [
+ key,
+ formatLegacyRefOnInit(value),
+ ])
+ );
+ }
+
+ if (Array.isArray(value)) {
+ return value.map(formatLegacyRefOnInit);
+ }
+
+ return value;
+}
+
+export function isLegacyFlowRefValueSchema(value: any): value is LegacyFlowRefValueSchema {
+ return (
+ isObject(value) &&
+ Object.keys(value).length === 2 &&
+ (value as any).type === 'ref' &&
+ typeof (value as any).content === 'string'
+ );
+}
+
+export function isNewFlowRefValueSchema(value: any): value is NewFlowRefValueSchema {
+ return (
+ isObject(value) &&
+ Object.keys(value).length === 2 &&
+ (value as any).type === 'ref' &&
+ Array.isArray((value as any).content)
+ );
+}
+
+export function formatLegacyRefToNewRef(value: LegacyFlowRefValueSchema) {
+ const keyPath = value.content.split('.');
+
+ if (keyPath[1] === 'outputs') {
+ return {
+ type: 'ref',
+ content: [`${keyPath[0]}.${keyPath[1]}`, ...(keyPath.length > 2 ? keyPath.slice(2) : [])],
+ };
+ }
+
+ return {
+ type: 'ref',
+ content: keyPath,
+ };
+}
+
+export function formatNewRefToLegacyRef(value: NewFlowRefValueSchema) {
+ return {
+ type: 'ref',
+ content: value.content.join('.'),
+ };
+}
diff --git a/packages/materials/form-antd-materials/src/utils/format-legacy-refs/readme.md b/packages/materials/form-antd-materials/src/utils/format-legacy-refs/readme.md
new file mode 100644
index 00000000..f80ea06e
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/utils/format-legacy-refs/readme.md
@@ -0,0 +1,38 @@
+# Notice
+
+In `@flowgram.ai/form-materials@0.2.0`, for introducing loop-related materials,
+
+The FlowRefValueSchema type definition is updated:
+
+```typescript
+interface LegacyFlowRefValueSchema {
+ type: 'ref';
+ content: string;
+}
+
+interface NewFlowRefValueSchema {
+ type: 'ref';
+ content: string[];
+}
+```
+
+
+
+For making sure backend json will not be changed in your application, we provide `format-legacy-ref` utils for upgrading
+
+
+How to use:
+
+1. Call formatLegacyRefOnSubmit on the formData before submitting
+2. Call formatLegacyRefOnInit on the formData after submitting
+
+Example:
+
+```typescript
+import { formatLegacyRefOnSubmit, formatLegacyRefOnInit } from '@flowgram.ai/form-materials';
+
+formMeta: {
+ formatOnSubmit: (data) => formatLegacyRefOnSubmit(data),
+ formatOnInit: (data) => formatLegacyRefOnInit(data),
+}
+```
diff --git a/packages/materials/form-antd-materials/src/utils/index.ts b/packages/materials/form-antd-materials/src/utils/index.ts
new file mode 100644
index 00000000..7c50de92
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/utils/index.ts
@@ -0,0 +1,3 @@
+export * from './format-legacy-refs';
+export * from './json-schema';
+export * from './svg-icon';
diff --git a/packages/materials/form-antd-materials/src/utils/json-schema/config.json b/packages/materials/form-antd-materials/src/utils/json-schema/config.json
new file mode 100644
index 00000000..f0a4babc
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/utils/json-schema/config.json
@@ -0,0 +1,5 @@
+{
+ "name": "json-schema",
+ "depMaterials": ["typings/json-schema"],
+ "depPackages": ["lodash"]
+}
diff --git a/packages/materials/form-antd-materials/src/utils/json-schema/index.ts b/packages/materials/form-antd-materials/src/utils/json-schema/index.ts
new file mode 100644
index 00000000..c7f1e8e2
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/utils/json-schema/index.ts
@@ -0,0 +1,161 @@
+import { get } from 'lodash';
+import { ASTFactory, ASTKind, ASTMatch, ASTNode, ASTNodeJSON, BaseType } from '@flowgram.ai/editor';
+
+import { IJsonSchema } from '../../typings/json-schema';
+
+export namespace JsonSchemaUtils {
+ /**
+ * Converts a JSON schema to an Abstract Syntax Tree (AST) representation.
+ * This function recursively processes the JSON schema and creates corresponding AST nodes.
+ *
+ * For more information on JSON Schema, refer to the official documentation:
+ * https://json-schema.org/
+ *
+ * @param jsonSchema - The JSON schema to convert.
+ * @returns An AST node representing the JSON schema, or undefined if the schema type is not recognized.
+ */
+ export function schemaToAST(jsonSchema: IJsonSchema): ASTNodeJSON | undefined {
+ const { type, extra } = jsonSchema || {};
+ const { weak = false } = extra || {};
+
+ if (!type) {
+ return undefined;
+ }
+
+ switch (type) {
+ case 'object':
+ if (weak) {
+ return { kind: ASTKind.Object, weak: true };
+ }
+ return ASTFactory.createObject({
+ properties: Object.entries(jsonSchema.properties || {})
+ /**
+ * Sorts the properties of a JSON schema based on the 'extra.index' field.
+ * If the 'extra.index' field is not present, the property will be treated as having an index of 0.
+ */
+ .sort((a, b) => (get(a?.[1], 'extra.index') || 0) - (get(b?.[1], 'extra.index') || 0))
+ .map(([key, _property]) => ({
+ key,
+ type: schemaToAST(_property),
+ meta: { description: _property.description },
+ })),
+ });
+ case 'array':
+ if (weak) {
+ return { kind: ASTKind.Array, weak: true };
+ }
+ return ASTFactory.createArray({
+ items: schemaToAST(jsonSchema.items!),
+ });
+ case 'map':
+ if (weak) {
+ return { kind: ASTKind.Map, weak: true };
+ }
+ return ASTFactory.createMap({
+ valueType: schemaToAST(jsonSchema.additionalProperties!),
+ });
+ case 'string':
+ return ASTFactory.createString();
+ case 'number':
+ return ASTFactory.createNumber();
+ case 'boolean':
+ return ASTFactory.createBoolean();
+ case 'integer':
+ return ASTFactory.createInteger();
+
+ default:
+ // If the type is not recognized, return CustomType
+ return ASTFactory.createCustomType({ typeName: type });
+ }
+ }
+
+ /**
+ * Convert AST To JSON Schema
+ * @param typeAST
+ * @returns
+ */
+ export function astToSchema(
+ typeAST: ASTNode,
+ options?: { drilldown?: boolean }
+ ): IJsonSchema | undefined {
+ const { drilldown = true } = options || {};
+
+ if (ASTMatch.isString(typeAST)) {
+ return {
+ type: 'string',
+ };
+ }
+
+ if (ASTMatch.isBoolean(typeAST)) {
+ return {
+ type: 'boolean',
+ };
+ }
+
+ if (ASTMatch.isNumber(typeAST)) {
+ return {
+ type: 'number',
+ };
+ }
+
+ if (ASTMatch.isInteger(typeAST)) {
+ return {
+ type: 'integer',
+ };
+ }
+
+ if (ASTMatch.isObject(typeAST)) {
+ return {
+ type: 'object',
+ properties: drilldown
+ ? Object.fromEntries(
+ Object.entries(typeAST.properties).map(([key, value]) => [key, astToSchema(value)!])
+ )
+ : {},
+ };
+ }
+
+ if (ASTMatch.isArray(typeAST)) {
+ return {
+ type: 'array',
+ items: drilldown ? astToSchema(typeAST.items) : undefined,
+ };
+ }
+
+ if (ASTMatch.isMap(typeAST)) {
+ return {
+ type: 'map',
+ items: drilldown ? astToSchema(typeAST.valueType) : undefined,
+ };
+ }
+
+ if (ASTMatch.isCustomType(typeAST)) {
+ return {
+ type: typeAST.typeName,
+ };
+ }
+
+ return undefined;
+ }
+
+ /**
+ * Check if the AST type is match the JSON Schema
+ * @param typeAST
+ * @param schema
+ * @returns
+ */
+ export function isASTMatchSchema(
+ typeAST: BaseType,
+ schema: IJsonSchema | IJsonSchema[]
+ ): boolean {
+ if (Array.isArray(schema)) {
+ return typeAST.isTypeEqual(
+ ASTFactory.createUnion({
+ types: schema.map((_schema) => schemaToAST(_schema)!).filter(Boolean),
+ })
+ );
+ }
+
+ return typeAST.isTypeEqual(schemaToAST(schema));
+ }
+}
diff --git a/packages/materials/form-antd-materials/src/utils/svg-icon/index.tsx b/packages/materials/form-antd-materials/src/utils/svg-icon/index.tsx
new file mode 100644
index 00000000..88360180
--- /dev/null
+++ b/packages/materials/form-antd-materials/src/utils/svg-icon/index.tsx
@@ -0,0 +1,8 @@
+import React from 'react';
+
+export function SvgIcon(props: {
+ size?: 'inherit' | 'extra-small' | 'small' | 'default' | 'large' | 'extra-large';
+ svg: React.ReactNode;
+}) {
+ return {props.svg};
+}
diff --git a/packages/materials/form-antd-materials/tsconfig.json b/packages/materials/form-antd-materials/tsconfig.json
new file mode 100644
index 00000000..a189f267
--- /dev/null
+++ b/packages/materials/form-antd-materials/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "extends": "@flowgram.ai/ts-config/tsconfig.flow.path.json",
+ "compilerOptions": {
+ "jsx": "react"
+ },
+ "include": ["./src", "./bin/**/*.ts"],
+ "exclude": ["node_modules"]
+}
diff --git a/packages/materials/form-antd-materials/vitest.config.ts b/packages/materials/form-antd-materials/vitest.config.ts
new file mode 100644
index 00000000..97c9de9b
--- /dev/null
+++ b/packages/materials/form-antd-materials/vitest.config.ts
@@ -0,0 +1,26 @@
+const path = require('path');
+import { defineConfig } from 'vitest/config';
+
+export default defineConfig({
+ build: {
+ commonjsOptions: {
+ transformMixedEsModules: true,
+ },
+ },
+ test: {
+ globals: true,
+ mockReset: false,
+ environment: 'jsdom',
+ setupFiles: [path.resolve(__dirname, './vitest.setup.ts')],
+ include: ['**/?(*.){test,spec}.?(c|m)[jt]s?(x)'],
+ exclude: [
+ '**/__mocks__**',
+ '**/node_modules/**',
+ '**/dist/**',
+ '**/lib/**', // lib 编译结果忽略掉
+ '**/cypress/**',
+ '**/.{idea,git,cache,output,temp}/**',
+ '**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build}.config.*',
+ ],
+ },
+});
diff --git a/packages/materials/form-antd-materials/vitest.setup.ts b/packages/materials/form-antd-materials/vitest.setup.ts
new file mode 100644
index 00000000..d2c9bc6e
--- /dev/null
+++ b/packages/materials/form-antd-materials/vitest.setup.ts
@@ -0,0 +1 @@
+import 'reflect-metadata';
diff --git a/rush.json b/rush.json
index 2950745e..27d532c0 100644
--- a/rush.json
+++ b/rush.json
@@ -851,6 +851,15 @@
"team-flow"
]
},
+ {
+ "packageName": "@flowgram.ai/form-antd-materials",
+ "projectFolder": "packages/materials/form-antd-materials",
+ "versionPolicyName": "publishPolicy",
+ "tags": [
+ "level-1",
+ "team-flow"
+ ]
+ },
{
"packageName": "@flowgram.ai/free-auto-layout-plugin",
"projectFolder": "packages/plugins/free-auto-layout-plugin",
@@ -927,6 +936,16 @@
"demo"
]
},
+ {
+ "packageName": "@flowgram.ai/demo-nextjs-antd",
+ "projectFolder": "apps/demo-nextjs-antd",
+ "versionPolicyName": "appPolicy",
+ "tags": [
+ "level-1",
+ "team-flow",
+ "demo"
+ ]
+ },
{
"packageName": "@flowgram.ai/demo-react-16",
"projectFolder": "apps/demo-react-16",